[
  {
    "path": ".gitattributes",
    "content": "###############################################################################\n# Set default behavior to automatically normalize line endings.\n###############################################################################\n* text eol=lf\n\n###############################################################################\n# Set default behavior for command prompt diff.\n#\n# This is need for earlier builds of msysgit that does not have it on by\n# default for csharp files.\n# Note: This is only used by command line\n###############################################################################\n#*.cs     diff=csharp\n\n###############################################################################\n# Set the merge driver for project and solution files\n#\n# Merging from the command prompt will add diff markers to the files if there\n# are conflicts (Merging from VS is not affected by the settings below, in VS\n# the diff markers are never inserted). Diff markers may cause the following \n# file extensions to fail to load in VS. An alternative would be to treat\n# these files as binary and thus will always conflict and require user\n# intervention with every merge. To do so, just uncomment the entries below\n###############################################################################\n#*.sln       merge=binary\n#*.csproj    merge=binary\n#*.vbproj    merge=binary\n#*.vcxproj   merge=binary\n#*.vcproj    merge=binary\n#*.dbproj    merge=binary\n#*.fsproj    merge=binary\n#*.lsproj    merge=binary\n#*.wixproj   merge=binary\n#*.modelproj merge=binary\n#*.sqlproj   merge=binary\n#*.wwaproj   merge=binary\n\n###############################################################################\n# behavior for image files\n#\n# image files are treated as binary by default.\n###############################################################################\n#*.jpg   binary\n#*.png   binary\n#*.gif   binary\n\n###############################################################################\n# diff behavior for common document formats\n# \n# Convert binary document formats to text before diffing them. This feature\n# is only available from the command line. Turn it on by uncommenting the \n# entries below.\n###############################################################################\n#*.doc   diff=astextplain\n#*.DOC   diff=astextplain\n#*.docx  diff=astextplain\n#*.DOCX  diff=astextplain\n#*.dot   diff=astextplain\n#*.DOT   diff=astextplain\n#*.pdf   diff=astextplain\n#*.PDF   diff=astextplain\n#*.rtf   diff=astextplain\n#*.RTF   diff=astextplain\n"
  },
  {
    "path": ".gitignore",
    "content": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n\nPokemonBattleEngineExtras/DumpedData\n\n# User-specific files\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# User-specific files (MonoDevelop/Xamarin Studio)\n*.userprefs\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\nbld/\n[Bb]in/\n[Oo]bj/\n[Ll]og/\n\n# Visual Studio 2015 cache/options directory\n.vs/\n# Uncomment if you have tasks that create the project's static files in wwwroot\n#wwwroot/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n# NUNIT\n*.VisualState.xml\nTestResult.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n# DNX\nproject.lock.json\nproject.fragment.lock.json\nartifacts/\n\n*_i.c\n*_p.c\n*_i.h\n*.ilk\n*.meta\n*.obj\n*.pch\n*.pdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opendb\n*.opensdf\n*.sdf\n*.cachefile\n*.VC.db\n*.VC.VC.opendb\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n*.sap\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# JustCode is a .NET coding add-in\n.JustCode\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\nnCrunchTemp_*\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n# TODO: Comment the next line if you want to checkin your web deploy settings\n# but database connection strings (with potential passwords) will be unencrypted\n#*.pubxml\n*.publishproj\n\n# Microsoft Azure Web App publish settings. Comment the next line if you want to\n# checkin your Azure Web App publish settings, but sensitive information contained\n# in these scripts will be unencrypted\nPublishScripts/\n\n# NuGet Packages\n*.nupkg\n# The packages folder can be ignored because of Package Restore\n**/packages/*\n# except build/, which is used as an MSBuild target.\n!**/packages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/packages/repositories.config\n# NuGet v3's project.json files produces more ignoreable files\n*.nuget.props\n*.nuget.targets\n\n# Microsoft Azure Build Output\ncsx/\n*.build.csdef\n\n# Microsoft Azure Emulator\necf/\nrcf/\n\n# Windows Store app package directories and files\nAppPackages/\nBundleArtifacts/\nPackage.StoreAssociation.xml\n_pkginfo.txt\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!*.[Cc]ache/\n\n# Others\nClientBin/\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.jfm\n*.pfx\n*.publishsettings\nnode_modules/\norleans.codegen.cs\n\n# Since there are multiple workflows, uncomment next line to ignore bower_components\n# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)\n#bower_components/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\n\n# SQL Server files\n*.mdf\n*.ldf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# GhostDoc plugin setting file\n*.GhostDoc.xml\n\n# Node.js Tools for Visual Studio\n.ntvs_analysis.dat\n\n# Visual Studio 6 build log\n*.plg\n\n# Visual Studio 6 workspace options file\n*.opt\n\n# Visual Studio LightSwitch build output\n**/*.HTMLClient/GeneratedArtifacts\n**/*.DesktopClient/GeneratedArtifacts\n**/*.DesktopClient/ModelManifest.xml\n**/*.Server/GeneratedArtifacts\n**/*.Server/ModelManifest.xml\n_Pvt_Extensions\n\n# Paket dependency manager\n.paket/paket.exe\npaket-files/\n\n# FAKE - F# Make\n.fake/\n\n# JetBrains Rider\n.idea/\n*.sln.iml\n\n# CodeRush\n.cr/\n\n# Python Tools for Visual Studio (PTVS)\n__pycache__/\n*.pyc"
  },
  {
    "path": "LICENSE.md",
    "content": "MIT License\n\nCopyright (c) 2022 Kermalis\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE."
  },
  {
    "path": "PokemonBattleEngine/Battle/Battle.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Packets;\nusing Kermalis.PokemonBattleEngine.Utils;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace Kermalis.PokemonBattleEngine.Battle;\n\n/// <summary>Represents a specific Pokémon battle.</summary>\npublic sealed partial class PBEBattle\n{\n\tpublic delegate void BattleStateChangedEvent(PBEBattle battle);\n\tpublic event BattleStateChangedEvent? OnStateChanged;\n\tprivate PBEBattleState _battleState;\n\tpublic PBEBattleState BattleState\n\t{\n\t\tget => _battleState;\n\t\tprivate set\n\t\t{\n\t\t\tif (value != _battleState)\n\t\t\t{\n\t\t\t\t_battleState = value;\n\t\t\t\tOnStateChanged?.Invoke(this);\n\t\t\t}\n\t\t}\n\t}\n\tpublic ushort TurnNumber { get; set; }\n\tpublic PBEBattleResult? BattleResult { get; set; }\n\n\tprivate readonly PBERandom _rand;\n\tpublic bool IsLocallyHosted { get; }\n\tpublic PBEBattleType BattleType { get; }\n\tpublic PBEBattleTerrain BattleTerrain { get; }\n\tpublic PBEBattleFormat BattleFormat { get; }\n\tpublic PBESettings Settings { get; }\n\tpublic PBETeams Teams { get; }\n\tpublic PBETrainers Trainers { get; }\n\tpublic List<PBEBattlePokemon> ActiveBattlers { get; } = new(6);\n\tprivate readonly List<PBEBattlePokemon> _turnOrder;\n\n\tpublic PBEWeather Weather { get; set; }\n\tpublic byte WeatherCounter { get; set; }\n\tpublic PBEBattleStatus BattleStatus { get; set; }\n\tpublic byte TrickRoomCount { get; set; }\n\n\tpublic List<IPBEPacket> Events { get; } = new();\n\n\t// Trainer battle\n\tprivate PBEBattle(PBEBattleFormat battleFormat, PBESettings settings, IReadOnlyList<PBETrainerInfo> ti0, IReadOnlyList<PBETrainerInfo> ti1,\n\t\tPBEBattleTerrain battleTerrain, PBEWeather weather, int? randomSeed)\n\t{\n\t\tif (battleFormat >= PBEBattleFormat.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(battleFormat));\n\t\t}\n\t\tif (battleTerrain >= PBEBattleTerrain.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(battleTerrain));\n\t\t}\n\t\tif (weather >= PBEWeather.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(weather));\n\t\t}\n\n\t\tsettings.ShouldBeReadOnly(nameof(settings));\n\t\t_rand = new PBERandom(randomSeed ?? PBEDataProvider.GlobalRandom.RandomInt());\n\t\tIsLocallyHosted = true;\n\t\tBattleType = PBEBattleType.Trainer;\n\t\tBattleTerrain = battleTerrain;\n\t\tBattleFormat = battleFormat;\n\t\tSettings = settings;\n\t\tWeather = weather;\n\t\tTeams = new PBETeams(this, ti0, ti1, out PBETrainers trainers);\n\t\tTrainers = trainers;\n\t\t_turnOrder = new List<PBEBattlePokemon>(6);\n\t\tQueueUpPokemon();\n\t}\n\t// Wild battle\n\tprivate PBEBattle(PBEBattleFormat battleFormat, PBESettings settings, IReadOnlyList<PBETrainerInfo> ti, PBEWildInfo wi,\n\t\tPBEBattleTerrain battleTerrain, PBEWeather weather, int? randomSeed)\n\t{\n\t\tif (battleFormat >= PBEBattleFormat.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(battleFormat));\n\t\t}\n\t\tif (battleTerrain >= PBEBattleTerrain.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(battleTerrain));\n\t\t}\n\t\tif (weather >= PBEWeather.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(weather));\n\t\t}\n\t\tsettings.ShouldBeReadOnly(nameof(settings));\n\t\t_rand = new PBERandom(randomSeed ?? PBEDataProvider.GlobalRandom.RandomInt());\n\t\tIsLocallyHosted = true;\n\t\tBattleType = PBEBattleType.Wild;\n\t\tBattleTerrain = battleTerrain;\n\t\tBattleFormat = battleFormat;\n\t\tSettings = settings;\n\t\tWeather = weather;\n\t\tTeams = new PBETeams(this, ti, wi, out PBETrainers trainers);\n\t\tTrainers = trainers;\n\t\t_turnOrder = new List<PBEBattlePokemon>(6);\n\t\tQueueUpPokemon();\n\t}\n\t// Remote battle\n\tprivate PBEBattle(PBEBattlePacket packet)\n\t{\n\t\tIsLocallyHosted = false;\n\t\tBattleType = packet.BattleType;\n\t\tBattleFormat = packet.BattleFormat;\n\t\tBattleTerrain = packet.BattleTerrain;\n\t\tWeather = packet.Weather;\n\t\tSettings = packet.Settings;\n\t\tTeams = new PBETeams(this, packet, out PBETrainers trainers);\n\t\tTrainers = trainers;\n\t\t// These two will never be used in a non-local battle\n\t\t_rand = null!;\n\t\t_turnOrder = null!;\n\t}\n\n\tpublic static PBEBattle CreateTrainerBattle(PBEBattleFormat battleFormat, PBESettings settings, PBETrainerInfo ti0, PBETrainerInfo ti1,\n\t\tPBEBattleTerrain battleTerrain = PBEBattleTerrain.Plain, PBEWeather weather = PBEWeather.None, int? randomSeed = null)\n\t{\n\t\treturn new PBEBattle(battleFormat, settings, new[] { ti0 }, new[] { ti1 }, battleTerrain, weather, randomSeed);\n\t}\n\tpublic static PBEBattle CreateTrainerBattle(PBEBattleFormat battleFormat, PBESettings settings, IReadOnlyList<PBETrainerInfo> ti0, IReadOnlyList<PBETrainerInfo> ti1,\n\t\tPBEBattleTerrain battleTerrain = PBEBattleTerrain.Plain, PBEWeather weather = PBEWeather.None, int? randomSeed = null)\n\t{\n\t\treturn new PBEBattle(battleFormat, settings, ti0, ti1, battleTerrain, weather, randomSeed);\n\t}\n\tpublic static PBEBattle CreateWildBattle(PBEBattleFormat battleFormat, PBESettings settings, PBETrainerInfo ti0, PBEWildInfo wi,\n\t\tPBEBattleTerrain battleTerrain = PBEBattleTerrain.Plain, PBEWeather weather = PBEWeather.None, int? randomSeed = null)\n\t{\n\t\treturn new PBEBattle(battleFormat, settings, new[] { ti0 }, wi, battleTerrain, weather, randomSeed);\n\t}\n\tpublic static PBEBattle CreateWildBattle(PBEBattleFormat battleFormat, PBESettings settings, IReadOnlyList<PBETrainerInfo> ti0, PBEWildInfo wi,\n\t\tPBEBattleTerrain battleTerrain = PBEBattleTerrain.Plain, PBEWeather weather = PBEWeather.None, int? randomSeed = null)\n\t{\n\t\treturn new PBEBattle(battleFormat, settings, ti0, wi, battleTerrain, weather, randomSeed);\n\t}\n\tpublic static PBEBattle CreateRemoteBattle(PBEBattlePacket packet)\n\t{\n\t\treturn new PBEBattle(packet);\n\t}\n\n\tprivate void QueueUp(PBETeam team, PBEFieldPosition pos, ref PBETrainer? tr, ref int i)\n\t{\n\t\t// See which trainer owns this spot\n\t\tPBETrainer t = team.GetTrainer(pos);\n\t\t// If it's not the previous trainer, we start at their first PKMN\n\t\tif (tr != t)\n\t\t{\n\t\t\ti = 0;\n\t\t\ttr = t;\n\t\t}\n\t\tPBEList<PBEBattlePokemon> party = t.Party;\n\t// If the check index is valid, try to send out a non-fainted non-ignore PKMN\n\ttryget:\n\t\tif (i < party.Count)\n\t\t{\n\t\t\tPBEBattlePokemon p = party[i];\n\t\t\t// If we should ignore this PKMN, try to get the one in the next index\n\t\t\tif (!p.CanBattle)\n\t\t\t{\n\t\t\t\ti++;\n\t\t\t\tgoto tryget;\n\t\t\t}\n\t\t\t// Valid PKMN, send it out\n\t\t\tp.Trainer.SwitchInQueue.Add((p, pos));\n\t\t\t// Wild PKMN should be out already\n\t\t\tif (team.IsWild)\n\t\t\t{\n\t\t\t\tp.FieldPosition = pos;\n\t\t\t\tActiveBattlers.Add(p);\n\t\t\t}\n\t\t\t// Next slot to check\n\t\t\ti++;\n\t\t}\n\t}\n\tinternal void QueueUpPokemon()\n\t{\n\t\tswitch (BattleFormat)\n\t\t{\n\t\t\tcase PBEBattleFormat.Single:\n\t\t\t{\n\t\t\t\tforeach (PBETeam team in Teams)\n\t\t\t\t{\n\t\t\t\t\tPBETrainer? t = null;\n\t\t\t\t\tint i = 0;\n\t\t\t\t\tQueueUp(team, PBEFieldPosition.Center, ref t, ref i);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEBattleFormat.Double:\n\t\t\t{\n\t\t\t\tforeach (PBETeam team in Teams)\n\t\t\t\t{\n\t\t\t\t\tPBETrainer? t = null;\n\t\t\t\t\tint i = 0;\n\t\t\t\t\tQueueUp(team, PBEFieldPosition.Left, ref t, ref i);\n\t\t\t\t\tQueueUp(team, PBEFieldPosition.Right, ref t, ref i);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEBattleFormat.Triple:\n\t\t\t{\n\t\t\t\tforeach (PBETeam team in Teams)\n\t\t\t\t{\n\t\t\t\t\tPBETrainer? t = null;\n\t\t\t\t\tint i = 0;\n\t\t\t\t\tQueueUp(team, PBEFieldPosition.Left, ref t, ref i);\n\t\t\t\t\tQueueUp(team, PBEFieldPosition.Center, ref t, ref i);\n\t\t\t\t\tQueueUp(team, PBEFieldPosition.Right, ref t, ref i);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEBattleFormat.Rotation:\n\t\t\t{\n\t\t\t\tforeach (PBETeam team in Teams)\n\t\t\t\t{\n\t\t\t\t\tPBETrainer? t = null;\n\t\t\t\t\tint i = 0;\n\t\t\t\t\tQueueUp(team, PBEFieldPosition.Center, ref t, ref i);\n\t\t\t\t\tQueueUp(team, PBEFieldPosition.Left, ref t, ref i);\n\t\t\t\t\tQueueUp(team, PBEFieldPosition.Right, ref t, ref i);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(BattleFormat));\n\t\t}\n\n\t\tBattleState = PBEBattleState.ReadyToBegin;\n\t}\n\tprivate void CheckLocal()\n\t{\n\t\tif (!IsLocallyHosted)\n\t\t{\n\t\t\tthrow new InvalidOperationException(\"This battle is not locally hosted\");\n\t\t}\n\t}\n\t/// <summary>Begins the battle.</summary>\n\t/// <exception cref=\"InvalidOperationException\">Thrown when <see cref=\"BattleState\"/> is not <see cref=\"PBEBattleState.ReadyToBegin\"/>.</exception>\n\tpublic void Begin()\n\t{\n\t\tCheckLocal();\n\t\tif (_battleState != PBEBattleState.ReadyToBegin)\n\t\t{\n\t\t\tthrow new InvalidOperationException($\"{nameof(BattleState)} must be {PBEBattleState.ReadyToBegin} to begin the battle.\");\n\t\t}\n\t\tBattleState = PBEBattleState.Processing;\n\t\tBroadcastBattle(); // The first packet sent is PBEBattlePacket which replays rely on\n\t\t\t\t\t\t   // Wild Pokémon appearing\n\t\tif (BattleType == PBEBattleType.Wild)\n\t\t{\n\t\t\tPBETeam team = Teams[1];\n\t\t\tPBETrainer trainer = team.Trainers[0];\n\t\t\tint count = trainer.SwitchInQueue.Count;\n\t\t\tvar appearances = new PBEPkmnAppearedInfo[count];\n\t\t\tfor (int i = 0; i < count; i++)\n\t\t\t{\n\t\t\t\tappearances[i] = new PBEPkmnAppearedInfo(trainer.SwitchInQueue[i].Pkmn);\n\t\t\t}\n\t\t\ttrainer.SwitchInQueue.Clear();\n\t\t\tBroadcastWildPkmnAppeared(appearances);\n\t\t}\n\t\tSwitchesOrActions();\n\t}\n\t/// <summary>Runs a turn.</summary>\n\t/// <exception cref=\"InvalidOperationException\">Thrown when <see cref=\"BattleState\"/> is not <see cref=\"PBEBattleState.ReadyToRunTurn\"/>.</exception>\n\tpublic void RunTurn()\n\t{\n\t\tCheckLocal();\n\t\tif (_battleState != PBEBattleState.ReadyToRunTurn)\n\t\t{\n\t\t\tthrow new InvalidOperationException($\"{nameof(BattleState)} must be {PBEBattleState.ReadyToRunTurn} to run a turn.\");\n\t\t}\n\t\tBattleState = PBEBattleState.Processing;\n\t\tFleeCheck();\n\t\tif (EndCheck())\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\tDetermineTurnOrder();\n\t\tRunActionsInOrder();\n\t\tTurnEnded();\n\t}\n\tpublic void RunSwitches()\n\t{\n\t\tCheckLocal();\n\t\tif (_battleState != PBEBattleState.ReadyToRunSwitches)\n\t\t{\n\t\t\tthrow new InvalidOperationException($\"{nameof(BattleState)} must be {PBEBattleState.ReadyToRunSwitches} to run switches.\");\n\t\t}\n\t\tBattleState = PBEBattleState.Processing;\n\t\tFleeCheck();\n\t\tif (EndCheck())\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\tSwitchesOrActions();\n\t}\n\n\t/// <summary>Sets <see cref=\"BattleState\"/> to <see cref=\"PBEBattleState.Ended\"/> and clears <see cref=\"OnNewEvent\"/> and <see cref=\"OnStateChanged\"/>. Does not touch <see cref=\"BattleResult\"/>.</summary>\n\tpublic void SetEnded()\n\t{\n\t\tif (_battleState != PBEBattleState.Ended)\n\t\t{\n\t\t\tBattleState = PBEBattleState.Ended;\n\t\t\tOnNewEvent = null;\n\t\t\tOnStateChanged = null;\n\t\t}\n\t}\n\tprivate bool EndCheck()\n\t{\n\t\tif (BattleResult is not null)\n\t\t{\n\t\t\tBroadcastBattleResult(BattleResult.Value);\n\t\t\tforeach (PBEBattlePokemon pkmn in ActiveBattlers)\n\t\t\t{\n\t\t\t\tpkmn.ApplyNaturalCure(); // Natural Cure happens at the end of the battle. Pokémon should be copied when BattleState is set to \"Ended\", not upon battle result.\n\t\t\t}\n\t\t\tSetEnded();\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\tprivate void SwitchesOrActions()\n\t{\n\t\t// Checking SwitchInQueue count since SwitchInsRequired is set to 0 after submitting switches\n\t\tPBETrainer[] trainersWithSwitchIns = Trainers.Where(t => t.SwitchInQueue.Count > 0).ToArray();\n\t\tif (trainersWithSwitchIns.Length > 0)\n\t\t{\n\t\t\tvar list = new List<PBEBattlePokemon>(6);\n\t\t\tforeach (PBETrainer trainer in trainersWithSwitchIns)\n\t\t\t{\n\t\t\t\tint count = trainer.SwitchInQueue.Count;\n\t\t\t\tvar switches = new PBEPkmnAppearedInfo[count];\n\t\t\t\tfor (int i = 0; i < count; i++)\n\t\t\t\t{\n\t\t\t\t\t(PBEBattlePokemon pkmn, PBEFieldPosition pos) = trainer.SwitchInQueue[i];\n\t\t\t\t\tpkmn.FieldPosition = pos;\n\t\t\t\t\tswitches[i] = CreateSwitchInInfo(pkmn);\n\t\t\t\t\tPBETrainer.SwitchTwoPokemon(pkmn, pos); // Swap after Illusion\n\t\t\t\t\tActiveBattlers.Add(pkmn); // Add before broadcast\n\t\t\t\t\tlist.Add(pkmn);\n\t\t\t\t}\n\t\t\t\tBroadcastPkmnSwitchIn(trainer, switches);\n\t\t\t}\n\t\t\tDoSwitchInEffects(list);\n\t\t}\n\n\t\tforeach (PBETrainer trainer in Trainers)\n\t\t{\n\t\t\tint available = trainer.NumConsciousPkmn - trainer.NumPkmnOnField;\n\t\t\ttrainer.SwitchInsRequired = 0;\n\t\t\ttrainer.SwitchInQueue.Clear();\n\t\t\tif (available > 0)\n\t\t\t{\n\t\t\t\tswitch (BattleFormat)\n\t\t\t\t{\n\t\t\t\t\tcase PBEBattleFormat.Single:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!trainer.IsSpotOccupied(PBEFieldPosition.Center))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttrainer.SwitchInsRequired = 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEBattleFormat.Double:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (trainer.OwnsSpot(PBEFieldPosition.Left) && !trainer.IsSpotOccupied(PBEFieldPosition.Left))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tavailable--;\n\t\t\t\t\t\t\ttrainer.SwitchInsRequired++;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (available > 0 && trainer.OwnsSpot(PBEFieldPosition.Right) && !trainer.IsSpotOccupied(PBEFieldPosition.Right))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttrainer.SwitchInsRequired++;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEBattleFormat.Rotation:\n\t\t\t\t\tcase PBEBattleFormat.Triple:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (trainer.OwnsSpot(PBEFieldPosition.Left) && !trainer.IsSpotOccupied(PBEFieldPosition.Left))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tavailable--;\n\t\t\t\t\t\t\ttrainer.SwitchInsRequired++;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (available > 0 && trainer.OwnsSpot(PBEFieldPosition.Center) && !trainer.IsSpotOccupied(PBEFieldPosition.Center))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tavailable--;\n\t\t\t\t\t\t\ttrainer.SwitchInsRequired++;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (available > 0 && trainer.OwnsSpot(PBEFieldPosition.Right) && !trainer.IsSpotOccupied(PBEFieldPosition.Right))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttrainer.SwitchInsRequired++;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(BattleFormat));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\ttrainersWithSwitchIns = Trainers.Where(t => t.SwitchInsRequired > 0).ToArray();\n\t\tif (trainersWithSwitchIns.Length > 0)\n\t\t{\n\t\t\tBattleState = PBEBattleState.WaitingForSwitchIns;\n\t\t\tforeach (PBETrainer trainer in trainersWithSwitchIns)\n\t\t\t{\n\t\t\t\tBroadcastSwitchInRequest(trainer);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (EndCheck())\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tforeach (PBEBattlePokemon pkmn in ActiveBattlers)\n\t\t\t{\n\t\t\t\tpkmn.HasUsedMoveThisTurn = false;\n\t\t\t\tpkmn.TurnAction = null;\n\t\t\t\tpkmn.SpeedBoost_AbleToSpeedBoostThisTurn = pkmn.Ability == PBEAbility.SpeedBoost;\n\n\t\t\t\tif (pkmn.Status2.HasFlag(PBEStatus2.Flinching))\n\t\t\t\t{\n\t\t\t\t\tBroadcastStatus2(pkmn, pkmn, PBEStatus2.Flinching, PBEStatusAction.Ended);\n\t\t\t\t}\n\t\t\t\tif (pkmn.Status2.HasFlag(PBEStatus2.HelpingHand))\n\t\t\t\t{\n\t\t\t\t\tBroadcastStatus2(pkmn, pkmn, PBEStatus2.HelpingHand, PBEStatusAction.Ended);\n\t\t\t\t}\n\t\t\t\tif (pkmn.Status2.HasFlag(PBEStatus2.LockOn))\n\t\t\t\t{\n\t\t\t\t\tif (--pkmn.LockOnTurns == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tpkmn.LockOnPokemon = null;\n\t\t\t\t\t\tBroadcastStatus2(pkmn, pkmn, PBEStatus2.LockOn, PBEStatusAction.Ended);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (pkmn.Protection_Used)\n\t\t\t\t{\n\t\t\t\t\tpkmn.Protection_Counter++;\n\t\t\t\t\tpkmn.Protection_Used = false;\n\t\t\t\t\tif (pkmn.Status2.HasFlag(PBEStatus2.Protected))\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastStatus2(pkmn, pkmn, PBEStatus2.Protected, PBEStatusAction.Ended);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tpkmn.Protection_Counter = 0;\n\t\t\t\t}\n\t\t\t\tif (pkmn.Status2.HasFlag(PBEStatus2.Roost))\n\t\t\t\t{\n\t\t\t\t\tpkmn.EndRoost();\n\t\t\t\t\tBroadcastStatus2(pkmn, pkmn, PBEStatus2.Roost, PBEStatusAction.Ended);\n\t\t\t\t}\n\t\t\t}\n\t\t\tforeach (PBETeam team in Teams)\n\t\t\t{\n\t\t\t\tif (team.TeamStatus.HasFlag(PBETeamStatus.QuickGuard))\n\t\t\t\t{\n\t\t\t\t\tBroadcastTeamStatus(team, PBETeamStatus.QuickGuard, PBETeamStatusAction.Ended);\n\t\t\t\t}\n\t\t\t\tif (team.TeamStatus.HasFlag(PBETeamStatus.WideGuard))\n\t\t\t\t{\n\t\t\t\t\tBroadcastTeamStatus(team, PBETeamStatus.WideGuard, PBETeamStatusAction.Ended);\n\t\t\t\t}\n\t\t\t}\n\t\t\tforeach (PBETrainer trainer in Trainers)\n\t\t\t{\n\t\t\t\ttrainer.ActionsRequired.Clear();\n\t\t\t\ttrainer.ActionsRequired.AddRange(trainer.ActiveBattlersOrdered);\n\t\t\t}\n\n\t\t\t// #318 - We check pkmn on the field instead of conscious pkmn because of multi-battles\n\t\t\t// It still works if there's only one trainer on the team since we check for available switch-ins above\n\t\t\tif (BattleFormat == PBEBattleFormat.Triple && Teams.All(t => t.NumPkmnOnField == 1))\n\t\t\t{\n\t\t\t\tPBEBattlePokemon pkmn0 = ActiveBattlers[0],\n\t\t\t\t\t\tpkmn1 = ActiveBattlers[1];\n\t\t\t\tif ((pkmn0.FieldPosition == PBEFieldPosition.Left && pkmn1.FieldPosition == PBEFieldPosition.Left) || (pkmn0.FieldPosition == PBEFieldPosition.Right && pkmn1.FieldPosition == PBEFieldPosition.Right))\n\t\t\t\t{\n\t\t\t\t\tPBEFieldPosition pkmn0OldPos = pkmn0.FieldPosition,\n\t\t\t\t\t\t\tpkmn1OldPos = pkmn1.FieldPosition;\n\t\t\t\t\tpkmn0.FieldPosition = PBEFieldPosition.Center;\n\t\t\t\t\tpkmn1.FieldPosition = PBEFieldPosition.Center;\n\t\t\t\t\tBroadcastAutoCenter(pkmn0, pkmn0OldPos, pkmn1, pkmn1OldPos);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tTurnNumber++;\n\t\t\tBroadcastTurnBegan();\n\t\t\tforeach (PBETeam team in Teams)\n\t\t\t{\n\t\t\t\tbool old = team.MonFaintedThisTurn; // Fire events in a specific order\n\t\t\t\tteam.MonFaintedThisTurn = false;\n\t\t\t\tteam.MonFaintedLastTurn = old;\n\t\t\t}\n\t\t\tBattleState = PBEBattleState.WaitingForActions;\n\t\t\tforeach (PBETrainer trainer in Trainers.Where(t => t.NumConsciousPkmn > 0))\n\t\t\t{\n\t\t\t\tBroadcastActionsRequest(trainer);\n\t\t\t}\n\t\t}\n\t}\n\tprivate IEnumerable<PBEBattlePokemon> GetActingOrder(IEnumerable<PBEBattlePokemon> pokemon, bool ignoreItemsThatActivate)\n\t{\n\t\tvar evaluated = new List<(PBEBattlePokemon Pokemon, float Speed)>(); // TODO: Full Incense, Lagging Tail, Stall, Quick Claw\n\t\tforeach (PBEBattlePokemon pkmn in pokemon)\n\t\t{\n\t\t\tfloat speed = pkmn.Speed * GetStatChangeModifier(pkmn.SpeedChange, false);\n\n\t\t\tswitch (pkmn.Item)\n\t\t\t{\n\t\t\t\tcase PBEItem.ChoiceScarf:\n\t\t\t\t{\n\t\t\t\t\tspeed *= 1.5f;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEItem.MachoBrace:\n\t\t\t\tcase PBEItem.PowerAnklet:\n\t\t\t\tcase PBEItem.PowerBand:\n\t\t\t\tcase PBEItem.PowerBelt:\n\t\t\t\tcase PBEItem.PowerBracer:\n\t\t\t\tcase PBEItem.PowerLens:\n\t\t\t\tcase PBEItem.PowerWeight:\n\t\t\t\t{\n\t\t\t\t\tspeed *= 0.5f;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEItem.QuickPowder:\n\t\t\t\t{\n\t\t\t\t\tif (pkmn.OriginalSpecies == PBESpecies.Ditto && !pkmn.Status2.HasFlag(PBEStatus2.Transformed))\n\t\t\t\t\t{\n\t\t\t\t\t\tspeed *= 2.0f;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (ShouldDoWeatherEffects())\n\t\t\t{\n\t\t\t\tif (Weather == PBEWeather.HarshSunlight && pkmn.Ability == PBEAbility.Chlorophyll)\n\t\t\t\t{\n\t\t\t\t\tspeed *= 2.0f;\n\t\t\t\t}\n\t\t\t\telse if (Weather == PBEWeather.Rain && pkmn.Ability == PBEAbility.SwiftSwim)\n\t\t\t\t{\n\t\t\t\t\tspeed *= 2.0f;\n\t\t\t\t}\n\t\t\t\telse if (Weather == PBEWeather.Sandstorm && pkmn.Ability == PBEAbility.SandRush)\n\t\t\t\t{\n\t\t\t\t\tspeed *= 2.0f;\n\t\t\t\t}\n\t\t\t}\n\t\t\tswitch (pkmn.Ability)\n\t\t\t{\n\t\t\t\tcase PBEAbility.QuickFeet:\n\t\t\t\t{\n\t\t\t\t\tif (pkmn.Status1 != PBEStatus1.None)\n\t\t\t\t\t{\n\t\t\t\t\t\tspeed *= 1.5f;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEAbility.SlowStart:\n\t\t\t\t{\n\t\t\t\t\tif (pkmn.SlowStart_HinderTurnsLeft > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tspeed *= 0.5f;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (pkmn.Ability != PBEAbility.QuickFeet && pkmn.Status1 == PBEStatus1.Paralyzed)\n\t\t\t{\n\t\t\t\tspeed *= 0.25f;\n\t\t\t}\n\t\t\tif (pkmn.Team.TeamStatus.HasFlag(PBETeamStatus.Tailwind))\n\t\t\t{\n\t\t\t\tspeed *= 2.0f;\n\t\t\t}\n\n\t\t\t(PBEBattlePokemon Pokemon, float Speed) tup = (pkmn, speed);\n\t\t\tif (evaluated.Count == 0)\n\t\t\t{\n\t\t\t\tevaluated.Add(tup);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tint pkmnTiedWith = evaluated.FindIndex(t => t.Speed == speed);\n\t\t\t\tif (pkmnTiedWith != -1) // Speed tie - randomly go before or after the Pokémon it tied with\n\t\t\t\t{\n\t\t\t\t\tif (_rand.RandomBool())\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmnTiedWith == evaluated.Count - 1)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tevaluated.Add(tup);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tevaluated.Insert(pkmnTiedWith + 1, tup);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tevaluated.Insert(pkmnTiedWith, tup);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tint pkmnToGoBefore = evaluated.FindIndex(t => BattleStatus.HasFlag(PBEBattleStatus.TrickRoom) ? t.Speed > speed : t.Speed < speed);\n\t\t\t\t\tif (pkmnToGoBefore == -1)\n\t\t\t\t\t{\n\t\t\t\t\t\tevaluated.Add(tup);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tevaluated.Insert(pkmnToGoBefore, tup);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn evaluated.Select(t => t.Pokemon);\n\t}\n\tprivate void DetermineTurnOrder()\n\t{\n\t\tstatic int GetMovePrio(PBEBattlePokemon p)\n\t\t{\n\t\t\tIPBEMoveData mData = PBEDataProvider.Instance.GetMoveData(p.TurnAction!.FightMove);\n\t\t\tint priority = mData.Priority;\n\t\t\tif (p.Ability == PBEAbility.Prankster && mData.Category == PBEMoveCategory.Status)\n\t\t\t{\n\t\t\t\tpriority++;\n\t\t\t}\n\t\t\treturn priority;\n\t\t}\n\n\t\t_turnOrder.Clear();\n\t\t//const int PursuitPriority = +7;\n\t\tconst int SwitchRotatePriority = +6;\n\t\tconst int WildFleePriority = -7;\n\t\tList<PBEBattlePokemon> pkmnUsingItem = ActiveBattlers.FindAll(p => p.TurnAction?.Decision == PBETurnDecision.Item);\n\t\tList<PBEBattlePokemon> pkmnSwitchingOut = ActiveBattlers.FindAll(p => p.TurnAction?.Decision == PBETurnDecision.SwitchOut);\n\t\tList<PBEBattlePokemon> pkmnFighting = ActiveBattlers.FindAll(p => p.TurnAction?.Decision == PBETurnDecision.Fight);\n\t\tList<PBEBattlePokemon> wildFleeing = ActiveBattlers.FindAll(p => p.TurnAction?.Decision == PBETurnDecision.WildFlee);\n\t\t// Item use happens first:\n\t\t_turnOrder.AddRange(GetActingOrder(pkmnUsingItem, true));\n\t\t// Get move/switch/rotate/wildflee priority sorted\n\t\tIOrderedEnumerable<IGrouping<int, PBEBattlePokemon>> prios =\n\t\t\t\tpkmnSwitchingOut.Select(p => (p, SwitchRotatePriority))\n\t\t\t\t.Concat(pkmnFighting.Select(p => (p, GetMovePrio(p)))) // Get move priority\n                .Concat(wildFleeing.Select(p => (p, WildFleePriority)))\n\t\t\t\t.GroupBy(t => t.Item2, t => t.p)\n\t\t\t\t.OrderByDescending(t => t.Key);\n\t\tforeach (IGrouping<int, PBEBattlePokemon> bracket in prios)\n\t\t{\n\t\t\tbool ignoreItemsThatActivate = bracket.Key == SwitchRotatePriority || bracket.Key == WildFleePriority;\n\t\t\t_turnOrder.AddRange(GetActingOrder(bracket, ignoreItemsThatActivate));\n\t\t}\n\t}\n\tprivate void RunActionsInOrder()\n\t{\n\t\tforeach (PBEBattlePokemon pkmn in _turnOrder.ToArray()) // Copy the list so a faint or ejection does not cause a collection modified exception\n\t\t{\n\t\t\tif (BattleResult is not null) // Do not broadcast battle result by calling EndCheck() in here; do it in TurnEnded()\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse if (ActiveBattlers.Contains(pkmn))\n\t\t\t{\n\t\t\t\tswitch (pkmn.TurnAction!.Decision)\n\t\t\t\t{\n\t\t\t\t\tcase PBETurnDecision.Fight:\n\t\t\t\t\t{\n\t\t\t\t\t\tUseMove(pkmn, pkmn.TurnAction.FightMove, pkmn.TurnAction.FightTargets);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBETurnDecision.Item:\n\t\t\t\t\t{\n\t\t\t\t\t\tUseItem(pkmn, pkmn.TurnAction.UseItem);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBETurnDecision.SwitchOut:\n\t\t\t\t\t{\n\t\t\t\t\t\tSwitchTwoPokemon(pkmn, pkmn.Trainer.GetPokemon(pkmn.TurnAction.SwitchPokemonId));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBETurnDecision.WildFlee:\n\t\t\t\t\t{\n\t\t\t\t\t\tWildFleeCheck(pkmn);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(pkmn.TurnAction.Decision));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tprivate void TurnEnded()\n\t{\n\t\tif (EndCheck())\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\t// Verified: Effects before LightScreen/LuckyChant/Reflect/Safeguard/TrickRoom\n\t\tDoTurnEndedEffects();\n\n\t\tif (EndCheck())\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\t// Verified: LightScreen/LuckyChant/Reflect/Safeguard/TrickRoom are removed in the order they were added\n\t\tforeach (PBETeam team in Teams)\n\t\t{\n\t\t\tif (team.TeamStatus.HasFlag(PBETeamStatus.LightScreen))\n\t\t\t{\n\t\t\t\tteam.LightScreenCount--;\n\t\t\t\tif (team.LightScreenCount == 0)\n\t\t\t\t{\n\t\t\t\t\tBroadcastTeamStatus(team, PBETeamStatus.LightScreen, PBETeamStatusAction.Ended);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (team.TeamStatus.HasFlag(PBETeamStatus.LuckyChant))\n\t\t\t{\n\t\t\t\tteam.LuckyChantCount--;\n\t\t\t\tif (team.LuckyChantCount == 0)\n\t\t\t\t{\n\t\t\t\t\tBroadcastTeamStatus(team, PBETeamStatus.LuckyChant, PBETeamStatusAction.Ended);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (team.TeamStatus.HasFlag(PBETeamStatus.Reflect))\n\t\t\t{\n\t\t\t\tteam.ReflectCount--;\n\t\t\t\tif (team.ReflectCount == 0)\n\t\t\t\t{\n\t\t\t\t\tBroadcastTeamStatus(team, PBETeamStatus.Reflect, PBETeamStatusAction.Ended);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (team.TeamStatus.HasFlag(PBETeamStatus.Safeguard))\n\t\t\t{\n\t\t\t\tteam.SafeguardCount--;\n\t\t\t\tif (team.SafeguardCount == 0)\n\t\t\t\t{\n\t\t\t\t\tBroadcastTeamStatus(team, PBETeamStatus.Safeguard, PBETeamStatusAction.Ended);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (team.TeamStatus.HasFlag(PBETeamStatus.Tailwind))\n\t\t\t{\n\t\t\t\tteam.TailwindCount--;\n\t\t\t\tif (team.TailwindCount == 0)\n\t\t\t\t{\n\t\t\t\t\tBroadcastTeamStatus(team, PBETeamStatus.Tailwind, PBETeamStatusAction.Ended);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// Trick Room\n\t\tif (BattleStatus.HasFlag(PBEBattleStatus.TrickRoom))\n\t\t{\n\t\t\tTrickRoomCount--;\n\t\t\tif (TrickRoomCount == 0)\n\t\t\t{\n\t\t\t\tBroadcastBattleStatus(PBEBattleStatus.TrickRoom, PBEBattleStatusAction.Ended);\n\t\t\t}\n\t\t}\n\n\t\tSwitchesOrActions();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Battle/BattleActions.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Data.Utils;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.IO;\nusing System.Linq;\n\nnamespace Kermalis.PokemonBattleEngine.Battle;\n\npublic sealed class PBETurnAction\n{\n\tpublic byte PokemonId { get; }\n\tpublic PBETurnDecision Decision { get; }\n\tpublic PBEMove FightMove { get; }\n\tpublic PBETurnTarget FightTargets { get; internal set; } // Internal set because of PBEMoveTarget.RandomFoeSurrounding (TODO: Shouldn't this happen at runtime?)\n\tpublic PBEItem UseItem { get; }\n\tpublic byte SwitchPokemonId { get; }\n\n\tinternal PBETurnAction(EndianBinaryReader r)\n\t{\n\t\tPokemonId = r.ReadByte();\n\t\tDecision = r.ReadEnum<PBETurnDecision>();\n\t\tswitch (Decision)\n\t\t{\n\t\t\tcase PBETurnDecision.Fight:\n\t\t\t{\n\t\t\t\tFightMove = r.ReadEnum<PBEMove>();\n\t\t\t\tFightTargets = r.ReadEnum<PBETurnTarget>();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBETurnDecision.Item:\n\t\t\t{\n\t\t\t\tUseItem = r.ReadEnum<PBEItem>();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBETurnDecision.SwitchOut:\n\t\t\t{\n\t\t\t\tSwitchPokemonId = r.ReadByte();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBETurnDecision.WildFlee: break;\n\t\t\tdefault: throw new InvalidDataException(nameof(Decision));\n\t\t}\n\t}\n\t// Fight\n\tpublic PBETurnAction(PBEBattlePokemon pokemon, PBEMove fightMove, PBETurnTarget fightTargets)\n\t\t: this(pokemon.Id, fightMove, fightTargets) { }\n\tpublic PBETurnAction(byte pokemonId, PBEMove fightMove, PBETurnTarget fightTargets)\n\t{\n\t\tPokemonId = pokemonId;\n\t\tDecision = PBETurnDecision.Fight;\n\t\tFightMove = fightMove;\n\t\tFightTargets = fightTargets;\n\t}\n\t// Item\n\tpublic PBETurnAction(PBEBattlePokemon pokemon, PBEItem item)\n\t\t: this(pokemon.Id, item) { }\n\tpublic PBETurnAction(byte pokemonId, PBEItem item)\n\t{\n\t\tPokemonId = pokemonId;\n\t\tDecision = PBETurnDecision.Item;\n\t\tUseItem = item;\n\t}\n\t// Switch\n\tpublic PBETurnAction(PBEBattlePokemon pokemon, PBEBattlePokemon switchPokemon)\n\t\t: this(pokemon.Id, switchPokemon.Id) { }\n\tpublic PBETurnAction(byte pokemonId, byte switchPokemonId)\n\t{\n\t\tPokemonId = pokemonId;\n\t\tDecision = PBETurnDecision.SwitchOut;\n\t\tSwitchPokemonId = switchPokemonId;\n\t}\n\t// Internal wild flee\n\tinternal PBETurnAction(PBEBattlePokemon pokemon)\n\t\t: this(pokemon.Id) { }\n\tinternal PBETurnAction(byte pokemonId)\n\t{\n\t\tPokemonId = pokemonId;\n\t\tDecision = PBETurnDecision.WildFlee;\n\t}\n\n\tinternal void ToBytes(EndianBinaryWriter w)\n\t{\n\t\tw.WriteByte(PokemonId);\n\t\tw.WriteEnum(Decision);\n\t\tswitch (Decision)\n\t\t{\n\t\t\tcase PBETurnDecision.Fight:\n\t\t\t{\n\t\t\t\tw.WriteEnum(FightMove);\n\t\t\t\tw.WriteEnum(FightTargets);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBETurnDecision.Item:\n\t\t\t{\n\t\t\t\tw.WriteEnum(UseItem);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBETurnDecision.SwitchOut:\n\t\t\t{\n\t\t\t\tw.WriteByte(SwitchPokemonId);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBETurnDecision.WildFlee: break;\n\t\t\tdefault: throw new InvalidDataException(nameof(Decision));\n\t\t}\n\t}\n}\npublic sealed class PBESwitchIn\n{\n\tpublic byte PokemonId { get; }\n\tpublic PBEFieldPosition Position { get; }\n\n\tinternal PBESwitchIn(EndianBinaryReader r)\n\t{\n\t\tPokemonId = r.ReadByte();\n\t\tPosition = r.ReadEnum<PBEFieldPosition>();\n\t}\n\tpublic PBESwitchIn(PBEBattlePokemon pokemon, PBEFieldPosition position)\n\t\t: this(pokemon.Id, position) { }\n\tpublic PBESwitchIn(byte pokemonId, PBEFieldPosition position)\n\t{\n\t\tPokemonId = pokemonId;\n\t\tPosition = position;\n\t}\n\n\tinternal void ToBytes(EndianBinaryWriter w)\n\t{\n\t\tw.WriteByte(PokemonId);\n\t\tw.WriteEnum(Position);\n\t}\n}\npublic sealed partial class PBEBattle\n{\n\tinternal static bool AreActionsValid(PBETrainer trainer, IReadOnlyCollection<PBETurnAction> actions, [NotNullWhen(false)] out string? invalidReason)\n\t{\n\t\tif (trainer.Battle._battleState != PBEBattleState.WaitingForActions)\n\t\t{\n\t\t\tthrow new InvalidOperationException($\"{nameof(BattleState)} must be {PBEBattleState.WaitingForActions} to validate actions.\");\n\t\t}\n\t\tif (trainer.ActionsRequired.Count == 0)\n\t\t{\n\t\t\tinvalidReason = \"Actions were already submitted\";\n\t\t\treturn false;\n\t\t}\n\t\tif (actions.Count != trainer.ActionsRequired.Count)\n\t\t{\n\t\t\tinvalidReason = $\"Invalid amount of actions submitted; required amount is {trainer.ActionsRequired.Count}\";\n\t\t\treturn false;\n\t\t}\n\n\t\tvar verified = new List<PBEBattlePokemon>(trainer.ActionsRequired.Count);\n\t\tvar standBy = new List<PBEBattlePokemon>(trainer.ActionsRequired.Count);\n\t\tvar items = new Dictionary<PBEItem, int>(trainer.ActionsRequired.Count);\n\t\tforeach (PBETurnAction action in actions)\n\t\t{\n\t\t\tif (!trainer.TryGetPokemon(action.PokemonId, out PBEBattlePokemon? pkmn))\n\t\t\t{\n\t\t\t\tinvalidReason = $\"Invalid Pokémon ID ({action.PokemonId})\";\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (!trainer.ActionsRequired.Contains(pkmn))\n\t\t\t{\n\t\t\t\tinvalidReason = $\"Pokémon {action.PokemonId} not looking for actions\";\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (verified.Contains(pkmn))\n\t\t\t{\n\t\t\t\tinvalidReason = $\"Pokémon {action.PokemonId} was multiple actions\";\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tswitch (action.Decision)\n\t\t\t{\n\t\t\t\tcase PBETurnDecision.Fight:\n\t\t\t\t{\n\t\t\t\t\tif (Array.IndexOf(pkmn.GetUsableMoves(), action.FightMove) == -1)\n\t\t\t\t\t{\n\t\t\t\t\t\tinvalidReason = $\"{action.FightMove} is not usable by Pokémon {action.PokemonId}\";\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tif (action.FightMove == pkmn.TempLockedMove && action.FightTargets != pkmn.TempLockedTargets)\n\t\t\t\t\t{\n\t\t\t\t\t\tinvalidReason = $\"Pokémon {action.PokemonId} must target {pkmn.TempLockedTargets}\";\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tif (!AreTargetsValid(pkmn, action.FightMove, action.FightTargets))\n\t\t\t\t\t{\n\t\t\t\t\t\tinvalidReason = $\"Invalid move targets for Pokémon {action.PokemonId}'s {action.FightMove}\";\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBETurnDecision.Item:\n\t\t\t\t{\n\t\t\t\t\tif (pkmn.TempLockedMove != PBEMove.None)\n\t\t\t\t\t{\n\t\t\t\t\t\tinvalidReason = $\"Pokémon {action.PokemonId} must use {pkmn.TempLockedMove}\";\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tif (!trainer.Inventory.TryGetValue(action.UseItem, out PBEBattleInventory.PBEBattleInventorySlot? slot))\n\t\t\t\t\t{\n\t\t\t\t\t\tinvalidReason = $\"Trainer \\\"{trainer.Name}\\\" does not have any {action.UseItem}\"; // Handles wild Pokémon\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tbool used = items.TryGetValue(action.UseItem, out int amtUsed);\n\t\t\t\t\tif (!used)\n\t\t\t\t\t{\n\t\t\t\t\t\tamtUsed = 0;\n\t\t\t\t\t}\n\t\t\t\t\tlong newAmt = slot.Quantity - amtUsed;\n\t\t\t\t\tif (newAmt <= 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tinvalidReason = $\"Tried to use too many {action.UseItem}\";\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tif (trainer.Battle.BattleType == PBEBattleType.Wild && trainer.Team.OpposingTeam.ActiveBattlers.Count > 1\n\t\t\t\t\t\t&& PBEDataUtils.AllBalls.Contains(action.UseItem))\n\t\t\t\t\t{\n\t\t\t\t\t\tinvalidReason = $\"Cannot throw a ball at multiple wild Pokémon\";\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tamtUsed++;\n\t\t\t\t\tif (used)\n\t\t\t\t\t{\n\t\t\t\t\t\titems[action.UseItem] = amtUsed;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\titems.Add(action.UseItem, amtUsed);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBETurnDecision.SwitchOut:\n\t\t\t\t{\n\t\t\t\t\tif (!pkmn.CanSwitchOut())\n\t\t\t\t\t{\n\t\t\t\t\t\tinvalidReason = $\"Pokémon {action.PokemonId} cannot switch out\";\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tif (!trainer.TryGetPokemon(action.SwitchPokemonId, out PBEBattlePokemon? switchPkmn))\n\t\t\t\t\t{\n\t\t\t\t\t\tinvalidReason = $\"Invalid switch Pokémon ID ({action.PokemonId})\";\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tif (switchPkmn.HP == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tinvalidReason = $\"Switch Pokémon {action.PokemonId} is fainted\";\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tif (switchPkmn.PBEIgnore)\n\t\t\t\t\t{\n\t\t\t\t\t\tinvalidReason = $\"Switch Pokémon {action.PokemonId} cannot battle\";\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tif (switchPkmn.FieldPosition != PBEFieldPosition.None)\n\t\t\t\t\t{\n\t\t\t\t\t\tinvalidReason = $\"Switch Pokémon {action.PokemonId} is already on the field\";\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tif (standBy.Contains(switchPkmn))\n\t\t\t\t\t{\n\t\t\t\t\t\tinvalidReason = $\"Switch Pokémon {action.PokemonId} was asked to be switched in multiple times\";\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tstandBy.Add(switchPkmn);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t{\n\t\t\t\t\tinvalidReason = $\"Invalid turn decision ({action.Decision})\";\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\tverified.Add(pkmn);\n\t\t}\n\t\tinvalidReason = null;\n\t\treturn true;\n\t}\n\tinternal static bool SelectActionsIfValid(PBETrainer trainer, IReadOnlyCollection<PBETurnAction> actions, [NotNullWhen(false)] out string? invalidReason)\n\t{\n\t\tif (!AreActionsValid(trainer, actions, out invalidReason))\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\ttrainer.ActionsRequired.Clear();\n\t\tforeach (PBETurnAction action in actions)\n\t\t{\n\t\t\tPBEBattlePokemon pkmn = trainer.GetPokemon(action.PokemonId);\n\t\t\tif (action.Decision == PBETurnDecision.Fight && pkmn.GetMoveTargets(action.FightMove) == PBEMoveTarget.RandomFoeSurrounding)\n\t\t\t{\n\t\t\t\tswitch (trainer.Battle.BattleFormat)\n\t\t\t\t{\n\t\t\t\t\tcase PBEBattleFormat.Single:\n\t\t\t\t\tcase PBEBattleFormat.Rotation:\n\t\t\t\t\t{\n\t\t\t\t\t\taction.FightTargets = PBETurnTarget.FoeCenter;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEBattleFormat.Double:\n\t\t\t\t\t{\n\t\t\t\t\t\taction.FightTargets = trainer.Battle._rand.RandomBool() ? PBETurnTarget.FoeLeft : PBETurnTarget.FoeRight;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEBattleFormat.Triple:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\taction.FightTargets = trainer.Battle._rand.RandomBool() ? PBETurnTarget.FoeCenter : PBETurnTarget.FoeRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tPBETeam oppTeam = trainer.Team.OpposingTeam;\n\t\t\t\t\t\t\tint r; // Keep randomly picking until a non-fainted foe is selected\n\t\t\t\t\t\troll:\n\t\t\t\t\t\t\tr = trainer.Battle._rand.RandomInt(0, 2);\n\t\t\t\t\t\t\tif (r == 0)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (oppTeam.IsSpotOccupied(PBEFieldPosition.Left))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\taction.FightTargets = PBETurnTarget.FoeLeft;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tgoto roll;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (r == 1)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (oppTeam.IsSpotOccupied(PBEFieldPosition.Center))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\taction.FightTargets = PBETurnTarget.FoeCenter;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tgoto roll;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (oppTeam.IsSpotOccupied(PBEFieldPosition.Right))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\taction.FightTargets = PBETurnTarget.FoeRight;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tgoto roll;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\taction.FightTargets = trainer.Battle._rand.RandomBool() ? PBETurnTarget.FoeLeft : PBETurnTarget.FoeCenter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tdefault: throw new InvalidDataException(nameof(trainer.Battle.BattleFormat));\n\t\t\t\t}\n\t\t\t}\n\t\t\tpkmn.TurnAction = action;\n\t\t}\n\t\tif (trainer.Battle.Trainers.All(t => t.ActionsRequired.Count == 0))\n\t\t{\n\t\t\ttrainer.Battle.BattleState = PBEBattleState.ReadyToRunTurn;\n\t\t}\n\t\treturn true;\n\t}\n\n\tinternal static bool AreSwitchesValid(PBETrainer trainer, IReadOnlyCollection<PBESwitchIn> switches, [NotNullWhen(false)] out string? invalidReason)\n\t{\n\t\tif (trainer.Battle._battleState != PBEBattleState.WaitingForSwitchIns)\n\t\t{\n\t\t\tthrow new InvalidOperationException($\"{nameof(BattleState)} must be {PBEBattleState.WaitingForSwitchIns} to validate switches.\");\n\t\t}\n\t\tif (trainer.SwitchInsRequired == 0)\n\t\t{\n\t\t\tinvalidReason = \"Switches were already submitted\";\n\t\t\treturn false;\n\t\t}\n\t\tif (switches.Count != trainer.SwitchInsRequired)\n\t\t{\n\t\t\tinvalidReason = $\"Invalid amount of switches submitted; required amount is {trainer.SwitchInsRequired}\";\n\t\t\treturn false;\n\t\t}\n\t\tvar verified = new List<PBEBattlePokemon>(trainer.SwitchInsRequired);\n\t\tforeach (PBESwitchIn s in switches)\n\t\t{\n\t\t\tif (s.Position == PBEFieldPosition.None || s.Position >= PBEFieldPosition.MAX || !trainer.OwnsSpot(s.Position))\n\t\t\t{\n\t\t\t\tinvalidReason = $\"Invalid position ({s.PokemonId})\";\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (!trainer.TryGetPokemon(s.PokemonId, out PBEBattlePokemon? pkmn))\n\t\t\t{\n\t\t\t\tinvalidReason = $\"Invalid Pokémon ID ({s.PokemonId})\";\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (pkmn.HP == 0)\n\t\t\t{\n\t\t\t\tinvalidReason = $\"Pokémon {s.PokemonId} is fainted\";\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (pkmn.PBEIgnore)\n\t\t\t{\n\t\t\t\tinvalidReason = $\"Pokémon {s.PokemonId} cannot battle\";\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (pkmn.FieldPosition != PBEFieldPosition.None)\n\t\t\t{\n\t\t\t\tinvalidReason = $\"Pokémon {s.PokemonId} is already on the field\";\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (verified.Contains(pkmn))\n\t\t\t{\n\t\t\t\tinvalidReason = $\"Pokémon {s.PokemonId} was asked to be switched in multiple times\";\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tverified.Add(pkmn);\n\t\t}\n\t\tinvalidReason = null;\n\t\treturn true;\n\t}\n\tinternal static bool SelectSwitchesIfValid(PBETrainer trainer, IReadOnlyCollection<PBESwitchIn> switches, [NotNullWhen(false)] out string? invalidReason)\n\t{\n\t\tif (!AreSwitchesValid(trainer, switches, out invalidReason))\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\ttrainer.SwitchInsRequired = 0;\n\t\tforeach (PBESwitchIn s in switches)\n\t\t{\n\t\t\ttrainer.SwitchInQueue.Add((trainer.GetPokemon(s.PokemonId), s.Position));\n\t\t}\n\t\tif (trainer.Battle.Trainers.All(t => t.SwitchInsRequired == 0))\n\t\t{\n\t\t\ttrainer.Battle.BattleState = PBEBattleState.ReadyToRunSwitches;\n\t\t}\n\t\treturn true;\n\t}\n\n\tinternal static bool IsFleeValid(PBETrainer trainer, [NotNullWhen(false)] out string? invalidReason)\n\t{\n\t\tif (trainer.Battle.BattleType != PBEBattleType.Wild)\n\t\t{\n\t\t\tthrow new InvalidOperationException($\"{nameof(BattleType)} must be {PBEBattleType.Wild} to flee.\");\n\t\t}\n\t\tswitch (trainer.Battle._battleState)\n\t\t{\n\t\t\tcase PBEBattleState.WaitingForActions:\n\t\t\t{\n\t\t\t\tif (trainer.ActionsRequired.Count == 0)\n\t\t\t\t{\n\t\t\t\t\tinvalidReason = \"Actions were already submitted\";\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tPBEBattlePokemon pkmn = trainer.ActiveBattlersOrdered.First();\n\t\t\t\tif (pkmn.TempLockedMove != PBEMove.None)\n\t\t\t\t{\n\t\t\t\t\tinvalidReason = $\"Pokémon {pkmn.Id} must use {pkmn.TempLockedMove}\";\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEBattleState.WaitingForSwitchIns:\n\t\t\t{\n\t\t\t\tif (trainer.SwitchInsRequired == 0)\n\t\t\t\t{\n\t\t\t\t\tinvalidReason = \"Switches were already submitted\";\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: throw new InvalidOperationException($\"{nameof(BattleState)} must be {PBEBattleState.WaitingForActions} or {PBEBattleState.WaitingForSwitchIns} to flee.\");\n\t\t}\n\t\tinvalidReason = null;\n\t\treturn true;\n\t}\n\tinternal static bool SelectFleeIfValid(PBETrainer trainer, [NotNullWhen(false)] out string? invalidReason)\n\t{\n\t\tif (!IsFleeValid(trainer, out invalidReason))\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\ttrainer.RequestedFlee = true;\n\t\tif (trainer.Battle._battleState == PBEBattleState.WaitingForActions)\n\t\t{\n\t\t\ttrainer.ActionsRequired.Clear();\n\t\t\tif (trainer.Battle.Trainers.All(t => t.ActionsRequired.Count == 0))\n\t\t\t{\n\t\t\t\ttrainer.Battle.BattleState = PBEBattleState.ReadyToRunTurn;\n\t\t\t}\n\t\t}\n\t\telse // WaitingForSwitches\n\t\t{\n\t\t\ttrainer.SwitchInsRequired = 0;\n\t\t\tif (trainer.Battle.Trainers.All(t => t.SwitchInsRequired == 0))\n\t\t\t{\n\t\t\t\ttrainer.Battle.BattleState = PBEBattleState.ReadyToRunSwitches;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n}\npublic sealed partial class PBETrainer\n{\n\tpublic bool AreActionsValid([NotNullWhen(false)] out string? invalidReason, params PBETurnAction[] actions)\n\t{\n\t\treturn PBEBattle.AreActionsValid(this, actions, out invalidReason);\n\t}\n\tpublic bool AreActionsValid(IReadOnlyCollection<PBETurnAction> actions, [NotNullWhen(false)] out string? invalidReason)\n\t{\n\t\treturn PBEBattle.AreActionsValid(this, actions, out invalidReason);\n\t}\n\tpublic bool SelectActionsIfValid([NotNullWhen(false)] out string? invalidReason, params PBETurnAction[] actions)\n\t{\n\t\treturn PBEBattle.SelectActionsIfValid(this, actions, out invalidReason);\n\t}\n\tpublic bool SelectActionsIfValid(IReadOnlyCollection<PBETurnAction> actions, [NotNullWhen(false)] out string? invalidReason)\n\t{\n\t\treturn PBEBattle.SelectActionsIfValid(this, actions, out invalidReason);\n\t}\n\n\tpublic bool AreSwitchesValid([NotNullWhen(false)] out string? invalidReason, params PBESwitchIn[] switches)\n\t{\n\t\treturn PBEBattle.AreSwitchesValid(this, switches, out invalidReason);\n\t}\n\tpublic bool AreSwitchesValid(IReadOnlyCollection<PBESwitchIn> switches, [NotNullWhen(false)] out string? invalidReason)\n\t{\n\t\treturn PBEBattle.AreSwitchesValid(this, switches, out invalidReason);\n\t}\n\tpublic bool SelectSwitchesIfValid([NotNullWhen(false)] out string? invalidReason, params PBESwitchIn[] switches)\n\t{\n\t\treturn PBEBattle.SelectSwitchesIfValid(this, switches, out invalidReason);\n\t}\n\tpublic bool SelectSwitchesIfValid(IReadOnlyCollection<PBESwitchIn> switches, [NotNullWhen(false)] out string? invalidReason)\n\t{\n\t\treturn PBEBattle.SelectSwitchesIfValid(this, switches, out invalidReason);\n\t}\n\n\tpublic bool IsFleeValid([NotNullWhen(false)] out string? invalidReason)\n\t{\n\t\treturn PBEBattle.IsFleeValid(this, out invalidReason);\n\t}\n\tpublic bool SelectFleeIfValid([NotNullWhen(false)] out string? invalidReason)\n\t{\n\t\treturn PBEBattle.SelectFleeIfValid(this, out invalidReason);\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Battle/BattleDamage.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data;\nusing System;\nusing System.Linq;\n\nnamespace Kermalis.PokemonBattleEngine.Battle;\n\npublic sealed partial class PBEBattle\n{\n\t/// <summary>Gets the influence a stat change has on a stat.</summary>\n\t/// <param name=\"change\">The stat change.</param>\n\t/// <param name=\"forMissing\">True if the stat is <see cref=\"PBEStat.Accuracy\"/> or <see cref=\"PBEStat.Evasion\"/>.</param>\n\tpublic static float GetStatChangeModifier(sbyte change, bool forMissing)\n\t{\n\t\tfloat baseVal = forMissing ? 3 : 2;\n\t\tfloat numerator = Math.Max(baseVal, baseVal + change);\n\t\tfloat denominator = Math.Max(baseVal, baseVal - change);\n\t\treturn numerator / denominator;\n\t}\n\n\t// Verified: Sturdy and Substitute only activate on damaging attacks (so draining HP or liquid ooze etc can bypass sturdy)\n\tprivate ushort DealDamage(PBEBattlePokemon culprit, PBEBattlePokemon victim, int hp, bool ignoreSubstitute = true, bool ignoreSturdy = true)\n\t{\n\t\tif (hp < 1)\n\t\t{\n\t\t\thp = 1;\n\t\t}\n\t\tif (!ignoreSubstitute && victim.Status2.HasFlag(PBEStatus2.Substitute))\n\t\t{\n\t\t\tushort oldSubHP = victim.SubstituteHP;\n\t\t\tvictim.SubstituteHP = (ushort)Math.Max(0, victim.SubstituteHP - hp);\n\t\t\tushort damageAmt = (ushort)(oldSubHP - victim.SubstituteHP);\n\t\t\tBroadcastStatus2(victim, culprit, PBEStatus2.Substitute, PBEStatusAction.Damage);\n\t\t\treturn damageAmt;\n\t\t}\n\t\tushort oldHP = victim.HP;\n\t\tfloat oldPercentage = victim.HPPercentage;\n\t\tvictim.HP = (ushort)Math.Max(0, victim.HP - hp);\n\t\tbool sturdyHappened = false, focusBandHappened = false, focusSashHappened = false;\n\t\tif (!ignoreSturdy && victim.HP == 0)\n\t\t{\n\t\t\t// TODO: Endure\n\t\t\tif (oldHP == victim.MaxHP && victim.Ability == PBEAbility.Sturdy && !culprit.HasCancellingAbility())\n\t\t\t{\n\t\t\t\tsturdyHappened = true;\n\t\t\t\tvictim.HP = 1;\n\t\t\t}\n\t\t\telse if (victim.Item == PBEItem.FocusBand && _rand.RandomBool(10, 100))\n\t\t\t{\n\t\t\t\tfocusBandHappened = true;\n\t\t\t\tvictim.HP = 1;\n\t\t\t}\n\t\t\telse if (oldHP == victim.MaxHP && victim.Item == PBEItem.FocusSash)\n\t\t\t{\n\t\t\t\tfocusSashHappened = true;\n\t\t\t\tvictim.HP = 1;\n\t\t\t}\n\t\t}\n\t\tvictim.UpdateHPPercentage();\n\t\tBroadcastPkmnHPChanged(victim, oldHP, oldPercentage);\n\t\tif (sturdyHappened)\n\t\t{\n\t\t\tBroadcastAbility(victim, culprit, PBEAbility.Sturdy, PBEAbilityAction.Damage);\n\t\t\tBroadcastEndure(victim);\n\t\t}\n\t\telse if (focusBandHappened)\n\t\t{\n\t\t\tBroadcastItem(victim, culprit, PBEItem.FocusBand, PBEItemAction.Damage);\n\t\t}\n\t\telse if (focusSashHappened)\n\t\t{\n\t\t\tBroadcastItem(victim, culprit, PBEItem.FocusSash, PBEItemAction.Consumed);\n\t\t}\n\t\treturn (ushort)(oldHP - victim.HP);\n\t}\n\t/// <summary>Restores HP to <paramref name=\"pkmn\"/> and broadcasts the HP changing if it changes.</summary>\n\t/// <param name=\"pkmn\">The Pokémon receiving the HP.</param>\n\t/// <param name=\"hp\">The amount of HP <paramref name=\"pkmn\"/> will try to gain.</param>\n\t/// <returns>The amount of HP restored.</returns>\n\tprivate ushort HealDamage(PBEBattlePokemon pkmn, int hp)\n\t{\n\t\tif (hp < 1)\n\t\t{\n\t\t\thp = 1;\n\t\t}\n\t\tushort oldHP = pkmn.HP;\n\t\tfloat oldPercentage = pkmn.HPPercentage;\n\t\tpkmn.HP = (ushort)Math.Min(pkmn.MaxHP, pkmn.HP + hp); // Always try to heal at least 1 HP\n\t\tushort healAmt = (ushort)(pkmn.HP - oldHP);\n\t\tif (healAmt > 0)\n\t\t{\n\t\t\tpkmn.UpdateHPPercentage();\n\t\t\tBroadcastPkmnHPChanged(pkmn, oldHP, oldPercentage);\n\t\t}\n\t\treturn healAmt;\n\t}\n\n\tprivate float CalculateBasePower(PBEBattlePokemon user, PBEBattlePokemon[] targets, IPBEMoveData mData, PBEType moveType)\n\t{\n\t\tfloat basePower;\n\n\t\t#region Get move's base power\n\t\tswitch (mData.Effect)\n\t\t{\n\t\t\tcase PBEMoveEffect.CrushGrip:\n\t\t\t{\n\t\t\t\tbasePower = Math.Max(1, targets.Select(t => (float)mData.Power * t.HP / t.MaxHP).Average());\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.Eruption:\n\t\t\t{\n\t\t\t\tbasePower = Math.Max(1, mData.Power * user.HP / user.MaxHP);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.Flail:\n\t\t\t{\n\t\t\t\tint val = 48 * user.HP / user.MaxHP;\n\t\t\t\tif (val < 2)\n\t\t\t\t{\n\t\t\t\t\tbasePower = 200;\n\t\t\t\t}\n\t\t\t\telse if (val < 4)\n\t\t\t\t{\n\t\t\t\t\tbasePower = 150;\n\t\t\t\t}\n\t\t\t\telse if (val < 8)\n\t\t\t\t{\n\t\t\t\t\tbasePower = 100;\n\t\t\t\t}\n\t\t\t\telse if (val < 16)\n\t\t\t\t{\n\t\t\t\t\tbasePower = 80;\n\t\t\t\t}\n\t\t\t\telse if (val < 32)\n\t\t\t\t{\n\t\t\t\t\tbasePower = 40;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tbasePower = 20;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.Frustration:\n\t\t\t{\n\t\t\t\tbasePower = Math.Max(1, (byte.MaxValue - user.Friendship) / 2.5f);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.GrassKnot:\n\t\t\t{\n\t\t\t\tbasePower = targets.Select(t =>\n\t\t\t\t{\n\t\t\t\t\tif (t.Weight >= 200.0f)\n\t\t\t\t\t{\n\t\t\t\t\t\treturn 120f;\n\t\t\t\t\t}\n\t\t\t\t\telse if (t.Weight >= 100.0f)\n\t\t\t\t\t{\n\t\t\t\t\t\treturn 100f;\n\t\t\t\t\t}\n\t\t\t\t\telse if (t.Weight >= 50.0f)\n\t\t\t\t\t{\n\t\t\t\t\t\treturn 80f;\n\t\t\t\t\t}\n\t\t\t\t\telse if (t.Weight >= 25.0f)\n\t\t\t\t\t{\n\t\t\t\t\t\treturn 60f;\n\t\t\t\t\t}\n\t\t\t\t\telse if (t.Weight >= 10.0f)\n\t\t\t\t\t{\n\t\t\t\t\t\treturn 40f;\n\t\t\t\t\t}\n\t\t\t\t\treturn 20f;\n\t\t\t\t}).Average();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.HeatCrash:\n\t\t\t{\n\t\t\t\tbasePower = targets.Select(t =>\n\t\t\t\t{\n\t\t\t\t\tfloat relative = user.Weight / t.Weight;\n\t\t\t\t\tif (relative < 2)\n\t\t\t\t\t{\n\t\t\t\t\t\treturn 40f;\n\t\t\t\t\t}\n\t\t\t\t\telse if (relative < 3)\n\t\t\t\t\t{\n\t\t\t\t\t\treturn 60f;\n\t\t\t\t\t}\n\t\t\t\t\telse if (relative < 4)\n\t\t\t\t\t{\n\t\t\t\t\t\treturn 80f;\n\t\t\t\t\t}\n\t\t\t\t\telse if (relative < 5)\n\t\t\t\t\t{\n\t\t\t\t\t\treturn 100f;\n\t\t\t\t\t}\n\t\t\t\t\treturn 120f;\n\t\t\t\t}).Average();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.HiddenPower:\n\t\t\t{\n\t\t\t\tbasePower = user.IndividualValues!.GetHiddenPowerBasePower(Settings);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.Magnitude:\n\t\t\t{\n\t\t\t\tint val = _rand.RandomInt(0, 99);\n\t\t\t\tbyte magnitude;\n\t\t\t\tif (val < 5) // Magnitude 4 - 5%\n\t\t\t\t{\n\t\t\t\t\tmagnitude = 4;\n\t\t\t\t\tbasePower = 10;\n\t\t\t\t}\n\t\t\t\telse if (val < 15) // Magnitude 5 - 10%\n\t\t\t\t{\n\t\t\t\t\tmagnitude = 5;\n\t\t\t\t\tbasePower = 30;\n\t\t\t\t}\n\t\t\t\telse if (val < 35) // Magnitude 6 - 20%\n\t\t\t\t{\n\t\t\t\t\tmagnitude = 6;\n\t\t\t\t\tbasePower = 50;\n\t\t\t\t}\n\t\t\t\telse if (val < 65) // Magnitude 7 - 30%\n\t\t\t\t{\n\t\t\t\t\tmagnitude = 7;\n\t\t\t\t\tbasePower = 70;\n\t\t\t\t}\n\t\t\t\telse if (val < 85) // Magnitude 8 - 20%\n\t\t\t\t{\n\t\t\t\t\tmagnitude = 8;\n\t\t\t\t\tbasePower = 90;\n\t\t\t\t}\n\t\t\t\telse if (val < 95) // Magnitude 9 - 10%\n\t\t\t\t{\n\t\t\t\t\tmagnitude = 9;\n\t\t\t\t\tbasePower = 110;\n\t\t\t\t}\n\t\t\t\telse // Magnitude 10 - 5%\n\t\t\t\t{\n\t\t\t\t\tmagnitude = 10;\n\t\t\t\t\tbasePower = 150;\n\t\t\t\t}\n\t\t\t\tBroadcastMagnitude(magnitude);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.Punishment:\n\t\t\t{\n\t\t\t\tbasePower = Math.Max(1, Math.Min(200, targets.Select(t => mData.Power + (20f * t.GetPositiveStatTotal())).Average()));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.Return:\n\t\t\t{\n\t\t\t\tbasePower = Math.Max(1, user.Friendship / 2.5f);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.StoredPower:\n\t\t\t{\n\t\t\t\tbasePower = mData.Power + (20 * user.GetPositiveStatTotal());\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\tbasePower = Math.Max(1, (int)mData.Power);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t#endregion\n\n\t\t// Technician goes before any other power boosts\n\t\tif (user.Ability == PBEAbility.Technician && basePower <= 60)\n\t\t{\n\t\t\tbasePower *= 1.5f;\n\t\t}\n\n\t\t#region Item-specific power boosts\n\t\tswitch (moveType)\n\t\t{\n\t\t\tcase PBEType.Bug:\n\t\t\t{\n\t\t\t\tswitch (user.Item)\n\t\t\t\t{\n\t\t\t\t\tcase PBEItem.InsectPlate:\n\t\t\t\t\tcase PBEItem.SilverPowder:\n\t\t\t\t\t{\n\t\t\t\t\t\tbasePower *= 1.2f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.BugGem:\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastItem(user, user, PBEItem.BugGem, PBEItemAction.Consumed);\n\t\t\t\t\t\tbasePower *= 1.5f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEType.Dark:\n\t\t\t{\n\t\t\t\tswitch (user.Item)\n\t\t\t\t{\n\t\t\t\t\tcase PBEItem.BlackGlasses:\n\t\t\t\t\tcase PBEItem.DreadPlate:\n\t\t\t\t\t{\n\t\t\t\t\t\tbasePower *= 1.2f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.DarkGem:\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastItem(user, user, PBEItem.DarkGem, PBEItemAction.Consumed);\n\t\t\t\t\t\tbasePower *= 1.5f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEType.Dragon:\n\t\t\t{\n\t\t\t\tswitch (user.Item)\n\t\t\t\t{\n\t\t\t\t\tcase PBEItem.AdamantOrb:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (user.OriginalSpecies == PBESpecies.Dialga)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbasePower *= 1.2f;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.DracoPlate:\n\t\t\t\t\tcase PBEItem.DragonFang:\n\t\t\t\t\t{\n\t\t\t\t\t\tbasePower *= 1.2f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.GriseousOrb:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (user.OriginalSpecies == PBESpecies.Giratina && user.RevertForm == PBEForm.Giratina_Origin)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbasePower *= 1.2f;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.LustrousOrb:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (user.OriginalSpecies == PBESpecies.Palkia)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbasePower *= 1.2f;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.DragonGem:\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastItem(user, user, PBEItem.DragonGem, PBEItemAction.Consumed);\n\t\t\t\t\t\tbasePower *= 1.5f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEType.Electric:\n\t\t\t{\n\t\t\t\tswitch (user.Item)\n\t\t\t\t{\n\t\t\t\t\tcase PBEItem.Magnet:\n\t\t\t\t\tcase PBEItem.ZapPlate:\n\t\t\t\t\t{\n\t\t\t\t\t\tbasePower *= 1.2f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.ElectricGem:\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastItem(user, user, PBEItem.ElectricGem, PBEItemAction.Consumed);\n\t\t\t\t\t\tbasePower *= 1.5f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEType.Fighting:\n\t\t\t{\n\t\t\t\tswitch (user.Item)\n\t\t\t\t{\n\t\t\t\t\tcase PBEItem.BlackBelt:\n\t\t\t\t\tcase PBEItem.FistPlate:\n\t\t\t\t\t{\n\t\t\t\t\t\tbasePower *= 1.2f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.FightingGem:\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastItem(user, user, PBEItem.FightingGem, PBEItemAction.Consumed);\n\t\t\t\t\t\tbasePower *= 1.5f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEType.Fire:\n\t\t\t{\n\t\t\t\tswitch (user.Item)\n\t\t\t\t{\n\t\t\t\t\tcase PBEItem.Charcoal:\n\t\t\t\t\tcase PBEItem.FlamePlate:\n\t\t\t\t\t{\n\t\t\t\t\t\tbasePower *= 1.2f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.FireGem:\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastItem(user, user, PBEItem.FireGem, PBEItemAction.Consumed);\n\t\t\t\t\t\tbasePower *= 1.5f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEType.Flying:\n\t\t\t{\n\t\t\t\tswitch (user.Item)\n\t\t\t\t{\n\t\t\t\t\tcase PBEItem.SharpBeak:\n\t\t\t\t\tcase PBEItem.SkyPlate:\n\t\t\t\t\t{\n\t\t\t\t\t\tbasePower *= 1.2f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.FlyingGem:\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastItem(user, user, PBEItem.FlyingGem, PBEItemAction.Consumed);\n\t\t\t\t\t\tbasePower *= 1.5f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEType.Ghost:\n\t\t\t{\n\t\t\t\tswitch (user.Item)\n\t\t\t\t{\n\t\t\t\t\tcase PBEItem.GriseousOrb:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (user.OriginalSpecies == PBESpecies.Giratina && user.RevertForm == PBEForm.Giratina_Origin)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbasePower *= 1.2f;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.SpellTag:\n\t\t\t\t\tcase PBEItem.SpookyPlate:\n\t\t\t\t\t{\n\t\t\t\t\t\tbasePower *= 1.2f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.GhostGem:\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastItem(user, user, PBEItem.GhostGem, PBEItemAction.Consumed);\n\t\t\t\t\t\tbasePower *= 1.5f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEType.Grass:\n\t\t\t{\n\t\t\t\tswitch (user.Item)\n\t\t\t\t{\n\t\t\t\t\tcase PBEItem.MeadowPlate:\n\t\t\t\t\tcase PBEItem.MiracleSeed:\n\t\t\t\t\tcase PBEItem.RoseIncense:\n\t\t\t\t\t{\n\t\t\t\t\t\tbasePower *= 1.2f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.GrassGem:\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastItem(user, user, PBEItem.GrassGem, PBEItemAction.Consumed);\n\t\t\t\t\t\tbasePower *= 1.5f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEType.Ground:\n\t\t\t{\n\t\t\t\tswitch (user.Item)\n\t\t\t\t{\n\t\t\t\t\tcase PBEItem.EarthPlate:\n\t\t\t\t\tcase PBEItem.SoftSand:\n\t\t\t\t\t{\n\t\t\t\t\t\tbasePower *= 1.2f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.GroundGem:\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastItem(user, user, PBEItem.GroundGem, PBEItemAction.Consumed);\n\t\t\t\t\t\tbasePower *= 1.5f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEType.Ice:\n\t\t\t{\n\t\t\t\tswitch (user.Item)\n\t\t\t\t{\n\t\t\t\t\tcase PBEItem.IciclePlate:\n\t\t\t\t\tcase PBEItem.NeverMeltIce:\n\t\t\t\t\t{\n\t\t\t\t\t\tbasePower *= 1.2f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.IceGem:\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastItem(user, user, PBEItem.IceGem, PBEItemAction.Consumed);\n\t\t\t\t\t\tbasePower *= 1.5f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEType.None:\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEType.Normal:\n\t\t\t{\n\t\t\t\tswitch (user.Item)\n\t\t\t\t{\n\t\t\t\t\tcase PBEItem.SilkScarf:\n\t\t\t\t\t{\n\t\t\t\t\t\tbasePower *= 1.2f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.NormalGem:\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastItem(user, user, PBEItem.NormalGem, PBEItemAction.Consumed);\n\t\t\t\t\t\tbasePower *= 1.5f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEType.Poison:\n\t\t\t{\n\t\t\t\tswitch (user.Item)\n\t\t\t\t{\n\t\t\t\t\tcase PBEItem.PoisonBarb:\n\t\t\t\t\tcase PBEItem.ToxicPlate:\n\t\t\t\t\t{\n\t\t\t\t\t\tbasePower *= 1.2f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.PoisonGem:\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastItem(user, user, PBEItem.PoisonGem, PBEItemAction.Consumed);\n\t\t\t\t\t\tbasePower *= 1.5f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEType.Psychic:\n\t\t\t{\n\t\t\t\tswitch (user.Item)\n\t\t\t\t{\n\t\t\t\t\tcase PBEItem.MindPlate:\n\t\t\t\t\tcase PBEItem.OddIncense:\n\t\t\t\t\tcase PBEItem.TwistedSpoon:\n\t\t\t\t\t{\n\t\t\t\t\t\tbasePower *= 1.2f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.PsychicGem:\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastItem(user, user, PBEItem.PsychicGem, PBEItemAction.Consumed);\n\t\t\t\t\t\tbasePower *= 1.5f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEType.Rock:\n\t\t\t{\n\t\t\t\tswitch (user.Item)\n\t\t\t\t{\n\t\t\t\t\tcase PBEItem.HardStone:\n\t\t\t\t\tcase PBEItem.RockIncense:\n\t\t\t\t\tcase PBEItem.StonePlate:\n\t\t\t\t\t{\n\t\t\t\t\t\tbasePower *= 1.2f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.RockGem:\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastItem(user, user, PBEItem.RockGem, PBEItemAction.Consumed);\n\t\t\t\t\t\tbasePower *= 1.5f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEType.Steel:\n\t\t\t{\n\t\t\t\tswitch (user.Item)\n\t\t\t\t{\n\t\t\t\t\tcase PBEItem.AdamantOrb:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (user.OriginalSpecies == PBESpecies.Dialga)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbasePower *= 1.2f;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.IronPlate:\n\t\t\t\t\tcase PBEItem.MetalCoat:\n\t\t\t\t\t{\n\t\t\t\t\t\tbasePower *= 1.2f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.SteelGem:\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastItem(user, user, PBEItem.SteelGem, PBEItemAction.Consumed);\n\t\t\t\t\t\tbasePower *= 1.5f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEType.Water:\n\t\t\t{\n\t\t\t\tswitch (user.Item)\n\t\t\t\t{\n\t\t\t\t\tcase PBEItem.LustrousOrb:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (user.OriginalSpecies == PBESpecies.Palkia)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbasePower *= 1.2f;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.MysticWater:\n\t\t\t\t\tcase PBEItem.SeaIncense:\n\t\t\t\t\tcase PBEItem.SplashPlate:\n\t\t\t\t\tcase PBEItem.WaveIncense:\n\t\t\t\t\t{\n\t\t\t\t\t\tbasePower *= 1.2f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.WaterGem:\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastItem(user, user, PBEItem.WaterGem, PBEItemAction.Consumed);\n\t\t\t\t\t\tbasePower *= 1.5f;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(moveType));\n\t\t}\n\t\t#endregion\n\n\t\t#region Move-specific power boosts\n\t\tswitch (mData.Effect)\n\t\t{\n\t\t\tcase PBEMoveEffect.Acrobatics:\n\t\t\t{\n\t\t\t\tif (user.Item == PBEItem.None)\n\t\t\t\t{\n\t\t\t\t\tbasePower *= 2.0f;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.Brine:\n\t\t\t{\n\t\t\t\tif (Array.FindIndex(targets, t => t.HP <= t.HP / 2) != -1)\n\t\t\t\t{\n\t\t\t\t\tbasePower *= 2.0f;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.Facade:\n\t\t\t{\n\t\t\t\tif (user.Status1 == PBEStatus1.Burned || user.Status1 == PBEStatus1.Paralyzed || user.Status1 == PBEStatus1.Poisoned || user.Status1 == PBEStatus1.BadlyPoisoned)\n\t\t\t\t{\n\t\t\t\t\tbasePower *= 2.0f;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.Hex:\n\t\t\t{\n\t\t\t\tif (Array.FindIndex(targets, t => t.Status1 != PBEStatus1.None) != -1)\n\t\t\t\t{\n\t\t\t\t\tbasePower *= 2.0f;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.Payback:\n\t\t\t{\n\t\t\t\tif (Array.FindIndex(targets, t => t.HasUsedMoveThisTurn) != -1)\n\t\t\t\t{\n\t\t\t\t\tbasePower *= 2.0f;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.Retaliate:\n\t\t\t{\n\t\t\t\tif (user.Team.MonFaintedLastTurn)\n\t\t\t\t{\n\t\t\t\t\tbasePower *= 2.0f;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.SmellingSalt:\n\t\t\t{\n\t\t\t\tif (Array.FindIndex(targets, t => t.Status1 == PBEStatus1.Paralyzed) != -1)\n\t\t\t\t{\n\t\t\t\t\tbasePower *= 2.0f;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.Venoshock:\n\t\t\t{\n\t\t\t\tif (Array.FindIndex(targets, t => t.Status1 == PBEStatus1.Poisoned || t.Status1 == PBEStatus1.BadlyPoisoned) != -1)\n\t\t\t\t{\n\t\t\t\t\tbasePower *= 2.0f;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.WakeUpSlap:\n\t\t\t{\n\t\t\t\tif (Array.FindIndex(targets, t => t.Status1 == PBEStatus1.Asleep) != -1)\n\t\t\t\t{\n\t\t\t\t\tbasePower *= 2.0f;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.WeatherBall:\n\t\t\t{\n\t\t\t\tif (ShouldDoWeatherEffects() && Weather != PBEWeather.None)\n\t\t\t\t{\n\t\t\t\t\tbasePower *= 2.0f;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t#endregion\n\n\t\t#region Weather-specific power boosts\n\t\tif (ShouldDoWeatherEffects())\n\t\t{\n\t\t\tswitch (Weather)\n\t\t\t{\n\t\t\t\tcase PBEWeather.HarshSunlight:\n\t\t\t\t{\n\t\t\t\t\tif (moveType == PBEType.Fire)\n\t\t\t\t\t{\n\t\t\t\t\t\tbasePower *= 1.5f;\n\t\t\t\t\t}\n\t\t\t\t\telse if (moveType == PBEType.Water)\n\t\t\t\t\t{\n\t\t\t\t\t\tbasePower *= 0.5f;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEWeather.Rain:\n\t\t\t\t{\n\t\t\t\t\tif (moveType == PBEType.Water)\n\t\t\t\t\t{\n\t\t\t\t\t\tbasePower *= 1.5f;\n\t\t\t\t\t}\n\t\t\t\t\telse if (moveType == PBEType.Fire)\n\t\t\t\t\t{\n\t\t\t\t\t\tbasePower *= 0.5f;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEWeather.Sandstorm:\n\t\t\t\t{\n\t\t\t\t\tif (user.Ability == PBEAbility.SandForce && (moveType == PBEType.Rock || moveType == PBEType.Ground || moveType == PBEType.Steel))\n\t\t\t\t\t{\n\t\t\t\t\t\tbasePower *= 1.3f;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t#endregion\n\n\t\t#region Other power boosts\n\t\tif (user.Status2.HasFlag(PBEStatus2.HelpingHand))\n\t\t{\n\t\t\tbasePower *= 1.5f;\n\t\t}\n\t\tif (user.Ability == PBEAbility.FlareBoost && mData.Category == PBEMoveCategory.Special && user.Status1 == PBEStatus1.Burned)\n\t\t{\n\t\t\tbasePower *= 1.5f;\n\t\t}\n\t\tif (user.Ability == PBEAbility.ToxicBoost && mData.Category == PBEMoveCategory.Physical && (user.Status1 == PBEStatus1.Poisoned || user.Status1 == PBEStatus1.BadlyPoisoned))\n\t\t{\n\t\t\tbasePower *= 1.5f;\n\t\t}\n\t\tif (user.Item == PBEItem.LifeOrb)\n\t\t{\n\t\t\tbasePower *= 1.3f;\n\t\t}\n\t\tif (user.Ability == PBEAbility.IronFist && mData.Flags.HasFlag(PBEMoveFlag.AffectedByIronFist))\n\t\t{\n\t\t\tbasePower *= 1.2f;\n\t\t}\n\t\tif (user.Ability == PBEAbility.Reckless && mData.Flags.HasFlag(PBEMoveFlag.AffectedByReckless))\n\t\t{\n\t\t\tbasePower *= 1.2f;\n\t\t}\n\t\tif (user.Item == PBEItem.MuscleBand && mData.Category == PBEMoveCategory.Physical)\n\t\t{\n\t\t\tbasePower *= 1.1f;\n\t\t}\n\t\tif (user.Item == PBEItem.WiseGlasses && mData.Category == PBEMoveCategory.Special)\n\t\t{\n\t\t\tbasePower *= 1.1f;\n\t\t}\n\t\t#endregion\n\n\t\treturn basePower;\n\t}\n\tprivate float CalculateDamageMultiplier(PBEBattlePokemon user, PBEBattlePokemon target, IPBEMoveData mData, PBEType moveType, PBEResult moveResult, bool criticalHit)\n\t{\n\t\tfloat damageMultiplier = 1;\n\t\tif (target.Status2.HasFlag(PBEStatus2.Airborne) && mData.Flags.HasFlag(PBEMoveFlag.DoubleDamageAirborne))\n\t\t{\n\t\t\tdamageMultiplier *= 2.0f;\n\t\t}\n\t\tif (target.Minimize_Used && mData.Flags.HasFlag(PBEMoveFlag.DoubleDamageMinimized))\n\t\t{\n\t\t\tdamageMultiplier *= 2.0f;\n\t\t}\n\t\tif (target.Status2.HasFlag(PBEStatus2.Underground) && mData.Flags.HasFlag(PBEMoveFlag.DoubleDamageUnderground))\n\t\t{\n\t\t\tdamageMultiplier *= 2.0f;\n\t\t}\n\t\tif (target.Status2.HasFlag(PBEStatus2.Underwater) && mData.Flags.HasFlag(PBEMoveFlag.DoubleDamageUnderwater))\n\t\t{\n\t\t\tdamageMultiplier *= 2.0f;\n\t\t}\n\n\t\tif (criticalHit)\n\t\t{\n\t\t\tdamageMultiplier *= Settings.CritMultiplier;\n\t\t\tif (user.Ability == PBEAbility.Sniper)\n\t\t\t{\n\t\t\t\tdamageMultiplier *= 1.5f;\n\t\t\t}\n\t\t}\n\t\telse if (user.Ability != PBEAbility.Infiltrator)\n\t\t{\n\t\t\tif ((target.Team.TeamStatus.HasFlag(PBETeamStatus.Reflect) && mData.Category == PBEMoveCategory.Physical)\n\t\t\t\t|| (target.Team.TeamStatus.HasFlag(PBETeamStatus.LightScreen) && mData.Category == PBEMoveCategory.Special))\n\t\t\t{\n\t\t\t\tif (target.Team.NumPkmnOnField == 1)\n\t\t\t\t{\n\t\t\t\t\tdamageMultiplier *= 0.5f;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tdamageMultiplier *= 0.66f;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tswitch (moveResult)\n\t\t{\n\t\t\tcase PBEResult.NotVeryEffective_Type:\n\t\t\t{\n\t\t\t\tif (user.Ability == PBEAbility.TintedLens)\n\t\t\t\t{\n\t\t\t\t\tdamageMultiplier *= 2.0f;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEResult.SuperEffective_Type:\n\t\t\t{\n\t\t\t\tif ((target.Ability == PBEAbility.Filter || target.Ability == PBEAbility.SolidRock) && !user.HasCancellingAbility())\n\t\t\t\t{\n\t\t\t\t\tdamageMultiplier *= 0.75f;\n\t\t\t\t}\n\t\t\t\tif (user.Item == PBEItem.ExpertBelt)\n\t\t\t\t{\n\t\t\t\t\tdamageMultiplier *= 1.2f;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (user.ReceivesSTAB(moveType))\n\t\t{\n\t\t\tif (user.Ability == PBEAbility.Adaptability)\n\t\t\t{\n\t\t\t\tdamageMultiplier *= 2.0f;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tdamageMultiplier *= 1.5f;\n\t\t\t}\n\t\t}\n\t\tif (mData.Category == PBEMoveCategory.Physical && user.Status1 == PBEStatus1.Burned && user.Ability != PBEAbility.Guts)\n\t\t{\n\t\t\tdamageMultiplier *= 0.5f;\n\t\t}\n\t\tif (moveType == PBEType.Fire && target.Ability == PBEAbility.Heatproof && !user.HasCancellingAbility())\n\t\t{\n\t\t\tdamageMultiplier *= 0.5f;\n\t\t}\n\n\t\treturn damageMultiplier;\n\t}\n\n\tprivate float CalculateAttack(PBEBattlePokemon user, PBEBattlePokemon target, PBEType moveType, float initialAttack)\n\t{\n\t\tfloat attack = initialAttack;\n\n\t\tif (user.Ability == PBEAbility.HugePower || user.Ability == PBEAbility.PurePower)\n\t\t{\n\t\t\tattack *= 2.0f;\n\t\t}\n\t\tif (user.Item == PBEItem.ThickClub && (user.OriginalSpecies == PBESpecies.Cubone || user.OriginalSpecies == PBESpecies.Marowak))\n\t\t{\n\t\t\tattack *= 2.0f;\n\t\t}\n\t\tif (user.Item == PBEItem.LightBall && user.OriginalSpecies == PBESpecies.Pikachu)\n\t\t{\n\t\t\tattack *= 2.0f;\n\t\t}\n\t\tif (moveType == PBEType.Bug && user.Ability == PBEAbility.Swarm && user.HP <= user.MaxHP / 3)\n\t\t{\n\t\t\tattack *= 1.5f;\n\t\t}\n\t\tif (moveType == PBEType.Fire && user.Ability == PBEAbility.Blaze && user.HP <= user.MaxHP / 3)\n\t\t{\n\t\t\tattack *= 1.5f;\n\t\t}\n\t\tif (moveType == PBEType.Grass && user.Ability == PBEAbility.Overgrow && user.HP <= user.MaxHP / 3)\n\t\t{\n\t\t\tattack *= 1.5f;\n\t\t}\n\t\tif (moveType == PBEType.Water && user.Ability == PBEAbility.Torrent && user.HP <= user.MaxHP / 3)\n\t\t{\n\t\t\tattack *= 1.5f;\n\t\t}\n\t\tif (user.Ability == PBEAbility.Hustle)\n\t\t{\n\t\t\tattack *= 1.5f;\n\t\t}\n\t\tif (user.Ability == PBEAbility.Guts && user.Status1 != PBEStatus1.None)\n\t\t{\n\t\t\tattack *= 1.5f;\n\t\t}\n\t\tif (user.Item == PBEItem.ChoiceBand)\n\t\t{\n\t\t\tattack *= 1.5f;\n\t\t}\n\t\tif (!user.HasCancellingAbility() && ShouldDoWeatherEffects() && Weather == PBEWeather.HarshSunlight && user.Team.ActiveBattlers.FindIndex(p => p.Ability == PBEAbility.FlowerGift) != -1)\n\t\t{\n\t\t\tattack *= 1.5f;\n\t\t}\n\t\tif ((moveType == PBEType.Fire || moveType == PBEType.Ice) && target.Ability == PBEAbility.ThickFat && !user.HasCancellingAbility())\n\t\t{\n\t\t\tattack *= 0.5f;\n\t\t}\n\t\tif (user.Ability == PBEAbility.Defeatist && user.HP <= user.MaxHP / 2)\n\t\t{\n\t\t\tattack *= 0.5f;\n\t\t}\n\t\tif (user.Ability == PBEAbility.SlowStart && user.SlowStart_HinderTurnsLeft > 0)\n\t\t{\n\t\t\tattack *= 0.5f;\n\t\t}\n\n\t\treturn attack;\n\t}\n\tprivate static float CalculateDefense(PBEBattlePokemon user, PBEBattlePokemon target, float initialDefense)\n\t{\n\t\tfloat defense = initialDefense;\n\n\t\tif (target.Item == PBEItem.MetalPowder && target.OriginalSpecies == PBESpecies.Ditto && !target.Status2.HasFlag(PBEStatus2.Transformed))\n\t\t{\n\t\t\tdefense *= 2.0f;\n\t\t}\n\t\tif (target.Ability == PBEAbility.MarvelScale && target.Status1 != PBEStatus1.None && !user.HasCancellingAbility())\n\t\t{\n\t\t\tdefense *= 1.5f;\n\t\t}\n\t\tif (target.Item == PBEItem.Eviolite && PBEDataProvider.Instance.HasEvolutions(target.OriginalSpecies, target.RevertForm))\n\t\t{\n\t\t\tdefense *= 1.5f;\n\t\t}\n\n\t\treturn defense;\n\t}\n\tprivate float CalculateSpAttack(PBEBattlePokemon user, PBEBattlePokemon target, PBEType moveType, float initialSpAttack)\n\t{\n\t\tfloat spAttack = initialSpAttack;\n\n\t\tif (user.Item == PBEItem.DeepSeaTooth && user.OriginalSpecies == PBESpecies.Clamperl)\n\t\t{\n\t\t\tspAttack *= 2.0f;\n\t\t}\n\t\tif (user.Item == PBEItem.LightBall && user.OriginalSpecies == PBESpecies.Pikachu)\n\t\t{\n\t\t\tspAttack *= 2.0f;\n\t\t}\n\t\tif (moveType == PBEType.Bug && user.Ability == PBEAbility.Swarm && user.HP <= user.MaxHP / 3)\n\t\t{\n\t\t\tspAttack *= 1.5f;\n\t\t}\n\t\tif (moveType == PBEType.Fire && user.Ability == PBEAbility.Blaze && user.HP <= user.MaxHP / 3)\n\t\t{\n\t\t\tspAttack *= 1.5f;\n\t\t}\n\t\tif (moveType == PBEType.Grass && user.Ability == PBEAbility.Overgrow && user.HP <= user.MaxHP / 3)\n\t\t{\n\t\t\tspAttack *= 1.5f;\n\t\t}\n\t\tif (moveType == PBEType.Water && user.Ability == PBEAbility.Torrent && user.HP <= user.MaxHP / 3)\n\t\t{\n\t\t\tspAttack *= 1.5f;\n\t\t}\n\t\tif (ShouldDoWeatherEffects() && Weather == PBEWeather.HarshSunlight && user.Ability == PBEAbility.SolarPower)\n\t\t{\n\t\t\tspAttack *= 1.5f;\n\t\t}\n\t\tif (user.Item == PBEItem.SoulDew && (user.OriginalSpecies == PBESpecies.Latias || user.OriginalSpecies == PBESpecies.Latios))\n\t\t{\n\t\t\tspAttack *= 1.5f;\n\t\t}\n\t\tif (user.Item == PBEItem.ChoiceSpecs)\n\t\t{\n\t\t\tspAttack *= 1.5f;\n\t\t}\n\t\tif ((user.Ability == PBEAbility.Minus || user.Ability == PBEAbility.Plus) && user.Team.ActiveBattlers.FindIndex(p => p != user && (p.Ability == PBEAbility.Minus || p.Ability == PBEAbility.Plus)) != -1)\n\t\t{\n\t\t\tspAttack *= 1.5f;\n\t\t}\n\t\tif ((moveType == PBEType.Fire || moveType == PBEType.Ice) && target.Ability == PBEAbility.ThickFat && !user.HasCancellingAbility())\n\t\t{\n\t\t\tspAttack *= 0.5f;\n\t\t}\n\t\tif (user.Ability == PBEAbility.Defeatist && user.HP <= user.MaxHP / 2)\n\t\t{\n\t\t\tspAttack *= 0.5f;\n\t\t}\n\n\t\treturn spAttack;\n\t}\n\tprivate float CalculateSpDefense(PBEBattlePokemon user, PBEBattlePokemon target, float initialSpDefense)\n\t{\n\t\tfloat spDefense = initialSpDefense;\n\n\t\tif (target.Item == PBEItem.DeepSeaScale && target.OriginalSpecies == PBESpecies.Clamperl)\n\t\t{\n\t\t\tspDefense *= 2.0f;\n\t\t}\n\t\tif (target.Item == PBEItem.SoulDew && (target.OriginalSpecies == PBESpecies.Latias || target.OriginalSpecies == PBESpecies.Latios))\n\t\t{\n\t\t\tspDefense *= 1.5f;\n\t\t}\n\t\tif (target.Item == PBEItem.Eviolite && PBEDataProvider.Instance.HasEvolutions(target.OriginalSpecies, target.RevertForm))\n\t\t{\n\t\t\tspDefense *= 1.5f;\n\t\t}\n\t\tif (ShouldDoWeatherEffects())\n\t\t{\n\t\t\tif (Weather == PBEWeather.Sandstorm && target.HasType(PBEType.Rock))\n\t\t\t{\n\t\t\t\tspDefense *= 1.5f;\n\t\t\t}\n\t\t\tif (!user.HasCancellingAbility() && Weather == PBEWeather.HarshSunlight && target.Team.ActiveBattlers.FindIndex(p => p.Ability == PBEAbility.FlowerGift) != -1)\n\t\t\t{\n\t\t\t\tspDefense *= 1.5f;\n\t\t\t}\n\t\t}\n\n\t\treturn spDefense;\n\t}\n\n\tprivate int CalculateDamage(PBEBattlePokemon user, float a, float d, float basePower)\n\t{\n\t\tfloat damage;\n\t\tdamage = (2 * user.Level / 5) + 2;\n\t\tdamage = damage * a * basePower / d;\n\t\tdamage /= 50;\n\t\tdamage += 2;\n\t\treturn (int)(damage * ((100f - _rand.RandomInt(0, 15)) / 100));\n\t}\n\tprivate int CalculateConfusionDamage(PBEBattlePokemon pkmn)\n\t{\n\t\t// Verified: Unaware has no effect on confusion damage\n\t\tfloat m = GetStatChangeModifier(pkmn.AttackChange, false);\n\t\tfloat a = CalculateAttack(pkmn, pkmn, PBEType.None, pkmn.Attack * m);\n\t\tm = GetStatChangeModifier(pkmn.DefenseChange, false);\n\t\tfloat d = CalculateDefense(pkmn, pkmn, pkmn.Defense * m);\n\t\treturn CalculateDamage(pkmn, a, d, 40);\n\t}\n\tprivate int CalculateDamage(PBEBattlePokemon user, PBEBattlePokemon target, IPBEMoveData mData, PBEType moveType, float basePower, bool criticalHit)\n\t{\n\t\tPBEBattlePokemon aPkmn;\n\t\tPBEMoveCategory aCat = mData.Category, dCat;\n\t\tswitch (mData.Effect)\n\t\t{\n\t\t\tcase PBEMoveEffect.FoulPlay:\n\t\t\t{\n\t\t\t\taPkmn = target;\n\t\t\t\tdCat = aCat;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.Psyshock:\n\t\t\t{\n\t\t\t\taPkmn = user;\n\t\t\t\tdCat = PBEMoveCategory.Physical;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\taPkmn = user;\n\t\t\t\tdCat = aCat;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tbool ignoreA = user != target && target.Ability == PBEAbility.Unaware && !user.HasCancellingAbility();\n\t\tbool ignoreD = user != target && (mData.Effect == PBEMoveEffect.ChipAway || user.Ability == PBEAbility.Unaware);\n\t\tfloat a, d;\n\t\tif (aCat == PBEMoveCategory.Physical)\n\t\t{\n\t\t\tfloat m = ignoreA ? 1 : GetStatChangeModifier(criticalHit ? Math.Max((sbyte)0, aPkmn.AttackChange) : aPkmn.AttackChange, false);\n\t\t\ta = CalculateAttack(user, target, moveType, aPkmn.Attack * m);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfloat m = ignoreA ? 1 : GetStatChangeModifier(criticalHit ? Math.Max((sbyte)0, aPkmn.SpAttackChange) : aPkmn.SpAttackChange, false);\n\t\t\ta = CalculateSpAttack(user, target, moveType, aPkmn.SpAttack * m);\n\t\t}\n\t\tif (dCat == PBEMoveCategory.Physical)\n\t\t{\n\t\t\tfloat m = ignoreD ? 1 : GetStatChangeModifier(criticalHit ? Math.Min((sbyte)0, target.DefenseChange) : target.DefenseChange, false);\n\t\t\td = CalculateDefense(user, target, target.Defense * m);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfloat m = ignoreD ? 1 : GetStatChangeModifier(criticalHit ? Math.Min((sbyte)0, target.SpDefenseChange) : target.SpDefenseChange, false);\n\t\t\td = CalculateSpDefense(user, target, target.SpDefense * m);\n\t\t}\n\n\t\treturn CalculateDamage(user, a, d, basePower);\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Battle/BattleEffects.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Data.Utils;\nusing Kermalis.PokemonBattleEngine.Packets;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\n\nnamespace Kermalis.PokemonBattleEngine.Battle;\n\npublic sealed partial class PBEBattle\n{\n\tprivate bool _calledFromOtherMove = false;\n\n\tprivate void DoSwitchInEffects(IEnumerable<PBEBattlePokemon> battlers, PBEBattlePokemon? forcedInBy = null)\n\t{\n\t\t// Set EXP Seens first\n\t\tforeach (PBEBattlePokemon pkmn in battlers)\n\t\t{\n\t\t\tPBETeam opTeam = pkmn.Team.OpposingTeam;\n\t\t\tforeach (PBEBattlePokemon op in opTeam.ActiveBattlers)\n\t\t\t{\n\t\t\t\top.AddEXPPokemon(pkmn);\n\t\t\t\tpkmn.AddEXPPokemon(op);\n\t\t\t}\n\t\t}\n\n\t\tIEnumerable<PBEBattlePokemon> order = GetActingOrder(battlers, true);\n\n\t\tforeach (PBEBattlePokemon pkmn in order)\n\t\t{\n\t\t\tbool grounded = pkmn.IsGrounded(forcedInBy) == PBEResult.Success;\n\t\t\t// Verified: (Spikes/StealthRock/ToxicSpikes in the order they were applied) before ability\n\t\t\tif (grounded && pkmn.Team.TeamStatus.HasFlag(PBETeamStatus.Spikes))\n\t\t\t{\n\t\t\t\tBroadcastTeamStatusDamage(pkmn.Team, PBETeamStatus.Spikes, pkmn);\n\t\t\t\tDealDamage(pkmn, pkmn, (int)(pkmn.MaxHP / (10.0 - (2 * pkmn.Team.SpikeCount))));\n\t\t\t\tif (FaintCheck(pkmn))\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tLowHPBerryCheck(pkmn, forcedToEatBy: forcedInBy);\n\t\t\t}\n\t\t\tif (pkmn.Team.TeamStatus.HasFlag(PBETeamStatus.StealthRock))\n\t\t\t{\n\t\t\t\tBroadcastTeamStatusDamage(pkmn.Team, PBETeamStatus.StealthRock, pkmn);\n\t\t\t\tDealDamage(pkmn, pkmn, (int)(pkmn.MaxHP * PBETypeEffectiveness.GetStealthRockMultiplier(pkmn.Type1, pkmn.Type2)));\n\t\t\t\tif (FaintCheck(pkmn))\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tLowHPBerryCheck(pkmn, forcedToEatBy: forcedInBy);\n\t\t\t}\n\t\t\tif (grounded && pkmn.Team.TeamStatus.HasFlag(PBETeamStatus.ToxicSpikes))\n\t\t\t{\n\t\t\t\tif (pkmn.HasType(PBEType.Poison))\n\t\t\t\t{\n\t\t\t\t\tpkmn.Team.ToxicSpikeCount = 0;\n\t\t\t\t\tBroadcastTeamStatus(pkmn.Team, PBETeamStatus.ToxicSpikes, PBETeamStatusAction.Cleared);\n\t\t\t\t}\n\t\t\t\telse if (pkmn.IsPoisonPossible(forcedInBy, ignoreSubstitute: true) == PBEResult.Success)\n\t\t\t\t{\n\t\t\t\t\tif (pkmn.Team.ToxicSpikeCount == 1)\n\t\t\t\t\t{\n\t\t\t\t\t\tpkmn.Status1 = PBEStatus1.Poisoned;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tpkmn.Status1 = PBEStatus1.BadlyPoisoned;\n\t\t\t\t\t\tpkmn.Status1Counter = 1;\n\t\t\t\t\t}\n\t\t\t\t\tBroadcastStatus1(pkmn, pkmn, pkmn.Status1, PBEStatusAction.Added);\n\t\t\t\t\t// Immunity activates in ActivateAbility() below\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tActivateAbility(pkmn, true);\n\t\t}\n\n\t\t// Verified: Castform/Cherrim transformation goes last. Even if multiple weather abilities activate, they will not change until every ability has been activated\n\t\tCastformCherrimCheck(order);\n\t}\n\tprivate void DoPostHitEffects(PBEBattlePokemon user, PBEBattlePokemon victim, IPBEMoveData mData, PBEType moveType)\n\t{\n\t\tIllusionBreak(victim, user); // Verified: Illusion before Rocky Helmet\n\t\tif (victim.HP > 0 && victim.Ability == PBEAbility.Justified && moveType == PBEType.Dark) // Verified: Justified before Rocky Helmet\n\t\t{\n\t\t\tBroadcastAbility(victim, user, PBEAbility.Justified, PBEAbilityAction.Damage);\n\t\t\tApplyStatChangeIfPossible(victim, victim, PBEStat.Attack, +1);\n\t\t}\n\t\tif (victim.HP > 0 && victim.Ability == PBEAbility.Rattled && (moveType == PBEType.Bug || moveType == PBEType.Dark || moveType == PBEType.Ghost)) // Verified: Rattled before Rocky Helmet\n\t\t{\n\t\t\tBroadcastAbility(victim, user, PBEAbility.Rattled, PBEAbilityAction.Damage);\n\t\t\tApplyStatChangeIfPossible(victim, victim, PBEStat.Speed, +1);\n\t\t}\n\t\tif (victim.HP > 0 && victim.Ability == PBEAbility.WeakArmor && mData.Category == PBEMoveCategory.Physical) // Verified: Weak Armor before Rocky Helmet\n\t\t{\n\t\t\tBroadcastAbility(victim, user, PBEAbility.WeakArmor, PBEAbilityAction.Damage);\n\t\t\tApplyStatChangeIfPossible(victim, victim, PBEStat.Defense, -1);\n\t\t\tApplyStatChangeIfPossible(victim, victim, PBEStat.Speed, +1);\n\t\t}\n\n\t\tif (mData.Flags.HasFlag(PBEMoveFlag.MakesContact))\n\t\t{\n\t\t\tif (user.HP > 0 && victim.Ability == PBEAbility.Mummy && user.Ability != PBEAbility.Multitype && user.Ability != PBEAbility.Mummy && user.Ability != PBEAbility.ZenMode)\n\t\t\t{\n\t\t\t\tBroadcastAbility(victim, user, PBEAbility.Mummy, PBEAbilityAction.Damage);\n\t\t\t\tSetAbility(victim, user, PBEAbility.Mummy);\n\t\t\t}\n\t\t\tif (user.HP > 0 && (victim.Ability == PBEAbility.IronBarbs || victim.Ability == PBEAbility.RoughSkin))\n\t\t\t{\n\t\t\t\tBroadcastAbility(victim, user, victim.Ability, PBEAbilityAction.Damage);\n\t\t\t\tDealDamage(victim, user, user.MaxHP / 8);\n\t\t\t\tif (!FaintCheck(user))\n\t\t\t\t{\n\t\t\t\t\tLowHPBerryCheck(user);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Verified: Cute Charm can activate when victim is about to faint\n\t\t\tif (user.HP > 0 && victim.Ability == PBEAbility.CuteCharm && user.IsAttractionPossible(victim) == PBEResult.Success && GetManipulableChance(victim, 30))\n\t\t\t{\n\t\t\t\tBroadcastAbility(victim, user, PBEAbility.CuteCharm, PBEAbilityAction.ChangedStatus);\n\t\t\t\tCauseInfatuation(user, victim);\n\t\t\t}\n\t\t\tif (user.HP > 0 && victim.Ability == PBEAbility.EffectSpore && user.Status1 == PBEStatus1.None)\n\t\t\t{\n\t\t\t\t// Commented in case the Rainbow affects Effect Spore\n\t\t\t\t//int randomNum = PBERandom.RandomInt(0, 99);\n\t\t\t\tif (GetManipulableChance(victim, 30))\n\t\t\t\t{\n\t\t\t\t\t// Spaghetti code taken from the assembly in generation 5 games\n\t\t\t\t\tPBEStatus1 status = PBEStatus1.None;\n\t\t\t\t\tint randomNum = _rand.RandomInt(0, 29);\n\t\t\t\t\tif (randomNum <= 20)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (randomNum > 10) // 11-20 (10%)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// TODO: Can it really not paralyze electric? I thought that's gen 6+\n\t\t\t\t\t\t\tif (!user.HasType(PBEType.Electric) && user.IsParalysisPossible(victim) == PBEResult.Success)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tstatus = PBEStatus1.Paralyzed;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse // 0-10 (11%)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (user.IsSleepPossible(victim) == PBEResult.Success)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tstatus = PBEStatus1.Asleep;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse // 21-29 (9%)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (user.IsPoisonPossible(victim) == PBEResult.Success)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tstatus = PBEStatus1.Poisoned;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (status != PBEStatus1.None)\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastAbility(victim, user, PBEAbility.EffectSpore, PBEAbilityAction.ChangedStatus);\n\t\t\t\t\t\tuser.Status1 = status;\n\t\t\t\t\t\tif (status == PBEStatus1.Asleep)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSetSleepTurns(user, Settings.SleepMinTurns, Settings.SleepMaxTurns);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tuser.Status1Counter = 0;\n\t\t\t\t\t\tBroadcastStatus1(user, victim, status, PBEStatusAction.Added);\n\t\t\t\t\t\tAntiStatusAbilityCheck(user);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (user.HP > 0 && victim.Ability == PBEAbility.FlameBody && user.IsBurnPossible(victim) == PBEResult.Success && GetManipulableChance(victim, 30))\n\t\t\t{\n\t\t\t\tBroadcastAbility(victim, user, PBEAbility.FlameBody, PBEAbilityAction.ChangedStatus);\n\t\t\t\tuser.Status1 = PBEStatus1.Burned;\n\t\t\t\tBroadcastStatus1(user, victim, PBEStatus1.Burned, PBEStatusAction.Added);\n\t\t\t\tAntiStatusAbilityCheck(user);\n\t\t\t}\n\t\t\tif (user.HP > 0 && victim.Ability == PBEAbility.PoisonPoint && user.IsPoisonPossible(victim) == PBEResult.Success && GetManipulableChance(victim, 30))\n\t\t\t{\n\t\t\t\tBroadcastAbility(victim, user, PBEAbility.PoisonPoint, PBEAbilityAction.ChangedStatus);\n\t\t\t\tuser.Status1 = PBEStatus1.Poisoned;\n\t\t\t\tBroadcastStatus1(user, victim, PBEStatus1.Poisoned, PBEStatusAction.Added);\n\t\t\t\tAntiStatusAbilityCheck(user);\n\t\t\t}\n\t\t\tif (user.HP > 0 && victim.Ability == PBEAbility.Static && user.IsParalysisPossible(victim) == PBEResult.Success && GetManipulableChance(victim, 30))\n\t\t\t{\n\t\t\t\tBroadcastAbility(victim, user, PBEAbility.Static, PBEAbilityAction.ChangedStatus);\n\t\t\t\tuser.Status1 = PBEStatus1.Paralyzed;\n\t\t\t\tBroadcastStatus1(user, victim, PBEStatus1.Paralyzed, PBEStatusAction.Added);\n\t\t\t\tAntiStatusAbilityCheck(user);\n\t\t\t}\n\t\t\t// Verified: Above abilities before Rocky Helmet\n\t\t\tif (user.HP > 0 && victim.Item == PBEItem.RockyHelmet)\n\t\t\t{\n\t\t\t\tBroadcastItem(victim, user, PBEItem.RockyHelmet, PBEItemAction.Damage);\n\t\t\t\tDealDamage(victim, user, user.MaxHP / 6);\n\t\t\t\tif (!FaintCheck(user))\n\t\t\t\t{\n\t\t\t\t\tLowHPBerryCheck(user);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tprivate void DoPostAttackedEffects(PBEBattlePokemon user, List<PBEAttackVictim> allies, List<PBEAttackVictim> foes, bool doLifeOrb,\n\t\tint? recoilDamage = null,\n\t\tPBEType colorChangeType = PBEType.None)\n\t{\n\t\t#region User\n\t\tif (user.HP > 0)\n\t\t{\n\t\t\t// Verified: Recoil before LifeOrb\n\t\t\t// Verified: Recoil calls berry check directly, and both can faint here\n\t\t\tif (recoilDamage is not null)\n\t\t\t{\n\t\t\t\tBroadcastRecoil(user);\n\t\t\t\tDealDamage(user, user, recoilDamage.Value);\n\t\t\t\tif (!FaintCheck(user))\n\t\t\t\t{\n\t\t\t\t\tLowHPBerryCheck(user);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (user.HP > 0 && doLifeOrb && user.Item == PBEItem.LifeOrb)\n\t\t\t{\n\t\t\t\tBroadcastItem(user, user, PBEItem.LifeOrb, PBEItemAction.Damage);\n\t\t\t\tDealDamage(user, user, user.MaxHP / 10);\n\t\t\t\tFaintCheck(user); // No berry check because we are holding Life Orb\n\t\t\t}\n\t\t}\n\t\t#endregion\n\n\t\t#region Victims\n\t\tvoid DoColorChange(IEnumerable<PBEBattlePokemon> order)\n\t\t{\n\t\t\tforeach (PBEBattlePokemon pkmn in order)\n\t\t\t{\n\t\t\t\tif (pkmn.Ability == PBEAbility.ColorChange && !pkmn.HasType(colorChangeType))\n\t\t\t\t{\n\t\t\t\t\tBroadcastAbility(pkmn, pkmn, PBEAbility.ColorChange, PBEAbilityAction.ChangedAppearance);\n\t\t\t\t\tBroadcastTypeChanged(pkmn, colorChangeType, PBEType.None);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tIEnumerable<PBEBattlePokemon> a = allies.Select(v => v.Pkmn).Where(p => p.HP > 0);\n\t\tIEnumerable<PBEBattlePokemon> f = foes.Select(v => v.Pkmn).Where(p => p.HP > 0);\n\t\t// Verified: ColorChange (foes, allies) before Berry\n\t\tif (colorChangeType != PBEType.None)\n\t\t{\n\t\t\tDoColorChange(f);\n\t\t\tDoColorChange(a);\n\t\t}\n\t\t// Verified: Berry (allies, foes) before AntiStatusAbility\n\t\tLowHPBerryCheck(a, forcedToEatBy: user);\n\t\tLowHPBerryCheck(f, forcedToEatBy: user);\n\t\t// Verified: AntiStatusAbility (allies, foes)\n\t\tAntiStatusAbilityCheck(a); // Heal a status that was given with the user's Mold Breaker\n\t\tAntiStatusAbilityCheck(f);\n\t\t#endregion\n\t}\n\tprivate void DoTurnEndedEffects()\n\t{\n\t\tIEnumerable<PBEBattlePokemon> order = GetActingOrder(ActiveBattlers, true);\n\t\t// Verified: Weather stops before doing damage\n\t\tif (Weather != PBEWeather.None && WeatherCounter > 0)\n\t\t{\n\t\t\tWeatherCounter--;\n\t\t\tif (WeatherCounter == 0)\n\t\t\t{\n\t\t\t\tPBEWeather w = Weather;\n\t\t\t\tWeather = PBEWeather.None;\n\t\t\t\tBroadcastWeather(w, PBEWeatherAction.Ended);\n\t\t\t\tCastformCherrimCheck(order);\n\t\t\t}\n\t\t}\n\t\t// Verified: Hailstorm/Sandstorm/IceBody/RainDish/SolarPower before all\n\t\tif (Weather != PBEWeather.None && ShouldDoWeatherEffects())\n\t\t{\n\t\t\tforeach (PBEBattlePokemon pkmn in order)\n\t\t\t{\n\t\t\t\tif (pkmn.HP == 0)\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tswitch (Weather)\n\t\t\t\t{\n\t\t\t\t\tcase PBEWeather.Hailstorm:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.Ability == PBEAbility.IceBody)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (pkmn.HP < pkmn.MaxHP)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tBroadcastAbility(pkmn, pkmn, pkmn.Ability, PBEAbilityAction.RestoredHP);\n\t\t\t\t\t\t\t\tHealDamage(pkmn, pkmn.MaxHP / Settings.IceBodyHealDenominator);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (!pkmn.HasType(PBEType.Ice)\n\t\t\t\t\t\t\t&& pkmn.Ability != PBEAbility.Overcoat\n\t\t\t\t\t\t\t&& pkmn.Ability != PBEAbility.SnowCloak)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tBroadcastWeatherDamage(PBEWeather.Hailstorm, pkmn);\n\t\t\t\t\t\t\tDealDamage(pkmn, pkmn, pkmn.MaxHP / Settings.HailDamageDenominator);\n\t\t\t\t\t\t\tif (!FaintCheck(pkmn))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tLowHPBerryCheck(pkmn);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEWeather.HarshSunlight:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.Ability == PBEAbility.SolarPower)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tBroadcastAbility(pkmn, pkmn, pkmn.Ability, PBEAbilityAction.Damage);\n\t\t\t\t\t\t\tDealDamage(pkmn, pkmn, pkmn.MaxHP / 8);\n\t\t\t\t\t\t\tif (!FaintCheck(pkmn))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tLowHPBerryCheck(pkmn);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEWeather.Rain:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.Ability == PBEAbility.RainDish && pkmn.HP < pkmn.MaxHP)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tBroadcastAbility(pkmn, pkmn, pkmn.Ability, PBEAbilityAction.RestoredHP);\n\t\t\t\t\t\t\tHealDamage(pkmn, pkmn.MaxHP / 16);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEWeather.Sandstorm:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!pkmn.HasType(PBEType.Rock)\n\t\t\t\t\t\t\t&& !pkmn.HasType(PBEType.Ground)\n\t\t\t\t\t\t\t&& !pkmn.HasType(PBEType.Steel)\n\t\t\t\t\t\t\t&& pkmn.Ability != PBEAbility.Overcoat\n\t\t\t\t\t\t\t&& pkmn.Ability != PBEAbility.SandForce\n\t\t\t\t\t\t\t&& pkmn.Ability != PBEAbility.SandRush\n\t\t\t\t\t\t\t&& pkmn.Ability != PBEAbility.SandVeil\n\t\t\t\t\t\t\t&& !pkmn.Status2.HasFlag(PBEStatus2.Underground)\n\t\t\t\t\t\t\t&& !pkmn.Status2.HasFlag(PBEStatus2.Underwater))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tBroadcastWeatherDamage(PBEWeather.Sandstorm, pkmn);\n\t\t\t\t\t\t\tDealDamage(pkmn, pkmn, pkmn.MaxHP / Settings.SandstormDamageDenominator);\n\t\t\t\t\t\t\tif (!FaintCheck(pkmn))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tLowHPBerryCheck(pkmn);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Verified: Healer/ShedSkin/BlackSludge/Leftovers before LeechSeed\n\t\tforeach (PBEBattlePokemon pkmn in order)\n\t\t{\n\t\t\tif (pkmn.HP == 0)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tswitch (pkmn.Ability)\n\t\t\t{\n\t\t\t\tcase PBEAbility.Healer:\n\t\t\t\t{\n\t\t\t\t\tforeach (PBEBattlePokemon ally in GetRuntimeSurrounding(pkmn, true, false))\n\t\t\t\t\t{\n\t\t\t\t\t\t// TODO: #265\n\t\t\t\t\t\tif (ally.Status1 != PBEStatus1.None && GetManipulableChance(pkmn, 30))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tBroadcastAbility(pkmn, ally, pkmn.Ability, PBEAbilityAction.ChangedStatus);\n\t\t\t\t\t\t\tPBEStatus1 status1 = ally.Status1;\n\t\t\t\t\t\t\tally.Status1 = PBEStatus1.None;\n\t\t\t\t\t\t\tally.Status1Counter = 0;\n\t\t\t\t\t\t\tally.SleepTurns = 0;\n\t\t\t\t\t\t\tBroadcastStatus1(ally, pkmn, status1, PBEStatusAction.Cleared);\n\t\t\t\t\t\t\tif (status1 == PBEStatus1.Asleep)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tCureNightmare(ally, pkmn);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEAbility.ShedSkin:\n\t\t\t\t{\n\t\t\t\t\tif (pkmn.Status1 != PBEStatus1.None && GetManipulableChance(pkmn, 30))\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastAbility(pkmn, pkmn, pkmn.Ability, PBEAbilityAction.ChangedStatus);\n\t\t\t\t\t\tPBEStatus1 status1 = pkmn.Status1;\n\t\t\t\t\t\tpkmn.Status1 = PBEStatus1.None;\n\t\t\t\t\t\tpkmn.Status1Counter = 0;\n\t\t\t\t\t\tpkmn.SleepTurns = 0;\n\t\t\t\t\t\tBroadcastStatus1(pkmn, pkmn, status1, PBEStatusAction.Cleared);\n\t\t\t\t\t\tif (status1 == PBEStatus1.Asleep)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tCureNightmare(pkmn, pkmn);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tswitch (pkmn.Item)\n\t\t\t{\n\t\t\t\tcase PBEItem.BlackSludge:\n\t\t\t\t{\n\t\t\t\t\tif (!pkmn.HasType(PBEType.Poison))\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastItem(pkmn, pkmn, pkmn.Item, PBEItemAction.Damage);\n\t\t\t\t\t\tDealDamage(pkmn, pkmn, pkmn.MaxHP / Settings.BlackSludgeDamageDenominator);\n\t\t\t\t\t\tFaintCheck(pkmn); // No need to call HealingBerryCheck() because if you are holding BlackSludge you are not holding a healing berry\n\t\t\t\t\t}\n\t\t\t\t\telse if (pkmn.HP < pkmn.MaxHP)\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastItem(pkmn, pkmn, pkmn.Item, PBEItemAction.RestoredHP);\n\t\t\t\t\t\tHealDamage(pkmn, pkmn.MaxHP / Settings.BlackSludgeHealDenominator);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEItem.Leftovers:\n\t\t\t\t{\n\t\t\t\t\tif (pkmn.HP < pkmn.MaxHP)\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastItem(pkmn, pkmn, pkmn.Item, PBEItemAction.RestoredHP);\n\t\t\t\t\t\tHealDamage(pkmn, pkmn.MaxHP / Settings.LeftoversHealDenominator);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Verified: LeechSeed before Status1/PoisonHeal\n\t\tforeach (PBEBattlePokemon pkmn in order)\n\t\t{\n\t\t\tif (pkmn.HP == 0 || !pkmn.Status2.HasFlag(PBEStatus2.LeechSeed))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!pkmn.SeededTeam!.TryGetPokemon(pkmn.SeededPosition, out PBEBattlePokemon? sucker))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tBroadcastStatus2(pkmn, sucker, PBEStatus2.LeechSeed, PBEStatusAction.Damage);\n\t\t\tint restoreAmt = DealDamage(sucker, pkmn, pkmn.MaxHP / Settings.LeechSeedDenominator);\n\n\t\t\t// In the games, the pkmn faints after taking damage (before liquid ooze/heal)\n\t\t\t// We cannot have it faint and then still broadcast ability like the games, similar to why we can't faint before Explosion\n\t\t\t// The faint order should be maintained, though, so the correct winner can be chosen\n\t\t\tApplyBigRoot(pkmn, ref restoreAmt);\n\t\t\tif (pkmn.Ability == PBEAbility.LiquidOoze)\n\t\t\t{\n\t\t\t\tBroadcastAbility(pkmn, sucker, PBEAbility.LiquidOoze, PBEAbilityAction.Damage);\n\t\t\t\tDealDamage(pkmn, sucker, restoreAmt);\n\t\t\t\tif (!FaintCheck(pkmn))\n\t\t\t\t{\n\t\t\t\t\tLowHPBerryCheck(pkmn);\n\t\t\t\t}\n\t\t\t\tif (!FaintCheck(sucker))\n\t\t\t\t{\n\t\t\t\t\tLowHPBerryCheck(sucker);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (!FaintCheck(pkmn))\n\t\t\t\t{\n\t\t\t\t\tLowHPBerryCheck(pkmn);\n\t\t\t\t}\n\t\t\t\tHealDamage(sucker, restoreAmt);\n\t\t\t}\n\t\t}\n\n\t\t// Verified: Status1/PoisonHeal before Curse\n\t\tforeach (PBEBattlePokemon pkmn in order)\n\t\t{\n\t\t\tif (pkmn.HP == 0)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tswitch (pkmn.Status1)\n\t\t\t{\n\t\t\t\tcase PBEStatus1.BadlyPoisoned:\n\t\t\t\tcase PBEStatus1.Poisoned:\n\t\t\t\t{\n\t\t\t\t\tif (pkmn.Ability != PBEAbility.PoisonHeal)\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastStatus1(pkmn, pkmn, pkmn.Status1, PBEStatusAction.Damage);\n\t\t\t\t\t\tint damage = pkmn.Status1 == PBEStatus1.BadlyPoisoned\n\t\t\t\t\t\t\t\t\t? pkmn.MaxHP * pkmn.Status1Counter / Settings.ToxicDamageDenominator\n\t\t\t\t\t\t\t\t\t: pkmn.MaxHP / Settings.PoisonDamageDenominator;\n\t\t\t\t\t\tDealDamage(pkmn, pkmn, damage);\n\t\t\t\t\t\tif (!FaintCheck(pkmn))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tLowHPBerryCheck(pkmn);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse if (pkmn.HP < pkmn.MaxHP)\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastAbility(pkmn, pkmn, PBEAbility.PoisonHeal, PBEAbilityAction.RestoredHP);\n\t\t\t\t\t\tHealDamage(pkmn, pkmn.MaxHP / 8);\n\t\t\t\t\t}\n\t\t\t\t\tif (pkmn.Status1 == PBEStatus1.BadlyPoisoned)\n\t\t\t\t\t{\n\t\t\t\t\t\tpkmn.Status1Counter++; // Counter still increments if PoisonHeal exists\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEStatus1.Burned:\n\t\t\t\t{\n\t\t\t\t\tBroadcastStatus1(pkmn, pkmn, pkmn.Status1, PBEStatusAction.Damage);\n\t\t\t\t\tint damage = pkmn.MaxHP / Settings.BurnDamageDenominator;\n\t\t\t\t\tif (pkmn.Ability == PBEAbility.Heatproof)\n\t\t\t\t\t{\n\t\t\t\t\t\tdamage /= 2;\n\t\t\t\t\t}\n\t\t\t\t\tDealDamage(pkmn, pkmn, damage);\n\t\t\t\t\tif (!FaintCheck(pkmn))\n\t\t\t\t\t{\n\t\t\t\t\t\tLowHPBerryCheck(pkmn);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Verified: Nightmare before Curse, not same loop\n\t\tforeach (PBEBattlePokemon pkmn in order)\n\t\t{\n\t\t\tif (pkmn.HP == 0 || !pkmn.Status2.HasFlag(PBEStatus2.Nightmare))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tBroadcastStatus2(pkmn, pkmn, PBEStatus2.Nightmare, PBEStatusAction.Damage);\n\t\t\tDealDamage(pkmn, pkmn, pkmn.MaxHP / 4);\n\t\t\tif (!FaintCheck(pkmn))\n\t\t\t{\n\t\t\t\tLowHPBerryCheck(pkmn);\n\t\t\t}\n\t\t}\n\n\t\t// Verified: Curse before MagnetRise\n\t\tforeach (PBEBattlePokemon pkmn in order)\n\t\t{\n\t\t\tif (pkmn.HP == 0 || !pkmn.Status2.HasFlag(PBEStatus2.Cursed))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tBroadcastStatus2(pkmn, pkmn, PBEStatus2.Cursed, PBEStatusAction.Damage);\n\t\t\tDealDamage(pkmn, pkmn, pkmn.MaxHP / Settings.CurseDenominator);\n\t\t\tif (!FaintCheck(pkmn))\n\t\t\t{\n\t\t\t\tLowHPBerryCheck(pkmn);\n\t\t\t}\n\t\t}\n\n\t\t// Verified: MagnetRise before Abilities/Orbs\n\t\tforeach (PBEBattlePokemon pkmn in order)\n\t\t{\n\t\t\tif (pkmn.HP == 0 || !pkmn.Status2.HasFlag(PBEStatus2.MagnetRise) || pkmn.MagnetRiseTurns == 0)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tpkmn.MagnetRiseTurns--;\n\t\t\tif (pkmn.MagnetRiseTurns == 0)\n\t\t\t{\n\t\t\t\tBroadcastStatus2(pkmn, pkmn, PBEStatus2.MagnetRise, PBEStatusAction.Ended);\n\t\t\t}\n\t\t}\n\n\t\t// Verified: BadDreams/Moody/SlowStart/SpeedBoost before Orbs, but activate together\n\t\tforeach (PBEBattlePokemon pkmn in order)\n\t\t{\n\t\t\tif (pkmn.HP == 0)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// Ability before Orb\n\t\t\tswitch (pkmn.Ability)\n\t\t\t{\n\t\t\t\tcase PBEAbility.BadDreams:\n\t\t\t\t{\n\t\t\t\t\tforeach (PBEBattlePokemon victim in GetRuntimeSurrounding(pkmn, false, true).Where(p => p.Status1 == PBEStatus1.Asleep))\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastAbility(pkmn, victim, PBEAbility.BadDreams, PBEAbilityAction.Damage);\n\t\t\t\t\t\tDealDamage(pkmn, victim, pkmn.MaxHP / 8);\n\t\t\t\t\t\tif (!FaintCheck(victim))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tLowHPBerryCheck(victim);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEAbility.Moody:\n\t\t\t\t{\n\t\t\t\t\tList<PBEStat> statsThatCanGoUp = PBEDataUtils.MoodyStats.FindAll(s => pkmn.GetStatChange(s) < Settings.MaxStatChange);\n\t\t\t\t\tPBEStat? upStat = statsThatCanGoUp.Count == 0 ? null : _rand.RandomElement(statsThatCanGoUp);\n\t\t\t\t\tList<PBEStat> statsThatCanGoDown = PBEDataUtils.MoodyStats.FindAll(s => pkmn.GetStatChange(s) > -Settings.MaxStatChange);\n\t\t\t\t\tif (upStat is not null)\n\t\t\t\t\t{\n\t\t\t\t\t\tstatsThatCanGoDown.Remove(upStat.Value);\n\t\t\t\t\t}\n\t\t\t\t\tPBEStat? downStat = statsThatCanGoDown.Count == 0 ? null : _rand.RandomElement(statsThatCanGoDown);\n\t\t\t\t\tif (upStat is not null || downStat is not null)\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastAbility(pkmn, pkmn, pkmn.Ability, PBEAbilityAction.Stats);\n\t\t\t\t\t\tif (upStat is not null)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tApplyStatChangeIfPossible(pkmn, pkmn, upStat.Value, +2);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (downStat is not null)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tApplyStatChangeIfPossible(pkmn, pkmn, downStat.Value, -1);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEAbility.SlowStart:\n\t\t\t\t{\n\t\t\t\t\tif (pkmn.SlowStart_HinderTurnsLeft > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tpkmn.SlowStart_HinderTurnsLeft--;\n\t\t\t\t\t\tif (pkmn.SlowStart_HinderTurnsLeft == 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tBroadcastAbility(pkmn, pkmn, PBEAbility.SlowStart, PBEAbilityAction.SlowStart_Ended);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEAbility.SpeedBoost:\n\t\t\t\t{\n\t\t\t\t\tif (pkmn.SpeedBoost_AbleToSpeedBoostThisTurn && pkmn.SpeedChange < Settings.MaxStatChange)\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastAbility(pkmn, pkmn, pkmn.Ability, PBEAbilityAction.Stats);\n\t\t\t\t\t\tApplyStatChangeIfPossible(pkmn, pkmn, PBEStat.Speed, +1);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Orb\n\t\t\tswitch (pkmn.Item)\n\t\t\t{\n\t\t\t\tcase PBEItem.FlameOrb:\n\t\t\t\t{\n\t\t\t\t\tif (pkmn.IsBurnPossible(null, ignoreSubstitute: true, ignoreSafeguard: true) == PBEResult.Success)\n\t\t\t\t\t{\n\t\t\t\t\t\tpkmn.Status1 = PBEStatus1.Burned;\n\t\t\t\t\t\tBroadcastItem(pkmn, pkmn, pkmn.Item, PBEItemAction.Announced);\n\t\t\t\t\t\tBroadcastStatus1(pkmn, pkmn, PBEStatus1.Burned, PBEStatusAction.Added);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEItem.ToxicOrb:\n\t\t\t\t{\n\t\t\t\t\tif (pkmn.IsPoisonPossible(null, ignoreSubstitute: true, ignoreSafeguard: true) == PBEResult.Success)\n\t\t\t\t\t{\n\t\t\t\t\t\tpkmn.Status1 = PBEStatus1.BadlyPoisoned;\n\t\t\t\t\t\tpkmn.Status1Counter = 1;\n\t\t\t\t\t\tBroadcastItem(pkmn, pkmn, pkmn.Item, PBEItemAction.Announced);\n\t\t\t\t\t\tBroadcastStatus1(pkmn, pkmn, PBEStatus1.BadlyPoisoned, PBEStatusAction.Added);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic bool ShouldDoWeatherEffects()\n\t{\n\t\t// If HP is needed to be above 0, use HPPercentage so clients can continue to use this\n\t\t// However, I see no instance of this getting called where an ActiveBattler has 0 hp\n\t\treturn ActiveBattlers.FindIndex(p => p.Ability == PBEAbility.AirLock || p.Ability == PBEAbility.CloudNine) == -1;\n\t}\n\tpublic bool WillLeafGuardActivate()\n\t{\n\t\treturn ShouldDoWeatherEffects() && Weather == PBEWeather.HarshSunlight;\n\t}\n\n\tprivate void FleeCheck()\n\t{\n\t\tforeach (PBETrainer trainer in Trainers)\n\t\t{\n\t\t\tif (!trainer.RequestedFlee)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (trainer.IsWild)\n\t\t\t{\n\t\t\t\tPBEBattlePokemon wildPkmn = trainer.ActiveBattlersOrdered.First();\n\t\t\t\twildPkmn.TurnAction = new PBETurnAction(wildPkmn); // Convert into a WildFlee turn action for the first Pokémon\n\t\t\t\ttrainer.RequestedFlee = false;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tPBEBattlePokemon? pkmn = trainer.ActiveBattlersOrdered.FirstOrDefault();\n\t\t\tif (pkmn is not null)\n\t\t\t{\n\t\t\t\t// Verified: RunAway before SmokeBall\n\t\t\t\tif (pkmn.Ability == PBEAbility.RunAway)\n\t\t\t\t{\n\t\t\t\t\tBroadcastAbility(pkmn, pkmn, PBEAbility.RunAway, PBEAbilityAction.Announced);\n\t\t\t\t\tSetEscaped(pkmn);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (pkmn.Item == PBEItem.SmokeBall)\n\t\t\t\t{\n\t\t\t\t\tBroadcastItem(pkmn, pkmn, PBEItem.SmokeBall, PBEItemAction.Announced);\n\t\t\t\t\tSetEscaped(pkmn);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tpkmn = trainer.Party[0]; // Use the first fainted Pokémon's speed if there's no active battler\n\t\t\t}\n\t\t\t// TODO: I'm using the gen 3/4 formula below.\n\t\t\t// TODO: Figure out the gen 5 formula, as well as what to use in a double wild battle\n\t\t\tint a = pkmn.Speed;\n\t\t\tint b = (int)trainer.Team.OpposingTeam.ActiveBattlers.Average(p => p.Speed);\n\t\t\tint c = ++trainer.Team.NumTimesTriedToFlee; // Increment even if guaranteed\n\t\t\tbool success;\n\t\t\tif (a > b)\n\t\t\t{\n\t\t\t\tsuccess = true;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tint f = ((a * 128 / b) + (30 * c)) % 256;\n\t\t\t\tsuccess = _rand.RandomInt(0, 255) < f;\n\t\t\t}\n\t\t\tif (success)\n\t\t\t{\n\t\t\t\tSetEscaped(pkmn);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tBroadcastFleeFailed(pkmn);\n\t\t\t}\n\t\t\ttrainer.RequestedFlee = false;\n\t\t}\n\t}\n\tprivate void WildFleeCheck(PBEBattlePokemon pkmn)\n\t{\n\t\t// TODO: Trapping effects\n\t\tSetEscaped(pkmn);\n\t}\n\n\tprivate void CalcEXP(PBEBattlePokemon loser)\n\t{\n\t\tIPBEPokemonData loserPData = PBEDataProvider.Instance.GetPokemonData(loser);\n\t\tfloat modTrainer = loser.IsWild ? 1 : 1.5f;\n\t\tint expYield = loserPData.BaseEXPYield;\n\t\tint levelLoser = loser.Level;\n\t\tfloat modPassPower = PBEDataProvider.Instance.GetEXPModifier(this);\n\t\tint amtParticipated = loser.EXPPokemon.Count(pk => pk.Trainer.GainsEXP && pk.HP > 0);\n\t\tint amtEXPShare = loser.EXPPokemon.Count(pk => pk.Trainer.GainsEXP && pk.Item == PBEItem.ExpShare);\n\t\tforeach (PBEBattlePokemon victor in loser.EXPPokemon)\n\t\t{\n\t\t\tif (!victor.Trainer.GainsEXP || victor.HP == 0 || victor.Level >= Settings.MaxLevel)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tint levelVictor = victor.Level;\n\t\t\tfloat modParticipators;\n\t\t\tif (amtEXPShare == 0)\n\t\t\t{\n\t\t\t\tmodParticipators = amtParticipated;\n\t\t\t}\n\t\t\telse if (victor.Item == PBEItem.ExpShare)\n\t\t\t{\n\t\t\t\tmodParticipators = 2 * amtEXPShare;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tmodParticipators = 2 * amtParticipated;\n\t\t\t}\n\t\t\tfloat modTraded = PBEDataProvider.Instance.GetEXPTradeModifier(victor);\n\t\t\tfloat modLuckyEgg = victor.Item == PBEItem.LuckyEgg ? 1.5f : 1;\n\n\t\t\tfloat result1H = modTrainer * expYield * levelLoser;\n\t\t\tfloat result1L = 5 * modParticipators;\n\t\t\tfloat result1 = result1H / result1L;\n\t\t\tfloat result2H = MathF.Pow(2 * levelLoser + 10, 2.5f);\n\t\t\tfloat result2L = MathF.Pow(levelLoser + levelVictor + 10, 2.5f);\n\t\t\tfloat result2 = result2H / result2L;\n\t\t\tfloat combined = result1 * result2 + 1;\n\t\t\tfloat final = combined * modTraded * modLuckyEgg * modPassPower;\n\t\t\tGiveEXP(victor, (uint)final);\n\t\t}\n\t}\n\tprivate void GiveEXP(PBEBattlePokemon victor, uint amount)\n\t{\n\t\t// TODO: Should we allow remote battles with learning moves? No packets right now\n\t\tBroadcastPkmnEXPEarned(victor, amount);\n\t\tPBEGrowthRate growthRate = PBEDataProvider.Instance.GetPokemonData(victor).GrowthRate;\n\ttop:\n\t\tuint oldEXP = victor.EXP;\n\t\tuint nextLevelAmt = PBEDataProvider.Instance.GetEXPRequired(growthRate, (byte)(victor.Level + 1));\n\t\tif (oldEXP + amount >= nextLevelAmt)\n\t\t{\n\t\t\tvictor.EXP = nextLevelAmt;\n\t\t\tBroadcastPkmnEXPChanged(victor, oldEXP);\n\t\t\tvictor.Level++;\n\t\t\tvictor.SetStats(true, false);\n\t\t\tBroadcastPkmnLevelChanged(victor);\n\t\t\t// BUG: PBEStatus2.PowerTrick is not cleared when leveling up, even though the stats are replaced (meaning it can still be baton passed)\n\t\t\tif (Settings.BugFix && victor.Status2.HasFlag(PBEStatus2.PowerTrick))\n\t\t\t{\n\t\t\t\tBroadcastStatus2(victor, victor, PBEStatus2.PowerTrick, PBEStatusAction.Ended);\n\t\t\t}\n\t\t\tif (victor.Level == Settings.MaxLevel)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tuint grewBy = nextLevelAmt - oldEXP;\n\t\t\tamount -= grewBy;\n\t\t\tif (amount > 0)\n\t\t\t{\n\t\t\t\tgoto top; // Keep gaining and leveling\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvictor.EXP = oldEXP + amount;\n\t\t\tBroadcastPkmnEXPChanged(victor, oldEXP);\n\t\t}\n\t}\n\n\tprivate static float PokedexCountTable(int count, float g600, float g450, float g300, float g150, float g30, float ge0)\n\t{\n\t\tif (count > 600)\n\t\t{\n\t\t\treturn g600;\n\t\t}\n\t\tif (count > 450)\n\t\t{\n\t\t\treturn g450;\n\t\t}\n\t\tif (count > 300)\n\t\t{\n\t\t\treturn g300;\n\t\t}\n\t\tif (count > 150)\n\t\t{\n\t\t\treturn g150;\n\t\t}\n\t\tif (count > 30)\n\t\t{\n\t\t\treturn g30;\n\t\t}\n\t\treturn ge0;\n\t}\n\tprivate void GenerateCapture(PBEBattlePokemon user, PBEBattlePokemon wildPkmn, PBEItem ball, out byte shakes, out bool success, out bool isCriticalCapture)\n\t{\n\t\tif (PBEDataProvider.Instance.IsGuaranteedCapture(this, wildPkmn.OriginalSpecies, wildPkmn.RevertForm))\n\t\t{\n\t\t\tshakes = 3;\n\t\t\tsuccess = true;\n\t\t\tisCriticalCapture = false;\n\t\t\treturn;\n\t\t}\n\t\tIPBEPokemonData pData = PBEDataProvider.Instance.GetPokemonData(wildPkmn.OriginalSpecies, wildPkmn.RevertForm);\n\t\tfloat rate = pData.CatchRate * PBEDataProvider.Instance.GetCatchRateModifier(this);\n\t\tfloat bonusBall = 1;\n\t\tswitch (ball)\n\t\t{\n\t\t\tcase PBEItem.GreatBall:\n\t\t\tcase PBEItem.SafariBall:\n\t\t\tcase PBEItem.SportBall: bonusBall = 1.5f; break;\n\t\t\tcase PBEItem.UltraBall: bonusBall = 2; break;\n\t\t\tcase PBEItem.MasterBall:\n\t\t\tcase PBEItem.ParkBall: bonusBall = 255; break;\n\t\t\tcase PBEItem.FastBall:\n\t\t\t{\n\t\t\t\tif (wildPkmn.Speed >= 100)\n\t\t\t\t{\n\t\t\t\t\trate *= 4;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEItem.LevelBall:\n\t\t\t{\n\t\t\t\tint wl = wildPkmn.Level;\n\t\t\t\tint ul = user.Level;\n\t\t\t\tif (ul > wl * 4)\n\t\t\t\t{\n\t\t\t\t\trate *= 8;\n\t\t\t\t}\n\t\t\t\telse if (ul > wl * 2)\n\t\t\t\t{\n\t\t\t\t\trate *= 4;\n\t\t\t\t}\n\t\t\t\telse if (ul > wl)\n\t\t\t\t{\n\t\t\t\t\trate *= 2;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEItem.LureBall:\n\t\t\t{\n\t\t\t\tif (PBEDataProvider.Instance.IsFishing(this))\n\t\t\t\t{\n\t\t\t\t\trate *= 3;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEItem.HeavyBall:\n\t\t\t{\n\t\t\t\tfloat weight = pData.Weight;\n\t\t\t\tif (weight >= 409.6f)\n\t\t\t\t{\n\t\t\t\t\trate += 40;\n\t\t\t\t}\n\t\t\t\telse if (weight >= 307.2f)\n\t\t\t\t{\n\t\t\t\t\trate += 30;\n\t\t\t\t}\n\t\t\t\telse if (weight >= 204.8f)\n\t\t\t\t{\n\t\t\t\t\trate += 20;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\trate -= 20;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEItem.LoveBall:\n\t\t\t{\n\t\t\t\tif (user.Species == wildPkmn.Species && user.Gender.IsOppositeGender(wildPkmn.Gender))\n\t\t\t\t{\n\t\t\t\t\trate *= 8;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEItem.MoonBall:\n\t\t\t{\n\t\t\t\tif (PBEDataProvider.Instance.IsMoonBallFamily(wildPkmn.OriginalSpecies, wildPkmn.RevertForm))\n\t\t\t\t{\n\t\t\t\t\trate *= 4;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEItem.NetBall:\n\t\t\t{\n\t\t\t\tif (wildPkmn.HasType(PBEType.Bug) || wildPkmn.HasType(PBEType.Water))\n\t\t\t\t{\n\t\t\t\t\tbonusBall = 3;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEItem.NestBall:\n\t\t\t{\n\t\t\t\tbonusBall = Math.Max(1, (41 - wildPkmn.Level) / 10);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEItem.RepeatBall:\n\t\t\t{\n\t\t\t\tif (PBEDataProvider.Instance.IsRepeatBallSpecies(wildPkmn.OriginalSpecies))\n\t\t\t\t{\n\t\t\t\t\tbonusBall = 3;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEItem.TimerBall:\n\t\t\t{\n\t\t\t\tbonusBall = Math.Min(4, 1 + (TurnNumber * 0.3f));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEItem.DiveBall:\n\t\t\t{\n\t\t\t\tif (PBEDataProvider.Instance.IsFishing(this) || PBEDataProvider.Instance.IsSurfing(this) || PBEDataProvider.Instance.IsUnderwater(this))\n\t\t\t\t{\n\t\t\t\t\tbonusBall = 3.5f;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEItem.DuskBall:\n\t\t\t{\n\t\t\t\tif (PBEDataProvider.Instance.IsDuskBallSetting(this))\n\t\t\t\t{\n\t\t\t\t\tbonusBall = 3.5f;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEItem.QuickBall:\n\t\t\t{\n\t\t\t\tif (TurnNumber == 1)\n\t\t\t\t{\n\t\t\t\t\tbonusBall = 5;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\trate = Math.Clamp(rate, 1, 255);\n\t\tfloat bonusStatus;\n\t\tswitch (wildPkmn.Status1)\n\t\t{\n\t\t\tcase PBEStatus1.Asleep:\n\t\t\tcase PBEStatus1.Frozen: bonusStatus = 2.5f; break;\n\t\t\tcase PBEStatus1.None: bonusStatus = 1; break;\n\t\t\tdefault: bonusStatus = 1.5f; break;\n\t\t}\n\t\tfloat pkmnFactor = (3 * wildPkmn.MaxHP) - (2 * wildPkmn.HP);\n\t\tint pkmnCaught = PBEDataProvider.Instance.GetSpeciesCaught();\n\t\tif (PBEDataProvider.Instance.IsDarkGrass(this))\n\t\t{\n\t\t\tpkmnFactor *= PokedexCountTable(pkmnCaught, 1, 0.9f, 0.8f, 0.7f, 0.5f, 0.3f);\n\t\t}\n\t\tfloat a = pkmnFactor * rate * bonusBall / (3 * wildPkmn.MaxHP) * bonusStatus;\n\t\tfloat c = a * PokedexCountTable(pkmnCaught, 2.5f, 2, 1.5f, 1, 0.5f, 0); // Critical capture modifier\n\t\tisCriticalCapture = _rand.RandomInt(0, 0xFF) < c / 6;\n\t\tbyte numShakes = isCriticalCapture ? (byte)1 : (byte)3;\n\t\tif (a >= 0xFF)\n\t\t{\n\t\t\tshakes = numShakes; // Skip shake checks\n\t\t\tsuccess = true;\n\t\t\treturn;\n\t\t}\n\t\tfloat b = 0x10000 / MathF.Sqrt(MathF.Sqrt(0xFF / a));\n\t\tfor (shakes = 0; shakes < numShakes; shakes++)\n\t\t{\n\t\t\tif (_rand.RandomInt(0, 0xFFFF) >= b)\n\t\t\t{\n\t\t\t\tbreak; // Shake check fails\n\t\t\t}\n\t\t}\n\t\tsuccess = shakes == numShakes;\n\t\tif (shakes == 2)\n\t\t{\n\t\t\tshakes = 3; // If there are only 2 shakes and a failure, shake three times and still fail\n\t\t}\n\t}\n\tprivate void UseItem(PBEBattlePokemon user, PBEItem item)\n\t{\n\t\tBroadcastItemTurn(user, item, PBEItemTurnAction.Attempt);\n\t\tif (PBEDataUtils.AllBalls.Contains(item))\n\t\t{\n\t\t\tuser.Trainer.Inventory.Remove(item);\n\t\t\tif (BattleType != PBEBattleType.Wild)\n\t\t\t{\n\t\t\t\tgoto fail;\n\t\t\t}\n\t\t\tPBEBattlePokemon wildPkmn = user.Team.OpposingTeam.ActiveBattlers.Single();\n\t\t\tGenerateCapture(user, wildPkmn, item, out byte numShakes, out bool success, out bool critical);\n\t\t\tBroadcastCapture(wildPkmn, item, numShakes, success, critical);\n\t\t\tif (success)\n\t\t\t{\n\t\t\t\twildPkmn.CaughtBall = wildPkmn.KnownCaughtBall = item;\n\t\t\t\tBattleResult = PBEBattleResult.WildCapture;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tswitch (item)\n\t\t\t{\n\t\t\t\tcase PBEItem.FluffyTail:\n\t\t\t\tcase PBEItem.PokeDoll:\n\t\t\t\tcase PBEItem.PokeToy:\n\t\t\t\t{\n\t\t\t\t\tuser.Trainer.Inventory.Remove(item);\n\t\t\t\t\tif (BattleType == PBEBattleType.Wild)\n\t\t\t\t\t{\n\t\t\t\t\t\tSetEscaped(user);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tgoto fail;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\tfail:\n\t\tBroadcastItemTurn(user, item, PBEItemTurnAction.NoEffect);\n\t}\n\n\tprivate void UseMove(PBEBattlePokemon user, PBEMove move, PBETurnTarget requestedTargets)\n\t{\n\t\t// Cancel the semi-invulnerable move if the user is affected by its status1\n\t\tif (!_calledFromOtherMove && PreMoveStatusCheck(user, move))\n\t\t{\n\t\t\tif (user.Status2.HasFlag(PBEStatus2.Airborne))\n\t\t\t{\n\t\t\t\tBroadcastMoveLock_Temporary(user, PBEMove.None, PBETurnTarget.None);\n\t\t\t\tBroadcastStatus2(user, user, PBEStatus2.Airborne, PBEStatusAction.Ended);\n\t\t\t}\n\t\t\tif (user.Status2.HasFlag(PBEStatus2.ShadowForce))\n\t\t\t{\n\t\t\t\tBroadcastMoveLock_Temporary(user, PBEMove.None, PBETurnTarget.None);\n\t\t\t\tBroadcastStatus2(user, user, PBEStatus2.ShadowForce, PBEStatusAction.Ended);\n\t\t\t}\n\t\t\tif (user.Status2.HasFlag(PBEStatus2.Underground))\n\t\t\t{\n\t\t\t\tBroadcastMoveLock_Temporary(user, PBEMove.None, PBETurnTarget.None);\n\t\t\t\tBroadcastStatus2(user, user, PBEStatus2.Underground, PBEStatusAction.Ended);\n\t\t\t}\n\t\t\tif (user.Status2.HasFlag(PBEStatus2.Underwater))\n\t\t\t{\n\t\t\t\tBroadcastMoveLock_Temporary(user, PBEMove.None, PBETurnTarget.None);\n\t\t\t\tBroadcastStatus2(user, user, PBEStatus2.Underwater, PBEStatusAction.Ended);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tIPBEMoveData mData = PBEDataProvider.Instance.GetMoveData(move);\n\t\tPBEBattlePokemon[] targets = GetRuntimeTargets(user, requestedTargets, user.GetMoveTargets(mData) == PBEMoveTarget.SingleNotSelf, _rand);\n\t\tswitch (mData.Effect)\n\t\t{\n\t\t\tcase PBEMoveEffect.Acrobatics:\n\t\t\tcase PBEMoveEffect.Brine:\n\t\t\tcase PBEMoveEffect.ChipAway:\n\t\t\tcase PBEMoveEffect.CrushGrip:\n\t\t\tcase PBEMoveEffect.Eruption:\n\t\t\tcase PBEMoveEffect.Facade:\n\t\t\tcase PBEMoveEffect.Flail:\n\t\t\tcase PBEMoveEffect.FoulPlay:\n\t\t\tcase PBEMoveEffect.Frustration:\n\t\t\tcase PBEMoveEffect.GrassKnot:\n\t\t\tcase PBEMoveEffect.HeatCrash:\n\t\t\tcase PBEMoveEffect.Hex:\n\t\t\tcase PBEMoveEffect.HiddenPower:\n\t\t\tcase PBEMoveEffect.Hit:\n\t\t\tcase PBEMoveEffect.Judgment:\n\t\t\tcase PBEMoveEffect.Magnitude:\n\t\t\tcase PBEMoveEffect.Payback:\n\t\t\tcase PBEMoveEffect.Psyshock:\n\t\t\tcase PBEMoveEffect.Punishment:\n\t\t\tcase PBEMoveEffect.Retaliate:\n\t\t\tcase PBEMoveEffect.Return:\n\t\t\tcase PBEMoveEffect.StoredPower:\n\t\t\tcase PBEMoveEffect.TechnoBlast:\n\t\t\tcase PBEMoveEffect.Venoshock:\n\t\t\tcase PBEMoveEffect.WeatherBall: Ef_Hit(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.Attract: Ef_TryForceStatus2(user, targets, move, mData, PBEStatus2.Infatuated); break;\n\t\t\tcase PBEMoveEffect.BellyDrum: Ef_BellyDrum(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.Bounce: Ef_Bounce(user, targets, move, mData, requestedTargets); break;\n\t\t\tcase PBEMoveEffect.BrickBreak: Ef_BrickBreak(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.Burn: Ef_TryForceStatus1(user, targets, move, mData, PBEStatus1.Burned); break;\n\t\t\tcase PBEMoveEffect.Camouflage: Ef_Camouflage(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.ChangeTarget_ACC: Ef_ChangeTargetStats(user, targets, move, mData, new[] { (PBEStat.Accuracy, mData.EffectParam) }); break;\n\t\t\tcase PBEMoveEffect.ChangeTarget_ATK: Ef_ChangeTargetStats(user, targets, move, mData, new[] { (PBEStat.Attack, mData.EffectParam) }); break;\n\t\t\tcase PBEMoveEffect.ChangeTarget_DEF: Ef_ChangeTargetStats(user, targets, move, mData, new[] { (PBEStat.Defense, mData.EffectParam) }); break;\n\t\t\tcase PBEMoveEffect.ChangeTarget_EVA:\n\t\t\tcase PBEMoveEffect.Minimize: Ef_ChangeTargetStats(user, targets, move, mData, new[] { (PBEStat.Evasion, mData.EffectParam) }); break;\n\t\t\tcase PBEMoveEffect.ChangeTarget_SPATK: Ef_ChangeTargetStats(user, targets, move, mData, new[] { (PBEStat.SpAttack, mData.EffectParam) }); break;\n\t\t\tcase PBEMoveEffect.ChangeTarget_SPATK__IfAttractionPossible: Ef_ChangeTargetStats(user, targets, move, mData, new[] { (PBEStat.SpAttack, mData.EffectParam) }, requireAttraction: true); break;\n\t\t\tcase PBEMoveEffect.ChangeTarget_SPDEF: Ef_ChangeTargetStats(user, targets, move, mData, new[] { (PBEStat.SpDefense, mData.EffectParam) }); break;\n\t\t\tcase PBEMoveEffect.ChangeTarget_SPE: Ef_ChangeTargetStats(user, targets, move, mData, new[] { (PBEStat.Speed, mData.EffectParam) }); break;\n\t\t\tcase PBEMoveEffect.Confuse: Ef_TryForceStatus2(user, targets, move, mData, PBEStatus2.Confused); break;\n\t\t\tcase PBEMoveEffect.Conversion: Ef_Conversion(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.Curse: Ef_Curse(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.Dig: SemiInvulnerableChargeMove(user, targets, move, mData, requestedTargets, PBEStatus2.Underground); break;\n\t\t\tcase PBEMoveEffect.Dive: SemiInvulnerableChargeMove(user, targets, move, mData, requestedTargets, PBEStatus2.Underwater); break;\n\t\t\tcase PBEMoveEffect.Endeavor: Ef_Endeavor(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.Entrainment: Ef_Entrainment(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.Feint: Ef_Feint(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.FinalGambit: Ef_FinalGambit(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.Flatter: Ef_Flatter(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.Fly: SemiInvulnerableChargeMove(user, targets, move, mData, requestedTargets, PBEStatus2.Airborne); break;\n\t\t\tcase PBEMoveEffect.FocusEnergy: Ef_TryForceStatus2(user, targets, move, mData, PBEStatus2.Pumped); break;\n\t\t\tcase PBEMoveEffect.Foresight: Ef_TryForceStatus2(user, targets, move, mData, PBEStatus2.Identified); break;\n\t\t\tcase PBEMoveEffect.GastroAcid: Ef_SetOtherAbility(user, targets, move, mData, PBEAbility.None, false); break;\n\t\t\tcase PBEMoveEffect.Growth: Ef_Growth(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.Hail: Ef_TryForceWeather(user, move, mData, PBEWeather.Hailstorm); break;\n\t\t\tcase PBEMoveEffect.Haze: Ef_Haze(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.HelpingHand: Ef_HelpingHand(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.Hit__2Times: Ef_MultiHit(user, targets, move, mData, 2); break;\n\t\t\tcase PBEMoveEffect.Hit__2Times__MaybePoison: Ef_MultiHit(user, targets, move, mData, 2, status1: PBEStatus1.Poisoned, chanceToInflictStatus1: mData.EffectParam); break;\n\t\t\tcase PBEMoveEffect.Hit__2To5Times: Ef_MultiHit_2To5(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.Hit__MaybeBurn: Ef_Hit(user, targets, move, mData, status1: PBEStatus1.Burned, chanceToInflictStatus1: mData.EffectParam); break;\n\t\t\tcase PBEMoveEffect.Hit__MaybeBurn__10PercentFlinch: Ef_Hit(user, targets, move, mData, status1: PBEStatus1.Burned, chanceToInflictStatus1: mData.EffectParam, status2: PBEStatus2.Flinching, chanceToInflictStatus2: 10); break;\n\t\t\tcase PBEMoveEffect.Hit__MaybeBurnFreezeParalyze: Ef_Hit__MaybeBurnFreezeParalyze(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.Hit__MaybeConfuse: Ef_Hit(user, targets, move, mData, status2: PBEStatus2.Confused, chanceToInflictStatus2: mData.EffectParam); break;\n\t\t\tcase PBEMoveEffect.Hit__MaybeFlinch: Ef_Hit(user, targets, move, mData, status2: PBEStatus2.Flinching, chanceToInflictStatus2: mData.EffectParam); break;\n\t\t\tcase PBEMoveEffect.Hit__MaybeFreeze: Ef_Hit(user, targets, move, mData, status1: PBEStatus1.Frozen, chanceToInflictStatus1: mData.EffectParam); break;\n\t\t\tcase PBEMoveEffect.Hit__MaybeFreeze__10PercentFlinch: Ef_Hit(user, targets, move, mData, status1: PBEStatus1.Frozen, chanceToInflictStatus1: mData.EffectParam, status2: PBEStatus2.Flinching, chanceToInflictStatus2: 10); break;\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerTarget_ACC_By1: Ef_Hit__MaybeChangeTargetStats(user, targets, move, mData, new[] { (PBEStat.Accuracy, -1) }, mData.EffectParam); break;\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerTarget_ATK_By1: Ef_Hit__MaybeChangeTargetStats(user, targets, move, mData, new[] { (PBEStat.Attack, -1) }, mData.EffectParam); break;\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerTarget_DEF_By1: Ef_Hit__MaybeChangeTargetStats(user, targets, move, mData, new[] { (PBEStat.Defense, -1) }, mData.EffectParam); break;\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerTarget_SPATK_By1: Ef_Hit__MaybeChangeTargetStats(user, targets, move, mData, new[] { (PBEStat.SpAttack, -1) }, mData.EffectParam); break;\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerTarget_SPDEF_By1: Ef_Hit__MaybeChangeTargetStats(user, targets, move, mData, new[] { (PBEStat.SpDefense, -1) }, mData.EffectParam); break;\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerTarget_SPDEF_By2: Ef_Hit__MaybeChangeTargetStats(user, targets, move, mData, new[] { (PBEStat.SpDefense, -2) }, mData.EffectParam); break;\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerTarget_SPE_By1: Ef_Hit__MaybeChangeTargetStats(user, targets, move, mData, new[] { (PBEStat.Speed, -1) }, mData.EffectParam); break;\n\t\t\tcase PBEMoveEffect.Hit__MaybeParalyze: Ef_Hit(user, targets, move, mData, status1: PBEStatus1.Paralyzed, chanceToInflictStatus1: mData.EffectParam); break;\n\t\t\tcase PBEMoveEffect.Hit__MaybeParalyze__10PercentFlinch: Ef_Hit(user, targets, move, mData, status1: PBEStatus1.Paralyzed, chanceToInflictStatus1: mData.EffectParam, status2: PBEStatus2.Flinching, chanceToInflictStatus2: 10); break;\n\t\t\tcase PBEMoveEffect.Hit__MaybePoison: Ef_Hit(user, targets, move, mData, status1: PBEStatus1.Poisoned, chanceToInflictStatus1: mData.EffectParam); break;\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerUser_ATK_DEF_By1: Ef_Hit__MaybeChangeUserStats(user, targets, move, mData, new[] { (PBEStat.Attack, -1), (PBEStat.Defense, -1) }, mData.EffectParam); break;\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerUser_DEF_SPDEF_By1: Ef_Hit__MaybeChangeUserStats(user, targets, move, mData, new[] { (PBEStat.Defense, -1), (PBEStat.SpDefense, -1) }, mData.EffectParam); break;\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerUser_SPATK_By2: Ef_Hit__MaybeChangeUserStats(user, targets, move, mData, new[] { (PBEStat.SpAttack, -2) }, mData.EffectParam); break;\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerUser_SPE_By1: Ef_Hit__MaybeChangeUserStats(user, targets, move, mData, new[] { (PBEStat.Speed, -1) }, mData.EffectParam); break;\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerUser_SPE_DEF_SPDEF_By1: Ef_Hit__MaybeChangeUserStats(user, targets, move, mData, new[] { (PBEStat.Speed, -1), (PBEStat.Defense, -1), (PBEStat.SpDefense, -1) }, mData.EffectParam); break;\n\t\t\tcase PBEMoveEffect.Hit__MaybeRaiseUser_ATK_By1: Ef_Hit__MaybeChangeUserStats(user, targets, move, mData, new[] { (PBEStat.Attack, +1) }, mData.EffectParam); break;\n\t\t\tcase PBEMoveEffect.Hit__MaybeRaiseUser_ATK_DEF_SPATK_SPDEF_SPE_By1: Ef_Hit__MaybeChangeUserStats(user, targets, move, mData, new[] { (PBEStat.Attack, +1), (PBEStat.Defense, +1), (PBEStat.SpAttack, +1), (PBEStat.SpDefense, +1), (PBEStat.Speed, +1) }, mData.EffectParam); break;\n\t\t\tcase PBEMoveEffect.Hit__MaybeRaiseUser_DEF_By1: Ef_Hit__MaybeChangeUserStats(user, targets, move, mData, new[] { (PBEStat.Defense, +1) }, mData.EffectParam); break;\n\t\t\tcase PBEMoveEffect.Hit__MaybeRaiseUser_SPATK_By1: Ef_Hit__MaybeChangeUserStats(user, targets, move, mData, new[] { (PBEStat.SpAttack, +1) }, mData.EffectParam); break;\n\t\t\tcase PBEMoveEffect.Hit__MaybeRaiseUser_SPE_By1: Ef_Hit__MaybeChangeUserStats(user, targets, move, mData, new[] { (PBEStat.Speed, +1) }, mData.EffectParam); break;\n\t\t\tcase PBEMoveEffect.Hit__MaybeToxic: Ef_Hit(user, targets, move, mData, status1: PBEStatus1.BadlyPoisoned, chanceToInflictStatus1: mData.EffectParam); break;\n\t\t\tcase PBEMoveEffect.HPDrain: Ef_HPDrain(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.HPDrain__RequireSleep: Ef_HPDrain(user, targets, move, mData, requireSleep: true); break;\n\t\t\tcase PBEMoveEffect.LeechSeed: Ef_TryForceStatus2(user, targets, move, mData, PBEStatus2.LeechSeed); break;\n\t\t\tcase PBEMoveEffect.LightScreen: Ef_TryForceTeamStatus(user, move, mData, PBETeamStatus.LightScreen); break;\n\t\t\tcase PBEMoveEffect.LockOn: Ef_TryForceStatus2(user, targets, move, mData, PBEStatus2.LockOn); break;\n\t\t\tcase PBEMoveEffect.LowerTarget_ATK_DEF_By1: Ef_ChangeTargetStats(user, targets, move, mData, new[] { (PBEStat.Attack, -1), (PBEStat.Defense, -1) }); break;\n\t\t\tcase PBEMoveEffect.LowerTarget_DEF_SPDEF_By1_Raise_ATK_SPATK_SPE_By2: Ef_ChangeTargetStats(user, targets, move, mData, new[] { (PBEStat.Defense, -1), (PBEStat.SpDefense, -1), (PBEStat.Attack, +2), (PBEStat.SpAttack, +2), (PBEStat.Speed, +2) }); break;\n\t\t\tcase PBEMoveEffect.LuckyChant: Ef_TryForceTeamStatus(user, move, mData, PBETeamStatus.LuckyChant); break;\n\t\t\tcase PBEMoveEffect.MagnetRise: Ef_TryForceStatus2(user, targets, move, mData, PBEStatus2.MagnetRise); break;\n\t\t\tcase PBEMoveEffect.Metronome: Ef_Metronome(user, move, mData); break;\n\t\t\tcase PBEMoveEffect.MiracleEye: Ef_TryForceStatus2(user, targets, move, mData, PBEStatus2.MiracleEye); break;\n\t\t\tcase PBEMoveEffect.Moonlight: Ef_Moonlight(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.Nightmare: Ef_TryForceStatus2(user, targets, move, mData, PBEStatus2.Nightmare); break;\n\t\t\tcase PBEMoveEffect.Nothing: Ef_Nothing(user, move, mData); break;\n\t\t\tcase PBEMoveEffect.OneHitKnockout: Ef_OneHitKnockout(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.PainSplit: Ef_PainSplit(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.Paralyze: Ef_TryForceStatus1(user, targets, move, mData, PBEStatus1.Paralyzed); break;\n\t\t\tcase PBEMoveEffect.PayDay: Ef_PayDay(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.Poison: Ef_TryForceStatus1(user, targets, move, mData, PBEStatus1.Poisoned); break;\n\t\t\tcase PBEMoveEffect.PowerTrick: Ef_TryForceStatus2(user, targets, move, mData, PBEStatus2.PowerTrick); break;\n\t\t\tcase PBEMoveEffect.Protect: Ef_TryForceStatus2(user, targets, move, mData, PBEStatus2.Protected); break;\n\t\t\tcase PBEMoveEffect.PsychUp: Ef_PsychUp(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.Psywave: Ef_Psywave(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.QuickGuard: Ef_TryForceTeamStatus(user, move, mData, PBETeamStatus.QuickGuard); break;\n\t\t\tcase PBEMoveEffect.RainDance: Ef_TryForceWeather(user, move, mData, PBEWeather.Rain); break;\n\t\t\tcase PBEMoveEffect.RaiseTarget_ATK_ACC_By1: Ef_ChangeTargetStats(user, targets, move, mData, new[] { (PBEStat.Attack, +1), (PBEStat.Accuracy, +1) }); break;\n\t\t\tcase PBEMoveEffect.RaiseTarget_ATK_DEF_By1: Ef_ChangeTargetStats(user, targets, move, mData, new[] { (PBEStat.Attack, +1), (PBEStat.Defense, +1) }); break;\n\t\t\tcase PBEMoveEffect.RaiseTarget_ATK_DEF_ACC_By1: Ef_ChangeTargetStats(user, targets, move, mData, new[] { (PBEStat.Attack, +1), (PBEStat.Defense, +1), (PBEStat.Accuracy, +1) }); break;\n\t\t\tcase PBEMoveEffect.RaiseTarget_ATK_SPATK_By1: Ef_ChangeTargetStats(user, targets, move, mData, new[] { (PBEStat.Attack, +1), (PBEStat.SpAttack, +1) }); break;\n\t\t\tcase PBEMoveEffect.RaiseTarget_ATK_SPE_By1: Ef_ChangeTargetStats(user, targets, move, mData, new[] { (PBEStat.Attack, +1), (PBEStat.Speed, +1) }); break;\n\t\t\tcase PBEMoveEffect.RaiseTarget_DEF_SPDEF_By1: Ef_ChangeTargetStats(user, targets, move, mData, new[] { (PBEStat.Defense, +1), (PBEStat.SpDefense, +1) }); break;\n\t\t\tcase PBEMoveEffect.RaiseTarget_SPATK_SPDEF_By1: Ef_ChangeTargetStats(user, targets, move, mData, new[] { (PBEStat.SpAttack, +1), (PBEStat.SpDefense, +1) }); break;\n\t\t\tcase PBEMoveEffect.RaiseTarget_SPATK_SPDEF_SPE_By1: Ef_ChangeTargetStats(user, targets, move, mData, new[] { (PBEStat.SpAttack, +1), (PBEStat.SpDefense, +1), (PBEStat.Speed, +1) }); break;\n\t\t\tcase PBEMoveEffect.RaiseTarget_SPE_By2_ATK_By1: Ef_ChangeTargetStats(user, targets, move, mData, new[] { (PBEStat.Speed, +2), (PBEStat.Attack, +1) }); break;\n\t\t\tcase PBEMoveEffect.Recoil: Ef_Recoil(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.Recoil__10PercentBurn: Ef_Recoil(user, targets, move, mData, status1: PBEStatus1.Burned, chanceToInflictStatus1: 10); break;\n\t\t\tcase PBEMoveEffect.Recoil__10PercentParalyze: Ef_Recoil(user, targets, move, mData, status1: PBEStatus1.Paralyzed, chanceToInflictStatus1: 10); break;\n\t\t\tcase PBEMoveEffect.Reflect: Ef_TryForceTeamStatus(user, move, mData, PBETeamStatus.Reflect); break;\n\t\t\tcase PBEMoveEffect.ReflectType: Ef_ReflectType(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.Refresh: Ef_Refresh(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.Rest: Ef_Rest(user, move, mData); break;\n\t\t\tcase PBEMoveEffect.RestoreTargetHP: Ef_RestoreTargetHP(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.RolePlay: Ef_RolePlay(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.Roost: Ef_Roost(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.Safeguard: Ef_TryForceTeamStatus(user, move, mData, PBETeamStatus.Safeguard); break;\n\t\t\tcase PBEMoveEffect.Sandstorm: Ef_TryForceWeather(user, move, mData, PBEWeather.Sandstorm); break;\n\t\t\tcase PBEMoveEffect.SecretPower: Ef_SecretPower(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.SeismicToss: Ef_SeismicToss(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.Selfdestruct: Ef_Selfdestruct(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.SetDamage: Ef_SetDamage(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.ShadowForce: Ef_ShadowForce(user, targets, move, mData, requestedTargets); break;\n\t\t\tcase PBEMoveEffect.SimpleBeam: Ef_SetOtherAbility(user, targets, move, mData, PBEAbility.Simple, true); break;\n\t\t\tcase PBEMoveEffect.Sleep: Ef_TryForceStatus1(user, targets, move, mData, PBEStatus1.Asleep); break;\n\t\t\tcase PBEMoveEffect.SmellingSalt: Ef_SmellingSalt(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.Snore: Ef_Snore(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.Soak: Ef_Soak(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.Spikes: Ef_TryForceTeamStatus(user, move, mData, PBETeamStatus.Spikes); break;\n\t\t\tcase PBEMoveEffect.StealthRock: Ef_TryForceTeamStatus(user, move, mData, PBETeamStatus.StealthRock); break;\n\t\t\tcase PBEMoveEffect.Struggle: Ef_Struggle(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.Substitute: Ef_TryForceStatus2(user, targets, move, mData, PBEStatus2.Substitute); break;\n\t\t\tcase PBEMoveEffect.SuckerPunch: Ef_SuckerPunch(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.SunnyDay: Ef_TryForceWeather(user, move, mData, PBEWeather.HarshSunlight); break;\n\t\t\tcase PBEMoveEffect.SuperFang: Ef_SuperFang(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.Swagger: Ef_Swagger(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.Tailwind: Ef_TryForceTeamStatus(user, move, mData, PBETeamStatus.Tailwind); break;\n\t\t\tcase PBEMoveEffect.Teleport: Ef_Teleport(user, move, mData); break;\n\t\t\tcase PBEMoveEffect.ThunderWave: Ef_TryForceStatus1(user, targets, move, mData, PBEStatus1.Paralyzed, thunderWave: true); break;\n\t\t\tcase PBEMoveEffect.Toxic: Ef_TryForceStatus1(user, targets, move, mData, PBEStatus1.BadlyPoisoned); break;\n\t\t\tcase PBEMoveEffect.ToxicSpikes: Ef_TryForceTeamStatus(user, move, mData, PBETeamStatus.ToxicSpikes); break;\n\t\t\tcase PBEMoveEffect.Transform: Ef_TryForceStatus2(user, targets, move, mData, PBEStatus2.Transformed); break;\n\t\t\tcase PBEMoveEffect.TrickRoom: Ef_TryForceBattleStatus(user, move, mData, PBEBattleStatus.TrickRoom); break;\n\t\t\tcase PBEMoveEffect.WakeUpSlap: Ef_WakeUpSlap(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.Whirlwind: Ef_Whirlwind(user, targets, move, mData); break;\n\t\t\tcase PBEMoveEffect.WideGuard: Ef_TryForceTeamStatus(user, move, mData, PBETeamStatus.WideGuard); break;\n\t\t\tcase PBEMoveEffect.WorrySeed: Ef_SetOtherAbility(user, targets, move, mData, PBEAbility.Insomnia, true); break;\n\t\t\tdefault: throw new InvalidDataException(nameof(mData.Effect));\n\t\t}\n\t}\n\n\tprivate bool PreMoveStatusCheck(PBEBattlePokemon user, PBEMove move)\n\t{\n\t\tIPBEMoveData mData = PBEDataProvider.Instance.GetMoveData(move);\n\n\t\t// Verified: Sleep and Freeze don't interact with Flinch unless they come out of the status\n\t\t// Sleep causes Confusion, Flinch, and Infatuation to activate if the user is trying to use Snore\n\t\tif (user.Status1 == PBEStatus1.Asleep)\n\t\t{\n\t\t\tuser.Status1Counter++;\n\t\t\tif (user.Status1Counter > user.SleepTurns)\n\t\t\t{\n\t\t\t\tuser.Status1 = PBEStatus1.None;\n\t\t\t\tuser.Status1Counter = 0;\n\t\t\t\tuser.SleepTurns = 0;\n\t\t\t\tBroadcastStatus1(user, user, PBEStatus1.Asleep, PBEStatusAction.Ended);\n\t\t\t\tCureNightmare(user, user);\n\t\t\t}\n\t\t\telse if (mData.Effect != PBEMoveEffect.Snore)\n\t\t\t{\n\t\t\t\tBroadcastStatus1(user, user, PBEStatus1.Asleep, PBEStatusAction.CausedImmobility);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\telse if (user.Status1 == PBEStatus1.Frozen)\n\t\t{\n\t\t\tif (mData.Flags.HasFlag(PBEMoveFlag.DefrostsUser) || _rand.RandomBool(20, 100))\n\t\t\t{\n\t\t\t\tuser.Status1 = PBEStatus1.None;\n\t\t\t\tBroadcastStatus1(user, user, PBEStatus1.Frozen, PBEStatusAction.Ended);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tBroadcastStatus1(user, user, PBEStatus1.Frozen, PBEStatusAction.CausedImmobility);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\t// Verified: Flinch before Confusion, Infatuation, and Paralysis can do anything\n\t\tif (user.Status2.HasFlag(PBEStatus2.Flinching))\n\t\t{\n\t\t\tBroadcastStatus2(user, user, PBEStatus2.Flinching, PBEStatusAction.CausedImmobility);\n\t\t\tif (user.Ability == PBEAbility.Steadfast && user.SpeedChange < Settings.MaxStatChange)\n\t\t\t{\n\t\t\t\tBroadcastAbility(user, user, PBEAbility.Steadfast, PBEAbilityAction.Stats);\n\t\t\t\tApplyStatChangeIfPossible(user, user, PBEStat.Speed, +1);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\t// Verified: Confusion before Infatuation and Paralysis\n\t\tif (user.Status2.HasFlag(PBEStatus2.Confused))\n\t\t{\n\t\t\tuser.ConfusionCounter++;\n\t\t\tif (user.ConfusionCounter > user.ConfusionTurns)\n\t\t\t{\n\t\t\t\tuser.ConfusionCounter = 0;\n\t\t\t\tuser.ConfusionTurns = 0;\n\t\t\t\tBroadcastStatus2(user, user, PBEStatus2.Confused, PBEStatusAction.Ended);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tBroadcastStatus2(user, user, PBEStatus2.Confused, PBEStatusAction.Announced);\n\t\t\t\tif (_rand.RandomBool(50, 100))\n\t\t\t\t{\n\t\t\t\t\tint damage = CalculateConfusionDamage(user);\n\t\t\t\t\tDealDamage(user, user, damage, ignoreSturdy: false);\n\t\t\t\t\tBroadcastStatus2(user, user, PBEStatus2.Confused, PBEStatusAction.Damage);\n\t\t\t\t\t// BUG: Confusion damage does not activate these items\n\t\t\t\t\tif (!FaintCheck(user) && Settings.BugFix)\n\t\t\t\t\t{\n\t\t\t\t\t\tLowHPBerryCheck(user);\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// Verified: Paralysis before Infatuation\n\t\tif (user.Status1 == PBEStatus1.Paralyzed && _rand.RandomBool(25, 100))\n\t\t{\n\t\t\tBroadcastStatus1(user, user, PBEStatus1.Paralyzed, PBEStatusAction.CausedImmobility);\n\t\t\treturn true;\n\t\t}\n\t\t// Infatuation\n\t\tif (user.Status2.HasFlag(PBEStatus2.Infatuated))\n\t\t{\n\t\t\tBroadcastStatus2(user, user.InfatuatedWithPokemon!, PBEStatus2.Infatuated, PBEStatusAction.Announced);\n\t\t\tif (_rand.RandomBool(50, 100))\n\t\t\t{\n\t\t\t\tBroadcastStatus2(user, user.InfatuatedWithPokemon!, PBEStatus2.Infatuated, PBEStatusAction.CausedImmobility);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\tprivate bool MissCheck(PBEBattlePokemon user, PBEBattlePokemon target, IPBEMoveData mData)\n\t{\n\t\tif (user == target)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\t// Verified: WideGuard happens before Protect\n\t\tif (target.Team.TeamStatus.HasFlag(PBETeamStatus.WideGuard) && mData.Category != PBEMoveCategory.Status && PBEDataUtils.IsSpreadMove(user.GetMoveTargets(mData)))\n\t\t{\n\t\t\tBroadcastTeamStatusDamage(target.Team, PBETeamStatus.WideGuard, target);\n\t\t\treturn true;\n\t\t}\n\t\t// Feint ignores Quick Guard unless the target is an ally\n\t\tif (target.Team.TeamStatus.HasFlag(PBETeamStatus.QuickGuard) && mData.Priority > 0 && (mData.Effect != PBEMoveEffect.Feint || user.Team == target.Team))\n\t\t{\n\t\t\tBroadcastTeamStatusDamage(target.Team, PBETeamStatus.QuickGuard, target);\n\t\t\treturn true;\n\t\t}\n\t\tif (target.Status2.HasFlag(PBEStatus2.Protected) && mData.Flags.HasFlag(PBEMoveFlag.AffectedByProtect))\n\t\t{\n\t\t\tBroadcastStatus2(target, user, PBEStatus2.Protected, PBEStatusAction.Damage);\n\t\t\treturn true;\n\t\t}\n\t\tif (user.Status2.HasFlag(PBEStatus2.LockOn) && user.LockOnPokemon == target)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\tif (user.Ability == PBEAbility.NoGuard || target.Ability == PBEAbility.NoGuard)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\tif (target.Status2.HasFlag(PBEStatus2.Airborne) && !(mData.Flags.HasFlag(PBEMoveFlag.DoubleDamageAirborne) || mData.Flags.HasFlag(PBEMoveFlag.HitsAirborne)))\n\t\t{\n\t\t\tgoto miss;\n\t\t}\n\t\tif (target.Status2.HasFlag(PBEStatus2.ShadowForce))\n\t\t{\n\t\t\tgoto miss;\n\t\t}\n\t\tif (target.Status2.HasFlag(PBEStatus2.Underground) && !(mData.Flags.HasFlag(PBEMoveFlag.DoubleDamageUnderground) || mData.Flags.HasFlag(PBEMoveFlag.HitsUnderground)))\n\t\t{\n\t\t\tgoto miss;\n\t\t}\n\t\tif (target.Status2.HasFlag(PBEStatus2.Underwater) && !(mData.Flags.HasFlag(PBEMoveFlag.DoubleDamageUnderwater) || mData.Flags.HasFlag(PBEMoveFlag.HitsUnderwater)))\n\t\t{\n\t\t\tgoto miss;\n\t\t}\n\t\t// These go after semi-invulnerable\n\t\tfloat chance = mData.Accuracy;\n\t\tif (chance == 0) // Moves that don't miss\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\tif (ShouldDoWeatherEffects())\n\t\t{\n\t\t\tif (Weather == PBEWeather.Hailstorm && mData.Flags.HasFlag(PBEMoveFlag.NeverMissHail))\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (mData.Flags.HasFlag(PBEMoveFlag.NeverMissRain))\n\t\t\t{\n\t\t\t\tif (Weather == PBEWeather.Rain)\n\t\t\t\t{\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tif (Weather == PBEWeather.HarshSunlight)\n\t\t\t\t{\n\t\t\t\t\tchance = Math.Min(50, chance);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (mData.Effect == PBEMoveEffect.OneHitKnockout)\n\t\t{\n\t\t\tchance = user.Level - target.Level + chance;\n\t\t\tif (chance < 1)\n\t\t\t{\n\t\t\t\tgoto miss;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tgoto roll; // Skip all modifiers\n\t\t\t}\n\t\t}\n\t\tif (target.Ability == PBEAbility.WonderSkin && mData.Category == PBEMoveCategory.Status && !user.HasCancellingAbility())\n\t\t{\n\t\t\tchance = Math.Min(50, chance);\n\t\t}\n\t\tbool ignoreA = mData.Category != PBEMoveCategory.Status && target.Ability == PBEAbility.Unaware && !user.HasCancellingAbility();\n\t\tbool ignoreE = mData.Effect == PBEMoveEffect.ChipAway || (mData.Category != PBEMoveCategory.Status && user.Ability == PBEAbility.Unaware);\n\t\tfloat accuracy = ignoreA ? 1 : GetStatChangeModifier(user.AccuracyChange, true);\n\t\tfloat evasion;\n\t\tif (ignoreE)\n\t\t{\n\t\t\tevasion = 1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tbool ignorePositive = target.Status2.HasFlag(PBEStatus2.Identified) || target.Status2.HasFlag(PBEStatus2.MiracleEye);\n\t\t\tevasion = GetStatChangeModifier(ignorePositive ? Math.Min((sbyte)0, target.EvasionChange) : target.EvasionChange, true);\n\t\t}\n\t\tchance *= accuracy / evasion;\n\t\tif (user.Ability == PBEAbility.Compoundeyes)\n\t\t{\n\t\t\tchance *= 1.3f;\n\t\t}\n\t\tif (user.Team.ActiveBattlers.FindIndex(p => p.Ability == PBEAbility.VictoryStar) != -1)\n\t\t{\n\t\t\tchance *= 1.1f;\n\t\t}\n\t\tif (user.Ability == PBEAbility.Hustle && mData.Category == PBEMoveCategory.Physical)\n\t\t{\n\t\t\tchance *= 0.8f;\n\t\t}\n\t\tif (!user.HasCancellingAbility() && ShouldDoWeatherEffects())\n\t\t{\n\t\t\tif (Weather == PBEWeather.Sandstorm && target.Ability == PBEAbility.SandVeil)\n\t\t\t{\n\t\t\t\tchance *= 0.8f;\n\t\t\t}\n\t\t\tif (Weather == PBEWeather.Hailstorm && target.Ability == PBEAbility.SnowCloak)\n\t\t\t{\n\t\t\t\tchance *= 0.8f;\n\t\t\t}\n\t\t}\n\t\tif (target.Item == PBEItem.BrightPowder)\n\t\t{\n\t\t\tchance *= 0.9f;\n\t\t}\n\t\tif (target.Item == PBEItem.LaxIncense)\n\t\t{\n\t\t\tchance *= 0.9f;\n\t\t}\n\t\tif (user.Item == PBEItem.WideLens)\n\t\t{\n\t\t\tchance *= 1.1f;\n\t\t}\n\t\tif (target.Ability == PBEAbility.TangledFeet && target.Status2.HasFlag(PBEStatus2.Confused) && !user.HasCancellingAbility())\n\t\t{\n\t\t\tchance *= 0.5f;\n\t\t}\n\troll:\n\t\tif (_rand.RandomBool((int)chance, 100))\n\t\t{\n\t\t\treturn false;\n\t\t}\n\tmiss:\n\t\tBroadcastMoveResult(user, target, PBEResult.Missed);\n\t\treturn true;\n\t}\n\tprivate bool AttackTypeCheck(PBEBattlePokemon user, PBEBattlePokemon target, PBEType moveType, out PBEResult result, out float damageMultiplier)\n\t{\n\t\tresult = PBETypeEffectiveness.IsAffectedByAttack(user, target, moveType, out damageMultiplier);\n\t\tif (result == PBEResult.Ineffective_Ability)\n\t\t{\n\t\t\tBroadcastAbility(target, user, target.Ability, PBEAbilityAction.Damage);\n\t\t}\n\t\tif (result != PBEResult.NotVeryEffective_Type && result != PBEResult.Success && result != PBEResult.SuperEffective_Type)\n\t\t{\n\t\t\tBroadcastMoveResult(user, target, result);\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\tprivate bool CritCheck(PBEBattlePokemon user, PBEBattlePokemon target, IPBEMoveData mData)\n\t{\n\t\tif (((target.Ability == PBEAbility.BattleArmor || target.Ability == PBEAbility.ShellArmor) && !user.HasCancellingAbility())\n\t\t\t|| target.Team.TeamStatus.HasFlag(PBETeamStatus.LuckyChant))\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\tif (mData.Flags.HasFlag(PBEMoveFlag.AlwaysCrit))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t\tbyte stage = 0;\n\t\tif (user.Status2.HasFlag(PBEStatus2.Pumped))\n\t\t{\n\t\t\tstage += 2;\n\t\t}\n\t\tif (user.OriginalSpecies == PBESpecies.Chansey && user.Item == PBEItem.LuckyPunch)\n\t\t{\n\t\t\tstage += 2;\n\t\t}\n\t\tif (user.OriginalSpecies == PBESpecies.Farfetchd && user.Item == PBEItem.Stick)\n\t\t{\n\t\t\tstage += 2;\n\t\t}\n\t\tif (mData.Flags.HasFlag(PBEMoveFlag.HighCritChance))\n\t\t{\n\t\t\tstage += 1;\n\t\t}\n\t\tif (user.Ability == PBEAbility.SuperLuck)\n\t\t{\n\t\t\tstage += 1;\n\t\t}\n\t\tif (user.Item == PBEItem.RazorClaw || user.Item == PBEItem.ScopeLens)\n\t\t{\n\t\t\tstage += 1;\n\t\t}\n\t\tfloat chance;\n\t\tswitch (stage)\n\t\t{\n\t\t\tcase 0: chance = 6.25f; break;\n\t\t\tcase 1: chance = 12.5f; break;\n\t\t\tcase 2: chance = 25; break;\n\t\t\tcase 3: chance = 33.3f; break;\n\t\t\tdefault: chance = 50; break;\n\t\t}\n\t\treturn _rand.RandomBool((int)(chance * 100), 100 * 100);\n\t}\n\tprivate void TrySetLoser(PBEBattlePokemon pkmn)\n\t{\n\t\tif (BattleResult is null && pkmn.Team.NumConsciousPkmn == 0)\n\t\t{\n\t\t\tBattleResult = pkmn.Team.Id == 0 ? PBEBattleResult.Team1Win : PBEBattleResult.Team0Win;\n\t\t}\n\t}\n\tprivate void SetEscaped(PBEBattlePokemon pkmn)\n\t{\n\t\tBattleResult = pkmn.IsWild ? PBEBattleResult.WildFlee : PBEBattleResult.WildEscape;\n\t}\n\tprivate bool FaintCheck(PBEBattlePokemon pkmn)\n\t{\n\t\tif (pkmn.HP == 0)\n\t\t{\n\t\t\t_turnOrder.Remove(pkmn);\n\t\t\tActiveBattlers.Remove(pkmn);\n\t\t\tPBEFieldPosition oldPos = pkmn.FieldPosition;\n\t\t\tpkmn.ClearForFaint();\n\t\t\tBroadcastPkmnFainted(pkmn, oldPos);\n\t\t\tRemoveInfatuationsAndLockOns(pkmn);\n\t\t\tCalcEXP(pkmn);\n\t\t\tpkmn.EXPPokemon.Clear();\n\t\t\tpkmn.Team.MonFaintedThisTurn = true;\n\t\t\tTrySetLoser(pkmn);\n\t\t\tCastformCherrimCheckAll();\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\tprivate bool GetManipulableChance(PBEBattlePokemon pkmn, int chance)\n\t{\n\t\t// TODO: Does the Rainbow affect abilities activating, such as CuteCharm/Static, Healer/ShedSkin, etc, and which side of the field would they activate from? Victim?\n\t\t// TODO: If it does affect abilities, does it affect Effect Spore? It uses its own weird rng\n\t\tif (pkmn.Ability == PBEAbility.SereneGrace)\n\t\t{\n\t\t\tchance *= 2;\n\t\t}\n\t\treturn _rand.RandomBool(chance, 100);\n\t}\n\n\tprivate void ActivateAbility(PBEBattlePokemon pkmn, bool switchIn)\n\t{\n\t\tif (!switchIn)\n\t\t{\n\t\t\tCastformCherrimCheck(pkmn); // Switch-Ins check this after all Pokémon are sent out\n\t\t}\n\t\tAntiStatusAbilityCheck(pkmn);\n\t\tswitch (pkmn.Ability)\n\t\t{\n\t\t\tcase PBEAbility.AirLock:\n\t\t\tcase PBEAbility.CloudNine:\n\t\t\t{\n\t\t\t\tif (switchIn)\n\t\t\t\t{\n\t\t\t\t\tBroadcastAbility(pkmn, pkmn, pkmn.Ability, PBEAbilityAction.Weather);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tCastformCherrimCheckAll();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEAbility.Anticipation:\n\t\t\t{\n\t\t\t\tforeach (PBEBattlePokemon opponent in pkmn.Team.OpposingTeam.ActiveBattlers)\n\t\t\t\t{\n\t\t\t\t\tforeach (PBEBattleMoveset.PBEBattleMovesetSlot moveSlot in opponent.Moves)\n\t\t\t\t\t{\n\t\t\t\t\t\tPBEMove move = moveSlot.Move;\n\t\t\t\t\t\tif (move != PBEMove.None)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tIPBEMoveData mData = PBEDataProvider.Instance.GetMoveData(move);\n\t\t\t\t\t\t\tif (mData.Category != PBEMoveCategory.Status)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfloat d = PBETypeEffectiveness.GetEffectiveness(mData.Type, pkmn);\n\t\t\t\t\t\t\t\tif (d > 1)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tBroadcastAbility(pkmn, pkmn, PBEAbility.Anticipation, PBEAbilityAction.Announced);\n\t\t\t\t\t\t\t\t\tgoto bottomAnticipation;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tbottomAnticipation:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEAbility.Download:\n\t\t\t{\n\t\t\t\tList<PBEBattlePokemon> oppActive = pkmn.Team.OpposingTeam.ActiveBattlers;\n\t\t\t\tif (oppActive.Count != 0)\n\t\t\t\t{\n\t\t\t\t\tPBEStat stat = oppActive.Average(p => p.Defense * GetStatChangeModifier(p.DefenseChange, false))\n\t\t\t\t\t\t\t\t< oppActive.Average(p => p.SpDefense * GetStatChangeModifier(p.SpDefenseChange, false))\n\t\t\t\t\t\t\t\t? PBEStat.Attack : PBEStat.SpAttack;\n\t\t\t\t\tif (pkmn.GetStatChange(stat) < Settings.MaxStatChange)\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastAbility(pkmn, pkmn, PBEAbility.Download, PBEAbilityAction.Stats);\n\t\t\t\t\t\tApplyStatChangeIfPossible(pkmn, pkmn, stat, +1);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEAbility.Drizzle:\n\t\t\t{\n\t\t\t\tif (Weather != PBEWeather.Rain || WeatherCounter != 0)\n\t\t\t\t{\n\t\t\t\t\tBroadcastAbility(pkmn, pkmn, pkmn.Ability, PBEAbilityAction.Weather);\n\t\t\t\t\tSetWeather(PBEWeather.Rain, 0, switchIn);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEAbility.Drought:\n\t\t\t{\n\t\t\t\tif (Weather != PBEWeather.HarshSunlight || WeatherCounter != 0)\n\t\t\t\t{\n\t\t\t\t\tBroadcastAbility(pkmn, pkmn, pkmn.Ability, PBEAbilityAction.Weather);\n\t\t\t\t\tSetWeather(PBEWeather.HarshSunlight, 0, switchIn);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEAbility.Imposter:\n\t\t\t{\n\t\t\t\tPBEFieldPosition targetPos = GetPositionAcross(BattleFormat, pkmn.FieldPosition);\n\t\t\t\tif (pkmn.Team.OpposingTeam.TryGetPokemon(targetPos, out PBEBattlePokemon? target)\n\t\t\t\t\t&& target.IsTransformPossible(pkmn) == PBEResult.Success)\n\t\t\t\t{\n\t\t\t\t\tBroadcastAbility(pkmn, target, pkmn.Ability, PBEAbilityAction.ChangedAppearance);\n\t\t\t\t\tDoTransform(pkmn, target);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEAbility.Intimidate:\n\t\t\t{\n\t\t\t\t// Verified: Do not announce if the positions are empty\n\t\t\t\tIReadOnlyList<PBEBattlePokemon> targets = GetRuntimeSurrounding(pkmn, false, true);\n\t\t\t\tif (targets.Count > 0)\n\t\t\t\t{\n\t\t\t\t\t// Verified: Announce even if nobody is lowered (due to Substitute, Minimized Attack, or Ability)\n\t\t\t\t\tBroadcastAbility(pkmn, pkmn, PBEAbility.Intimidate, PBEAbilityAction.Stats);\n\t\t\t\t\tforeach (PBEBattlePokemon target in GetActingOrder(targets, true))\n\t\t\t\t\t{\n\t\t\t\t\t\tApplyStatChangeIfPossible(pkmn, target, PBEStat.Attack, -1); // Verified: Substitute, Minimized Attack, and Ability are announced\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEAbility.MoldBreaker:\n\t\t\tcase PBEAbility.Teravolt:\n\t\t\tcase PBEAbility.Turboblaze:\n\t\t\t{\n\t\t\t\tBroadcastAbility(pkmn, pkmn, pkmn.Ability, PBEAbilityAction.Announced);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEAbility.SandStream:\n\t\t\t{\n\t\t\t\tif (Weather != PBEWeather.Sandstorm || WeatherCounter != 0)\n\t\t\t\t{\n\t\t\t\t\tBroadcastAbility(pkmn, pkmn, pkmn.Ability, PBEAbilityAction.Weather);\n\t\t\t\t\tSetWeather(PBEWeather.Sandstorm, 0, switchIn);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEAbility.SlowStart:\n\t\t\t{\n\t\t\t\tpkmn.SlowStart_HinderTurnsLeft = 5;\n\t\t\t\tBroadcastAbility(pkmn, pkmn, PBEAbility.SlowStart, PBEAbilityAction.Announced);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEAbility.SnowWarning:\n\t\t\t{\n\t\t\t\tif (Weather != PBEWeather.Hailstorm || WeatherCounter != 0)\n\t\t\t\t{\n\t\t\t\t\tBroadcastAbility(pkmn, pkmn, pkmn.Ability, PBEAbilityAction.Weather);\n\t\t\t\t\tSetWeather(PBEWeather.Hailstorm, 0, switchIn);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tprivate void CastformCherrimCheckAll()\n\t{\n\t\tCastformCherrimCheck(GetActingOrder(ActiveBattlers, true));\n\t}\n\tprivate void CastformCherrimCheck(IEnumerable<PBEBattlePokemon> order)\n\t{\n\t\tforeach (PBEBattlePokemon pkmn in order)\n\t\t{\n\t\t\tCastformCherrimCheck(pkmn);\n\t\t}\n\t}\n\tprivate void CastformCherrimCheck(PBEBattlePokemon pkmn)\n\t{\n\t\tif (pkmn.HP == 0)\n\t\t{\n\t\t\treturn; // #344 - Castform/Cherrim can change form while fainting from Explosion, if they kill someone with a weather-blocking ability\n\t\t}\n\t\tif (pkmn.Species == PBESpecies.Castform && pkmn.OriginalSpecies == PBESpecies.Castform)\n\t\t{\n\t\t\tPBEForm newForm = PBEForm.Castform;\n\t\t\tif (pkmn.Ability == PBEAbility.Forecast && ShouldDoWeatherEffects())\n\t\t\t{\n\t\t\t\tswitch (Weather)\n\t\t\t\t{\n\t\t\t\t\tcase PBEWeather.Hailstorm: newForm = PBEForm.Castform_Snowy; break;\n\t\t\t\t\tcase PBEWeather.HarshSunlight: newForm = PBEForm.Castform_Sunny; break;\n\t\t\t\t\tcase PBEWeather.Rain: newForm = PBEForm.Castform_Rainy; break;\n\t\t\t\t}\n\t\t\t\tif (newForm != pkmn.Form)\n\t\t\t\t{\n\t\t\t\t\tBroadcastAbility(pkmn, pkmn, pkmn.Ability, PBEAbilityAction.ChangedAppearance);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (newForm != pkmn.Form)\n\t\t\t{\n\t\t\t\tBroadcastPkmnFormChanged(pkmn, newForm, pkmn.Ability, pkmn.KnownAbility, false);\n\t\t\t}\n\t\t}\n\t\telse if (pkmn.Species == PBESpecies.Cherrim && pkmn.OriginalSpecies == PBESpecies.Cherrim)\n\t\t{\n\t\t\tPBEForm newForm = PBEForm.Cherrim;\n\t\t\tif (pkmn.Ability == PBEAbility.FlowerGift && ShouldDoWeatherEffects())\n\t\t\t{\n\t\t\t\tif (Weather == PBEWeather.HarshSunlight)\n\t\t\t\t{\n\t\t\t\t\tnewForm = PBEForm.Cherrim_Sunshine;\n\t\t\t\t}\n\t\t\t\tif (newForm != pkmn.Form)\n\t\t\t\t{\n\t\t\t\t\tBroadcastAbility(pkmn, pkmn, pkmn.Ability, PBEAbilityAction.ChangedAppearance);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (newForm != pkmn.Form)\n\t\t\t{\n\t\t\t\tBroadcastPkmnFormChanged(pkmn, newForm, pkmn.Ability, pkmn.KnownAbility, false);\n\t\t\t}\n\t\t}\n\t}\n\tprivate void ShayminCheck(PBEBattlePokemon pkmn)\n\t{\n\t\t// If a Shaymin_Sky is given MagmaArmor and then Frozen, it will change to Shaymin and obtain Shaymin's ability, therefore losing MagmaArmor and as a result will not be cured of its Frozen status.\n\t\tif (pkmn.Species == PBESpecies.Shaymin && pkmn.OriginalSpecies == PBESpecies.Shaymin && pkmn.Form == PBEForm.Shaymin_Sky && pkmn.Status1 == PBEStatus1.Frozen)\n\t\t{\n\t\t\tconst PBEForm newForm = PBEForm.Shaymin;\n\t\t\tPBEAbility newAbility = PBEDataProvider.Instance.GetPokemonData(PBESpecies.Shaymin, newForm).Abilities[0];\n\t\t\tBroadcastPkmnFormChanged(pkmn, newForm, newAbility, PBEAbility.MAX, true);\n\t\t\tActivateAbility(pkmn, false);\n\t\t}\n\t}\n\tprivate void IllusionBreak(PBEBattlePokemon pkmn, PBEBattlePokemon breaker)\n\t{\n\t\tif (pkmn.Status2.HasFlag(PBEStatus2.Disguised))\n\t\t{\n\t\t\tpkmn.KnownGender = pkmn.Gender;\n\t\t\tpkmn.KnownCaughtBall = pkmn.CaughtBall;\n\t\t\tpkmn.KnownNickname = pkmn.Nickname;\n\t\t\tpkmn.KnownShiny = pkmn.Shiny;\n\t\t\tpkmn.KnownSpecies = pkmn.Species;\n\t\t\tpkmn.KnownType1 = pkmn.Type1;\n\t\t\tpkmn.KnownType2 = pkmn.Type2;\n\t\t\tBroadcastIllusion(pkmn);\n\t\t\tBroadcastAbility(pkmn, breaker, PBEAbility.Illusion, PBEAbilityAction.ChangedAppearance);\n\t\t\tBroadcastStatus2(pkmn, breaker, PBEStatus2.Disguised, PBEStatusAction.Ended);\n\t\t}\n\t}\n\tprivate void AntiStatusAbilityCheck(IEnumerable<PBEBattlePokemon> order)\n\t{\n\t\tforeach (PBEBattlePokemon pkmn in order)\n\t\t{\n\t\t\tAntiStatusAbilityCheck(pkmn);\n\t\t}\n\t}\n\tprivate void AntiStatusAbilityCheck(PBEBattlePokemon pkmn)\n\t{\n\t\tswitch (pkmn.Ability)\n\t\t{\n\t\t\tcase PBEAbility.Immunity:\n\t\t\t{\n\t\t\t\tif (pkmn.Status1 == PBEStatus1.BadlyPoisoned || pkmn.Status1 == PBEStatus1.Poisoned)\n\t\t\t\t{\n\t\t\t\t\tPBEStatus1 oldStatus1 = pkmn.Status1;\n\t\t\t\t\tpkmn.Status1 = PBEStatus1.None;\n\t\t\t\t\tpkmn.Status1Counter = 0;\n\t\t\t\t\tBroadcastAbility(pkmn, pkmn, pkmn.Ability, PBEAbilityAction.ChangedStatus);\n\t\t\t\t\tBroadcastStatus1(pkmn, pkmn, oldStatus1, PBEStatusAction.Cleared);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEAbility.Insomnia:\n\t\t\tcase PBEAbility.VitalSpirit:\n\t\t\t{\n\t\t\t\tif (pkmn.Status1 == PBEStatus1.Asleep)\n\t\t\t\t{\n\t\t\t\t\tpkmn.Status1 = PBEStatus1.None;\n\t\t\t\t\tpkmn.Status1Counter = 0;\n\t\t\t\t\tpkmn.SleepTurns = 0;\n\t\t\t\t\tBroadcastAbility(pkmn, pkmn, pkmn.Ability, PBEAbilityAction.ChangedStatus);\n\t\t\t\t\tBroadcastStatus1(pkmn, pkmn, PBEStatus1.Asleep, PBEStatusAction.Cleared);\n\t\t\t\t\tCureNightmare(pkmn, pkmn);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEAbility.Limber:\n\t\t\t{\n\t\t\t\tif (pkmn.Status1 == PBEStatus1.Paralyzed)\n\t\t\t\t{\n\t\t\t\t\tpkmn.Status1 = PBEStatus1.None;\n\t\t\t\t\tBroadcastAbility(pkmn, pkmn, pkmn.Ability, PBEAbilityAction.ChangedStatus);\n\t\t\t\t\tBroadcastStatus1(pkmn, pkmn, PBEStatus1.Paralyzed, PBEStatusAction.Cleared);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEAbility.MagmaArmor:\n\t\t\t{\n\t\t\t\tif (pkmn.Status1 == PBEStatus1.Frozen)\n\t\t\t\t{\n\t\t\t\t\tpkmn.Status1 = PBEStatus1.None;\n\t\t\t\t\tBroadcastAbility(pkmn, pkmn, pkmn.Ability, PBEAbilityAction.ChangedStatus);\n\t\t\t\t\tBroadcastStatus1(pkmn, pkmn, PBEStatus1.Frozen, PBEStatusAction.Cleared);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEAbility.Oblivious:\n\t\t\t{\n\t\t\t\tif (pkmn.Status2.HasFlag(PBEStatus2.Infatuated))\n\t\t\t\t{\n\t\t\t\t\tpkmn.InfatuatedWithPokemon = null;\n\t\t\t\t\tBroadcastAbility(pkmn, pkmn, pkmn.Ability, PBEAbilityAction.ChangedStatus);\n\t\t\t\t\tBroadcastStatus2(pkmn, pkmn, PBEStatus2.Infatuated, PBEStatusAction.Cleared);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEAbility.OwnTempo:\n\t\t\t{\n\t\t\t\tif (pkmn.Status2.HasFlag(PBEStatus2.Confused))\n\t\t\t\t{\n\t\t\t\t\tpkmn.ConfusionCounter = 0;\n\t\t\t\t\tpkmn.ConfusionTurns = 0;\n\t\t\t\t\tBroadcastAbility(pkmn, pkmn, pkmn.Ability, PBEAbilityAction.ChangedStatus);\n\t\t\t\t\tBroadcastStatus2(pkmn, pkmn, PBEStatus2.Confused, PBEStatusAction.Cleared);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEAbility.WaterVeil:\n\t\t\t{\n\t\t\t\tif (pkmn.Status1 == PBEStatus1.Burned)\n\t\t\t\t{\n\t\t\t\t\tpkmn.Status1 = PBEStatus1.None;\n\t\t\t\t\tBroadcastAbility(pkmn, pkmn, pkmn.Ability, PBEAbilityAction.ChangedStatus);\n\t\t\t\t\tBroadcastStatus1(pkmn, pkmn, PBEStatus1.Burned, PBEStatusAction.Cleared);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tprivate void CauseConfusion(PBEBattlePokemon target, PBEBattlePokemon other)\n\t{\n\t\ttarget.ConfusionCounter = 0;\n\t\ttarget.ConfusionTurns = (byte)_rand.RandomInt(Settings.ConfusionMinTurns, Settings.ConfusionMaxTurns);\n\t\tBroadcastStatus2(target, other, PBEStatus2.Confused, PBEStatusAction.Added);\n\t\tAntiStatusAbilityCheck(target);\n\t}\n\tprivate void CauseInfatuation(PBEBattlePokemon target, PBEBattlePokemon other)\n\t{\n\t\ttarget.InfatuatedWithPokemon = other;\n\t\tBroadcastStatus2(target, other, PBEStatus2.Infatuated, PBEStatusAction.Added);\n\t\tif (target.Item == PBEItem.DestinyKnot && other.IsAttractionPossible(target) == PBEResult.Success)\n\t\t{\n\t\t\tBroadcastItem(target, other, PBEItem.DestinyKnot, PBEItemAction.Announced);\n\t\t\tother.InfatuatedWithPokemon = target;\n\t\t\tBroadcastStatus2(other, target, PBEStatus2.Infatuated, PBEStatusAction.Added);\n\t\t}\n\t\tAntiStatusAbilityCheck(target);\n\t}\n\t// TODO: Use & add packet handlers\n\tprivate void WhiteHerbCheck(PBEBattlePokemon pkmn)\n\t{\n\t\tif (pkmn.Item == PBEItem.WhiteHerb)\n\t\t{\n\t\t\tPBEStat[] negStats = pkmn.GetStatsLessThan(0);\n\t\t\tif (negStats.Length > 0)\n\t\t\t{\n\t\t\t\tforeach (PBEStat s in negStats)\n\t\t\t\t{\n\t\t\t\t\tpkmn.SetStatChange(s, 0);\n\t\t\t\t}\n\t\t\t\tBroadcastItem(pkmn, pkmn, PBEItem.WhiteHerb, PBEItemAction.Consumed);\n\t\t\t}\n\t\t}\n\t}\n\tprivate bool PowerHerbCheck(PBEBattlePokemon pkmn)\n\t{\n\t\tif (pkmn.Item == PBEItem.PowerHerb)\n\t\t{\n\t\t\tBroadcastItem(pkmn, pkmn, PBEItem.PowerHerb, PBEItemAction.Consumed);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\tprivate void LowHPBerryCheck(IEnumerable<PBEBattlePokemon> order, PBEBattlePokemon? forcedToEatBy = null)\n\t{\n\t\tforeach (PBEBattlePokemon pkmn in order)\n\t\t{\n\t\t\tLowHPBerryCheck(pkmn, forcedToEatBy: forcedToEatBy);\n\t\t}\n\t}\n\tprivate void LowHPBerryCheck(PBEBattlePokemon pkmn, PBEBattlePokemon? forcedToEatBy = null)\n\t{\n\t\tforcedToEatBy ??= pkmn;\n\t\tvoid DoConfuseBerry(PBEFlavor flavor)\n\t\t{\n\t\t\tBroadcastItem(pkmn, forcedToEatBy, pkmn.Item, PBEItemAction.Consumed);\n\t\t\tHealDamage(pkmn, pkmn.MaxHP / 8);\n\t\t\t// Verified: Ignores Safeguard & Substitute, but not Own Tempo\n\t\t\t// Mold Breaker etc actually affect whether Own Tempo is ignored, which is what forcedToEatBy is for\n\t\t\t// I verified each of the times the Pokémon eats to check if Mold Breaker affected the outcome\n\t\t\tif (pkmn.Nature.GetRelationshipToFlavor(flavor) < 0 && pkmn.IsConfusionPossible(forcedToEatBy, ignoreSubstitute: true, ignoreSafeguard: true) == PBEResult.Success)\n\t\t\t{\n\t\t\t\tCauseConfusion(pkmn, forcedToEatBy);\n\t\t\t}\n\t\t}\n\t\tvoid DoHealItem(int hp)\n\t\t{\n\t\t\tBroadcastItem(pkmn, forcedToEatBy, pkmn.Item, PBEItemAction.Consumed);\n\t\t\tHealDamage(pkmn, hp);\n\t\t}\n\t\tvoid DoStatItem(PBEStat stat, int change)\n\t\t{\n\t\t\t// Verified: Mold Breaker affects Contrary/Simple here, unlike with Belly Drum\n\t\t\tif (pkmn.IsStatChangePossible(stat, forcedToEatBy, change, out sbyte oldValue, out sbyte newValue, ignoreSubstitute: true) == PBEResult.Success)\n\t\t\t{\n\t\t\t\tBroadcastItem(pkmn, forcedToEatBy, pkmn.Item, PBEItemAction.Consumed);\n\t\t\t\tSetStatAndBroadcast(pkmn, stat, oldValue, newValue);\n\t\t\t}\n\t\t}\n\n\t\tif (pkmn.HP <= pkmn.MaxHP / 4)\n\t\t{\n\t\t\tswitch (pkmn.Item)\n\t\t\t{\n\t\t\t\tcase PBEItem.ApicotBerry: DoStatItem(PBEStat.SpDefense, +1); break;\n\t\t\t\tcase PBEItem.GanlonBerry: DoStatItem(PBEStat.Defense, +1); break;\n\t\t\t\tcase PBEItem.LiechiBerry: DoStatItem(PBEStat.Attack, +1); break;\n\t\t\t\tcase PBEItem.PetayaBerry: DoStatItem(PBEStat.SpAttack, +1); break;\n\t\t\t\tcase PBEItem.SalacBerry: DoStatItem(PBEStat.Speed, +1); break;\n\t\t\t\tcase PBEItem.StarfBerry:\n\t\t\t\t{\n\t\t\t\t\t// Verified: Starf Berry does not activate for Accuracy or Evasion, or if all other stats are maximized\n\t\t\t\t\tList<PBEStat> statsThatCanGoUp = PBEDataUtils.StarfBerryStats.FindAll(s => pkmn.GetStatChange(s) < Settings.MaxStatChange);\n\t\t\t\t\tif (statsThatCanGoUp.Count > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tDoStatItem(_rand.RandomElement(statsThatCanGoUp), +2);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (pkmn.HP <= pkmn.MaxHP / 2)\n\t\t{\n\t\t\tswitch (pkmn.Item)\n\t\t\t{\n\t\t\t\tcase PBEItem.AguavBerry: DoConfuseBerry(PBEFlavor.Bitter); break;\n\t\t\t\tcase PBEItem.BerryJuice: DoHealItem(20); break;\n\t\t\t\tcase PBEItem.FigyBerry: DoConfuseBerry(PBEFlavor.Spicy); break;\n\t\t\t\tcase PBEItem.IapapaBerry: DoConfuseBerry(PBEFlavor.Sour); break;\n\t\t\t\tcase PBEItem.MagoBerry: DoConfuseBerry(PBEFlavor.Sweet); break;\n\t\t\t\tcase PBEItem.OranBerry: DoHealItem(10); break;\n\t\t\t\tcase PBEItem.SitrusBerry: DoHealItem(pkmn.MaxHP / 4); break;\n\t\t\t\tcase PBEItem.WikiBerry: DoConfuseBerry(PBEFlavor.Dry); break;\n\t\t\t}\n\t\t}\n\t}\n\tprivate void SetAbility(PBEBattlePokemon user, PBEBattlePokemon target, PBEAbility ability)\n\t{\n\t\t// This func assumes new ability is different from current\n\t\tPBEAbility oldAbility = target.Ability;\n\t\tBroadcastAbilityReplaced(target, ability);\n\n\t\tswitch (oldAbility)\n\t\t{\n\t\t\tcase PBEAbility.Illusion:\n\t\t\t{\n\t\t\t\tIllusionBreak(target, user);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEAbility.SlowStart:\n\t\t\t{\n\t\t\t\ttarget.SlowStart_HinderTurnsLeft = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEAbility.SpeedBoost:\n\t\t\t{\n\t\t\t\ttarget.SpeedBoost_AbleToSpeedBoostThisTurn = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tActivateAbility(target, false);\n\t}\n\tprivate void SetWeather(PBEWeather weather, byte weatherCounter, bool switchIn)\n\t{\n\t\tWeather = weather;\n\t\tWeatherCounter = weatherCounter;\n\t\tBroadcastWeather(Weather, PBEWeatherAction.Added);\n\t\tif (!switchIn)\n\t\t{\n\t\t\tCastformCherrimCheckAll(); // Switch-Ins check this after all Pokémon are sent out\n\t\t}\n\t}\n\n\tprivate void RecordExecutedMove(PBEBattlePokemon user, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tuser.HasUsedMoveThisTurn = true;\n\t\t// Doesn't care if there is a Choice Locked move already. As long as the user knows it, it will become locked. (Metronome calling a move the user knows, Ditto transforming into someone else with transform)\n\t\tif ((user.Item == PBEItem.ChoiceBand || user.Item == PBEItem.ChoiceScarf || user.Item == PBEItem.ChoiceSpecs) && user.Moves.Contains(move))\n\t\t{\n\t\t\tBroadcastMoveLock_ChoiceItem(user, move);\n\t\t}\n\t\tif (mData.Effect == PBEMoveEffect.Minimize)\n\t\t{\n\t\t\tuser.Minimize_Used = true;\n\t\t}\n\t}\n\tprivate void PPReduce(PBEBattlePokemon pkmn, PBEMove move)\n\t{\n\t\tif (!_calledFromOtherMove)\n\t\t{\n\t\t\tconst int amountToReduce = 1;\n\t\t\t// TODO: If target is not self and has pressure\n\t\t\tPBEBattleMoveset.PBEBattleMovesetSlot slot = pkmn.Moves[move]!;\n\t\t\tint oldPP = slot.PP;\n\t\t\tint newPP = Math.Max(0, oldPP - amountToReduce);\n\t\t\tint amountReduced = oldPP - newPP;\n\t\t\tslot.PP = newPP;\n\t\t\tpkmn.UpdateKnownPP(move, amountReduced);\n\t\t\tBroadcastMovePPChanged(pkmn, move, amountReduced);\n\t\t}\n\t}\n\n\tprivate static void ApplyBigRoot(PBEBattlePokemon pkmn, ref int restoreAmt)\n\t{\n\t\tif (pkmn.Item == PBEItem.BigRoot)\n\t\t{\n\t\t\trestoreAmt += (int)(restoreAmt * 0.3);\n\t\t}\n\t}\n\tprivate void CureNightmare(PBEBattlePokemon wakingUp, PBEBattlePokemon pokemon2)\n\t{\n\t\tif (wakingUp.Status2.HasFlag(PBEStatus2.Nightmare))\n\t\t{\n\t\t\tBroadcastStatus2(wakingUp, pokemon2, PBEStatus2.Nightmare, PBEStatusAction.Ended);\n\t\t}\n\t}\n\tprivate void SetSleepTurns(PBEBattlePokemon pkmn, int minTurns, int maxTurns)\n\t{\n\t\tpkmn.SleepTurns = (byte)(_rand.RandomInt(minTurns, maxTurns) / (pkmn.Ability == PBEAbility.EarlyBird ? 2 : 1));\n\t}\n\tprivate void DoTransform(PBEBattlePokemon user, PBEBattlePokemon target)\n\t{\n\t\tuser.Transform(target);\n\t\tBroadcastTransform(user, target);\n\t\tBroadcastStatus2(user, target, PBEStatus2.Transformed, PBEStatusAction.Added);\n\t\t// Remove power trick (so it cannot be baton passed)\n\t\tif (user.Status2.HasFlag(PBEStatus2.PowerTrick))\n\t\t{\n\t\t\tBroadcastStatus2(user, user, PBEStatus2.PowerTrick, PBEStatusAction.Ended);\n\t\t}\n\t\tif (!user.Moves.Contains(user.ChoiceLockedMove))\n\t\t{\n\t\t\tBroadcastMoveLock_ChoiceItem(user, PBEMove.None);\n\t\t}\n\t}\n\tprivate PBEResult ApplyStatus1IfPossible(PBEBattlePokemon user, PBEBattlePokemon target, PBEStatus1 status, bool broadcastUnsuccessful)\n\t{\n\t\tPBEResult result;\n\t\tswitch (status)\n\t\t{\n\t\t\tcase PBEStatus1.Asleep: result = target.IsSleepPossible(user); break;\n\t\t\tcase PBEStatus1.BadlyPoisoned:\n\t\t\tcase PBEStatus1.Poisoned: result = target.IsPoisonPossible(user); break;\n\t\t\tcase PBEStatus1.Burned: result = target.IsBurnPossible(user); break;\n\t\t\tcase PBEStatus1.Frozen: result = target.IsFreezePossible(user); break;\n\t\t\tcase PBEStatus1.Paralyzed: result = target.IsParalysisPossible(user); break;\n\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(status));\n\t\t}\n\t\tif (result == PBEResult.Success)\n\t\t{\n\t\t\ttarget.Status1 = status;\n\t\t\tif (status == PBEStatus1.BadlyPoisoned)\n\t\t\t{\n\t\t\t\ttarget.Status1Counter = 1;\n\t\t\t}\n\t\t\telse if (status == PBEStatus1.Asleep)\n\t\t\t{\n\t\t\t\tSetSleepTurns(target, Settings.SleepMinTurns, Settings.SleepMaxTurns);\n\t\t\t\ttarget.Status1Counter = 0;\n\t\t\t}\n\t\t\tBroadcastStatus1(target, user, status, PBEStatusAction.Added);\n\t\t\tShayminCheck(target);\n\t\t}\n\t\telse if (broadcastUnsuccessful)\n\t\t{\n\t\t\tif (result == PBEResult.Ineffective_Ability)\n\t\t\t{\n\t\t\t\tBroadcastAbility(target, user, target.Ability, PBEAbilityAction.PreventedStatus);\n\t\t\t}\n\t\t\tBroadcastMoveResult(user, target, result);\n\t\t}\n\t\treturn result;\n\t}\n\tprivate PBEResult ApplyStatus2IfPossible(PBEBattlePokemon user, PBEBattlePokemon target, PBEStatus2 status, bool broadcastUnsuccessful)\n\t{\n\t\tPBEResult result;\n\t\tswitch (status)\n\t\t{\n\t\t\tcase PBEStatus2.Confused:\n\t\t\t{\n\t\t\t\tresult = target.IsConfusionPossible(user);\n\t\t\t\tif (result == PBEResult.Success)\n\t\t\t\t{\n\t\t\t\t\tCauseConfusion(target, user);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEStatus2.Cursed:\n\t\t\t{\n\t\t\t\tif (!target.Status2.HasFlag(PBEStatus2.Cursed))\n\t\t\t\t{\n\t\t\t\t\tBroadcastStatus2(target, user, PBEStatus2.Cursed, PBEStatusAction.Added);\n\t\t\t\t\tDealDamage(user, user, user.MaxHP / 2);\n\t\t\t\t\tif (!FaintCheck(user))\n\t\t\t\t\t{\n\t\t\t\t\t\tLowHPBerryCheck(user);\n\t\t\t\t\t}\n\t\t\t\t\tresult = PBEResult.Success;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tresult = PBEResult.Ineffective_Status;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEStatus2.Flinching:\n\t\t\t{\n\t\t\t\tresult = target.IsFlinchPossible(user);\n\t\t\t\tif (result == PBEResult.Success)\n\t\t\t\t{\n\t\t\t\t\ttarget.Status2 |= PBEStatus2.Flinching; // No broadcast, not known\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEStatus2.HelpingHand:\n\t\t\t{\n\t\t\t\tif (!target.HasUsedMoveThisTurn)\n\t\t\t\t{\n\t\t\t\t\tBroadcastStatus2(target, user, PBEStatus2.HelpingHand, PBEStatusAction.Added);\n\t\t\t\t\tresult = PBEResult.Success;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tresult = PBEResult.InvalidConditions;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEStatus2.Identified:\n\t\t\t{\n\t\t\t\tif (!target.Status2.HasFlag(PBEStatus2.Identified))\n\t\t\t\t{\n\t\t\t\t\tBroadcastStatus2(target, user, PBEStatus2.Identified, PBEStatusAction.Added);\n\t\t\t\t\tresult = PBEResult.Success;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tresult = PBEResult.Ineffective_Status;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEStatus2.Infatuated:\n\t\t\t{\n\t\t\t\tresult = target.IsAttractionPossible(user);\n\t\t\t\tif (result == PBEResult.Success)\n\t\t\t\t{\n\t\t\t\t\tCauseInfatuation(target, user);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEStatus2.LeechSeed:\n\t\t\t{\n\t\t\t\tresult = target.IsLeechSeedPossible();\n\t\t\t\tif (result == PBEResult.Success)\n\t\t\t\t{\n\t\t\t\t\ttarget.SeededPosition = user.FieldPosition;\n\t\t\t\t\ttarget.SeededTeam = user.Team;\n\t\t\t\t\tBroadcastStatus2(target, user, PBEStatus2.LeechSeed, PBEStatusAction.Added);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEStatus2.LockOn:\n\t\t\t{\n\t\t\t\tif (!user.Status2.HasFlag(PBEStatus2.LockOn))\n\t\t\t\t{\n\t\t\t\t\tuser.LockOnPokemon = target;\n\t\t\t\t\tuser.LockOnTurns = 2;\n\t\t\t\t\tBroadcastStatus2(user, target, PBEStatus2.LockOn, PBEStatusAction.Added);\n\t\t\t\t\tresult = PBEResult.Success;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tresult = PBEResult.Ineffective_Status;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEStatus2.MagnetRise:\n\t\t\t{\n\t\t\t\tresult = target.IsMagnetRisePossible();\n\t\t\t\tif (result == PBEResult.Success)\n\t\t\t\t{\n\t\t\t\t\ttarget.MagnetRiseTurns = 5;\n\t\t\t\t\tBroadcastStatus2(target, user, PBEStatus2.MagnetRise, PBEStatusAction.Added);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEStatus2.MiracleEye:\n\t\t\t{\n\t\t\t\tif (!target.Status2.HasFlag(PBEStatus2.MiracleEye))\n\t\t\t\t{\n\t\t\t\t\tBroadcastStatus2(target, user, PBEStatus2.MiracleEye, PBEStatusAction.Added);\n\t\t\t\t\tresult = PBEResult.Success;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tresult = PBEResult.Ineffective_Status;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEStatus2.Nightmare:\n\t\t\t{\n\t\t\t\tif (target.Status1 == PBEStatus1.Asleep && !target.Status2.HasFlag(PBEStatus2.Nightmare))\n\t\t\t\t{\n\t\t\t\t\tBroadcastStatus2(target, user, PBEStatus2.Nightmare, PBEStatusAction.Added);\n\t\t\t\t\tresult = PBEResult.Success;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tresult = PBEResult.Ineffective_Status;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEStatus2.PowerTrick:\n\t\t\t{\n\t\t\t\ttarget.ApplyPowerTrickChange();\n\t\t\t\tBroadcastStatus2(target, user, PBEStatus2.PowerTrick, PBEStatusAction.Added);\n\t\t\t\tresult = PBEResult.Success;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEStatus2.Protected:\n\t\t\t{\n\t\t\t\t// TODO: If the user goes last, fail\n\t\t\t\tif (_rand.RandomBool(user.GetProtectionChance(), ushort.MaxValue))\n\t\t\t\t{\n\t\t\t\t\tuser.Protection_Used = true;\n\t\t\t\t\tBroadcastStatus2(user, user, PBEStatus2.Protected, PBEStatusAction.Added);\n\t\t\t\t\tresult = PBEResult.Success;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tresult = PBEResult.InvalidConditions;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEStatus2.Pumped:\n\t\t\t{\n\t\t\t\tif (!target.Status2.HasFlag(PBEStatus2.Pumped))\n\t\t\t\t{\n\t\t\t\t\tBroadcastStatus2(target, user, PBEStatus2.Pumped, PBEStatusAction.Added);\n\t\t\t\t\tresult = PBEResult.Success;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tresult = PBEResult.Ineffective_Status;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEStatus2.Substitute:\n\t\t\t{\n\t\t\t\tresult = target.IsSubstitutePossible();\n\t\t\t\tif (result == PBEResult.Success)\n\t\t\t\t{\n\t\t\t\t\tint hpRequired = target.MaxHP / 4;\n\t\t\t\t\tDealDamage(user, target, hpRequired);\n\t\t\t\t\tLowHPBerryCheck(target); // Verified: Berry is eaten between damage and substitute\n\t\t\t\t\ttarget.SubstituteHP = (ushort)hpRequired;\n\t\t\t\t\tBroadcastStatus2(target, user, PBEStatus2.Substitute, PBEStatusAction.Added);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEStatus2.Transformed:\n\t\t\t{\n\t\t\t\tresult = target.IsTransformPossible(user);\n\t\t\t\tif (result == PBEResult.Success)\n\t\t\t\t{\n\t\t\t\t\tDoTransform(user, target);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(status));\n\t\t}\n\t\tif (broadcastUnsuccessful && result != PBEResult.Success)\n\t\t{\n\t\t\tif (result == PBEResult.Ineffective_Ability)\n\t\t\t{\n\t\t\t\tBroadcastAbility(target, user, target.Ability, PBEAbilityAction.PreventedStatus);\n\t\t\t}\n\t\t\tBroadcastMoveResult(user, target, result);\n\t\t}\n\t\treturn result;\n\t}\n\tprivate void ApplyStatChangeIfPossible(PBEBattlePokemon user, PBEBattlePokemon target, PBEStat stat, int change, bool isSecondaryEffect = false)\n\t{\n\t\tPBEResult result = target.IsStatChangePossible(stat, user, change, out sbyte oldValue, out sbyte newValue);\n\t\tbool broadcast;\n\t\tif (result == PBEResult.Success)\n\t\t{\n\t\t\ttarget.SetStatChange(stat, newValue);\n\t\t\tbroadcast = true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (result == PBEResult.Ineffective_Ability)\n\t\t\t{\n\t\t\t\tif (!isSecondaryEffect)\n\t\t\t\t{\n\t\t\t\t\tBroadcastAbility(target, user, target.Ability, PBEAbilityAction.Stats);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (result == PBEResult.Ineffective_Substitute)\n\t\t\t{\n\t\t\t\tif (!isSecondaryEffect)\n\t\t\t\t{\n\t\t\t\t\tBroadcastMoveResult(user, target, PBEResult.Ineffective_Substitute);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Do not broadcast \"could not be lowered!\" for Mud-Slap, etc\n\t\t\tbroadcast = !isSecondaryEffect;\n\t\t}\n\t\tif (broadcast)\n\t\t{\n\t\t\tBroadcastPkmnStatChanged(target, stat, oldValue, newValue);\n\t\t}\n\t}\n\tprivate void SetStatAndBroadcast(PBEBattlePokemon pkmn, PBEStat stat, sbyte oldValue, sbyte newValue)\n\t{\n\t\tpkmn.SetStatChange(stat, newValue);\n\t\tBroadcastPkmnStatChanged(pkmn, stat, oldValue, newValue);\n\t}\n\n\tprivate static PBEPkmnAppearedInfo CreateSwitchInInfo(PBEBattlePokemon pkmn)\n\t{\n\t\tif (pkmn.Ability == PBEAbility.Illusion)\n\t\t{\n\t\t\tPBEBattlePokemon? p = pkmn.GetPkmnWouldDisguiseAs();\n\t\t\tif (p is not null)\n\t\t\t{\n\t\t\t\tpkmn.Status2 |= PBEStatus2.Disguised; // No broadcast, not known\n\t\t\t\tpkmn.KnownGender = p.Gender;\n\t\t\t\tpkmn.KnownCaughtBall = p.CaughtBall;\n\t\t\t\tpkmn.KnownNickname = p.Nickname;\n\t\t\t\tpkmn.KnownShiny = p.Shiny;\n\t\t\t\tpkmn.KnownSpecies = p.OriginalSpecies;\n\t\t\t\tpkmn.KnownForm = p.Form;\n\t\t\t\tIPBEPokemonData pData = PBEDataProvider.Instance.GetPokemonData(pkmn.KnownSpecies, pkmn.KnownForm);\n\t\t\t\tpkmn.KnownType1 = pData.Type1;\n\t\t\t\tpkmn.KnownType2 = pData.Type2;\n\t\t\t}\n\t\t}\n\t\treturn new PBEPkmnAppearedInfo(pkmn);\n\t}\n\tprivate void SwitchTwoPokemon(PBEBattlePokemon pkmnLeaving, PBEBattlePokemon pkmnComing, PBEBattlePokemon? forcedByPkmn = null)\n\t{\n\t\t_turnOrder.Remove(pkmnLeaving);\n\t\tActiveBattlers.Remove(pkmnLeaving);\n\t\tPBEFieldPosition pos = pkmnLeaving.FieldPosition;\n\t\tpkmnLeaving.ClearForSwitch();\n\t\tBroadcastPkmnSwitchOut(pkmnLeaving, pos, forcedByPkmn);\n\t\tRemoveInfatuationsAndLockOns(pkmnLeaving);\n\t\tpkmnComing.FieldPosition = pos;\n\t\tvar switches = new PBEPkmnAppearedInfo[] { CreateSwitchInInfo(pkmnComing) };\n\t\tPBETrainer.SwitchTwoPokemon(pkmnLeaving, pkmnComing); // Swap after Illusion\n\t\tActiveBattlers.Add(pkmnComing); // Add to active before broadcast\n\t\tBroadcastPkmnSwitchIn(pkmnComing.Trainer, switches, forcedByPkmn);\n\t\tif (forcedByPkmn is not null)\n\t\t{\n\t\t\tBroadcastDraggedOut(pkmnComing);\n\t\t}\n\t\tDoSwitchInEffects(new[] { pkmnComing }, forcedByPkmn);\n\t\tCastformCherrimCheckAll();\n\t}\n\tprivate void RemoveInfatuationsAndLockOns(PBEBattlePokemon pkmnLeaving)\n\t{\n\t\tforeach (PBEBattlePokemon pkmn in ActiveBattlers)\n\t\t{\n\t\t\tif (pkmn.Status2.HasFlag(PBEStatus2.Infatuated) && pkmn.InfatuatedWithPokemon == pkmnLeaving)\n\t\t\t{\n\t\t\t\tpkmn.InfatuatedWithPokemon = null;\n\t\t\t\tBroadcastStatus2(pkmn, pkmn, PBEStatus2.Infatuated, PBEStatusAction.Ended);\n\t\t\t}\n\t\t\tif (pkmn.Status2.HasFlag(PBEStatus2.LockOn) && pkmn.LockOnPokemon == pkmnLeaving)\n\t\t\t{\n\t\t\t\tpkmn.LockOnPokemon = null;\n\t\t\t\tpkmn.LockOnTurns = 0;\n\t\t\t\tBroadcastStatus2(pkmn, pkmn, PBEStatus2.LockOn, PBEStatusAction.Ended);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void SemiInvulnerableChargeMove(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData, PBETurnTarget requestedTargets, PBEStatus2 status2,\n\t\tAction<PBEBattlePokemon, ushort>? beforePostHit = null,\n\t\tAction<PBEBattlePokemon>? afterPostHit = null)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\ttop:\n\t\tif (user.Status2.HasFlag(status2))\n\t\t{\n\t\t\tBroadcastMoveLock_Temporary(user, PBEMove.None, PBETurnTarget.None);\n\t\t\tBroadcastStatus2(user, user, status2, PBEStatusAction.Ended);\n\t\t\tif (targets.Length == 0)\n\t\t\t{\n\t\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tBasicHit(user, targets, mData, beforePostHit: beforePostHit, afterPostHit: afterPostHit);\n\t\t\t}\n\t\t\tRecordExecutedMove(user, move, mData); // Should only count as the last used move if it finishes charging\n\t\t}\n\t\telse\n\t\t{\n\t\t\tPPReduce(user, move);\n\t\t\tBroadcastMoveLock_Temporary(user, move, requestedTargets);\n\t\t\tBroadcastStatus2(user, user, status2, PBEStatusAction.Added);\n\t\t\tif (PowerHerbCheck(user))\n\t\t\t{\n\t\t\t\tgoto top;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void Ef_TryForceStatus1(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData, PBEStatus1 status, bool thunderWave = false)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t{\n\t\t\t\tif (!MissCheck(user, target, mData))\n\t\t\t\t{\n\t\t\t\t\tif (thunderWave)\n\t\t\t\t\t{\n\t\t\t\t\t\tPBEResult result = PBETypeEffectiveness.ThunderWaveTypeCheck(user, target, move);\n\t\t\t\t\t\tif (result != PBEResult.Success)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tBroadcastMoveResult(user, target, result);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tApplyStatus1IfPossible(user, target, status, true);\n\t\t\t\t\tAntiStatusAbilityCheck(target);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t\treturn;\n\t}\n\tprivate void Ef_TryForceStatus2(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData, PBEStatus2 status)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t{\n\t\t\t\tif (!MissCheck(user, target, mData))\n\t\t\t\t{\n\t\t\t\t\tApplyStatus2IfPossible(user, target, status, true);\n\t\t\t\t\tAntiStatusAbilityCheck(target);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_TryForceBattleStatus(PBEBattlePokemon user, PBEMove move, IPBEMoveData mData, PBEBattleStatus status)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tswitch (status)\n\t\t{\n\t\t\tcase PBEBattleStatus.TrickRoom:\n\t\t\t{\n\t\t\t\tif (!BattleStatus.HasFlag(PBEBattleStatus.TrickRoom))\n\t\t\t\t{\n\t\t\t\t\tTrickRoomCount = 5;\n\t\t\t\t\tBroadcastBattleStatus(PBEBattleStatus.TrickRoom, PBEBattleStatusAction.Added);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tTrickRoomCount = 0;\n\t\t\t\t\tBroadcastBattleStatus(PBEBattleStatus.TrickRoom, PBEBattleStatusAction.Cleared);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(status));\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_TryForceTeamStatus(PBEBattlePokemon user, PBEMove move, IPBEMoveData mData, PBETeamStatus status)\n\t{\n\t\tPBEResult result;\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tswitch (status)\n\t\t{\n\t\t\tcase PBETeamStatus.LightScreen:\n\t\t\t{\n\t\t\t\tif (!user.Team.TeamStatus.HasFlag(PBETeamStatus.LightScreen))\n\t\t\t\t{\n\t\t\t\t\tuser.Team.LightScreenCount = (byte)(Settings.LightScreenTurns + (user.Item == PBEItem.LightClay ? Settings.LightClayTurnExtension : 0));\n\t\t\t\t\tBroadcastTeamStatus(user.Team, PBETeamStatus.LightScreen, PBETeamStatusAction.Added);\n\t\t\t\t\tresult = PBEResult.Success;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tresult = PBEResult.Ineffective_Status;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBETeamStatus.LuckyChant:\n\t\t\t{\n\t\t\t\tif (!user.Team.TeamStatus.HasFlag(PBETeamStatus.LuckyChant))\n\t\t\t\t{\n\t\t\t\t\tuser.Team.LuckyChantCount = 5;\n\t\t\t\t\tBroadcastTeamStatus(user.Team, PBETeamStatus.LuckyChant, PBETeamStatusAction.Added);\n\t\t\t\t\tresult = PBEResult.Success;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tresult = PBEResult.Ineffective_Status;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBETeamStatus.QuickGuard:\n\t\t\t{\n\t\t\t\tif (!user.Team.TeamStatus.HasFlag(PBETeamStatus.QuickGuard) && _rand.RandomBool(user.GetProtectionChance(), ushort.MaxValue))\n\t\t\t\t{\n\t\t\t\t\tuser.Protection_Used = true;\n\t\t\t\t\tBroadcastTeamStatus(user.Team, PBETeamStatus.QuickGuard, PBETeamStatusAction.Added);\n\t\t\t\t\tresult = PBEResult.Success;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tresult = PBEResult.Ineffective_Status;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBETeamStatus.Reflect:\n\t\t\t{\n\t\t\t\tif (!user.Team.TeamStatus.HasFlag(PBETeamStatus.Reflect))\n\t\t\t\t{\n\t\t\t\t\tuser.Team.ReflectCount = (byte)(Settings.ReflectTurns + (user.Item == PBEItem.LightClay ? Settings.LightClayTurnExtension : 0));\n\t\t\t\t\tBroadcastTeamStatus(user.Team, PBETeamStatus.Reflect, PBETeamStatusAction.Added);\n\t\t\t\t\tresult = PBEResult.Success;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tresult = PBEResult.Ineffective_Status;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBETeamStatus.Safeguard:\n\t\t\t{\n\t\t\t\tif (!user.Team.TeamStatus.HasFlag(PBETeamStatus.Safeguard))\n\t\t\t\t{\n\t\t\t\t\tuser.Team.SafeguardCount = 5;\n\t\t\t\t\tBroadcastTeamStatus(user.Team, PBETeamStatus.Safeguard, PBETeamStatusAction.Added);\n\t\t\t\t\tresult = PBEResult.Success;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tresult = PBEResult.Ineffective_Status;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBETeamStatus.Spikes:\n\t\t\t{\n\t\t\t\tif (user.Team.OpposingTeam.SpikeCount < 3)\n\t\t\t\t{\n\t\t\t\t\tuser.Team.OpposingTeam.SpikeCount++;\n\t\t\t\t\tBroadcastTeamStatus(user.Team.OpposingTeam, PBETeamStatus.Spikes, PBETeamStatusAction.Added);\n\t\t\t\t\tresult = PBEResult.Success;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tresult = PBEResult.Ineffective_Status;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBETeamStatus.StealthRock:\n\t\t\t{\n\t\t\t\tif (!user.Team.OpposingTeam.TeamStatus.HasFlag(PBETeamStatus.StealthRock))\n\t\t\t\t{\n\t\t\t\t\tBroadcastTeamStatus(user.Team.OpposingTeam, PBETeamStatus.StealthRock, PBETeamStatusAction.Added);\n\t\t\t\t\tresult = PBEResult.Success;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tresult = PBEResult.Ineffective_Status;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBETeamStatus.Tailwind:\n\t\t\t{\n\t\t\t\tif (!user.Team.TeamStatus.HasFlag(PBETeamStatus.Tailwind))\n\t\t\t\t{\n\t\t\t\t\tuser.Team.TailwindCount = 4;\n\t\t\t\t\tBroadcastTeamStatus(user.Team, PBETeamStatus.Tailwind, PBETeamStatusAction.Added);\n\t\t\t\t\tresult = PBEResult.Success;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tresult = PBEResult.Ineffective_Status;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBETeamStatus.ToxicSpikes:\n\t\t\t{\n\t\t\t\tif (user.Team.OpposingTeam.ToxicSpikeCount < 2)\n\t\t\t\t{\n\t\t\t\t\tuser.Team.OpposingTeam.ToxicSpikeCount++;\n\t\t\t\t\tBroadcastTeamStatus(user.Team.OpposingTeam, PBETeamStatus.ToxicSpikes, PBETeamStatusAction.Added);\n\t\t\t\t\tresult = PBEResult.Success;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tresult = PBEResult.Ineffective_Status;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBETeamStatus.WideGuard:\n\t\t\t{\n\t\t\t\tif (!user.Team.TeamStatus.HasFlag(PBETeamStatus.WideGuard) && _rand.RandomBool(user.GetProtectionChance(), ushort.MaxValue))\n\t\t\t\t{\n\t\t\t\t\tuser.Protection_Used = true;\n\t\t\t\t\tBroadcastTeamStatus(user.Team, PBETeamStatus.WideGuard, PBETeamStatusAction.Added);\n\t\t\t\t\tresult = PBEResult.Success;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tresult = PBEResult.Ineffective_Status;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(status));\n\t\t}\n\t\tif (result != PBEResult.Success)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, result);\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_TryForceWeather(PBEBattlePokemon user, PBEMove move, IPBEMoveData mData, PBEWeather weather)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (Weather == weather)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.Ineffective_Status);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tbyte turns;\n\t\t\tPBEItem extensionItem;\n\t\t\tbyte itemTurnExtension;\n\t\t\tswitch (weather)\n\t\t\t{\n\t\t\t\tcase PBEWeather.Hailstorm:\n\t\t\t\t{\n\t\t\t\t\tturns = Settings.HailTurns;\n\t\t\t\t\textensionItem = PBEItem.IcyRock;\n\t\t\t\t\titemTurnExtension = Settings.IcyRockTurnExtension;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEWeather.HarshSunlight:\n\t\t\t\t{\n\t\t\t\t\tturns = Settings.SunTurns;\n\t\t\t\t\textensionItem = PBEItem.HeatRock;\n\t\t\t\t\titemTurnExtension = Settings.HeatRockTurnExtension;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEWeather.Rain:\n\t\t\t\t{\n\t\t\t\t\tturns = Settings.RainTurns;\n\t\t\t\t\textensionItem = PBEItem.DampRock;\n\t\t\t\t\titemTurnExtension = Settings.DampRockTurnExtension;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEWeather.Sandstorm:\n\t\t\t\t{\n\t\t\t\t\tturns = Settings.SandstormTurns;\n\t\t\t\t\textensionItem = PBEItem.SmoothRock;\n\t\t\t\t\titemTurnExtension = Settings.SmoothRockTurnExtension;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(weather));\n\t\t\t}\n\t\t\tSetWeather(weather, (byte)(turns + (user.Item == extensionItem ? itemTurnExtension : 0)), false);\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\n\tprivate void Ef_Growth(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tint change = WillLeafGuardActivate() ? +2 : +1;\n\t\tEf_ChangeTargetStats(user, targets, move, mData, new[] { (PBEStat.Attack, change), (PBEStat.SpAttack, change) });\n\t}\n\tprivate void Ef_ChangeTargetStats(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData, (PBEStat, int)[] statChanges, bool requireAttraction = false)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t{\n\t\t\t\tif (!MissCheck(user, target, mData))\n\t\t\t\t{\n\t\t\t\t\tPBEResult result = requireAttraction ? target.IsAttractionPossible(user, ignoreCurrentStatus: true) : PBEResult.Success;\n\t\t\t\t\tif (result != PBEResult.Success)\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastMoveResult(user, target, result);\n\t\t\t\t\t}\n\t\t\t\t\telse if (user != target && target.Status2.HasFlag(PBEStatus2.Substitute))\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastMoveResult(user, target, PBEResult.Ineffective_Substitute);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (int i = 0; i < statChanges.Length; i++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t(PBEStat stat, int change) = statChanges[i];\n\t\t\t\t\t\t\tApplyStatChangeIfPossible(user, target, stat, change);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_Hit__MaybeChangeTargetStats(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData, (PBEStat, int)[] statChanges, int chanceToChangeStats)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvoid BeforePostHit(PBEBattlePokemon target, ushort damageDealt)\n\t\t\t{\n\t\t\t\tif (target.HP == 0 || !GetManipulableChance(user, chanceToChangeStats))\n\t\t\t\t{\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tfor (int i = 0; i < statChanges.Length; i++)\n\t\t\t\t{\n\t\t\t\t\t(PBEStat stat, int change) = statChanges[i];\n\t\t\t\t\tApplyStatChangeIfPossible(user, target, stat, change, isSecondaryEffect: true);\n\t\t\t\t}\n\t\t\t}\n\t\t\tBasicHit(user, targets, mData, beforePostHit: BeforePostHit);\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_Hit__MaybeChangeUserStats(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData, (PBEStat, int)[] statChanges, int chanceToChangeStats)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvoid BeforePostHit(PBEBattlePokemon target, ushort damageDealt)\n\t\t\t{\n\t\t\t\tif (user.HP > 0 && GetManipulableChance(user, chanceToChangeStats))\n\t\t\t\t{\n\t\t\t\t\tfor (int i = 0; i < statChanges.Length; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\t(PBEStat stat, int change) = statChanges[i];\n\t\t\t\t\t\tApplyStatChangeIfPossible(user, user, stat, change, isSecondaryEffect: true);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tBasicHit(user, targets, mData, beforePostHit: BeforePostHit);\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\n\tprivate void Ef_Entrainment(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tvar blockedUserAbilities = new PBEAbility[] { PBEAbility.FlowerGift, PBEAbility.Forecast, PBEAbility.Illusion,\n\t\t\t\tPBEAbility.Imposter, PBEAbility.Trace };\n\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t{\n\t\t\t\tif (!MissCheck(user, target, mData))\n\t\t\t\t{\n\t\t\t\t\tif (target.Ability == user.Ability || blockedUserAbilities.Contains(user.Ability))\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastMoveResult(user, target, PBEResult.InvalidConditions);\n\t\t\t\t\t}\n\t\t\t\t\telse if (target.Ability == PBEAbility.Multitype || target.Ability == PBEAbility.Truant)\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastMoveResult(user, target, PBEResult.Ineffective_Ability);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tSetAbility(user, target, user.Ability);\n\t\t\t\t\t\t// TODO: #234 - Reveal other Pokémon's Ability\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_RolePlay(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tvar blockedUserAbilities = new PBEAbility[] { PBEAbility.Imposter, PBEAbility.Multitype, PBEAbility.ZenMode };\n\t\tvar blockedTargetAbilities = new PBEAbility[] { PBEAbility.FlowerGift, PBEAbility.Forecast, PBEAbility.Illusion,\n\t\t\t\tPBEAbility.Imposter, PBEAbility.Multitype, PBEAbility.Trace, PBEAbility.WonderGuard, PBEAbility.ZenMode };\n\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t{\n\t\t\t\tif (!MissCheck(user, target, mData))\n\t\t\t\t{\n\t\t\t\t\tif (target.Ability == user.Ability || blockedUserAbilities.Contains(user.Ability) || blockedTargetAbilities.Contains(target.Ability))\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastMoveResult(user, target, PBEResult.InvalidConditions);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tSetAbility(user, user, target.Ability);\n\t\t\t\t\t\t// TODO: #234 - Reveal other Pokémon's Ability\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_SetOtherAbility(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData, PBEAbility ability, bool blockedByTruant)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t{\n\t\t\t\tif (!MissCheck(user, target, mData))\n\t\t\t\t{\n\t\t\t\t\tif (target.Ability == ability)\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastMoveResult(user, target, PBEResult.InvalidConditions);\n\t\t\t\t\t}\n\t\t\t\t\telse if (target.Ability == PBEAbility.Multitype || (blockedByTruant && target.Ability == PBEAbility.Truant))\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastMoveResult(user, target, PBEResult.Ineffective_Ability);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tSetAbility(user, target, ability);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\n\tprivate void Ef_Bounce(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData, PBETurnTarget requestedTargets)\n\t{\n\t\tvoid BeforePostHit(PBEBattlePokemon target, ushort damageDealt)\n\t\t{\n\t\t\tif (target.HP > 0 && GetManipulableChance(user, mData.EffectParam))\n\t\t\t{\n\t\t\t\tApplyStatus1IfPossible(user, target, PBEStatus1.Paralyzed, false);\n\t\t\t}\n\t\t}\n\t\tSemiInvulnerableChargeMove(user, targets, move, mData, requestedTargets, PBEStatus2.Airborne, beforePostHit: BeforePostHit);\n\t}\n\tprivate void Ef_ShadowForce(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData, PBETurnTarget requestedTargets)\n\t{\n\t\tvoid AfterPostHit(PBEBattlePokemon target)\n\t\t{\n\t\t\tif (target.HP > 0 && target.Status2.HasFlag(PBEStatus2.Protected))\n\t\t\t{\n\t\t\t\tBroadcastStatus2(target, user, PBEStatus2.Protected, PBEStatusAction.Cleared);\n\t\t\t}\n\t\t\tif (target.Team.TeamStatus.HasFlag(PBETeamStatus.QuickGuard))\n\t\t\t{\n\t\t\t\tBroadcastTeamStatus(target.Team, PBETeamStatus.QuickGuard, PBETeamStatusAction.Cleared);\n\t\t\t}\n\t\t\tif (target.Team.TeamStatus.HasFlag(PBETeamStatus.WideGuard))\n\t\t\t{\n\t\t\t\tBroadcastTeamStatus(target.Team, PBETeamStatus.WideGuard, PBETeamStatusAction.Cleared);\n\t\t\t}\n\t\t}\n\t\tSemiInvulnerableChargeMove(user, targets, move, mData, requestedTargets, PBEStatus2.ShadowForce, afterPostHit: AfterPostHit);\n\t}\n\n\tprivate void Ef_BrickBreak(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvoid BeforeDoingDamage(PBEBattlePokemon target)\n\t\t\t{\n\t\t\t\t// Verified: Reflect then Light Screen\n\t\t\t\tif (target.Team.TeamStatus.HasFlag(PBETeamStatus.Reflect))\n\t\t\t\t{\n\t\t\t\t\ttarget.Team.ReflectCount = 0;\n\t\t\t\t\tBroadcastTeamStatus(target.Team, PBETeamStatus.Reflect, PBETeamStatusAction.Cleared);\n\t\t\t\t}\n\t\t\t\tif (target.Team.TeamStatus.HasFlag(PBETeamStatus.LightScreen))\n\t\t\t\t{\n\t\t\t\t\ttarget.Team.LightScreenCount = 0;\n\t\t\t\t\tBroadcastTeamStatus(target.Team, PBETeamStatus.LightScreen, PBETeamStatusAction.Cleared);\n\t\t\t\t}\n\t\t\t}\n\t\t\tBasicHit(user, targets, mData, beforeDoingDamage: BeforeDoingDamage);\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_Feint(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvoid BeforeDoingDamage(PBEBattlePokemon target)\n\t\t\t{\n\t\t\t\tif (target.HP > 0 && target.Status2.HasFlag(PBEStatus2.Protected))\n\t\t\t\t{\n\t\t\t\t\tBroadcastStatus2(target, user, PBEStatus2.Protected, PBEStatusAction.Cleared);\n\t\t\t\t}\n\t\t\t\tif (target.Team == user.Team.OpposingTeam)\n\t\t\t\t{\n\t\t\t\t\tif (target.Team.TeamStatus.HasFlag(PBETeamStatus.QuickGuard))\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastTeamStatus(target.Team, PBETeamStatus.QuickGuard, PBETeamStatusAction.Cleared);\n\t\t\t\t\t}\n\t\t\t\t\tif (target.Team.TeamStatus.HasFlag(PBETeamStatus.WideGuard))\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastTeamStatus(target.Team, PBETeamStatus.WideGuard, PBETeamStatusAction.Cleared);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tBasicHit(user, targets, mData, beforeDoingDamage: BeforeDoingDamage);\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_Hit(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData, PBEStatus1 status1 = PBEStatus1.None, int chanceToInflictStatus1 = 0, PBEStatus2 status2 = PBEStatus2.None, int chanceToInflictStatus2 = 0)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvoid BeforePostHit(PBEBattlePokemon target, ushort damageDealt)\n\t\t\t{\n\t\t\t\tif (target.HP == 0)\n\t\t\t\t{\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (status1 != PBEStatus1.None && GetManipulableChance(user, chanceToInflictStatus1))\n\t\t\t\t{\n\t\t\t\t\tApplyStatus1IfPossible(user, target, status1, false);\n\t\t\t\t}\n\t\t\t\tif (status2 != PBEStatus2.None && GetManipulableChance(user, chanceToInflictStatus2))\n\t\t\t\t{\n\t\t\t\t\tApplyStatus2IfPossible(user, target, status2, false);\n\t\t\t\t}\n\t\t\t}\n\t\t\tBasicHit(user, targets, mData, beforePostHit: status1 != PBEStatus1.None || status2 != PBEStatus2.None ? BeforePostHit : null);\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_Hit__MaybeBurnFreezeParalyze(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvoid BeforePostHit(PBEBattlePokemon target, ushort damageDealt)\n\t\t\t{\n\t\t\t\tif (target.HP == 0 || !GetManipulableChance(user, mData.EffectParam))\n\t\t\t\t{\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tPBEStatus1 status1;\n\t\t\t\tint val = _rand.RandomInt(0, 2);\n\t\t\t\tif (val == 0)\n\t\t\t\t{\n\t\t\t\t\tstatus1 = PBEStatus1.Burned;\n\t\t\t\t}\n\t\t\t\telse if (val == 1)\n\t\t\t\t{\n\t\t\t\t\tstatus1 = PBEStatus1.Frozen;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tstatus1 = PBEStatus1.Paralyzed;\n\t\t\t\t}\n\t\t\t\tApplyStatus1IfPossible(user, target, status1, false);\n\t\t\t}\n\t\t\tBasicHit(user, targets, mData, beforePostHit: BeforePostHit);\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_MultiHit(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData, byte numHits, bool subsequentMissChecks = false, PBEStatus1 status1 = PBEStatus1.None, int chanceToInflictStatus1 = 0)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvoid BeforePostHit(PBEBattlePokemon target)\n\t\t\t{\n\t\t\t\tif (target.HP > 0 && GetManipulableChance(user, chanceToInflictStatus1))\n\t\t\t\t{\n\t\t\t\t\tApplyStatus1IfPossible(user, target, status1, false);\n\t\t\t\t}\n\t\t\t}\n\t\t\tMultiHit(user, targets, mData, numHits, subsequentMissChecks: subsequentMissChecks, beforePostHit: status1 != PBEStatus1.None ? BeforePostHit : null); // Doesn't need to be its own func but neater\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_MultiHit_2To5(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tbyte numHits;\n\t\tif (user.Ability == PBEAbility.SkillLink)\n\t\t{\n\t\t\tnumHits = 5;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint val = _rand.RandomInt(0, 5);\n\t\t\tif (val < 2)\n\t\t\t{\n\t\t\t\tnumHits = 2;\n\t\t\t}\n\t\t\telse if (val < 4)\n\t\t\t{\n\t\t\t\tnumHits = 3;\n\t\t\t}\n\t\t\telse if (val < 5)\n\t\t\t{\n\t\t\t\tnumHits = 4;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tnumHits = 5;\n\t\t\t}\n\t\t}\n\t\tEf_MultiHit(user, targets, move, mData, numHits);\n\t}\n\tprivate void Ef_PayDay(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvoid BeforeDoingDamage(PBEBattlePokemon target)\n\t\t\t{\n\t\t\t\tBroadcastPayDay();\n\t\t\t}\n\t\t\tBasicHit(user, targets, mData, beforeDoingDamage: BeforeDoingDamage);\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_Recoil(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData, PBEStatus1 status1 = PBEStatus1.None, int chanceToInflictStatus1 = 0, PBEStatus2 status2 = PBEStatus2.None, int chanceToInflictStatus2 = 0)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint? RecoilFunc(int totalDamageDealt)\n\t\t\t{\n\t\t\t\treturn user.Ability == PBEAbility.RockHead || totalDamageDealt == 0 ? null : totalDamageDealt / mData.EffectParam;\n\t\t\t}\n\t\t\tvoid BeforePostHit(PBEBattlePokemon target, ushort damageDealt)\n\t\t\t{\n\t\t\t\tif (target.HP == 0)\n\t\t\t\t{\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (status1 != PBEStatus1.None && GetManipulableChance(user, chanceToInflictStatus1))\n\t\t\t\t{\n\t\t\t\t\tApplyStatus1IfPossible(user, target, status1, false);\n\t\t\t\t}\n\t\t\t\tif (status2 != PBEStatus2.None && GetManipulableChance(user, chanceToInflictStatus2))\n\t\t\t\t{\n\t\t\t\t\tApplyStatus2IfPossible(user, target, status2, false);\n\t\t\t\t}\n\t\t\t}\n\t\t\tBasicHit(user, targets, mData, recoilFunc: RecoilFunc, beforePostHit: status1 != PBEStatus1.None || status2 != PBEStatus2.None ? BeforePostHit : null);\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_SecretPower(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvoid BeforePostHit(PBEBattlePokemon target, ushort damageDealt)\n\t\t\t{\n\t\t\t\t// BUG: SecretPower is unaffected by SereneGrace and the Rainbow\n\t\t\t\tif (target.HP > 0\n\t\t\t\t\t&& (Settings.BugFix\n\t\t\t\t\t? GetManipulableChance(user, mData.EffectParam)\n\t\t\t\t\t: _rand.RandomBool(mData.EffectParam, 100))\n\t\t\t\t\t)\n\t\t\t\t{\n\t\t\t\t\tswitch (BattleTerrain)\n\t\t\t\t\t{\n\t\t\t\t\t\tcase PBEBattleTerrain.Cave: ApplyStatus2IfPossible(user, target, PBEStatus2.Flinching, false); break;\n\t\t\t\t\t\tcase PBEBattleTerrain.Grass: ApplyStatus1IfPossible(user, target, PBEStatus1.Asleep, false); break;\n\t\t\t\t\t\tcase PBEBattleTerrain.Plain: ApplyStatus1IfPossible(user, target, PBEStatus1.Paralyzed, false); break;\n\t\t\t\t\t\tcase PBEBattleTerrain.Puddle: ApplyStatChangeIfPossible(user, target, PBEStat.Speed, -1, isSecondaryEffect: true); break;\n\t\t\t\t\t\tcase PBEBattleTerrain.Sand: ApplyStatChangeIfPossible(user, target, PBEStat.Accuracy, -1, isSecondaryEffect: true); break;\n\t\t\t\t\t\tcase PBEBattleTerrain.Snow: ApplyStatus1IfPossible(user, target, PBEStatus1.Frozen, false); break;\n\t\t\t\t\t\tcase PBEBattleTerrain.Water: ApplyStatChangeIfPossible(user, target, PBEStat.Attack, -1, isSecondaryEffect: true); break;\n\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(BattleTerrain));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tBasicHit(user, targets, mData, beforePostHit: BeforePostHit);\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_Selfdestruct(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\t// TODO: Damp\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\t// In gen 5, the user faints first (and loses if possible)\n\t\t// Due to technical limitations, we cannot faint first, but we should still make the user lose so it is the same behavior\n\t\tDealDamage(user, user, user.MaxHP);\n\t\tTrySetLoser(user);\n\t\tif (targets.Length == 0) // You still faint if there are no targets\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tBasicHit(user, targets, mData);\n\t\t}\n\t\tFaintCheck(user); // No berry check because we are always fainted\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_SmellingSalt(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvoid AfterPostHit(PBEBattlePokemon target)\n\t\t\t{\n\t\t\t\tif (target.HP > 0 && target.Status1 == PBEStatus1.Paralyzed)\n\t\t\t\t{\n\t\t\t\t\ttarget.Status1 = PBEStatus1.None;\n\t\t\t\t\tBroadcastStatus1(target, user, PBEStatus1.Paralyzed, PBEStatusAction.Cleared);\n\t\t\t\t}\n\t\t\t}\n\t\t\tBasicHit(user, targets, mData, afterPostHit: AfterPostHit);\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_Snore(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\t// TODO: Snore etc fail in BasicHit?\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse if (user.Status1 != PBEStatus1.Asleep)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.Ineffective_Status);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvoid BeforePostHit(PBEBattlePokemon target, ushort damageDealt)\n\t\t\t{\n\t\t\t\tif (target.HP > 0 && GetManipulableChance(user, mData.EffectParam))\n\t\t\t\t{\n\t\t\t\t\tApplyStatus2IfPossible(user, target, PBEStatus2.Flinching, false);\n\t\t\t\t}\n\t\t\t}\n\t\t\tBasicHit(user, targets, mData, beforePostHit: BeforePostHit);\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_Struggle(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastStruggle(user);\n\t\tBroadcastMoveUsed(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint? RecoilFunc(int totalDamageDealt)\n\t\t\t{\n\t\t\t\treturn user.MaxHP / mData.EffectParam;\n\t\t\t}\n\t\t\tBasicHit(user, targets, mData, recoilFunc: RecoilFunc);\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_SuckerPunch(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tPBEResult FailFunc(PBEBattlePokemon target)\n\t\t\t{\n\t\t\t\tif (target.TurnAction is null // Just switched in, used item, etc\n\t\t\t\t\t|| target.HasUsedMoveThisTurn\n\t\t\t\t\t|| target.TurnAction.Decision != PBETurnDecision.Fight\n\t\t\t\t\t|| PBEDataProvider.Instance.GetMoveData(target.TurnAction.FightMove).Category == PBEMoveCategory.Status)\n\t\t\t\t{\n\t\t\t\t\tconst PBEResult result = PBEResult.InvalidConditions;\n\t\t\t\t\tBroadcastMoveResult(user, target, result);\n\t\t\t\t\treturn result;\n\t\t\t\t}\n\t\t\t\treturn PBEResult.Success;\n\t\t\t}\n\t\t\tBasicHit(user, targets, mData, failFunc: FailFunc);\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_WakeUpSlap(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvoid AfterPostHit(PBEBattlePokemon target)\n\t\t\t{\n\t\t\t\tif (target.HP > 0 && target.Status1 == PBEStatus1.Asleep)\n\t\t\t\t{\n\t\t\t\t\ttarget.Status1 = PBEStatus1.None;\n\t\t\t\t\ttarget.Status1Counter = 0;\n\t\t\t\t\ttarget.SleepTurns = 0;\n\t\t\t\t\tBroadcastStatus1(target, user, PBEStatus1.Asleep, PBEStatusAction.Cleared);\n\t\t\t\t\tCureNightmare(target, user);\n\t\t\t\t}\n\t\t\t}\n\t\t\tBasicHit(user, targets, mData, afterPostHit: AfterPostHit);\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\n\tprivate void Ef_Endeavor(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint DamageFunc(PBEBattlePokemon target)\n\t\t\t{\n\t\t\t\treturn target.HP - user.HP;\n\t\t\t}\n\t\t\tPBEResult FailFunc(PBEBattlePokemon target)\n\t\t\t{\n\t\t\t\tif (target.HP <= user.HP)\n\t\t\t\t{\n\t\t\t\t\tconst PBEResult result = PBEResult.InvalidConditions;\n\t\t\t\t\tBroadcastMoveResult(user, target, result);\n\t\t\t\t\treturn result;\n\t\t\t\t}\n\t\t\t\treturn PBEResult.Success;\n\t\t\t}\n\t\t\tFixedDamageHit(user, targets, mData, DamageFunc, failFunc: FailFunc);\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_FinalGambit(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint oldHP = user.HP;\n\t\t\tint DamageFunc(PBEBattlePokemon target)\n\t\t\t{\n\t\t\t\t// Only faint/deal damage first time\n\t\t\t\tif (user.HP > 0)\n\t\t\t\t{\n\t\t\t\t\tDealDamage(user, user, oldHP);\n\t\t\t\t\tFaintCheck(user); // No berry check because we are always fainted\n\t\t\t\t}\n\t\t\t\treturn oldHP;\n\t\t\t}\n\t\t\tFixedDamageHit(user, targets, mData, DamageFunc);\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_OneHitKnockout(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint DamageFunc(PBEBattlePokemon target)\n\t\t\t{\n\t\t\t\treturn target.HP;\n\t\t\t}\n\t\t\tPBEResult FailFunc(PBEBattlePokemon target)\n\t\t\t{\n\t\t\t\tif (target.Level > user.Level)\n\t\t\t\t{\n\t\t\t\t\tconst PBEResult result = PBEResult.Ineffective_Level;\n\t\t\t\t\tBroadcastMoveResult(user, target, result);\n\t\t\t\t\treturn result;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\treturn PBEResult.Success;\n\t\t\t\t}\n\t\t\t}\n\t\t\tvoid BeforePostHit()\n\t\t\t{\n\t\t\t\t// This Any is for Sturdy survivors\n\t\t\t\tif (Array.FindIndex(targets, p => p.HP == 0) != -1)\n\t\t\t\t{\n\t\t\t\t\tBroadcastOneHitKnockout();\n\t\t\t\t}\n\t\t\t}\n\t\t\tFixedDamageHit(user, targets, mData, DamageFunc, failFunc: FailFunc, beforePostHit: BeforePostHit);\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_Psywave(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint DamageFunc(PBEBattlePokemon target)\n\t\t\t{\n\t\t\t\treturn user.Level * (_rand.RandomInt(0, 100) + 50) / 100;\n\t\t\t}\n\t\t\tFixedDamageHit(user, targets, mData, DamageFunc);\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_SeismicToss(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint DamageFunc(PBEBattlePokemon target)\n\t\t\t{\n\t\t\t\treturn user.Level;\n\t\t\t}\n\t\t\tFixedDamageHit(user, targets, mData, DamageFunc);\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_SetDamage(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint DamageFunc(PBEBattlePokemon target)\n\t\t\t{\n\t\t\t\treturn mData.EffectParam;\n\t\t\t}\n\t\t\tFixedDamageHit(user, targets, mData, DamageFunc);\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_SuperFang(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstatic int DamageFunc(PBEBattlePokemon target)\n\t\t\t{\n\t\t\t\treturn target.HP / 2;\n\t\t\t}\n\t\t\tFixedDamageHit(user, targets, mData, DamageFunc);\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\n\tprivate void Ef_HPDrain(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData, bool requireSleep = false)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tPBEResult FailFunc(PBEBattlePokemon target)\n\t\t\t{\n\t\t\t\tif (target.Status1 != PBEStatus1.Asleep)\n\t\t\t\t{\n\t\t\t\t\tconst PBEResult result = PBEResult.Ineffective_Status;\n\t\t\t\t\tBroadcastMoveResult(user, target, result);\n\t\t\t\t\treturn result;\n\t\t\t\t}\n\t\t\t\treturn PBEResult.Success;\n\t\t\t}\n\t\t\tvoid BeforePostHit(PBEBattlePokemon target, ushort damageDealt)\n\t\t\t{\n\t\t\t\tif (user.HP == 0)\n\t\t\t\t{\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tint restoreAmt = (int)(damageDealt * (mData.EffectParam / 100.0));\n\t\t\t\tApplyBigRoot(user, ref restoreAmt);\n\t\t\t\tif (target.Ability == PBEAbility.LiquidOoze)\n\t\t\t\t{\n\t\t\t\t\t// Verified: User faints first here, target faints at normal spot afterwards\n\t\t\t\t\tBroadcastAbility(target, user, PBEAbility.LiquidOoze, PBEAbilityAction.Damage);\n\t\t\t\t\tDealDamage(target, user, restoreAmt);\n\t\t\t\t\tif (!FaintCheck(user))\n\t\t\t\t\t{\n\t\t\t\t\t\tLowHPBerryCheck(user);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (HealDamage(user, restoreAmt) > 0)\n\t\t\t\t{\n\t\t\t\t\tBroadcastHPDrained(target);\n\t\t\t\t}\n\t\t\t}\n\t\t\tBasicHit(user, targets, mData, failFunc: requireSleep ? FailFunc : null, beforePostHit: BeforePostHit);\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_Moonlight(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint denominator;\n\t\t\tif (ShouldDoWeatherEffects())\n\t\t\t{\n\t\t\t\tswitch (Weather)\n\t\t\t\t{\n\t\t\t\t\tcase PBEWeather.None: denominator = 2; break;\n\t\t\t\t\tcase PBEWeather.HarshSunlight: denominator = 3; break;\n\t\t\t\t\tdefault: denominator = 4; break;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tdenominator = 2;\n\t\t\t}\n\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t{\n\t\t\t\tif (!MissCheck(user, target, mData))\n\t\t\t\t{\n\t\t\t\t\tif (HealDamage(user, user.MaxHP / denominator) == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastMoveResult(user, user, PBEResult.Ineffective_Stat);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_PainSplit(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t{\n\t\t\t\tif (!MissCheck(user, target, mData))\n\t\t\t\t{\n\t\t\t\t\tif (target.Status2.HasFlag(PBEStatus2.Substitute))\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastMoveResult(user, target, PBEResult.Ineffective_Substitute);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tint total = user.HP + target.HP;\n\t\t\t\t\t\tint hp = total / 2;\n\t\t\t\t\t\tforeach (PBEBattlePokemon pkmn in new PBEBattlePokemon[] { user, target })\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (hp > pkmn.HP)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tHealDamage(pkmn, hp - pkmn.HP);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (hp < pkmn.HP)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tDealDamage(user, pkmn, pkmn.HP - hp);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tBroadcastPainSplit(user, target);\n\t\t\t\t\t\tLowHPBerryCheck(user);\n\t\t\t\t\t\tLowHPBerryCheck(target, forcedToEatBy: user); // Verified: Berry is activated but no illusion breaking\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  // Verified: ColorChange/LifeOrb is ignored\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_Rest(PBEBattlePokemon user, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (user.Status1 == PBEStatus1.Asleep)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.Ineffective_Status);\n\t\t}\n\t\telse if (user.HP == user.MaxHP)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.Ineffective_Stat);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tPBEResult result = user.IsSleepPossible(null, ignoreSubstitute: true, ignoreCurrentStatus: true, ignoreSafeguard: true);\n\t\t\tif (result == PBEResult.Ineffective_Ability)\n\t\t\t{\n\t\t\t\tBroadcastAbility(user, user, user.Ability, PBEAbilityAction.PreventedStatus);\n\t\t\t}\n\t\t\tif (result != PBEResult.Success)\n\t\t\t{\n\t\t\t\tBroadcastMoveResult(user, user, result);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tuser.Status1 = PBEStatus1.Asleep;\n\t\t\t\tSetSleepTurns(user, Settings.SleepMaxTurns, Settings.SleepMaxTurns); // Not a typo; Rest always sleeps for max turns\n\t\t\t\tuser.Status1Counter = 0;\n\t\t\t\tBroadcastStatus1(user, user, PBEStatus1.Asleep, PBEStatusAction.Added);\n\t\t\t\tHealDamage(user, user.MaxHP);\n\t\t\t}\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_RestoreTargetHP(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t{\n\t\t\t\tif (!MissCheck(user, target, mData))\n\t\t\t\t{\n\t\t\t\t\tif (user != target && target.Status2.HasFlag(PBEStatus2.Substitute))\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastMoveResult(user, target, PBEResult.Ineffective_Substitute);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tif (HealDamage(target, (int)(target.MaxHP * (mData.EffectParam / 100.0))) == 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tBroadcastMoveResult(user, target, PBEResult.Ineffective_Stat);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_Roost(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t{\n\t\t\t\tif (!MissCheck(user, target, mData))\n\t\t\t\t{\n\t\t\t\t\tif (target.Status2.HasFlag(PBEStatus2.Roost))\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastMoveResult(user, target, PBEResult.Ineffective_Status);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tif (HealDamage(target, (int)(target.MaxHP * (mData.EffectParam / 100.0))) == 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tBroadcastMoveResult(user, target, PBEResult.Ineffective_Stat);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttarget.StartRoost();\n\t\t\t\t\t\t\tBroadcastStatus2(target, user, PBEStatus2.Roost, PBEStatusAction.Added);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\n\tprivate void Ef_BellyDrum(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t{\n\t\t\t\tif (!MissCheck(user, target, mData))\n\t\t\t\t{\n\t\t\t\t\tint requirement = target.MaxHP / 2;\n\t\t\t\t\t// BUG: The games do not check if the target has Contrary\n\t\t\t\t\tsbyte oldValue = 0, newValue = 0;\n\t\t\t\t\tif (target.HP <= requirement\n\t\t\t\t\t\t|| (Settings.BugFix\n\t\t\t\t\t\t? target.IsStatChangePossible(PBEStat.Attack, user, byte.MaxValue, out oldValue, out newValue) != PBEResult.Success\n\t\t\t\t\t\t: target.AttackChange == Settings.MaxStatChange)\n\t\t\t\t\t\t)\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastMoveResult(user, target, PBEResult.InvalidConditions);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tDealDamage(user, target, requirement);\n\t\t\t\t\t\tif (Settings.BugFix)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSetStatAndBroadcast(target, PBEStat.Attack, oldValue, newValue);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tApplyStatChangeIfPossible(user, target, PBEStat.Attack, byte.MaxValue); // byte.MaxValue will work for all PBESettings\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_Camouflage(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t{\n\t\t\t\tif (!MissCheck(user, target, mData))\n\t\t\t\t{\n\t\t\t\t\tPBEType type;\n\t\t\t\t\tswitch (BattleTerrain)\n\t\t\t\t\t{\n\t\t\t\t\t\tcase PBEBattleTerrain.Cave: type = PBEType.Rock; break;\n\t\t\t\t\t\tcase PBEBattleTerrain.Grass: type = PBEType.Grass; break;\n\t\t\t\t\t\tcase PBEBattleTerrain.Plain: type = PBEType.Normal; break;\n\t\t\t\t\t\tcase PBEBattleTerrain.Puddle: type = PBEType.Ground; break;\n\t\t\t\t\t\tcase PBEBattleTerrain.Sand: type = PBEType.Ground; break;\n\t\t\t\t\t\tcase PBEBattleTerrain.Snow: type = PBEType.Ice; break;\n\t\t\t\t\t\tcase PBEBattleTerrain.Water: type = PBEType.Water; break;\n\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(BattleTerrain));\n\t\t\t\t\t}\n\t\t\t\t\t// Verified: Works on dual-type, fails on single-type\n\t\t\t\t\tif (target.Type1 == type && target.Type2 == PBEType.None)\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastMoveResult(user, target, PBEResult.InvalidConditions);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastTypeChanged(target, type, PBEType.None);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_Conversion(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t{\n\t\t\t\tif (!MissCheck(user, target, mData))\n\t\t\t\t{\n\t\t\t\t\tPBEBattleMoveset moves = target.Moves;\n\t\t\t\t\tint count = moves.Count;\n\t\t\t\t\tvar available = new List<PBEType>(count);\n\t\t\t\t\tfor (int i = 0; i < count; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tPBEMove m = moves[i].Move;\n\t\t\t\t\t\tif (m != PBEMove.None && m != move)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tPBEType type = PBEDataProvider.Instance.GetMoveData(m).Type;\n\t\t\t\t\t\t\tif (!target.HasType(type))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tavailable.Add(type);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (available.Count == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastMoveResult(user, target, PBEResult.InvalidConditions);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastTypeChanged(target, _rand.RandomElement(available), PBEType.None);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_Curse(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (user.HasType(PBEType.Ghost))\n\t\t\t{\n\t\t\t\tif (targets.Length == 1 && targets[0] == user) // Just gained the Ghost type after selecting the move, so get a random target\n\t\t\t\t{\n\t\t\t\t\tPBEFieldPosition prioritizedPos = GetPositionAcross(BattleFormat, user.FieldPosition);\n\t\t\t\t\tPBETurnTarget moveTarget = prioritizedPos == PBEFieldPosition.Left ? PBETurnTarget.FoeLeft : prioritizedPos == PBEFieldPosition.Center ? PBETurnTarget.FoeCenter : PBETurnTarget.FoeRight;\n\t\t\t\t\ttargets = GetRuntimeTargets(user, moveTarget, false, _rand);\n\t\t\t\t}\n\t\t\t\tif (targets.Length == 0)\n\t\t\t\t{\n\t\t\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!MissCheck(user, target, mData))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tApplyStatus2IfPossible(user, target, PBEStatus2.Cursed, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t\t{\n\t\t\t\t\tif (target.SpeedChange == -Settings.MaxStatChange\n\t\t\t\t\t\t&& target.AttackChange == Settings.MaxStatChange\n\t\t\t\t\t\t&& target.DefenseChange == Settings.MaxStatChange)\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastMoveResult(user, target, PBEResult.Ineffective_Stat);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tApplyStatChangeIfPossible(user, target, PBEStat.Speed, -1);\n\t\t\t\t\t\tApplyStatChangeIfPossible(user, target, PBEStat.Attack, +1);\n\t\t\t\t\t\tApplyStatChangeIfPossible(user, target, PBEStat.Defense, +1);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_Flatter(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t{\n\t\t\t\tif (!MissCheck(user, target, mData))\n\t\t\t\t{\n\t\t\t\t\tif (target.Status2.HasFlag(PBEStatus2.Substitute))\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastMoveResult(user, target, PBEResult.Ineffective_Substitute);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tApplyStatChangeIfPossible(user, target, PBEStat.SpAttack, +1);\n\t\t\t\t\t\tApplyStatus2IfPossible(user, target, PBEStatus2.Confused, true);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_Haze(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tforeach (PBEBattlePokemon pkmn in targets)\n\t\t{\n\t\t\tpkmn.ClearStatChanges();\n\t\t}\n\t\tBroadcastHaze();\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_HelpingHand(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t{\n\t\t\t\t// TODO: When triple battle shifting happens, all moves that can target allies but not the user will have to check if the user targetted itself due to shifting.\n\t\t\t\t// For now, I'll put this check here, because this is the only move that will attempt to target the user when the move cannot normally do so (single/rotation battle).\n\t\t\t\tif (target == user)\n\t\t\t\t{\n\t\t\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tApplyStatus2IfPossible(user, target, PBEStatus2.HelpingHand, true); // No MissCheck because should be able to hit through protect, shadowforce, etc\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_Metronome(PBEBattlePokemon user, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\t// Record before the called move is recorded\n\t\tRecordExecutedMove(user, move, mData);\n\n\t\tPBEMove calledMove = _rand.RandomElement(PBEDataUtils.MetronomeMoves);\n\t\t_calledFromOtherMove = true;\n\t\tUseMove(user, calledMove, GetRandomTargetForMetronome(user, calledMove, _rand));\n\t\t_calledFromOtherMove = false;\n\t}\n\tprivate void Ef_Nothing(PBEBattlePokemon user, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tBroadcastNothingHappened();\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_PsychUp(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t{\n\t\t\t\tif (!MissCheck(user, target, mData))\n\t\t\t\t{\n\t\t\t\t\tBroadcastPsychUp(user, target);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_ReflectType(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t{\n\t\t\t\tif (!MissCheck(user, target, mData))\n\t\t\t\t{\n\t\t\t\t\t// Fail if pure flying-type roosts\n\t\t\t\t\tif (target.Type1 == PBEType.None && target.Type2 == PBEType.None)\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastMoveResult(user, target, PBEResult.InvalidConditions);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastReflectType(user, target);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_Refresh(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t{\n\t\t\t\tif (!MissCheck(user, target, mData))\n\t\t\t\t{\n\t\t\t\t\tPBEStatus1 status1 = target.Status1;\n\t\t\t\t\tif (status1 == PBEStatus1.None || status1 == PBEStatus1.Asleep || status1 == PBEStatus1.Frozen)\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastMoveResult(user, target, PBEResult.InvalidConditions);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\ttarget.Status1 = PBEStatus1.None;\n\t\t\t\t\t\ttarget.Status1Counter = 0;\n\t\t\t\t\t\tBroadcastStatus1(target, user, status1, PBEStatusAction.Cleared);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_Soak(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t{\n\t\t\t\tif (!MissCheck(user, target, mData))\n\t\t\t\t{\n\t\t\t\t\tBroadcastTypeChanged(target, PBEType.Water, PBEType.None);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_Swagger(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t{\n\t\t\t\tif (!MissCheck(user, target, mData))\n\t\t\t\t{\n\t\t\t\t\tif (target.Status2.HasFlag(PBEStatus2.Substitute))\n\t\t\t\t\t{\n\t\t\t\t\t\tBroadcastMoveResult(user, target, PBEResult.Ineffective_Substitute);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tApplyStatChangeIfPossible(user, target, PBEStat.Attack, +2);\n\t\t\t\t\t\tApplyStatus2IfPossible(user, target, PBEStatus2.Confused, true);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_Teleport(PBEBattlePokemon user, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\t// TODO: Trapping effects, SmokeBall\n\t\t// In gen 5 there is a bug that prevents wild Pokémon holding a SmokeBall from escaping if they are affected by trapping effects\n\t\tif (BattleType == PBEBattleType.Wild && BattleFormat == PBEBattleFormat.Single)\n\t\t{\n\t\t\tSetEscaped(user);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.InvalidConditions);\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n\tprivate void Ef_Whirlwind(PBEBattlePokemon user, PBEBattlePokemon[] targets, PBEMove move, IPBEMoveData mData)\n\t{\n\t\tBroadcastMoveUsed(user, move);\n\t\tPPReduce(user, move);\n\t\tif (targets.Length == 0)\n\t\t{\n\t\t\tBroadcastMoveResult(user, user, PBEResult.NoTarget);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t{\n\t\t\t\tif (MissCheck(user, target, mData))\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// TODO: Trapping effects\n\t\t\t\tif (BattleType == PBEBattleType.Wild)\n\t\t\t\t{\n\t\t\t\t\tif (BattleFormat == PBEBattleFormat.Single)\n\t\t\t\t\t{\n\t\t\t\t\t\t// Wild single battle requires user's level to be >= target's level, then it'll end the battle\n\t\t\t\t\t\tif (user.Level < target.Level)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tBroadcastMoveResult(user, target, PBEResult.Ineffective_Level);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tSetEscaped(target);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t// Trainer using whirlwind in a wild double+ battle will cause it to fail (even if there's only one wild Pokémon left)\n\t\t\t\t\t\tif (!user.IsWild)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tBroadcastMoveResult(user, target, PBEResult.InvalidConditions);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// A wild Pokémon using it will cause it to switch the target out (as normal below)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tList<PBEBattlePokemon> possibleSwitcheroonies = target.Trainer.Party.FindAll(p => p.FieldPosition == PBEFieldPosition.None && p.CanBattle);\n\t\t\t\tif (possibleSwitcheroonies.Count == 0)\n\t\t\t\t{\n\t\t\t\t\tBroadcastMoveResult(user, target, PBEResult.InvalidConditions);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tSwitchTwoPokemon(target, _rand.RandomElement(possibleSwitcheroonies), user);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tRecordExecutedMove(user, move, mData);\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Battle/BattleEffects_HitLogic.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data;\nusing System;\nusing System.Collections.Generic;\n\nnamespace Kermalis.PokemonBattleEngine.Battle;\n\n// This constant looping order that's present in hitting as well as turn ended effects is very weird and unnecessary, but I mimic it for accuracy\n// That's why this file exists in favor of the order I had before\npublic sealed partial class PBEBattle\n{\n\tprivate class PBEAttackVictim\n\t{\n\t\tpublic PBEBattlePokemon Pkmn { get; }\n\t\tpublic PBEResult Result { get; }\n\t\tpublic float TypeEffectiveness { get; }\n\t\tpublic bool Crit { get; set; }\n\t\tpublic ushort Damage { get; set; }\n\n\t\tpublic PBEAttackVictim(PBEBattlePokemon pkmn, PBEResult result, float typeEffectiveness)\n\t\t{\n\t\t\tPkmn = pkmn; Result = result; TypeEffectiveness = typeEffectiveness;\n\t\t}\n\t}\n\n\t// TODO: TripleKick miss logic\n\tprivate void Hit_GetVictims(PBEBattlePokemon user, PBEBattlePokemon[] targets, IPBEMoveData mData, PBEType moveType, out List<PBEAttackVictim> victims,\n\t\tFunc<PBEBattlePokemon, PBEResult>? failFunc = null)\n\t{\n\t\tvictims = new List<PBEAttackVictim>(targets.Length);\n\t\tforeach (PBEBattlePokemon target in targets)\n\t\t{\n\t\t\tif (!AttackTypeCheck(user, target, moveType, out PBEResult result, out float typeEffectiveness))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// Verified: These fails are after type effectiveness (So SuckerPunch will not affect Ghost types due to Normalize before it fails due to invalid conditions)\n\t\t\tif (failFunc is not null && failFunc.Invoke(target) != PBEResult.Success)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tvictims.Add(new PBEAttackVictim(target, result, typeEffectiveness));\n\t\t}\n\t\tif (victims.Count == 0)\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\tvictims.RemoveAll(t => MissCheck(user, t.Pkmn, mData));\n\t\treturn;\n\t}\n\t// Outs are for hit targets that were not behind substitute\n\tprivate static void Hit_HitTargets(PBETeam user, Action<List<PBEAttackVictim>> doSub, Action<List<PBEAttackVictim>> doNormal, List<PBEAttackVictim> victims,\n\t\tout List<PBEAttackVictim> allies, out List<PBEAttackVictim> foes)\n\t{\n\t\tList<PBEAttackVictim> subAllies = victims.FindAll(v =>\n\t\t\t{\n\t\t\t\tPBEBattlePokemon pkmn = v.Pkmn;\n\t\t\t\treturn pkmn.Team == user && pkmn.Status2.HasFlag(PBEStatus2.Substitute);\n\t\t\t});\n\t\tallies = victims.FindAll(v =>\n\t\t{\n\t\t\tPBEBattlePokemon pkmn = v.Pkmn;\n\t\t\treturn pkmn.Team == user && !pkmn.Status2.HasFlag(PBEStatus2.Substitute);\n\t\t});\n\t\tList<PBEAttackVictim> subFoes = victims.FindAll(v =>\n\t\t\t{\n\t\t\t\tPBEBattlePokemon pkmn = v.Pkmn;\n\t\t\t\treturn pkmn.Team != user && pkmn.Status2.HasFlag(PBEStatus2.Substitute);\n\t\t\t});\n\t\tfoes = victims.FindAll(v =>\n\t\t{\n\t\t\tPBEBattlePokemon pkmn = v.Pkmn;\n\t\t\treturn pkmn.Team != user && !pkmn.Status2.HasFlag(PBEStatus2.Substitute);\n\t\t});\n\t\tdoSub(subAllies);\n\t\tdoNormal(allies);\n\t\tdoSub(subFoes);\n\t\tdoNormal(foes);\n\t}\n\tprivate void Hit_DoCrit(List<PBEAttackVictim> victims)\n\t{\n\t\tforeach (PBEAttackVictim victim in victims)\n\t\t{\n\t\t\tif (victim.Crit)\n\t\t\t{\n\t\t\t\tBroadcastMoveCrit(victim.Pkmn);\n\t\t\t}\n\t\t}\n\t}\n\tprivate void Hit_DoMoveResult(PBEBattlePokemon user, List<PBEAttackVictim> victims)\n\t{\n\t\tforeach (PBEAttackVictim victim in victims)\n\t\t{\n\t\t\tPBEResult result = victim.Result;\n\t\t\tif (result != PBEResult.Success)\n\t\t\t{\n\t\t\t\tBroadcastMoveResult(user, victim.Pkmn, result);\n\t\t\t}\n\t\t}\n\t}\n\tprivate void Hit_FaintCheck(List<PBEAttackVictim> victims)\n\t{\n\t\tforeach (PBEAttackVictim victim in victims)\n\t\t{\n\t\t\tFaintCheck(victim.Pkmn);\n\t\t}\n\t}\n\n\tprivate void BasicHit(PBEBattlePokemon user, PBEBattlePokemon[] targets, IPBEMoveData mData,\n\t\tFunc<PBEBattlePokemon, PBEResult>? failFunc = null,\n\t\tAction<PBEBattlePokemon>? beforeDoingDamage = null,\n\t\tAction<PBEBattlePokemon, ushort>? beforePostHit = null,\n\t\tAction<PBEBattlePokemon>? afterPostHit = null,\n\t\tFunc<int, int?>? recoilFunc = null)\n\t{\n\t\t// Targets array is [FoeLeft, FoeCenter, FoeRight, AllyLeft, AllyCenter, AllyRight]\n\t\t// User can faint or heal with a berry at LiquidOoze, IronBarbs/RockyHelmet, and also at Recoil/LifeOrb\n\t\t// -------------Official order-------------\n\t\t// Setup   - [effectiveness/fail checks foes], [effectiveness/fail checks allies], [miss/protection checks foes] [miss/protection checks allies], gem,\n\t\t// Allies  - [sub damage allies, sub effectiveness allies, sub crit allies, sub break allies], [hit allies], [effectiveness allies], [crit allies], [posthit allies], [faint allies],\n\t\t// Foes    - [sub damage foes, sub effectiveness foes, sub crit foes, sub break foes], [hit foes], [effectiveness foes], [crit foes], [posthit foes], [faint foes],\n\t\t// Cleanup - recoil, lifeorb, [colorchange foes], [colorchange allies], [berry allies], [berry foes], [antistatusability allies], [antistatusability foes], exp\n\n\t\tPBEType moveType = user.GetMoveType(mData);\n\t\t// DreamEater checks for sleep before gem activates\n\t\t// SuckerPunch fails\n\t\tHit_GetVictims(user, targets, mData, moveType, out List<PBEAttackVictim> victims, failFunc: failFunc);\n\t\tif (victims.Count == 0)\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\tfloat basePower = CalculateBasePower(user, targets, mData, moveType); // Gem activates here\n\t\tfloat initDamageMultiplier = victims.Count > 1 ? 0.75f : 1;\n\t\tint totalDamageDealt = 0;\n\t\tvoid CalcDamage(PBEAttackVictim victim)\n\t\t{\n\t\t\tPBEBattlePokemon target = victim.Pkmn;\n\t\t\tPBEResult result = victim.Result;\n\t\t\tfloat damageMultiplier = initDamageMultiplier * victim.TypeEffectiveness;\n\t\t\t// Brick Break destroys Light Screen and Reflect before doing damage (after gem)\n\t\t\t// Feint destroys protection\n\t\t\t// Pay Day scatters coins\n\t\t\tbeforeDoingDamage?.Invoke(target);\n\t\t\tbool crit = CritCheck(user, target, mData);\n\t\t\tdamageMultiplier *= CalculateDamageMultiplier(user, target, mData, moveType, result, crit);\n\t\t\tint damage = (int)(damageMultiplier * CalculateDamage(user, target, mData, moveType, basePower, crit));\n\t\t\tvictim.Damage = DealDamage(user, target, damage, ignoreSubstitute: false, ignoreSturdy: false);\n\t\t\ttotalDamageDealt += victim.Damage;\n\t\t\tvictim.Crit = crit;\n\t\t}\n\t\tvoid DoSub(List<PBEAttackVictim> subs)\n\t\t{\n\t\t\tforeach (PBEAttackVictim victim in subs)\n\t\t\t{\n\t\t\t\tCalcDamage(victim);\n\t\t\t\tPBEBattlePokemon target = victim.Pkmn;\n\t\t\t\tPBEResult result = victim.Result;\n\t\t\t\tif (result != PBEResult.Success)\n\t\t\t\t{\n\t\t\t\t\tBroadcastMoveResult(user, target, result);\n\t\t\t\t}\n\t\t\t\tif (victim.Crit)\n\t\t\t\t{\n\t\t\t\t\tBroadcastMoveCrit(target);\n\t\t\t\t}\n\t\t\t\tif (target.SubstituteHP == 0)\n\t\t\t\t{\n\t\t\t\t\tBroadcastStatus2(target, user, PBEStatus2.Substitute, PBEStatusAction.Ended);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tvoid DoNormal(List<PBEAttackVictim> normals)\n\t\t{\n\t\t\tforeach (PBEAttackVictim victim in normals)\n\t\t\t{\n\t\t\t\tCalcDamage(victim);\n\t\t\t}\n\t\t\tHit_DoMoveResult(user, normals);\n\t\t\tHit_DoCrit(normals);\n\t\t\tforeach (PBEAttackVictim victim in normals)\n\t\t\t{\n\t\t\t\tPBEBattlePokemon target = victim.Pkmn;\n\t\t\t\t// Stats/statuses are changed before post-hit effects\n\t\t\t\t// HP-draining moves restore HP\n\t\t\t\tbeforePostHit?.Invoke(target, victim.Damage); // TODO: LiquidOoze fainting/healing\n\t\t\t\tDoPostHitEffects(user, target, mData, moveType);\n\t\t\t\t// ShadowForce destroys protection\n\t\t\t\t// SmellingSalt cures paralysis\n\t\t\t\t// WakeUpSlap cures sleep\n\t\t\t\tafterPostHit?.Invoke(target); // Verified: These happen before Recoil/LifeOrb\n\t\t\t}\n\t\t\tHit_FaintCheck(normals);\n\t\t}\n\n\t\tHit_HitTargets(user.Team, DoSub, DoNormal, victims, out List<PBEAttackVictim> allies, out List<PBEAttackVictim> foes);\n\t\tDoPostAttackedEffects(user, allies, foes, true, recoilDamage: recoilFunc?.Invoke(totalDamageDealt), colorChangeType: moveType);\n\t}\n\t// None of these moves are multi-target\n\tprivate void FixedDamageHit(PBEBattlePokemon user, PBEBattlePokemon[] targets, IPBEMoveData mData, Func<PBEBattlePokemon, int> damageFunc,\n\t\tFunc<PBEBattlePokemon, PBEResult>? failFunc = null,\n\t\tAction? beforePostHit = null)\n\t{\n\t\tPBEType moveType = user.GetMoveType(mData);\n\t\t// Endeavor fails if the target's HP is <= the user's HP\n\t\t// One hit knockout moves fail if the target's level is > the user's level\n\t\tHit_GetVictims(user, targets, mData, moveType, out List<PBEAttackVictim> victims, failFunc: failFunc);\n\t\tif (victims.Count == 0)\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\t// BUG: Gems activate for these moves despite base power not being involved\n\t\tif (!Settings.BugFix)\n\t\t{\n\t\t\t_ = CalculateBasePower(user, targets, mData, moveType);\n\t\t}\n\t\tvoid CalcDamage(PBEAttackVictim victim)\n\t\t{\n\t\t\tPBEBattlePokemon target = victim.Pkmn;\n\t\t\t// FinalGambit user faints here\n\t\t\tvictim.Damage = DealDamage(user, target, damageFunc.Invoke(target));\n\t\t}\n\t\tvoid DoSub(List<PBEAttackVictim> subs)\n\t\t{\n\t\t\tforeach (PBEAttackVictim victim in subs)\n\t\t\t{\n\t\t\t\tCalcDamage(victim);\n\t\t\t\tPBEBattlePokemon target = victim.Pkmn;\n\t\t\t\tif (target.SubstituteHP == 0)\n\t\t\t\t{\n\t\t\t\t\tBroadcastStatus2(target, user, PBEStatus2.Substitute, PBEStatusAction.Ended);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tvoid DoNormal(List<PBEAttackVictim> normals)\n\t\t{\n\t\t\tforeach (PBEAttackVictim victim in normals)\n\t\t\t{\n\t\t\t\tCalcDamage(victim);\n\t\t\t}\n\t\t\tforeach (PBEAttackVictim victim in normals)\n\t\t\t{\n\t\t\t\tPBEBattlePokemon target = victim.Pkmn;\n\t\t\t\t// \"It's a one-hit KO!\"\n\t\t\t\tbeforePostHit?.Invoke();\n\t\t\t\tDoPostHitEffects(user, target, mData, moveType);\n\t\t\t}\n\t\t\tHit_FaintCheck(normals);\n\t\t}\n\n\t\tHit_HitTargets(user.Team, DoSub, DoNormal, victims, out List<PBEAttackVictim> allies, out List<PBEAttackVictim> foes);\n\t\tDoPostAttackedEffects(user, allies, foes, false, colorChangeType: moveType);\n\t}\n\t// None of these moves are multi-target\n\tprivate void MultiHit(PBEBattlePokemon user, PBEBattlePokemon[] targets, IPBEMoveData mData, byte numHits,\n\t\tbool subsequentMissChecks = false,\n\t\tAction<PBEBattlePokemon>? beforePostHit = null)\n\t{\n\t\tPBEType moveType = user.GetMoveType(mData);\n\t\tHit_GetVictims(user, targets, mData, moveType, out List<PBEAttackVictim> victims);\n\t\tif (victims.Count == 0)\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\tfloat basePower = CalculateBasePower(user, targets, mData, moveType); // Verified: Gem boost applies to all hits\n\t\tfloat initDamageMultiplier = victims.Count > 1 ? 0.75f : 1;\n\t\tvoid CalcDamage(PBEAttackVictim victim)\n\t\t{\n\t\t\tPBEBattlePokemon target = victim.Pkmn;\n\t\t\tPBEResult result = victim.Result;\n\t\t\tfloat damageMultiplier = initDamageMultiplier * victim.TypeEffectiveness;\n\t\t\tbool crit = CritCheck(user, target, mData);\n\t\t\tdamageMultiplier *= CalculateDamageMultiplier(user, target, mData, moveType, result, crit);\n\t\t\tint damage = (int)(damageMultiplier * CalculateDamage(user, target, mData, moveType, basePower, crit));\n\t\t\tvictim.Damage = DealDamage(user, target, damage, ignoreSubstitute: false, ignoreSturdy: false);\n\t\t\tvictim.Crit = crit;\n\t\t}\n\t\tvoid DoSub(List<PBEAttackVictim> subs)\n\t\t{\n\t\t\tforeach (PBEAttackVictim victim in subs)\n\t\t\t{\n\t\t\t\tCalcDamage(victim);\n\t\t\t\tPBEBattlePokemon target = victim.Pkmn;\n\t\t\t\tif (victim.Crit)\n\t\t\t\t{\n\t\t\t\t\tBroadcastMoveCrit(target);\n\t\t\t\t}\n\t\t\t\tif (target.SubstituteHP == 0)\n\t\t\t\t{\n\t\t\t\t\tBroadcastStatus2(target, user, PBEStatus2.Substitute, PBEStatusAction.Ended);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tvoid DoNormal(List<PBEAttackVictim> normals)\n\t\t{\n\t\t\tnormals.RemoveAll(v => v.Pkmn.HP == 0); // Remove ones that fainted from previous hits\n\t\t\tforeach (PBEAttackVictim victim in normals)\n\t\t\t{\n\t\t\t\tCalcDamage(victim);\n\t\t\t}\n\t\t\tHit_DoCrit(normals);\n\t\t\tforeach (PBEAttackVictim victim in normals)\n\t\t\t{\n\t\t\t\tPBEBattlePokemon target = victim.Pkmn;\n\t\t\t\t// Twineedle has a chance to poison on each strike\n\t\t\t\tbeforePostHit?.Invoke(target);\n\t\t\t\tDoPostHitEffects(user, target, mData, moveType);\n\t\t\t}\n\t\t}\n\n\t\tbyte hit = 0;\n\t\tList<PBEAttackVictim> allies, foes;\n\t\tdo\n\t\t{\n\t\t\tHit_HitTargets(user.Team, DoSub, DoNormal, victims, out allies, out foes);\n\t\t\thit++;\n\t\t} while (hit < numHits && user.HP > 0 && user.Status1 != PBEStatus1.Asleep && victims.FindIndex(v => v.Pkmn.HP > 0) != -1);\n\t\tHit_DoMoveResult(user, allies);\n\t\tHit_DoMoveResult(user, foes);\n\t\tBroadcastMultiHit(hit);\n\t\tHit_FaintCheck(allies);\n\t\tHit_FaintCheck(foes);\n\t\tDoPostAttackedEffects(user, allies, foes, true, colorChangeType: moveType);\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Battle/BattleEnums.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data;\nusing System;\n\nnamespace Kermalis.PokemonBattleEngine.Battle;\n\n/// <summary>Represents the battle's terrain.</summary>\npublic enum PBEBattleTerrain : byte\n{\n\tCave, // Rock, RockSlide, RockThrow\n\tGrass, // Grass, SeedBomb, NeedleArm\n\t/// <summary>Used for bridges, buildings, and link battles.</summary>\n\tPlain, // Normal, TriAttack, BodySlam\n\tPuddle, // Ground, MudBomb, MudShot\n\tSand, // Ground, Earthquake, MudSlap\n\tSnow, // Ice, Blizzard, Avalanche\n\tWater, // Water, HydroPump, WaterPulse\n\tMAX\n}\n/// <summary>Represents the format of a specific battle.</summary>\npublic enum PBEBattleFormat : byte\n{\n\t/// <summary>A 1v1 battle. Each Pokémon is able to use moves or switch out for another Pokémon.</summary>\n\tSingle,\n\t/// <summary>A 2v2 battle where all Pokémon are able to use moves or switch out for another Pokémon.</summary>\n\tDouble,\n\t/// <summary>A 3v3 battle where all Pokémon are able to use moves, shift positions with a teammate, or switch out for another Pokémon.</summary>\n\tTriple,\n\t/// <summary>A 3v3 battle where only the front Pokémon are able to force a team rotation, use a move, or switch out for another Pokémon.</summary>\n\t/// <remarks>Team rotation does not take up a turn and can be done once per turn.</remarks>\n\tRotation,\n\t/// <summary>Invalid battle format.</summary>\n\tMAX\n}\npublic enum PBEBattleType : byte\n{\n\tTrainer,\n\tWild\n}\n/// <summary>Represents the current state of a specific battle.</summary>\npublic enum PBEBattleState : byte\n{\n\t/// <summary>The battle is waiting for teams.</summary>\n\tWaitingForPlayers,\n\t/// <summary>The battle is ready to begin.</summary>\n\tReadyToBegin,\n\t/// <summary>The battle is waiting for players to send actions.</summary>\n\tWaitingForActions,\n\t/// <summary>The battle is ready to run a turn.</summary>\n\tReadyToRunTurn,\n\t/// <summary>The battle is processing.</summary>\n\tProcessing,\n\t/// <summary>The battle is waiting for players to send switch-ins.</summary>\n\tWaitingForSwitchIns,\n\t/// <summary>The battle is ready to run switches.</summary>\n\tReadyToRunSwitches,\n\t/// <summary>The battle ended.</summary>\n\tEnded\n}\n/// <summary>Represents the result of an ended battle.</summary>\npublic enum PBEBattleResult : byte\n{\n\t/// <summary>Team 0 forfeited.</summary>\n\tTeam0Forfeit,\n\t/// <summary>Team 0 defeated Team 1.</summary>\n\tTeam0Win,\n\t/// <summary>Team 1 forfeited.</summary>\n\tTeam1Forfeit,\n\t/// <summary>Team 1 defeated Team 0.</summary>\n\tTeam1Win,\n\t/// <summary>A wild Pokémon was captured.</summary>\n\tWildCapture,\n\t/// <summary>The trainer(s) escaped from the wild Pokémon.</summary>\n\tWildEscape,\n\t/// <summary>A wild Pokémon escaped from the trainer(s).</summary>\n\tWildFlee\n}\n/// <summary>Represents the weather in a specific battle.</summary>\npublic enum PBEWeather : byte\n{\n\t/// <summary>There is no weather.</summary>\n\tNone,\n\t/// <summary>It is hailing.</summary>\n\tHailstorm,\n\t/// <summary>The sunlight is harsh.</summary>\n\tHarshSunlight,\n\t/// <summary>It is raining.</summary>\n\tRain,\n\t/// <summary>A sandstorm is brewing.</summary>\n\tSandstorm,\n\tMAX\n}\n/// <summary>Represents a position on the battle field.</summary>\npublic enum PBEFieldPosition : byte\n{\n\t/// <summary>A Pokémon is not on the field.</summary>\n\tNone,\n\t/// <summary>The Pokémon to a player's left in a Double, Triple, or Rotation battle.</summary>\n\tLeft,\n\t/// <summary>The Pokémon in the center of the field in a Single, Triple, or Rotation battle.</summary>\n\tCenter,\n\t/// <summary>The Pokémon to a player's right in a Double, Triple, or Rotation battle.</summary>\n\tRight,\n\tMAX\n}\n/// <summary>Represents a <see cref=\"PBEMove\"/>'s targets.</summary>\n[Flags]\npublic enum PBETurnTarget : byte\n{\n\t/// <summary>The Pokémon has not chosen any targets.</summary>\n\tNone,\n\t/// <summary>The move is targetting the player's left Pokémon.</summary>\n\tAllyLeft = 1 << 0,\n\t/// <summary>The move is targetting the player's center Pokémon.</summary>\n\tAllyCenter = 1 << 1,\n\t/// <summary>The move is targetting the player's right Pokémon.</summary>\n\tAllyRight = 1 << 2,\n\t/// <summary>The move is targetting the opponent's left Pokémon.</summary>\n\tFoeLeft = 1 << 3,\n\t/// <summary>The move is targetting the opponent's center Pokémon.</summary>\n\tFoeCenter = 1 << 4,\n\t/// <summary>The move is targetting the opponent's right Pokémon.</summary>\n\tFoeRight = 1 << 5\n}\n/// <summary>Represents a Pokémon's decision for a turn.</summary>\npublic enum PBETurnDecision : byte\n{\n\t/// <summary>The Pokémon has not made a decision.</summary>\n\tNone,\n\t/// <summary>The Pokémon has chosen to use a move.</summary>\n\tFight,\n\t/// <summary>The Pokémon has chosen to use an item.</summary>\n\tItem,\n\t/// <summary>The Pokémon has chosen to switch out for another Pokémon.</summary>\n\tSwitchOut,\n\t/// <summary>The wild Pokémon has chosen to use flee.</summary>\n\tWildFlee\n}\n/// <summary>Represents a specific Pokémon's non-volatile status.</summary>\npublic enum PBEStatus1 : byte\n{\n\t/// <summary>The Pokémon has no status.</summary>\n\tNone,\n\t/// <summary>The Pokémon is asleep.</summary>\n\tAsleep,\n\t/// <summary>The Pokémon is badly poisoned.</summary>\n\tBadlyPoisoned,\n\t/// <summary>The Pokémon is burned.</summary>\n\tBurned,\n\t/// <summary>The Pokémon is frozen.</summary>\n\tFrozen,\n\t/// <summary>The Pokémon is paralyzed.</summary>\n\tParalyzed,\n\t/// <summary>The Pokémon is poisoned.</summary>\n\tPoisoned,\n\tMAX\n}\n/// <summary>Represents a specific Pokémon's volatile status.</summary>\n[Flags]\npublic enum PBEStatus2 : uint\n{\n\t/// <summary>The Pokémon has no status.</summary>\n\tNone,\n\t/// <summary>The Pokémon is high up in the air. A move will miss against the Pokémon unless it has <see cref=\"PBEMoveFlag.HitsAirborne\"/> or either Pokémon has <see cref=\"PBEAbility.NoGuard\"/>.</summary>\n\tAirborne = 1 << 0,\n\t/// <summary>The Pokémon is confused and may hurt itself instead of execute its chosen move.</summary>\n\tConfused = 1 << 1,\n\t/// <summary>The Pokémon is cursed and will take damage at the end of each turn.</summary>\n\tCursed = 1 << 2,\n\t/// <summary>The Pokémon is disguised with <see cref=\"PBEAbility.Illusion\"/>.</summary>\n\tDisguised = 1 << 3,\n\t/// <summary>The Pokémon is flinching and will be unable to move this turn.</summary>\n\tFlinching = 1 << 4,\n\t/// <summary>The Pokémon will gain a power boost due to <see cref=\"PBEMoveEffect.HelpingHand\"/>.</summary>\n\tHelpingHand = 1 << 5,\n\tIdentified = 1 << 6,\n\t/// <summary>The Pokémon is infatuated with <see cref=\"PBEBattlePokemon.InfatuatedWithPokemon\"/> and may be unable to move this turn.</summary>\n\tInfatuated = 1 << 7,\n\t/// <summary>The Pokémon is seeded and HP will be stolen at the end of each turn.</summary>\n\tLeechSeed = 1 << 8,\n\tLockOn = 1 << 9,\n\tMagnetRise = 1 << 10,\n\tMiracleEye = 1 << 11,\n\tNightmare = 1 << 12,\n\t/// <summary>The Pokémon's <see cref=\"PBEStat.Attack\"/> and <see cref=\"PBEStat.Defense\"/> are switched.</summary>\n\tPowerTrick = 1 << 13,\n\t/// <summary>The Pokémon is protected from moves this turn.</summary>\n\tProtected = 1 << 14,\n\t/// <summary>The Pokémon is under the effect of <see cref=\"PBEMoveEffect.FocusEnergy\"/> or <see cref=\"PBEItem.LansatBerry\"/> and has a higher chance of landing critical hits.</summary>\n\tPumped = 1 << 15,\n\tRoost = 1 << 16,\n\tShadowForce = 1 << 17,\n\t/// <summary>The Pokémon is behind a substitute that will take damage on behalf of the Pokémon and prevent most moves from affecting the Pokémon.</summary>\n\tSubstitute = 1 << 18,\n\t/// <summary>The Pokémon is transformed into another Pokémon.</summary>\n\tTransformed = 1 << 19,\n\t/// <summary>The Pokémon is underground. A move will miss against the Pokémon unless it has <see cref=\"PBEMoveFlag.HitsUnderground\"/> or either Pokémon has <see cref=\"PBEAbility.NoGuard\"/>.</summary>\n\tUnderground = 1 << 20,\n\t/// <summary>The Pokémon is underwater. A move will miss against the Pokémon unless it has <see cref=\"PBEMoveFlag.HitsUnderwater\"/> or either Pokémon has <see cref=\"PBEAbility.NoGuard\"/>.</summary>\n\tUnderwater = 1 << 21\n}\n/// <summary>Keeps track of which types <see cref=\"PBEStatus2.Roost\"/> changes to/from <see cref=\"PBEType.Flying\"/>.</summary>\n[Flags]\npublic enum PBERoostTypes : byte\n{\n\tNone,\n\tKnownType1 = 1 << 0,\n\tKnownType2 = 1 << 1,\n\tType1 = 1 << 2,\n\tType2 = 1 << 3\n}\n/// <summary>Represents a specific <see cref=\"PBEBattle\"/>'s status.</summary>\n[Flags]\npublic enum PBEBattleStatus : byte\n{\n\t/// <summary>The battle has no status.</summary>\n\tNone,\n\t/// <summary>The acting order of Pokémon in this battle is reversed.</summary>\n\tTrickRoom = 1 << 0 // TODO: Full Incense, Lagging Tail, Stall, Quick Claw\n}\n/// <summary>Represents a specific <see cref=\"PBETeam\"/>'s status.</summary>\n[Flags]\npublic enum PBETeamStatus : ushort\n{\n\t/// <summary>The team has no status.</summary>\n\tNone,\n\t/// <summary>The team will take less damage from <see cref=\"PBEMoveCategory.Special\"/> moves.</summary>\n\tLightScreen = 1 << 0,\n\t/// <summary>The team is shielded from critical hits.</summary>\n\tLuckyChant = 1 << 1,\n\tQuickGuard = 1 << 2,\n\t/// <summary>The team will take less damage from <see cref=\"PBEMoveCategory.Physical\"/> moves.</summary>\n\tReflect = 1 << 3,\n\tSafeguard = 1 << 4,\n\t/// <summary>Grounded Pokémon that switch in will take damage. The amount of damage is based on <see cref=\"PBETeam.SpikeCount\"/>. </summary>\n\tSpikes = 1 << 5, // TODO: Gravity, magic guard, iron ball, baton pass with ingrain, air balloon\n\t/// <summary>Pokémon that switch in will take damage. The amount of damage is based on the effectiveness of <see cref=\"PBEType.Rock\"/> on the Pokémon. </summary>\n\tStealthRock = 1 << 6, // TODO: magic guard\n\tTailwind = 1 << 7,\n\t/// <summary>Grounded Pokémon that switch in will be <see cref=\"PBEStatus1.Poisoned\"/> if <see cref=\"PBETeam.ToxicSpikeCount\"/> is 1 or <see cref=\"PBEStatus1.BadlyPoisoned\"/> if it is 2.\n\t/// Grounded <see cref=\"PBEType.Poison\"/> Pokémon will remove toxic spikes.</summary>\n\tToxicSpikes = 1 << 8, // TODO: Gravity, magic guard, iron ball, baton pass with ingrain, air balloon, synchronize with roar/whirlwind\n\t/// <summary>The team is protected from spread moves for a turn.</summary>\n\tWideGuard = 1 << 9\n}\n/// <summary>Represents an action regarding a <see cref=\"PBEAbility\"/>.</summary>\npublic enum PBEAbilityAction : byte\n{\n\t/// <summary>The ability is first announced.</summary>\n\tAnnounced = 0,\n\t/// <summary>The ability caused a Pokémon to change its appearance.</summary>\n\tChangedAppearance = 1,\n\t/// <summary>The ability changed a Pokémon's <see cref=\"PBEStatus1\"/> or <see cref=\"PBEStatus2\"/>.</summary>\n\tChangedStatus = 2,\n\t/// <summary>The ability was involved with damage.</summary>\n\tDamage = 3,\n\t/// <summary>The ability prevented a Pokémon from being inflicted with a <see cref=\"PBEStatus1\"/> or <see cref=\"PBEStatus2\"/>.</summary>\n\tPreventedStatus = 4,\n\t/// <summary>The ability restored a Pokémon's HP.</summary>\n\tRestoredHP = 5,\n\t/// <summary><see cref=\"PBEAbility.SlowStart\"/> ended.</summary>\n\tSlowStart_Ended = 6,\n\t/// <summary>The ability was involved with stats.</summary>\n\tStats = 7,\n\t/// <summary>The ability was involved with weather.</summary>\n\tWeather = 8\n}\n/// <summary>Represents an action regarding a <see cref=\"PBEItem\"/>.</summary>\npublic enum PBEItemAction : byte\n{\n\t/// <summary>The item initiated its main effect.</summary>\n\tAnnounced = 0,\n\t/// <summary>The item was consumed by a Pokémon.</summary>\n\tConsumed = 1,\n\t/// <summary>The item was involved with damage.</summary>\n\tDamage = 2,\n\t/// <summary>The item restored HP to a Pokémon.</summary>\n\tRestoredHP = 3\n}\npublic enum PBEItemTurnAction : byte\n{\n\tNoEffect = 0,\n\tAttempt = 1,\n\tSuccess = 2\n}\n/// <summary>Represents an action regarding a <see cref=\"PBEStatus1\"/> or <see cref=\"PBEStatus2\"/>.</summary>\npublic enum PBEStatusAction : byte\n{\n\t/// <summary>The status was added to a Pokémon.</summary>\n\t/// <example>The Pokémon became <see cref=\"PBEStatus1.Paralyzed\"/>.</example>\n\tAdded = 0,\n\t/// <summary>The status activated its main effect.</summary>\n\t/// <example><see cref=\"PBEStatus2.Infatuated\"/> states the Pokémon is in love.</example>\n\tAnnounced = 1,\n\t/// <summary>The status caused a Pokémon to be immobile.</summary>\n\t/// <example><see cref=\"PBEStatus2.Flinching\"/> prevented movement.</example>\n\tCausedImmobility = 2,\n\t/// <summary>The status was cured from a Pokémon.</summary>\n\t/// <example><see cref=\"PBEAbility.Limber\"/> cured a Pokémon of <see cref=\"PBEStatus1.Paralyzed\"/>.</example>\n\tCleared = 3,\n\t/// <summary>The status was involved with damage.</summary>\n\t/// <example>A Pokémon's <see cref=\"PBEStatus2.Substitute\"/> took damage.</example>\n\tDamage = 4,\n\t/// <summary>The status has ended naturally.</summary>\n\t/// <example>A Pokémon with <see cref=\"PBEStatus2.Confused\"/> regained its senses.</example>\n\tEnded = 5\n}\npublic enum PBEBattleStatusAction : byte\n{\n\tAdded = 0,\n\tCleared = 1,\n\tEnded = 2\n}\n/// <summary>Represents an action regarding a <see cref=\"PBETeamStatus\"/>.</summary>\npublic enum PBETeamStatusAction : byte\n{\n\t/// <summary>The status was added to a team.</summary>\n\t/// <example>A team set up <see cref=\"PBETeamStatus.LightScreen\"/>.</example>\n\tAdded = 0,\n\t/// <summary>The status was forcefully removed from a team.</summary>\n\t/// <example>A Pokémon used <see cref=\"PBEMoveEffect.BrickBreak\"/> and destroyed <see cref=\"PBETeamStatus.Reflect\"/>.</example>\n\tCleared = 1,\n\t/// <summary>The status ended naturally.</summary>\n\t/// <example><see cref=\"PBETeamStatus.LightScreen\"/> wore off.</example>\n\tEnded = 2\n}\n/// <summary>Represents the result of an intention.</summary>\npublic enum PBEResult : byte\n{\n\t/// <summary>No failure.</summary>\n\tSuccess = 0,\n\t/// <summary>Failure due to a <see cref=\"PBEAbility\"/>.</summary>\n\tIneffective_Ability = 1,\n\t/// <summary>Failure due to a <see cref=\"PBEGender\"/>.</summary>\n\tIneffective_Gender = 2,\n\t/// <summary>Failure due to a Pokémon's level.</summary>\n\tIneffective_Level = 3,\n\t/// <summary>Failure due to <see cref=\"PBEStatus2.MagnetRise\"/>.</summary>\n\tIneffective_MagnetRise = 4,\n\t/// <summary>Failure due to <see cref=\"PBETeamStatus.Safeguard\"/>.</summary>\n\tIneffective_Safeguard = 5,\n\t/// <summary>Failure due to a <see cref=\"PBEStat\"/>.</summary>\n\tIneffective_Stat = 6,\n\t/// <summary>Failure due to a <see cref=\"PBEStatus1\"/>, <see cref=\"PBEStatus2\"/>, <see cref=\"PBETeamStatus\"/>, <see cref=\"PBEBattleStatus\"/>, or <see cref=\"PBEWeather\"/>.</summary>\n\tIneffective_Status = 7,\n\t/// <summary>Failure due to <see cref=\"PBEStatus2.Substitute\"/>.</summary>\n\tIneffective_Substitute = 8,\n\t/// <summary>Failure due to a <see cref=\"PBEType\"/>.</summary>\n\tIneffective_Type = 9,\n\t/// <summary>Failure due to the intention's unmet special conditions.</summary>\n\tInvalidConditions = 10,\n\t/// <summary>Failure due to accuracy and/or evasion.</summary>\n\tMissed = 11,\n\t/// <summary>Failure due to having no available targets.</summary>\n\tNoTarget = 12,\n\t/// <summary>Limited success due to a Pokémon's <see cref=\"PBEType\"/>.</summary>\n\tNotVeryEffective_Type = 13,\n\t/// <summary>Great success due to a Pokémon's <see cref=\"PBEType\"/>.</summary>\n\tSuperEffective_Type = 14\n}\n/// <summary>Represents an action regarding a <see cref=\"PBEWeather\"/>.</summary>\npublic enum PBEWeatherAction : byte\n{\n\t/// <summary>The weather was added to the battle.</summary>\n\tAdded = 0,\n\t/// <summary>The weather was removed from the battle.</summary>\n\tEnded = 1\n}\npublic enum PBESpecialMessage : byte\n{\n\tDraggedOut = 0,\n\tEndure = 1,\n\tHPDrained = 2,\n\tMagnitude = 3,\n\tMultiHit = 4,\n\tNothingHappened = 5,\n\tOneHitKnockout = 6,\n\tPainSplit = 7,\n\tPayDay = 8,\n\tRecoil = 9,\n\tStruggle = 10\n}\npublic enum PBEMoveLockType : byte\n{\n\tChoiceItem = 0,\n\tTemporary = 1\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Battle/BattleEvents.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Data.Utils;\nusing Kermalis.PokemonBattleEngine.Packets;\nusing Kermalis.PokemonBattleEngine.Utils;\nusing System;\nusing System.IO;\nusing System.Linq;\n\nnamespace Kermalis.PokemonBattleEngine.Battle;\n\npublic sealed partial class PBEBattle\n{\n\tpublic delegate void BattleEvent(PBEBattle battle, IPBEPacket packet);\n\tpublic event BattleEvent? OnNewEvent;\n\n\tprivate void Broadcast(IPBEPacket packet)\n\t{\n\t\tEvents.Add(packet);\n\t\tOnNewEvent?.Invoke(this, packet);\n\t}\n\n\tprivate void BroadcastAbility(PBEBattlePokemon abilityOwner, PBEBattlePokemon pokemon2, PBEAbility ability, PBEAbilityAction abilityAction)\n\t{\n\t\tabilityOwner.Ability = ability;\n\t\tabilityOwner.KnownAbility = ability;\n\t\tBroadcast(new PBEAbilityPacket(abilityOwner, pokemon2, ability, abilityAction));\n\t}\n\tprivate void BroadcastAbilityReplaced(PBEBattlePokemon abilityOwner, PBEAbility newAbility)\n\t{\n\t\tPBEAbility? oldAbility = newAbility == PBEAbility.None ? null : abilityOwner.Ability; // Gastro Acid does not reveal previous ability\n\t\tabilityOwner.Ability = newAbility;\n\t\tabilityOwner.KnownAbility = newAbility;\n\t\tBroadcast(new PBEAbilityReplacedPacket(abilityOwner, oldAbility, newAbility));\n\t}\n\tprivate void BroadcastBattleStatus(PBEBattleStatus battleStatus, PBEBattleStatusAction battleStatusAction)\n\t{\n\t\tswitch (battleStatusAction)\n\t\t{\n\t\t\tcase PBEBattleStatusAction.Added: BattleStatus |= battleStatus; break;\n\t\t\tcase PBEBattleStatusAction.Cleared:\n\t\t\tcase PBEBattleStatusAction.Ended: BattleStatus &= ~battleStatus; break;\n\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(battleStatusAction));\n\t\t}\n\t\tBroadcast(new PBEBattleStatusPacket(battleStatus, battleStatusAction));\n\t}\n\tprivate void BroadcastCapture(PBEBattlePokemon pokemon, PBEItem ball, byte numShakes, bool success, bool critical)\n\t{\n\t\tBroadcast(new PBECapturePacket(pokemon, ball, numShakes, success, critical));\n\t}\n\tprivate void BroadcastFleeFailed(PBEBattlePokemon pokemon)\n\t{\n\t\tBroadcast(new PBEFleeFailedPacket(pokemon));\n\t}\n\tprivate void BroadcastHaze()\n\t{\n\t\tBroadcast(new PBEHazePacket());\n\t}\n\tprivate void BroadcastIllusion(PBEBattlePokemon pokemon)\n\t{\n\t\tBroadcast(new PBEIllusionPacket(pokemon));\n\t}\n\tprivate void BroadcastItem(PBEBattlePokemon itemHolder, PBEBattlePokemon pokemon2, PBEItem item, PBEItemAction itemAction)\n\t{\n\t\tswitch (itemAction)\n\t\t{\n\t\t\tcase PBEItemAction.Consumed:\n\t\t\t{\n\t\t\t\titemHolder.Item = PBEItem.None;\n\t\t\t\titemHolder.KnownItem = PBEItem.None;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\titemHolder.Item = item;\n\t\t\t\titemHolder.KnownItem = item;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tBroadcast(new PBEItemPacket(itemHolder, pokemon2, item, itemAction));\n\t}\n\tprivate void BroadcastItemTurn(PBEBattlePokemon itemUser, PBEItem item, PBEItemTurnAction itemAction)\n\t{\n\t\tBroadcast(new PBEItemTurnPacket(itemUser, item, itemAction));\n\t}\n\tprivate void BroadcastMoveCrit(PBEBattlePokemon victim)\n\t{\n\t\tBroadcast(new PBEMoveCritPacket(victim));\n\t}\n\tprivate void BroadcastMoveLock_ChoiceItem(PBEBattlePokemon moveUser, PBEMove lockedMove)\n\t{\n\t\tmoveUser.ChoiceLockedMove = lockedMove;\n\t\tBroadcast(new PBEMoveLockPacket(moveUser, PBEMoveLockType.ChoiceItem, lockedMove));\n\t}\n\tprivate void BroadcastMoveLock_Temporary(PBEBattlePokemon moveUser, PBEMove lockedMove, PBETurnTarget lockedTargets)\n\t{\n\t\tmoveUser.TempLockedMove = lockedMove;\n\t\tmoveUser.TempLockedTargets = lockedTargets;\n\t\tBroadcast(new PBEMoveLockPacket(moveUser, PBEMoveLockType.Temporary, lockedMove, lockedTargets));\n\t}\n\tprivate void BroadcastMovePPChanged(PBEBattlePokemon moveUser, PBEMove move, int amountReduced)\n\t{\n\t\tBroadcast(new PBEMovePPChangedPacket(moveUser, move, amountReduced));\n\t}\n\tprivate void BroadcastMoveResult(PBEBattlePokemon moveUser, PBEBattlePokemon pokemon2, PBEResult result)\n\t{\n\t\tBroadcast(new PBEMoveResultPacket(moveUser, pokemon2, result));\n\t}\n\tprivate void BroadcastMoveUsed(PBEBattlePokemon moveUser, PBEMove move)\n\t{\n\t\tbool owned;\n\t\tif (!_calledFromOtherMove && moveUser.Moves.Contains(move))\n\t\t{\n\t\t\t// Check if this move is known first. If you check for PBEMove.MAX then you will get multiple results\n\t\t\tif (!moveUser.KnownMoves.Contains(move))\n\t\t\t{\n\t\t\t\tmoveUser.KnownMoves[PBEMove.MAX]!.Move = move;\n\t\t\t}\n\t\t\towned = true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\towned = false;\n\t\t}\n\t\tBroadcast(new PBEMoveUsedPacket(moveUser, move, owned));\n\t}\n\tprivate void BroadcastPkmnEXPChanged(PBEBattlePokemon pokemon, uint oldEXP)\n\t{\n\t\tBroadcast(new PBEPkmnEXPChangedPacket(pokemon, oldEXP));\n\t}\n\tprivate void BroadcastPkmnEXPEarned(PBEBattlePokemon pokemon, uint earned)\n\t{\n\t\tBroadcast(new PBEPkmnEXPEarnedPacket(pokemon, earned));\n\t}\n\tprivate void BroadcastPkmnFainted(PBEBattlePokemon pokemon, PBEFieldPosition oldPosition)\n\t{\n\t\tBroadcast(new PBEPkmnFaintedPacket(pokemon, oldPosition));\n\t}\n\tprivate void BroadcastPkmnFormChanged(PBEBattlePokemon pokemon, PBEForm newForm, PBEAbility newAbility, PBEAbility newKnownAbility, bool isRevertForm)\n\t{\n\t\tpokemon.Ability = newAbility;\n\t\tpokemon.KnownAbility = newKnownAbility;\n\t\tpokemon.Form = newForm;\n\t\tpokemon.KnownForm = newForm;\n\t\tif (isRevertForm)\n\t\t{\n\t\t\tpokemon.RevertForm = newForm;\n\t\t\tpokemon.RevertAbility = newAbility;\n\t\t}\n\t\t// This calcs all stats and then adds/removes HP based on the new MaxHP. So if the new MaxHP was 5 more than old, the mon would gain 5 HP.\n\t\t// This is the same logic a level-up and evolution would use when HP changes.\n\t\tpokemon.SetStats(true, false);\n\t\tIPBEPokemonData pData = PBEDataProvider.Instance.GetPokemonData(pokemon.Species, newForm);\n\t\tPBEType type1 = pData.Type1;\n\t\tpokemon.Type1 = type1;\n\t\tpokemon.KnownType1 = type1;\n\t\tPBEType type2 = pData.Type2;\n\t\tpokemon.Type2 = type2;\n\t\tpokemon.KnownType2 = type2;\n\t\tfloat weight = pData.Weight; // TODO: Is weight updated here? Bulbapedia claims in Autotomize's page that it is not\n\t\tpokemon.Weight = weight;\n\t\tpokemon.KnownWeight = weight;\n\t\tBroadcast(new PBEPkmnFormChangedPacket(pokemon, isRevertForm));\n\t\t// BUG: PBEStatus2.PowerTrick is not cleared when changing form (meaning it can still be baton passed)\n\t\tif (Settings.BugFix && pokemon.Status2.HasFlag(PBEStatus2.PowerTrick))\n\t\t{\n\t\t\tBroadcastStatus2(pokemon, pokemon, PBEStatus2.PowerTrick, PBEStatusAction.Ended);\n\t\t}\n\t}\n\tprivate void BroadcastPkmnHPChanged(PBEBattlePokemon pokemon, ushort oldHP, float oldHPPercentage)\n\t{\n\t\tBroadcast(new PBEPkmnHPChangedPacket(pokemon, oldHP, oldHPPercentage));\n\t}\n\tprivate void BroadcastPkmnLevelChanged(PBEBattlePokemon pokemon)\n\t{\n\t\tBroadcast(new PBEPkmnLevelChangedPacket(pokemon));\n\t}\n\tprivate void BroadcastPkmnStatChanged(PBEBattlePokemon pokemon, PBEStat stat, sbyte oldValue, sbyte newValue)\n\t{\n\t\tBroadcast(new PBEPkmnStatChangedPacket(pokemon, stat, oldValue, newValue));\n\t}\n\tprivate void BroadcastPkmnSwitchIn(PBETrainer trainer, PBEPkmnAppearedInfo[] switchIns, PBEBattlePokemon? forcedByPokemon = null)\n\t{\n\t\tBroadcast(new PBEPkmnSwitchInPacket(trainer, switchIns, forcedByPokemon));\n\t}\n\tprivate void BroadcastPkmnSwitchOut(PBEBattlePokemon pokemon, PBEFieldPosition oldPosition, PBEBattlePokemon? forcedByPokemon = null)\n\t{\n\t\tBroadcast(new PBEPkmnSwitchOutPacket(pokemon, oldPosition, forcedByPokemon));\n\t}\n\tprivate void BroadcastPsychUp(PBEBattlePokemon user, PBEBattlePokemon target)\n\t{\n\t\tuser.AttackChange = target.AttackChange;\n\t\tuser.DefenseChange = target.DefenseChange;\n\t\tuser.SpAttackChange = target.SpAttackChange;\n\t\tuser.SpDefenseChange = target.SpDefenseChange;\n\t\tuser.SpeedChange = target.SpeedChange;\n\t\tuser.AccuracyChange = target.AccuracyChange;\n\t\tuser.EvasionChange = target.EvasionChange;\n\t\tBroadcast(new PBEPsychUpPacket(user, target));\n\t}\n\tprivate void BroadcastReflectType(PBEBattlePokemon user, PBEBattlePokemon target)\n\t{\n\t\tuser.Type1 = user.KnownType1 = target.KnownType1 = target.Type1;\n\t\tuser.Type2 = user.KnownType2 = target.KnownType2 = target.Type2;\n\t\tBroadcast(new PBEReflectTypePacket(user, target));\n\t}\n\n\tprivate void BroadcastDraggedOut(PBEBattlePokemon pokemon)\n\t{\n\t\tBroadcast(new PBESpecialMessagePacket(PBESpecialMessage.DraggedOut, pokemon));\n\t}\n\tprivate void BroadcastEndure(PBEBattlePokemon pokemon)\n\t{\n\t\tBroadcast(new PBESpecialMessagePacket(PBESpecialMessage.Endure, pokemon));\n\t}\n\tprivate void BroadcastHPDrained(PBEBattlePokemon pokemon)\n\t{\n\t\tBroadcast(new PBESpecialMessagePacket(PBESpecialMessage.HPDrained, pokemon));\n\t}\n\tprivate void BroadcastMagnitude(byte magnitude)\n\t{\n\t\tBroadcast(new PBESpecialMessagePacket(PBESpecialMessage.Magnitude, magnitude));\n\t}\n\tprivate void BroadcastMultiHit(byte numHits)\n\t{\n\t\tBroadcast(new PBESpecialMessagePacket(PBESpecialMessage.MultiHit, numHits));\n\t}\n\tprivate void BroadcastNothingHappened()\n\t{\n\t\tBroadcast(new PBESpecialMessagePacket(PBESpecialMessage.NothingHappened));\n\t}\n\tprivate void BroadcastOneHitKnockout()\n\t{\n\t\tBroadcast(new PBESpecialMessagePacket(PBESpecialMessage.OneHitKnockout));\n\t}\n\tprivate void BroadcastPainSplit(PBEBattlePokemon user, PBEBattlePokemon target)\n\t{\n\t\tBroadcast(new PBESpecialMessagePacket(PBESpecialMessage.PainSplit, user, target));\n\t}\n\tprivate void BroadcastPayDay()\n\t{\n\t\tBroadcast(new PBESpecialMessagePacket(PBESpecialMessage.PayDay));\n\t}\n\tprivate void BroadcastRecoil(PBEBattlePokemon pokemon)\n\t{\n\t\tBroadcast(new PBESpecialMessagePacket(PBESpecialMessage.Recoil, pokemon));\n\t}\n\tprivate void BroadcastStruggle(PBEBattlePokemon pokemon)\n\t{\n\t\tBroadcast(new PBESpecialMessagePacket(PBESpecialMessage.Struggle, pokemon));\n\t}\n\n\tprivate void BroadcastStatus1(PBEBattlePokemon status1Receiver, PBEBattlePokemon pokemon2, PBEStatus1 status1, PBEStatusAction statusAction)\n\t{\n\t\tBroadcast(new PBEStatus1Packet(status1Receiver, pokemon2, status1, statusAction));\n\t}\n\tprivate void BroadcastStatus2(PBEBattlePokemon status2Receiver, PBEBattlePokemon pokemon2, PBEStatus2 status2, PBEStatusAction statusAction)\n\t{\n\t\tswitch (statusAction)\n\t\t{\n\t\t\tcase PBEStatusAction.Added:\n\t\t\tcase PBEStatusAction.Announced:\n\t\t\tcase PBEStatusAction.CausedImmobility:\n\t\t\tcase PBEStatusAction.Damage: status2Receiver.Status2 |= status2; status2Receiver.KnownStatus2 |= status2; break;\n\t\t\tcase PBEStatusAction.Cleared:\n\t\t\tcase PBEStatusAction.Ended: status2Receiver.Status2 &= ~status2; status2Receiver.KnownStatus2 &= ~status2; break;\n\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(statusAction));\n\t\t}\n\t\tBroadcast(new PBEStatus2Packet(status2Receiver, pokemon2, status2, statusAction));\n\t}\n\tprivate void BroadcastTeamStatus(PBETeam team, PBETeamStatus teamStatus, PBETeamStatusAction teamStatusAction)\n\t{\n\t\tswitch (teamStatusAction)\n\t\t{\n\t\t\tcase PBETeamStatusAction.Added: team.TeamStatus |= teamStatus; break;\n\t\t\tcase PBETeamStatusAction.Cleared:\n\t\t\tcase PBETeamStatusAction.Ended: team.TeamStatus &= ~teamStatus; break;\n\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(teamStatusAction));\n\t\t}\n\t\tBroadcast(new PBETeamStatusPacket(team, teamStatus, teamStatusAction));\n\t}\n\tprivate void BroadcastTeamStatusDamage(PBETeam team, PBETeamStatus teamStatus, PBEBattlePokemon damageVictim)\n\t{\n\t\tteam.TeamStatus |= teamStatus;\n\t\tBroadcast(new PBETeamStatusDamagePacket(team, teamStatus, damageVictim));\n\t}\n\tprivate void BroadcastTransform(PBEBattlePokemon user, PBEBattlePokemon target)\n\t{\n\t\tBroadcast(new PBETransformPacket(user, target));\n\t}\n\tprivate void BroadcastTypeChanged(PBEBattlePokemon pokemon, PBEType type1, PBEType type2)\n\t{\n\t\tpokemon.Type1 = type1;\n\t\tpokemon.KnownType1 = type1;\n\t\tpokemon.Type2 = type2;\n\t\tpokemon.KnownType2 = type2;\n\t\tBroadcast(new PBETypeChangedPacket(pokemon, type1, type2));\n\t}\n\tprivate void BroadcastWeather(PBEWeather weather, PBEWeatherAction weatherAction)\n\t{\n\t\tBroadcast(new PBEWeatherPacket(weather, weatherAction));\n\t}\n\tprivate void BroadcastWeatherDamage(PBEWeather weather, PBEBattlePokemon damageVictim)\n\t{\n\t\tBroadcast(new PBEWeatherDamagePacket(weather, damageVictim));\n\t}\n\tprivate void BroadcastWildPkmnAppeared(PBEPkmnAppearedInfo[] appearances)\n\t{\n\t\tBroadcast(new PBEWildPkmnAppearedPacket(appearances));\n\t}\n\tprivate void BroadcastActionsRequest(PBETrainer trainer)\n\t{\n\t\tBroadcast(new PBEActionsRequestPacket(trainer));\n\t}\n\tprivate void BroadcastAutoCenter(PBEBattlePokemon pokemon0, PBEFieldPosition pokemon0OldPosition, PBEBattlePokemon pokemon1, PBEFieldPosition pokemon1OldPosition)\n\t{\n\t\tBroadcast(new PBEAutoCenterPacket(pokemon0, pokemon0OldPosition, pokemon1, pokemon1OldPosition));\n\t}\n\tprivate void BroadcastBattle()\n\t{\n\t\tBroadcast(new PBEBattlePacket(this));\n\t}\n\tprivate void BroadcastBattleResult(PBEBattleResult r)\n\t{\n\t\tBroadcast(new PBEBattleResultPacket(r));\n\t}\n\tprivate void BroadcastSwitchInRequest(PBETrainer trainer)\n\t{\n\t\tBroadcast(new PBESwitchInRequestPacket(trainer));\n\t}\n\tprivate void BroadcastTurnBegan()\n\t{\n\t\tBroadcast(new PBETurnBeganPacket(TurnNumber));\n\t}\n\n\tpublic static string? GetDefaultMessage(PBEBattle battle, IPBEPacket packet, bool showRawHP = false, PBETrainer? userTrainer = null,\n\t\tFunc<PBEBattlePokemon, bool, string>? pkmnNameFunc = null, Func<PBETrainer, string>? trainerNameFunc = null, Func<PBETeam, bool, string>? teamNameFunc = null)\n\t{\n\t\t// This is not used by switching in or out or wild Pokémon appearing; those always use the known nickname\n\t\tstring GetPkmnName(PBEBattlePokemon pkmn, bool firstLetterCapitalized)\n\t\t{\n\t\t\tif (pkmnNameFunc is not null)\n\t\t\t{\n\t\t\t\treturn pkmnNameFunc(pkmn, firstLetterCapitalized);\n\t\t\t}\n\t\t\tif (pkmn.IsWild)\n\t\t\t{\n\t\t\t\tstring wildPrefix = firstLetterCapitalized ? \"The wild \" : \"the wild \";\n\t\t\t\treturn wildPrefix + pkmn.KnownNickname;\n\t\t\t}\n\t\t\t// Replay/spectator always see prefix, but if you're battling a multi-battle, your Pokémon should still have no prefix\n\t\t\tif (userTrainer is null || (pkmn.Trainer != userTrainer && pkmn.Team.Trainers.Count > 1))\n\t\t\t{\n\t\t\t\treturn $\"{GetTrainerName(pkmn.Trainer)}'s {pkmn.KnownNickname}\";\n\t\t\t}\n\t\t\tstring ownerPrefix = string.Empty;\n\t\t\tstring foePrefix = firstLetterCapitalized ? \"The foe's \" : \"the foe's \";\n\t\t\tstring prefix = pkmn.Trainer == userTrainer ? ownerPrefix : foePrefix;\n\t\t\treturn prefix + pkmn.KnownNickname;\n\t\t}\n\t\tstring GetTrainerName(PBETrainer trainer)\n\t\t{\n\t\t\tif (trainerNameFunc is not null)\n\t\t\t{\n\t\t\t\treturn trainerNameFunc(trainer);\n\t\t\t}\n\t\t\treturn trainer.Name;\n\t\t}\n\t\tstring GetRawCombinedName(PBETeam team, bool firstLetterCapitalized)\n\t\t{\n\t\t\tif (team.IsWild)\n\t\t\t{\n\t\t\t\tstring prefix = firstLetterCapitalized ? \"The\" : \"the\";\n\t\t\t\treturn prefix + \" wild Pokémon\";\n\t\t\t}\n\t\t\treturn team.CombinedName;\n\t\t}\n\t\t// This is not used by PBEBattleResultPacket; those use GetRawCombinedName()\n\t\tstring GetTeamName(PBETeam team, bool firstLetterCapitalized)\n\t\t{\n\t\t\tif (teamNameFunc is not null)\n\t\t\t{\n\t\t\t\treturn teamNameFunc(team, firstLetterCapitalized);\n\t\t\t}\n\t\t\tif (userTrainer is null)\n\t\t\t{\n\t\t\t\treturn $\"{GetRawCombinedName(team, firstLetterCapitalized)}'s\";\n\t\t\t}\n\t\t\tstring ownerPrefix = firstLetterCapitalized ? \"Your\" : \"your\";\n\t\t\tstring foePrefix = firstLetterCapitalized ? \"The opposing\" : \"the opposing\";\n\t\t\treturn team == userTrainer.Team ? ownerPrefix : foePrefix;\n\t\t}\n\t\tstring DoHiddenHP(PBEBattlePokemon pokemon, float percentageChange, float absPercentageChange)\n\t\t{\n\t\t\treturn string.Format(\"{0} {1} {2:P2} of its HP!\", GetPkmnName(pokemon, true), percentageChange <= 0 ? \"lost\" : \"restored\", absPercentageChange);\n\t\t}\n\n\t\tswitch (packet)\n\t\t{\n\t\t\tcase PBEAbilityPacket ap:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon abilityOwner = ap.AbilityOwnerTrainer.GetPokemon(ap.AbilityOwner);\n\t\t\t\tPBEBattlePokemon pokemon2 = ap.AbilityOwnerTrainer.GetPokemon(ap.Pokemon2);\n\t\t\t\tbool abilityOwnerCaps = true,\n\t\t\t\t\t\t\tpokemon2Caps = true;\n\t\t\t\tstring message;\n\t\t\t\tswitch (ap.Ability)\n\t\t\t\t{\n\t\t\t\t\tcase PBEAbility.AirLock:\n\t\t\t\t\tcase PBEAbility.CloudNine:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ap.AbilityAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEAbilityAction.Weather: message = \"{0}'s {2} causes the effects of weather to disappear!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ap.AbilityAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEAbility.Anticipation:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ap.AbilityAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEAbilityAction.Announced: message = \"{0}'s {2} made it shudder!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ap.AbilityAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEAbility.BadDreams:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ap.AbilityAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEAbilityAction.Damage: message = \"{1} is tormented by {0}'s {2}!\"; abilityOwnerCaps = false; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ap.AbilityAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEAbility.BigPecks:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ap.AbilityAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEAbilityAction.Stats: message = $\"{{0}}'s {PBEDataProvider.Instance.GetStatName(PBEStat.Defense).English} was not lowered!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ap.AbilityAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEAbility.ClearBody:\n\t\t\t\t\tcase PBEAbility.WhiteSmoke:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ap.AbilityAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEAbilityAction.Stats: message = \"{0}'s {2} prevents stat reduction!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ap.AbilityAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEAbility.ColorChange:\n\t\t\t\t\tcase PBEAbility.FlowerGift:\n\t\t\t\t\tcase PBEAbility.Forecast:\n\t\t\t\t\tcase PBEAbility.Imposter:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ap.AbilityAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEAbilityAction.ChangedAppearance: message = \"{0}'s {2} activated!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ap.AbilityAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEAbility.CuteCharm:\n\t\t\t\t\tcase PBEAbility.EffectSpore:\n\t\t\t\t\tcase PBEAbility.FlameBody:\n\t\t\t\t\tcase PBEAbility.Healer:\n\t\t\t\t\tcase PBEAbility.PoisonPoint:\n\t\t\t\t\tcase PBEAbility.ShedSkin:\n\t\t\t\t\tcase PBEAbility.Static:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ap.AbilityAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEAbilityAction.ChangedStatus: message = \"{0}'s {2} activated!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ap.AbilityAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEAbility.Download:\n\t\t\t\t\tcase PBEAbility.Intimidate:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ap.AbilityAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEAbilityAction.Stats: message = \"{0}'s {2} activated!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ap.AbilityAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEAbility.Drizzle:\n\t\t\t\t\tcase PBEAbility.Drought:\n\t\t\t\t\tcase PBEAbility.SandStream:\n\t\t\t\t\tcase PBEAbility.SnowWarning:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ap.AbilityAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEAbilityAction.Weather: message = \"{0}'s {2} activated!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ap.AbilityAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEAbility.HyperCutter:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ap.AbilityAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEAbilityAction.Stats: message = $\"{{0}}'s {PBEDataProvider.Instance.GetStatName(PBEStat.Attack).English} was not lowered!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ap.AbilityAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEAbility.IceBody:\n\t\t\t\t\tcase PBEAbility.PoisonHeal:\n\t\t\t\t\tcase PBEAbility.RainDish:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ap.AbilityAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEAbilityAction.RestoredHP: message = \"{0}'s {2} activated!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ap.AbilityAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEAbility.Illusion:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ap.AbilityAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEAbilityAction.ChangedAppearance: goto bottom;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ap.AbilityAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEAbility.Immunity:\n\t\t\t\t\tcase PBEAbility.Insomnia:\n\t\t\t\t\tcase PBEAbility.Limber:\n\t\t\t\t\tcase PBEAbility.MagmaArmor:\n\t\t\t\t\tcase PBEAbility.Oblivious:\n\t\t\t\t\tcase PBEAbility.OwnTempo:\n\t\t\t\t\tcase PBEAbility.VitalSpirit:\n\t\t\t\t\tcase PBEAbility.WaterVeil:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ap.AbilityAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEAbilityAction.ChangedStatus:\n\t\t\t\t\t\t\tcase PBEAbilityAction.PreventedStatus: message = \"{0}'s {2} activated!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ap.AbilityAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEAbility.IronBarbs:\n\t\t\t\t\tcase PBEAbility.Justified:\n\t\t\t\t\tcase PBEAbility.Levitate:\n\t\t\t\t\tcase PBEAbility.Mummy:\n\t\t\t\t\tcase PBEAbility.Rattled:\n\t\t\t\t\tcase PBEAbility.RoughSkin:\n\t\t\t\t\tcase PBEAbility.SolarPower:\n\t\t\t\t\tcase PBEAbility.Sturdy:\n\t\t\t\t\tcase PBEAbility.WeakArmor:\n\t\t\t\t\tcase PBEAbility.WonderGuard:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ap.AbilityAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEAbilityAction.Damage: message = \"{0}'s {2} activated!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ap.AbilityAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEAbility.KeenEye:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ap.AbilityAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEAbilityAction.Stats: message = $\"{{0}}'s {PBEDataProvider.Instance.GetStatName(PBEStat.Accuracy).English} was not lowered!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ap.AbilityAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEAbility.LeafGuard:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ap.AbilityAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEAbilityAction.PreventedStatus: message = \"{0}'s {2} activated!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ap.AbilityAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEAbility.LiquidOoze:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ap.AbilityAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEAbilityAction.Damage: message = \"{1} sucked up the liquid ooze!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ap.AbilityAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEAbility.MoldBreaker:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ap.AbilityAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEAbilityAction.Announced: message = \"{0} breaks the mold!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ap.AbilityAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEAbility.Moody:\n\t\t\t\t\tcase PBEAbility.SpeedBoost:\n\t\t\t\t\tcase PBEAbility.Steadfast:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ap.AbilityAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEAbilityAction.Stats: message = \"{0}'s {2} activated!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ap.AbilityAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEAbility.RunAway:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ap.AbilityAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEAbilityAction.Announced: message = \"{0}'s {2} activated!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ap.AbilityAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEAbility.SlowStart:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ap.AbilityAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEAbilityAction.Announced: message = \"{0} can't get it going!\"; break;\n\t\t\t\t\t\t\tcase PBEAbilityAction.SlowStart_Ended: message = \"{0} finally got its act together!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ap.AbilityAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEAbility.Teravolt:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ap.AbilityAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEAbilityAction.Announced: message = \"{0} is radiating a bursting aura!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ap.AbilityAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEAbility.Turboblaze:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ap.AbilityAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEAbilityAction.Announced: message = \"{0} is radiating a blazing aura!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ap.AbilityAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ap.Ability));\n\t\t\t\t}\n\t\t\t\treturn string.Format(message, GetPkmnName(abilityOwner, abilityOwnerCaps), GetPkmnName(pokemon2, pokemon2Caps), PBEDataProvider.Instance.GetAbilityName(ap.Ability).English);\n\t\t\t}\n\t\t\tcase PBEAbilityReplacedPacket arp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon abilityOwner = arp.AbilityOwnerTrainer.GetPokemon(arp.AbilityOwner);\n\t\t\t\tstring message;\n\t\t\t\tswitch (arp.NewAbility)\n\t\t\t\t{\n\t\t\t\t\tcase PBEAbility.None: message = \"{0}'s {1} was suppressed!\"; break;\n\t\t\t\t\tdefault: message = \"{0}'s {1} was changed to {2}!\"; break;\n\t\t\t\t}\n\t\t\t\treturn string.Format(message,\n\t\t\t\t\tGetPkmnName(abilityOwner, true),\n\t\t\t\t\tarp.OldAbility is null ? \"Ability\" : PBEDataProvider.Instance.GetAbilityName(arp.OldAbility.Value).English,\n\t\t\t\t\tPBEDataProvider.Instance.GetAbilityName(arp.NewAbility).English);\n\t\t\t}\n\t\t\tcase PBEBattleStatusPacket bsp:\n\t\t\t{\n\t\t\t\tstring message;\n\t\t\t\tswitch (bsp.BattleStatus)\n\t\t\t\t{\n\t\t\t\t\tcase PBEBattleStatus.TrickRoom:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (bsp.BattleStatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEBattleStatusAction.Added: message = \"The dimensions were twisted!\"; break;\n\t\t\t\t\t\t\tcase PBEBattleStatusAction.Cleared:\n\t\t\t\t\t\t\tcase PBEBattleStatusAction.Ended: message = \"The twisted dimensions returned to normal!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(bsp.BattleStatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tdefault: throw new InvalidDataException(nameof(bsp.BattleStatus));\n\t\t\t\t}\n\t\t\t\treturn message;\n\t\t\t}\n\t\t\tcase PBECapturePacket cp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon pokemon = cp.PokemonTrainer.GetPokemon(cp.Pokemon);\n\t\t\t\tstring ballEnglish = PBEDataProvider.Instance.GetItemName(cp.Ball).English;\n\t\t\t\tif (cp.Success)\n\t\t\t\t{\n\t\t\t\t\treturn string.Format(\"Gotcha! {0} was caught with the {1} after {2} shake{3}!\", pokemon.Nickname, ballEnglish, cp.NumShakes, cp.NumShakes == 1 ? string.Empty : \"s\");\n\t\t\t\t}\n\t\t\t\tif (cp.NumShakes == 0)\n\t\t\t\t{\n\t\t\t\t\treturn \"The Pokémon broke free without shaking!\";\n\t\t\t\t}\n\t\t\t\treturn string.Format(\"The Pokémon broke free after {0} shake{1}!\", cp.NumShakes, cp.NumShakes == 1 ? string.Empty : \"s\");\n\t\t\t}\n\t\t\tcase PBEFleeFailedPacket ffp:\n\t\t\t{\n\t\t\t\tstring name;\n\t\t\t\tif (ffp.Pokemon == PBEFieldPosition.None)\n\t\t\t\t{\n\t\t\t\t\tname = GetTrainerName(ffp.PokemonTrainer);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tPBEBattlePokemon pokemon = ffp.PokemonTrainer.GetPokemon(ffp.Pokemon);\n\t\t\t\t\tname = GetPkmnName(pokemon, true);\n\t\t\t\t}\n\t\t\t\treturn string.Format(\"{0} could not get away!\", name);\n\t\t\t}\n\t\t\tcase PBEHazePacket _:\n\t\t\t{\n\t\t\t\treturn \"All stat changes were eliminated!\";\n\t\t\t}\n\t\t\tcase PBEItemPacket ip:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon itemHolder = ip.ItemHolderTrainer.GetPokemon(ip.ItemHolder);\n\t\t\t\tPBEBattlePokemon pokemon2 = ip.Pokemon2Trainer.GetPokemon(ip.Pokemon2);\n\t\t\t\tbool itemHolderCaps = true,\n\t\t\t\t\t\t\tpokemon2Caps = false;\n\t\t\t\tstring message;\n\t\t\t\tswitch (ip.Item)\n\t\t\t\t{\n\t\t\t\t\tcase PBEItem.AguavBerry:\n\t\t\t\t\tcase PBEItem.BerryJuice:\n\t\t\t\t\tcase PBEItem.FigyBerry:\n\t\t\t\t\tcase PBEItem.IapapaBerry:\n\t\t\t\t\tcase PBEItem.MagoBerry:\n\t\t\t\t\tcase PBEItem.OranBerry:\n\t\t\t\t\tcase PBEItem.SitrusBerry:\n\t\t\t\t\tcase PBEItem.WikiBerry:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ip.ItemAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEItemAction.Consumed: message = \"{0} restored its health using its {2}!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ip.ItemAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.ApicotBerry:\n\t\t\t\t\tcase PBEItem.GanlonBerry:\n\t\t\t\t\tcase PBEItem.LiechiBerry:\n\t\t\t\t\tcase PBEItem.PetayaBerry:\n\t\t\t\t\tcase PBEItem.SalacBerry:\n\t\t\t\t\tcase PBEItem.StarfBerry:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ip.ItemAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEItemAction.Consumed: message = \"{0} used its {2}!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ip.ItemAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.BugGem:\n\t\t\t\t\tcase PBEItem.DarkGem:\n\t\t\t\t\tcase PBEItem.DragonGem:\n\t\t\t\t\tcase PBEItem.ElectricGem:\n\t\t\t\t\tcase PBEItem.FightingGem:\n\t\t\t\t\tcase PBEItem.FireGem:\n\t\t\t\t\tcase PBEItem.FlyingGem:\n\t\t\t\t\tcase PBEItem.GhostGem:\n\t\t\t\t\tcase PBEItem.GrassGem:\n\t\t\t\t\tcase PBEItem.GroundGem:\n\t\t\t\t\tcase PBEItem.IceGem:\n\t\t\t\t\tcase PBEItem.NormalGem:\n\t\t\t\t\tcase PBEItem.PoisonGem:\n\t\t\t\t\tcase PBEItem.PsychicGem:\n\t\t\t\t\tcase PBEItem.RockGem:\n\t\t\t\t\tcase PBEItem.SteelGem:\n\t\t\t\t\tcase PBEItem.WaterGem:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ip.ItemAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEItemAction.Consumed: message = \"The {2} strengthened {0}'s power!\"; itemHolderCaps = false; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ip.ItemAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.BlackSludge:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ip.ItemAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEItemAction.Damage: message = \"{0} is hurt by its {2}!\"; break;\n\t\t\t\t\t\t\tcase PBEItemAction.RestoredHP: message = \"{0} restored a little HP using its {2}!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ip.ItemAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.DestinyKnot:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ip.ItemAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEItemAction.Announced: message = \"{0}'s {2} activated!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ip.ItemAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.FlameOrb:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ip.ItemAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEItemAction.Announced: message = \"{0} was burned by its {2}!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ip.ItemAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.FocusBand:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ip.ItemAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEItemAction.Damage: message = \"{0} hung on using its {2}!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ip.ItemAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.FocusSash:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ip.ItemAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEItemAction.Consumed: message = \"{0} hung on using its {2}!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ip.ItemAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.Leftovers:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ip.ItemAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEItemAction.RestoredHP: message = \"{0} restored a little HP using its {2}!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ip.ItemAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.LifeOrb:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ip.ItemAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEItemAction.Damage: message = \"{0} is hurt by its {2}!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ip.ItemAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.PowerHerb:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ip.ItemAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEItemAction.Consumed: message = \"{0} became fully charged due to its {2}!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ip.ItemAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.RockyHelmet:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ip.ItemAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEItemAction.Damage: message = \"{1} was hurt by the {2}!\"; pokemon2Caps = true; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ip.ItemAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.SmokeBall:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ip.ItemAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEItemAction.Announced: message = \"{0} used its {2}!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ip.ItemAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItem.ToxicOrb:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (ip.ItemAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEItemAction.Announced: message = \"{0} was badly poisoned by its {2}!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ip.ItemAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ip.Item));\n\t\t\t\t}\n\t\t\t\treturn string.Format(message, GetPkmnName(itemHolder, itemHolderCaps), GetPkmnName(pokemon2, pokemon2Caps), PBEDataProvider.Instance.GetItemName(ip.Item).English);\n\t\t\t}\n\t\t\tcase PBEItemTurnPacket itp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon itemUser = itp.ItemUserTrainer.GetPokemon(itp.ItemUser);\n\t\t\t\tstring itemEnglish = PBEDataProvider.Instance.GetItemName(itp.Item).English;\n\t\t\t\tswitch (itp.ItemAction)\n\t\t\t\t{\n\t\t\t\t\tcase PBEItemTurnAction.Attempt:\n\t\t\t\t\t{\n\t\t\t\t\t\tstring word;\n\t\t\t\t\t\tif (PBEDataUtils.AllBalls.Contains(itp.Item))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tword = \"threw\";\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tword = \"used\";\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn string.Format(\"{0} {1} the {2}.\", GetTrainerName(itemUser.Trainer), word, itemEnglish);\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItemTurnAction.NoEffect:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (PBEDataUtils.AllBalls.Contains(itp.Item))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn \"The trainer blocked the ball! Don't be a thief!\";\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn string.Format(\"The {0} had no effect.\", itemEnglish);\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEItemTurnAction.Success:\n\t\t\t\t\t{\n\t\t\t\t\t\t//string message;\n\t\t\t\t\t\tswitch (itp.Item)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// No \"success\" items yet\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(itp.Item));\n\t\t\t\t\t\t}\n\t\t\t\t\t\t//return string.Format(message, GetPkmnName(itemUser, true), itemEnglish);\n\t\t\t\t\t}\n\t\t\t\t\tdefault: throw new InvalidDataException(nameof(itp.ItemAction));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcase PBEMoveCritPacket mcp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon victim = mcp.VictimTrainer.GetPokemon(mcp.Victim);\n\t\t\t\treturn string.Format(\"A critical hit on {0}!\", GetPkmnName(victim, false));\n\t\t\t}\n\t\t\tcase PBEMovePPChangedPacket mpcp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon moveUser = mpcp.MoveUserTrainer.GetPokemon(mpcp.MoveUser);\n\t\t\t\treturn string.Format(\"{0}'s {1} {3} {2} PP!\",\n\t\t\t\t\tGetPkmnName(moveUser, true),\n\t\t\t\t\tPBEDataProvider.Instance.GetMoveName(mpcp.Move).English,\n\t\t\t\t\tMath.Abs(mpcp.AmountReduced),\n\t\t\t\t\tmpcp.AmountReduced >= 0 ? \"lost\" : \"gained\");\n\t\t\t}\n\t\t\tcase PBEMoveResultPacket mrp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon moveUser = mrp.MoveUserTrainer.GetPokemon(mrp.MoveUser);\n\t\t\t\tPBEBattlePokemon pokemon2 = mrp.Pokemon2Trainer.GetPokemon(mrp.Pokemon2);\n\t\t\t\tbool pokemon2Caps = true;\n\t\t\t\tstring message;\n\t\t\t\tswitch (mrp.Result)\n\t\t\t\t{\n\t\t\t\t\tcase PBEResult.Ineffective_Ability: message = \"{1} is protected by its Ability!\"; break;\n\t\t\t\t\tcase PBEResult.Ineffective_Gender: message = \"It doesn't affect {1}...\"; pokemon2Caps = false; break;\n\t\t\t\t\tcase PBEResult.Ineffective_Level: message = \"{1} is protected by its level!\"; break;\n\t\t\t\t\tcase PBEResult.Ineffective_MagnetRise: message = $\"{{1}} is protected by {PBEDataProvider.Instance.GetMoveName(PBEMove.MagnetRise).English}!\"; break;\n\t\t\t\t\tcase PBEResult.Ineffective_Safeguard: message = $\"{{1}} is protected by {PBEDataProvider.Instance.GetMoveName(PBEMove.Safeguard).English}!\"; break;\n\t\t\t\t\tcase PBEResult.Ineffective_Stat:\n\t\t\t\t\tcase PBEResult.Ineffective_Status:\n\t\t\t\t\tcase PBEResult.InvalidConditions: message = \"But it failed!\"; break;\n\t\t\t\t\tcase PBEResult.Ineffective_Substitute: message = $\"{{1}} is protected by {PBEDataProvider.Instance.GetMoveName(PBEMove.Substitute).English}!\"; break;\n\t\t\t\t\tcase PBEResult.Ineffective_Type: message = \"{1} is protected by its Type!\"; break;\n\t\t\t\t\tcase PBEResult.Missed: message = \"{0}'s attack missed {1}!\"; pokemon2Caps = false; break;\n\t\t\t\t\tcase PBEResult.NoTarget: message = \"But there was no target...\"; break;\n\t\t\t\t\tcase PBEResult.NotVeryEffective_Type: message = \"It's not very effective on {1}...\"; pokemon2Caps = false; break;\n\t\t\t\t\tcase PBEResult.SuperEffective_Type: message = \"It's super effective on {1}!\"; pokemon2Caps = false; break;\n\t\t\t\t\tdefault: throw new InvalidDataException(nameof(mrp.Result));\n\t\t\t\t}\n\t\t\t\treturn string.Format(message, GetPkmnName(moveUser, true), GetPkmnName(pokemon2, pokemon2Caps));\n\t\t\t}\n\t\t\tcase PBEMoveUsedPacket mup:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon moveUser = mup.MoveUserTrainer.GetPokemon(mup.MoveUser);\n\t\t\t\treturn string.Format(\"{0} used {1}!\", GetPkmnName(moveUser, true), PBEDataProvider.Instance.GetMoveName(mup.Move).English);\n\t\t\t}\n\t\t\tcase PBEPkmnFaintedPacket pfp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon pokemon = pfp.PokemonTrainer.GetPokemon(pfp.Pokemon);\n\t\t\t\treturn string.Format(\"{0} fainted!\", GetPkmnName(pokemon, true));\n\t\t\t}\n\t\t\tcase PBEPkmnEXPEarnedPacket peep:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon pokemon = peep.PokemonTrainer.GetPokemon(peep.Pokemon);\n\t\t\t\treturn string.Format(\"{0} earned {1} EXP point(s)!\", GetPkmnName(pokemon, true), peep.Earned);\n\t\t\t}\n\t\t\tcase PBEPkmnFaintedPacket_Hidden pfph:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon pokemon = pfph.PokemonTrainer.GetPokemon(pfph.OldPosition);\n\t\t\t\treturn string.Format(\"{0} fainted!\", GetPkmnName(pokemon, true));\n\t\t\t}\n\t\t\tcase IPBEPkmnFormChangedPacket pfcp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon pokemon = pfcp.PokemonTrainer.GetPokemon(pfcp.Pokemon);\n\t\t\t\treturn string.Format(\"{0}'s new form is {1}!\", GetPkmnName(pokemon, true), PBEDataProvider.Instance.GetFormName(pokemon.Species, pfcp.NewForm).English);\n\t\t\t}\n\t\t\tcase PBEPkmnHPChangedPacket phcp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon pokemon = phcp.PokemonTrainer.GetPokemon(phcp.Pokemon);\n\t\t\t\tfloat percentageChange = phcp.NewHPPercentage - phcp.OldHPPercentage;\n\t\t\t\tfloat absPercentageChange = Math.Abs(percentageChange);\n\t\t\t\tif (showRawHP || userTrainer == pokemon.Trainer) // Owner should see raw values\n\t\t\t\t{\n\t\t\t\t\tint change = phcp.NewHP - phcp.OldHP;\n\t\t\t\t\tint absChange = Math.Abs(change);\n\t\t\t\t\treturn string.Format(\"{0} {1} {2} ({3:P2}) HP!\", GetPkmnName(pokemon, true), change <= 0 ? \"lost\" : \"restored\", absChange, absPercentageChange);\n\t\t\t\t}\n\t\t\t\treturn DoHiddenHP(pokemon, percentageChange, absPercentageChange);\n\t\t\t}\n\t\t\tcase PBEPkmnHPChangedPacket_Hidden phcph:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon pokemon = phcph.PokemonTrainer.GetPokemon(phcph.Pokemon);\n\t\t\t\tfloat percentageChange = phcph.NewHPPercentage - phcph.OldHPPercentage;\n\t\t\t\tfloat absPercentageChange = Math.Abs(percentageChange);\n\t\t\t\treturn DoHiddenHP(pokemon, percentageChange, absPercentageChange);\n\t\t\t}\n\t\t\tcase PBEPkmnLevelChangedPacket plcp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon pokemon = plcp.PokemonTrainer.GetPokemon(plcp.Pokemon);\n\t\t\t\treturn string.Format(\"{0} grew to level {1}!\", GetPkmnName(pokemon, true), plcp.NewLevel);\n\t\t\t}\n\t\t\tcase PBEPkmnStatChangedPacket pscp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon pokemon = pscp.PokemonTrainer.GetPokemon(pscp.Pokemon);\n\t\t\t\tstring statName, message;\n\t\t\t\tswitch (pscp.Stat)\n\t\t\t\t{\n\t\t\t\t\tcase PBEStat.Accuracy: statName = \"Accuracy\"; break;\n\t\t\t\t\tcase PBEStat.Attack: statName = \"Attack\"; break;\n\t\t\t\t\tcase PBEStat.Defense: statName = \"Defense\"; break;\n\t\t\t\t\tcase PBEStat.Evasion: statName = \"Evasion\"; break;\n\t\t\t\t\tcase PBEStat.SpAttack: statName = \"Special Attack\"; break;\n\t\t\t\t\tcase PBEStat.SpDefense: statName = \"Special Defense\"; break;\n\t\t\t\t\tcase PBEStat.Speed: statName = \"Speed\"; break;\n\t\t\t\t\tdefault: throw new InvalidDataException(nameof(pscp.Stat));\n\t\t\t\t}\n\t\t\t\tint change = pscp.NewValue - pscp.OldValue;\n\t\t\t\tswitch (change)\n\t\t\t\t{\n\t\t\t\t\tcase -2: message = \"harshly fell\"; break;\n\t\t\t\t\tcase -1: message = \"fell\"; break;\n\t\t\t\t\tcase +1: message = \"rose\"; break;\n\t\t\t\t\tcase +2: message = \"rose sharply\"; break;\n\t\t\t\t\tdefault:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (change == 0 && pscp.NewValue == -battle.Settings.MaxStatChange)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmessage = \"won't go lower\";\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (change == 0 && pscp.NewValue == battle.Settings.MaxStatChange)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmessage = \"won't go higher\";\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (change <= -3)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmessage = \"severely fell\";\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (change >= +3)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmessage = \"rose drastically\";\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new Exception();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn string.Format(\"{0}'s {1} {2}!\", GetPkmnName(pokemon, true), statName, message);\n\t\t\t}\n\t\t\tcase IPBEPkmnSwitchInPacket psip:\n\t\t\t{\n\t\t\t\tif (!psip.Forced)\n\t\t\t\t{\n\t\t\t\t\treturn string.Format(\"{1} sent out {0}!\", psip.SwitchIns.Select(s => s.Nickname).ToArray().Andify(), GetTrainerName(psip.Trainer));\n\t\t\t\t}\n\t\t\t\tgoto bottom;\n\t\t\t}\n\t\t\tcase PBEPkmnSwitchOutPacket psop:\n\t\t\t{\n\t\t\t\tif (!psop.Forced)\n\t\t\t\t{\n\t\t\t\t\tPBEBattlePokemon pokemon = psop.PokemonTrainer.GetPokemon(psop.Pokemon);\n\t\t\t\t\treturn string.Format(\"{1} withdrew {0}!\", pokemon.KnownNickname, GetTrainerName(psop.PokemonTrainer));\n\t\t\t\t}\n\t\t\t\tgoto bottom;\n\t\t\t}\n\t\t\tcase PBEPkmnSwitchOutPacket_Hidden psoph:\n\t\t\t{\n\t\t\t\tif (!psoph.Forced)\n\t\t\t\t{\n\t\t\t\t\tPBEBattlePokemon pokemon = psoph.PokemonTrainer.GetPokemon(psoph.OldPosition);\n\t\t\t\t\treturn string.Format(\"{1} withdrew {0}!\", pokemon.KnownNickname, GetTrainerName(psoph.PokemonTrainer));\n\t\t\t\t}\n\t\t\t\tgoto bottom;\n\t\t\t}\n\t\t\tcase PBEPsychUpPacket pup:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon user = pup.UserTrainer.GetPokemon(pup.User);\n\t\t\t\tPBEBattlePokemon target = pup.TargetTrainer.GetPokemon(pup.Target);\n\t\t\t\treturn string.Format(\"{0} copied {1}'s stat changes!\", GetPkmnName(user, true), GetPkmnName(target, false));\n\t\t\t}\n\t\t\tcase PBEReflectTypePacket rtp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon user = rtp.UserTrainer.GetPokemon(rtp.User);\n\t\t\t\tPBEBattlePokemon target = rtp.TargetTrainer.GetPokemon(rtp.Target);\n\t\t\t\tstring type1Str = PBEDataProvider.Instance.GetTypeName(rtp.Type1).English;\n\t\t\t\treturn string.Format(\"{0} copied {1}'s {2}\",\n\t\t\t\t\tGetPkmnName(user, true),\n\t\t\t\t\tGetPkmnName(target, false),\n\t\t\t\t\trtp.Type2 == PBEType.None ? $\"{type1Str} type!\" : $\"{type1Str} and {PBEDataProvider.Instance.GetTypeName(rtp.Type2).English} types!\");\n\t\t\t}\n\t\t\tcase PBEReflectTypePacket_Hidden rtph:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon user = rtph.UserTrainer.GetPokemon(rtph.User);\n\t\t\t\tPBEBattlePokemon target = rtph.TargetTrainer.GetPokemon(rtph.Target);\n\t\t\t\treturn string.Format(\"{0} copied {1}'s types!\", GetPkmnName(user, true), GetPkmnName(target, false));\n\t\t\t}\n\t\t\tcase PBESpecialMessagePacket smp: // TODO: Clean\n\t\t\t{\n\t\t\t\tstring message;\n\t\t\t\tswitch (smp.Message)\n\t\t\t\t{\n\t\t\t\t\tcase PBESpecialMessage.DraggedOut: message = string.Format(\"{0} was dragged out!\", GetPkmnName(((PBETrainer)smp.Params[0]).GetPokemon((PBEFieldPosition)smp.Params[1]), true)); break;\n\t\t\t\t\tcase PBESpecialMessage.Endure: message = string.Format(\"{0} endured the hit!\", GetPkmnName(((PBETrainer)smp.Params[0]).GetPokemon((PBEFieldPosition)smp.Params[1]), true)); break;\n\t\t\t\t\tcase PBESpecialMessage.HPDrained: message = string.Format(\"{0} had its energy drained!\", GetPkmnName(((PBETrainer)smp.Params[0]).GetPokemon((PBEFieldPosition)smp.Params[1]), true)); break;\n\t\t\t\t\tcase PBESpecialMessage.Magnitude: message = string.Format(\"Magnitude {0}!\", (byte)smp.Params[0]); break;\n\t\t\t\t\tcase PBESpecialMessage.MultiHit: message = string.Format(\"Hit {0} time(s)!\", (byte)smp.Params[0]); break;\n\t\t\t\t\tcase PBESpecialMessage.NothingHappened: message = \"But nothing happened!\"; break;\n\t\t\t\t\tcase PBESpecialMessage.OneHitKnockout: message = \"It's a one-hit KO!\"; break;\n\t\t\t\t\tcase PBESpecialMessage.PainSplit: message = \"The battlers shared their pain!\"; break;\n\t\t\t\t\tcase PBESpecialMessage.PayDay: message = \"Coins were scattered everywhere!\"; break;\n\t\t\t\t\tcase PBESpecialMessage.Recoil: message = string.Format(\"{0} is damaged by recoil!\", GetPkmnName(((PBETrainer)smp.Params[0]).GetPokemon((PBEFieldPosition)smp.Params[1]), true)); break;\n\t\t\t\t\tcase PBESpecialMessage.Struggle: message = string.Format(\"{0} has no moves left!\", GetPkmnName(((PBETrainer)smp.Params[0]).GetPokemon((PBEFieldPosition)smp.Params[1]), true)); break;\n\t\t\t\t\tdefault: throw new InvalidDataException(nameof(smp.Message));\n\t\t\t\t}\n\t\t\t\treturn message;\n\t\t\t}\n\t\t\tcase PBEStatus1Packet s1p:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon status1Receiver = s1p.Status1ReceiverTrainer.GetPokemon(s1p.Status1Receiver);\n\t\t\t\tstring message;\n\t\t\t\tswitch (s1p.Status1)\n\t\t\t\t{\n\t\t\t\t\tcase PBEStatus1.Asleep:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s1p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added: message = \"{0} fell asleep!\"; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.CausedImmobility: message = \"{0} is fast asleep.\"; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Cleared:\n\t\t\t\t\t\t\tcase PBEStatusAction.Ended: message = \"{0} woke up!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(s1p.StatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus1.BadlyPoisoned:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s1p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added: message = \"{0} was badly poisoned!\"; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Cleared: message = \"{0} was cured of its poisoning.\"; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Damage: message = \"{0} was hurt by poison!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(s1p.StatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus1.Burned:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s1p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added: message = \"{0} was burned!\"; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Cleared: message = \"{0}'s burn was healed.\"; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Damage: message = \"{0} was hurt by its burn!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(s1p.StatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus1.Frozen:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s1p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added: message = \"{0} was frozen solid!\"; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.CausedImmobility: message = \"{0} is frozen solid!\"; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Cleared:\n\t\t\t\t\t\t\tcase PBEStatusAction.Ended: message = \"{0} thawed out!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(s1p.StatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus1.Paralyzed:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s1p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added: message = \"{0} is paralyzed! It may be unable to move!\"; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.CausedImmobility: message = \"{0} is paralyzed! It can't move!\"; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Cleared: message = \"{0} was cured of paralysis.\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(s1p.StatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus1.Poisoned:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s1p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added: message = \"{0} was poisoned!\"; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Cleared: message = \"{0} was cured of its poisoning.\"; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Damage: message = \"{0} was hurt by poison!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(s1p.StatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tdefault: throw new InvalidDataException(nameof(s1p.Status1));\n\t\t\t\t}\n\t\t\t\treturn string.Format(message, GetPkmnName(status1Receiver, true));\n\t\t\t}\n\t\t\tcase PBEStatus2Packet s2p:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon status2Receiver = s2p.Status2ReceiverTrainer.GetPokemon(s2p.Status2Receiver);\n\t\t\t\tPBEBattlePokemon pokemon2 = s2p.Pokemon2Trainer.GetPokemon(s2p.Pokemon2);\n\t\t\t\tstring message;\n\t\t\t\tbool status2ReceiverCaps = true,\n\t\t\t\t\t\t\tpokemon2Caps = false;\n\t\t\t\tswitch (s2p.Status2)\n\t\t\t\t{\n\t\t\t\t\tcase PBEStatus2.Airborne:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s2p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added: message = \"{0} flew up high!\"; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Ended: goto bottom;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(s2p.StatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus2.Confused:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s2p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added: message = \"{0} became confused!\"; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Announced: message = \"{0} is confused!\"; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Cleared:\n\t\t\t\t\t\t\tcase PBEStatusAction.Ended: message = \"{0} snapped out of its confusion.\"; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Damage: message = \"It hurt itself in its confusion!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(s2p.StatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus2.Cursed:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s2p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added: message = \"{1} cut its own HP and laid a curse on {0}!\"; status2ReceiverCaps = false; pokemon2Caps = true; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Damage: message = \"{0} is afflicted by the curse!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(s2p.StatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus2.Disguised:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s2p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Ended: message = \"{0}'s illusion wore off!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(s2p.StatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus2.Flinching:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s2p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.CausedImmobility: message = \"{0} flinched and couldn't move!\"; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Ended: goto bottom;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(s2p.StatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus2.Identified:\n\t\t\t\t\tcase PBEStatus2.MiracleEye:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s2p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added: message = \"{0} was identified!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(s2p.StatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus2.HelpingHand:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s2p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added: message = \"{1} is ready to help {0}!\"; status2ReceiverCaps = false; pokemon2Caps = true; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Ended: goto bottom;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(s2p.StatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus2.Infatuated:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s2p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added: message = \"{0} fell in love with {1}!\"; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Announced: message = \"{0} is in love with {1}!\"; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.CausedImmobility: message = \"{0} is immobilized by love!\"; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Cleared:\n\t\t\t\t\t\t\tcase PBEStatusAction.Ended: message = \"{0} got over its infatuation.\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(s2p.StatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus2.LeechSeed:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s2p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added: message = \"{0} was seeded!\"; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Damage: message = \"{0}'s health is sapped by Leech Seed!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(s2p.StatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus2.LockOn:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s2p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added: message = \"{0} took aim at {1}!\"; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Ended: goto bottom;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(s2p.StatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus2.MagnetRise:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s2p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added: message = \"{0} levitated with electromagnetism!\"; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Ended: message = \"{0}'s electromagnetism wore off!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(s2p.StatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus2.Nightmare:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s2p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added: message = \"{0} began having a nightmare!\"; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Damage: message = \"{0} is locked in a nightmare!\"; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Ended: goto bottom;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(s2p.StatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus2.PowerTrick:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s2p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added: message = \"{0} switched its Attack and Defense!\"; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Ended: goto bottom;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(s2p.StatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus2.Protected:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s2p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added:\n\t\t\t\t\t\t\tcase PBEStatusAction.Damage: message = \"{0} protected itself!\"; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Cleared: message = \"{1} broke through {0}'s protection!\"; status2ReceiverCaps = false; pokemon2Caps = true; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Ended: goto bottom;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(s2p.StatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus2.Pumped:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s2p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added: message = \"{0} is getting pumped!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(s2p.StatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus2.Roost:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s2p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added:\n\t\t\t\t\t\t\tcase PBEStatusAction.Ended: goto bottom;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(s2p.StatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus2.ShadowForce:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s2p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added: message = \"{0} vanished instantly!\"; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Ended: goto bottom;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(s2p.StatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus2.Substitute:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s2p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added: message = \"{0} put in a substitute!\"; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Damage: message = \"The substitute took damage for {0}!\"; status2ReceiverCaps = false; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Ended: message = \"{0}'s substitute faded!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(s2p.StatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus2.Transformed:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s2p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added: message = \"{0} transformed into {1}!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(s2p.StatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus2.Underground:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s2p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added: message = \"{0} burrowed its way under the ground!\"; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Ended: goto bottom;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(s2p.StatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus2.Underwater:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s2p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added: message = \"{0} hid underwater!\"; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Ended: goto bottom;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(s2p.StatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tdefault: throw new InvalidDataException(nameof(s2p.Status2));\n\t\t\t\t}\n\t\t\t\treturn string.Format(message, GetPkmnName(status2Receiver, status2ReceiverCaps), GetPkmnName(pokemon2, pokemon2Caps));\n\t\t\t}\n\t\t\tcase PBETeamStatusPacket tsp:\n\t\t\t{\n\t\t\t\tstring message;\n\t\t\t\tbool teamCaps = true;\n\t\t\t\tswitch (tsp.TeamStatus)\n\t\t\t\t{\n\t\t\t\t\tcase PBETeamStatus.LightScreen:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (tsp.TeamStatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBETeamStatusAction.Added: message = \"Light Screen raised {0} team's Special Defense!\"; teamCaps = false; break;\n\t\t\t\t\t\t\tcase PBETeamStatusAction.Cleared:\n\t\t\t\t\t\t\tcase PBETeamStatusAction.Ended: message = \"{0} team's Light Screen wore off!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(tsp.TeamStatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBETeamStatus.LuckyChant:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (tsp.TeamStatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBETeamStatusAction.Added: message = \"The Lucky Chant shielded {0} team from critical hits!\"; teamCaps = false; break;\n\t\t\t\t\t\t\tcase PBETeamStatusAction.Ended: message = \"{0} team's Lucky Chant wore off!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(tsp.TeamStatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBETeamStatus.QuickGuard:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (tsp.TeamStatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBETeamStatusAction.Added: message = \"Quick Guard protected {0} team!\"; teamCaps = false; break;\n\t\t\t\t\t\t\tcase PBETeamStatusAction.Cleared: message = \"{0} team's Quick Guard was destroyed!\"; break;\n\t\t\t\t\t\t\tcase PBETeamStatusAction.Ended: goto bottom;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(tsp.TeamStatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBETeamStatus.Reflect:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (tsp.TeamStatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBETeamStatusAction.Added: message = \"Reflect raised {0} team's Defense!\"; teamCaps = false; break;\n\t\t\t\t\t\t\tcase PBETeamStatusAction.Cleared:\n\t\t\t\t\t\t\tcase PBETeamStatusAction.Ended: message = \"{0} team's Reflect wore off!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(tsp.TeamStatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBETeamStatus.Safeguard:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (tsp.TeamStatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBETeamStatusAction.Added: message = \"{0} team became cloaked in a mystical veil!\"; break;\n\t\t\t\t\t\t\tcase PBETeamStatusAction.Ended: message = \"{0} team is no longer protected by Safeguard!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(tsp.TeamStatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBETeamStatus.Spikes:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (tsp.TeamStatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBETeamStatusAction.Added: message = \"Spikes were scattered all around the feet of {0} team!\"; teamCaps = false; break;\n\t\t\t\t\t\t\t//case PBETeamStatusAction.Cleared: message = \"The spikes disappeared from around {0} team's feet!\"; teamCaps = false; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(tsp.TeamStatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBETeamStatus.StealthRock:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (tsp.TeamStatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBETeamStatusAction.Added: message = \"Pointed stones float in the air around {0} team!\"; teamCaps = false; break;\n\t\t\t\t\t\t\t//case PBETeamStatusAction.Cleared: message = \"The pointed stones disappeared from around {0} team!\"; teamCaps = false; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(tsp.TeamStatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBETeamStatus.Tailwind:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (tsp.TeamStatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBETeamStatusAction.Added: message = \"The tailwind blew from behind {0} team!\"; teamCaps = false; break;\n\t\t\t\t\t\t\tcase PBETeamStatusAction.Ended: message = \"{0} team's tailwind petered out!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(tsp.TeamStatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBETeamStatus.ToxicSpikes:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (tsp.TeamStatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBETeamStatusAction.Added: message = \"Poison spikes were scattered all around {0} team's feet!\"; break;\n\t\t\t\t\t\t\tcase PBETeamStatusAction.Cleared: message = \"The poison spikes disappeared from around {0} team's feet!\"; break;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(tsp.TeamStatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBETeamStatus.WideGuard:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (tsp.TeamStatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBETeamStatusAction.Added: message = \"Wide Guard protected {0} team!\"; break;\n\t\t\t\t\t\t\tcase PBETeamStatusAction.Cleared: message = \"{0} team's Wide Guard was destroyed!\"; break;\n\t\t\t\t\t\t\tcase PBETeamStatusAction.Ended: goto bottom;\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(tsp.TeamStatusAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tdefault: throw new InvalidDataException(nameof(tsp.TeamStatus));\n\t\t\t\t}\n\t\t\t\treturn string.Format(message, GetTeamName(tsp.Team, teamCaps));\n\t\t\t}\n\t\t\tcase PBETeamStatusDamagePacket tsdp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon damageVictim = tsdp.DamageVictimTrainer.GetPokemon(tsdp.DamageVictim);\n\t\t\t\tstring message;\n\t\t\t\tbool damageVictimCaps = false;\n\t\t\t\tswitch (tsdp.TeamStatus)\n\t\t\t\t{\n\t\t\t\t\tcase PBETeamStatus.QuickGuard: message = \"Quick Guard protected {0}!\"; break;\n\t\t\t\t\tcase PBETeamStatus.Spikes: message = \"{0} is hurt by the spikes!\"; damageVictimCaps = true; break;\n\t\t\t\t\tcase PBETeamStatus.StealthRock: message = \"Pointed stones dug into {0}!\"; break;\n\t\t\t\t\tcase PBETeamStatus.WideGuard: message = \"Wide Guard protected {0}!\"; break;\n\t\t\t\t\tdefault: throw new InvalidDataException(nameof(tsdp.TeamStatus));\n\t\t\t\t}\n\t\t\t\treturn string.Format(message, GetPkmnName(damageVictim, damageVictimCaps));\n\t\t\t}\n\t\t\tcase PBETypeChangedPacket tcp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon pokemon = tcp.PokemonTrainer.GetPokemon(tcp.Pokemon);\n\t\t\t\tstring type1Str = PBEDataProvider.Instance.GetTypeName(tcp.Type1).English;\n\t\t\t\treturn string.Format(\"{0} transformed into the {1}\",\n\t\t\t\t\tGetPkmnName(pokemon, true),\n\t\t\t\t\ttcp.Type2 == PBEType.None ? $\"{type1Str} type!\" : $\"{type1Str} and {PBEDataProvider.Instance.GetTypeName(tcp.Type2).English} types!\");\n\t\t\t}\n\t\t\tcase PBEWeatherPacket wp:\n\t\t\t{\n\t\t\t\tswitch (wp.Weather)\n\t\t\t\t{\n\t\t\t\t\tcase PBEWeather.Hailstorm:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (wp.WeatherAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEWeatherAction.Added: return \"It started to hail!\";\n\t\t\t\t\t\t\tcase PBEWeatherAction.Ended: return \"The hail stopped.\";\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(wp.WeatherAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEWeather.HarshSunlight:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (wp.WeatherAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEWeatherAction.Added: return \"The sunlight turned harsh!\";\n\t\t\t\t\t\t\tcase PBEWeatherAction.Ended: return \"The sunlight faded.\";\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(wp.WeatherAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEWeather.Rain:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (wp.WeatherAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEWeatherAction.Added: return \"It started to rain!\";\n\t\t\t\t\t\t\tcase PBEWeatherAction.Ended: return \"The rain stopped.\";\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(wp.WeatherAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEWeather.Sandstorm:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (wp.WeatherAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEWeatherAction.Added: return \"A sandstorm kicked up!\";\n\t\t\t\t\t\t\tcase PBEWeatherAction.Ended: return \"The sandstorm subsided.\";\n\t\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(wp.WeatherAction));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdefault: throw new InvalidDataException(nameof(wp.Weather));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcase PBEWeatherDamagePacket wdp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon damageVictim = wdp.DamageVictimTrainer.GetPokemon(wdp.DamageVictim);\n\t\t\t\tstring message;\n\t\t\t\tswitch (wdp.Weather)\n\t\t\t\t{\n\t\t\t\t\tcase PBEWeather.Hailstorm: message = \"{0} is buffeted by the hail!\"; break;\n\t\t\t\t\tcase PBEWeather.Sandstorm: message = \"{0} is buffeted by the sandstorm!\"; break;\n\t\t\t\t\tdefault: throw new InvalidDataException(nameof(wdp.Weather));\n\t\t\t\t}\n\t\t\t\treturn string.Format(message, GetPkmnName(damageVictim, true));\n\t\t\t}\n\t\t\tcase IPBEWildPkmnAppearedPacket wpap:\n\t\t\t{\n\t\t\t\treturn string.Format(\"{0}{1} appeared!\", wpap.Pokemon.Count == 1 ? \"A wild \" : \"Oh! A wild \", wpap.Pokemon.Select(s => s.Nickname).ToArray().Andify());\n\t\t\t}\n\t\t\tcase PBEActionsRequestPacket arp:\n\t\t\t{\n\t\t\t\treturn string.Format(\"{0} must submit actions for {1} Pokémon.\", GetTrainerName(arp.Trainer), arp.Pokemon.Count);\n\t\t\t}\n\t\t\tcase IPBEAutoCenterPacket _:\n\t\t\t{\n\t\t\t\treturn \"The battlers shifted to the center!\";\n\t\t\t}\n\t\t\tcase PBEBattleResultPacket brp:\n\t\t\t{\n\t\t\t\tbool team0Caps = true;\n\t\t\t\tbool team1Caps = false;\n\t\t\t\tstring message;\n\t\t\t\tswitch (brp.BattleResult)\n\t\t\t\t{\n\t\t\t\t\tcase PBEBattleResult.Team0Forfeit: message = \"{0} forfeited.\"; break;\n\t\t\t\t\tcase PBEBattleResult.Team0Win: message = \"{0} defeated {1}!\"; break;\n\t\t\t\t\tcase PBEBattleResult.Team1Forfeit: message = \"{1} forfeited.\"; team1Caps = true; break;\n\t\t\t\t\tcase PBEBattleResult.Team1Win: message = \"{1} defeated {0}!\"; team0Caps = false; team1Caps = true; break;\n\t\t\t\t\tcase PBEBattleResult.WildCapture: goto bottom;\n\t\t\t\t\tcase PBEBattleResult.WildEscape: message = \"{0} got away!\"; break;\n\t\t\t\t\tcase PBEBattleResult.WildFlee: message = \"{1} got away!\"; team1Caps = true; break;\n\t\t\t\t\tdefault: throw new InvalidDataException(nameof(brp.BattleResult));\n\t\t\t\t}\n\t\t\t\treturn string.Format(message, GetRawCombinedName(battle.Teams[0], team0Caps), GetRawCombinedName(battle.Teams[1], team1Caps));\n\t\t\t}\n\t\t\tcase PBESwitchInRequestPacket sirp:\n\t\t\t{\n\t\t\t\treturn string.Format(\"{0} must send in {1} Pokémon.\", GetTrainerName(sirp.Trainer), sirp.Amount);\n\t\t\t}\n\t\t\tcase PBETurnBeganPacket tbp:\n\t\t\t{\n\t\t\t\treturn string.Format(\"Turn {0} is starting.\", tbp.TurnNumber);\n\t\t\t}\n\t\t}\n\tbottom:\n\t\treturn null;\n\t}\n\n\t/// <summary>Writes battle events to <see cref=\"Console.Out\"/> in English.</summary>\n\t/// <param name=\"battle\">The battle that <paramref name=\"packet\"/> belongs to.</param>\n\t/// <param name=\"packet\">The battle event packet.</param>\n\t/// <exception cref=\"ArgumentNullException\">Thrown when <paramref name=\"battle\"/> or <paramref name=\"packet\"/> are null.</exception>\n\tpublic static void ConsoleBattleEventHandler(PBEBattle battle, IPBEPacket packet)\n\t{\n\t\tstring? message = GetDefaultMessage(battle, packet, showRawHP: true);\n\t\tif (string.IsNullOrEmpty(message))\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\tConsole.WriteLine(message);\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Battle/BattleInventory.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Packets;\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace Kermalis.PokemonBattleEngine.Battle;\n\npublic sealed class PBEBattleInventory : IReadOnlyDictionary<PBEItem, PBEBattleInventory.PBEBattleInventorySlot>\n{\n\tpublic sealed class PBEBattleInventorySlot\n\t{\n\t\tpublic PBEItem Item { get; }\n\t\tpublic uint Quantity { get; internal set; }\n\n\t\tinternal PBEBattleInventorySlot(PBEItem item, uint quantity)\n\t\t{\n\t\t\tItem = item;\n\t\t\tQuantity = quantity;\n\t\t}\n\t}\n\n\tprivate readonly Dictionary<PBEItem, PBEBattleInventorySlot> _slots;\n\n\tpublic PBEBattleInventorySlot this[PBEItem key] => _slots[key];\n\tpublic IEnumerable<PBEItem> Keys => _slots.Keys;\n\tpublic IEnumerable<PBEBattleInventorySlot> Values => _slots.Values;\n\tpublic int Count => _slots.Count;\n\n\tinternal PBEBattleInventory(IReadOnlyList<(PBEItem item, uint quantity)> items)\n\t{\n\t\t_slots = new Dictionary<PBEItem, PBEBattleInventorySlot>(items.Count);\n\t\tforeach ((PBEItem item, uint quantity) in items)\n\t\t{\n\t\t\tif (item == PBEItem.None || !Enum.IsDefined(item))\n\t\t\t{\n\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(items), $\"Invalid item returned: {item}\");\n\t\t\t}\n\t\t\tif (!_slots.TryGetValue(item, out PBEBattleInventorySlot? slot))\n\t\t\t{\n\t\t\t\tslot = new PBEBattleInventorySlot(item, quantity);\n\t\t\t\t_slots.Add(item, slot);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tslot.Quantity += quantity;\n\t\t\t}\n\t\t}\n\t}\n\tinternal PBEBattleInventory(IReadOnlyList<PBEBattlePacket.PBETeamInfo.PBETrainerInfo.PBEInventorySlotInfo> items)\n\t{\n\t\t_slots = new Dictionary<PBEItem, PBEBattleInventorySlot>(items.Count);\n\t\tforeach (PBEBattlePacket.PBETeamInfo.PBETrainerInfo.PBEInventorySlotInfo slot in items)\n\t\t{\n\t\t\t_slots.Add(slot.Item, new PBEBattleInventorySlot(slot.Item, slot.Quantity));\n\t\t}\n\t}\n\n\tpublic bool ContainsKey(PBEItem key)\n\t{\n\t\treturn _slots.ContainsKey(key);\n\t}\n\tpublic bool TryGetValue(PBEItem key, [NotNullWhen(true)] out PBEBattleInventorySlot? value)\n\t{\n\t\treturn _slots.TryGetValue(key, out value);\n\t}\n\tpublic IEnumerator<KeyValuePair<PBEItem, PBEBattleInventorySlot>> GetEnumerator()\n\t{\n\t\treturn _slots.GetEnumerator();\n\t}\n\tIEnumerator IEnumerable.GetEnumerator()\n\t{\n\t\treturn _slots.GetEnumerator();\n\t}\n\n\tprivate static readonly PBEBattleInventory _empty = new(Array.Empty<(PBEItem, uint)>());\n\tinternal static PBEBattleInventory Empty()\n\t{\n\t\treturn _empty;\n\t}\n\n\tinternal void Remove(PBEItem item)\n\t{\n\t\t_slots[item].Quantity--;\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Battle/BattleMoveset.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Data.Utils;\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.ComponentModel;\n\nnamespace Kermalis.PokemonBattleEngine.Battle;\n\npublic sealed class PBEBattleMoveset : IReadOnlyList<PBEBattleMoveset.PBEBattleMovesetSlot>\n{\n\tpublic sealed class PBEBattleMovesetSlot : INotifyPropertyChanged\n\t{\n\t\tprivate void OnPropertyChanged(string property)\n\t\t{\n\t\t\tPropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));\n\t\t}\n\t\tpublic event PropertyChangedEventHandler? PropertyChanged;\n\n\t\tprivate PBEMove _move;\n\t\tpublic PBEMove Move\n\t\t{\n\t\t\tget => _move;\n\t\t\tset\n\t\t\t{\n\t\t\t\tif (_move != value)\n\t\t\t\t{\n\t\t\t\t\t_move = value;\n\t\t\t\t\tOnPropertyChanged(nameof(Move));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tprivate int _pp;\n\t\tpublic int PP\n\t\t{\n\t\t\tget => _pp;\n\t\t\tset\n\t\t\t{\n\t\t\t\tif (_pp != value)\n\t\t\t\t{\n\t\t\t\t\t_pp = value;\n\t\t\t\t\tOnPropertyChanged(nameof(PP));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tprivate int _maxPP;\n\t\tpublic int MaxPP\n\t\t{\n\t\t\tget => _maxPP;\n\t\t\tset\n\t\t\t{\n\t\t\t\tif (_maxPP != value)\n\t\t\t\t{\n\t\t\t\t\t_maxPP = value;\n\t\t\t\t\tOnPropertyChanged(nameof(MaxPP));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tinternal PBEBattleMovesetSlot()\n\t\t{\n\t\t\t_move = PBEMove.MAX;\n\t\t}\n\t\tinternal PBEBattleMovesetSlot(PBEMove move, int pp, int maxPP)\n\t\t{\n\t\t\t_move = move;\n\t\t\t_pp = pp;\n\t\t\t_maxPP = maxPP;\n\t\t}\n\t}\n\n\tprivate readonly PBEBattleMovesetSlot[] _list;\n\tpublic int Count => _list.Length;\n\tpublic PBEBattleMovesetSlot this[int index]\n\t{\n\t\tget\n\t\t{\n\t\t\tif (index >= _list.Length)\n\t\t\t{\n\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(index));\n\t\t\t}\n\t\t\treturn _list[index];\n\t\t}\n\t}\n\tpublic PBEBattleMovesetSlot? this[PBEMove move]\n\t{\n\t\tget\n\t\t{\n\t\t\tfor (int i = 0; i < _list.Length; i++)\n\t\t\t{\n\t\t\t\tPBEBattleMovesetSlot slot = _list[i];\n\t\t\t\tif (slot.Move == move)\n\t\t\t\t{\n\t\t\t\t\treturn slot;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tinternal PBEBattleMoveset(PBESettings settings)\n\t{\n\t\tint count = settings.NumMoves;\n\t\t_list = new PBEBattleMovesetSlot[count];\n\t\tfor (int i = 0; i < count; i++)\n\t\t{\n\t\t\t_list[i] = new PBEBattleMovesetSlot();\n\t\t}\n\t}\n\tinternal PBEBattleMoveset(PBESettings settings, PBEReadOnlyPartyMoveset moveset)\n\t{\n\t\tint count = moveset.Count;\n\t\tif (count != settings.NumMoves)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(moveset), $\"Moveset count must be equal to \\\"{nameof(settings.NumMoves)}\\\" ({settings.NumMoves}).\");\n\t\t}\n\t\t_list = new PBEBattleMovesetSlot[count];\n\t\tfor (int i = 0; i < count; i++)\n\t\t{\n\t\t\tPBEReadOnlyPartyMoveset.PBEReadOnlyPartyMovesetSlot slot = moveset[i];\n\t\t\tPBEMove move = slot.Move;\n\t\t\tint maxPP = PBEDataUtils.CalcMaxPP(move, slot.PPUps, settings);\n\t\t\t_list[i] = new PBEBattleMovesetSlot(move, slot.PP, maxPP);\n\t\t}\n\t}\n\tinternal PBEBattleMoveset(PBEBattleMoveset other)\n\t{\n\t\tint count = other._list.Length;\n\t\t_list = new PBEBattleMovesetSlot[count];\n\t\tfor (int i = 0; i < count; i++)\n\t\t{\n\t\t\tPBEBattleMovesetSlot oSlot = other._list[i];\n\t\t\t_list[i] = new PBEBattleMovesetSlot(oSlot.Move, oSlot.PP, oSlot.MaxPP);\n\t\t}\n\t}\n\n\tpublic static int GetTransformPP(PBESettings settings, PBEMove move)\n\t{\n\t\tsettings.ShouldBeReadOnly(nameof(settings));\n\t\tif (move == PBEMove.None)\n\t\t{\n\t\t\treturn 0;\n\t\t}\n\t\tif (move >= PBEMove.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(move));\n\t\t}\n\t\tIPBEMoveData mData = PBEDataProvider.Instance.GetMoveData(move);\n\t\tif (!mData.IsMoveUsable())\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(move));\n\t\t}\n\t\treturn mData.PPTier == 0 ? 1 : settings.PPMultiplier;\n\t}\n\tpublic static int GetNonTransformPP(PBESettings settings, PBEMove move, byte ppUps)\n\t{\n\t\tsettings.ShouldBeReadOnly(nameof(settings));\n\t\tif (move == PBEMove.None)\n\t\t{\n\t\t\treturn 0;\n\t\t}\n\t\tif (move >= PBEMove.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(move));\n\t\t}\n\t\tIPBEMoveData mData = PBEDataProvider.Instance.GetMoveData(move);\n\t\tif (!mData.IsMoveUsable())\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(move));\n\t\t}\n\t\tbyte tier = mData.PPTier;\n\t\treturn Math.Max(1, (tier * settings.PPMultiplier) + (tier * ppUps));\n\t}\n\n\tinternal static void DoTransform(PBEBattlePokemon user, PBEBattlePokemon target)\n\t{\n\t\tPBEBattleMoveset? targetKnownBackup = null;\n\t\tif (user.Trainer != target.Trainer)\n\t\t{\n\t\t\ttargetKnownBackup = new PBEBattleMoveset(target.KnownMoves);\n\t\t}\n\t\tPBESettings settings = user.Battle.Settings;\n\t\tfor (int i = 0; i < settings.NumMoves; i++)\n\t\t{\n\t\t\tPBEBattleMovesetSlot userMove = user.Moves._list[i];\n\t\t\tPBEBattleMovesetSlot userKnownMove = user.KnownMoves._list[i];\n\t\t\tPBEBattleMovesetSlot targetMove = target.Moves._list[i];\n\t\t\tPBEBattleMovesetSlot targetKnownMove = target.KnownMoves._list[i];\n\t\t\tPBEMove move;\n\t\t\tint pp;\n\t\t\tif (user.Trainer == target.Trainer)\n\t\t\t{\n\t\t\t\tmove = targetMove.Move;\n\t\t\t\tpp = move == PBEMove.MAX ? 0 : GetTransformPP(settings, move);\n\t\t\t\tuserMove.Move = move;\n\t\t\t\tuserMove.PP = pp;\n\t\t\t\tuserMove.MaxPP = pp;\n\n\t\t\t\tmove = targetKnownMove.Move;\n\t\t\t\tpp = move == PBEMove.MAX ? 0 : GetTransformPP(settings, move);\n\t\t\t\tuserKnownMove.Move = move;\n\t\t\t\tuserKnownMove.PP = 0;\n\t\t\t\tuserKnownMove.MaxPP = pp;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tmove = targetMove.Move;\n\t\t\t\tpp = move == PBEMove.MAX ? 0 : GetTransformPP(settings, move);\n\t\t\t\tuserMove.Move = move;\n\t\t\t\tuserMove.PP = pp;\n\t\t\t\tuserMove.MaxPP = pp;\n\t\t\t\ttargetKnownMove.Move = move;\n\t\t\t\t// Try to copy known PP from previous known moves\n\t\t\t\tPBEBattleMovesetSlot? bSlot = targetKnownBackup![move];\n\t\t\t\tif (bSlot is null) // bSlot is null if the current move was not previously known\n\t\t\t\t{\n\t\t\t\t\ttargetKnownMove.PP = 0;\n\t\t\t\t\ttargetKnownMove.MaxPP = 0;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\ttargetKnownMove.PP = bSlot.PP;\n\t\t\t\t\ttargetKnownMove.MaxPP = bSlot.MaxPP;\n\t\t\t\t}\n\t\t\t\tuserKnownMove.Move = move;\n\t\t\t\tuserKnownMove.PP = 0;\n\t\t\t\tuserKnownMove.MaxPP = pp;\n\t\t\t}\n\t\t}\n\t}\n\tinternal ReadOnlyCollection<PBEMove> ForTransformPacket()\n\t{\n\t\tvar moves = new PBEMove[_list.Length];\n\t\tfor (int i = 0; i < _list.Length; i++)\n\t\t{\n\t\t\tmoves[i] = _list[i].Move;\n\t\t}\n\t\treturn new ReadOnlyCollection<PBEMove>(moves);\n\t}\n\t// Reorders after one move is changed. It won't work if there are multiple culprit spots\n\tinternal void Organize()\n\t{\n\t\tfor (int i = 0; i < _list.Length - 1; i++)\n\t\t{\n\t\t\tPBEBattleMovesetSlot slot = _list[i];\n\t\t\tif (slot.Move != PBEMove.None && slot.Move != PBEMove.MAX)\n\t\t\t{\n\t\t\t\tcontinue; // Skip populated slots\n\t\t\t}\n\n\t\t\tPBEBattleMovesetSlot nextSlot = _list[i + 1];\n\t\t\tif (nextSlot.Move != PBEMove.None && nextSlot.Move != PBEMove.MAX)\n\t\t\t{\n\t\t\t\t_list[i] = nextSlot;\n\t\t\t\t_list[i + 1] = slot; // Swap slots since next slot has a move but current doesn't\n\t\t\t}\n\t\t}\n\t}\n\tinternal void Reset(PBEBattleMoveset other)\n\t{\n\t\tfor (int i = 0; i < _list.Length; i++)\n\t\t{\n\t\t\tPBEBattleMovesetSlot slot = _list[i];\n\t\t\tPBEBattleMovesetSlot oSlot = other._list[i];\n\t\t\tslot.Move = oSlot.Move;\n\t\t\tslot.PP = oSlot.PP;\n\t\t\tslot.MaxPP = oSlot.MaxPP;\n\t\t}\n\t}\n\tinternal void SetUnknown()\n\t{\n\t\tfor (int i = 0; i < _list.Length; i++)\n\t\t{\n\t\t\tPBEBattleMovesetSlot slot = _list[i];\n\t\t\tslot.Move = PBEMove.MAX;\n\t\t\tslot.PP = 0;\n\t\t\tslot.MaxPP = 0;\n\t\t}\n\t}\n\n\tpublic bool Contains(PBEMove move)\n\t{\n\t\treturn this[move] is not null;\n\t}\n\tpublic bool Contains(PBEMoveEffect effect)\n\t{\n\t\tfor (int i = 0; i < _list.Length; i++)\n\t\t{\n\t\t\tPBEMove move = _list[i].Move;\n\t\t\tif (move != PBEMove.None && move != PBEMove.MAX && PBEDataProvider.Instance.GetMoveData(move).Effect == effect)\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t// TODO: This is copied from PBEMovesetInterfaceExtensions\n\tpublic int CountMoves()\n\t{\n\t\tint num = 0;\n\t\tfor (int i = 0; i < _list.Length; i++)\n\t\t{\n\t\t\tif (_list[i].Move != PBEMove.None)\n\t\t\t{\n\t\t\t\tnum++;\n\t\t\t}\n\t\t}\n\t\treturn num;\n\t}\n\n\tpublic IEnumerator<PBEBattleMovesetSlot> GetEnumerator()\n\t{\n\t\tfor (int i = 0; i < _list.Length; i++)\n\t\t{\n\t\t\tyield return _list[i];\n\t\t}\n\t}\n\tIEnumerator IEnumerable.GetEnumerator()\n\t{\n\t\treturn GetEnumerator();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Battle/BattlePokemon.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Data.Utils;\nusing Kermalis.PokemonBattleEngine.Packets;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\n\nnamespace Kermalis.PokemonBattleEngine.Battle;\n\n/// <summary>Represents a specific Pokémon during a battle.</summary>\npublic sealed class PBEBattlePokemon : IPBEPokemonKnownTypes, IPBEPokemonTypes, IPBESpeciesForm\n{\n\tpublic PBEBattle Battle { get; }\n\tpublic PBETeam Team { get; }\n\tpublic PBETrainer Trainer { get; }\n\tpublic byte Id { get; }\n\tpublic bool IsWild => Team.IsWild;\n\tpublic bool IsLocallyHosted => Battle.IsLocallyHosted;\n\tpublic bool PBEIgnore { get; }\n\n\tpublic bool CanBattle => HP > 0 && !PBEIgnore;\n\n\t#region Basic Properties\n\n\t/// <summary>The Pokémon's current HP.</summary>\n\tpublic ushort HP { get; set; }\n\t/// <summary>The Pokémon's maximum HP.</summary>\n\tpublic ushort MaxHP { get; set; }\n\t/// <summary>The Pokémon's current HP as a percentage.</summary>\n\tpublic float HPPercentage { get; set; }\n\t/// <summary>The Pokémon's attack stat.</summary>\n\tpublic ushort Attack { get; set; }\n\tpublic sbyte AttackChange { get; set; }\n\t/// <summary>The Pokémon's defense stat.</summary>\n\tpublic ushort Defense { get; set; }\n\tpublic sbyte DefenseChange { get; set; }\n\t/// <summary>The Pokémon's special attack stat.</summary>\n\tpublic ushort SpAttack { get; set; }\n\tpublic sbyte SpAttackChange { get; set; }\n\t/// <summary>The Pokémon's special defense stat.</summary>\n\tpublic ushort SpDefense { get; set; }\n\tpublic sbyte SpDefenseChange { get; set; }\n\t/// <summary>The Pokémon's speed stat.</summary>\n\tpublic ushort Speed { get; set; }\n\tpublic sbyte SpeedChange { get; set; }\n\tpublic sbyte AccuracyChange { get; set; }\n\tpublic sbyte EvasionChange { get; set; }\n\tpublic PBEReadOnlyStatCollection? OriginalEffortValues { get; }\n\tpublic PBEStatCollection? EffortValues { get; }\n\tpublic PBEReadOnlyStatCollection? IndividualValues { get; }\n\tpublic byte Friendship { get; set; }\n\tpublic byte OriginalLevel { get; set; }\n\t/// <summary>The Pokémon's level.</summary>\n\tpublic byte Level { get; set; }\n\tpublic uint OriginalEXP { get; set; }\n\tpublic uint EXP { get; set; }\n\t/// <summary>The Pokémon's nature.</summary>\n\tpublic PBENature Nature { get; set; }\n\t/// <summary>The moveset the Pokémon had upon entering battle.</summary>\n\tpublic PBEReadOnlyPartyMoveset? OriginalMoveset { get; }\n\n\t/// <summary>The Pokémon's field position.</summary>\n\tpublic PBEFieldPosition FieldPosition { get; set; }\n\t/// <summary>The Pokémon's current ability.</summary>\n\tpublic PBEAbility Ability { get; set; }\n\t/// <summary>The ability the Pokémon is known to have.</summary>\n\tpublic PBEAbility KnownAbility { get; set; }\n\t/// <summary>The ability the Pokémon had upon entering battle.</summary>\n\tpublic PBEAbility OriginalAbility { get; set; }\n\t/// <summary>The ability the Pokémon will regain upon switching out, fainting, or the battle ending.</summary>\n\tpublic PBEAbility RevertAbility { get; set; }\n\t/// <summary>The Pokémon's gender.</summary>\n\tpublic PBEGender Gender { get; set; }\n\t/// <summary>The gender the Pokémon looks like (affected by transforming and disguising).</summary>\n\tpublic PBEGender KnownGender { get; set; }\n\t/// <summary>The Pokémon's current item.</summary>\n\tpublic PBEItem Item { get; set; }\n\t/// <summary>The item the Pokémon is known to have.</summary>\n\tpublic PBEItem KnownItem { get; set; }\n\t/// <summary>The item the Pokémon had upon entering battle.</summary>\n\tpublic PBEItem OriginalItem { get; set; }\n\t/// <summary>The Pokémon's current ball (affected by catching).</summary>\n\tpublic PBEItem CaughtBall { get; set; }\n\t/// <summary>The ball the Pokémon is known to be in (affected by disguising).</summary>\n\tpublic PBEItem KnownCaughtBall { get; set; }\n\t/// <summary>The ball the Pokémon was in upon entering battle.</summary>\n\tpublic PBEItem OriginalCaughtBall { get; set; }\n\t/// <summary>The moves the Pokémon currently has.</summary>\n\tpublic PBEBattleMoveset Moves { get; }\n\t/// <summary>The moves the Pokémon is known to have.</summary>\n\tpublic PBEBattleMoveset KnownMoves { get; }\n\t/// <summary>The nickname the Pokémon normally has.</summary>\n\tpublic string Nickname { get; set; }\n\t/// <summary>The nickname the Pokémon is known to have.</summary>\n\tpublic string KnownNickname { get; set; }\n\t/// <summary>The shininess the Pokémon normally has.</summary>\n\tpublic bool Shiny { get; set; }\n\t/// <summary>The shininess everyone sees the Pokémon has.</summary>\n\tpublic bool KnownShiny { get; set; }\n\tpublic bool Pokerus { get; set; }\n\t/// <summary>The current species of the Pokémon (affected by transforming and form changing).</summary>\n\tpublic PBESpecies Species { get; set; }\n\t/// <summary>The species everyone sees the Pokémon as (affected by transforming, disguising, and form changing).</summary>\n\tpublic PBESpecies KnownSpecies { get; set; }\n\t/// <summary>The species the Pokémon was upon entering battle.</summary>\n\tpublic PBESpecies OriginalSpecies { get; set; }\n\tpublic PBEForm Form { get; set; }\n\tpublic PBEForm KnownForm { get; set; }\n\tpublic PBEForm OriginalForm { get; set; }\n\tpublic PBEForm RevertForm { get; set; }\n\tpublic PBEStatus1 Status1 { get; set; }\n\tpublic PBEStatus1 OriginalStatus1 { get; set; }\n\tpublic PBEStatus2 Status2 { get; set; }\n\tpublic PBEStatus2 KnownStatus2 { get; set; }\n\t/// <summary>The Pokémon's first type.</summary>\n\tpublic PBEType Type1 { get; set; }\n\t/// <summary>The first type everyone believes the Pokémon has.</summary>\n\tpublic PBEType KnownType1 { get; set; }\n\t/// <summary>The Pokémon's second type.</summary>\n\tpublic PBEType Type2 { get; set; }\n\t/// <summary>The second type everyone believes the Pokémon has.</summary>\n\tpublic PBEType KnownType2 { get; set; }\n\tpublic float Weight { get; set; }\n\tpublic float KnownWeight { get; set; }\n\n\t#endregion\n\n\t#region Statuses\n\n\t/// <summary>The counter used for <see cref=\"PBEStatus1.BadlyPoisoned\"/> and <see cref=\"PBEStatus1.Asleep\"/>.</summary>\n\tpublic byte Status1Counter { get; set; }\n\t/// <summary>The amount of turns the Pokémon will sleep for before waking.</summary>\n\tpublic byte SleepTurns { get; set; }\n\t/// <summary>The counter used for <see cref=\"PBEStatus2.Confused\"/>.</summary>\n\tpublic byte ConfusionCounter { get; set; }\n\t/// <summary>The amount of turns the Pokémon will be confused for before snapping out of it.</summary>\n\tpublic byte ConfusionTurns { get; set; }\n\t/// <summary>The Pokémon that <see cref=\"PBEStatus2.Infatuated\"/> is bound to.</summary>\n\tpublic PBEBattlePokemon? InfatuatedWithPokemon { get; set; }\n\t/// <summary>The amount of turns until <see cref=\"PBEStatus2.MagnetRise\"/> ends.</summary>\n\tpublic byte MagnetRiseTurns { get; set; }\n\t/// <summary>The Pokémon that <see cref=\"PBEStatus2.LockOn\"/> is bound to.</summary>\n\tpublic PBEBattlePokemon? LockOnPokemon { get; set; }\n\tpublic byte LockOnTurns { get; set; }\n\t/// <summary>The amount of times the Pokémon has successfully used <see cref=\"PBEMoveEffect.Protect\"/>, <see cref=\"PBEMoveEffect.QuickGuard\"/>, and/or <see cref=\"PBEMoveEffect.WideGuard\"/> consecutively.</summary>\n\tpublic int Protection_Counter { get; set; }\n\tpublic bool Protection_Used { get; set; }\n\tpublic PBERoostTypes RoostTypes { get; set; }\n\t/// <summary>The position to return <see cref=\"PBEStatus2.LeechSeed\"/> HP to on <see cref=\"SeededTeam\"/>.</summary>\n\tpublic PBEFieldPosition SeededPosition { get; set; }\n\t/// <summary>The team responsible for <see cref=\"PBEStatus2.LeechSeed\"/>.</summary>\n\tpublic PBETeam? SeededTeam { get; set; }\n\t/// <summary>The amount of HP the Pokémon's <see cref=\"PBEStatus2.Substitute\"/> has left.</summary>\n\tpublic ushort SubstituteHP { get; set; }\n\tpublic PBEBattleMoveset TransformBackupMoves { get; }\n\n\t#endregion\n\n\t#region Actions\n\n\t/// <summary><see langword=\"true\"/> if the Pokémon has successfully executed a move this turn.</summary>\n\tpublic bool HasUsedMoveThisTurn { get; set; }\n\t/// <summary>The action the Pokémon will try to perform when the turn is run. <see langword=\"null\"/> if the Pokémon just switched in, attempted to flee, etc.</summary>\n\tpublic PBETurnAction? TurnAction { get; set; }\n\t/// <summary>The move the Pokémon is forced to use by multi-turn moves.</summary>\n\tpublic PBEMove TempLockedMove { get; set; } // TODO: Tests - Does a pkmn lose its temp locked move if it runs out of pp on the move, all moves, or ever? (Some move can lower its pp while it's being used?)\n\t/// <summary>The targets the Pokémon is forced to target by multi-turn moves.</summary>\n\tpublic PBETurnTarget TempLockedTargets { get; set; }\n\t/// <summary>The move the Pokémon is forced to use by its choice item.</summary>\n\tpublic PBEMove ChoiceLockedMove { get; set; } // TODO: Tests - Does a pkmn lose its choice locked move if it runs out of pp on the move, all moves, or ever?\n\n\t#endregion\n\n\t#region Special Flags\n\n\t/// <summary>True if the Pokémon has successfully used <see cref=\"PBEMoveEffect.Minimize\"/> which makes it succeptible to double damage from <see cref=\"PBEMoveFlag.DoubleDamageMinimized\"/>.</summary>\n\tpublic bool Minimize_Used { get; set; }\n\t/// <summary>The amount of turns left until a Pokémon with <see cref=\"PBEAbility.SlowStart\"/> loses its hinderance.</summary>\n\tpublic byte SlowStart_HinderTurnsLeft { get; set; }\n\t/// <summary>True if the Pokémon was present at the start of the turn, which would allow <see cref=\"PBEAbility.SpeedBoost\"/> to activate.</summary>\n\tpublic bool SpeedBoost_AbleToSpeedBoostThisTurn { get; set; }\n\n\t#endregion\n\n\tpublic List<PBEBattlePokemon> EXPPokemon { get; } = new();\n\n\t#region Constructors\n\n\tprivate PBEBattlePokemon(PBETrainer trainer, byte id,\n\t\tPBESpecies species, PBEForm form, string nickname, byte level, uint exp, byte friendship, bool shiny, bool pokerus,\n\t\tPBEAbility ability, PBENature nature, PBEGender gender, PBEItem item, PBEItem caughtBall,\n\t\tPBEReadOnlyStatCollection evs, PBEReadOnlyStatCollection ivs, PBEReadOnlyPartyMoveset moves)\n\t{\n\t\tBattle = trainer.Battle;\n\t\tTeam = trainer.Team;\n\t\tTrainer = trainer;\n\t\tId = id;\n\t\tSpecies = OriginalSpecies = KnownSpecies = species;\n\t\tForm = OriginalForm = KnownForm = RevertForm = form;\n\t\tIPBEPokemonData pData = PBEDataProvider.Instance.GetPokemonData(species, form);\n\t\tKnownType1 = Type1 = pData.Type1;\n\t\tKnownType2 = Type2 = pData.Type2;\n\t\tKnownWeight = Weight = pData.Weight;\n\t\tNickname = KnownNickname = nickname;\n\t\tLevel = OriginalLevel = level;\n\t\tEXP = OriginalEXP = exp;\n\t\tFriendship = friendship;\n\t\tShiny = KnownShiny = shiny;\n\t\tPokerus = pokerus;\n\t\tAbility = OriginalAbility = RevertAbility = ability;\n\t\tKnownAbility = PBEAbility.MAX;\n\t\tNature = nature;\n\t\tGender = KnownGender = gender;\n\t\tItem = OriginalItem = item;\n\t\tKnownItem = (PBEItem)ushort.MaxValue;\n\t\tKnownCaughtBall = CaughtBall = OriginalCaughtBall = caughtBall;\n\t\tOriginalEffortValues = evs;\n\t\tEffortValues = new PBEStatCollection(evs);\n\t\tIndividualValues = new PBEReadOnlyStatCollection(ivs);\n\t\tSetStats(true, true);\n\t\tOriginalMoveset = moves;\n\t\tPBESettings settings = Battle.Settings;\n\t\tMoves = new PBEBattleMoveset(settings, moves);\n\t\tKnownMoves = new PBEBattleMoveset(settings);\n\t\tTransformBackupMoves = new PBEBattleMoveset(settings);\n\t}\n\tprivate PBEBattlePokemon(PBETrainer trainer, byte id, IPBEPokemon pkmn, PBEReadOnlyPartyMoveset moves)\n\t\t: this(trainer, id,\n\t\t\t  pkmn.Species, pkmn.Form, pkmn.Nickname, pkmn.Level, pkmn.EXP, pkmn.Friendship, pkmn.Shiny, pkmn.Pokerus,\n\t\t\t  pkmn.Ability, pkmn.Nature, pkmn.Gender, pkmn.Item, pkmn.CaughtBall,\n\t\t\t  new PBEReadOnlyStatCollection(pkmn.EffortValues), new PBEReadOnlyStatCollection(pkmn.IndividualValues), moves)\n\t{\n\t\tPBEIgnore = pkmn.PBEIgnore;\n\t}\n\tinternal PBEBattlePokemon(PBETrainer trainer, byte id, IPBEPokemon pkmn)\n\t\t: this(trainer, id, pkmn, new PBEReadOnlyPartyMoveset(trainer.Battle.Settings, pkmn.Moveset))\n\t{\n\t\ttrainer.Party.Add(this);\n\t}\n\tinternal PBEBattlePokemon(PBETrainer trainer, byte id, IPBEPartyPokemon pkmn)\n\t\t: this(trainer, id, pkmn, new PBEReadOnlyPartyMoveset(pkmn.Moveset))\n\t{\n\t\tushort hp = pkmn.HP;\n\t\tif (hp > MaxHP)\n\t\t{\n\t\t\tthrow new InvalidDataException(nameof(pkmn.HP));\n\t\t}\n\t\tHP = hp;\n\t\tUpdateHPPercentage();\n\t\tPBEStatus1 status1 = pkmn.Status1;\n\t\tif (status1 >= PBEStatus1.MAX)\n\t\t{\n\t\t\tthrow new InvalidDataException(nameof(pkmn.Status1));\n\t\t}\n\t\tif (status1 == PBEStatus1.BadlyPoisoned)\n\t\t{\n\t\t\tStatus1Counter = 1;\n\t\t}\n\t\tStatus1 = OriginalStatus1 = status1;\n\t\tbyte sleepTurns = pkmn.SleepTurns;\n\t\tif (status1 != PBEStatus1.Asleep && sleepTurns != 0)\n\t\t{\n\t\t\tthrow new InvalidDataException(nameof(pkmn.SleepTurns));\n\t\t}\n\t\tSleepTurns = sleepTurns;\n\t\ttrainer.Party.Add(this);\n\t}\n\tinternal PBEBattlePokemon(PBETrainer trainer, PBEBattlePacket.PBETeamInfo.PBETrainerInfo.PBEBattlePokemonInfo info)\n\t\t: this(trainer, info.Id,\n\t\t\t  info.Species, info.Form, info.Nickname, info.Level, info.EXP, info.Friendship, info.Shiny, info.Pokerus,\n\t\t\t  info.Ability, info.Nature, info.Gender, info.Item, info.CaughtBall,\n\t\t\t  info.EffortValues, info.IndividualValues, info.Moveset)\n\t{\n\t\tStatus1 = info.Status1;\n\t\tif (Status1 == PBEStatus1.BadlyPoisoned)\n\t\t{\n\t\t\tStatus1Counter = 1;\n\t\t}\n\t}\n\tprivate PBEBattlePokemon(PBETrainer trainer, IPBEPkmnAppearedInfo_Hidden info)\n\t{\n\t\tBattle = trainer.Battle;\n\t\tTeam = trainer.Team;\n\t\tTrainer = trainer;\n\t\tFieldPosition = info.FieldPosition;\n\t\tHPPercentage = info.HPPercentage;\n\t\tStatus1 = info.Status1;\n\t\tif (Status1 == PBEStatus1.BadlyPoisoned)\n\t\t{\n\t\t\tStatus1Counter = 1;\n\t\t}\n\t\tLevel = info.Level;\n\t\tKnownAbility = Ability = PBEAbility.MAX;\n\t\tKnownGender = Gender = info.Gender;\n\t\tKnownItem = Item = (PBEItem)ushort.MaxValue;\n\t\tMoves = new PBEBattleMoveset(Battle.Settings); // For Transform\n\t\tKnownMoves = new PBEBattleMoveset(Battle.Settings);\n\t\tTransformBackupMoves = new PBEBattleMoveset(Battle.Settings); // For Transform\n\t\tKnownNickname = Nickname = info.Nickname;\n\t\tKnownShiny = Shiny = info.Shiny;\n\t\tKnownSpecies = Species = info.Species;\n\t\tKnownForm = Form = info.Form;\n\t\tIPBEPokemonData pData = PBEDataProvider.Instance.GetPokemonData(KnownSpecies, KnownForm);\n\t\tKnownType1 = Type1 = pData.Type1;\n\t\tKnownType2 = Type2 = pData.Type2;\n\t\tKnownWeight = Weight = pData.Weight;\n\t\ttrainer.Party.Add(this);\n\t\tBattle.ActiveBattlers.Add(this);\n\t}\n\tprivate PBEBattlePokemon(PBETrainer trainer, IPBEPkmnSwitchInInfo_Hidden info)\n\t\t: this(trainer, (IPBEPkmnAppearedInfo_Hidden)info)\n\t{\n\t\tKnownCaughtBall = CaughtBall = info.CaughtBall;\n\t}\n\tpublic PBEBattlePokemon(PBETrainer trainer, PBEPkmnSwitchInPacket_Hidden.PBEPkmnSwitchInInfo info)\n\t\t: this(trainer, (IPBEPkmnSwitchInInfo_Hidden)info) { }\n\tpublic PBEBattlePokemon(PBEBattle battle, PBEWildPkmnAppearedPacket_Hidden.PBEWildPkmnInfo info)\n\t\t: this(battle.Teams[1].Trainers[0], info) { }\n\n\t#endregion\n\n\tpublic void AddEXPPokemon(PBEBattlePokemon pkmn)\n\t{\n\t\tif (!EXPPokemon.Contains(pkmn))\n\t\t{\n\t\t\tEXPPokemon.Add(pkmn);\n\t\t}\n\t}\n\t/// <summary>For use with level ups. Does not send any packets.</summary>\n\tpublic void LearnMove(PBEMove move, int index)\n\t{\n\t\tbool transformed = Status2.HasFlag(PBEStatus2.Transformed);\n\t\tPBEBattleMoveset moves = transformed ? TransformBackupMoves : Moves;\n\t\tPBEBattleMoveset.PBEBattleMovesetSlot slot = moves[index];\n\t\tPBEMove oldMove = slot.Move;\n\t\tslot.Move = move;\n\t\tint pp = PBEDataUtils.CalcMaxPP(move, 0, Battle.Settings);\n\t\tslot.PP = pp;\n\t\tslot.MaxPP = pp;\n\t\t// Update known moves below\n\t\tif (!transformed && FieldPosition != PBEFieldPosition.None)\n\t\t{\n\t\t\tmoves = KnownMoves;\n\t\t\tPBEBattleMoveset.PBEBattleMovesetSlot? slot2 = moves[oldMove];\n\t\t\tif (slot2 is not null) // Check if move is known first\n\t\t\t{\n\t\t\t\tslot2.Move = PBEMove.MAX; // Make the move unknown if the old move was known\n\t\t\t\tslot2.PP = 0;\n\t\t\t\tmoves.Organize();\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void ApplyPowerTrickChange()\n\t{\n\t\tushort a = Attack;\n\t\tAttack = Defense;\n\t\tDefense = a;\n\t}\n\t/// <summary>Applies effects that occur on switching out or escaping such as <see cref=\"PBEAbility.NaturalCure\"/>.</summary>\n\tpublic void ApplyNaturalCure()\n\t{\n\t\tif (!PBEIgnore && Ability == PBEAbility.NaturalCure)\n\t\t{\n\t\t\tStatus1 = PBEStatus1.None;\n\t\t\tStatus1Counter = 0;\n\t\t\tSleepTurns = 0;\n\t\t}\n\t}\n\tprivate void ResetSpecies()\n\t{\n\t\tSpecies = KnownSpecies = OriginalSpecies;\n\t\tForm = KnownForm = RevertForm;\n\t\tAbility = KnownAbility = RevertAbility;\n\t\tIPBEPokemonData pData = PBEDataProvider.Instance.GetPokemonData(this);\n\t\tKnownAbility = PBEAbility.MAX;\n\t\tKnownGender = Gender;\n\t\tKnownItem = (PBEItem)ushort.MaxValue;\n\t\tKnownMoves.SetUnknown();\n\t\tKnownNickname = Nickname;\n\t\tKnownShiny = Shiny;\n\t\tKnownType1 = Type1 = pData.Type1;\n\t\tKnownType2 = Type2 = pData.Type2;\n\t}\n\tprivate void ResetVolatileStuff()\n\t{\n\t\tConfusionCounter = 0;\n\t\tConfusionTurns = 0;\n\t\tLockOnPokemon = null;\n\t\tLockOnTurns = 0;\n\t\tMagnetRiseTurns = 0;\n\t\tProtection_Counter = 0;\n\t\tProtection_Used = false;\n\t\tSeededPosition = PBEFieldPosition.None;\n\t\tSeededTeam = null;\n\t\tSubstituteHP = 0;\n\t\tif (Status2.HasFlag(PBEStatus2.Transformed))\n\t\t{\n\t\t\tMoves.Reset(TransformBackupMoves);\n\t\t\tTransformBackupMoves.SetUnknown();\n\t\t}\n\t\tStatus2 = PBEStatus2.None;\n\t\tKnownStatus2 = PBEStatus2.None;\n\n\t\tHasUsedMoveThisTurn = false;\n\t\tTurnAction = null;\n\t\tTempLockedMove = PBEMove.None;\n\t\tTempLockedTargets = PBETurnTarget.None;\n\t\tChoiceLockedMove = PBEMove.None;\n\n\t\tMinimize_Used = false;\n\t\tSlowStart_HinderTurnsLeft = 0;\n\t\tSpeedBoost_AbleToSpeedBoostThisTurn = false;\n\t}\n\t/// <summary>Sets and clears all information required for switching out.</summary>\n\tpublic void ClearForSwitch()\n\t{\n\t\tEXPPokemon.Clear();\n\t\tFieldPosition = PBEFieldPosition.None;\n\t\tApplyNaturalCure();\n\t\tif (Ability == PBEAbility.Regenerator)\n\t\t{\n\t\t\tHP = Math.Clamp((ushort)(HP + (MaxHP / 3)), ushort.MinValue, MaxHP);\n\t\t\tUpdateHPPercentage();\n\t\t}\n\t\tResetSpecies();\n\t\tClearStatChanges();\n\t\tif (Status1 == PBEStatus1.Asleep)\n\t\t{\n\t\t\tStatus1Counter = 0;\n\t\t}\n\t\telse if (Status1 == PBEStatus1.BadlyPoisoned)\n\t\t{\n\t\t\tStatus1Counter = 1;\n\t\t}\n\t\tResetVolatileStuff();\n\t\tSetStats(false, false);\n\t}\n\t/// <summary>Sets and clears all information required for fainting.</summary>\n\tpublic void ClearForFaint()\n\t{\n\t\tFieldPosition = PBEFieldPosition.None;\n\t\tResetSpecies();\n\t\tClearStatChanges();\n\t\tStatus1 = PBEStatus1.None;\n\t\tResetVolatileStuff();\n\t\tSetStats(false, false);\n\t}\n\t// GameFreak are very inconsistent with how they handle Power Trick when recalculating stats\n\t// Form change, Transform, Level up\n\tpublic void SetStats(bool calculateHP, bool setMaxHPIfCalcHP)\n\t{\n\t\tIPBEPokemonData pData = PBEDataProvider.Instance.GetPokemonData(this);\n\t\tPBENature nature = Nature;\n\t\tPBEStatCollection evs = EffortValues!;\n\t\tPBEReadOnlyStatCollection ivs = IndividualValues!;\n\t\tbyte level = Level;\n\t\tPBESettings settings = Battle.Settings;\n\t\tif (calculateHP)\n\t\t{\n\t\t\tushort oldMaxHP = MaxHP;\n\t\t\tushort hp = PBEDataUtils.CalculateStat(pData, PBEStat.HP, nature, evs.HP, ivs.HP, level, settings);\n\t\t\tMaxHP = hp;\n\t\t\tif (setMaxHPIfCalcHP)\n\t\t\t{\n\t\t\t\tHP = hp;\n\t\t\t}\n\t\t\telse if (HP != 0 && oldMaxHP != hp) // Don't change for a fainted mon\n\t\t\t{\n\t\t\t\tHP = (ushort)Math.Max(1, HP + (hp - oldMaxHP));\n\t\t\t}\n\t\t\tUpdateHPPercentage();\n\t\t}\n\t\tAttack = PBEDataUtils.CalculateStat(pData, PBEStat.Attack, nature, evs.Attack, ivs.Attack, level, settings);\n\t\tDefense = PBEDataUtils.CalculateStat(pData, PBEStat.Defense, nature, evs.Defense, ivs.Defense, level, settings);\n\t\tSpAttack = PBEDataUtils.CalculateStat(pData, PBEStat.SpAttack, nature, evs.SpAttack, ivs.SpAttack, level, settings);\n\t\tSpDefense = PBEDataUtils.CalculateStat(pData, PBEStat.SpDefense, nature, evs.SpDefense, ivs.SpDefense, level, settings);\n\t\tSpeed = PBEDataUtils.CalculateStat(pData, PBEStat.Speed, nature, evs.Speed, ivs.Speed, level, settings);\n\t}\n\t/// <summary>Copies the <paramref name=\"target\"/>, does not set <see cref=\"PBEStatus2.Transformed\"/>.</summary>\n\t/// <param name=\"target\">The Pokémon to transform into.</param>\n\tpublic void Transform(PBEBattlePokemon target)\n\t{\n\t\tif (Trainer != target.Trainer)\n\t\t{\n\t\t\tKnownAbility = target.KnownAbility = Ability = target.Ability;\n\t\t\tKnownType1 = target.KnownType1 = Type1 = target.Type1;\n\t\t\tKnownType2 = target.KnownType2 = Type2 = target.Type2;\n\t\t\tKnownWeight = target.KnownWeight = Weight = target.Weight;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tAbility = target.Ability;\n\t\t\tKnownAbility = target.KnownAbility;\n\t\t\tType1 = target.Type1;\n\t\t\tKnownType1 = target.KnownType1;\n\t\t\tType2 = target.Type2;\n\t\t\tKnownType2 = target.KnownType2;\n\t\t\tWeight = target.Weight;\n\t\t\tKnownWeight = target.KnownWeight;\n\t\t}\n\t\tKnownGender = target.KnownGender = target.Gender;\n\t\tKnownShiny = target.KnownShiny = target.Shiny;\n\t\tKnownSpecies = target.KnownSpecies = Species = target.Species;\n\t\tKnownForm = target.KnownForm = Form = target.Form;\n\t\tAttack = target.Attack;\n\t\tDefense = target.Defense;\n\t\tSpAttack = target.SpAttack;\n\t\tSpDefense = target.SpDefense;\n\t\tSpeed = target.Speed;\n\t\tAttackChange = target.AttackChange;\n\t\tDefenseChange = target.DefenseChange;\n\t\tSpAttackChange = target.SpAttackChange;\n\t\tSpDefenseChange = target.SpDefenseChange;\n\t\tSpeedChange = target.SpeedChange;\n\t\tAccuracyChange = target.AccuracyChange;\n\t\tEvasionChange = target.EvasionChange;\n\t\tTransformBackupMoves.Reset(Moves);\n\t\tPBEBattleMoveset.DoTransform(this, target);\n\t}\n\tpublic void UpdateKnownPP(PBEMove move, int amountReduced)\n\t{\n\t\tif (move == PBEMove.None || move >= PBEMove.MAX || !PBEDataUtils.IsMoveUsable(move))\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(move));\n\t\t}\n\t\tPBEBattleMoveset.PBEBattleMovesetSlot knownSlot = KnownMoves[move]!;\n\t\tknownSlot.PP += amountReduced;\n\t\tif (knownSlot.MaxPP == 0)\n\t\t{\n\t\t\tif (Status2.HasFlag(PBEStatus2.Transformed))\n\t\t\t{\n\t\t\t\tknownSlot.MaxPP = PBEBattleMoveset.GetTransformPP(Battle.Settings, move);\n\t\t\t}\n\t\t\telse if (Battle.Settings.MaxPPUps == 0 || knownSlot.PP > PBEBattleMoveset.GetNonTransformPP(Battle.Settings, move, (byte)(Battle.Settings.MaxPPUps - 1)))\n\t\t\t{\n\t\t\t\tknownSlot.MaxPP = PBEBattleMoveset.GetNonTransformPP(Battle.Settings, move, Battle.Settings.MaxPPUps);\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>Divides <see cref=\"HP\"/> by <see cref=\"MaxHP\"/> and places the result in <see cref=\"HPPercentage\"/>.</summary>\n\tpublic void UpdateHPPercentage()\n\t{\n\t\tHPPercentage = (float)HP / MaxHP;\n\t}\n\tpublic void StartRoost()\n\t{\n\t\tPBERoostTypes t = PBERoostTypes.None;\n\t\tif (Type1 == PBEType.Flying)\n\t\t{\n\t\t\tif (Type2 == PBEType.None)\n\t\t\t{\n\t\t\t\tType1 = PBEType.Normal; // Pure flying-type becomes Normal-type\n\t\t\t\tt |= PBERoostTypes.Type1;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tType1 = Type2;\n\t\t\t\tType2 = PBEType.None;\n\t\t\t\tt |= PBERoostTypes.Type2;\n\t\t\t}\n\t\t}\n\t\tif (Type2 == PBEType.Flying)\n\t\t{\n\t\t\tType2 = PBEType.None;\n\t\t\tt |= PBERoostTypes.Type2;\n\t\t}\n\t\tif (KnownType1 == PBEType.Flying)\n\t\t{\n\t\t\tif (KnownType2 == PBEType.None)\n\t\t\t{\n\t\t\t\tKnownType1 = PBEType.Normal;\n\t\t\t\tt |= PBERoostTypes.KnownType1;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tKnownType1 = KnownType2;\n\t\t\t\tKnownType2 = PBEType.None;\n\t\t\t\tt |= PBERoostTypes.KnownType2;\n\t\t\t}\n\t\t}\n\t\tif (KnownType2 == PBEType.Flying)\n\t\t{\n\t\t\tKnownType2 = PBEType.None;\n\t\t\tt |= PBERoostTypes.KnownType2;\n\t\t}\n\t\tRoostTypes = t;\n\t}\n\tpublic void EndRoost()\n\t{\n\t\tPBERoostTypes t = RoostTypes;\n\t\tif (t.HasFlag(PBERoostTypes.Type1))\n\t\t{\n\t\t\tType1 = PBEType.Flying;\n\t\t}\n\t\tif (t.HasFlag(PBERoostTypes.Type2))\n\t\t{\n\t\t\tType2 = PBEType.Flying;\n\t\t}\n\t\tif (t.HasFlag(PBERoostTypes.KnownType1))\n\t\t{\n\t\t\tKnownType1 = PBEType.Flying;\n\t\t}\n\t\tif (t.HasFlag(PBERoostTypes.KnownType2))\n\t\t{\n\t\t\tKnownType2 = PBEType.Flying;\n\t\t}\n\t\tRoostTypes = PBERoostTypes.None;\n\t}\n\tpublic bool HasCancellingAbility(bool useKnownInfo = false)\n\t{\n\t\tPBEAbility kAbility = useKnownInfo ? KnownAbility : Ability;\n\t\treturn kAbility == PBEAbility.MoldBreaker || kAbility == PBEAbility.Teravolt || kAbility == PBEAbility.Turboblaze;\n\t}\n\tpublic PBEStat[] GetChangedStats()\n\t{\n\t\tvar list = new List<PBEStat>(7);\n\t\tif (AttackChange != 0)\n\t\t{\n\t\t\tlist.Add(PBEStat.Attack);\n\t\t}\n\t\tif (DefenseChange != 0)\n\t\t{\n\t\t\tlist.Add(PBEStat.Defense);\n\t\t}\n\t\tif (SpAttackChange != 0)\n\t\t{\n\t\t\tlist.Add(PBEStat.SpAttack);\n\t\t}\n\t\tif (SpDefenseChange != 0)\n\t\t{\n\t\t\tlist.Add(PBEStat.SpDefense);\n\t\t}\n\t\tif (SpeedChange != 0)\n\t\t{\n\t\t\tlist.Add(PBEStat.Speed);\n\t\t}\n\t\tif (AccuracyChange != 0)\n\t\t{\n\t\t\tlist.Add(PBEStat.Accuracy);\n\t\t}\n\t\tif (EvasionChange != 0)\n\t\t{\n\t\t\tlist.Add(PBEStat.Evasion);\n\t\t}\n\t\treturn list.ToArray();\n\t}\n\tpublic PBEStat[] GetStatsLessThan(int i)\n\t{\n\t\tvar list = new List<PBEStat>(7);\n\t\tif (AttackChange < i)\n\t\t{\n\t\t\tlist.Add(PBEStat.Attack);\n\t\t}\n\t\tif (DefenseChange < i)\n\t\t{\n\t\t\tlist.Add(PBEStat.Defense);\n\t\t}\n\t\tif (SpAttackChange < i)\n\t\t{\n\t\t\tlist.Add(PBEStat.SpAttack);\n\t\t}\n\t\tif (SpDefenseChange < i)\n\t\t{\n\t\t\tlist.Add(PBEStat.SpDefense);\n\t\t}\n\t\tif (SpeedChange < i)\n\t\t{\n\t\t\tlist.Add(PBEStat.Speed);\n\t\t}\n\t\tif (AccuracyChange < i)\n\t\t{\n\t\t\tlist.Add(PBEStat.Accuracy);\n\t\t}\n\t\tif (EvasionChange < i)\n\t\t{\n\t\t\tlist.Add(PBEStat.Evasion);\n\t\t}\n\t\treturn list.ToArray();\n\t}\n\tpublic PBEStat[] GetStatsGreaterThan(int i)\n\t{\n\t\tvar list = new List<PBEStat>(7);\n\t\tif (AttackChange > i)\n\t\t{\n\t\t\tlist.Add(PBEStat.Attack);\n\t\t}\n\t\tif (DefenseChange > i)\n\t\t{\n\t\t\tlist.Add(PBEStat.Defense);\n\t\t}\n\t\tif (SpAttackChange > i)\n\t\t{\n\t\t\tlist.Add(PBEStat.SpAttack);\n\t\t}\n\t\tif (SpDefenseChange > i)\n\t\t{\n\t\t\tlist.Add(PBEStat.SpDefense);\n\t\t}\n\t\tif (SpeedChange > i)\n\t\t{\n\t\t\tlist.Add(PBEStat.Speed);\n\t\t}\n\t\tif (AccuracyChange > i)\n\t\t{\n\t\t\tlist.Add(PBEStat.Accuracy);\n\t\t}\n\t\tif (EvasionChange > i)\n\t\t{\n\t\t\tlist.Add(PBEStat.Evasion);\n\t\t}\n\t\treturn list.ToArray();\n\t}\n\tpublic sbyte GetStatChange(PBEStat stat)\n\t{\n\t\tswitch (stat)\n\t\t{\n\t\t\tcase PBEStat.Attack: return AttackChange;\n\t\t\tcase PBEStat.Defense: return DefenseChange;\n\t\t\tcase PBEStat.SpAttack: return SpAttackChange;\n\t\t\tcase PBEStat.SpDefense: return SpDefenseChange;\n\t\t\tcase PBEStat.Speed: return SpeedChange;\n\t\t\tcase PBEStat.Accuracy: return AccuracyChange;\n\t\t\tcase PBEStat.Evasion: return EvasionChange;\n\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(stat));\n\t\t}\n\t}\n\tpublic sbyte SetStatChange(PBEStat stat, int value)\n\t{\n\t\tsbyte maxStatChange = Battle.Settings.MaxStatChange;\n\t\tsbyte val = (sbyte)Math.Clamp(value, -maxStatChange, maxStatChange);\n\t\tswitch (stat)\n\t\t{\n\t\t\tcase PBEStat.Accuracy: AccuracyChange = val; break;\n\t\t\tcase PBEStat.Attack: AttackChange = val; break;\n\t\t\tcase PBEStat.Defense: DefenseChange = val; break;\n\t\t\tcase PBEStat.Evasion: EvasionChange = val; break;\n\t\t\tcase PBEStat.SpAttack: SpAttackChange = val; break;\n\t\t\tcase PBEStat.SpDefense: SpDefenseChange = val; break;\n\t\t\tcase PBEStat.Speed: SpeedChange = val; break;\n\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(stat));\n\t\t}\n\t\treturn val;\n\t}\n\tpublic PBEResult IsStatChangePossible(PBEStat stat, PBEBattlePokemon causer, int change, out sbyte oldValue, out sbyte newValue, bool useKnownInfo = false, bool ignoreSubstitute = false)\n\t{\n\t\toldValue = GetStatChange(stat);\n\n\t\tif (causer != this && !ignoreSubstitute && (useKnownInfo ? KnownStatus2 : Status2).HasFlag(PBEStatus2.Substitute))\n\t\t{\n\t\t\tnewValue = oldValue;\n\t\t\treturn PBEResult.Ineffective_Substitute;\n\t\t}\n\n\t\t// These abilities do not activate when the Pokémon changes its own stat\n\t\tif (causer != this && !causer.HasCancellingAbility())\n\t\t{\n\t\t\tswitch (useKnownInfo ? KnownAbility : Ability)\n\t\t\t{\n\t\t\t\tcase PBEAbility.BigPecks:\n\t\t\t\t{\n\t\t\t\t\tif (change < 0 && stat == PBEStat.Defense)\n\t\t\t\t\t{\n\t\t\t\t\t\tnewValue = oldValue;\n\t\t\t\t\t\treturn PBEResult.Ineffective_Ability;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEAbility.ClearBody:\n\t\t\t\tcase PBEAbility.WhiteSmoke:\n\t\t\t\t{\n\t\t\t\t\tif (change < 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tnewValue = oldValue;\n\t\t\t\t\t\treturn PBEResult.Ineffective_Ability;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEAbility.HyperCutter:\n\t\t\t\t{\n\t\t\t\t\tif (change < 0 && stat == PBEStat.Attack)\n\t\t\t\t\t{\n\t\t\t\t\t\tnewValue = oldValue;\n\t\t\t\t\t\treturn PBEResult.Ineffective_Ability;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEAbility.KeenEye:\n\t\t\t\t{\n\t\t\t\t\tif (change < 0 && stat == PBEStat.Accuracy)\n\t\t\t\t\t{\n\t\t\t\t\t\tnewValue = oldValue;\n\t\t\t\t\t\treturn PBEResult.Ineffective_Ability;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Verified: Contrary/Simple are silent\n\t\t// These abilities activate when the Pokémon changes its own stat\n\t\tif (causer == this || !causer.HasCancellingAbility())\n\t\t{\n\t\t\tswitch (useKnownInfo ? KnownAbility : Ability)\n\t\t\t{\n\t\t\t\tcase PBEAbility.Contrary: change *= -1; break;\n\t\t\t\tcase PBEAbility.Simple: change *= 2; break;\n\t\t\t}\n\t\t}\n\n\t\tsbyte maxStatChange = Battle.Settings.MaxStatChange;\n\t\tnewValue = (sbyte)Math.Clamp(oldValue + change, -maxStatChange, maxStatChange);\n\n\t\tif (oldValue == newValue)\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Stat;\n\t\t}\n\t\treturn PBEResult.Success;\n\t}\n\tpublic void ClearStatChanges()\n\t{\n\t\tAttackChange = 0;\n\t\tDefenseChange = 0;\n\t\tSpAttackChange = 0;\n\t\tSpDefenseChange = 0;\n\t\tSpeedChange = 0;\n\t\tAccuracyChange = 0;\n\t\tEvasionChange = 0;\n\t}\n\t/// <summary>For use with <see cref=\"PBEMoveEffect.Punishment\"/> and <see cref=\"PBEMoveEffect.StoredPower\"/>.</summary>\n\tpublic int GetPositiveStatTotal()\n\t{\n\t\treturn GetStatsGreaterThan(0).Sum(s => GetStatChange(s));\n\t}\n\tpublic PBEType GetMoveType(IPBEMoveData mData, bool useKnownInfo = false)\n\t{\n\t\tif (!mData.IsMoveUsable())\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(mData));\n\t\t}\n\t\tswitch (mData.Effect)\n\t\t{\n\t\t\tcase PBEMoveEffect.HiddenPower:\n\t\t\t{\n\t\t\t\tif (!useKnownInfo)\n\t\t\t\t{\n\t\t\t\t\treturn IndividualValues!.GetHiddenPowerType();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.Judgment:\n\t\t\t{\n\t\t\t\tswitch (useKnownInfo ? KnownItem : Item)\n\t\t\t\t{\n\t\t\t\t\tcase PBEItem.DracoPlate: return PBEType.Dragon;\n\t\t\t\t\tcase PBEItem.DreadPlate: return PBEType.Dark;\n\t\t\t\t\tcase PBEItem.EarthPlate: return PBEType.Ground;\n\t\t\t\t\tcase PBEItem.FistPlate: return PBEType.Fighting;\n\t\t\t\t\tcase PBEItem.FlamePlate: return PBEType.Fire;\n\t\t\t\t\tcase PBEItem.IciclePlate: return PBEType.Ice;\n\t\t\t\t\tcase PBEItem.InsectPlate: return PBEType.Bug;\n\t\t\t\t\tcase PBEItem.IronPlate: return PBEType.Steel;\n\t\t\t\t\tcase PBEItem.MeadowPlate: return PBEType.Grass;\n\t\t\t\t\tcase PBEItem.MindPlate: return PBEType.Psychic;\n\t\t\t\t\tcase PBEItem.SkyPlate: return PBEType.Flying;\n\t\t\t\t\tcase PBEItem.SplashPlate: return PBEType.Water;\n\t\t\t\t\tcase PBEItem.SpookyPlate: return PBEType.Ghost;\n\t\t\t\t\tcase PBEItem.StonePlate: return PBEType.Rock;\n\t\t\t\t\tcase PBEItem.ToxicPlate: return PBEType.Poison;\n\t\t\t\t\tcase PBEItem.ZapPlate: return PBEType.Electric;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.Struggle:\n\t\t\t{\n\t\t\t\treturn PBEType.None;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.TechnoBlast:\n\t\t\t{\n\t\t\t\tswitch (useKnownInfo ? KnownItem : Item)\n\t\t\t\t{\n\t\t\t\t\tcase PBEItem.BurnDrive: return PBEType.Fire;\n\t\t\t\t\tcase PBEItem.ChillDrive: return PBEType.Ice;\n\t\t\t\t\tcase PBEItem.DouseDrive: return PBEType.Water;\n\t\t\t\t\tcase PBEItem.ShockDrive: return PBEType.Electric;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.WeatherBall:\n\t\t\t{\n\t\t\t\tif (Battle.ShouldDoWeatherEffects())\n\t\t\t\t{\n\t\t\t\t\tswitch (Battle.Weather)\n\t\t\t\t\t{\n\t\t\t\t\t\tcase PBEWeather.Hailstorm: return PBEType.Ice;\n\t\t\t\t\t\tcase PBEWeather.HarshSunlight: return PBEType.Fire;\n\t\t\t\t\t\tcase PBEWeather.Rain: return PBEType.Water;\n\t\t\t\t\t\tcase PBEWeather.Sandstorm: return PBEType.Rock;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif ((useKnownInfo ? KnownAbility : Ability) == PBEAbility.Normalize)\n\t\t{\n\t\t\treturn PBEType.Normal;\n\t\t}\n\t\treturn mData.Type;\n\t}\n\tpublic PBEType GetMoveType(PBEMove move, bool useKnownInfo = false)\n\t{\n\t\tif (move == PBEMove.None || move >= PBEMove.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(move));\n\t\t}\n\t\treturn GetMoveType(PBEDataProvider.Instance.GetMoveData(move), useKnownInfo: useKnownInfo);\n\t}\n\tpublic PBEMoveTarget GetMoveTargets(IPBEMoveData mData)\n\t{\n\t\tif (!mData.IsMoveUsable())\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(mData));\n\t\t}\n\t\tif (mData.Effect == PBEMoveEffect.Curse)\n\t\t{\n\t\t\tif (this.HasType(PBEType.Ghost))\n\t\t\t{\n\t\t\t\treturn PBEMoveTarget.SingleSurrounding;\n\t\t\t}\n\t\t\treturn PBEMoveTarget.Self;\n\t\t}\n\t\treturn mData.Targets;\n\t}\n\t/// <summary>Gets the possible targets that a move can target when used by this Pokémon.</summary>\n\t/// <param name=\"move\">The move to check.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Thrown when <paramref name=\"move\"/> is invalid.</exception>\n\tpublic PBEMoveTarget GetMoveTargets(PBEMove move)\n\t{\n\t\tif (move == PBEMove.None || move >= PBEMove.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(move));\n\t\t}\n\t\treturn GetMoveTargets(PBEDataProvider.Instance.GetMoveData(move));\n\t}\n\t/// <summary>Returns True if the Pokémon is only able to use <see cref=\"PBEMove.Struggle\"/>.</summary>\n\tpublic bool IsForcedToStruggle()\n\t{\n\t\tif (TempLockedMove != PBEMove.None) // Temp locked moves deduct PP on the first turn and don't on the second, so having a temp locked move means it is supposed to be used again for the second turn\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\telse if ((ChoiceLockedMove != PBEMove.None && Moves[ChoiceLockedMove]!.PP == 0) // If the choice locked move has 0 pp, it is forced to struggle\n\t\t\t|| Moves.All(s => s.PP == 0) // If all moves have 0 pp, then the user is forced to struggle\n\t\t\t)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\t/// <summary>Returns True if the Pokémon can switch out. Does not check if the Pokémon is on the field or if there are available Pokémon to switch into.</summary>\n\tpublic bool CanSwitchOut()\n\t{\n\t\treturn TempLockedMove == PBEMove.None;\n\t}\n\tpublic bool CanHitThroughSafeguard()\n\t{\n\t\treturn Ability == PBEAbility.Infiltrator;\n\t}\n\tpublic PBEResult IsAttractionPossible(PBEBattlePokemon causer, bool useKnownInfo = false, bool ignoreCurrentStatus = false)\n\t{\n\t\tif (causer == this)\n\t\t{\n\t\t\treturn PBEResult.InvalidConditions;\n\t\t}\n\t\tPBEStatus2 kStatus2 = useKnownInfo ? KnownStatus2 : Status2;\n\t\tif (!ignoreCurrentStatus && kStatus2.HasFlag(PBEStatus2.Infatuated))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Status;\n\t\t}\n\t\tPBEGender kGender = useKnownInfo ? KnownGender : Gender;\n\t\tif (!kGender.IsOppositeGender(causer.Gender))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Gender;\n\t\t}\n\t\tPBEAbility kAbility = useKnownInfo ? KnownAbility : Ability;\n\t\tif (!causer.HasCancellingAbility() && kAbility == PBEAbility.Oblivious)\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Ability;\n\t\t}\n\t\treturn PBEResult.Success;\n\t}\n\tpublic PBEResult IsBurnPossible(PBEBattlePokemon? other, bool useKnownInfo = false, bool ignoreSubstitute = false, bool ignoreCurrentStatus = false, bool ignoreSafeguard = false)\n\t{\n\t\tPBEStatus2 kStatus2 = useKnownInfo ? KnownStatus2 : Status2;\n\t\tif (!ignoreSubstitute && kStatus2.HasFlag(PBEStatus2.Substitute))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Substitute;\n\t\t}\n\t\tif (!ignoreCurrentStatus && Status1 != PBEStatus1.None)\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Status;\n\t\t}\n\t\tif (!ignoreSafeguard && other?.CanHitThroughSafeguard() == true && Team.TeamStatus.HasFlag(PBETeamStatus.Safeguard))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Safeguard;\n\t\t}\n\t\tif (this.HasType(PBEType.Fire, useKnownInfo))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Type;\n\t\t}\n\t\tPBEAbility kAbility = useKnownInfo ? KnownAbility : Ability;\n\t\tif (other?.HasCancellingAbility() != true && (kAbility == PBEAbility.WaterVeil || (kAbility == PBEAbility.LeafGuard && Battle.WillLeafGuardActivate())))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Ability;\n\t\t}\n\t\treturn PBEResult.Success;\n\t}\n\tpublic PBEResult IsConfusionPossible(PBEBattlePokemon? other, bool useKnownInfo = false, bool ignoreSubstitute = false, bool ignoreCurrentStatus = false, bool ignoreSafeguard = false)\n\t{\n\t\tPBEStatus2 kStatus2 = useKnownInfo ? KnownStatus2 : Status2;\n\t\tif (!ignoreSubstitute && kStatus2.HasFlag(PBEStatus2.Substitute))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Substitute;\n\t\t}\n\t\tif (!ignoreCurrentStatus && kStatus2.HasFlag(PBEStatus2.Confused))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Status;\n\t\t}\n\t\tif (!ignoreSafeguard && other?.CanHitThroughSafeguard() == true && Team.TeamStatus.HasFlag(PBETeamStatus.Safeguard))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Safeguard;\n\t\t}\n\t\tPBEAbility kAbility = useKnownInfo ? KnownAbility : Ability;\n\t\tif (other?.HasCancellingAbility() != true && kAbility == PBEAbility.OwnTempo)\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Ability;\n\t\t}\n\t\treturn PBEResult.Success;\n\t}\n\tpublic PBEResult IsFreezePossible(PBEBattlePokemon? other, bool useKnownInfo = false, bool ignoreSubstitute = false, bool ignoreCurrentStatus = false, bool ignoreSafeguard = false)\n\t{\n\t\tPBEStatus2 kStatus2 = useKnownInfo ? KnownStatus2 : Status2;\n\t\tif (!ignoreSubstitute && kStatus2.HasFlag(PBEStatus2.Substitute))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Substitute;\n\t\t}\n\t\tif (!ignoreCurrentStatus && Status1 != PBEStatus1.None)\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Status;\n\t\t}\n\t\tif (!ignoreSafeguard && other?.CanHitThroughSafeguard() == true && Team.TeamStatus.HasFlag(PBETeamStatus.Safeguard))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Safeguard;\n\t\t}\n\t\tif (this.HasType(PBEType.Ice, useKnownInfo))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Type;\n\t\t}\n\t\tPBEAbility kAbility = useKnownInfo ? KnownAbility : Ability;\n\t\tif (other?.HasCancellingAbility() != true && (kAbility == PBEAbility.MagmaArmor || (kAbility == PBEAbility.LeafGuard && Battle.WillLeafGuardActivate())))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Ability;\n\t\t}\n\t\treturn PBEResult.Success;\n\t}\n\tpublic PBEResult IsFlinchPossible(PBEBattlePokemon? other, bool useKnownInfo = false)\n\t{\n\t\tPBEStatus2 kStatus2 = useKnownInfo ? KnownStatus2 : Status2;\n\t\tif (kStatus2.HasFlag(PBEStatus2.Substitute))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Substitute;\n\t\t}\n\t\tif (kStatus2.HasFlag(PBEStatus2.Flinching))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Status;\n\t\t}\n\t\tPBEAbility kAbility = useKnownInfo ? KnownAbility : Ability;\n\t\tif (other?.HasCancellingAbility() != true && kAbility == PBEAbility.InnerFocus)\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Ability;\n\t\t}\n\t\treturn PBEResult.Success;\n\t}\n\tpublic PBEResult IsGrounded(PBEBattlePokemon? other, bool useKnownInfo = false)\n\t{\n\t\tif (this.HasType(PBEType.Flying, useKnownInfo))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Type;\n\t\t}\n\t\tPBEAbility kAbility = useKnownInfo ? KnownAbility : Ability;\n\t\tif (other?.HasCancellingAbility() != true && kAbility == PBEAbility.Levitate)\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Ability;\n\t\t}\n\t\tPBEStatus2 kStatus2 = useKnownInfo ? KnownStatus2 : Status2;\n\t\tif (kStatus2.HasFlag(PBEStatus2.MagnetRise))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_MagnetRise;\n\t\t}\n\t\treturn PBEResult.Success;\n\t}\n\tpublic PBEResult IsLeechSeedPossible(bool useKnownInfo = false)\n\t{\n\t\tPBEStatus2 kStatus2 = useKnownInfo ? KnownStatus2 : Status2;\n\t\tif (kStatus2.HasFlag(PBEStatus2.Substitute))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Substitute;\n\t\t}\n\t\tif (kStatus2.HasFlag(PBEStatus2.LeechSeed))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Status;\n\t\t}\n\t\tif (this.HasType(PBEType.Grass, useKnownInfo))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Type;\n\t\t}\n\t\treturn PBEResult.Success;\n\t}\n\tpublic PBEResult IsMagnetRisePossible(bool useKnownInfo = false)\n\t{\n\t\tPBEStatus2 kStatus2 = useKnownInfo ? KnownStatus2 : Status2;\n\t\tif (kStatus2.HasFlag(PBEStatus2.MagnetRise))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Status;\n\t\t}\n\t\treturn PBEResult.Success;\n\t}\n\tpublic PBEResult IsParalysisPossible(PBEBattlePokemon? other, bool useKnownInfo = false, bool ignoreSubstitute = false, bool ignoreCurrentStatus = false, bool ignoreSafeguard = false)\n\t{\n\t\tPBEStatus2 kStatus2 = useKnownInfo ? KnownStatus2 : Status2;\n\t\tif (!ignoreSubstitute && kStatus2.HasFlag(PBEStatus2.Substitute))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Substitute;\n\t\t}\n\t\tif (!ignoreCurrentStatus && Status1 != PBEStatus1.None)\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Status;\n\t\t}\n\t\tif (!ignoreSafeguard && other?.CanHitThroughSafeguard() == true && Team.TeamStatus.HasFlag(PBETeamStatus.Safeguard))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Safeguard;\n\t\t}\n\t\tPBEAbility kAbility = useKnownInfo ? KnownAbility : Ability;\n\t\tif (other?.HasCancellingAbility() != true && (kAbility == PBEAbility.Limber || (kAbility == PBEAbility.LeafGuard && Battle.WillLeafGuardActivate())))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Ability;\n\t\t}\n\t\treturn PBEResult.Success;\n\t}\n\tpublic PBEResult IsPoisonPossible(PBEBattlePokemon? other, bool useKnownInfo = false, bool ignoreSubstitute = false, bool ignoreCurrentStatus = false, bool ignoreSafeguard = false)\n\t{\n\t\tPBEStatus2 kStatus2 = useKnownInfo ? KnownStatus2 : Status2;\n\t\tif (!ignoreSubstitute && kStatus2.HasFlag(PBEStatus2.Substitute))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Substitute;\n\t\t}\n\t\tif (!ignoreCurrentStatus && Status1 != PBEStatus1.None)\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Status;\n\t\t}\n\t\tif (!ignoreSafeguard && other?.CanHitThroughSafeguard() == true && Team.TeamStatus.HasFlag(PBETeamStatus.Safeguard))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Safeguard;\n\t\t}\n\t\tif (this.HasType(PBEType.Poison, useKnownInfo) || this.HasType(PBEType.Steel, useKnownInfo))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Type;\n\t\t}\n\t\tPBEAbility kAbility = useKnownInfo ? KnownAbility : Ability;\n\t\tif (other?.HasCancellingAbility() != true && (kAbility == PBEAbility.Immunity || (kAbility == PBEAbility.LeafGuard && Battle.WillLeafGuardActivate())))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Ability;\n\t\t}\n\t\treturn PBEResult.Success;\n\t}\n\tpublic PBEResult IsSleepPossible(PBEBattlePokemon? other, bool useKnownInfo = false, bool ignoreSubstitute = false, bool ignoreCurrentStatus = false, bool ignoreSafeguard = false)\n\t{\n\t\tPBEStatus2 kStatus2 = useKnownInfo ? KnownStatus2 : Status2;\n\t\tif (!ignoreSubstitute && kStatus2.HasFlag(PBEStatus2.Substitute))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Substitute;\n\t\t}\n\t\tif (!ignoreCurrentStatus && Status1 != PBEStatus1.None)\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Status;\n\t\t}\n\t\tif (!ignoreSafeguard && other?.CanHitThroughSafeguard() == true && Team.TeamStatus.HasFlag(PBETeamStatus.Safeguard))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Safeguard;\n\t\t}\n\t\tPBEAbility kAbility = useKnownInfo ? KnownAbility : Ability;\n\t\tif (other?.HasCancellingAbility() != true && (kAbility == PBEAbility.Insomnia || (kAbility == PBEAbility.LeafGuard && Battle.WillLeafGuardActivate()) || kAbility == PBEAbility.VitalSpirit))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Ability;\n\t\t}\n\t\treturn PBEResult.Success;\n\t}\n\tpublic PBEResult IsSubstitutePossible(bool useKnownInfo = false, bool ignoreCurrentStatus = false)\n\t{\n\t\tPBEStatus2 kStatus2 = useKnownInfo ? KnownStatus2 : Status2;\n\t\tif (!ignoreCurrentStatus && kStatus2.HasFlag(PBEStatus2.Substitute))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Status;\n\t\t}\n\t\tint hpRequired = MaxHP / 4;\n\t\tif (hpRequired < 1 || HP <= hpRequired)\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Stat;\n\t\t}\n\t\treturn PBEResult.Success;\n\t}\n\tpublic PBEResult IsTransformPossible(PBEBattlePokemon user, bool useKnownInfo = false)\n\t{\n\t\tif (user.Status2.HasFlag(PBEStatus2.Transformed))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Status;\n\t\t}\n\t\tPBEStatus2 kStatus2 = useKnownInfo ? KnownStatus2 : Status2;\n\t\tif (kStatus2.HasFlag(PBEStatus2.Substitute))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Substitute;\n\t\t}\n\t\tif (kStatus2.HasFlag(PBEStatus2.Disguised) || kStatus2.HasFlag(PBEStatus2.Transformed))\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Status;\n\t\t}\n\t\treturn PBEResult.Success;\n\t}\n\t/// <summary>Returns an array of moves the Pokémon can use.</summary>\n\tpublic PBEMove[] GetUsableMoves()\n\t{\n\t\tif (IsForcedToStruggle())\n\t\t{\n\t\t\treturn new PBEMove[1] { PBEMove.Struggle };\n\t\t}\n\t\telse if (TempLockedMove != PBEMove.None)\n\t\t{\n\t\t\treturn new PBEMove[1] { TempLockedMove };\n\t\t}\n\t\telse if (ChoiceLockedMove != PBEMove.None)\n\t\t{\n\t\t\treturn new PBEMove[1] { ChoiceLockedMove }; // IsForcedToStruggle() being false means the choice locked move still has PP\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint numMoves = Moves.Count;\n\t\t\tvar usableMoves = new List<PBEMove>(numMoves);\n\t\t\tfor (int i = 0; i < numMoves; i++)\n\t\t\t{\n\t\t\t\tPBEBattleMoveset.PBEBattleMovesetSlot slot = Moves[i];\n\t\t\t\tif (slot.PP > 0)\n\t\t\t\t{\n\t\t\t\t\tusableMoves.Add(slot.Move);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn usableMoves.ToArray();\n\t\t}\n\t}\n\t/// <summary>Gets the chance of a protection move succeeding (based on <see cref=\"Protection_Counter\"/>) out of <see cref=\"ushort.MaxValue\"/>.</summary>\n\tpublic ushort GetProtectionChance()\n\t{\n\t\tint count = Protection_Counter;\n\t\treturn count == 0 ? ushort.MaxValue : (ushort)(ushort.MaxValue / (count * 2));\n\t}\n\tpublic PBEBattlePokemon? GetPkmnWouldDisguiseAs()\n\t{\n\t\tPBEList<PBEBattlePokemon> party = Trainer.Party;\n\t\tfor (int i = party.Count - 1; i >= 0; i--)\n\t\t{\n\t\t\tPBEBattlePokemon p = party[i];\n\t\t\t// Does not copy eggs\n\t\t\tif (p.CanBattle)\n\t\t\t{\n\t\t\t\t// If this Pokémon is the \"last\" conscious one, it will go out as itself (loop breaks)\n\t\t\t\t// The only way to disguise as a Pokémon that's on the battlefield is the first turn of a Double/Triple/Rotation battle\n\t\t\t\tif (p.OriginalSpecies != OriginalSpecies)\n\t\t\t\t{\n\t\t\t\t\treturn p;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/// <summary>Will only be accurate for the host</summary>\n\tpublic override string ToString()\n\t{\n\t\tvar sb = new StringBuilder();\n\t\tstring formStr = PBEDataUtils.HasForms(Species, false) ? $\" ({PBEDataProvider.Instance.GetFormName(this).English})\" : string.Empty;\n\t\tsb.AppendLine($\"{Nickname}/{PBEDataProvider.Instance.GetSpeciesName(Species).English}{formStr} {Gender.ToSymbol()} Lv.{Level}\");\n\t\tsb.AppendLine($\"HP: {HP}/{MaxHP} ({HPPercentage:P2})\");\n\t\tsb.Append($\"Types: {PBEDataProvider.Instance.GetTypeName(Type1).English}\");\n\t\tif (Type2 != PBEType.None)\n\t\t{\n\t\t\tsb.Append($\"/{PBEDataProvider.Instance.GetTypeName(Type2).English}\");\n\t\t}\n\t\tsb.AppendLine();\n\t\tsb.Append($\"Known types: {PBEDataProvider.Instance.GetTypeName(KnownType1).English}\");\n\t\tif (KnownType2 != PBEType.None)\n\t\t{\n\t\t\tsb.Append($\"/{PBEDataProvider.Instance.GetTypeName(KnownType2).English}\");\n\t\t}\n\t\tsb.AppendLine();\n\t\tsb.AppendLine($\"Position: {Team.CombinedName}'s {FieldPosition}\");\n\t\tsb.AppendLine($\"Status1: {Status1}\");\n\t\tif (Status1 == PBEStatus1.Asleep)\n\t\t{\n\t\t\tsb.AppendLine($\"Asleep turns: {Status1Counter}/{SleepTurns}\");\n\t\t}\n\t\telse if (Status1 == PBEStatus1.BadlyPoisoned)\n\t\t{\n\t\t\tsb.AppendLine($\"Toxic counter: {Status1Counter}\");\n\t\t}\n\t\tsb.AppendLine($\"Status2: {Status2}\");\n\t\tif (Status2.HasFlag(PBEStatus2.Confused))\n\t\t{\n\t\t\tsb.AppendLine($\"Confusion turns: {ConfusionCounter}/{ConfusionTurns}\");\n\t\t}\n\t\tif (Status2.HasFlag(PBEStatus2.Disguised))\n\t\t{\n\t\t\tformStr = PBEDataUtils.HasForms(KnownSpecies, false) ? $\" ({PBEDataProvider.Instance.GetFormName(KnownSpecies, KnownForm).English})\" : string.Empty;\n\t\t\tsb.AppendLine($\"Disguised as: {KnownNickname}/{PBEDataProvider.Instance.GetSpeciesName(KnownSpecies).English}{formStr} {KnownGender.ToSymbol()}\");\n\t\t}\n\t\tif (Battle.BattleFormat != PBEBattleFormat.Single)\n\t\t{\n\t\t\tif (Status2.HasFlag(PBEStatus2.Infatuated))\n\t\t\t{\n\t\t\t\tsb.AppendLine($\"Infatuated with: {InfatuatedWithPokemon!.Trainer.Name}'s {InfatuatedWithPokemon.Nickname}\");\n\t\t\t}\n\t\t\tif (Status2.HasFlag(PBEStatus2.LeechSeed))\n\t\t\t{\n\t\t\t\tsb.AppendLine($\"Seeded position: {SeededTeam!.CombinedName}'s {SeededPosition}\");\n\t\t\t}\n\t\t\tif (Status2.HasFlag(PBEStatus2.LockOn))\n\t\t\t{\n\t\t\t\tsb.AppendLine($\"Taking aim at: {LockOnPokemon!.Trainer.Name}'s {LockOnPokemon.Nickname}\");\n\t\t\t}\n\t\t}\n\t\tif (Status2.HasFlag(PBEStatus2.Substitute))\n\t\t{\n\t\t\tsb.AppendLine($\"Substitute HP: {SubstituteHP}\");\n\t\t}\n\t\tsb.AppendLine($\"Stats: [A] {Attack}, [D] {Defense}, [SA] {SpAttack}, [SD] {SpDefense}, [S] {Speed}, [W] {Weight:0.0}\");\n\t\tPBEStat[] statChanges = GetChangedStats();\n\t\tif (statChanges.Length > 0)\n\t\t{\n\t\t\tvar statStrs = new List<string>(7);\n\t\t\tif (Array.IndexOf(statChanges, PBEStat.Attack) != -1)\n\t\t\t{\n\t\t\t\tstatStrs.Add($\"[A] x{PBEBattle.GetStatChangeModifier(AttackChange, false):0.00}\");\n\t\t\t}\n\t\t\tif (Array.IndexOf(statChanges, PBEStat.Defense) != -1)\n\t\t\t{\n\t\t\t\tstatStrs.Add($\"[D] x{PBEBattle.GetStatChangeModifier(DefenseChange, false):0.00}\");\n\t\t\t}\n\t\t\tif (Array.IndexOf(statChanges, PBEStat.SpAttack) != -1)\n\t\t\t{\n\t\t\t\tstatStrs.Add($\"[SA] x{PBEBattle.GetStatChangeModifier(SpAttackChange, false):0.00}\");\n\t\t\t}\n\t\t\tif (Array.IndexOf(statChanges, PBEStat.SpDefense) != -1)\n\t\t\t{\n\t\t\t\tstatStrs.Add($\"[SD] x{PBEBattle.GetStatChangeModifier(SpDefenseChange, false):0.00}\");\n\t\t\t}\n\t\t\tif (Array.IndexOf(statChanges, PBEStat.Speed) != -1)\n\t\t\t{\n\t\t\t\tstatStrs.Add($\"[S] x{PBEBattle.GetStatChangeModifier(SpeedChange, false):0.00}\");\n\t\t\t}\n\t\t\tif (Array.IndexOf(statChanges, PBEStat.Accuracy) != -1)\n\t\t\t{\n\t\t\t\tstatStrs.Add($\"[AC] x{PBEBattle.GetStatChangeModifier(AccuracyChange, true):0.00}\");\n\t\t\t}\n\t\t\tif (Array.IndexOf(statChanges, PBEStat.Evasion) != -1)\n\t\t\t{\n\t\t\t\tstatStrs.Add($\"[E] x{PBEBattle.GetStatChangeModifier(EvasionChange, true):0.00}\");\n\t\t\t}\n\t\t\tsb.AppendLine($\"Stat changes: {string.Join(\", \", statStrs)}\");\n\t\t}\n\t\tsb.AppendLine($\"Ability: {PBEDataProvider.Instance.GetAbilityName(Ability).English}\");\n\t\tsb.AppendLine($\"Known ability: {(KnownAbility == PBEAbility.MAX ? \"???\" : PBEDataProvider.Instance.GetAbilityName(KnownAbility).English)}\");\n\t\tsb.AppendLine($\"Item: {PBEDataProvider.Instance.GetItemName(Item).English}\");\n\t\tsb.AppendLine($\"Known item: {(KnownItem == (PBEItem)ushort.MaxValue ? \"???\" : PBEDataProvider.Instance.GetItemName(KnownItem).English)}\");\n\t\tif (Moves.Contains(PBEMoveEffect.Frustration) || Moves.Contains(PBEMoveEffect.Return))\n\t\t{\n\t\t\tsb.AppendLine($\"Friendship: {Friendship} ({Friendship / byte.MaxValue:P2})\");\n\t\t}\n\t\tif (Moves.Contains(PBEMoveEffect.HiddenPower))\n\t\t{\n\t\t\tPBEReadOnlyStatCollection ivs = IndividualValues!;\n\t\t\tsb.AppendLine($\"{PBEDataProvider.Instance.GetMoveName(PBEMove.HiddenPower).English}: {PBEDataProvider.Instance.GetTypeName(ivs.GetHiddenPowerType()).English}|{ivs.GetHiddenPowerBasePower(Battle.Settings)}\");\n\t\t}\n\t\tsb.Append(\"Moves: \");\n\t\tfor (int i = 0; i < Battle.Settings.NumMoves; i++)\n\t\t{\n\t\t\tPBEBattleMoveset.PBEBattleMovesetSlot slot = Moves[i];\n\t\t\tPBEMove move = slot.Move;\n\t\t\tif (i > 0)\n\t\t\t{\n\t\t\t\tsb.Append(\", \");\n\t\t\t}\n\t\t\tsb.Append(PBEDataProvider.Instance.GetMoveName(slot.Move).English);\n\t\t\tif (move != PBEMove.None)\n\t\t\t{\n\t\t\t\tsb.Append($\" ({slot.PP}/{slot.MaxPP})\");\n\t\t\t}\n\t\t}\n\t\tsb.AppendLine();\n\t\tsb.Append(\"Known moves: \");\n\t\tfor (int i = 0; i < Battle.Settings.NumMoves; i++)\n\t\t{\n\t\t\tPBEBattleMoveset.PBEBattleMovesetSlot slot = KnownMoves[i];\n\t\t\tPBEMove move = slot.Move;\n\t\t\tint pp = slot.PP;\n\t\t\tint maxPP = slot.MaxPP;\n\t\t\tif (i > 0)\n\t\t\t{\n\t\t\t\tsb.Append(\", \");\n\t\t\t}\n\t\t\tsb.Append(move == PBEMove.MAX ? \"???\" : PBEDataProvider.Instance.GetMoveName(move).English);\n\t\t\tif (move != PBEMove.None && move != PBEMove.MAX)\n\t\t\t{\n\t\t\t\tsb.Append($\" ({pp}{(maxPP == 0 ? \")\" : $\"/{maxPP})\")}\");\n\t\t\t}\n\t\t}\n\t\tsb.AppendLine();\n\t\tsb.Append($\"Usable moves: {string.Join(\", \", GetUsableMoves().Select(m => PBEDataProvider.Instance.GetMoveName(m).English))}\");\n\t\treturn sb.ToString();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Battle/BattleReplay.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Packets;\nusing Kermalis.PokemonBattleEngine.Utils;\nusing System;\nusing System.IO;\nusing System.Linq;\nusing System.Security.Cryptography;\n\nnamespace Kermalis.PokemonBattleEngine.Battle;\n\npublic sealed partial class PBEBattle\n{\n\tprivate const ushort CUR_REPLAY_VERSION = 0;\n\n\tpublic string GetDefaultReplayFileName()\n\t{\n\t\t// \"2020-12-30 23-59-59 - Team 1 vs Team 2.pbereplay\"\n\t\treturn PBEUtils.ToSafeFileName(new string(string.Format(\"{0:yyyy-MM-dd HH-mm-ss} - {1} vs {2}\", DateTime.Now, Teams[0].CombinedName, Teams[1].CombinedName).Take(200).ToArray())) + \".pbereplay\";\n\t}\n\tprivate void CheckCanSaveReplay()\n\t{\n\t\tif (!IsLocallyHosted)\n\t\t{\n\t\t\tthrow new InvalidOperationException(\"Can only save replays of locally hosted battles\");\n\t\t}\n\t\tif (_battleState != PBEBattleState.Ended)\n\t\t{\n\t\t\tthrow new InvalidOperationException($\"{nameof(BattleState)} must be {PBEBattleState.Ended} to save a replay.\");\n\t\t}\n\t}\n\n\tpublic void SaveReplay()\n\t{\n\t\tCheckCanSaveReplay();\n\t\tSaveReplay(GetDefaultReplayFileName());\n\t}\n\tpublic void SaveReplayToFolder(string path)\n\t{\n\t\tCheckCanSaveReplay();\n\t\tSaveReplay(Path.Combine(path, GetDefaultReplayFileName()));\n\t}\n\tpublic void SaveReplay(string path)\n\t{\n\t\tCheckCanSaveReplay();\n\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tvar w = new EndianBinaryWriter(ms);\n\t\t\tw.WriteUInt16(CUR_REPLAY_VERSION);\n\t\t\tw.WriteInt32(_rand.Seed);\n\n\t\t\tint numEvents = Events.Count;\n\t\t\tw.WriteInt32(numEvents);\n\t\t\tfor (int i = 0; i < numEvents; i++)\n\t\t\t{\n\t\t\t\tbyte[] data = Events[i].Data.ToArray();\n\t\t\t\tw.WriteUInt16((ushort)data.Length);\n\t\t\t\tw.WriteBytes(data);\n\t\t\t}\n\n\t\t\tms.Position = 0;\n\t\t\tw.WriteBytes(MD5.HashData(ms));\n\n\t\t\tFile.WriteAllBytes(path, ms.ToArray());\n\t\t}\n\t}\n\n\tpublic static PBEBattle LoadReplay(string path, PBEPacketProcessor packetProcessor)\n\t{\n\t\tbyte[] fileBytes = File.ReadAllBytes(path);\n\t\tusing (var s = new MemoryStream(fileBytes))\n\t\t{\n\t\t\tvar r = new EndianBinaryReader(s);\n\n\t\t\tbyte[] hash;\n\t\t\thash = MD5.HashData(fileBytes.AsSpan(0, fileBytes.Length - 16));\n\t\t\tfor (int i = 0; i < 16; i++)\n\t\t\t{\n\t\t\t\tif (hash[i] != fileBytes[fileBytes.Length - 16 + i])\n\t\t\t\t{\n\t\t\t\t\tthrow new InvalidDataException();\n\t\t\t\t}\n\t\t\t}\n\t\t\tushort version = r.ReadUInt16(); // Unused for now\n\t\t\tint seed = r.ReadInt32(); // Unused for now\n\t\t\tPBEBattle b = null!; // The first packet should be a PBEBattlePacket\n\t\t\tint numEvents = r.ReadInt32();\n\t\t\tif (numEvents < 1)\n\t\t\t{\n\t\t\t\tthrow new InvalidDataException();\n\t\t\t}\n\t\t\tfor (int i = 0; i < numEvents; i++)\n\t\t\t{\n\t\t\t\tbyte[] data = new byte[r.ReadUInt16()];\n\t\t\t\tr.ReadBytes(data);\n\t\t\t\tIPBEPacket packet = packetProcessor.CreatePacket(data, b);\n\t\t\t\tif (packet is PBEBattlePacket bp)\n\t\t\t\t{\n\t\t\t\t\tif (i != 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tthrow new InvalidDataException();\n\t\t\t\t\t}\n\t\t\t\t\tb = new PBEBattle(bp);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (i == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tthrow new InvalidDataException();\n\t\t\t\t\t}\n\t\t\t\t\tif (packet is PBEWildPkmnAppearedPacket wpap)\n\t\t\t\t\t{\n\t\t\t\t\t\tPBETrainer wildTrainer = b.Teams[1].Trainers[0];\n\t\t\t\t\t\tforeach (PBEPkmnAppearedInfo info in wpap.Pokemon)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tPBEBattlePokemon pkmn = wildTrainer.GetPokemon(info.Pokemon);\n\t\t\t\t\t\t\t// Process disguise and position now\n\t\t\t\t\t\t\tpkmn.FieldPosition = info.FieldPosition;\n\t\t\t\t\t\t\tif (info.IsDisguised)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tpkmn.Status2 |= PBEStatus2.Disguised;\n\t\t\t\t\t\t\t\tpkmn.KnownCaughtBall = info.CaughtBall;\n\t\t\t\t\t\t\t\tpkmn.KnownGender = info.Gender;\n\t\t\t\t\t\t\t\tpkmn.KnownNickname = info.Nickname;\n\t\t\t\t\t\t\t\tpkmn.KnownShiny = info.Shiny;\n\t\t\t\t\t\t\t\tpkmn.KnownSpecies = info.Species;\n\t\t\t\t\t\t\t\tpkmn.KnownForm = info.Form;\n\t\t\t\t\t\t\t\tIPBEPokemonData pData = PBEDataProvider.Instance.GetPokemonData(info);\n\t\t\t\t\t\t\t\tpkmn.KnownType1 = pData.Type1;\n\t\t\t\t\t\t\t\tpkmn.KnownType2 = pData.Type2;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tb.ActiveBattlers.Add(pkmn);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tb.Events.Add(packet);\n\t\t\t\t}\n\t\t\t}\n\t\t\tb.BattleState = PBEBattleState.Ended;\n\t\t\treturn b;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Battle/BattleTargets.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Data.Utils;\nusing Kermalis.PokemonBattleEngine.Utils;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\n\nnamespace Kermalis.PokemonBattleEngine.Battle;\n\npublic sealed partial class PBEBattle\n{\n\t/// <summary>Gets the position across from the inputted position for a specific battle format.</summary>\n\t/// <param name=\"battleFormat\">The battle format.</param>\n\t/// <param name=\"position\">The position.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Thrown when <paramref name=\"battleFormat\"/> is invalid or <paramref name=\"position\"/> is invalid for <paramref name=\"battleFormat\"/>.</exception>\n\tpublic static PBEFieldPosition GetPositionAcross(PBEBattleFormat battleFormat, PBEFieldPosition position)\n\t{\n\t\tswitch (battleFormat)\n\t\t{\n\t\t\tcase PBEBattleFormat.Single:\n\t\t\tcase PBEBattleFormat.Rotation:\n\t\t\t{\n\t\t\t\tif (position == PBEFieldPosition.Center)\n\t\t\t\t{\n\t\t\t\t\treturn PBEFieldPosition.Center;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(position));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcase PBEBattleFormat.Double:\n\t\t\t{\n\t\t\t\tif (position == PBEFieldPosition.Left)\n\t\t\t\t{\n\t\t\t\t\treturn PBEFieldPosition.Right;\n\t\t\t\t}\n\t\t\t\telse if (position == PBEFieldPosition.Right)\n\t\t\t\t{\n\t\t\t\t\treturn PBEFieldPosition.Left;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(position));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcase PBEBattleFormat.Triple:\n\t\t\t{\n\t\t\t\tif (position == PBEFieldPosition.Left)\n\t\t\t\t{\n\t\t\t\t\treturn PBEFieldPosition.Right;\n\t\t\t\t}\n\t\t\t\telse if (position == PBEFieldPosition.Center)\n\t\t\t\t{\n\t\t\t\t\treturn PBEFieldPosition.Center;\n\t\t\t\t}\n\t\t\t\telse if (position == PBEFieldPosition.Right)\n\t\t\t\t{\n\t\t\t\t\treturn PBEFieldPosition.Left;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(position));\n\t\t\t\t}\n\t\t\t}\n\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(battleFormat));\n\t\t}\n\t}\n\n\t/// <summary>Gets the Pokémon surrounding <paramref name=\"pkmn\"/>.</summary>\n\t/// <param name=\"pkmn\">The Pokémon to check.</param>\n\t/// <param name=\"includeAllies\">True if allies should be included, False otherwise.</param>\n\t/// <param name=\"includeFoes\">True if foes should be included, False otherwise.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Thrown when <paramref name=\"pkmn\"/>'s <see cref=\"PBEBattle\"/>'s <see cref=\"BattleFormat\"/> is invalid or <paramref name=\"pkmn\"/>'s <see cref=\"PBEBattlePokemon.FieldPosition\"/> is invalid for <paramref name=\"pkmn\"/>'s <see cref=\"PBEBattle\"/>'s <see cref=\"BattleFormat\"/>.</exception>\n\tpublic static IReadOnlyList<PBEBattlePokemon> GetRuntimeSurrounding(PBEBattlePokemon pkmn, bool includeAllies, bool includeFoes)\n\t{\n\t\tif (!includeAllies && !includeFoes)\n\t\t{\n\t\t\tthrow new ArgumentException($\"\\\"{nameof(includeAllies)}\\\" and \\\"{nameof(includeFoes)}\\\" were false.\");\n\t\t}\n\t\tList<PBEBattlePokemon> allies = pkmn.Team.ActiveBattlers.FindAll(p => p != pkmn);\n\t\tList<PBEBattlePokemon> foes = pkmn.Team.OpposingTeam.ActiveBattlers;\n\t\tswitch (pkmn.Battle.BattleFormat)\n\t\t{\n\t\t\tcase PBEBattleFormat.Single:\n\t\t\t{\n\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t{\n\t\t\t\t\tif (includeFoes)\n\t\t\t\t\t{\n\t\t\t\t\t\treturn foes.FindAll(p => p.FieldPosition == PBEFieldPosition.Center);\n\t\t\t\t\t}\n\t\t\t\t\treturn Array.Empty<PBEBattlePokemon>();\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcase PBEBattleFormat.Double:\n\t\t\t{\n\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t{\n\t\t\t\t\tList<PBEBattlePokemon> ret = null!;\n\t\t\t\t\tif (includeAllies)\n\t\t\t\t\t{\n\t\t\t\t\t\tret = allies.FindAll(p => p.FieldPosition == PBEFieldPosition.Right);\n\t\t\t\t\t\tif (!includeFoes)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn ret;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (includeFoes)\n\t\t\t\t\t{\n\t\t\t\t\t\tList<PBEBattlePokemon> f = foes.FindAll(p => p.FieldPosition == PBEFieldPosition.Left || p.FieldPosition == PBEFieldPosition.Right);\n\t\t\t\t\t\tif (!includeAllies)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn f;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tret.AddRange(f);\n\t\t\t\t\t\treturn ret;\n\t\t\t\t\t}\n\t\t\t\t\treturn Array.Empty<PBEBattlePokemon>();\n\t\t\t\t}\n\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t{\n\t\t\t\t\tList<PBEBattlePokemon> ret = null!;\n\t\t\t\t\tif (includeAllies)\n\t\t\t\t\t{\n\t\t\t\t\t\tret = allies.FindAll(p => p.FieldPosition == PBEFieldPosition.Left);\n\t\t\t\t\t\tif (!includeFoes)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn ret;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (includeFoes)\n\t\t\t\t\t{\n\t\t\t\t\t\tList<PBEBattlePokemon> f = foes.FindAll(p => p.FieldPosition == PBEFieldPosition.Left || p.FieldPosition == PBEFieldPosition.Right);\n\t\t\t\t\t\tif (!includeAllies)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn f;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tret.AddRange(f);\n\t\t\t\t\t\treturn ret;\n\t\t\t\t\t}\n\t\t\t\t\treturn Array.Empty<PBEBattlePokemon>();\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcase PBEBattleFormat.Triple:\n\t\t\tcase PBEBattleFormat.Rotation:\n\t\t\t{\n\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t{\n\t\t\t\t\tList<PBEBattlePokemon> ret = null!;\n\t\t\t\t\tif (includeAllies)\n\t\t\t\t\t{\n\t\t\t\t\t\tret = allies.FindAll(p => p.FieldPosition == PBEFieldPosition.Center);\n\t\t\t\t\t\tif (!includeFoes)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn ret;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (includeFoes)\n\t\t\t\t\t{\n\t\t\t\t\t\tList<PBEBattlePokemon> f = foes.FindAll(p => p.FieldPosition == PBEFieldPosition.Center || p.FieldPosition == PBEFieldPosition.Right);\n\t\t\t\t\t\tif (!includeAllies)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn f;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tret.AddRange(f);\n\t\t\t\t\t\treturn ret;\n\t\t\t\t\t}\n\t\t\t\t\treturn Array.Empty<PBEBattlePokemon>();\n\t\t\t\t}\n\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t{\n\t\t\t\t\tList<PBEBattlePokemon> ret = null!;\n\t\t\t\t\tif (includeAllies)\n\t\t\t\t\t{\n\t\t\t\t\t\tret = allies.FindAll(p => p.FieldPosition == PBEFieldPosition.Left || p.FieldPosition == PBEFieldPosition.Right);\n\t\t\t\t\t\tif (!includeFoes)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn ret;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (includeFoes)\n\t\t\t\t\t{\n\t\t\t\t\t\tList<PBEBattlePokemon> f = foes.FindAll(p => p.FieldPosition == PBEFieldPosition.Left || p.FieldPosition == PBEFieldPosition.Center || p.FieldPosition == PBEFieldPosition.Right);\n\t\t\t\t\t\tif (!includeAllies)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn f;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tret.AddRange(f);\n\t\t\t\t\t\treturn ret;\n\t\t\t\t\t}\n\t\t\t\t\treturn Array.Empty<PBEBattlePokemon>();\n\t\t\t\t}\n\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t{\n\t\t\t\t\tList<PBEBattlePokemon> ret = null!;\n\t\t\t\t\tif (includeAllies)\n\t\t\t\t\t{\n\t\t\t\t\t\tret = allies.FindAll(p => p.FieldPosition == PBEFieldPosition.Center);\n\t\t\t\t\t\tif (!includeFoes)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn ret;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (includeFoes)\n\t\t\t\t\t{\n\t\t\t\t\t\tList<PBEBattlePokemon> f = foes.FindAll(p => p.FieldPosition == PBEFieldPosition.Center || p.FieldPosition == PBEFieldPosition.Left);\n\t\t\t\t\t\tif (!includeAllies)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn f;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tret.AddRange(f);\n\t\t\t\t\t\treturn ret;\n\t\t\t\t\t}\n\t\t\t\t\treturn Array.Empty<PBEBattlePokemon>();\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t}\n\t\t\t}\n\t\t\tdefault: throw new InvalidDataException(nameof(pkmn.Battle.BattleFormat));\n\t\t}\n\t}\n\n\tprivate static void FindFoeLeftTarget(PBEBattlePokemon user, bool canHitFarCorners, List<PBEBattlePokemon> targets)\n\t{\n\t\tPBETeam ot = user.Team.OpposingTeam;\n\t\tif (!ot.TryGetPokemon(PBEFieldPosition.Left, out PBEBattlePokemon? pkmn))\n\t\t{\n\t\t\t// Left not found; fallback to its teammate\n\t\t\tswitch (user.Battle.BattleFormat)\n\t\t\t{\n\t\t\t\tcase PBEBattleFormat.Double:\n\t\t\t\t{\n\t\t\t\t\tif (!ot.TryGetPokemon(PBEFieldPosition.Right, out pkmn))\n\t\t\t\t\t{\n\t\t\t\t\t\treturn; // Nobody left and nobody right; fail\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEBattleFormat.Triple:\n\t\t\t\t{\n\t\t\t\t\tif (!ot.TryGetPokemon(PBEFieldPosition.Center, out pkmn))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (user.FieldPosition != PBEFieldPosition.Right || canHitFarCorners)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// Center fainted as well but the user can reach far right\n\t\t\t\t\t\t\tif (!ot.TryGetPokemon(PBEFieldPosition.Right, out pkmn))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn; // Nobody left, center, or right; fail\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn; // Nobody left and nobody center; fail since we can't reach the right\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tdefault: throw new InvalidOperationException();\n\t\t\t}\n\t\t}\n\t\ttargets.Add(pkmn);\n\t}\n\tprivate static void FindFoeCenterTarget(PBEBattlePokemon user, bool canHitFarCorners, PBERandom rand, List<PBEBattlePokemon> targets)\n\t{\n\t\tPBETeam ot = user.Team.OpposingTeam;\n\t\tif (!ot.TryGetPokemon(PBEFieldPosition.Center, out PBEBattlePokemon? pkmn))\n\t\t{\n\t\t\tswitch (user.Battle.BattleFormat)\n\t\t\t{\n\t\t\t\tcase PBEBattleFormat.Single:\n\t\t\t\tcase PBEBattleFormat.Rotation: return;\n\t\t\t\tdefault: throw new InvalidOperationException();\n\t\t\t\tcase PBEBattleFormat.Triple:\n\t\t\t\t{\n\t\t\t\t\t// Center not found; fallback to its teammate\n\t\t\t\t\tswitch (user.FieldPosition)\n\t\t\t\t\t{\n\t\t\t\t\t\tcase PBEFieldPosition.Left:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (!ot.TryGetPokemon(PBEFieldPosition.Right, out pkmn))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (canHitFarCorners)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tif (!ot.TryGetPokemon(PBEFieldPosition.Left, out pkmn))\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\treturn; // Nobody center, right, or left; fail\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\treturn; // Nobody center and nobody right; fail since we can't reach the left\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcase PBEFieldPosition.Center:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (!ot.TryGetPokemon(PBEFieldPosition.Left, out PBEBattlePokemon? left))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (!ot.TryGetPokemon(PBEFieldPosition.Right, out PBEBattlePokemon? right))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\treturn; // Nobody left or right; fail\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tpkmn = right; // Nobody left; pick right\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (!ot.TryGetPokemon(PBEFieldPosition.Right, out PBEBattlePokemon? right))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tpkmn = left; // Nobody right; pick left\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tpkmn = rand.RandomBool() ? left : right; // Left and right present; randomly select left or right\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcase PBEFieldPosition.Right:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (!ot.TryGetPokemon(PBEFieldPosition.Left, out pkmn))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (canHitFarCorners)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tif (!ot.TryGetPokemon(PBEFieldPosition.Right, out pkmn))\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\treturn; // Nobody center, left, or right; fail\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\treturn; // Nobody center and nobody left; fail since we can't reach the right\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tdefault: throw new InvalidDataException();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\ttargets.Add(pkmn);\n\t}\n\tprivate static void FindFoeRightTarget(PBEBattlePokemon user, bool canHitFarCorners, List<PBEBattlePokemon> targets)\n\t{\n\t\tPBETeam ot = user.Team.OpposingTeam;\n\t\tif (!ot.TryGetPokemon(PBEFieldPosition.Right, out PBEBattlePokemon? pkmn))\n\t\t{\n\t\t\t// Right not found; fallback to its teammate\n\t\t\tswitch (user.Battle.BattleFormat)\n\t\t\t{\n\t\t\t\tcase PBEBattleFormat.Double:\n\t\t\t\t{\n\t\t\t\t\tif (!ot.TryGetPokemon(PBEFieldPosition.Left, out pkmn))\n\t\t\t\t\t{\n\t\t\t\t\t\treturn; // Nobody right and nobody left; fail\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEBattleFormat.Triple:\n\t\t\t\t{\n\t\t\t\t\tif (!ot.TryGetPokemon(PBEFieldPosition.Center, out pkmn))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (user.FieldPosition != PBEFieldPosition.Left || canHitFarCorners)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// Center fainted as well but the user can reach far left\n\t\t\t\t\t\t\tif (!ot.TryGetPokemon(PBEFieldPosition.Left, out pkmn))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn; // Nobody right, center, or left; fail\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn; // Nobody right and nobody center; fail since we can't reach the left\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tdefault: throw new InvalidOperationException();\n\t\t\t}\n\t\t}\n\t\ttargets.Add(pkmn);\n\t}\n\t/// <summary>Gets all Pokémon that will be hit.</summary>\n\t/// <param name=\"user\">The Pokémon that will act.</param>\n\t/// <param name=\"requestedTargets\">The targets the Pokémon wishes to hit.</param>\n\t/// <param name=\"canHitFarCorners\">Whether the move can hit far Pokémon in a triple battle.</param>\n\t/// <param name=\"rand\">The random to use.</param>\n\tprivate static PBEBattlePokemon[] GetRuntimeTargets(PBEBattlePokemon user, PBETurnTarget requestedTargets, bool canHitFarCorners, PBERandom rand)\n\t{\n\t\tvar targets = new List<PBEBattlePokemon>();\n\t\t// Foes first, then allies (since initial attack effects run that way)\n\t\tif (requestedTargets.HasFlag(PBETurnTarget.FoeLeft))\n\t\t{\n\t\t\tFindFoeLeftTarget(user, canHitFarCorners, targets);\n\t\t}\n\t\tif (requestedTargets.HasFlag(PBETurnTarget.FoeCenter))\n\t\t{\n\t\t\tFindFoeCenterTarget(user, canHitFarCorners, rand, targets);\n\t\t}\n\t\tif (requestedTargets.HasFlag(PBETurnTarget.FoeRight))\n\t\t{\n\t\t\tFindFoeRightTarget(user, canHitFarCorners, targets);\n\t\t}\n\t\tPBETeam t = user.Team;\n\t\tif (requestedTargets.HasFlag(PBETurnTarget.AllyLeft))\n\t\t{\n\t\t\tt.TryAddPokemonToCollection(PBEFieldPosition.Left, targets);\n\t\t}\n\t\tif (requestedTargets.HasFlag(PBETurnTarget.AllyCenter))\n\t\t{\n\t\t\tt.TryAddPokemonToCollection(PBEFieldPosition.Center, targets);\n\t\t}\n\t\tif (requestedTargets.HasFlag(PBETurnTarget.AllyRight))\n\t\t{\n\t\t\tt.TryAddPokemonToCollection(PBEFieldPosition.Right, targets);\n\t\t}\n\t\treturn targets.Distinct().ToArray(); // Remove duplicate targets\n\t}\n\n\t/// <summary>Determines whether chosen targets are valid for a given move.</summary>\n\t/// <param name=\"pkmn\">The Pokémon that will act.</param>\n\t/// <param name=\"move\">The move the Pokémon wishes to use.</param>\n\t/// <param name=\"targets\">The targets bitfield to validate.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Thrown when <paramref name=\"targets\"/>, <paramref name=\"move\"/>, <paramref name=\"pkmn\"/>'s <see cref=\"PBEBattlePokemon.FieldPosition\"/>, or <paramref name=\"pkmn\"/>'s <see cref=\"PBEBattle\"/>'s <see cref=\"BattleFormat\"/> is invalid.</exception>\n\tpublic static bool AreTargetsValid(PBEBattlePokemon pkmn, PBEMove move, PBETurnTarget targets)\n\t{\n\t\tif (move == PBEMove.None || move >= PBEMove.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(move));\n\t\t}\n\t\tIPBEMoveData mData = PBEDataProvider.Instance.GetMoveData(move);\n\t\tif (!mData.IsMoveUsable())\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(move));\n\t\t}\n\t\treturn AreTargetsValid(pkmn, mData, targets);\n\t}\n\tpublic static bool AreTargetsValid(PBEBattlePokemon pkmn, IPBEMoveData mData, PBETurnTarget targets)\n\t{\n\t\tPBEMoveTarget possibleTargets = pkmn.GetMoveTargets(mData);\n\t\tswitch (pkmn.Battle.BattleFormat)\n\t\t{\n\t\t\tcase PBEBattleFormat.Single:\n\t\t\t{\n\t\t\t\tswitch (possibleTargets)\n\t\t\t\t{\n\t\t\t\t\tcase PBEMoveTarget.All:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == (PBETurnTarget.AllyCenter | PBETurnTarget.FoeCenter);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllFoes:\n\t\t\t\t\tcase PBEMoveTarget.AllFoesSurrounding:\n\t\t\t\t\tcase PBEMoveTarget.AllSurrounding:\n\t\t\t\t\tcase PBEMoveTarget.SingleFoeSurrounding:\n\t\t\t\t\tcase PBEMoveTarget.SingleNotSelf:\n\t\t\t\t\tcase PBEMoveTarget.SingleSurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == PBETurnTarget.FoeCenter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllTeam:\n\t\t\t\t\tcase PBEMoveTarget.RandomFoeSurrounding:\n\t\t\t\t\tcase PBEMoveTarget.Self:\n\t\t\t\t\tcase PBEMoveTarget.SelfOrAllySurrounding:\n\t\t\t\t\tcase PBEMoveTarget.SingleAllySurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == PBETurnTarget.AllyCenter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdefault: throw new InvalidDataException(nameof(possibleTargets));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcase PBEBattleFormat.Double:\n\t\t\t{\n\t\t\t\tswitch (possibleTargets)\n\t\t\t\t{\n\t\t\t\t\tcase PBEMoveTarget.All:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == (PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeRight);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllFoes:\n\t\t\t\t\tcase PBEMoveTarget.AllFoesSurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == (PBETurnTarget.FoeLeft | PBETurnTarget.FoeRight);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllTeam:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == (PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllSurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == (PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeRight);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == (PBETurnTarget.AllyLeft | PBETurnTarget.FoeLeft | PBETurnTarget.FoeRight);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.RandomFoeSurrounding:\n\t\t\t\t\tcase PBEMoveTarget.Self:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == PBETurnTarget.AllyLeft;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == PBETurnTarget.AllyRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.SelfOrAllySurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == PBETurnTarget.AllyLeft || targets == PBETurnTarget.AllyRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.SingleAllySurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == PBETurnTarget.AllyRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == PBETurnTarget.AllyLeft;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.SingleFoeSurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == PBETurnTarget.FoeLeft || targets == PBETurnTarget.FoeRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.SingleNotSelf:\n\t\t\t\t\tcase PBEMoveTarget.SingleSurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == PBETurnTarget.AllyRight || targets == PBETurnTarget.FoeLeft || targets == PBETurnTarget.FoeRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == PBETurnTarget.AllyLeft || targets == PBETurnTarget.FoeLeft || targets == PBETurnTarget.FoeRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdefault: throw new InvalidDataException(nameof(possibleTargets));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcase PBEBattleFormat.Triple:\n\t\t\t{\n\t\t\t\tswitch (possibleTargets)\n\t\t\t\t{\n\t\t\t\t\tcase PBEMoveTarget.All:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == (PBETurnTarget.AllyLeft | PBETurnTarget.AllyCenter | PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllFoes:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == (PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllFoesSurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == (PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == (PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == (PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllSurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == (PBETurnTarget.AllyCenter | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == (PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == (PBETurnTarget.AllyCenter | PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllTeam:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == (PBETurnTarget.AllyLeft | PBETurnTarget.AllyCenter | PBETurnTarget.AllyRight);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.RandomFoeSurrounding:\n\t\t\t\t\tcase PBEMoveTarget.Self:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == PBETurnTarget.AllyLeft;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == PBETurnTarget.AllyCenter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == PBETurnTarget.AllyRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.SelfOrAllySurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == PBETurnTarget.AllyLeft || targets == PBETurnTarget.AllyCenter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == PBETurnTarget.AllyLeft || targets == PBETurnTarget.AllyCenter || targets == PBETurnTarget.AllyRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == PBETurnTarget.AllyCenter || targets == PBETurnTarget.AllyRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.SingleAllySurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == PBETurnTarget.AllyCenter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == PBETurnTarget.AllyLeft || targets == PBETurnTarget.AllyRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.SingleFoeSurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == PBETurnTarget.FoeCenter || targets == PBETurnTarget.FoeRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == PBETurnTarget.FoeLeft || targets == PBETurnTarget.FoeCenter || targets == PBETurnTarget.FoeRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == PBETurnTarget.FoeLeft || targets == PBETurnTarget.FoeCenter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.SingleNotSelf:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == PBETurnTarget.AllyCenter || targets == PBETurnTarget.AllyRight || targets == PBETurnTarget.FoeLeft || targets == PBETurnTarget.FoeCenter || targets == PBETurnTarget.FoeRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == PBETurnTarget.AllyLeft || targets == PBETurnTarget.AllyRight || targets == PBETurnTarget.FoeLeft || targets == PBETurnTarget.FoeCenter || targets == PBETurnTarget.FoeRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == PBETurnTarget.AllyLeft || targets == PBETurnTarget.AllyCenter || targets == PBETurnTarget.FoeLeft || targets == PBETurnTarget.FoeCenter || targets == PBETurnTarget.FoeRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.SingleSurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == PBETurnTarget.AllyCenter || targets == PBETurnTarget.FoeCenter || targets == PBETurnTarget.FoeRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == PBETurnTarget.AllyLeft || targets == PBETurnTarget.AllyRight || targets == PBETurnTarget.FoeLeft || targets == PBETurnTarget.FoeCenter || targets == PBETurnTarget.FoeRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == PBETurnTarget.AllyCenter || targets == PBETurnTarget.FoeLeft || targets == PBETurnTarget.FoeCenter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdefault: throw new InvalidDataException(nameof(possibleTargets));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcase PBEBattleFormat.Rotation:\n\t\t\t{\n\t\t\t\tswitch (possibleTargets)\n\t\t\t\t{\n\t\t\t\t\tcase PBEMoveTarget.All:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == (PBETurnTarget.AllyCenter | PBETurnTarget.FoeCenter);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllFoes:\n\t\t\t\t\tcase PBEMoveTarget.AllFoesSurrounding:\n\t\t\t\t\tcase PBEMoveTarget.AllSurrounding:\n\t\t\t\t\tcase PBEMoveTarget.SingleFoeSurrounding:\n\t\t\t\t\tcase PBEMoveTarget.SingleNotSelf:\n\t\t\t\t\tcase PBEMoveTarget.SingleSurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == PBETurnTarget.FoeCenter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllTeam:\n\t\t\t\t\tcase PBEMoveTarget.RandomFoeSurrounding:\n\t\t\t\t\tcase PBEMoveTarget.Self:\n\t\t\t\t\tcase PBEMoveTarget.SelfOrAllySurrounding:\n\t\t\t\t\tcase PBEMoveTarget.SingleAllySurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn targets == PBETurnTarget.AllyCenter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdefault: throw new InvalidDataException(nameof(possibleTargets));\n\t\t\t\t}\n\t\t\t}\n\t\t\tdefault: throw new InvalidDataException(nameof(pkmn.Battle.BattleFormat));\n\t\t}\n\t}\n\n\t/// <summary>Gets a random target a move can hit when called by <see cref=\"PBEMoveEffect.Metronome\"/>.</summary>\n\t/// <param name=\"pkmn\">The Pokémon using <paramref name=\"calledMove\"/>.</param>\n\t/// <param name=\"calledMove\">The move being called.</param>\n\t/// <param name=\"rand\">The random to use.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Thrown when <paramref name=\"calledMove\"/>, <paramref name=\"pkmn\"/>'s <see cref=\"PBEBattlePokemon.FieldPosition\"/>, or <paramref name=\"pkmn\"/>'s <see cref=\"PBEBattle\"/>'s <see cref=\"BattleFormat\"/> is invalid.</exception>\n\tpublic static PBETurnTarget GetRandomTargetForMetronome(PBEBattlePokemon pkmn, PBEMove calledMove, PBERandom rand)\n\t{\n\t\tif (calledMove == PBEMove.None || calledMove >= PBEMove.MAX || !PBEDataUtils.IsMoveUsable(calledMove))\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(calledMove));\n\t\t}\n\t\tIPBEMoveData mData = PBEDataProvider.Instance.GetMoveData(calledMove);\n\t\tif (!mData.IsMoveUsable())\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(calledMove));\n\t\t}\n\t\tPBEMoveTarget possibleTargets = pkmn.GetMoveTargets(mData);\n\t\tswitch (pkmn.Battle.BattleFormat)\n\t\t{\n\t\t\tcase PBEBattleFormat.Single:\n\t\t\t{\n\t\t\t\tswitch (possibleTargets)\n\t\t\t\t{\n\t\t\t\t\tcase PBEMoveTarget.All:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyCenter | PBETurnTarget.FoeCenter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllFoes:\n\t\t\t\t\tcase PBEMoveTarget.AllFoesSurrounding:\n\t\t\t\t\tcase PBEMoveTarget.AllSurrounding:\n\t\t\t\t\tcase PBEMoveTarget.RandomFoeSurrounding:\n\t\t\t\t\tcase PBEMoveTarget.SingleFoeSurrounding:\n\t\t\t\t\tcase PBEMoveTarget.SingleNotSelf:\n\t\t\t\t\tcase PBEMoveTarget.SingleSurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.FoeCenter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllTeam:\n\t\t\t\t\tcase PBEMoveTarget.Self:\n\t\t\t\t\tcase PBEMoveTarget.SelfOrAllySurrounding:\n\t\t\t\t\tcase PBEMoveTarget.SingleAllySurrounding: // Helping Hand cannot be called by Metronome anyway\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyCenter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdefault: throw new InvalidDataException(nameof(possibleTargets));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcase PBEBattleFormat.Double:\n\t\t\t{\n\t\t\t\tswitch (possibleTargets)\n\t\t\t\t{\n\t\t\t\t\tcase PBEMoveTarget.All:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllFoes:\n\t\t\t\t\tcase PBEMoveTarget.AllFoesSurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.FoeLeft | PBETurnTarget.FoeRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllTeam:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllSurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyLeft | PBETurnTarget.FoeLeft | PBETurnTarget.FoeRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.Self:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyLeft;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.SelfOrAllySurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (rand.RandomBool())\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn PBETurnTarget.AllyLeft;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn PBETurnTarget.AllyRight;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.SingleAllySurrounding: // Helping Hand cannot be called by Metronome anyway\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyLeft;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.RandomFoeSurrounding:\n\t\t\t\t\tcase PBEMoveTarget.SingleFoeSurrounding:\n\t\t\t\t\tcase PBEMoveTarget.SingleNotSelf:\n\t\t\t\t\tcase PBEMoveTarget.SingleSurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (rand.RandomBool())\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn PBETurnTarget.FoeLeft;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn PBETurnTarget.FoeRight;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdefault: throw new InvalidDataException(nameof(possibleTargets));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcase PBEBattleFormat.Triple:\n\t\t\t{\n\t\t\t\tswitch (possibleTargets)\n\t\t\t\t{\n\t\t\t\t\tcase PBEMoveTarget.All:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyLeft | PBETurnTarget.AllyCenter | PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllFoes:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllFoesSurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllSurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyCenter | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyCenter | PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllTeam:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyLeft | PBETurnTarget.AllyCenter | PBETurnTarget.AllyRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.Self:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyLeft;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyCenter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.SelfOrAllySurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (rand.RandomBool())\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn PBETurnTarget.AllyLeft;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn PBETurnTarget.AllyCenter;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tint val = rand.RandomInt(0, 2);\n\t\t\t\t\t\t\tif (val == 0)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn PBETurnTarget.AllyLeft;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (val == 1)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn PBETurnTarget.AllyCenter;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn PBETurnTarget.AllyRight;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (rand.RandomBool())\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn PBETurnTarget.AllyCenter;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn PBETurnTarget.AllyRight;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.SingleAllySurrounding: // Helping Hand cannot be called by Metronome anyway\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyCenter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (rand.RandomBool())\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn PBETurnTarget.AllyLeft;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn PBETurnTarget.AllyRight;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyCenter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.RandomFoeSurrounding:\n\t\t\t\t\tcase PBEMoveTarget.SingleFoeSurrounding:\n\t\t\t\t\tcase PBEMoveTarget.SingleSurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (rand.RandomBool())\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn PBETurnTarget.FoeCenter;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn PBETurnTarget.FoeRight;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tint val = rand.RandomInt(0, 2);\n\t\t\t\t\t\t\tif (val == 0)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn PBETurnTarget.FoeLeft;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (val == 1)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn PBETurnTarget.FoeCenter;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn PBETurnTarget.FoeRight;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (rand.RandomBool())\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn PBETurnTarget.FoeLeft;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn PBETurnTarget.FoeCenter;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.SingleNotSelf:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tint val = rand.RandomInt(0, 2);\n\t\t\t\t\t\t\tif (val == 0)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn PBETurnTarget.FoeLeft;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (val == 1)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn PBETurnTarget.FoeCenter;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn PBETurnTarget.FoeRight;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdefault: throw new InvalidDataException(nameof(possibleTargets));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcase PBEBattleFormat.Rotation:\n\t\t\t{\n\t\t\t\tswitch (possibleTargets)\n\t\t\t\t{\n\t\t\t\t\tcase PBEMoveTarget.All:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyCenter | PBETurnTarget.FoeCenter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllFoes:\n\t\t\t\t\tcase PBEMoveTarget.AllFoesSurrounding:\n\t\t\t\t\tcase PBEMoveTarget.AllSurrounding:\n\t\t\t\t\tcase PBEMoveTarget.RandomFoeSurrounding:\n\t\t\t\t\tcase PBEMoveTarget.SingleFoeSurrounding:\n\t\t\t\t\tcase PBEMoveTarget.SingleNotSelf:\n\t\t\t\t\tcase PBEMoveTarget.SingleSurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.FoeCenter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllTeam:\n\t\t\t\t\tcase PBEMoveTarget.Self:\n\t\t\t\t\tcase PBEMoveTarget.SelfOrAllySurrounding:\n\t\t\t\t\tcase PBEMoveTarget.SingleAllySurrounding: // Helping Hand cannot be called by Metronome anyway\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyCenter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new InvalidDataException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdefault: throw new InvalidDataException(nameof(possibleTargets));\n\t\t\t\t}\n\t\t\t}\n\t\t\tdefault: throw new InvalidDataException(nameof(pkmn.Battle.BattleFormat));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Battle/BattleTeam.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Packets;\nusing Kermalis.PokemonBattleEngine.Utils;\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Diagnostics.CodeAnalysis;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\n\nnamespace Kermalis.PokemonBattleEngine.Battle;\n\npublic sealed class PBETeams : IReadOnlyList<PBETeam>\n{\n\tprivate readonly PBETeam _team0;\n\tprivate readonly PBETeam _team1;\n\n\tpublic int Count => 2;\n\tpublic PBETeam this[int index]\n\t{\n\t\tget\n\t\t{\n\t\t\tswitch (index)\n\t\t\t{\n\t\t\t\tcase 0: return _team0;\n\t\t\t\tcase 1: return _team1;\n\t\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(index));\n\t\t\t}\n\t\t}\n\t}\n\n\t// Trainer battle\n\tinternal PBETeams(PBEBattle battle, IReadOnlyList<PBETrainerInfo> ti0, IReadOnlyList<PBETrainerInfo> ti1, out PBETrainers trainers)\n\t{\n\t\tvar allTrainers = new List<PBETrainer>();\n\t\t_team0 = new PBETeam(battle, 0, ti0, allTrainers);\n\t\t_team1 = new PBETeam(battle, 1, ti1, allTrainers);\n\t\t_team0.OpposingTeam = _team1;\n\t\t_team1.OpposingTeam = _team0;\n\t\ttrainers = new PBETrainers(allTrainers);\n\t}\n\t// Wild battle\n\tinternal PBETeams(PBEBattle battle, IReadOnlyList<PBETrainerInfo> ti, PBEWildInfo wi, out PBETrainers trainers)\n\t{\n\t\tvar allTrainers = new List<PBETrainer>();\n\t\t_team0 = new PBETeam(battle, 0, ti, allTrainers);\n\t\t_team1 = new PBETeam(battle, 1, wi, allTrainers);\n\t\t_team0.OpposingTeam = _team1;\n\t\t_team1.OpposingTeam = _team0;\n\t\ttrainers = new PBETrainers(allTrainers);\n\t}\n\t// Remote battle\n\tinternal PBETeams(PBEBattle battle, PBEBattlePacket packet, out PBETrainers trainers)\n\t{\n\t\tvar allTrainers = new List<PBETrainer>();\n\t\t_team0 = new PBETeam(battle, packet.Teams[0], allTrainers);\n\t\t_team1 = new PBETeam(battle, packet.Teams[1], allTrainers);\n\t\t_team0.OpposingTeam = _team1;\n\t\t_team1.OpposingTeam = _team0;\n\t\ttrainers = new PBETrainers(allTrainers);\n\t}\n\n\tpublic bool All(Predicate<PBETeam> match)\n\t{\n\t\treturn match(_team0) && match(_team1);\n\t}\n\n\tpublic IEnumerator<PBETeam> GetEnumerator()\n\t{\n\t\tyield return _team0;\n\t\tyield return _team1;\n\t}\n\tIEnumerator IEnumerable.GetEnumerator()\n\t{\n\t\tyield return _team0;\n\t\tyield return _team1;\n\t}\n}\n/// <summary>Represents a team in a specific <see cref=\"PBEBattle\"/>.</summary>\npublic sealed class PBETeam\n{\n\t/// <summary>The battle this team and its party belongs to.</summary>\n\tpublic PBEBattle Battle { get; }\n\tpublic PBETeam OpposingTeam { get; internal set; }\n\tpublic ReadOnlyCollection<PBETrainer> Trainers { get; }\n\tpublic byte Id { get; }\n\tpublic bool IsWild => Battle.BattleType == PBEBattleType.Wild && Id == 1;\n\n\tpublic string CombinedName { get; }\n\tpublic IEnumerable<PBEBattlePokemon> CombinedParty => Trainers.SelectMany(t => t.Party);\n\tpublic List<PBEBattlePokemon> ActiveBattlers => Battle.ActiveBattlers.FindAll(p => p.Team == this);\n\tpublic int NumConsciousPkmn => Trainers.Sum(t => t.NumConsciousPkmn);\n\tpublic int NumPkmnOnField => Trainers.Sum(t => t.NumPkmnOnField);\n\n\tpublic int NumTimesTriedToFlee { get; set; }\n\tpublic PBETeamStatus TeamStatus { get; set; }\n\tpublic byte LightScreenCount { get; set; }\n\tpublic byte LuckyChantCount { get; set; }\n\tpublic byte ReflectCount { get; set; }\n\tpublic byte SafeguardCount { get; set; }\n\tpublic byte SpikeCount { get; set; }\n\tpublic byte TailwindCount { get; set; }\n\tpublic byte ToxicSpikeCount { get; set; }\n\tpublic bool MonFaintedLastTurn { get; set; }\n\tpublic bool MonFaintedThisTurn { get; set; }\n\n\t// Trainer battle\n\tinternal PBETeam(PBEBattle battle, byte id, IReadOnlyList<PBETrainerInfo> ti, List<PBETrainer> allTrainers)\n\t{\n\t\tint count = ti.Count;\n\t\tif (!VerifyTrainerCount(battle.BattleFormat, count))\n\t\t{\n\t\t\tthrow new ArgumentException($\"Illegal trainer count (Format: {battle.BattleFormat}, Team: {id}, Count: {count}\");\n\t\t}\n\t\tforeach (PBETrainerInfo t in ti)\n\t\t{\n\t\t\tif (!t.IsOkayForSettings(battle.Settings))\n\t\t\t{\n\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(ti), \"Team settings do not comply with battle settings.\");\n\t\t\t}\n\t\t}\n\t\tBattle = battle;\n\t\tId = id;\n\t\tvar trainers = new PBETrainer[ti.Count];\n\t\tfor (int i = 0; i < ti.Count; i++)\n\t\t{\n\t\t\ttrainers[i] = new PBETrainer(this, ti[i], allTrainers);\n\t\t}\n\t\tTrainers = new ReadOnlyCollection<PBETrainer>(trainers);\n\t\tCombinedName = GetCombinedName();\n\t\tOpposingTeam = null!; // OpposingTeam is set in PBETeams after both are created\n\t}\n\t// Wild battle\n\tinternal PBETeam(PBEBattle battle, byte id, PBEWildInfo wi, List<PBETrainer> allTrainers)\n\t{\n\t\tint count = wi.Party.Count;\n\t\tif (!VerifyWildCount(battle.BattleFormat, count))\n\t\t{\n\t\t\tthrow new ArgumentException($\"Illegal wild Pokémon count (Format: {battle.BattleFormat}, Count: {count}\");\n\t\t}\n\t\tif (!wi.IsOkayForSettings(battle.Settings))\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(wi), \"Team settings do not comply with battle settings.\");\n\t\t}\n\t\tBattle = battle;\n\t\tId = id;\n\t\tTrainers = new ReadOnlyCollection<PBETrainer>(new[] { new PBETrainer(this, wi, allTrainers) });\n\t\tCombinedName = GetCombinedName();\n\t\tOpposingTeam = null!; // OpposingTeam is set in PBETeams after both are created\n\t}\n\t// Remote battle\n\tinternal PBETeam(PBEBattle battle, PBEBattlePacket.PBETeamInfo info, List<PBETrainer> allTrainers)\n\t{\n\t\tReadOnlyCollection<PBEBattlePacket.PBETeamInfo.PBETrainerInfo> ti = info.Trainers;\n\t\tint count = ti.Count;\n\t\tif (!VerifyTrainerCount(battle.BattleFormat, count))\n\t\t{\n\t\t\tthrow new InvalidDataException();\n\t\t}\n\t\tBattle = battle;\n\t\tId = info.Id;\n\t\tvar trainers = new PBETrainer[ti.Count];\n\t\tfor (int i = 0; i < trainers.Length; i++)\n\t\t{\n\t\t\ttrainers[i] = new PBETrainer(this, ti[i], allTrainers);\n\t\t}\n\t\tTrainers = new ReadOnlyCollection<PBETrainer>(trainers);\n\t\tCombinedName = GetCombinedName();\n\t\tOpposingTeam = null!; // OpposingTeam is set in PBETeams after both are created\n\t}\n\tprivate static bool VerifyWildCount(PBEBattleFormat format, int count)\n\t{\n\t\tswitch (format)\n\t\t{\n\t\t\tcase PBEBattleFormat.Single: return count == 1;\n\t\t\tcase PBEBattleFormat.Double: return count >= 1 && count <= 2;\n\t\t\tcase PBEBattleFormat.Rotation:\n\t\t\tcase PBEBattleFormat.Triple: return count >= 1 && count <= 3;\n\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(format));\n\t\t}\n\t}\n\tprivate static bool VerifyTrainerCount(PBEBattleFormat format, int count)\n\t{\n\t\tswitch (format)\n\t\t{\n\t\t\tcase PBEBattleFormat.Single:\n\t\t\tcase PBEBattleFormat.Rotation: return count == 1;\n\t\t\tcase PBEBattleFormat.Double: return count == 1 || count == 2;\n\t\t\tcase PBEBattleFormat.Triple: return count == 1 || count == 3;\n\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(format));\n\t\t}\n\t}\n\tprivate string GetCombinedName()\n\t{\n\t\tstring[] names = new string[Trainers.Count];\n\t\tfor (int i = 0; i < names.Length; i++)\n\t\t{\n\t\t\tnames[i] = Trainers[i].Name;\n\t\t}\n\t\treturn names.Andify();\n\t}\n\n\tpublic bool IsSpotOccupied(PBEFieldPosition pos)\n\t{\n\t\tforeach (PBEBattlePokemon p in ActiveBattlers)\n\t\t{\n\t\t\tif (p.FieldPosition == pos)\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\tpublic bool TryGetPokemon(PBEFieldPosition pos, [NotNullWhen(true)] out PBEBattlePokemon? pkmn)\n\t{\n\t\tforeach (PBEBattlePokemon p in ActiveBattlers)\n\t\t{\n\t\t\tif (p.FieldPosition == pos)\n\t\t\t{\n\t\t\t\tpkmn = p;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tpkmn = null;\n\t\treturn false;\n\t}\n\tpublic void TryAddPokemonToCollection(PBEFieldPosition pos, ICollection<PBEBattlePokemon> list)\n\t{\n\t\tif (TryGetPokemon(pos, out PBEBattlePokemon? pkmn))\n\t\t{\n\t\t\tlist.Add(pkmn);\n\t\t}\n\t}\n\n\tpublic override string ToString()\n\t{\n\t\tvar sb = new StringBuilder();\n\t\tsb.AppendLine($\"Team {Id}:\");\n\t\tsb.AppendLine($\"TeamStatus: {TeamStatus}\");\n\t\t//sb.AppendLine($\"NumPkmn: {Party.Length}\");\n\t\tsb.AppendLine($\"NumConsciousPkmn: {NumConsciousPkmn}\");\n\t\tsb.AppendLine($\"NumPkmnOnField: {NumPkmnOnField}\");\n\t\treturn sb.ToString();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Battle/BattleTrainer.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Packets;\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq;\n\nnamespace Kermalis.PokemonBattleEngine.Battle;\n\npublic sealed class PBETrainers : IReadOnlyList<PBETrainer>\n{\n\tprivate readonly List<PBETrainer> _trainers;\n\n\tpublic int Count => _trainers.Count;\n\tpublic PBETrainer this[int index] => _trainers[index];\n\n\tinternal PBETrainers(List<PBETrainer> trainers)\n\t{\n\t\t_trainers = trainers;\n\t}\n\n\tpublic IEnumerator<PBETrainer> GetEnumerator()\n\t{\n\t\treturn _trainers.GetEnumerator();\n\t}\n\tIEnumerator IEnumerable.GetEnumerator()\n\t{\n\t\treturn _trainers.GetEnumerator();\n\t}\n}\npublic sealed partial class PBETrainer\n{\n\tpublic PBEBattle Battle { get; }\n\tpublic PBETeam Team { get; }\n\tpublic PBEList<PBEBattlePokemon> Party { get; }\n\tpublic string Name { get; }\n\tpublic bool GainsEXP { get; }\n\tpublic PBEBattleInventory Inventory { get; }\n\tpublic byte Id { get; }\n\tpublic bool IsWild => Team.IsWild;\n\n\tpublic List<PBEBattlePokemon> ActiveBattlers => Battle.ActiveBattlers.FindAll(p => p.Trainer == this);\n\tpublic IEnumerable<PBEBattlePokemon> ActiveBattlersOrdered => ActiveBattlers.OrderBy(p => p.FieldPosition);\n\tpublic int NumConsciousPkmn => Party.Count(p => p.CanBattle);\n\tpublic int NumPkmnOnField => Party.Count(p => p.FieldPosition != PBEFieldPosition.None);\n\n\tpublic bool RequestedFlee { get; set; }\n\tpublic List<PBEBattlePokemon> ActionsRequired { get; } = new(3); // PBEBattleState.WaitingForActions\n\tpublic byte SwitchInsRequired { get; set; } // PBEBattleState.WaitingForSwitchIns\n\tpublic List<(PBEBattlePokemon Pkmn, PBEFieldPosition Pos)> SwitchInQueue { get; } = new(3); // PBEBattleState.WaitingForSwitchIns\n\n\t// Trainer battle / wild battle\n\tprivate PBETrainer(PBETeam team, PBETrainerInfoBase ti, string name, ReadOnlyCollection<(PBEItem Item, uint Quantity)>? inventory, List<PBETrainer> trainers)\n\t{\n\t\tBattle = team.Battle;\n\t\tTeam = team;\n\t\tId = (byte)trainers.Count;\n\t\tName = name;\n\t\tif (inventory is null || inventory.Count == 0) // Wild trainer\n\t\t{\n\t\t\tInventory = PBEBattleInventory.Empty();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tInventory = new PBEBattleInventory(inventory);\n\t\t}\n\t\tReadOnlyCollection<IPBEPokemon> tiParty = ti.Party;\n\t\tParty = new PBEList<PBEBattlePokemon>(tiParty.Count);\n\t\tfor (byte i = 0; i < tiParty.Count; i++)\n\t\t{\n\t\t\tIPBEPokemon pkmn = tiParty[i];\n\t\t\tif (pkmn is IPBEPartyPokemon partyPkmn)\n\t\t\t{\n\t\t\t\t_ = new PBEBattlePokemon(this, i, partyPkmn);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t_ = new PBEBattlePokemon(this, i, pkmn);\n\t\t\t}\n\t\t}\n\t\ttrainers.Add(this);\n\t}\n\t// Trainer battle\n\tinternal PBETrainer(PBETeam team, PBETrainerInfo ti, List<PBETrainer> trainers)\n\t\t: this(team, ti, ti.Name, ti.Inventory, trainers)\n\t{\n\t\tGainsEXP = ti.GainsEXP;\n\t}\n\t// Wild battle\n\tinternal PBETrainer(PBETeam team, PBEWildInfo wi, List<PBETrainer> trainers)\n\t\t: this(team, wi, \"The wild Pokémon\", null, trainers) { }\n\t// Remote battle\n\tinternal PBETrainer(PBETeam team, PBEBattlePacket.PBETeamInfo.PBETrainerInfo info, List<PBETrainer> trainers)\n\t{\n\t\tBattle = team.Battle;\n\t\tTeam = team;\n\t\tId = info.Id;\n\t\tName = team.IsWild ? \"The wild Pokémon\" : info.Name;\n\t\tInventory = info.Inventory.Count == 0 ? PBEBattleInventory.Empty() : new PBEBattleInventory(info.Inventory);\n\t\tParty = new PBEList<PBEBattlePokemon>(info.Party.Select(p => new PBEBattlePokemon(this, p)));\n\t\ttrainers.Add(this);\n\t}\n\n\tpublic static void Remove(PBEBattlePokemon pokemon)\n\t{\n\t\tpokemon.Trainer.Party.Remove(pokemon);\n\t}\n\tpublic static void SwitchTwoPokemon(PBEBattlePokemon a, PBEFieldPosition pos)\n\t{\n\t\tif (pos == PBEFieldPosition.None || pos >= PBEFieldPosition.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(pos));\n\t\t}\n\t\tPBETrainer t = a.Trainer;\n\t\tPBEBattlePokemon b = t.Party[t.GetFieldPositionIndex(pos)];\n\t\tif (a != b)\n\t\t{\n\t\t\tt.Party.Swap(a, b);\n\t\t}\n\t}\n\tpublic static void SwitchTwoPokemon(PBEBattlePokemon a, PBEBattlePokemon b)\n\t{\n\t\tif (a != b)\n\t\t{\n\t\t\tPBETrainer t = a.Trainer;\n\t\t\tif (t != b.Trainer)\n\t\t\t{\n\t\t\t\tthrow new ArgumentException(nameof(a.Trainer));\n\t\t\t}\n\t\t\tt.Party.Swap(a, b);\n\t\t}\n\t}\n\n\tpublic bool IsSpotOccupied(PBEFieldPosition pos)\n\t{\n\t\tforeach (PBEBattlePokemon p in ActiveBattlers)\n\t\t{\n\t\t\tif (p.FieldPosition == pos)\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\tpublic bool TryGetPokemon(PBEFieldPosition pos, [NotNullWhen(true)] out PBEBattlePokemon? pkmn)\n\t{\n\t\tforeach (PBEBattlePokemon p in ActiveBattlers)\n\t\t{\n\t\t\tif (p.FieldPosition == pos)\n\t\t\t{\n\t\t\t\tpkmn = p;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tpkmn = null;\n\t\treturn false;\n\t}\n\tpublic bool TryGetPokemon(byte pkmnId, [NotNullWhen(true)] out PBEBattlePokemon? pkmn)\n\t{\n\t\tforeach (PBEBattlePokemon p in Party)\n\t\t{\n\t\t\tif (p.Id == pkmnId)\n\t\t\t{\n\t\t\t\tpkmn = p;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tpkmn = null;\n\t\treturn false;\n\t}\n\tpublic PBEBattlePokemon GetPokemon(PBEFieldPosition pos)\n\t{\n\t\tforeach (PBEBattlePokemon p in ActiveBattlers)\n\t\t{\n\t\t\tif (p.FieldPosition == pos)\n\t\t\t{\n\t\t\t\treturn p;\n\t\t\t}\n\t\t}\n\t\tthrow new InvalidOperationException();\n\t}\n\tpublic PBEBattlePokemon GetPokemon(byte pkmnId)\n\t{\n\t\tforeach (PBEBattlePokemon p in Party)\n\t\t{\n\t\t\tif (p.Id == pkmnId)\n\t\t\t{\n\t\t\t\treturn p;\n\t\t\t}\n\t\t}\n\t\tthrow new InvalidOperationException();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Battle/BattleUtils.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data;\nusing System;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Battle;\n\npublic static class PBEBattleUtils\n{\n\tpublic static PBETurnTarget GetSpreadMoveTargets(PBEBattlePokemon pkmn, PBEMoveTarget targets)\n\t{\n\t\tswitch (pkmn.Battle.BattleFormat)\n\t\t{\n\t\t\tcase PBEBattleFormat.Single:\n\t\t\t{\n\t\t\t\tswitch (targets)\n\t\t\t\t{\n\t\t\t\t\tcase PBEMoveTarget.All:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyCenter | PBETurnTarget.FoeCenter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new ArgumentException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllFoes:\n\t\t\t\t\tcase PBEMoveTarget.AllFoesSurrounding:\n\t\t\t\t\tcase PBEMoveTarget.AllSurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.FoeCenter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new ArgumentException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllTeam:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyCenter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new ArgumentException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(targets));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcase PBEBattleFormat.Double:\n\t\t\t{\n\t\t\t\tswitch (targets)\n\t\t\t\t{\n\t\t\t\t\tcase PBEMoveTarget.All:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new ArgumentException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllFoes:\n\t\t\t\t\tcase PBEMoveTarget.AllFoesSurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.FoeLeft | PBETurnTarget.FoeRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new ArgumentException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllTeam:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new ArgumentException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllSurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyLeft | PBETurnTarget.FoeLeft | PBETurnTarget.FoeRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new ArgumentException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(targets));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcase PBEBattleFormat.Triple:\n\t\t\t{\n\t\t\t\tswitch (targets)\n\t\t\t\t{\n\t\t\t\t\tcase PBEMoveTarget.All:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyLeft | PBETurnTarget.AllyCenter | PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new ArgumentException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllFoes:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new ArgumentException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllFoesSurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new ArgumentException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllSurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyCenter | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyCenter | PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new ArgumentException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllTeam:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyLeft | PBETurnTarget.AllyCenter | PBETurnTarget.AllyRight;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new ArgumentException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(targets));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcase PBEBattleFormat.Rotation:\n\t\t\t{\n\t\t\t\tswitch (targets)\n\t\t\t\t{\n\t\t\t\t\tcase PBEMoveTarget.All:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyCenter | PBETurnTarget.FoeCenter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new ArgumentException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllFoes:\n\t\t\t\t\tcase PBEMoveTarget.AllFoesSurrounding:\n\t\t\t\t\tcase PBEMoveTarget.AllSurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.FoeCenter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new ArgumentException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.AllTeam:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn PBETurnTarget.AllyCenter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new ArgumentException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(targets));\n\t\t\t\t}\n\t\t\t}\n\t\t\tdefault: throw new InvalidDataException(nameof(pkmn.Battle.BattleFormat));\n\t\t}\n\t}\n\tpublic static PBETurnTarget[] GetPossibleTargets(PBEBattlePokemon pkmn, PBEMoveTarget targets)\n\t{\n\t\tswitch (pkmn.Battle.BattleFormat)\n\t\t{\n\t\t\tcase PBEBattleFormat.Single:\n\t\t\t{\n\t\t\t\tswitch (targets)\n\t\t\t\t{\n\t\t\t\t\tcase PBEMoveTarget.SingleFoeSurrounding:\n\t\t\t\t\tcase PBEMoveTarget.SingleNotSelf:\n\t\t\t\t\tcase PBEMoveTarget.SingleSurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn new PBETurnTarget[] { PBETurnTarget.FoeCenter };\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new ArgumentException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.RandomFoeSurrounding:\n\t\t\t\t\tcase PBEMoveTarget.Self:\n\t\t\t\t\tcase PBEMoveTarget.SelfOrAllySurrounding:\n\t\t\t\t\tcase PBEMoveTarget.SingleAllySurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn new PBETurnTarget[] { PBETurnTarget.AllyCenter };\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new ArgumentException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(targets));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcase PBEBattleFormat.Double:\n\t\t\t{\n\t\t\t\tswitch (targets)\n\t\t\t\t{\n\t\t\t\t\tcase PBEMoveTarget.RandomFoeSurrounding:\n\t\t\t\t\tcase PBEMoveTarget.Self:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn new PBETurnTarget[] { PBETurnTarget.AllyLeft };\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn new PBETurnTarget[] { PBETurnTarget.AllyRight };\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new ArgumentException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.SelfOrAllySurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn new PBETurnTarget[] { PBETurnTarget.AllyLeft, PBETurnTarget.AllyRight };\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new ArgumentException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.SingleAllySurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn new PBETurnTarget[] { PBETurnTarget.AllyRight };\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn new PBETurnTarget[] { PBETurnTarget.AllyLeft };\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new ArgumentException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.SingleFoeSurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn new PBETurnTarget[] { PBETurnTarget.FoeLeft, PBETurnTarget.FoeRight };\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new ArgumentException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.SingleNotSelf:\n\t\t\t\t\tcase PBEMoveTarget.SingleSurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn new PBETurnTarget[] { PBETurnTarget.AllyRight, PBETurnTarget.FoeLeft, PBETurnTarget.FoeRight };\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn new PBETurnTarget[] { PBETurnTarget.AllyLeft, PBETurnTarget.FoeLeft, PBETurnTarget.FoeRight };\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new ArgumentException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(targets));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcase PBEBattleFormat.Triple:\n\t\t\t{\n\t\t\t\tswitch (targets)\n\t\t\t\t{\n\t\t\t\t\tcase PBEMoveTarget.RandomFoeSurrounding:\n\t\t\t\t\tcase PBEMoveTarget.Self:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn new PBETurnTarget[] { PBETurnTarget.AllyLeft };\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn new PBETurnTarget[] { PBETurnTarget.AllyCenter };\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn new PBETurnTarget[] { PBETurnTarget.AllyRight };\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new ArgumentException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.SelfOrAllySurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn new PBETurnTarget[] { PBETurnTarget.AllyLeft, PBETurnTarget.AllyCenter };\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn new PBETurnTarget[] { PBETurnTarget.AllyLeft, PBETurnTarget.AllyCenter, PBETurnTarget.AllyRight };\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn new PBETurnTarget[] { PBETurnTarget.AllyCenter, PBETurnTarget.AllyRight };\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new ArgumentException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.SingleAllySurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn new PBETurnTarget[] { PBETurnTarget.AllyCenter };\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn new PBETurnTarget[] { PBETurnTarget.AllyLeft, PBETurnTarget.AllyRight };\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new ArgumentException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.SingleFoeSurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn new PBETurnTarget[] { PBETurnTarget.FoeCenter, PBETurnTarget.FoeRight };\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn new PBETurnTarget[] { PBETurnTarget.FoeLeft, PBETurnTarget.FoeCenter, PBETurnTarget.FoeRight };\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn new PBETurnTarget[] { PBETurnTarget.FoeLeft, PBETurnTarget.FoeCenter };\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new ArgumentException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.SingleNotSelf:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn new PBETurnTarget[] { PBETurnTarget.AllyCenter, PBETurnTarget.AllyRight, PBETurnTarget.FoeLeft, PBETurnTarget.FoeCenter, PBETurnTarget.FoeRight };\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn new PBETurnTarget[] { PBETurnTarget.AllyLeft, PBETurnTarget.AllyRight, PBETurnTarget.FoeLeft, PBETurnTarget.FoeCenter, PBETurnTarget.FoeRight };\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn new PBETurnTarget[] { PBETurnTarget.AllyLeft, PBETurnTarget.AllyCenter, PBETurnTarget.FoeLeft, PBETurnTarget.FoeCenter, PBETurnTarget.FoeRight };\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new ArgumentException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.SingleSurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn new PBETurnTarget[] { PBETurnTarget.AllyCenter, PBETurnTarget.FoeCenter, PBETurnTarget.FoeRight };\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn new PBETurnTarget[] { PBETurnTarget.AllyLeft, PBETurnTarget.AllyRight, PBETurnTarget.FoeLeft, PBETurnTarget.FoeCenter, PBETurnTarget.FoeRight };\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn new PBETurnTarget[] { PBETurnTarget.AllyCenter, PBETurnTarget.FoeLeft, PBETurnTarget.FoeCenter };\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new ArgumentException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(targets));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcase PBEBattleFormat.Rotation:\n\t\t\t{\n\t\t\t\tswitch (targets)\n\t\t\t\t{\n\t\t\t\t\tcase PBEMoveTarget.SingleFoeSurrounding:\n\t\t\t\t\tcase PBEMoveTarget.SingleNotSelf:\n\t\t\t\t\tcase PBEMoveTarget.SingleSurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn new PBETurnTarget[] { PBETurnTarget.FoeCenter };\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new ArgumentException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMoveTarget.RandomFoeSurrounding:\n\t\t\t\t\tcase PBEMoveTarget.Self:\n\t\t\t\t\tcase PBEMoveTarget.SelfOrAllySurrounding:\n\t\t\t\t\tcase PBEMoveTarget.SingleAllySurrounding:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn new PBETurnTarget[] { PBETurnTarget.AllyCenter };\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new ArgumentException(nameof(pkmn.FieldPosition));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(targets));\n\t\t\t\t}\n\t\t\t}\n\t\t\tdefault: throw new InvalidDataException(nameof(pkmn.Battle.BattleFormat));\n\t\t}\n\t}\n\n\tinternal static void VerifyPosition(PBEBattleFormat format, PBEFieldPosition pos)\n\t{\n\t\tif (pos != PBEFieldPosition.None && pos < PBEFieldPosition.MAX)\n\t\t{\n\t\t\tswitch (format)\n\t\t\t{\n\t\t\t\tcase PBEBattleFormat.Single:\n\t\t\t\t{\n\t\t\t\t\tswitch (pos)\n\t\t\t\t\t{\n\t\t\t\t\t\tcase PBEFieldPosition.Center: return;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEBattleFormat.Double:\n\t\t\t\t{\n\t\t\t\t\tswitch (pos)\n\t\t\t\t\t{\n\t\t\t\t\t\tcase PBEFieldPosition.Left:\n\t\t\t\t\t\tcase PBEFieldPosition.Right: return;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEBattleFormat.Triple:\n\t\t\t\tcase PBEBattleFormat.Rotation:\n\t\t\t\t{\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthrow new ArgumentOutOfRangeException(nameof(pos));\n\t}\n\n\tpublic static int GetFieldPositionIndex(this PBETrainer trainer, PBEFieldPosition position)\n\t{\n\t\tif (!trainer.OwnsSpot(position))\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(position));\n\t\t}\n\t\tPBEBattleFormat battleFormat = trainer.Battle.BattleFormat;\n\t\tint index = trainer.Team.Trainers.IndexOf(trainer);\n\t\tswitch (battleFormat)\n\t\t{\n\t\t\tcase PBEBattleFormat.Single:\n\t\t\t{\n\t\t\t\tswitch (position)\n\t\t\t\t{\n\t\t\t\t\tcase PBEFieldPosition.Center: return 0;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEBattleFormat.Double:\n\t\t\t{\n\t\t\t\tswitch (position)\n\t\t\t\t{\n\t\t\t\t\tcase PBEFieldPosition.Left: return 0;\n\t\t\t\t\tcase PBEFieldPosition.Right: return index == 1 ? 0 : 1;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEBattleFormat.Triple:\n\t\t\t{\n\t\t\t\tswitch (position)\n\t\t\t\t{\n\t\t\t\t\tcase PBEFieldPosition.Left: return 0;\n\t\t\t\t\tcase PBEFieldPosition.Center: return index == 1 ? 0 : 1;\n\t\t\t\t\tcase PBEFieldPosition.Right: return index == 2 ? 0 : 2;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEBattleFormat.Rotation:\n\t\t\t{\n\t\t\t\tswitch (position)\n\t\t\t\t{\n\t\t\t\t\tcase PBEFieldPosition.Center: return 0;\n\t\t\t\t\tcase PBEFieldPosition.Left: return 1;\n\t\t\t\t\tcase PBEFieldPosition.Right: return 2;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tthrow new Exception();\n\t}\n\tpublic static bool OwnsSpot(this PBETrainer trainer, PBEFieldPosition pos)\n\t{\n\t\treturn GetTrainer(trainer.Team, pos) == trainer;\n\t}\n\tpublic static PBETrainer GetTrainer(this PBETeam team, PBEFieldPosition pos)\n\t{\n\t\tPBEBattleFormat format = team.Battle.BattleFormat;\n\t\tVerifyPosition(format, pos);\n\t\tint i = 0;\n\t\tif (team.Trainers.Count != 1)\n\t\t{\n\t\t\tswitch (format)\n\t\t\t{\n\t\t\t\tcase PBEBattleFormat.Double: i = pos == PBEFieldPosition.Left ? 0 : 1; break;\n\t\t\t\tcase PBEBattleFormat.Triple: i = pos == PBEFieldPosition.Left ? 0 : pos == PBEFieldPosition.Center ? 1 : 2; break;\n\t\t\t}\n\t\t}\n\t\treturn team.Trainers[i];\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Battle/TrainerInfo.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Data.Legality;\nusing Kermalis.PokemonBattleEngine.Utils;\nusing System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Linq;\n\nnamespace Kermalis.PokemonBattleEngine.Battle;\n\npublic abstract class PBETrainerInfoBase\n{\n\tpublic ReadOnlyCollection<IPBEPokemon> Party { get; }\n\tprivate readonly PBESettings? _requiredSettings;\n\n\tprotected PBETrainerInfoBase(IPBEPokemonCollection party)\n\t{\n\t\tif (party is IPBEPartyPokemonCollection ppc)\n\t\t{\n\t\t\tif (!ppc.Any(p => p.HP > 0 && !p.PBEIgnore))\n\t\t\t{\n\t\t\t\tthrow new ArgumentException(\"Party must have at least 1 conscious battler\", nameof(party));\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (!party.Any(p => !p.PBEIgnore))\n\t\t\t{\n\t\t\t\tthrow new ArgumentException(\"Party must have at least 1 conscious battler\", nameof(party));\n\t\t\t}\n\t\t}\n\t\tif (party is PBELegalPokemonCollection lp)\n\t\t{\n\t\t\t_requiredSettings = lp.Settings;\n\t\t}\n\t\tParty = new ReadOnlyCollection<IPBEPokemon>(party.ToArray());\n\t}\n\n\tpublic bool IsOkayForSettings(PBESettings settings)\n\t{\n\t\tsettings.ShouldBeReadOnly(nameof(settings));\n\t\tif (_requiredSettings is not null)\n\t\t{\n\t\t\treturn settings.Equals(_requiredSettings);\n\t\t}\n\t\tif (this is PBETrainerInfo ti && ti.GainsEXP && settings.MaxLevel > 100)\n\t\t{\n\t\t\tthrow new ArgumentException(\"Cannot start a battle with EXP enabled and a higher MaxLevel than 100. Not supported.\");\n\t\t}\n\t\treturn true;\n\t}\n}\npublic sealed class PBETrainerInfo : PBETrainerInfoBase\n{\n\tpublic string Name { get; }\n\tpublic bool GainsEXP { get; }\n\tpublic ReadOnlyCollection<(PBEItem Item, uint Quantity)> Inventory { get; }\n\n\tpublic PBETrainerInfo(IPBEPokemonCollection party, string name, bool gainsEXP, IList<(PBEItem Item, uint Quantity)>? inventory = null)\n\t\t: base(party)\n\t{\n\t\tif (string.IsNullOrWhiteSpace(name))\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(name));\n\t\t}\n\t\tName = name;\n\t\tGainsEXP = gainsEXP;\n\t\tif (inventory is null || inventory.Count == 0)\n\t\t{\n\t\t\tInventory = PBEEmptyReadOnlyCollection<(PBEItem, uint)>.Value;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tInventory = new ReadOnlyCollection<(PBEItem, uint)>(inventory);\n\t\t}\n\t}\n}\npublic sealed class PBEWildInfo : PBETrainerInfoBase\n{\n\tpublic PBEWildInfo(IPBEPokemonCollection party)\n\t\t: base(party) { }\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Battle/TypeEffectiveness.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data;\nusing System;\nusing System.Collections.Generic;\n\nnamespace Kermalis.PokemonBattleEngine.Battle;\n\npublic static class PBETypeEffectiveness\n{\n\t#region Static Collections\n\t/// <summary>The type effectiveness table. The first key is the attacking type and the second key is the defending type.</summary>\n\tprivate static readonly Dictionary<PBEType, Dictionary<PBEType, float>> _table = new()\n\t\t{\n\t\t\t{\n\t\t\t\tPBEType.None,\n\t\t\t\tnew Dictionary<PBEType, float>\n\t\t\t{\n\t\t\t\t{ PBEType.None, 1.0f },\n\t\t\t\t{ PBEType.Bug, 1.0f },\n\t\t\t\t{ PBEType.Dark, 1.0f },\n\t\t\t\t{ PBEType.Dragon, 1.0f },\n\t\t\t\t{ PBEType.Electric, 1.0f },\n\t\t\t\t{ PBEType.Fighting, 1.0f },\n\t\t\t\t{ PBEType.Fire, 1.0f },\n\t\t\t\t{ PBEType.Flying, 1.0f },\n\t\t\t\t{ PBEType.Ghost, 1.0f },\n\t\t\t\t{ PBEType.Grass, 1.0f },\n\t\t\t\t{ PBEType.Ground, 1.0f },\n\t\t\t\t{ PBEType.Ice, 1.0f },\n\t\t\t\t{ PBEType.Normal, 1.0f },\n\t\t\t\t{ PBEType.Poison, 1.0f },\n\t\t\t\t{ PBEType.Psychic, 1.0f },\n\t\t\t\t{ PBEType.Rock, 1.0f },\n\t\t\t\t{ PBEType.Steel, 1.0f },\n\t\t\t\t{ PBEType.Water, 1.0f },\n\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\tPBEType.Bug,\n\t\t\t\tnew Dictionary<PBEType, float>\n\t\t\t{\n\t\t\t\t{ PBEType.None, 1.0f },\n\t\t\t\t{ PBEType.Bug, 1.0f },\n\t\t\t\t{ PBEType.Dark, 2.0f },\n\t\t\t\t{ PBEType.Dragon, 1.0f },\n\t\t\t\t{ PBEType.Electric, 1.0f },\n\t\t\t\t{ PBEType.Fighting, 0.5f },\n\t\t\t\t{ PBEType.Fire, 0.5f },\n\t\t\t\t{ PBEType.Flying, 0.5f },\n\t\t\t\t{ PBEType.Ghost, 0.5f },\n\t\t\t\t{ PBEType.Grass, 2.0f },\n\t\t\t\t{ PBEType.Ground, 1.0f },\n\t\t\t\t{ PBEType.Ice, 1.0f },\n\t\t\t\t{ PBEType.Normal, 1.0f },\n\t\t\t\t{ PBEType.Poison, 0.5f },\n\t\t\t\t{ PBEType.Psychic, 2.0f },\n\t\t\t\t{ PBEType.Rock, 1.0f },\n\t\t\t\t{ PBEType.Steel, 0.5f },\n\t\t\t\t{ PBEType.Water, 1.0f },\n\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\tPBEType.Dark,\n\t\t\t\tnew Dictionary<PBEType, float>\n\t\t\t{\n\t\t\t\t{ PBEType.None, 1.0f },\n\t\t\t\t{ PBEType.Bug, 1.0f },\n\t\t\t\t{ PBEType.Dark, 0.5f },\n\t\t\t\t{ PBEType.Dragon, 1.0f },\n\t\t\t\t{ PBEType.Electric, 1.0f },\n\t\t\t\t{ PBEType.Fighting, 0.5f },\n\t\t\t\t{ PBEType.Fire, 1.0f },\n\t\t\t\t{ PBEType.Flying, 1.0f },\n\t\t\t\t{ PBEType.Ghost, 2.0f },\n\t\t\t\t{ PBEType.Grass, 1.0f },\n\t\t\t\t{ PBEType.Ground, 1.0f },\n\t\t\t\t{ PBEType.Ice, 1.0f },\n\t\t\t\t{ PBEType.Normal, 1.0f },\n\t\t\t\t{ PBEType.Poison, 1.0f },\n\t\t\t\t{ PBEType.Psychic, 2.0f },\n\t\t\t\t{ PBEType.Rock, 1.0f },\n\t\t\t\t{ PBEType.Steel, 0.5f },\n\t\t\t\t{ PBEType.Water, 1.0f },\n\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\tPBEType.Dragon,\n\t\t\t\tnew Dictionary<PBEType, float>\n\t\t\t{\n\t\t\t\t{ PBEType.None, 1.0f },\n\t\t\t\t{ PBEType.Bug, 1.0f },\n\t\t\t\t{ PBEType.Dark, 1.0f },\n\t\t\t\t{ PBEType.Dragon, 2.0f },\n\t\t\t\t{ PBEType.Electric, 1.0f },\n\t\t\t\t{ PBEType.Fighting, 1.0f },\n\t\t\t\t{ PBEType.Fire, 1.0f },\n\t\t\t\t{ PBEType.Flying, 1.0f },\n\t\t\t\t{ PBEType.Ghost, 1.0f },\n\t\t\t\t{ PBEType.Grass, 1.0f },\n\t\t\t\t{ PBEType.Ground, 1.0f },\n\t\t\t\t{ PBEType.Ice, 1.0f },\n\t\t\t\t{ PBEType.Normal, 1.0f },\n\t\t\t\t{ PBEType.Poison, 1.0f },\n\t\t\t\t{ PBEType.Psychic, 1.0f },\n\t\t\t\t{ PBEType.Rock, 1.0f },\n\t\t\t\t{ PBEType.Steel, 0.5f },\n\t\t\t\t{ PBEType.Water, 1.0f },\n\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\tPBEType.Electric,\n\t\t\t\tnew Dictionary<PBEType, float>\n\t\t\t{\n\t\t\t\t{ PBEType.None, 1.0f },\n\t\t\t\t{ PBEType.Bug, 1.0f },\n\t\t\t\t{ PBEType.Dark, 1.0f },\n\t\t\t\t{ PBEType.Dragon, 0.5f },\n\t\t\t\t{ PBEType.Electric, 0.5f },\n\t\t\t\t{ PBEType.Fighting, 1.0f },\n\t\t\t\t{ PBEType.Fire, 1.0f },\n\t\t\t\t{ PBEType.Flying, 2.0f },\n\t\t\t\t{ PBEType.Ghost, 1.0f },\n\t\t\t\t{ PBEType.Grass, 0.5f },\n\t\t\t\t{ PBEType.Ground, 0.0f },\n\t\t\t\t{ PBEType.Ice, 1.0f },\n\t\t\t\t{ PBEType.Normal, 1.0f },\n\t\t\t\t{ PBEType.Poison, 1.0f },\n\t\t\t\t{ PBEType.Psychic, 1.0f },\n\t\t\t\t{ PBEType.Rock, 1.0f },\n\t\t\t\t{ PBEType.Steel, 1.0f },\n\t\t\t\t{ PBEType.Water, 2.0f },\n\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\tPBEType.Fighting,\n\t\t\t\tnew Dictionary<PBEType, float>\n\t\t\t{\n\t\t\t\t{ PBEType.None, 1.0f },\n\t\t\t\t{ PBEType.Bug, 0.5f },\n\t\t\t\t{ PBEType.Dark, 2.0f },\n\t\t\t\t{ PBEType.Dragon, 1.0f },\n\t\t\t\t{ PBEType.Electric, 1.0f },\n\t\t\t\t{ PBEType.Fighting, 1.0f },\n\t\t\t\t{ PBEType.Fire, 1.0f },\n\t\t\t\t{ PBEType.Flying, 0.5f },\n\t\t\t\t{ PBEType.Ghost, 0.0f },\n\t\t\t\t{ PBEType.Grass, 1.0f },\n\t\t\t\t{ PBEType.Ground, 1.0f },\n\t\t\t\t{ PBEType.Ice, 2.0f },\n\t\t\t\t{ PBEType.Normal, 2.0f },\n\t\t\t\t{ PBEType.Poison, 0.5f },\n\t\t\t\t{ PBEType.Psychic, 0.5f },\n\t\t\t\t{ PBEType.Rock, 2.0f },\n\t\t\t\t{ PBEType.Steel, 2.0f },\n\t\t\t\t{ PBEType.Water, 1.0f },\n\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\tPBEType.Fire,\n\t\t\t\tnew Dictionary<PBEType, float>\n\t\t\t{\n\t\t\t\t{ PBEType.None, 1.0f },\n\t\t\t\t{ PBEType.Bug, 2.0f },\n\t\t\t\t{ PBEType.Dark, 1.0f },\n\t\t\t\t{ PBEType.Dragon, 0.5f },\n\t\t\t\t{ PBEType.Electric, 1.0f },\n\t\t\t\t{ PBEType.Fighting, 1.0f },\n\t\t\t\t{ PBEType.Fire, 0.5f },\n\t\t\t\t{ PBEType.Flying, 1.0f },\n\t\t\t\t{ PBEType.Ghost, 1.0f },\n\t\t\t\t{ PBEType.Grass, 2.0f },\n\t\t\t\t{ PBEType.Ground, 1.0f },\n\t\t\t\t{ PBEType.Ice, 2.0f },\n\t\t\t\t{ PBEType.Normal, 1.0f },\n\t\t\t\t{ PBEType.Poison, 1.0f },\n\t\t\t\t{ PBEType.Psychic, 1.0f },\n\t\t\t\t{ PBEType.Rock, 0.5f },\n\t\t\t\t{ PBEType.Steel, 2.0f },\n\t\t\t\t{ PBEType.Water, 0.5f },\n\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\tPBEType.Flying,\n\t\t\t\tnew Dictionary<PBEType, float>\n\t\t\t{\n\t\t\t\t{ PBEType.None, 1.0f },\n\t\t\t\t{ PBEType.Bug, 2.0f },\n\t\t\t\t{ PBEType.Dark, 1.0f },\n\t\t\t\t{ PBEType.Dragon, 1.0f },\n\t\t\t\t{ PBEType.Electric, 0.5f },\n\t\t\t\t{ PBEType.Fighting, 2.0f },\n\t\t\t\t{ PBEType.Fire, 1.0f },\n\t\t\t\t{ PBEType.Flying, 1.0f },\n\t\t\t\t{ PBEType.Ghost, 1.0f },\n\t\t\t\t{ PBEType.Grass, 2.0f },\n\t\t\t\t{ PBEType.Ground, 1.0f },\n\t\t\t\t{ PBEType.Ice, 1.0f },\n\t\t\t\t{ PBEType.Normal, 1.0f },\n\t\t\t\t{ PBEType.Poison, 1.0f },\n\t\t\t\t{ PBEType.Psychic, 1.0f },\n\t\t\t\t{ PBEType.Rock, 0.5f },\n\t\t\t\t{ PBEType.Steel, 0.5f },\n\t\t\t\t{ PBEType.Water, 1.0f },\n\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\tPBEType.Ghost,\n\t\t\t\tnew Dictionary<PBEType, float>\n\t\t\t{\n\t\t\t\t{ PBEType.None, 1.0f },\n\t\t\t\t{ PBEType.Bug, 1.0f },\n\t\t\t\t{ PBEType.Dark, 0.5f },\n\t\t\t\t{ PBEType.Dragon, 1.0f },\n\t\t\t\t{ PBEType.Electric, 1.0f },\n\t\t\t\t{ PBEType.Fighting, 1.0f },\n\t\t\t\t{ PBEType.Fire, 1.0f },\n\t\t\t\t{ PBEType.Flying, 1.0f },\n\t\t\t\t{ PBEType.Ghost, 2.0f },\n\t\t\t\t{ PBEType.Grass, 1.0f },\n\t\t\t\t{ PBEType.Ground, 1.0f },\n\t\t\t\t{ PBEType.Ice, 1.0f },\n\t\t\t\t{ PBEType.Normal, 0.0f },\n\t\t\t\t{ PBEType.Poison, 1.0f },\n\t\t\t\t{ PBEType.Psychic, 2.0f },\n\t\t\t\t{ PBEType.Rock, 1.0f },\n\t\t\t\t{ PBEType.Steel, 0.5f },\n\t\t\t\t{ PBEType.Water, 1.0f },\n\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\tPBEType.Grass,\n\t\t\t\tnew Dictionary<PBEType, float>\n\t\t\t{\n\t\t\t\t{ PBEType.None, 1.0f },\n\t\t\t\t{ PBEType.Bug, 0.5f },\n\t\t\t\t{ PBEType.Dark, 1.0f },\n\t\t\t\t{ PBEType.Dragon, 0.5f },\n\t\t\t\t{ PBEType.Electric, 1.0f },\n\t\t\t\t{ PBEType.Fighting, 1.0f },\n\t\t\t\t{ PBEType.Fire, 0.5f },\n\t\t\t\t{ PBEType.Flying, 0.5f },\n\t\t\t\t{ PBEType.Ghost, 1.0f },\n\t\t\t\t{ PBEType.Grass, 0.5f },\n\t\t\t\t{ PBEType.Ground, 2.0f },\n\t\t\t\t{ PBEType.Ice, 1.0f },\n\t\t\t\t{ PBEType.Normal, 1.0f },\n\t\t\t\t{ PBEType.Poison, 0.5f },\n\t\t\t\t{ PBEType.Psychic, 1.0f },\n\t\t\t\t{ PBEType.Rock, 2.0f },\n\t\t\t\t{ PBEType.Steel, 0.5f },\n\t\t\t\t{ PBEType.Water, 2.0f },\n\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\tPBEType.Ground,\n\t\t\t\tnew Dictionary<PBEType, float>\n\t\t\t{\n\t\t\t\t{ PBEType.None, 1.0f },\n\t\t\t\t{ PBEType.Bug, 0.5f },\n\t\t\t\t{ PBEType.Dark, 1.0f },\n\t\t\t\t{ PBEType.Dragon, 1.0f },\n\t\t\t\t{ PBEType.Electric, 2.0f },\n\t\t\t\t{ PBEType.Fighting, 1.0f },\n\t\t\t\t{ PBEType.Fire, 2.0f },\n\t\t\t\t{ PBEType.Flying, 0.0f },\n\t\t\t\t{ PBEType.Ghost, 1.0f },\n\t\t\t\t{ PBEType.Grass, 0.5f },\n\t\t\t\t{ PBEType.Ground, 1.0f },\n\t\t\t\t{ PBEType.Ice, 1.0f },\n\t\t\t\t{ PBEType.Normal, 1.0f },\n\t\t\t\t{ PBEType.Poison, 2.0f },\n\t\t\t\t{ PBEType.Psychic, 1.0f },\n\t\t\t\t{ PBEType.Rock, 2.0f },\n\t\t\t\t{ PBEType.Steel, 2.0f },\n\t\t\t\t{ PBEType.Water, 1.0f },\n\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\tPBEType.Ice,\n\t\t\t\tnew Dictionary<PBEType, float>\n\t\t\t{\n\t\t\t\t{ PBEType.None, 1.0f },\n\t\t\t\t{ PBEType.Bug, 1.0f },\n\t\t\t\t{ PBEType.Dark, 1.0f },\n\t\t\t\t{ PBEType.Dragon, 2.0f },\n\t\t\t\t{ PBEType.Electric, 1.0f },\n\t\t\t\t{ PBEType.Fighting, 1.0f },\n\t\t\t\t{ PBEType.Fire, 0.5f },\n\t\t\t\t{ PBEType.Flying, 2.0f },\n\t\t\t\t{ PBEType.Ghost, 1.0f },\n\t\t\t\t{ PBEType.Grass, 2.0f },\n\t\t\t\t{ PBEType.Ground, 2.0f },\n\t\t\t\t{ PBEType.Ice, 0.5f },\n\t\t\t\t{ PBEType.Normal, 1.0f },\n\t\t\t\t{ PBEType.Poison, 1.0f },\n\t\t\t\t{ PBEType.Psychic, 1.0f },\n\t\t\t\t{ PBEType.Rock, 1.0f },\n\t\t\t\t{ PBEType.Steel, 0.5f },\n\t\t\t\t{ PBEType.Water, 0.5f },\n\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\tPBEType.Normal,\n\t\t\t\tnew Dictionary<PBEType, float>\n\t\t\t{\n\t\t\t\t{ PBEType.None, 1.0f },\n\t\t\t\t{ PBEType.Bug, 1.0f },\n\t\t\t\t{ PBEType.Dark, 1.0f },\n\t\t\t\t{ PBEType.Dragon, 1.0f },\n\t\t\t\t{ PBEType.Electric, 1.0f },\n\t\t\t\t{ PBEType.Fighting, 1.0f },\n\t\t\t\t{ PBEType.Fire, 1.0f },\n\t\t\t\t{ PBEType.Flying, 1.0f },\n\t\t\t\t{ PBEType.Ghost, 0.0f },\n\t\t\t\t{ PBEType.Grass, 1.0f },\n\t\t\t\t{ PBEType.Ground, 1.0f },\n\t\t\t\t{ PBEType.Ice, 1.0f },\n\t\t\t\t{ PBEType.Normal, 1.0f },\n\t\t\t\t{ PBEType.Poison, 1.0f },\n\t\t\t\t{ PBEType.Psychic, 1.0f },\n\t\t\t\t{ PBEType.Rock, 0.5f },\n\t\t\t\t{ PBEType.Steel, 0.5f },\n\t\t\t\t{ PBEType.Water, 1.0f },\n\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\tPBEType.Poison,\n\t\t\t\tnew Dictionary<PBEType, float>\n\t\t\t{\n\t\t\t\t{ PBEType.None, 1.0f },\n\t\t\t\t{ PBEType.Bug, 1.0f },\n\t\t\t\t{ PBEType.Dark, 1.0f },\n\t\t\t\t{ PBEType.Dragon, 1.0f },\n\t\t\t\t{ PBEType.Electric, 1.0f },\n\t\t\t\t{ PBEType.Fighting, 1.0f },\n\t\t\t\t{ PBEType.Fire, 1.0f },\n\t\t\t\t{ PBEType.Flying, 1.0f },\n\t\t\t\t{ PBEType.Ghost, 0.5f },\n\t\t\t\t{ PBEType.Grass, 2.0f },\n\t\t\t\t{ PBEType.Ground, 0.5f },\n\t\t\t\t{ PBEType.Ice, 1.0f },\n\t\t\t\t{ PBEType.Normal, 1.0f },\n\t\t\t\t{ PBEType.Poison, 0.5f },\n\t\t\t\t{ PBEType.Psychic, 1.0f },\n\t\t\t\t{ PBEType.Rock, 0.5f },\n\t\t\t\t{ PBEType.Steel, 0.0f },\n\t\t\t\t{ PBEType.Water, 1.0f },\n\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\tPBEType.Psychic,\n\t\t\t\tnew Dictionary<PBEType, float>\n\t\t\t{\n\t\t\t\t{ PBEType.None, 1.0f },\n\t\t\t\t{ PBEType.Bug, 1.0f },\n\t\t\t\t{ PBEType.Dark, 0.0f },\n\t\t\t\t{ PBEType.Dragon, 1.0f },\n\t\t\t\t{ PBEType.Electric, 1.0f },\n\t\t\t\t{ PBEType.Fighting, 2.0f },\n\t\t\t\t{ PBEType.Fire, 1.0f },\n\t\t\t\t{ PBEType.Flying, 1.0f },\n\t\t\t\t{ PBEType.Ghost, 1.0f },\n\t\t\t\t{ PBEType.Grass, 1.0f },\n\t\t\t\t{ PBEType.Ground, 1.0f },\n\t\t\t\t{ PBEType.Ice, 1.0f },\n\t\t\t\t{ PBEType.Normal, 1.0f },\n\t\t\t\t{ PBEType.Poison, 2.0f },\n\t\t\t\t{ PBEType.Psychic, 0.5f },\n\t\t\t\t{ PBEType.Rock, 1.0f },\n\t\t\t\t{ PBEType.Steel, 0.5f },\n\t\t\t\t{ PBEType.Water, 1.0f },\n\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\tPBEType.Rock,\n\t\t\t\tnew Dictionary<PBEType, float>\n\t\t\t{\n\t\t\t\t{ PBEType.None, 1.0f },\n\t\t\t\t{ PBEType.Bug, 2.0f },\n\t\t\t\t{ PBEType.Dark, 1.0f },\n\t\t\t\t{ PBEType.Dragon, 1.0f },\n\t\t\t\t{ PBEType.Electric, 1.0f },\n\t\t\t\t{ PBEType.Fighting, 0.5f },\n\t\t\t\t{ PBEType.Fire, 2.0f },\n\t\t\t\t{ PBEType.Flying, 2.0f },\n\t\t\t\t{ PBEType.Ghost, 1.0f },\n\t\t\t\t{ PBEType.Grass, 1.0f },\n\t\t\t\t{ PBEType.Ground, 0.5f },\n\t\t\t\t{ PBEType.Ice, 2.0f },\n\t\t\t\t{ PBEType.Normal, 1.0f },\n\t\t\t\t{ PBEType.Poison, 1.0f },\n\t\t\t\t{ PBEType.Psychic, 1.0f },\n\t\t\t\t{ PBEType.Rock, 1.0f },\n\t\t\t\t{ PBEType.Steel, 0.5f },\n\t\t\t\t{ PBEType.Water, 1.0f },\n\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\tPBEType.Steel,\n\t\t\t\tnew Dictionary<PBEType, float>\n\t\t\t{\n\t\t\t\t{ PBEType.None, 1.0f },\n\t\t\t\t{ PBEType.Bug, 1.0f },\n\t\t\t\t{ PBEType.Dark, 1.0f },\n\t\t\t\t{ PBEType.Dragon, 1.0f },\n\t\t\t\t{ PBEType.Electric, 0.5f },\n\t\t\t\t{ PBEType.Fighting, 1.0f },\n\t\t\t\t{ PBEType.Fire, 0.5f },\n\t\t\t\t{ PBEType.Flying, 1.0f },\n\t\t\t\t{ PBEType.Ghost, 1.0f },\n\t\t\t\t{ PBEType.Grass, 1.0f },\n\t\t\t\t{ PBEType.Ground, 1.0f },\n\t\t\t\t{ PBEType.Ice, 2.0f },\n\t\t\t\t{ PBEType.Normal, 1.0f },\n\t\t\t\t{ PBEType.Poison, 1.0f },\n\t\t\t\t{ PBEType.Psychic, 1.0f },\n\t\t\t\t{ PBEType.Rock, 2.0f },\n\t\t\t\t{ PBEType.Steel, 0.5f },\n\t\t\t\t{ PBEType.Water, 0.5f },\n\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\tPBEType.Water,\n\t\t\t\tnew Dictionary<PBEType, float>\n\t\t\t{\n\t\t\t\t{ PBEType.None, 1.0f },\n\t\t\t\t{ PBEType.Bug, 1.0f },\n\t\t\t\t{ PBEType.Dark, 1.0f },\n\t\t\t\t{ PBEType.Dragon, 0.5f },\n\t\t\t\t{ PBEType.Electric, 1.0f },\n\t\t\t\t{ PBEType.Fighting, 1.0f },\n\t\t\t\t{ PBEType.Fire, 2.0f },\n\t\t\t\t{ PBEType.Flying, 1.0f },\n\t\t\t\t{ PBEType.Ghost, 1.0f },\n\t\t\t\t{ PBEType.Grass, 0.5f },\n\t\t\t\t{ PBEType.Ground, 2.0f },\n\t\t\t\t{ PBEType.Ice, 1.0f },\n\t\t\t\t{ PBEType.Normal, 1.0f },\n\t\t\t\t{ PBEType.Poison, 1.0f },\n\t\t\t\t{ PBEType.Psychic, 1.0f },\n\t\t\t\t{ PBEType.Rock, 2.0f },\n\t\t\t\t{ PBEType.Steel, 1.0f },\n\t\t\t\t{ PBEType.Water, 0.5f },\n\t\t\t}\n\t\t\t}\n\t\t};\n\t#endregion\n\n\tpublic static PBEResult IsAffectedByAttack(PBEBattlePokemon user, PBEBattlePokemon target, PBEType moveType, out float damageMultiplier, bool useKnownInfo = false)\n\t{\n\t\tif (moveType >= PBEType.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(moveType));\n\t\t}\n\n\t\tPBEResult result;\n\t\tif (moveType == PBEType.Ground)\n\t\t{\n\t\t\tresult = target.IsGrounded(user, useKnownInfo: useKnownInfo);\n\t\t\tif (result != PBEResult.Success)\n\t\t\t{\n\t\t\t\tdamageMultiplier = 0;\n\t\t\t\treturn result;\n\t\t\t}\n\t\t}\n\t\tbool ignoreGhost = user.Ability == PBEAbility.Scrappy || target.Status2.HasFlag(PBEStatus2.Identified),\n\t\t\t\tignoreDark = target.Status2.HasFlag(PBEStatus2.MiracleEye);\n\t\tdamageMultiplier = GetEffectiveness(moveType, target, useKnownInfo, ignoreGhost: ignoreGhost, ignoreDark: ignoreDark);\n\t\tif (damageMultiplier <= 0) // (-infinity, 0]\n\t\t{\n\t\t\tdamageMultiplier = 0;\n\t\t\treturn PBEResult.Ineffective_Type;\n\t\t}\n\t\telse if (damageMultiplier < 1) // (0, 1)\n\t\t{\n\t\t\tresult = PBEResult.NotVeryEffective_Type;\n\t\t}\n\t\telse if (damageMultiplier == 1) // [1, 1]\n\t\t{\n\t\t\tresult = PBEResult.Success;\n\t\t}\n\t\telse // (1, infinity)\n\t\t{\n\t\t\treturn PBEResult.SuperEffective_Type;\n\t\t}\n\t\tPBEAbility kAbility = useKnownInfo ? target.KnownAbility : target.Ability;\n\t\tif (kAbility == PBEAbility.WonderGuard && !user.HasCancellingAbility())\n\t\t{\n\t\t\tdamageMultiplier = 0;\n\t\t\tresult = PBEResult.Ineffective_Ability;\n\t\t}\n\t\treturn result;\n\t}\n\t/// <summary>Checks if <see cref=\"PBEMoveEffect.ThunderWave\"/>'s type affects the target, taking into account <see cref=\"PBEAbility.Normalize\"/>.</summary>\n\tpublic static PBEResult ThunderWaveTypeCheck(PBEBattlePokemon user, PBEBattlePokemon target, PBEMove move, bool useKnownInfo = false)\n\t{\n\t\tPBEType moveType = user.GetMoveType(move);\n\t\tfloat d = GetEffectiveness(moveType, target, useKnownInfo);\n\t\tif (d <= 0)\n\t\t{\n\t\t\treturn PBEResult.Ineffective_Type;\n\t\t}\n\t\treturn PBEResult.Success;\n\t}\n\n\tpublic static float GetEffectiveness(PBEType attackingType, PBEType defendingType, bool ignoreGhost = false, bool ignoreDark = false)\n\t{\n\t\tif (attackingType >= PBEType.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(attackingType));\n\t\t}\n\t\tif (defendingType >= PBEType.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(defendingType));\n\t\t}\n\n\t\tfloat d = _table[attackingType][defendingType];\n\t\tif (d <= 0 && ((ignoreGhost && defendingType == PBEType.Ghost) || (ignoreDark && defendingType == PBEType.Dark)))\n\t\t{\n\t\t\treturn 1;\n\t\t}\n\t\treturn d;\n\t}\n\tpublic static float GetEffectiveness(PBEType attackingType, PBEType defendingType1, PBEType defendingType2, bool ignoreGhost = false, bool ignoreDark = false)\n\t{\n\t\tfloat d = GetEffectiveness(attackingType, defendingType1, ignoreGhost: ignoreGhost, ignoreDark: ignoreDark);\n\t\td *= GetEffectiveness(attackingType, defendingType2, ignoreGhost: ignoreGhost, ignoreDark: ignoreDark);\n\t\treturn d;\n\t}\n\tpublic static float GetEffectiveness(PBEType attackingType, IPBEPokemonTypes defendingTypes, bool ignoreGhost = false, bool ignoreDark = false)\n\t{\n\t\treturn GetEffectiveness(attackingType, defendingTypes.Type1, defendingTypes.Type2, ignoreGhost: ignoreGhost, ignoreDark: ignoreDark);\n\t}\n\tpublic static float GetEffectiveness_Known(PBEType attackingType, IPBEPokemonKnownTypes defendingTypes, bool ignoreGhost = false, bool ignoreDark = false)\n\t{\n\t\treturn GetEffectiveness(attackingType, defendingTypes.KnownType1, defendingTypes.KnownType2, ignoreGhost: ignoreGhost, ignoreDark: ignoreDark);\n\t}\n\tpublic static float GetEffectiveness<T>(PBEType attackingType, T defendingTypes, bool useKnownInfo, bool ignoreGhost = false, bool ignoreDark = false)\n\t\twhere T : IPBEPokemonTypes, IPBEPokemonKnownTypes\n\t{\n\t\treturn GetEffectiveness(attackingType, useKnownInfo ? defendingTypes.KnownType1 : defendingTypes.Type1, useKnownInfo ? defendingTypes.KnownType2 : defendingTypes.Type2, ignoreGhost: ignoreGhost, ignoreDark: ignoreDark);\n\t}\n\tpublic static float GetStealthRockMultiplier(PBEType type1, PBEType type2)\n\t{\n\t\tif (type1 >= PBEType.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(type1));\n\t\t}\n\t\tif (type2 >= PBEType.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(type2));\n\t\t}\n\t\tfloat d = 0.125f;\n\t\td *= _table[PBEType.Rock][type1];\n\t\td *= _table[PBEType.Rock][type2];\n\t\treturn d;\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Data/DataEnums.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing System;\n\nnamespace Kermalis.PokemonBattleEngine.Data;\n\n/// <summary>Represents a language the engine supports.</summary>\npublic enum PBELanguage : byte\n{\n\tEnglish,\n\tFrench,\n\tGerman,\n\tItalian,\n\tJapanese_Kana,\n\tJapanese_Kanji,\n\tKorean,\n\tSpanish,\n\tMAX\n}\n/// <summary>Represents a specific Pokémon's gender.</summary>\npublic enum PBEGender : byte\n{\n\t/// <summary>The Pokémon is female.</summary>\n\tFemale,\n\t/// <summary>The Pokémon is genderless.</summary>\n\tGenderless,\n\t/// <summary>The Pokémon is male.</summary>\n\tMale,\n\t/// <summary>Invalid gender.</summary>\n\tMAX\n}\n/// <summary>Represents a Pokémon species' <see cref=\"PBEGender\"/> ratio.</summary>\npublic enum PBEGenderRatio : byte\n{\n\t/// <summary>The species is 87.5% male, 12.5% female.</summary>\n\tM7_F1 = 0x1F,\n\t/// <summary>The species is 75% male, 25% female.</summary>\n\tM3_F1 = 0x3F,\n\t/// <summary>The species is 50% male, 50% female.</summary>\n\tM1_F1 = 0x7F,\n\t/// <summary>The species is 25% male, 75% female.</summary>\n\tM1_F3 = 0xBF,\n\t/// <summary>The species is 0% male, 100% female.</summary>\n\tM0_F1 = 0xFE,\n\t/// <summary>The species is genderless.</summary>\n\tM0_F0 = 0xFF,\n\t/// <summary>The species is 100% male, 0% female.</summary>\n\tM1_F0 = 0x00\n}\npublic enum PBEGrowthRate : byte\n{\n\tErratic = 1,\n\tFast = 4,\n\tFluctuating = 2,\n\tMediumFast = 0,\n\tMediumSlow = 3,\n\tSlow = 5,\n\tMAX = 6 // 6 & 7 in-game are clones of MediumFast, but no Pokémon uses 6 or 7\n}\n/// <summary>Represents a Pokémon stat.</summary>\npublic enum PBEStat : byte\n{\n\t/// <summary>Hit points.</summary>\n\tHP,\n\t/// <summary>Attack.</summary>\n\tAttack,\n\t/// <summary>Defense.</summary>\n\tDefense,\n\t/// <summary>Special Attack.</summary>\n\tSpAttack,\n\t/// <summary>Special Defense.</summary>\n\tSpDefense,\n\t/// <summary>Speed.</summary>\n\tSpeed,\n\t/// <summary>Accuracy.</summary>\n\tAccuracy,\n\t/// <summary>Evasion.</summary>\n\tEvasion\n}\n/// <summary>Represents a specific <see cref=\"PBEMove\"/>'s category.</summary>\npublic enum PBEMoveCategory : byte\n{\n\t/// <summary>The move deals no damage.</summary>\n\tStatus,\n\t/// <summary>The move deals physical damage using the Attack and Defense stats.</summary>\n\tPhysical,\n\t/// <summary>The move deals special damage using the Special Attack and Special Defense stats.</summary>\n\tSpecial,\n\t/// <summary>Invalid category.</summary>\n\tMAX\n}\n/// <summary>Represents the various methods in which a Pokémon can learn a <see cref=\"PBEMove\"/>.</summary>\npublic enum PBEType : byte\n{\n\tNone,\n\tBug,\n\tDark,\n\tDragon,\n\tElectric,\n\tFighting,\n\tFire,\n\tFlying,\n\tGhost,\n\tGrass,\n\tGround,\n\tIce,\n\tNormal,\n\tPoison,\n\tPsychic,\n\tRock,\n\tSteel,\n\tWater,\n\tMAX\n}\n/// <summary>Represents a specific Pokémon's nature.</summary>\npublic enum PBENature : byte\n{\n\t/// <summary>The Pokémon favors attack and hinders special attack.</summary>\n\tAdamant = 3,\n\t/// <summary>The Pokémon doesn't favor any stat.</summary>\n\tBashful = 18,\n\t/// <summary>The Pokémon favors defense and hinders attack.</summary>\n\tBold = 5,\n\t/// <summary>The Pokémon favors attack and hinders speed.</summary>\n\tBrave = 2,\n\t/// <summary>The Pokémon favors special defense and hinders attack.</summary>\n\tCalm = 20,\n\t/// <summary>The Pokémon favors special defense and hinders special attack.</summary>\n\tCareful = 23,\n\t/// <summary>The Pokémon doesn't favor any stat.</summary>\n\tDocile = 6,\n\t/// <summary>The Pokémon favors special defense and hinders defense.</summary>\n\tGentle = 21,\n\t/// <summary>The Pokémon doesn't favor any stat.</summary>\n\tHardy = 0,\n\t/// <summary>The Pokémon favors speed and hinders defense.</summary>\n\tHasty = 11,\n\t/// <summary>The Pokémon favors defense and hinders special attack.</summary>\n\tImpish = 8,\n\t/// <summary>The Pokémon favors speed and hinders special attack.</summary>\n\tJolly = 13,\n\t/// <summary>The Pokémon favors defense and hinders special defense.</summary>\n\tLax = 9,\n\t/// <summary>The Pokémon favors attack and hinders defense.</summary>\n\tLonely = 1,\n\t/// <summary>The Pokémon favors special attack and hinders defense.</summary>\n\tMild = 16,\n\t/// <summary>The Pokémon favors special attack and hinders attack.</summary>\n\tModest = 15,\n\t/// <summary>The Pokémon favors speed and hinders special defense.</summary>\n\tNaive = 14,\n\t/// <summary>The Pokémon favors attack and hinders special defense.</summary>\n\tNaughty = 4,\n\t/// <summary>The Pokémon favors special attack and hinders speed.</summary>\n\tQuiet = 17,\n\t/// <summary>The Pokémon doesn't favor any stat.</summary>\n\tQuirky = 24,\n\t/// <summary>The Pokémon favors special attack and hinders special defense.</summary>\n\tRash = 19,\n\t/// <summary>The Pokémon favors defense and hinders speed.</summary>\n\tRelaxed = 7,\n\t/// <summary>The Pokémon favors special defense and hinders speed.</summary>\n\tSassy = 22,\n\t/// <summary>The Pokémon doesn't favor any stat.</summary>\n\tSerious = 12,\n\t/// <summary>The Pokémon favors speed and hinders attack.</summary>\n\tTimid = 10,\n\t/// <summary>Invalid nature.</summary>\n\tMAX = 25\n}\npublic enum PBEFlavor : byte\n{\n\tBitter = 3,\n\tDry = 2,\n\tSour = 1,\n\tSpicy = 0,\n\tSweet = 4,\n\tMAX = 5\n}\n/// <summary>Represents a specific Pokémon's held item.</summary>\npublic enum PBEItem : ushort\n{\n\t/// <summary>No item.</summary>\n\tNone = 0,\n\tAbsorbBulb = 545, // TODO\n\tAdamantOrb = 135,\n\tAguavBerry = 162,\n\tAirBalloon = 541, // TODO\n\t/// <summary>No effect.</summary>\n\tAmuletCoin = 223,\n\tAntidote = 18, // TODO\n\tApicotBerry = 205,\n\t/// <summary>No effect.</summary>\n\tArmorFossil = 104,\n\tAspearBerry = 153, // TODO\n\tAwakening = 21, // TODO\n\tBabiriBerry = 199, // TODO\n\t/// <summary>No effect.</summary>\n\tBalmMushroom = 580,\n\tBelueBerry = 183, // TODO\n\tBerryJuice = 43,\n\t/// <summary>No effect.</summary>\n\tBigMushroom = 87,\n\t/// <summary>No effect.</summary>\n\tBigNugget = 581,\n\t/// <summary>No effect.</summary>\n\tBigPearl = 89,\n\tBigRoot = 296,\n\tBindingBand = 544, // TODO\n\tBlackBelt = 241,\n\t/// <summary>No effect.</summary>\n\tBlackFlute = 68,\n\tBlackGlasses = 240,\n\tBlackSludge = 281,\n\t/// <summary>No effect.</summary>\n\tBlkApricorn = 491,\n\t/// <summary>No effect.</summary>\n\tBluApricorn = 486,\n\t/// <summary>No effect.</summary>\n\tBlueFlute = 65,\n\t/// <summary>No effect.</summary>\n\tBlueScarf = 261,\n\t/// <summary>No effect.</summary>\n\tBlueShard = 73,\n\tBlukBerry = 165, // TODO\n\t/// <summary>No effect.</summary>\n\tBridgeMailD = 145,\n\t/// <summary>No effect.</summary>\n\tBridgeMailM = 148,\n\t/// <summary>No effect.</summary>\n\tBridgeMailS = 144,\n\t/// <summary>No effect.</summary>\n\tBridgeMailT = 146,\n\t/// <summary>No effect.</summary>\n\tBridgeMailV = 147,\n\tBrightPowder = 213,\n\tBugGem = 558,\n\tBurnDrive = 118,\n\tBurnHeal = 19, // TODO\n\t/// <summary>No effect.</summary>\n\tCalcium = 49,\n\t/// <summary>No effect.</summary>\n\tCarbos = 48,\n\tCasteliacone = 591, // TODO\n\tCellBattery = 546, // TODO\n\tCharcoal = 249,\n\tChartiBerry = 195, // TODO\n\tCheriBerry = 149, // TODO\n\tCherishBall = 16,\n\tChestoBerry = 150, // TODO\n\tChilanBerry = 200, // TODO\n\tChillDrive = 119,\n\tChoiceBand = 220,\n\tChoiceScarf = 287,\n\tChoiceSpecs = 297,\n\tChopleBerry = 189, // TODO\n\t/// <summary>No effect.</summary>\n\tClawFossil = 100,\n\t/// <summary>No effect.</summary>\n\tCleanseTag = 224,\n\t/// <summary>No effect.</summary>\n\tCleverWing = 569,\n\tCobaBerry = 192, // TODO\n\tColburBerry = 198, // TODO\n\t/// <summary>No effect.</summary>\n\tCometShard = 583,\n\tCornnBerry = 175, // TODO\n\t/// <summary>No effect.</summary>\n\tCoverFossil = 572,\n\tCustapBerry = 210, // TODO\n\t/// <summary>No effect.</summary>\n\tDampMulch = 96,\n\tDampRock = 285,\n\tDarkGem = 562,\n\t/// <summary>No effect.</summary>\n\tDawnStone = 109,\n\tDeepSeaScale = 227,\n\tDeepSeaTooth = 226,\n\tDestinyKnot = 280,\n\tDireHit = 56, // TODO\n\tDiveBall = 7,\n\t/// <summary>No effect.</summary>\n\tDomeFossil = 102,\n\tDouseDrive = 116,\n\tDracoPlate = 311,\n\tDragonFang = 250,\n\tDragonGem = 561,\n\t/// <summary>No effect.</summary>\n\tDragonScale = 235,\n\tDreadPlate = 312,\n\tDreamBall = 576,\n\t/// <summary>No effect.</summary>\n\tDubiousDisc = 324,\n\tDurinBerry = 182, // TODO\n\tDuskBall = 13,\n\t/// <summary>No effect.</summary>\n\tDuskStone = 108,\n\tEarthPlate = 305,\n\tEjectButton = 547, // TODO\n\t/// <summary>No effect.</summary>\n\tElectirizer = 322,\n\tElectricGem = 550,\n\tElixir = 40, // TODO\n\tEnergyPowder = 34, // TODO\n\tEnergyRoot = 35, // TODO\n\tEnigmaBerry = 208, // TODO\n\t/// <summary>No effect.</summary>\n\tEscapeRope = 78,\n\tEther = 38, // TODO\n\t/// <summary>No effect.</summary>\n\tEverstone = 229,\n\tEviolite = 538,\n\tExpertBelt = 268,\n\tExpShare = 216,\n\tFastBall = 492,\n\t/// <summary>No effect.</summary>\n\tFavoredMail = 138,\n\tFightingGem = 553,\n\tFigyBerry = 159,\n\tFireGem = 548,\n\t/// <summary>No effect.</summary>\n\tFireStone = 82,\n\tFistPlate = 303,\n\t/// <summary>The Pokémon contracts <see cref=\"PBEStatus1.Burned\"/> at the end of each turn if it has no other <see cref=\"PBEStatus1\"/> and it does not have <see cref=\"PBEType.Fire\"/>.</summary>\n\tFlameOrb = 273,\n\tFlamePlate = 298,\n\tFloatStone = 539, // TODO\n\tFluffyTail = 64,\n\tFlyingGem = 556,\n\tFocusBand = 230,\n\tFocusSash = 275,\n\tFreshWater = 30, // TODO\n\tFriendBall = 497,\n\tFullHeal = 27, // TODO\n\tFullIncense = 316, // TODO\n\tFullRestore = 23, // TODO\n\tGanlonBerry = 202,\n\t/// <summary>No effect.</summary>\n\tGeniusWing = 568,\n\tGhostGem = 560,\n\t/// <summary>No effect.</summary>\n\tGooeyMulch = 98,\n\tGrassGem = 551,\n\tGreatBall = 3,\n\t/// <summary>No effect.</summary>\n\tGreenScarf = 263,\n\t/// <summary>No effect.</summary>\n\tGreenShard = 75,\n\t/// <summary>No effect.</summary>\n\tGreetMail = 137,\n\tGrepaBerry = 173, // TODO\n\tGripClaw = 286, // TODO\n\tGriseousOrb = 112,\n\t/// <summary>No effect.</summary>\n\tGrnApricorn = 488,\n\tGroundGem = 555,\n\t/// <summary>No effect.</summary>\n\tGrowthMulch = 95,\n\tGuardSpec = 55, // TODO\n\tHabanBerry = 197, // TODO\n\tHardStone = 238,\n\tHealBall = 14,\n\tHealPowder = 36, // TODO\n\t/// <summary>No effect.</summary>\n\tHealthWing = 565,\n\t/// <summary>No effect.</summary>\n\tHeartScale = 93,\n\tHeatRock = 284,\n\tHeavyBall = 495,\n\t/// <summary>No effect.</summary>\n\tHelixFossil = 101,\n\tHondewBerry = 172, // TODO\n\t/// <summary>No effect.</summary>\n\tHoney = 94,\n\t/// <summary>No effect.</summary>\n\tHPUp = 45,\n\tHyperPotion = 25, // TODO\n\tIapapaBerry = 163,\n\tIceGem = 552,\n\tIceHeal = 20, // TODO\n\tIciclePlate = 302,\n\tIcyRock = 282,\n\t/// <summary>No effect.</summary>\n\tInquiryMail = 141,\n\tInsectPlate = 308,\n\t/// <summary>No effect.</summary>\n\tIron = 47,\n\tIronBall = 278, // TODO\n\tIronPlate = 313,\n\tJabocaBerry = 211, // TODO\n\tKasibBerry = 196, // TODO\n\tKebiaBerry = 190, // TODO\n\tKelpsyBerry = 170, // TODO\n\tKingsRock = 221, // TODO\n\tLaggingTail = 279, // TODO\n\tLansatBerry = 206, // TODO\n\tLavaCookie = 42, // TODO\n\tLaxIncense = 255,\n\t/// <summary>No effect.</summary>\n\tLeafStone = 85,\n\tLeftovers = 234,\n\tLemonade = 32, // TODO\n\tLeppaBerry = 154, // TODO\n\tLevelBall = 493,\n\tLiechiBerry = 201,\n\tLifeOrb = 270,\n\tLightBall = 236,\n\tLightClay = 269,\n\t/// <summary>No effect.</summary>\n\tLikeMail = 142,\n\tLoveBall = 496,\n\t/// <summary>No effect.</summary>\n\tLuckIncense = 319,\n\tLuckyEgg = 231,\n\tLuckyPunch = 256,\n\tLumBerry = 157, // TODO\n\tLureBall = 494,\n\tLustrousOrb = 136,\n\tLuxuryBall = 11,\n\tMachoBrace = 215,\n\t/// <summary>No effect.</summary>\n\tMagmarizer = 323,\n\tMagnet = 242,\n\tMagoBerry = 161,\n\tMagostBerry = 176, // TODO\n\tMasterBall = 1,\n\tMaxElixir = 41, // TODO\n\tMaxEther = 39, // TODO\n\tMaxPotion = 24, // TODO\n\t/// <summary>No effect.</summary>\n\tMaxRepel = 77,\n\tMaxRevive = 29, // TODO\n\tMeadowPlate = 301,\n\tMentalHerb = 219, // TODO\n\tMetalCoat = 233,\n\tMetalPowder = 257,\n\tMetronome = 277, // TODO\n\tMicleBerry = 209, // TODO\n\tMindPlate = 307,\n\tMiracleSeed = 239,\n\tMoomooMilk = 33, // TODO\n\tMoonBall = 498,\n\t/// <summary>No effect.</summary>\n\tMoonStone = 81,\n\tMuscleBand = 266,\n\t/// <summary>No effect.</summary>\n\tMuscleWing = 566,\n\tMysticWater = 243,\n\tNanabBerry = 166, // TODO\n\tNestBall = 8,\n\tNetBall = 6,\n\tNeverMeltIce = 246,\n\tNomelBerry = 178, // TODO\n\tNormalGem = 564,\n\t/// <summary>No effect.</summary>\n\tNugget = 92,\n\tOccaBerry = 184, // TODO\n\tOddIncense = 314,\n\t/// <summary>No effect.</summary>\n\tOddKeystone = 111,\n\t/// <summary>No effect.</summary>\n\tOldAmber = 103,\n\tOldGateau = 54, // TODO\n\tOranBerry = 155,\n\t/// <summary>No effect.</summary>\n\tOvalStone = 110,\n\tPamtreBerry = 180, // TODO\n\tParalyzHeal = 22, // TODO\n\tParkBall = 500,\n\tPasshoBerry = 185, // TODO\n\t/// <summary>No effect.</summary>\n\tPassOrb = 575,\n\tPayapaBerry = 193, // TODO\n\t/// <summary>No effect.</summary>\n\tPearl = 88,\n\t/// <summary>No effect.</summary>\n\tPearlString = 582,\n\tPechaBerry = 151, // TODO\n\tPersimBerry = 156, // TODO\n\tPetayaBerry = 204,\n\tPinapBerry = 168, // TODO\n\t/// <summary>No effect.</summary>\n\tPinkScarf = 262,\n\t/// <summary>No effect.</summary>\n\tPlumeFossil = 573,\n\t/// <summary>No effect.</summary>\n\tPnkApricorn = 489,\n\tPoisonBarb = 245,\n\tPoisonGem = 554,\n\tPokeBall = 4,\n\tPokeDoll = 63,\n\tPokeToy = 577,\n\tPomegBerry = 169, // TODO\n\tPotion = 17, // TODO\n\tPowerAnklet = 293,\n\tPowerBand = 292,\n\tPowerBelt = 290,\n\tPowerBracer = 289,\n\tPowerHerb = 271,\n\tPowerLens = 291,\n\tPowerWeight = 294,\n\t/// <summary>No effect.</summary>\n\tPPMax = 53,\n\t/// <summary>No effect.</summary>\n\tPPUp = 51,\n\tPremierBall = 12,\n\t/// <summary>No effect.</summary>\n\tPrettyWing = 571,\n\t/// <summary>No effect.</summary>\n\tPrismScale = 537,\n\t/// <summary>No effect.</summary>\n\tProtector = 321,\n\t/// <summary>No effect.</summary>\n\tProtein = 46,\n\tPsychicGem = 557,\n\t/// <summary>No effect.</summary>\n\tPureIncense = 320,\n\tQualotBerry = 171, // TODO\n\tQuickBall = 15,\n\tQuickClaw = 217, // TODO\n\tQuickPowder = 274,\n\tRabutaBerry = 177, // TODO\n\tRageCandyBar = 504, // TODO\n\t/// <summary>No effect.</summary>\n\tRareBone = 106,\n\t/// <summary>No effect.</summary>\n\tRareCandy = 50,\n\tRawstBerry = 152, // TODO\n\tRazorClaw = 326,\n\tRazorFang = 327, // TODO\n\tRazzBerry = 164, // TODO\n\t/// <summary>No effect.</summary>\n\tReaperCloth = 325,\n\t/// <summary>No effect.</summary>\n\tRedApricorn = 485,\n\tRedCard = 542, // TODO\n\t/// <summary>No effect.</summary>\n\tRedFlute = 67,\n\t/// <summary>No effect.</summary>\n\tRedScarf = 260,\n\t/// <summary>No effect.</summary>\n\tRedShard = 72,\n\t/// <summary>No effect.</summary>\n\tRelicBand = 588,\n\t/// <summary>No effect.</summary>\n\tRelicCopper = 584,\n\t/// <summary>No effect.</summary>\n\tRelicCrown = 590,\n\t/// <summary>No effect.</summary>\n\tRelicGold = 586,\n\t/// <summary>No effect.</summary>\n\tRelicSilver = 585,\n\t/// <summary>No effect.</summary>\n\tRelicStatue = 589,\n\t/// <summary>No effect.</summary>\n\tRelicVase = 587,\n\tRepeatBall = 9,\n\t/// <summary>No effect.</summary>\n\tRepel = 79,\n\t/// <summary>No effect.</summary>\n\tReplyMail = 143,\n\t/// <summary>No effect.</summary>\n\tResistWing = 567,\n\tRevivalHerb = 37, // TODO\n\tRevive = 28, // TODO\n\tRindoBerry = 187, // TODO\n\tRingTarget = 543, // TODO\n\tRockGem = 559,\n\tRockIncense = 315,\n\tRockyHelmet = 540,\n\t/// <summary>No effect.</summary>\n\tRootFossil = 99,\n\tRoseIncense = 318,\n\tRowapBerry = 212, // TODO\n\t/// <summary>No effect.</summary>\n\tRSVPMail = 139,\n\tSacredAsh = 44, // TODO\n\tSafariBall = 5,\n\tSalacBerry = 203,\n\tScopeLens = 232,\n\tSeaIncense = 254,\n\tSharpBeak = 244,\n\tShedShell = 295, // TODO\n\tShellBell = 253, // TODO\n\t/// <summary>No effect.</summary>\n\tShinyStone = 107,\n\t/// <summary>No effect.</summary>\n\tShoalSalt = 70,\n\t/// <summary>No effect.</summary>\n\tShoalShell = 71,\n\tShockDrive = 117,\n\tShucaBerry = 191, // TODO\n\tSilkScarf = 251,\n\tSilverPowder = 222,\n\tSitrusBerry = 158,\n\t/// <summary>No effect.</summary>\n\tSkullFossil = 105,\n\tSkyPlate = 306,\n\tSmokeBall = 228,\n\tSmoothRock = 283,\n\tSodaPop = 31, // TODO\n\tSoftSand = 237,\n\t/// <summary>No effect.</summary>\n\tSootheBell = 218,\n\tSoulDew = 225,\n\tSpellTag = 247,\n\tSpelonBerry = 179, // TODO\n\tSplashPlate = 299,\n\tSpookyPlate = 310,\n\tSportBall = 499,\n\t/// <summary>No effect.</summary>\n\tStableMulch = 97,\n\t/// <summary>No effect.</summary>\n\tStardust = 90,\n\tStarfBerry = 207,\n\t/// <summary>No effect.</summary>\n\tStarPiece = 91,\n\tSteelGem = 563,\n\tStick = 259,\n\tStickyBarb = 288, // TODO\n\tStonePlate = 309,\n\t/// <summary>No effect.</summary>\n\tSunStone = 80,\n\tSuperPotion = 26, // TODO\n\t/// <summary>No effect.</summary>\n\tSuperRepel = 76,\n\tSweetHeart = 134, // TODO\n\t/// <summary>No effect.</summary>\n\tSwiftWing = 570,\n\tTamatoBerry = 174, // TODO\n\tTangaBerry = 194, // TODO\n\t/// <summary>No effect.</summary>\n\tThanksMail = 140,\n\tThickClub = 258,\n\t/// <summary>No effect.</summary>\n\tThunderstone = 83,\n\tTimerBall = 10,\n\t/// <summary>No effect.</summary>\n\tTinyMushroom = 86,\n\t/// <summary>The Pokémon contracts <see cref=\"PBEStatus1.BadlyPoisoned\"/> at the end of each turn if it has no other <see cref=\"PBEStatus1\"/> and it does not have <see cref=\"PBEType.Poison\"/> or <see cref=\"PBEType.Steel\"/>.</summary>\n\tToxicOrb = 272,\n\tToxicPlate = 304,\n\tTwistedSpoon = 248,\n\tUltraBall = 2,\n\tUpGrade = 252,\n\tWacanBerry = 186, // TODO\n\tWaterGem = 549,\n\t/// <summary>No effect.</summary>\n\tWaterStone = 84,\n\tWatmelBerry = 181, // TODO\n\tWaveIncense = 317,\n\tWepearBerry = 167, // TODO\n\tWhiteHerb = 214, // TODO\n\t/// <summary>No effect.</summary>\n\tWhiteFlute = 69,\n\tWideLens = 265,\n\tWikiBerry = 160,\n\tWiseGlasses = 267,\n\t/// <summary>No effect.</summary>\n\tWhtApricorn = 490,\n\tXAccuracy = 60, // TODO\n\tXAttack = 57, // TODO\n\tXDefend = 58, // TODO\n\tXSpecial = 61, // TODO\n\tXSpDef = 62, // TODO\n\tXSpeed = 59, // TODO\n\tYacheBerry = 188, // TODO\n\t/// <summary>No effect.</summary>\n\tYellowFlute = 66,\n\t/// <summary>No effect.</summary>\n\tYellowScarf = 264,\n\t/// <summary>No effect.</summary>\n\tYellowShard = 74,\n\t/// <summary>No effect.</summary>\n\tYlwApricorn = 487,\n\tZapPlate = 300,\n\t/// <summary>No effect.</summary>\n\tZinc = 52,\n\tZoomLens = 276 // TODO\n}\n/// <summary>Represents a specific Pokémon's special ability.</summary>\npublic enum PBEAbility : byte\n{\n\t/// <summary>The Pokémon's ability was suppressed with <see cref=\"PBEMoveEffect.GastroAcid\"/>.</summary>\n\tNone = 0,\n\t/// <summary>The Pokémon has a stronger same-type-attack-bonus.</summary>\n\tAdaptability = 91,\n\tAftermath = 106, // TODO\n\tAirLock = 76,\n\tAnalytic = 148, // TODO\n\tAngerPoint = 83, // TODO\n\tAnticipation = 107,\n\tArenaTrap = 71, // TODO\n\tBadDreams = 123,\n\t/// <summary>The Pokémon takes no critical hits.</summary>\n\tBattleArmor = 4,\n\tBigPecks = 145,\n\t/// <summary>When the Pokémon has low HP, its Fire-type moves get a power boost.</summary>\n\tBlaze = 66,\n\t/// <summary>The Pokémon gets a speed boost in harsh sunlight.</summary>\n\tChlorophyll = 34,\n\tClearBody = 29,\n\tCloudNine = 13,\n\tColorChange = 16,\n\t/// <summary>The Pokémon accuracy is boosted.</summary>\n\tCompoundeyes = 14,\n\tContrary = 126,\n\tCursedBody = 130, // TODO\n\tCuteCharm = 56,\n\tDamp = 6, // TODO\n\tDefeatist = 129,\n\tDefiant = 128, // TODO\n\tDownload = 88,\n\t/// <summary>The Pokémon changes the weather to infinite rain.</summary>\n\tDrizzle = 2,\n\t/// <summary>The Pokémon changes the weather to infinite harsh sunlight.</summary>\n\tDrought = 70,\n\tDrySkin = 87, // TODO\n\tEarlyBird = 48,\n\tEffectSpore = 27,\n\t/// <summary>The Pokémon takes less damage from incoming super-effective moves.</summary>\n\tFilter = 111,\n\tFlameBody = 49,\n\tFlareBoost = 138,\n\tFlashFire = 18, // TODO\n\tFlowerGift = 122,\n\tForecast = 59,\n\tForewarn = 108, // TODO (Also, does this activate when given/taken?)\n\tFriendGuard = 132, // TODO\n\tFrisk = 119, // TODO (Also, does this activate when given/taken?)\n\tGluttony = 82, // TODO\n\t/// <summary>The Pokémon's attack is boosted when it is afflicted with a <see cref=\"PBEStatus1\"/> and the damage reduction from <see cref=\"PBEStatus1.Burned\"/> is not applied.</summary>\n\tGuts = 62,\n\tHarvest = 139, // TODO\n\tHealer = 131,\n\t/// <summary>The Pokémon takes less damage from Fire-type moves and from a burn.</summary>\n\tHeatproof = 85,\n\tHeavyMetal = 134, // TODO\n\t/// <summary>No effect in battle.</summary>\n\tHoneyGather = 118,\n\t/// <summary>The Pokémon's attack is boosted.</summary>\n\tHugePower = 37,\n\t/// <summary>The Pokémon's attack is boosted, but its accuracy is lower for physical moves.</summary>\n\tHustle = 55,\n\tHydration = 93, // TODO\n\tHyperCutter = 52,\n\t/// <summary>In a hailstorm, the Pokémon takes no damage from the hailstorm and restores HP at the end of each turn.</summary>\n\tIceBody = 115,\n\t/// <summary>No effect in battle.</summary>\n\tIlluminate = 35,\n\tIllusion = 149,\n\tImmunity = 17,\n\t/// <summary>The Pokémon transforms into the foe across from it when switching in.</summary>\n\tImposter = 150,\n\tInfiltrator = 151, // TODO: Mist\n\tInnerFocus = 39,\n\tInsomnia = 15,\n\tIntimidate = 22,\n\tIronBarbs = 160,\n\t/// <summary>The power of moves with <see cref=\"PBEMoveFlag.AffectedByIronFist\"/> is increased.</summary>\n\tIronFist = 89,\n\tJustified = 154,\n\tKeenEye = 51,\n\tKlutz = 103, // TODO\n\tLeafGuard = 102,\n\t/// <summary>The Pokémon is immune to Ground-type moves and most entry hazards.</summary>\n\tLevitate = 26,\n\tLightMetal = 135, // TODO\n\tLightningrod = 31, // TODO\n\t/// <summary>The Pokémon cannot be paralyzed.</summary>\n\tLimber = 7,\n\tLiquidOoze = 64,\n\tMagicBounce = 156, // TODO\n\tMagicGuard = 98, // TODO\n\tMagmaArmor = 40,\n\tMagnetPull = 42, // TODO\n\t/// <summary>The Pokémon's defense is boosted when it is afflicted with a <see cref=\"PBEStatus1\"/>.</summary>\n\tMarvelScale = 63,\n\tMinus = 58,\n\tMoldBreaker = 104,\n\tMoody = 141,\n\tMotorDrive = 78, // TODO\n\tMoxie = 153, // TODO\n\tMultiscale = 136, // TODO\n\t/// <summary>No effect in battle.</summary>\n\tMultitype = 121,\n\tMummy = 152,\n\tNaturalCure = 30,\n\t/// <summary>The Pokémon will always hit and always get hit unless protection is used.</summary>\n\tNoGuard = 99,\n\tNormalize = 96,\n\tOblivious = 12,\n\t/// <summary>The Pokémon takes no damage from a hailstorm or sandstorm.</summary>\n\tOvercoat = 142,\n\t/// <summary>When the Pokémon has low HP, its Grass-type moves get a power boost.</summary>\n\tOvergrow = 65,\n\tOwnTempo = 20,\n\tPickpocket = 124, // TODO\n\tPickup = 53, // TODO\n\tPlus = 57,\n\tPoisonHeal = 90,\n\tPoisonPoint = 38,\n\tPoisonTouch = 143, // TODO\n\tPrankster = 158,\n\tPressure = 46, // TODO (Also, does this activate when given/taken?)\n\t/// <summary>The Pokémon's attack is boosted.</summary>\n\tPurePower = 74,\n\tQuickFeet = 95,\n\t/// <summary>In rain, the Pokémon restores HP at the end of each turn.</summary>\n\tRainDish = 44,\n\tRattled = 155,\n\tReckless = 120,\n\tRegenerator = 144,\n\tRivalry = 79, // TODO\n\tRockHead = 69,\n\tRoughSkin = 24,\n\tRunAway = 50,\n\t/// <summary>In a sandstorm, the Pokémon takes no damage from the sandstorm and its Rock-, Ground-, and Steel-type moves get a power boost.</summary>\n\tSandForce = 159,\n\t/// <summary>The Pokémon gets a speed boost in a sandstorm.</summary>\n\tSandRush = 146,\n\t/// <summary>The Pokémon changes the weather to an infinite sandstorm.</summary>\n\tSandStream = 45,\n\t/// <summary>In a sandstorm, the Pokémon takes no damage from the sandstorm and gets a 20% evasion boost.</summary>\n\tSandVeil = 8,\n\tSapSipper = 157, // TODO\n\tScrappy = 113,\n\tSereneGrace = 32,\n\tShadowTag = 23, // TODO\n\tShedSkin = 61,\n\tSheerForce = 125, // TODO\n\t/// <summary>The Pokémon takes no critical hits.</summary>\n\tShellArmor = 75,\n\tShieldDust = 19, // TODO\n\t/// <summary>The Pokémon's stat changes are doubled.</summary>\n\tSimple = 86,\n\tSkillLink = 92, // TODO: Effect on Triple Kick\n\tSlowStart = 112,\n\t/// <summary>The Pokémon deals more damage when landing critical hits.</summary>\n\tSniper = 97,\n\t/// <summary>In a hailstorm, the Pokémon takes no damage from the hailstorm and gets a 20% evasion boost.</summary>\n\tSnowCloak = 81,\n\t/// <summary>The Pokémon changes the weather to an infinite hailstorm.</summary>\n\tSnowWarning = 117,\n\t/// <summary>In harsh sunlight, the Pokémon gets a special attack boost and takes damage at the end of each turn.</summary>\n\tSolarPower = 94,\n\t/// <summary>The Pokémon takes less damage from incoming super-effective moves.</summary>\n\tSolidRock = 116,\n\tSoundproof = 43, // TODO\n\tSpeedBoost = 3,\n\tStall = 100, // TODO\n\tStatic = 9,\n\tSteadfast = 80,\n\tStench = 1, // TODO\n\tStickyHold = 60, // TODO\n\tStormDrain = 114, // TODO\n\tSturdy = 5,\n\tSuctionCups = 21, // TODO\n\t/// <summary>The Pokémon is more likely to land critical hits.</summary>\n\tSuperLuck = 105,\n\t/// <summary>When the Pokémon has low HP, its Bug-type moves get a power boost.</summary>\n\tSwarm = 68,\n\t/// <summary>The Pokémon gets a speed boost in rain.</summary>\n\tSwiftSwim = 33,\n\tSynchronize = 28, // TODO\n\tTangledFeet = 77,\n\tTechnician = 101,\n\tTelepathy = 140, // TODO\n\tTeravolt = 164,\n\t/// <summary>The Pokémon takes less damage from Ice- and Fire-type moves.</summary>\n\tThickFat = 47,\n\t/// <summary>The Pokémon deals double damage with outgoing not-very-effective moves.</summary>\n\tTintedLens = 110,\n\t/// <summary>When the Pokémon has low HP, its Water-type moves get a power boost.</summary>\n\tTorrent = 67,\n\tToxicBoost = 137,\n\tTrace = 36, // TODO (Also, does this activate when given/taken?)\n\tTruant = 54, // TODO\n\tTurboblaze = 163,\n\tUnaware = 109,\n\tUnburden = 84, // TODO\n\tUnnerve = 127, // TODO\n\tVictoryStar = 162,\n\tVitalSpirit = 72,\n\tVoltAbsorb = 10, // TODO\n\tWaterAbsorb = 11, // TODO\n\tWaterVeil = 41,\n\tWeakArmor = 133,\n\tWhiteSmoke = 73,\n\t/// <summary>The Pokémon is immune to all damaging moves except for moves that would deal super-effective damage.</summary>\n\tWonderGuard = 25,\n\tWonderSkin = 147,\n\tZenMode = 161, // TODO (Also, does this activate when given/taken?)\n\t/// <summary>Invalid ability.</summary>\n\tMAX = 165,\n}\n// Official IDs for the forms\n#pragma warning disable CA1069 // Enums values should not be duplicated\npublic enum PBEForm : byte\n{\n\tArceus = 0,\n\tArceus_Bug = 6,\n\tArceus_Dark = 16,\n\tArceus_Dragon = 15,\n\tArceus_Electric = 12,\n\tArceus_Fighting = 1,\n\tArceus_Fire = 9,\n\tArceus_Flying = 2,\n\tArceus_Ghost = 7,\n\tArceus_Grass = 11,\n\tArceus_Ground = 4,\n\tArceus_Ice = 14,\n\tArceus_Poison = 3,\n\tArceus_Psychic = 13,\n\tArceus_Rock = 5,\n\tArceus_Steel = 8,\n\tArceus_Water = 10,\n\tBasculin_Blue = 1,\n\tBasculin_Red = 0,\n\tBurmy_Plant = 0,\n\tBurmy_Sandy = 1,\n\tBurmy_Trash = 2,\n\tCastform = 0,\n\tCastform_Rainy = 2,\n\tCastform_Snowy = 3,\n\tCastform_Sunny = 1,\n\tCherrim = 0,\n\tCherrim_Sunshine = 1,\n\tDarmanitan = 0,\n\tDarmanitan_Zen = 1,\n\tDeerling_Autumn = 2,\n\tDeerling_Spring = 0,\n\tDeerling_Summer = 1,\n\tDeerling_Winter = 3,\n\tDeoxys = 0,\n\tDeoxys_Attack = 1,\n\tDeoxys_Defense = 2,\n\tDeoxys_Speed = 3,\n\tGastrodon_East = 1,\n\tGastrodon_West = 0,\n\tGenesect = 0,\n\tGenesect_Burn = 3,\n\tGenesect_Chill = 4,\n\tGenesect_Douse = 1,\n\tGenesect_Shock = 2,\n\tGiratina = 0,\n\tGiratina_Origin = 1,\n\tKeldeo = 0,\n\tKeldeo_Resolute = 1,\n\tKyurem = 0,\n\tKyurem_Black = 2,\n\tKyurem_White = 1,\n\tLandorus = 0,\n\tLandorus_Therian = 1,\n\tMeloetta = 0,\n\tMeloetta_Pirouette = 1,\n\tRotom = 0,\n\tRotom_Fan = 4,\n\tRotom_Frost = 3,\n\tRotom_Heat = 1,\n\tRotom_Mow = 5,\n\tRotom_Wash = 2,\n\tSawsbuck_Autumn = 2,\n\tSawsbuck_Spring = 0,\n\tSawsbuck_Summer = 1,\n\tSawsbuck_Winter = 3,\n\tShaymin = 0,\n\tShaymin_Sky = 1,\n\tShellos_East = 1,\n\tShellos_West = 0,\n\tThundurus = 0,\n\tThundurus_Therian = 1,\n\tTornadus = 0,\n\tTornadus_Therian = 1,\n\tUnown_A = 0,\n\tUnown_B = 1,\n\tUnown_C = 2,\n\tUnown_D = 3,\n\tUnown_E = 4,\n\tUnown_Exclamation = 26,\n\tUnown_F = 5,\n\tUnown_G = 6,\n\tUnown_H = 7,\n\tUnown_I = 8,\n\tUnown_J = 9,\n\tUnown_K = 10,\n\tUnown_L = 11,\n\tUnown_M = 12,\n\tUnown_N = 13,\n\tUnown_O = 14,\n\tUnown_P = 15,\n\tUnown_Q = 16,\n\tUnown_Question = 27,\n\tUnown_R = 17,\n\tUnown_S = 18,\n\tUnown_T = 19,\n\tUnown_U = 20,\n\tUnown_V = 21,\n\tUnown_W = 22,\n\tUnown_X = 23,\n\tUnown_Y = 24,\n\tUnown_Z = 25,\n\tWormadam_Plant = 0,\n\tWormadam_Sandy = 1,\n\tWormadam_Trash = 2\n#pragma warning restore CA1069 // Enums values should not be duplicated\n}\n/// <summary>Represents a specific Pokémon species.</summary>\npublic enum PBESpecies : ushort\n{\n\tAbomasnow = 460,\n\tAbra = 63,\n\tAbsol = 359,\n\tAccelgor = 617,\n\tAerodactyl = 142,\n\tAggron = 306,\n\tAipom = 190,\n\tAlakazam = 65,\n\tAlomomola = 594,\n\tAltaria = 334,\n\tAmbipom = 424,\n\tAmoonguss = 591,\n\tAmpharos = 181,\n\tAnorith = 347,\n\tArbok = 24,\n\tArcanine = 59,\n\tArceus = 493,\n\tArchen = 566,\n\tArcheops = 567,\n\tAriados = 168,\n\tArmaldo = 348,\n\tAron = 304,\n\tArticuno = 144,\n\tAudino = 531,\n\tAxew = 610,\n\tAzelf = 482,\n\tAzumarill = 184,\n\tAzurill = 298,\n\tBagon = 371,\n\tBaltoy = 343,\n\tBanette = 354,\n\tBarboach = 339,\n\tBasculin = 550,\n\tBastiodon = 411,\n\tBayleef = 153,\n\tBeartic = 614,\n\tBeautifly = 267,\n\tBeedrill = 15,\n\tBeheeyem = 606,\n\tBeldum = 374,\n\tBellossom = 182,\n\tBellsprout = 69,\n\tBibarel = 400,\n\tBidoof = 399,\n\tBisharp = 625,\n\tBlastoise = 9,\n\tBlaziken = 257,\n\tBlissey = 242,\n\tBlitzle = 522,\n\tBoldore = 525,\n\tBonsly = 438,\n\tBouffalant = 626,\n\tBraviary = 628,\n\tBreloom = 286,\n\tBronzong = 437,\n\tBronzor = 436,\n\tBudew = 406,\n\tBuizel = 418,\n\tBulbasaur = 1,\n\tBuneary = 427,\n\tBurmy = 412,\n\tButterfree = 12,\n\tCacnea = 331,\n\tCacturne = 332,\n\tCamerupt = 323,\n\tCarnivine = 455,\n\tCarracosta = 565,\n\tCarvanha = 318,\n\tCascoon = 268,\n\tCastform = 351,\n\tCaterpie = 10,\n\tCelebi = 251,\n\tChandelure = 609,\n\tChansey = 113,\n\tCharizard = 6,\n\tCharmander = 4,\n\tCharmeleon = 5,\n\tChatot = 441,\n\tCherrim = 421,\n\tCherubi = 420,\n\tChikorita = 152,\n\tChimchar = 390,\n\tChimecho = 358,\n\tChinchou = 170,\n\tChingling = 433,\n\tCinccino = 573,\n\tClamperl = 366,\n\tClaydol = 344,\n\tClefable = 36,\n\tClefairy = 35,\n\tCleffa = 173,\n\tCloyster = 91,\n\tCobalion = 638,\n\tCofagrigus = 563,\n\tCombee = 415,\n\tCombusken = 256,\n\tConkeldurr = 534,\n\tCorphish = 341,\n\tCorsola = 222,\n\tCottonee = 546,\n\tCradily = 346,\n\tCranidos = 408,\n\tCrawdaunt = 342,\n\tCresselia = 488,\n\tCroagunk = 453,\n\tCrobat = 169,\n\tCroconaw = 159,\n\tCrustle = 558,\n\tCryogonal = 615,\n\tCubchoo = 613,\n\tCubone = 104,\n\tCyndaquil = 155,\n\tDarkrai = 491,\n\tDarmanitan = 555,\n\tDarumaka = 554,\n\tDeerling = 585,\n\tDeino = 633,\n\tDelcatty = 301,\n\tDelibird = 225,\n\tDeoxys = 386,\n\tDewgong = 87,\n\tDewott = 502,\n\tDialga = 483,\n\tDiglett = 50,\n\tDitto = 132,\n\tDodrio = 85,\n\tDoduo = 84,\n\tDonphan = 232,\n\tDragonair = 148,\n\tDragonite = 149,\n\tDrapion = 452,\n\tDratini = 147,\n\tDrifblim = 426,\n\tDrifloon = 425,\n\tDrilbur = 529,\n\tDrowzee = 96,\n\tDruddigon = 621,\n\tDucklett = 580,\n\tDugtrio = 51,\n\tDunsparce = 206,\n\tDuosion = 578,\n\tDurant = 632,\n\tDusclops = 356,\n\tDusknoir = 477,\n\tDuskull = 355,\n\tDustox = 269,\n\tDwebble = 557,\n\tEelektrik = 603,\n\tEelektross = 604,\n\tEevee = 133,\n\tEkans = 23,\n\tElectabuzz = 125,\n\tElectivire = 466,\n\tElectrike = 309,\n\tElectrode = 101,\n\tElekid = 239,\n\tElgyem = 605,\n\tEmboar = 500,\n\tEmolga = 587,\n\tEmpoleon = 395,\n\tEntei = 244,\n\tEscavalier = 589,\n\tEspeon = 196,\n\tExcadrill = 530,\n\tExeggcute = 102,\n\tExeggutor = 103,\n\tExploud = 295,\n\tFarfetchd = 83,\n\tFearow = 22,\n\tFeebas = 349,\n\tFeraligatr = 160,\n\tFerroseed = 597,\n\tFerrothorn = 598,\n\tFinneon = 456,\n\tFlaaffy = 180,\n\tFlareon = 136,\n\tFloatzel = 419,\n\tFlygon = 330,\n\tFoongus = 590,\n\tForretress = 205,\n\tFraxure = 611,\n\tFrillish = 592,\n\tFroslass = 478,\n\tFurret = 162,\n\tGabite = 444,\n\tGallade = 475,\n\tGalvantula = 596,\n\tGarbodor = 569,\n\tGarchomp = 445,\n\tGardevoir = 282,\n\tGastly = 92,\n\tGastrodon = 423,\n\tGenesect = 649,\n\tGengar = 94,\n\tGeodude = 74,\n\tGible = 443,\n\tGigalith = 526,\n\tGirafarig = 203,\n\tGiratina = 487,\n\tGlaceon = 471,\n\tGlalie = 362,\n\tGlameow = 431,\n\tGligar = 207,\n\tGliscor = 472,\n\tGloom = 44,\n\tGolbat = 42,\n\tGoldeen = 118,\n\tGolduck = 55,\n\tGolem = 76,\n\tGolett = 622,\n\tGolurk = 623,\n\tGorebyss = 368,\n\tGothita = 574,\n\tGothitelle = 576,\n\tGothorita = 575,\n\tGranbull = 210,\n\tGraveler = 75,\n\tGrimer = 88,\n\tGrotle = 388,\n\tGroudon = 383,\n\tGrovyle = 253,\n\tGrowlithe = 58,\n\tGrumpig = 326,\n\tGulpin = 316,\n\tGurdurr = 533,\n\tGyarados = 130,\n\tHappiny = 440,\n\tHariyama = 297,\n\tHaunter = 93,\n\tHaxorus = 612,\n\tHeatmor = 631,\n\tHeatran = 485,\n\tHeracross = 214,\n\tHerdier = 507,\n\tHippopotas = 449,\n\tHippowdon = 450,\n\tHitmonchan = 107,\n\tHitmonlee = 106,\n\tHitmontop = 237,\n\tHoOh = 250,\n\tHonchkrow = 430,\n\tHoothoot = 163,\n\tHoppip = 187,\n\tHorsea = 116,\n\tHoundoom = 229,\n\tHoundour = 228,\n\tHuntail = 367,\n\tHydreigon = 635,\n\tHypno = 97,\n\tIgglybuff = 174,\n\tIllumise = 314,\n\tInfernape = 392,\n\tIvysaur = 2,\n\tJellicent = 593,\n\tJigglypuff = 39,\n\tJirachi = 385,\n\tJolteon = 135,\n\tJoltik = 595,\n\tJumpluff = 189,\n\tJynx = 124,\n\tKabuto = 140,\n\tKabutops = 141,\n\tKadabra = 64,\n\tKakuna = 14,\n\tKangaskhan = 115,\n\tKarrablast = 588,\n\tKecleon = 352,\n\tKeldeo = 647,\n\tKingdra = 230,\n\tKingler = 99,\n\tKirlia = 281,\n\tKlang = 600,\n\tKlink = 599,\n\tKlinklang = 601,\n\tKoffing = 109,\n\tKrabby = 98,\n\tKricketot = 401,\n\tKricketune = 402,\n\tKrokorok = 552,\n\tKrookodile = 553,\n\tKyogre = 382,\n\tKyurem = 646,\n\tLairon = 305,\n\tLampent = 608,\n\tLandorus = 645,\n\tLanturn = 171,\n\tLapras = 131,\n\tLarvesta = 636,\n\tLarvitar = 246,\n\tLatias = 380,\n\tLatios = 381,\n\tLeafeon = 470,\n\tLeavanny = 542,\n\tLedian = 166,\n\tLedyba = 165,\n\tLickilicky = 463,\n\tLickitung = 108,\n\tLiepard = 510,\n\tLileep = 345,\n\tLilligant = 549,\n\tLillipup = 506,\n\tLinoone = 264,\n\tLitwick = 607,\n\tLombre = 271,\n\tLopunny = 428,\n\tLotad = 270,\n\tLoudred = 294,\n\tLucario = 448,\n\tLudicolo = 272,\n\tLugia = 249,\n\tLumineon = 457,\n\tLunatone = 337,\n\tLuvdisc = 370,\n\tLuxio = 404,\n\tLuxray = 405,\n\tMachamp = 68,\n\tMachoke = 67,\n\tMachop = 66,\n\tMagby = 240,\n\tMagcargo = 219,\n\tMagikarp = 129,\n\tMagmar = 126,\n\tMagmortar = 467,\n\tMagnemite = 81,\n\tMagneton = 82,\n\tMagnezone = 462,\n\tMakuhita = 296,\n\tMamoswine = 473,\n\tManaphy = 490,\n\tMandibuzz = 630,\n\tManectric = 310,\n\tMankey = 56,\n\tMantine = 226,\n\tMantyke = 458,\n\tMaractus = 556,\n\tMareep = 179,\n\tMarill = 183,\n\tMarowak = 105,\n\tMarshtomp = 259,\n\tMasquerain = 284,\n\tMawile = 303,\n\tMedicham = 308,\n\tMeditite = 307,\n\tMeganium = 154,\n\tMeloetta = 648,\n\tMeowth = 52,\n\tMesprit = 481,\n\tMetagross = 376,\n\tMetang = 375,\n\tMetapod = 11,\n\tMew = 151,\n\tMewtwo = 150,\n\tMienfoo = 619,\n\tMienshao = 620,\n\tMightyena = 262,\n\tMilotic = 350,\n\tMiltank = 241,\n\tMimeJr = 439,\n\tMinccino = 572,\n\tMinun = 312,\n\tMisdreavus = 200,\n\tMismagius = 429,\n\tMoltres = 146,\n\tMonferno = 391,\n\tMothim = 414,\n\tMrMime = 122,\n\tMudkip = 258,\n\tMuk = 89,\n\tMunchlax = 446,\n\tMunna = 517,\n\tMurkrow = 198,\n\tMusharna = 518,\n\tNatu = 177,\n\tNidoking = 34,\n\tNidoqueen = 31,\n\tNidoran_F = 29,\n\tNidoran_M = 32,\n\tNidorina = 30,\n\tNidorino = 33,\n\tNincada = 290,\n\tNinetales = 38,\n\tNinjask = 291,\n\tNoctowl = 164,\n\tNosepass = 299,\n\tNumel = 322,\n\tNuzleaf = 274,\n\tOctillery = 224,\n\tOddish = 43,\n\tOmanyte = 138,\n\tOmastar = 139,\n\tOnix = 95,\n\tOshawott = 501,\n\tPachirisu = 417,\n\tPalkia = 484,\n\tPalpitoad = 536,\n\tPanpour = 515,\n\tPansage = 511,\n\tPansear = 513,\n\tParas = 46,\n\tParasect = 47,\n\tPatrat = 504,\n\tPawniard = 624,\n\tPelipper = 279,\n\tPersian = 53,\n\tPetilil = 548,\n\tPhanpy = 231,\n\tPhione = 489,\n\tPichu = 172,\n\tPidgeot = 18,\n\tPidgeotto = 17,\n\tPidgey = 16,\n\tPidove = 519,\n\tPignite = 499,\n\tPikachu = 25,\n\tPiloswine = 221,\n\tPineco = 204,\n\tPinsir = 127,\n\tPiplup = 393,\n\tPlusle = 311,\n\tPolitoed = 186,\n\tPoliwag = 60,\n\tPoliwhirl = 61,\n\tPoliwrath = 62,\n\tPonyta = 77,\n\tPoochyena = 261,\n\tPorygon = 137,\n\tPorygon2 = 233,\n\tPorygonZ = 474,\n\tPrimeape = 57,\n\tPrinplup = 394,\n\tProbopass = 476,\n\tPsyduck = 54,\n\tPupitar = 247,\n\tPurrloin = 509,\n\tPurugly = 432,\n\tQuagsire = 195,\n\tQuilava = 156,\n\tQwilfish = 211,\n\tRaichu = 26,\n\tRaikou = 243,\n\tRalts = 280,\n\tRampardos = 409,\n\tRapidash = 78,\n\tRaticate = 20,\n\tRattata = 19,\n\tRayquaza = 384,\n\tRegice = 378,\n\tRegigigas = 486,\n\tRegirock = 377,\n\tRegisteel = 379,\n\tRelicanth = 369,\n\tRemoraid = 223,\n\tReshiram = 643,\n\tReuniclus = 579,\n\tRhydon = 112,\n\tRhyhorn = 111,\n\tRhyperior = 464,\n\tRiolu = 447,\n\tRoggenrola = 524,\n\tRoselia = 315,\n\tRoserade = 407,\n\tRotom = 479,\n\tRufflet = 627,\n\tSableye = 302,\n\tSalamence = 373,\n\tSamurott = 503,\n\tSandile = 551,\n\tSandshrew = 27,\n\tSandslash = 28,\n\tSawk = 539,\n\tSawsbuck = 586,\n\tSceptile = 254,\n\tScizor = 212,\n\tScolipede = 545,\n\tScrafty = 560,\n\tScraggy = 559,\n\tScyther = 123,\n\tSeadra = 117,\n\tSeaking = 119,\n\tSealeo = 364,\n\tSeedot = 273,\n\tSeel = 86,\n\tSeismitoad = 537,\n\tSentret = 161,\n\tSerperior = 497,\n\tServine = 496,\n\tSeviper = 336,\n\tSewaddle = 540,\n\tSharpedo = 319,\n\tShaymin = 492,\n\tShedinja = 292,\n\tShelgon = 372,\n\tShellder = 90,\n\tShellos = 422,\n\tShelmet = 616,\n\tShieldon = 410,\n\tShiftry = 275,\n\tShinx = 403,\n\tShroomish = 285,\n\tShuckle = 213,\n\tShuppet = 353,\n\tSigilyph = 561,\n\tSilcoon = 266,\n\tSimipour = 516,\n\tSimisage = 512,\n\tSimisear = 514,\n\tSkarmory = 227,\n\tSkiploom = 188,\n\tSkitty = 300,\n\tSkorupi = 451,\n\tSkuntank = 435,\n\tSlaking = 289,\n\tSlakoth = 287,\n\tSlowbro = 80,\n\tSlowking = 199,\n\tSlowpoke = 79,\n\tSlugma = 218,\n\tSmeargle = 235,\n\tSmoochum = 238,\n\tSneasel = 215,\n\tSnivy = 495,\n\tSnorlax = 143,\n\tSnorunt = 361,\n\tSnover = 459,\n\tSnubbull = 209,\n\tSolosis = 577,\n\tSolrock = 338,\n\tSpearow = 21,\n\tSpheal = 363,\n\tSpinarak = 167,\n\tSpinda = 327,\n\tSpiritomb = 442,\n\tSpoink = 325,\n\tSquirtle = 7,\n\tStantler = 234,\n\tStaraptor = 398,\n\tStaravia = 397,\n\tStarly = 396,\n\tStarmie = 121,\n\tStaryu = 120,\n\tSteelix = 208,\n\tStoutland = 508,\n\tStunfisk = 618,\n\tStunky = 434,\n\tSudowoodo = 185,\n\tSuicune = 245,\n\tSunflora = 192,\n\tSunkern = 191,\n\tSurskit = 283,\n\tSwablu = 333,\n\tSwadloon = 541,\n\tSwalot = 317,\n\tSwampert = 260,\n\tSwanna = 581,\n\tSwellow = 277,\n\tSwinub = 220,\n\tSwoobat = 528,\n\tTaillow = 276,\n\tTangela = 114,\n\tTangrowth = 465,\n\tTauros = 128,\n\tTeddiursa = 216,\n\tTentacool = 72,\n\tTentacruel = 73,\n\tTepig = 498,\n\tTerrakion = 639,\n\tThroh = 538,\n\tThundurus = 642,\n\tTimburr = 532,\n\tTirtouga = 564,\n\tTogekiss = 468,\n\tTogepi = 175,\n\tTogetic = 176,\n\tTorchic = 255,\n\tTorkoal = 324,\n\tTornadus = 641,\n\tTorterra = 389,\n\tTotodile = 158,\n\tToxicroak = 454,\n\tTranquill = 520,\n\tTrapinch = 328,\n\tTreecko = 252,\n\tTropius = 357,\n\tTrubbish = 568,\n\tTurtwig = 387,\n\tTympole = 535,\n\tTynamo = 602,\n\tTyphlosion = 157,\n\tTyranitar = 248,\n\tTyrogue = 236,\n\tUmbreon = 197,\n\tUnfezant = 521,\n\tUnown = 201,\n\tUrsaring = 217,\n\tUxie = 480,\n\tVanillish = 583,\n\tVanillite = 582,\n\tVanilluxe = 584,\n\tVaporeon = 134,\n\tVenipede = 543,\n\tVenomoth = 49,\n\tVenonat = 48,\n\tVenusaur = 3,\n\tVespiquen = 416,\n\tVibrava = 329,\n\tVictini = 494,\n\tVictreebel = 71,\n\tVigoroth = 288,\n\tVileplume = 45,\n\tVirizion = 640,\n\tVolbeat = 313,\n\tVolcarona = 637,\n\tVoltorb = 100,\n\tVullaby = 629,\n\tVulpix = 37,\n\tWailmer = 320,\n\tWailord = 321,\n\tWalrein = 365,\n\tWartortle = 8,\n\tWatchog = 505,\n\tWeavile = 461,\n\tWeedle = 13,\n\tWeepinbell = 70,\n\tWeezing = 110,\n\tWhimsicott = 547,\n\tWhirlipede = 544,\n\tWhiscash = 340,\n\tWhismur = 293,\n\tWigglytuff = 40,\n\tWingull = 278,\n\tWobbuffet = 202,\n\tWoobat = 527,\n\tWooper = 194,\n\tWormadam = 413,\n\tWurmple = 265,\n\tWynaut = 360,\n\tXatu = 178,\n\tYamask = 562,\n\tYanma = 193,\n\tYanmega = 469,\n\tZangoose = 335,\n\tZapdos = 145,\n\tZebstrika = 523,\n\tZekrom = 644,\n\tZigzagoon = 263,\n\tZoroark = 571,\n\tZorua = 570,\n\tZubat = 41,\n\tZweilous = 634,\n\tMAX = 650\n}\npublic enum PBEMoveTarget : byte\n{\n\tAll,                   // Every battler (Ex. Perish Song)\n\tAllFoes,               // Every foe (Ex. Stealth Rock)\n\tAllFoesSurrounding,    // All foes surrounding (Ex. Growl)\n\tAllSurrounding,        // All battlers surrounding (Ex. Earthquake)\n\tAllTeam,               // User's entire team (Ex. Light Screen)\n\tRandomFoeSurrounding,  // Randomly picks a surrounding foe (Ex. Outrage)\n\tSelf,                  // Self (Ex. Growth)\n\tSelfOrAllySurrounding, // Self or adjacent ally (Ex. Acupressure)\n\tSingleAllySurrounding, // Adjacent ally (Ex. Helping Hand)\n\tSingleFoeSurrounding,  // Single foe surrounding (Ex. Me First)\n\tSingleNotSelf,         // Single battler except itself (Ex. Dark Pulse)\n\tSingleSurrounding,     // Single battler surrounding (Ex. Tackle)\n\tVaries                 // Possible targets vary (Ex. Curse)\n}\n/// <summary>Represents a specific <see cref=\"PBEMove\"/>'s flags.</summary>\n[Flags]\npublic enum PBEMoveFlag : ulong\n{\n\t/// <summary>The move has no flags.</summary>\n\tNone,\n\t/// <summary>The move's power is boosted by <see cref=\"PBEAbility.IronFist\"/>.</summary>\n\tAffectedByIronFist = 1 << 0,\n\tAffectedByMagicCoat = 1 << 1,\n\tAffectedByMirrorMove = 1 << 2,\n\tAffectedByProtect = 1 << 3,\n\t/// <summary>The move's power is boosted by <see cref=\"PBEAbility.Reckless\"/>.</summary>\n\tAffectedByReckless = 1 << 4,\n\tAffectedBySnatch = 1 << 5,\n\t/// <summary>The move is blocked by <see cref=\"PBEAbility.Soundproof\"/>.</summary>\n\tAffectedBySoundproof = 1 << 6,\n\t/// <summary>The move always lands a critical hit.</summary>\n\tAlwaysCrit = 1 << 7,\n\tBlockedByGravity = 1 << 8,\n\tBlockedFromAssist = 1 << 9,\n\tBlockedFromCopycat = 1 << 10,\n\tBlockedFromMeFirst = 1 << 12,\n\tBlockedFromMetronome = 1 << 13,\n\tBlockedFromMimic = 1 << 14,\n\tBlockedFromSketch = 1 << 15,\n\tBlockedFromSketchWhenSuccessful = 1 << 16,\n\tBlockedFromSleepTalk = 1 << 17,\n\t/// <summary>The move removes <see cref=\"PBEStatus1.Frozen\"/> from the user.</summary>\n\tDefrostsUser = 1 << 18,\n\tDoubleDamageAirborne = 1 << 19,\n\tDoubleDamageMinimized = 1 << 20,\n\tDoubleDamageUnderground = 1 << 21,\n\tDoubleDamageUnderwater = 1 << 22,\n\tDoubleDamageUserDefenseCurl = 1 << 23,\n\t/// <summary>The move has a higher chance of landing a critical hit.</summary>\n\tHighCritChance = 1 << 24,\n\t/// <summary>The move can hit <see cref=\"PBEStatus2.Airborne\"/> targets.</summary>\n\tHitsAirborne = 1 << 25,\n\t/// <summary>The move can hit <see cref=\"PBEStatus2.Underground\"/> targets.</summary>\n\tHitsUnderground = 1 << 26,\n\t/// <summary>The move can hit <see cref=\"PBEStatus2.Underwater\"/> targets.</summary>\n\tHitsUnderwater = 1 << 27,\n\t/// <summary>The user makes contact with the target, causing it to take damage from the target's <see cref=\"PBEAbility.IronBarbs\"/>, <see cref=\"PBEAbility.RoughSkin\"/>, and <see cref=\"PBEItem.RockyHelmet\"/>.</summary>\n\tMakesContact = 1 << 28,\n\tNeverMissHail = 1 << 29,\n\tNeverMissRain = 1 << 30,\n\tUnaffectedByGems = 1uL << 31 // TODO\n}\npublic enum PBEMoveEffect : byte\n{\n\tAcrobatics,\n\tAttract,\n\tBellyDrum,\n\tBounce,\n\tBrickBreak,\n\tBrine,\n\tBurn,\n\tCamouflage,\n\tChangeTarget_ACC,\n\tChangeTarget_ATK,\n\tChangeTarget_DEF,\n\tChangeTarget_EVA,\n\tChangeTarget_SPATK,\n\tChangeTarget_SPATK__IfAttractionPossible,\n\tChangeTarget_SPDEF,\n\tChangeTarget_SPE,\n\tChipAway,\n\tConfuse,\n\tConversion,\n\tCrushGrip,\n\tCurse,\n\tDig,\n\tDive,\n\tEndeavor,\n\tEntrainment,\n\tEruption,\n\tFacade,\n\tFeint,\n\tFinalGambit,\n\tFlatter,\n\tFlail,\n\tFly,\n\tFocusEnergy,\n\tForesight,\n\tFoulPlay,\n\tFrustration,\n\tGastroAcid,\n\tGrassKnot,\n\tGrowth,\n\tHail,\n\tHaze,\n\tHeatCrash,\n\tHelpingHand,\n\tHex,\n\tHiddenPower,\n\tHit,\n\tHit__2Times,\n\tHit__2Times__MaybePoison,\n\tHit__2To5Times,\n\tHit__MaybeBurn,\n\tHit__MaybeBurn__10PercentFlinch,\n\tHit__MaybeBurnFreezeParalyze,\n\tHit__MaybeConfuse,\n\tHit__MaybeFlinch,\n\tHit__MaybeFreeze,\n\tHit__MaybeFreeze__10PercentFlinch,\n\tHit__MaybeLowerTarget_ACC_By1,\n\tHit__MaybeLowerTarget_ATK_By1,\n\tHit__MaybeLowerTarget_DEF_By1,\n\tHit__MaybeLowerTarget_SPATK_By1,\n\tHit__MaybeLowerTarget_SPDEF_By1,\n\tHit__MaybeLowerTarget_SPDEF_By2,\n\tHit__MaybeLowerTarget_SPE_By1,\n\tHit__MaybeLowerUser_ATK_DEF_By1,\n\tHit__MaybeLowerUser_DEF_SPDEF_By1,\n\tHit__MaybeLowerUser_SPATK_By2,\n\tHit__MaybeLowerUser_SPE_By1,\n\tHit__MaybeLowerUser_SPE_DEF_SPDEF_By1,\n\tHit__MaybeParalyze,\n\tHit__MaybeParalyze__10PercentFlinch,\n\tHit__MaybePoison,\n\tHit__MaybeRaiseUser_ATK_By1,\n\tHit__MaybeRaiseUser_ATK_DEF_SPATK_SPDEF_SPE_By1,\n\tHit__MaybeRaiseUser_DEF_By1,\n\tHit__MaybeRaiseUser_SPATK_By1,\n\tHit__MaybeRaiseUser_SPE_By1,\n\tHit__MaybeToxic,\n\tHPDrain,\n\tHPDrain__RequireSleep,\n\tJudgment,\n\tLeechSeed,\n\tLightScreen,\n\tLockOn,\n\tLowerTarget_ATK_DEF_By1,\n\tLowerTarget_DEF_SPDEF_By1_Raise_ATK_SPATK_SPE_By2,\n\tLuckyChant,\n\tMagnetRise,\n\tMagnitude,\n\tMetronome,\n\tMinimize,\n\tMiracleEye,\n\tMoonlight,\n\tNightmare,\n\tNothing,\n\tOneHitKnockout,\n\tPainSplit,\n\tParalyze,\n\tPayback,\n\tPayDay,\n\tPoison,\n\tPowerTrick,\n\tProtect, // TODO: If the user goes last, fail\n\tPsychUp,\n\tPsyshock,\n\tPsywave,\n\tPunishment,\n\tQuickGuard,\n\tRainDance,\n\tRaiseTarget_ATK_ACC_By1,\n\tRaiseTarget_ATK_DEF_By1,\n\tRaiseTarget_ATK_DEF_ACC_By1,\n\tRaiseTarget_ATK_SPATK_By1,\n\tRaiseTarget_ATK_SPE_By1,\n\tRaiseTarget_DEF_SPDEF_By1,\n\tRaiseTarget_SPATK_SPDEF_By1,\n\tRaiseTarget_SPATK_SPDEF_SPE_By1,\n\tRaiseTarget_SPE_By2_ATK_By1,\n\tRecoil,\n\tRecoil__10PercentBurn,\n\tRecoil__10PercentParalyze,\n\tReflect,\n\tReflectType,\n\tRefresh,\n\tRest,\n\tRestoreTargetHP,\n\tRetaliate,\n\tReturn,\n\tRolePlay,\n\tRoost,\n\tSafeguard,\n\tSandstorm,\n\tSecretPower,\n\tSeismicToss,\n\tSelfdestruct,\n\tSetDamage,\n\tShadowForce,\n\tSimpleBeam,\n\tSketch, // TODO\n\tSleep,\n\tSmellingSalt,\n\tSnore,\n\tSoak,\n\tSpikes,\n\tStealthRock,\n\tStoredPower,\n\tStruggle,\n\tSubstitute,\n\tSuckerPunch,\n\tSunnyDay,\n\tSuperFang,\n\tSwagger,\n\tTailwind,\n\tTechnoBlast,\n\tTeleport, // TODO: Trapping effects & SmokeBall\n\tThunderWave,\n\tToxic,\n\tToxicSpikes,\n\tTransform,\n\tTrickRoom,\n\tVenoshock,\n\tWakeUpSlap,\n\tWeatherBall,\n\tWhirlwind, // TODO: Trapping effects\n\tWideGuard,\n\tWorrySeed,\n\tTODOMOVE // Moves that are not added yet\n}\npublic enum PBEMove : ushort\n{\n\tNone = 0,\n\tAbsorb = 71,\n\tAcid = 51,\n\tAcidArmor = 151,\n\tAcidSpray = 491,\n\tAcrobatics = 512,\n\tAcupressure = 367, // TODO\n\tAerialAce = 332,\n\tAeroblast = 177,\n\tAfterYou = 495, // TODO\n\tAgility = 97,\n\tAirCutter = 314,\n\tAirSlash = 403,\n\tAllySwitch = 502, // TODO\n\tAmnesia = 133,\n\tAncientPower = 246,\n\tAquaJet = 453,\n\tAquaRing = 392, // TODO\n\tAquaTail = 401,\n\tArmThrust = 292,\n\tAromatherapy = 312, // TODO\n\tAssist = 274, // TODO\n\tAssurance = 372, // TODO\n\tAstonish = 310,\n\tAttackOrder = 454,\n\tAttract = 213,\n\tAuraSphere = 396,\n\tAuroraBeam = 62,\n\tAutotomize = 475, // TODO\n\tAvalanche = 419, // TODO\n\tBarrage = 140,\n\tBarrier = 112,\n\tBatonPass = 226, // TODO\n\tBeatUp = 251, // TODO\n\tBellyDrum = 187,\n\tBestow = 516, // TODO\n\tBide = 117, // TODO\n\tBind = 20, // TODO\n\tBite = 44,\n\tBlastBurn = 307, // TODO\n\tBlazeKick = 299,\n\tBlizzard = 59,\n\tBlock = 335, // TODO\n\tBlueFlare = 551,\n\tBodySlam = 34,\n\tBoltStrike = 550,\n\tBoneClub = 125,\n\tBonemerang = 155,\n\tBoneRush = 198,\n\tBounce = 340,\n\tBraveBird = 413,\n\tBrickBreak = 280,\n\tBrine = 362,\n\tBubble = 145,\n\tBubbleBeam = 61,\n\tBugBite = 450, // TODO\n\tBugBuzz = 405,\n\tBulkUp = 339,\n\tBulldoze = 523,\n\tBulletPunch = 418,\n\tBulletSeed = 331,\n\tCalmMind = 347,\n\tCamouflage = 293,\n\tCaptivate = 445,\n\tCharge = 268, // TODO\n\tChargeBeam = 451,\n\tCharm = 204,\n\tChatter = 448,\n\tChipAway = 498,\n\tCircleThrow = 509, // TODO\n\tClamp = 128, // TODO\n\tClearSmog = 499, // TODO\n\tCloseCombat = 370,\n\tCoil = 489,\n\tCometPunch = 4,\n\tConfuseRay = 109,\n\tConfusion = 93,\n\tConstrict = 132,\n\tConversion = 160,\n\tConversion2 = 176, // TODO\n\tCopycat = 383, // TODO\n\tCosmicPower = 322,\n\tCottonGuard = 538,\n\tCottonSpore = 178,\n\tCounter = 68, // TODO\n\tCovet = 343, // TODO\n\tCrabhammer = 152,\n\tCrossChop = 238,\n\tCrossPoison = 440,\n\tCrunch = 242,\n\tCrushClaw = 306,\n\tCrushGrip = 462,\n\tCurse = 174,\n\tCut = 15,\n\tDarkPulse = 399,\n\tDarkVoid = 464,\n\tDefendOrder = 455,\n\tDefenseCurl = 111,\n\tDefog = 432, // TODO\n\tDestinyBond = 194, // TODO\n\tDetect = 197,\n\tDig = 91,\n\tDisable = 50, // TODO\n\tDischarge = 435,\n\tDive = 291,\n\tDizzyPunch = 146,\n\tDoomDesire = 353, // TODO\n\tDoubleEdge = 38,\n\tDoubleHit = 458,\n\tDoubleKick = 24,\n\tDoubleSlap = 3,\n\tDoubleTeam = 104,\n\tDracoMeteor = 434,\n\tDragonBreath = 225,\n\tDragonClaw = 337,\n\tDragonDance = 349,\n\tDragonPulse = 406,\n\tDragonRage = 82,\n\tDragonRush = 407,\n\tDragonTail = 525, // TODO\n\tDrainPunch = 409,\n\tDreamEater = 138,\n\tDrillPeck = 65,\n\tDrillRun = 529,\n\tDualChop = 530,\n\tDynamicPunch = 223,\n\tEarthPower = 414,\n\tEarthquake = 89,\n\tEchoedVoice = 497, // TODO\n\tEggBomb = 121,\n\tElectroBall = 486, // TODO\n\tElectroweb = 527,\n\tEmbargo = 373, // TODO\n\tEmber = 52,\n\tEncore = 227, // TODO\n\tEndeavor = 283,\n\tEndure = 203, // TODO\n\tEnergyBall = 412,\n\tEntrainment = 494,\n\tEruption = 284,\n\tExplosion = 153,\n\tExtrasensory = 326,\n\tExtremeSpeed = 245,\n\tFacade = 263,\n\tFaintAttack = 185,\n\tFakeOut = 252, // TODO\n\tFakeTears = 313,\n\tFalseSwipe = 206, // TODO\n\tFeatherDance = 297,\n\tFeint = 364,\n\tFieryDance = 552,\n\tFinalGambit = 515,\n\tFireBlast = 126,\n\tFireFang = 424,\n\tFirePledge = 519, // TODO\n\tFirePunch = 7,\n\tFireSpin = 83, // TODO\n\tFissure = 90,\n\tFlail = 175,\n\tFlameBurst = 481, // TODO\n\tFlameCharge = 488,\n\tFlamethrower = 53,\n\tFlameWheel = 172,\n\tFlareBlitz = 394,\n\tFlash = 148,\n\tFlashCannon = 430,\n\tFlatter = 260,\n\tFling = 374, // TODO\n\tFly = 19,\n\tFocusBlast = 411,\n\tFocusEnergy = 116,\n\tFocusPunch = 264, // TODO\n\tFollowMe = 266, // TODO\n\tForcePalm = 395,\n\tForesight = 193,\n\tFoulPlay = 492,\n\tFreezeShock = 553, // TODO\n\tFrenzyPlant = 338, // TODO\n\tFrostBreath = 524,\n\tFrustration = 218,\n\tFuryAttack = 31,\n\tFuryCutter = 210, // TODO\n\tFurySwipes = 154,\n\tFusionBolt = 559, // TODO\n\tFusionFlare = 558, // TODO\n\tFutureSight = 248, // TODO\n\tGastroAcid = 380, // TODO: Magic Bounce, Magic Coat\n\tGearGrind = 544,\n\tGigaDrain = 202,\n\tGigaImpact = 416, // TODO\n\tGlaciate = 549,\n\tGlare = 137,\n\tGrassKnot = 447,\n\tGrassPledge = 520, // TODO\n\tGrassWhistle = 320,\n\tGravity = 356, // TODO\n\tGrowl = 45,\n\tGrowth = 74,\n\tGrudge = 288, // TODO\n\tGuardSplit = 470, // TODO\n\tGuardSwap = 385, // TODO\n\tGuillotine = 12,\n\tGunkShot = 441,\n\tGust = 16,\n\tGyroBall = 360, // TODO\n\tHail = 258,\n\tHammerArm = 359,\n\tHarden = 106,\n\tHaze = 114,\n\tHeadbutt = 29,\n\tHeadCharge = 543,\n\tHeadSmash = 457,\n\tHealBell = 215, // TODO\n\tHealBlock = 377, // TODO\n\tHealingWish = 361, // TODO\n\tHealOrder = 456,\n\tHealPulse = 505,\n\tHeartStamp = 531,\n\tHeartSwap = 391, // TODO\n\tHeatCrash = 535,\n\tHeatWave = 257,\n\tHeavySlam = 484,\n\tHelpingHand = 270,\n\tHex = 506,\n\tHiddenPower = 237,\n\tHiJumpKick = 136, // TODO\n\tHoneClaws = 468,\n\tHornAttack = 30,\n\tHornDrill = 32,\n\tHornLeech = 532,\n\tHowl = 336,\n\tHurricane = 542,\n\tHydroCannon = 308, // TODO\n\tHydroPump = 56,\n\tHyperBeam = 63, // TODO\n\tHyperFang = 158,\n\tHyperVoice = 304,\n\tHypnosis = 95,\n\tIceBall = 301, // TODO\n\tIceBeam = 58,\n\tIceBurn = 554, // TODO\n\tIceFang = 423,\n\tIcePunch = 8,\n\tIceShard = 420,\n\tIcicleCrash = 556,\n\tIcicleSpear = 333,\n\tIcyWind = 196,\n\tImprison = 286, // TODO\n\tIncinerate = 510, // TODO\n\tInferno = 517,\n\tIngrain = 275, // TODO\n\tIronDefense = 334,\n\tIronHead = 442,\n\tIronTail = 231,\n\tJudgment = 449,\n\tJumpKick = 26, // TODO\n\tKarateChop = 2,\n\tKinesis = 134,\n\tKnockOff = 282, // TODO\n\tLastResort = 387, // TODO\n\tLavaPlume = 436,\n\tLeafBlade = 348,\n\tLeafStorm = 437,\n\tLeafTornado = 536,\n\tLeechLife = 141,\n\tLeechSeed = 73,\n\tLeer = 43,\n\tLick = 122,\n\tLightScreen = 113,\n\tLockOn = 199,\n\tLovelyKiss = 142,\n\tLowKick = 67,\n\tLowSweep = 490,\n\tLuckyChant = 381,\n\tLunarDance = 461, // TODO\n\tLusterPurge = 295,\n\tMachPunch = 183,\n\tMagicalLeaf = 345,\n\tMagicCoat = 277, // TODO\n\tMagicRoom = 478, // TODO\n\tMagmaStorm = 463, // TODO\n\tMagnetBomb = 443,\n\tMagnetRise = 393,\n\tMagnitude = 222,\n\tMeanLook = 212, // TODO\n\tMeditate = 96,\n\tMeFirst = 382, // TODO & TODO: Sucker Punch\n\tMegaDrain = 72,\n\tMegahorn = 224,\n\tMegaKick = 25,\n\tMegaPunch = 5,\n\tMemento = 262, // TODO\n\tMetalBurst = 368, // TODO\n\tMetalClaw = 232,\n\tMetalSound = 319,\n\tMeteorMash = 309,\n\tMetronome = 118,\n\tMilkDrink = 208,\n\tMimic = 102, // TODO\n\tMindReader = 170,\n\tMinimize = 107,\n\tMiracleEye = 357,\n\tMirrorCoat = 243, // TODO\n\tMirrorMove = 119, // TODO\n\tMirrorShot = 429,\n\tMist = 54, // TODO & TODO: Infiltrator\n\tMistBall = 296,\n\tMoonlight = 236,\n\tMorningSun = 234,\n\tMudBomb = 426,\n\tMuddyWater = 330,\n\tMudShot = 341,\n\tMudSlap = 189,\n\tMudSport = 300, // TODO\n\tNastyPlot = 417,\n\tNaturalGift = 363, // TODO\n\tNaturePower = 267, // TODO\n\tNeedleArm = 302,\n\tNightDaze = 539,\n\tNightmare = 171,\n\tNightShade = 101,\n\tNightSlash = 400,\n\tOctazooka = 190,\n\tOdorSleuth = 316,\n\tOminousWind = 466,\n\tOutrage = 200, // TODO\n\tOverheat = 315,\n\tPainSplit = 220,\n\tPayback = 371, // TODO: If the target used an item instead of a move\n\tPayDay = 6,\n\tPeck = 64,\n\tPerishSong = 195, // TODO\n\tPetalDance = 80, // TODO\n\tPinMissile = 42,\n\tPluck = 365, // TODO\n\tPoisonFang = 305,\n\tPoisonGas = 139,\n\tPoisonJab = 398,\n\tPoisonPowder = 77,\n\tPoisonSting = 40,\n\tPoisonTail = 342,\n\tPound = 1,\n\tPowderSnow = 181,\n\tPowerGem = 408,\n\tPowerSplit = 471, // TODO\n\tPowerSwap = 384, // TODO\n\tPowerTrick = 379,\n\tPowerWhip = 438,\n\tPresent = 217, // TODO\n\tProtect = 182,\n\tPsybeam = 60,\n\tPsychic = 94,\n\tPsychoBoost = 354,\n\tPsychoCut = 427,\n\tPsychoShift = 375, // TODO\n\tPsychUp = 244,\n\tPsyshock = 473,\n\tPsystrike = 540,\n\tPsywave = 149,\n\tPunishment = 386,\n\tPursuit = 228, // TODO\n\tQuash = 511, // TODO\n\tQuickAttack = 98,\n\tQuickGuard = 501,\n\tQuiverDance = 483,\n\tRage = 99, // TODO\n\tRagePowder = 476, // TODO\n\tRainDance = 240,\n\tRapidSpin = 229, // TODO\n\tRazorLeaf = 75,\n\tRazorShell = 534,\n\tRazorWind = 13, // TODO\n\tRecover = 105,\n\tRecycle = 278, // TODO\n\tReflect = 115,\n\tReflectType = 513,\n\tRefresh = 287,\n\tRelicSong = 547, // TODO\n\tRest = 156, // TODO: Uproar, Leaf Guard\n\tRetaliate = 514,\n\tReturn = 216,\n\tRevenge = 279, // TODO\n\tReversal = 179,\n\tRoar = 46, // TODO: Suction Cups, Soundproof, Ingrain\n\tRoarOfTime = 459, // TODO\n\tRockBlast = 350,\n\tRockClimb = 431,\n\tRockPolish = 397,\n\tRockSlide = 157,\n\tRockSmash = 249,\n\tRockThrow = 88,\n\tRockTomb = 317,\n\tRockWrecker = 439, // TODO\n\tRolePlay = 272,\n\tRollingKick = 27,\n\tRollout = 205, // TODO\n\tRoost = 355,\n\tRound = 496, // TODO\n\tSacredFire = 221,\n\tSacredSword = 533,\n\tSafeguard = 219,\n\tSandAttack = 28,\n\tSandstorm = 201,\n\tSandTomb = 328, // TODO\n\tScald = 503,\n\tScaryFace = 184,\n\tScratch = 10,\n\tScreech = 103,\n\tSearingShot = 545,\n\tSecretPower = 290,\n\tSecretSword = 548,\n\tSeedBomb = 402,\n\tSeedFlare = 465,\n\tSeismicToss = 69,\n\tSelfdestruct = 120,\n\tShadowBall = 247,\n\tShadowClaw = 421,\n\tShadowForce = 467,\n\tShadowPunch = 325,\n\tShadowSneak = 425,\n\tSharpen = 159,\n\tSheerCold = 329,\n\tShellSmash = 504,\n\tShiftGear = 508,\n\tShockWave = 351,\n\tSignalBeam = 324,\n\tSilverWind = 318,\n\tSimpleBeam = 493,\n\tSing = 47,\n\tSketch = 166, // TODO\n\tSkillSwap = 285, // TODO\n\tSkullBash = 130, // TODO\n\tSkyAttack = 143, // TODO\n\tSkyDrop = 507, // TODO\n\tSkyUppercut = 327,\n\tSlackOff = 303,\n\tSlam = 21,\n\tSlash = 163,\n\tSleepPowder = 79,\n\tSleepTalk = 214, // TODO & TODO: Moves such as ice ball/outrage that last multiple turns are not locked, even upon waking up (is this the same for metronome etc?)\n\tSludge = 124,\n\tSludgeBomb = 188,\n\tSludgeWave = 482,\n\tSmackDown = 479, // TODO\n\tSmellingSalt = 265,\n\tSmog = 123,\n\tSmokeScreen = 108,\n\tSnarl = 555,\n\tSnatch = 289, // TODO\n\tSnore = 173,\n\tSoak = 487,\n\tSoftboiled = 135,\n\tSolarBeam = 76, // TODO\n\tSonicBoom = 49,\n\tSpacialRend = 460,\n\tSpark = 209,\n\tSpiderWeb = 169, // TODO\n\tSpikeCannon = 131,\n\tSpikes = 191,\n\tSpite = 180, // TODO\n\tSpitUp = 255, // TODO\n\tSplash = 150,\n\tSpore = 147,\n\tStealthRock = 446,\n\tSteamroller = 537,\n\tSteelWing = 211,\n\tStockpile = 254, // TODO\n\tStomp = 23,\n\tStoneEdge = 444,\n\tStoredPower = 500,\n\tStormThrow = 480,\n\tStrength = 70,\n\tStringShot = 81,\n\tStruggle = 165,\n\tStruggleBug = 522,\n\tStunSpore = 78,\n\tSubmission = 66,\n\tSubstitute = 164,\n\tSuckerPunch = 389,\n\tSunnyDay = 241,\n\tSuperFang = 162,\n\tSuperpower = 276,\n\tSupersonic = 48,\n\tSurf = 57,\n\tSwagger = 207,\n\tSwallow = 256, // TODO\n\tSweetKiss = 186,\n\tSweetScent = 230,\n\tSwift = 129,\n\tSwitcheroo = 415, // TODO\n\tSwordsDance = 14,\n\tSynchronoise = 485, // TODO\n\tSynthesis = 235,\n\tTackle = 33,\n\tTailGlow = 294,\n\tTailSlap = 541,\n\tTailWhip = 39,\n\tTailwind = 366,\n\tTakeDown = 36,\n\tTaunt = 269, // TODO\n\tTechnoBlast = 546,\n\tTeeterDance = 298,\n\tTelekinesis = 477, // TODO\n\tTeleport = 100,\n\tThief = 168, // TODO\n\tThrash = 37, // TODO\n\tThunder = 87,\n\tThunderbolt = 85,\n\tThunderFang = 422,\n\tThunderPunch = 9,\n\tThunderShock = 84,\n\tThunderWave = 86,\n\tTickle = 321,\n\tTorment = 259, // TODO\n\tToxic = 92,\n\tToxicSpikes = 390,\n\tTransform = 144,\n\tTriAttack = 161,\n\tTrick = 271, // TODO\n\tTrickRoom = 433,\n\tTripleKick = 167, // TODO\n\tTrumpCard = 376, // TODO\n\tTwineedle = 41,\n\tTwister = 239,\n\tUproar = 253, // TODO\n\tUturn = 369, // TODO\n\tVacuumWave = 410,\n\tVCreate = 557,\n\tVenoshock = 474,\n\tViceGrip = 11,\n\tVineWhip = 22,\n\tVitalThrow = 233,\n\tVoltSwitch = 521, // TODO\n\tVoltTackle = 344,\n\tWakeUpSlap = 358,\n\tWaterfall = 127,\n\tWaterGun = 55,\n\tWaterPledge = 518, // TODO\n\tWaterPulse = 352,\n\tWaterSport = 346, // TODO\n\tWaterSpout = 323,\n\tWeatherBall = 311,\n\tWhirlpool = 250, // TODO\n\tWhirlwind = 18, // TODO: Suction Cups, Ingrain\n\tWideGuard = 469,\n\tWildCharge = 528,\n\tWillOWisp = 261,\n\tWingAttack = 17,\n\tWish = 273, // TODO\n\tWithdraw = 110,\n\tWonderRoom = 472, // TODO\n\tWoodHammer = 452,\n\tWorkUp = 526,\n\tWorrySeed = 388,\n\tWrap = 35, // TODO\n\tWringOut = 378,\n\tXScissor = 404,\n\tYawn = 281, // TODO\n\tZapCannon = 192,\n\tZenHeadbutt = 428,\n\t/// <summary>Invalid move.</summary>\n\tMAX = 560\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Data/DataProvider.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data.Legality;\nusing Kermalis.PokemonBattleEngine.Utils;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace Kermalis.PokemonBattleEngine.Data;\n\npublic abstract class PBEDataProvider\n{\n\tpublic static PBEDataProvider Instance { get; private set; } = null!;\n\n\tpublic static PBELanguage GlobalLanguage { get; private set; } = default;\n\tpublic static PBERandom GlobalRandom { get; private set; } = null!;\n\n\tprotected PBEDataProvider(PBELanguage language, PBERandom rand)\n\t{\n\t\tif (language >= PBELanguage.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(language));\n\t\t}\n\t\tGlobalLanguage = language;\n\t\tGlobalRandom = rand;\n\t\tInstance = this;\n\t}\n\n\t#region Data\n\n\tpublic abstract bool IsBerry(PBEItem item);\n\tpublic abstract IPBEBerryData GetBerryData(PBEItem item, bool cache = true);\n\tpublic abstract bool TryGetBerryData(PBEItem item, [NotNullWhen(true)] out IPBEBerryData? bData, bool cache = true);\n\tpublic abstract IPBEItemData GetItemData(PBEItem item, bool cache = true);\n\tpublic abstract IPBEMoveData GetMoveData(PBEMove move, bool cache = true);\n\tpublic abstract bool HasEvolutions(PBESpecies species, PBEForm form, bool cache = true);\n\tpublic virtual bool HasEvolutions(IPBESpeciesForm pkmn, bool cache = true)\n\t{\n\t\treturn HasEvolutions(pkmn.Species, pkmn.Form, cache);\n\t}\n\tpublic abstract IPBEPokemonData GetPokemonData(PBESpecies species, PBEForm form, bool cache = true);\n\n\tpublic abstract int GetSpeciesCaught();\n\n\t/// <summary>Used by <see cref=\"PBELegalMoveset\"/>.</summary>\n\tpublic abstract IReadOnlyCollection<PBEMove> GetLegalMoves(PBESpecies species, PBEForm form, byte level);\n\n\tpublic virtual IPBEPokemonData GetPokemonData(IPBESpeciesForm pkmn, bool cache = true)\n\t{\n\t\treturn GetPokemonData(pkmn.Species, pkmn.Form, cache: cache);\n\t}\n\n\t#endregion\n\n\t#region EXP\n\n\tpublic abstract uint GetEXPRequired(PBEGrowthRate type, byte level);\n\tpublic abstract byte GetEXPLevel(PBEGrowthRate type, uint exp);\n\t/// <summary>This is the boost to the EXP rate. In generation 5, Pass Powers boost the EXP rate.</summary>\n\tpublic abstract float GetEXPModifier(PBEBattle battle);\n\t/// <summary>In generation 5, this is 1 for ot, 1.5 for domestic trade, and 1.7 for international trade.</summary>\n\tpublic abstract float GetEXPTradeModifier(PBEBattlePokemon pkmn);\n\n\t#endregion\n\n\t#region Catching\n\n\tpublic abstract bool IsDarkGrass(PBEBattle battle);\n\tpublic abstract bool IsDuskBallSetting(PBEBattle battle);\n\tpublic abstract bool IsFishing(PBEBattle battle);\n\tpublic abstract bool IsGuaranteedCapture(PBEBattle battle, PBESpecies species, PBEForm form);\n\tpublic abstract bool IsMoonBallFamily(PBESpecies species, PBEForm form);\n\tpublic abstract bool IsRepeatBallSpecies(PBESpecies species);\n\tpublic abstract bool IsSurfing(PBEBattle battle);\n\tpublic abstract bool IsUnderwater(PBEBattle battle);\n\t/// <summary>This is the boost to the catch rate. In generation 5, Capture Powers boost the catch rate.</summary>\n\tpublic abstract float GetCatchRateModifier(PBEBattle battle);\n\n\tpublic virtual bool IsGuaranteedCapture(PBEBattle battle, IPBESpeciesForm pkmn)\n\t{\n\t\treturn IsGuaranteedCapture(battle, pkmn.Species, pkmn.Form);\n\t}\n\tpublic virtual bool IsMoonBallFamily(IPBESpeciesForm pkmn)\n\t{\n\t\treturn IsMoonBallFamily(pkmn.Species, pkmn.Form);\n\t}\n\n\t#endregion\n\n\t#region LocalizedString\n\n\tpublic abstract bool GetAbilityByName(string abilityName, [NotNullWhen(true)] out PBEAbility? ability);\n\tpublic abstract IPBEReadOnlyLocalizedString GetAbilityName(PBEAbility ability);\n\tpublic abstract bool GetFormByName(PBESpecies species, string formName, [NotNullWhen(true)] out PBEForm? form);\n\tpublic abstract IPBEReadOnlyLocalizedString GetFormName(PBESpecies species, PBEForm form);\n\tpublic abstract bool GetGenderByName(string genderName, [NotNullWhen(true)] out PBEGender? gender);\n\tpublic abstract IPBEReadOnlyLocalizedString GetGenderName(PBEGender gender);\n\tpublic abstract bool GetItemByName(string itemName, [NotNullWhen(true)] out PBEItem? item);\n\tpublic abstract IPBEReadOnlyLocalizedString GetItemName(PBEItem item);\n\tpublic abstract bool GetMoveByName(string moveName, [NotNullWhen(true)] out PBEMove? move);\n\tpublic abstract IPBEReadOnlyLocalizedString GetMoveName(PBEMove move);\n\tpublic abstract bool GetNatureByName(string natureName, [NotNullWhen(true)] out PBENature? nature);\n\tpublic abstract IPBEReadOnlyLocalizedString GetNatureName(PBENature nature);\n\tpublic abstract bool GetSpeciesByName(string speciesName, [NotNullWhen(true)] out PBESpecies? species);\n\tpublic abstract IPBEReadOnlyLocalizedString GetSpeciesName(PBESpecies species);\n\tpublic abstract bool GetStatByName(string statName, [NotNullWhen(true)] out PBEStat? stat);\n\tpublic abstract IPBEReadOnlyLocalizedString GetStatName(PBEStat stat);\n\tpublic abstract bool GetTypeByName(string typeName, [NotNullWhen(true)] out PBEType? type);\n\tpublic abstract IPBEReadOnlyLocalizedString GetTypeName(PBEType type);\n\n\tpublic virtual IPBEReadOnlyLocalizedString GetFormName(IPBESpeciesForm pkmn)\n\t{\n\t\treturn GetFormName(pkmn.Species, pkmn.Form);\n\t}\n\n\t#endregion\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Data/Interfaces/ItemData.cs",
    "content": "﻿namespace Kermalis.PokemonBattleEngine.Data;\n\npublic interface IPBEItemData\n{\n\t/// <summary>The power <see cref=\"PBEMoveEffect.Fling\"/> has when the user is holding this item. 0 will cause the move to fail.</summary>\n\tbyte FlingPower { get; }\n}\npublic interface IPBEBerryData\n{\n\tbyte Bitterness { get; }\n\tbyte Dryness { get; }\n\tbyte Sourness { get; }\n\tbyte Spicyness { get; }\n\tbyte Sweetness { get; }\n\t/// <summary>The power <see cref=\"PBEMoveEffect.NaturalGift\"/> has when the user is holding this item.</summary>\n\tbyte NaturalGiftPower { get; }\n\t/// <summary>The type <see cref=\"PBEMoveEffect.NaturalGift\"/> becomes when the user is holding this item.</summary>\n\tPBEType NaturalGiftType { get; }\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Data/Interfaces/LocalizedString.cs",
    "content": "﻿using System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Globalization;\n\nnamespace Kermalis.PokemonBattleEngine.Data;\n\npublic interface IPBEReadOnlyLocalizedString\n{\n\tstring English { get; }\n\tstring French { get; }\n\tstring German { get; }\n\tstring Italian { get; }\n\tstring Japanese_Kana { get; }\n\tstring Japanese_Kanji { get; }\n\tstring Korean { get; }\n\tstring Spanish { get; }\n}\npublic interface IPBELocalizedString : IPBEReadOnlyLocalizedString\n{\n\tnew string English { get; set; }\n\tnew string French { get; set; }\n\tnew string German { get; set; }\n\tnew string Italian { get; set; }\n\tnew string Japanese_Kana { get; set; }\n\tnew string Japanese_Kanji { get; set; }\n\tnew string Korean { get; set; }\n\tnew string Spanish { get; set; }\n}\n\npublic static class PBELanguageExtensions\n{\n\tpublic static bool ToPBELanguage(this CultureInfo cultureInfo, [NotNullWhen(true)] out PBELanguage? lang)\n\t{\n\t\tstring id = cultureInfo.TwoLetterISOLanguageName;\n\t\tswitch (id)\n\t\t{\n\t\t\tcase \"en\": lang = PBELanguage.English; return true;\n\t\t\tcase \"fr\": lang = PBELanguage.French; return true;\n\t\t\tcase \"de\": lang = PBELanguage.German; return true;\n\t\t\tcase \"it\": lang = PBELanguage.Italian; return true;\n\t\t\tcase \"ja\": lang = PBELanguage.Japanese_Kana; return true;\n\t\t\tcase \"ko\": lang = PBELanguage.Korean; return true;\n\t\t\tcase \"es\": lang = PBELanguage.Spanish; return true;\n\t\t}\n\t\tlang = default;\n\t\treturn false;\n\t}\n\tpublic static CultureInfo ToCultureInfo(this PBELanguage language)\n\t{\n\t\tswitch (language)\n\t\t{\n\t\t\tcase PBELanguage.English: return CultureInfo.GetCultureInfo(\"en-US\");\n\t\t\tcase PBELanguage.French: return CultureInfo.GetCultureInfo(\"fr-FR\");\n\t\t\tcase PBELanguage.German: return CultureInfo.GetCultureInfo(\"de-DE\");\n\t\t\tcase PBELanguage.Italian: return CultureInfo.GetCultureInfo(\"it-IT\");\n\t\t\tcase PBELanguage.Japanese_Kana:\n\t\t\tcase PBELanguage.Japanese_Kanji: return CultureInfo.GetCultureInfo(\"ja-JP\");\n\t\t\tcase PBELanguage.Korean: return CultureInfo.GetCultureInfo(\"ko-KR\");\n\t\t\tcase PBELanguage.Spanish: return CultureInfo.GetCultureInfo(\"es-ES\");\n\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(language));\n\t\t}\n\t}\n\n\tpublic static string FromGlobalLanguage(this IPBEReadOnlyLocalizedString str)\n\t{\n\t\treturn str.Get(PBEDataProvider.GlobalLanguage);\n\t}\n\tpublic static string Get(this IPBEReadOnlyLocalizedString str, PBELanguage lang)\n\t{\n\t\tswitch (lang)\n\t\t{\n\t\t\tcase PBELanguage.English: return str.English;\n\t\t\tcase PBELanguage.French: return str.French;\n\t\t\tcase PBELanguage.German: return str.German;\n\t\t\tcase PBELanguage.Italian: return str.Italian;\n\t\t\tcase PBELanguage.Japanese_Kana: return str.Japanese_Kana;\n\t\t\tcase PBELanguage.Japanese_Kanji: return str.Japanese_Kanji;\n\t\t\tcase PBELanguage.Korean: return str.Korean;\n\t\t\tcase PBELanguage.Spanish: return str.Spanish;\n\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(lang));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Data/Interfaces/MoveData.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data.Utils;\n\nnamespace Kermalis.PokemonBattleEngine.Data;\n\npublic interface IPBEMoveData\n{\n\tPBEType Type { get; }\n\tPBEMoveCategory Category { get; }\n\tsbyte Priority { get; }\n\t/// <summary>0 PPTier will become 1 PP (unaffected by pp ups)</summary>\n\tbyte PPTier { get; }\n\t/// <summary>0 power will show up as --</summary>\n\tbyte Power { get; }\n\t/// <summary>0 accuracy will show up as --</summary>\n\tbyte Accuracy { get; }\n\tPBEMoveEffect Effect { get; }\n\tint EffectParam { get; }\n\tPBEMoveTarget Targets { get; }\n\tPBEMoveFlag Flags { get; }\n}\n\npublic static class PBEMoveDataExtensions\n{\n\tpublic static bool HasSecondaryEffects(this IPBEMoveData mData, PBESettings settings)\n\t{\n\t\treturn PBEDataUtils.HasSecondaryEffects(mData.Effect, settings);\n\t}\n\tpublic static bool IsHPDrainMove(this IPBEMoveData mData)\n\t{\n\t\treturn PBEDataUtils.IsHPDrainMove(mData.Effect);\n\t}\n\tpublic static bool IsHPRestoreMove(this IPBEMoveData mData)\n\t{\n\t\treturn PBEDataUtils.IsHPRestoreMove(mData.Effect);\n\t}\n\tpublic static bool IsMultiHitMove(this IPBEMoveData mData)\n\t{\n\t\treturn PBEDataUtils.IsMultiHitMove(mData.Effect);\n\t}\n\tpublic static bool IsRecoilMove(this IPBEMoveData mData)\n\t{\n\t\treturn PBEDataUtils.IsRecoilMove(mData.Effect);\n\t}\n\tpublic static bool IsSetDamageMove(this IPBEMoveData mData)\n\t{\n\t\treturn PBEDataUtils.IsSetDamageMove(mData.Effect);\n\t}\n\tpublic static bool IsSpreadMove(this IPBEMoveData mData)\n\t{\n\t\treturn PBEDataUtils.IsSpreadMove(mData.Targets);\n\t}\n\tpublic static bool IsWeatherMove(this IPBEMoveData mData)\n\t{\n\t\treturn PBEDataUtils.IsWeatherMove(mData.Effect);\n\t}\n\n\t/// <summary>Temporary check to see if a move is usable, can be removed once all moves are added</summary>\n\tpublic static bool IsMoveUsable(this IPBEMoveData mData)\n\t{\n\t\treturn PBEDataUtils.IsMoveUsable(mData.Effect);\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Data/Interfaces/MovesetInterfaces.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing System.Collections.Generic;\nusing System.Text.Json;\n\nnamespace Kermalis.PokemonBattleEngine.Data;\n\npublic interface IPBEMovesetSlot\n{\n\tPBEMove Move { get; }\n\tbyte PPUps { get; }\n}\npublic interface IPBEPartyMovesetSlot : IPBEMovesetSlot\n{\n\tint PP { get; }\n}\npublic interface IPBEMoveset<T> : IReadOnlyList<T> where T : IPBEMovesetSlot\n{\n\t//\n}\npublic interface IPBEMoveset : IPBEMoveset<IPBEMovesetSlot>\n{\n\t//\n}\npublic interface IPBEPartyMoveset<T> : IReadOnlyList<T> where T : IPBEPartyMovesetSlot\n{\n\t//\n}\npublic interface IPBEPartyMoveset : IPBEPartyMoveset<IPBEPartyMovesetSlot>\n{\n\t//\n}\n\npublic static class PBEMovesetInterfaceExtensions\n{\n\tpublic static int CountMoves(this IPBEMoveset moves)\n\t{\n\t\tint num = 0;\n\t\tfor (int i = 0; i < moves.Count; i++)\n\t\t{\n\t\t\tif (moves[i].Move != PBEMove.None)\n\t\t\t{\n\t\t\t\tnum++;\n\t\t\t}\n\t\t}\n\t\treturn num;\n\t}\n\n\tinternal static void ToBytes(this IPBEMoveset moveset, EndianBinaryWriter w)\n\t{\n\t\tbyte count = (byte)moveset.Count;\n\t\tw.WriteByte(count);\n\t\tfor (int i = 0; i < count; i++)\n\t\t{\n\t\t\tIPBEMovesetSlot slot = moveset[i];\n\t\t\tw.WriteEnum(slot.Move);\n\t\t\tw.WriteByte(slot.PPUps);\n\t\t}\n\t}\n\tinternal static void ToBytes(this IPBEPartyMoveset moveset, EndianBinaryWriter w)\n\t{\n\t\tbyte count = (byte)moveset.Count;\n\t\tw.WriteByte(count);\n\t\tfor (int i = 0; i < count; i++)\n\t\t{\n\t\t\tIPBEPartyMovesetSlot slot = moveset[i];\n\t\t\tw.WriteEnum(slot.Move);\n\t\t\tw.WriteInt32(slot.PP);\n\t\t\tw.WriteByte(slot.PPUps);\n\t\t}\n\t}\n\tinternal static void ToJson(this IPBEMoveset moveset, Utf8JsonWriter w)\n\t{\n\t\tw.WriteStartArray();\n\t\tfor (int i = 0; i < moveset.Count; i++)\n\t\t{\n\t\t\tIPBEMovesetSlot slot = moveset[i];\n\t\t\tw.WriteStartObject();\n\t\t\tw.WriteString(nameof(IPBEMovesetSlot.Move), slot.Move.ToString());\n\t\t\tw.WriteNumber(nameof(IPBEMovesetSlot.PPUps), slot.PPUps);\n\t\t\tw.WriteEndObject();\n\t\t}\n\t\tw.WriteEndArray();\n\t}\n\tinternal static void ToJson(this IPBEPartyMoveset moveset, Utf8JsonWriter w)\n\t{\n\t\tw.WriteStartArray();\n\t\tfor (int i = 0; i < moveset.Count; i++)\n\t\t{\n\t\t\tIPBEPartyMovesetSlot slot = moveset[i];\n\t\t\tw.WriteStartObject();\n\t\t\tw.WriteString(nameof(IPBEPartyMovesetSlot.Move), slot.Move.ToString());\n\t\t\tw.WriteNumber(nameof(IPBEPartyMovesetSlot.PP), slot.PP);\n\t\t\tw.WriteNumber(nameof(IPBEPartyMovesetSlot.PPUps), slot.PPUps);\n\t\t\tw.WriteEndObject();\n\t\t}\n\t\tw.WriteEndArray();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Data/Interfaces/PokemonData.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace Kermalis.PokemonBattleEngine.Data;\n\npublic interface IPBEPokemonData : IPBEPokemonTypes, IPBESpeciesForm\n{\n\tIPBEReadOnlyStatCollection BaseStats { get; }\n\tPBEGenderRatio GenderRatio { get; }\n\tPBEGrowthRate GrowthRate { get; }\n\tushort BaseEXPYield { get; }\n\tbyte CatchRate { get; }\n\tbyte FleeRate { get; }\n\t/// <summary>Weight in Kilograms</summary>\n\tfloat Weight { get; }\n\tIReadOnlyList<PBEAbility> Abilities { get; }\n}\n\npublic static class PBEPokemonDataExtensions\n{\n\tpublic static bool HasAbility(this IPBEPokemonData pData, PBEAbility ability)\n\t{\n\t\tif (ability == PBEAbility.None || ability >= PBEAbility.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(ability));\n\t\t}\n\t\treturn pData.Abilities.Contains(ability);\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Data/Interfaces/PokemonInterfaces.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data.Utils;\nusing System;\nusing System.Collections.Generic;\nusing System.Text.Json;\n\nnamespace Kermalis.PokemonBattleEngine.Data;\n\n// Not separating this into IPBEWildPokemon for these reasons:\n// 1: A lot of work to do that\n// 2: If someone wants to do pal park or catch released Pokémon etc, they'd need all these things\n// 3: If they want just some things (like effort values pre-seeded) they'd also need this\npublic interface IPBEPokemon : IPBESpeciesForm\n{\n\t/// <summary>This marks the Pokémon to be ignored by the battle engine. The Pokémon will be treated like an egg or fainted Pokémon.\n\t/// Therefore, it won't be sent out, copied with <see cref=\"PBEAbility.Illusion\"/>, or count as a battler if the rest of the team faints.</summary>\n\tbool PBEIgnore { get; }\n\tPBEGender Gender { get; }\n\tstring Nickname { get; }\n\tbool Shiny { get; }\n\tbyte Level { get; }\n\tuint EXP { get; }\n\tbool Pokerus { get; }\n\tPBEItem Item { get; }\n\tbyte Friendship { get; }\n\tPBEAbility Ability { get; }\n\tPBENature Nature { get; }\n\tPBEItem CaughtBall { get; }\n\tIPBEStatCollection EffortValues { get; }\n\tIPBEReadOnlyStatCollection IndividualValues { get; }\n\tIPBEMoveset Moveset { get; }\n}\npublic interface IPBEPartyPokemon : IPBEPokemon\n{\n\tushort HP { get; }\n\tPBEStatus1 Status1 { get; }\n\tbyte SleepTurns { get; }\n\tnew IPBEPartyMoveset Moveset { get; }\n}\npublic interface IPBEPokemonCollection<T> : IReadOnlyList<T> where T : IPBEPokemon\n{\n}\npublic interface IPBEPokemonCollection : IReadOnlyList<IPBEPokemon>\n{\n}\npublic interface IPBEPartyPokemonCollection<T> : IReadOnlyList<T> where T : IPBEPartyPokemon\n{\n}\npublic interface IPBEPartyPokemonCollection : IReadOnlyList<IPBEPartyPokemon>\n{\n}\n\npublic interface IPBEPokemonTypes\n{\n\t/// <summary>The Pokémon's first type.</summary>\n\tPBEType Type1 { get; }\n\t/// <summary>The Pokémon's second type.</summary>\n\tPBEType Type2 { get; }\n}\npublic interface IPBEPokemonKnownTypes\n{\n\t/// <summary>The first type everyone believes the Pokémon has.</summary>\n\tPBEType KnownType1 { get; }\n\t/// <summary>The second type everyone believes the Pokémon has.</summary>\n\tPBEType KnownType2 { get; }\n}\npublic interface IPBESpeciesForm\n{\n\tPBESpecies Species { get; }\n\tPBEForm Form { get; }\n}\n\npublic static class PBEPokemonInterfaceExtensions\n{\n\tpublic static bool HasType(this IPBEPokemonTypes pkmn, PBEType type)\n\t{\n\t\tif (type >= PBEType.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(type));\n\t\t}\n\t\treturn pkmn.Type1 == type || pkmn.Type2 == type;\n\t}\n\tpublic static bool HasType_Known(this IPBEPokemonKnownTypes pkmn, PBEType type)\n\t{\n\t\tif (type >= PBEType.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(type));\n\t\t}\n\t\treturn pkmn.KnownType1 == type || pkmn.KnownType2 == type;\n\t}\n\tpublic static bool HasType<T>(this T pkmn, PBEType type, bool useKnownInfo) where T : IPBEPokemonKnownTypes, IPBEPokemonTypes\n\t{\n\t\treturn useKnownInfo ? HasType_Known(pkmn, type) : HasType(pkmn, type);\n\t}\n\tpublic static bool ReceivesSTAB(this IPBEPokemonTypes pkmn, PBEType type)\n\t{\n\t\treturn type != PBEType.None && HasType(pkmn, type);\n\t}\n\tpublic static bool ReceivesSTAB_Known(this IPBEPokemonKnownTypes pkmn, PBEType type)\n\t{\n\t\treturn type != PBEType.None && HasType_Known(pkmn, type);\n\t}\n\tpublic static bool ReceivesSTAB<T>(this T pkmn, PBEType type, bool useKnownInfo) where T : IPBEPokemonKnownTypes, IPBEPokemonTypes\n\t{\n\t\treturn useKnownInfo ? ReceivesSTAB_Known(pkmn, type) : ReceivesSTAB(pkmn, type);\n\t}\n\n\tinternal static void ToBytes(this IPBEPokemon pkmn, EndianBinaryWriter w)\n\t{\n\t\tw.WriteEnum(pkmn.Species);\n\t\tw.WriteEnum(pkmn.Form);\n\t\tw.WriteChars_NullTerminated(pkmn.Nickname);\n\t\tw.WriteByte(pkmn.Level);\n\t\tw.WriteUInt32(pkmn.EXP);\n\t\tw.WriteByte(pkmn.Friendship);\n\t\tw.WriteBoolean(pkmn.Shiny);\n\t\tw.WriteBoolean(pkmn.Pokerus);\n\t\tw.WriteEnum(pkmn.Ability);\n\t\tw.WriteEnum(pkmn.Nature);\n\t\tw.WriteEnum(pkmn.CaughtBall);\n\t\tw.WriteEnum(pkmn.Gender);\n\t\tw.WriteEnum(pkmn.Item);\n\t\tpkmn.EffortValues.ToBytes(w);\n\t\tpkmn.IndividualValues.ToBytes(w);\n\t\tpkmn.Moveset.ToBytes(w);\n\t}\n\tinternal static void ToJson(this IPBEPokemon pkmn, Utf8JsonWriter w)\n\t{\n\t\tw.WriteStartObject();\n\n\t\tPBESpecies species = pkmn.Species;\n\t\tw.WriteString(nameof(IPBEPokemon.Species), species.ToString());\n\t\tif (PBEDataUtils.HasForms(species, true))\n\t\t{\n\t\t\tw.WriteString(nameof(IPBEPokemon.Form), PBEDataUtils.GetNameOfForm(species, pkmn.Form));\n\t\t}\n\t\tw.WriteString(nameof(IPBEPokemon.Nickname), pkmn.Nickname);\n\t\tw.WriteNumber(nameof(IPBEPokemon.Level), pkmn.Level);\n\t\tw.WriteNumber(nameof(IPBEPokemon.EXP), pkmn.EXP);\n\t\tw.WriteNumber(nameof(IPBEPokemon.Friendship), pkmn.Friendship);\n\t\tw.WriteBoolean(nameof(IPBEPokemon.Shiny), pkmn.Shiny);\n\t\tw.WriteBoolean(nameof(IPBEPokemon.Pokerus), pkmn.Pokerus);\n\t\tw.WriteString(nameof(IPBEPokemon.Ability), pkmn.Ability.ToString());\n\t\tw.WriteString(nameof(IPBEPokemon.Nature), pkmn.Nature.ToString());\n\t\tw.WriteString(nameof(IPBEPokemon.CaughtBall), pkmn.CaughtBall.ToString());\n\t\tw.WriteString(nameof(IPBEPokemon.Gender), pkmn.Gender.ToString());\n\t\tw.WriteString(nameof(IPBEPokemon.Item), pkmn.Item.ToString());\n\t\tw.WritePropertyName(nameof(IPBEPokemon.EffortValues));\n\t\tpkmn.EffortValues.ToJson(w);\n\t\tw.WritePropertyName(nameof(IPBEPokemon.IndividualValues));\n\t\tpkmn.IndividualValues.ToJson(w);\n\t\tw.WritePropertyName(nameof(IPBEPokemon.Moveset));\n\t\tpkmn.Moveset.ToJson(w);\n\n\t\tw.WriteEndObject();\n\t}\n\n\tinternal static void ToBytes(this IPBEPokemonCollection party, EndianBinaryWriter w)\n\t{\n\t\tbyte count = (byte)party.Count;\n\t\tw.WriteByte(count);\n\t\tfor (int i = 0; i < count; i++)\n\t\t{\n\t\t\tparty[i].ToBytes(w);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Data/Interfaces/StatInterfaces.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Data.Utils;\nusing System;\nusing System.Text.Json;\n\nnamespace Kermalis.PokemonBattleEngine.Data;\n\npublic interface IPBEReadOnlyStatCollection\n{\n\tbyte HP { get; }\n\tbyte Attack { get; }\n\tbyte Defense { get; }\n\tbyte SpAttack { get; }\n\tbyte SpDefense { get; }\n\tbyte Speed { get; }\n}\npublic interface IPBEStatCollection : IPBEReadOnlyStatCollection\n{\n\tnew byte HP { get; set; }\n\tnew byte Attack { get; set; }\n\tnew byte Defense { get; set; }\n\tnew byte SpAttack { get; set; }\n\tnew byte SpDefense { get; set; }\n\tnew byte Speed { get; set; }\n}\n\npublic static class PBEStatInterfaceExtensions\n{\n\tpublic static byte GetStat(this IPBEReadOnlyStatCollection stats, PBEStat stat)\n\t{\n\t\tswitch (stat)\n\t\t{\n\t\t\tcase PBEStat.HP: return stats.HP;\n\t\t\tcase PBEStat.Attack: return stats.Attack;\n\t\t\tcase PBEStat.Defense: return stats.Defense;\n\t\t\tcase PBEStat.SpAttack: return stats.SpAttack;\n\t\t\tcase PBEStat.SpDefense: return stats.SpDefense;\n\t\t\tcase PBEStat.Speed: return stats.Speed;\n\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(stat));\n\t\t}\n\t}\n\tpublic static void SetStat(this IPBEStatCollection stats, PBEStat stat, byte value)\n\t{\n\t\tswitch (stat)\n\t\t{\n\t\t\tcase PBEStat.HP: stats.HP = value; break;\n\t\t\tcase PBEStat.Attack: stats.Attack = value; break;\n\t\t\tcase PBEStat.Defense: stats.Defense = value; break;\n\t\t\tcase PBEStat.SpAttack: stats.SpAttack = value; break;\n\t\t\tcase PBEStat.SpDefense: stats.SpDefense = value; break;\n\t\t\tcase PBEStat.Speed: stats.Speed = value; break;\n\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(stat));\n\t\t}\n\t}\n\n\tpublic static byte GetHiddenPowerBasePower(this IPBEReadOnlyStatCollection stats, PBESettings settings)\n\t{\n\t\treturn PBEDataUtils.GetHiddenPowerBasePower(stats.HP, stats.Attack, stats.Defense, stats.SpAttack, stats.SpDefense, stats.Speed, settings);\n\t}\n\tpublic static PBEType GetHiddenPowerType(this IPBEReadOnlyStatCollection stats)\n\t{\n\t\treturn PBEDataUtils.GetHiddenPowerType(stats.HP, stats.Attack, stats.Defense, stats.SpAttack, stats.SpDefense, stats.Speed);\n\t}\n\n\tinternal static void ToBytes(this IPBEReadOnlyStatCollection stats, EndianBinaryWriter w)\n\t{\n\t\tw.WriteByte(stats.HP);\n\t\tw.WriteByte(stats.Attack);\n\t\tw.WriteByte(stats.Defense);\n\t\tw.WriteByte(stats.SpAttack);\n\t\tw.WriteByte(stats.SpDefense);\n\t\tw.WriteByte(stats.Speed);\n\t}\n\tinternal static void ToJson(this IPBEReadOnlyStatCollection stats, Utf8JsonWriter w)\n\t{\n\t\tw.WriteStartObject();\n\n\t\tw.WriteNumber(nameof(IPBEReadOnlyStatCollection.HP), stats.HP);\n\t\tw.WriteNumber(nameof(IPBEReadOnlyStatCollection.Attack), stats.Attack);\n\t\tw.WriteNumber(nameof(IPBEReadOnlyStatCollection.Defense), stats.Defense);\n\t\tw.WriteNumber(nameof(IPBEReadOnlyStatCollection.SpAttack), stats.SpAttack);\n\t\tw.WriteNumber(nameof(IPBEReadOnlyStatCollection.SpDefense), stats.SpDefense);\n\t\tw.WriteNumber(nameof(IPBEReadOnlyStatCollection.Speed), stats.Speed);\n\n\t\tw.WriteEndObject();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Data/Legality/LegalEffortValues.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Utils;\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.IO;\nusing System.Linq;\nusing System.Text.Json.Nodes;\n\nnamespace Kermalis.PokemonBattleEngine.Data.Legality;\n\npublic sealed class PBELegalEffortValues : IPBEStatCollection, IEnumerable<PBELegalEffortValues.PBELegalEffortValue>, INotifyPropertyChanged\n{\n\tpublic sealed class PBELegalEffortValue : INotifyPropertyChanged\n\t{\n\t\tprivate void OnPropertyChanged(string property)\n\t\t{\n\t\t\tPropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));\n\t\t}\n\t\tpublic event PropertyChangedEventHandler? PropertyChanged;\n\n\t\tprivate readonly PBELegalEffortValues _parent;\n\n\t\tpublic PBEStat Stat { get; }\n\t\tprivate byte _value;\n\t\tpublic byte Value\n\t\t{\n\t\t\tget => _value;\n\t\t\tset\n\t\t\t{\n\t\t\t\tif (_value != value)\n\t\t\t\t{\n\t\t\t\t\tushort oldTotal = _parent.StatTotal;\n\t\t\t\t\tint newTotal = oldTotal - _value + value;\n\t\t\t\t\tif (newTotal > _parent.Settings.MaxTotalEVs)\n\t\t\t\t\t{\n\t\t\t\t\t\tbyte newValue = (byte)(value - (newTotal - _parent.Settings.MaxTotalEVs));\n\t\t\t\t\t\tif (_value != newValue)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tUpdate(newValue);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tUpdate(value);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tinternal PBELegalEffortValue(PBELegalEffortValues parent, PBEStat stat, byte value)\n\t\t{\n\t\t\t_parent = parent;\n\t\t\tStat = stat;\n\t\t\t_value = value;\n\t\t}\n\n\t\tprivate void Update(byte newValue)\n\t\t{\n\t\t\t_value = newValue;\n\t\t\tOnPropertyChanged(nameof(Value));\n\t\t\t_parent.OnPropertyChanged(nameof(_parent.StatTotal));\n\t\t}\n\t}\n\n\tprivate void OnPropertyChanged(string property)\n\t{\n\t\tPropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));\n\t}\n\tpublic event PropertyChangedEventHandler? PropertyChanged;\n\n\tpublic PBESettings Settings { get; }\n\tprivate readonly PBELegalEffortValue[] _evs;\n\tpublic PBELegalEffortValue this[PBEStat stat]\n\t{\n\t\tget\n\t\t{\n\t\t\tint statIndex = (int)stat;\n\t\t\tif (statIndex >= 6)\n\t\t\t{\n\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(stat));\n\t\t\t}\n\t\t\treturn _evs[statIndex];\n\t\t}\n\t}\n\n\tpublic ushort StatTotal\n\t{\n\t\tget\n\t\t{\n\t\t\tushort total = 0;\n\t\t\tfor (int i = 0; i < 6; i++)\n\t\t\t{\n\t\t\t\ttotal += _evs[i].Value;\n\t\t\t}\n\t\t\treturn total;\n\t\t}\n\t}\n\n\tpublic byte HP\n\t{\n\t\tget => _evs[0].Value;\n\t\tset => _evs[0].Value = value;\n\t}\n\tpublic byte Attack\n\t{\n\t\tget => _evs[1].Value;\n\t\tset => _evs[1].Value = value;\n\t}\n\tpublic byte Defense\n\t{\n\t\tget => _evs[2].Value;\n\t\tset => _evs[2].Value = value;\n\t}\n\tpublic byte SpAttack\n\t{\n\t\tget => _evs[3].Value;\n\t\tset => _evs[3].Value = value;\n\t}\n\tpublic byte SpDefense\n\t{\n\t\tget => _evs[4].Value;\n\t\tset => _evs[4].Value = value;\n\t}\n\tpublic byte Speed\n\t{\n\t\tget => _evs[5].Value;\n\t\tset => _evs[5].Value = value;\n\t}\n\n\tinternal PBELegalEffortValues(PBESettings settings, EndianBinaryReader r)\n\t{\n\t\tbyte hp = r.ReadByte();\n\t\tbyte attack = r.ReadByte();\n\t\tbyte defense = r.ReadByte();\n\t\tbyte spAttack = r.ReadByte();\n\t\tbyte spDefense = r.ReadByte();\n\t\tbyte speed = r.ReadByte();\n\t\tif (hp + attack + defense + spAttack + spDefense + speed > settings.MaxTotalEVs)\n\t\t{\n\t\t\tthrow new InvalidDataException();\n\t\t}\n\t\tSettings = settings;\n\t\t_evs = CreateEVs(hp, attack, defense, spAttack, spDefense, speed);\n\t}\n\tinternal PBELegalEffortValues(PBESettings settings, JsonObject jObj)\n\t{\n\t\tbyte hp = jObj.GetSafe(nameof(PBEStat.HP)).GetValue<byte>();\n\t\tbyte attack = jObj.GetSafe(nameof(PBEStat.Attack)).GetValue<byte>();\n\t\tbyte defense = jObj.GetSafe(nameof(PBEStat.Defense)).GetValue<byte>();\n\t\tbyte spAttack = jObj.GetSafe(nameof(PBEStat.SpAttack)).GetValue<byte>();\n\t\tbyte spDefense = jObj.GetSafe(nameof(PBEStat.SpDefense)).GetValue<byte>();\n\t\tbyte speed = jObj.GetSafe(nameof(PBEStat.Speed)).GetValue<byte>();\n\t\tif (hp + attack + defense + spAttack + spDefense + speed > settings.MaxTotalEVs)\n\t\t{\n\t\t\tthrow new InvalidDataException($\"Effort values total must not exceed \\\"{nameof(settings.MaxTotalEVs)}\\\" ({settings.MaxTotalEVs})\");\n\t\t}\n\t\tSettings = settings;\n\t\t_evs = CreateEVs(hp, attack, defense, spAttack, spDefense, speed);\n\t}\n\tinternal PBELegalEffortValues(PBELegalEffortValues other)\n\t{\n\t\tSettings = other.Settings;\n\t\t_evs = CreateEVs(other.HP, other.Attack, other.Defense, other.SpAttack, other.SpDefense, other.Speed);\n\t}\n\tpublic PBELegalEffortValues(PBESettings settings, bool randomize)\n\t{\n\t\tsettings.ShouldBeReadOnly(nameof(settings));\n\t\tSettings = settings;\n\t\t_evs = CreateEVs(0, 0, 0, 0, 0, 0);\n\t\tif (randomize)\n\t\t{\n\t\t\tRandomize();\n\t\t}\n\t}\n\n\tprivate PBELegalEffortValue[] CreateEVs(byte hp, byte attack, byte defense, byte spAttack, byte spDefense, byte speed)\n\t{\n\t\treturn new PBELegalEffortValue[6]\n\t\t{\n\t\t\tnew PBELegalEffortValue(this, PBEStat.HP, hp),\n\t\t\tnew PBELegalEffortValue(this, PBEStat.Attack, attack),\n\t\t\tnew PBELegalEffortValue(this, PBEStat.Defense, defense),\n\t\t\tnew PBELegalEffortValue(this, PBEStat.SpAttack, spAttack),\n\t\t\tnew PBELegalEffortValue(this, PBEStat.SpDefense, spDefense),\n\t\t\tnew PBELegalEffortValue(this, PBEStat.Speed, speed),\n\t\t};\n\t}\n\n\tpublic void Clear()\n\t{\n\t\tfor (int i = 0; i < 6; i++)\n\t\t{\n\t\t\t_evs[i].Value = 0;\n\t\t}\n\t}\n\tpublic void Equalize()\n\t{\n\t\tClear();\n\t\tfor (int i = 0; i < 6; i++)\n\t\t{\n\t\t\t_evs[i].Value = (byte)(Settings.MaxTotalEVs / 6);\n\t\t}\n\t}\n\tpublic void Randomize()\n\t{\n\t\tif (Settings.MaxTotalEVs == 0)\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\tbyte[] vals = new byte[6];\n\t\tint[] a = Enumerable.Repeat(0, 6 - 1)\n\t\t\t\t\t.Select(x => PBEDataProvider.GlobalRandom.RandomInt(1, Settings.MaxTotalEVs - 1))\n\t\t\t\t\t.Concat(new int[] { Settings.MaxTotalEVs })\n\t\t\t\t\t.OrderBy(x => x)\n\t\t\t\t\t.ToArray();\n\t\tushort total = 0;\n\t\tfor (int i = 0; i < 6; i++)\n\t\t{\n\t\t\tbyte b = (byte)Math.Min(byte.MaxValue, a[i] - total);\n\t\t\tvals[i] = b;\n\t\t\ttotal += b;\n\t\t}\n\t\t// This \"while\" will fix the issue where the speed stat was supposed to be above 255\n\t\tvar notMax = new List<int>(5);\n\t\twhile (total != Settings.MaxTotalEVs)\n\t\t{\n\t\t\tnotMax.Clear();\n\t\t\tfor (int i = 0; i < 6; i++)\n\t\t\t{\n\t\t\t\tif (vals[i] != byte.MaxValue)\n\t\t\t\t{\n\t\t\t\t\tnotMax.Add(i);\n\t\t\t\t}\n\t\t\t}\n\t\t\tint index = PBEDataProvider.GlobalRandom.RandomElement(notMax);\n\t\t\tbyte old = vals[index];\n\t\t\tbyte b = (byte)Math.Min(byte.MaxValue, old + (Settings.MaxTotalEVs - total));\n\t\t\tvals[index] = b;\n\t\t\ttotal += (byte)(b - old);\n\t\t}\n\t\tfor (int i = 0; i < 6; i++)\n\t\t{\n\t\t\t_evs[i].Value = vals[i];\n\t\t}\n\t}\n\n\tpublic IEnumerator<PBELegalEffortValue> GetEnumerator()\n\t{\n\t\tfor (int i = 0; i < 6; i++)\n\t\t{\n\t\t\tyield return _evs[i];\n\t\t}\n\t}\n\tIEnumerator IEnumerable.GetEnumerator()\n\t{\n\t\treturn GetEnumerator();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Data/Legality/LegalIndividualValues.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Data.Utils;\nusing Kermalis.PokemonBattleEngine.Utils;\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.IO;\nusing System.Text.Json.Nodes;\n\nnamespace Kermalis.PokemonBattleEngine.Data.Legality;\n\npublic sealed class PBELegalIndividualValues : IPBEStatCollection, IEnumerable<PBELegalIndividualValues.PBELegalIndividualValue>, INotifyPropertyChanged\n{\n\tpublic sealed class PBELegalIndividualValue : INotifyPropertyChanged\n\t{\n\t\tprivate void OnPropertyChanged(string property)\n\t\t{\n\t\t\tPropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));\n\t\t}\n\t\tpublic event PropertyChangedEventHandler? PropertyChanged;\n\n\t\tprivate readonly PBELegalIndividualValues _parent;\n\n\t\tpublic PBEStat Stat { get; }\n\t\tprivate byte _value;\n\t\tpublic byte Value\n\t\t{\n\t\t\tget => _value;\n\t\t\tset\n\t\t\t{\n\t\t\t\tif (_value != value)\n\t\t\t\t{\n\t\t\t\t\tif (value > _parent.Settings.MaxIVs)\n\t\t\t\t\t{\n\t\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value));\n\t\t\t\t\t}\n\t\t\t\t\t_value = value;\n\t\t\t\t\tOnPropertyChanged(nameof(Value));\n\t\t\t\t\t_parent.UpdateHiddenPower();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tinternal PBELegalIndividualValue(PBELegalIndividualValues parent, PBEStat stat, byte value)\n\t\t{\n\t\t\t_parent = parent;\n\t\t\tStat = stat;\n\t\t\t_value = value;\n\t\t}\n\t}\n\n\tprivate void OnPropertyChanged(string property)\n\t{\n\t\tPropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));\n\t}\n\tpublic event PropertyChangedEventHandler? PropertyChanged;\n\n\tpublic PBESettings Settings { get; }\n\tprivate readonly PBELegalIndividualValue[] _ivs;\n\tpublic PBELegalIndividualValue this[PBEStat stat]\n\t{\n\t\tget\n\t\t{\n\t\t\tint statIndex = (int)stat;\n\t\t\tif (statIndex >= 6)\n\t\t\t{\n\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(stat));\n\t\t\t}\n\t\t\treturn _ivs[statIndex];\n\t\t}\n\t}\n\n\tprivate PBEType _hiddenPowerType;\n\tpublic PBEType HiddenPowerType\n\t{\n\t\tget => _hiddenPowerType;\n\t\tprivate set\n\t\t{\n\t\t\tif (_hiddenPowerType != value)\n\t\t\t{\n\t\t\t\t_hiddenPowerType = value;\n\t\t\t\tOnPropertyChanged(nameof(HiddenPowerType));\n\t\t\t}\n\t\t}\n\t}\n\tprivate byte _hiddenPowerBasePower;\n\tpublic byte HiddenPowerBasePower\n\t{\n\t\tget => _hiddenPowerBasePower;\n\t\tprivate set\n\t\t{\n\t\t\tif (_hiddenPowerBasePower != value)\n\t\t\t{\n\t\t\t\t_hiddenPowerBasePower = value;\n\t\t\t\tOnPropertyChanged(nameof(HiddenPowerBasePower));\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic byte HP\n\t{\n\t\tget => _ivs[0].Value;\n\t\tset => _ivs[0].Value = value;\n\t}\n\tpublic byte Attack\n\t{\n\t\tget => _ivs[1].Value;\n\t\tset => _ivs[1].Value = value;\n\t}\n\tpublic byte Defense\n\t{\n\t\tget => _ivs[2].Value;\n\t\tset => _ivs[2].Value = value;\n\t}\n\tpublic byte SpAttack\n\t{\n\t\tget => _ivs[3].Value;\n\t\tset => _ivs[3].Value = value;\n\t}\n\tpublic byte SpDefense\n\t{\n\t\tget => _ivs[4].Value;\n\t\tset => _ivs[4].Value = value;\n\t}\n\tpublic byte Speed\n\t{\n\t\tget => _ivs[5].Value;\n\t\tset => _ivs[5].Value = value;\n\t}\n\n\tinternal PBELegalIndividualValues(PBESettings settings, EndianBinaryReader r)\n\t{\n\t\tvoid Validate(byte val)\n\t\t{\n\t\t\tif (val > settings.MaxIVs)\n\t\t\t{\n\t\t\t\tthrow new InvalidDataException();\n\t\t\t}\n\t\t}\n\t\tbyte hp = r.ReadByte();\n\t\tValidate(hp);\n\t\tbyte attack = r.ReadByte();\n\t\tValidate(attack);\n\t\tbyte defense = r.ReadByte();\n\t\tValidate(defense);\n\t\tbyte spAttack = r.ReadByte();\n\t\tValidate(spAttack);\n\t\tbyte spDefense = r.ReadByte();\n\t\tValidate(spDefense);\n\t\tbyte speed = r.ReadByte();\n\t\tValidate(speed);\n\t\tSettings = settings;\n\t\t_ivs = CreateIVs(hp, attack, defense, spAttack, spDefense, speed);\n\t\tUpdateHiddenPower();\n\t}\n\tinternal PBELegalIndividualValues(PBESettings settings, JsonObject jObj)\n\t{\n\t\tvoid Validate(byte val, string name)\n\t\t{\n\t\t\tif (val > settings.MaxIVs)\n\t\t\t{\n\t\t\t\tthrow new InvalidDataException($\"\\\"{name}\\\" individual value must not exceed \\\"{nameof(settings.MaxIVs)}\\\" ({settings.MaxIVs})\");\n\t\t\t}\n\t\t}\n\t\tbyte hp = jObj.GetSafe(nameof(PBEStat.HP)).GetValue<byte>();\n\t\tValidate(hp, nameof(PBEStat.HP));\n\t\tbyte attack = jObj.GetSafe(nameof(PBEStat.Attack)).GetValue<byte>();\n\t\tValidate(attack, nameof(PBEStat.Attack));\n\t\tbyte defense = jObj.GetSafe(nameof(PBEStat.Defense)).GetValue<byte>();\n\t\tValidate(defense, nameof(PBEStat.Defense));\n\t\tbyte spAttack = jObj.GetSafe(nameof(PBEStat.SpAttack)).GetValue<byte>();\n\t\tValidate(spAttack, nameof(PBEStat.SpAttack));\n\t\tbyte spDefense = jObj.GetSafe(nameof(PBEStat.SpDefense)).GetValue<byte>();\n\t\tValidate(spDefense, nameof(PBEStat.SpDefense));\n\t\tbyte speed = jObj.GetSafe(nameof(PBEStat.Speed)).GetValue<byte>();\n\t\tValidate(speed, nameof(PBEStat.Speed));\n\t\tSettings = settings;\n\t\t_ivs = CreateIVs(hp, attack, defense, spAttack, spDefense, speed);\n\t\tUpdateHiddenPower();\n\t}\n\tinternal PBELegalIndividualValues(PBELegalIndividualValues other)\n\t{\n\t\tSettings = other.Settings;\n\t\t_ivs = CreateIVs(other.HP, other.Attack, other.Defense, other.SpAttack, other.SpDefense, other.Speed);\n\t\tUpdateHiddenPower();\n\t}\n\tpublic PBELegalIndividualValues(PBESettings settings, bool randomize)\n\t{\n\t\tsettings.ShouldBeReadOnly(nameof(settings));\n\t\tSettings = settings;\n\t\t_ivs = CreateIVs(0, 0, 0, 0, 0, 0);\n\t\tUpdateHiddenPower();\n\t\tif (randomize)\n\t\t{\n\t\t\tRandomize();\n\t\t}\n\t}\n\n\tprivate PBELegalIndividualValue[] CreateIVs(byte hp, byte attack, byte defense, byte spAttack, byte spDefense, byte speed)\n\t{\n\t\treturn new PBELegalIndividualValue[6]\n\t\t{\n\t\t\t\tnew PBELegalIndividualValue(this, PBEStat.HP, hp),\n\t\t\t\tnew PBELegalIndividualValue(this, PBEStat.Attack, attack),\n\t\t\t\tnew PBELegalIndividualValue(this, PBEStat.Defense, defense),\n\t\t\t\tnew PBELegalIndividualValue(this, PBEStat.SpAttack, spAttack),\n\t\t\t\tnew PBELegalIndividualValue(this, PBEStat.SpDefense, spDefense),\n\t\t\t\tnew PBELegalIndividualValue(this, PBEStat.Speed, speed)\n\t\t};\n\t}\n\tprivate void UpdateHiddenPower()\n\t{\n\t\tHiddenPowerType = PBEDataUtils.GetHiddenPowerType(HP, Attack, Defense, SpAttack, SpDefense, Speed);\n\t\tHiddenPowerBasePower = PBEDataUtils.GetHiddenPowerBasePower(HP, Attack, Defense, SpAttack, SpDefense, Speed, Settings);\n\t}\n\n\tpublic void Clear()\n\t{\n\t\tfor (int i = 0; i < 6; i++)\n\t\t{\n\t\t\t_ivs[i].Value = 0;\n\t\t}\n\t}\n\tpublic void Maximize()\n\t{\n\t\tfor (int i = 0; i < 6; i++)\n\t\t{\n\t\t\t_ivs[i].Value = Settings.MaxIVs;\n\t\t}\n\t}\n\tpublic void Randomize()\n\t{\n\t\tfor (int i = 0; i < 6; i++)\n\t\t{\n\t\t\t_ivs[i].Value = (byte)PBEDataProvider.GlobalRandom.RandomInt(0, Settings.MaxIVs);\n\t\t}\n\t}\n\n\tpublic IEnumerator<PBELegalIndividualValue> GetEnumerator()\n\t{\n\t\tfor (int i = 0; i < 6; i++)\n\t\t{\n\t\t\tyield return _ivs[i];\n\t\t}\n\t}\n\tIEnumerator IEnumerable.GetEnumerator()\n\t{\n\t\treturn GetEnumerator();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Data/Legality/LegalMoveset.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data.Utils;\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.IO;\nusing System.Linq;\n\nnamespace Kermalis.PokemonBattleEngine.Data.Legality;\n\npublic sealed class PBELegalMoveset : IPBEMoveset, IPBEMoveset<PBELegalMoveset.PBELegalMovesetSlot>, INotifyPropertyChanged\n{\n\tpublic sealed class PBELegalMovesetSlot : IPBEMovesetSlot, INotifyPropertyChanged\n\t{\n\t\tprivate void OnPropertyChanged(string property)\n\t\t{\n\t\t\tPropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));\n\t\t}\n\t\tpublic event PropertyChangedEventHandler? PropertyChanged;\n\n\t\tprivate readonly PBELegalMoveset _parent;\n\t\tprivate readonly int _index;\n\n\t\tpublic PBEAlphabeticalList<PBEMove> Allowed { get; }\n\t\tprivate PBEMove _move;\n\t\tpublic PBEMove Move\n\t\t{\n\t\t\tget => _move;\n\t\t\tset\n\t\t\t{\n\t\t\t\tPBEMove old = _move;\n\t\t\t\tif (old != value)\n\t\t\t\t{\n\t\t\t\t\tif (value >= PBEMove.MAX || (value != PBEMove.None && !PBEDataUtils.IsMoveUsable(value)))\n\t\t\t\t\t{\n\t\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value));\n\t\t\t\t\t}\n\t\t\t\t\tif (!_isMoveEditable)\n\t\t\t\t\t{\n\t\t\t\t\t\tthrow new InvalidOperationException($\"Slot {_index}'s move cannot be changed because there is no move in slot {_index - 1}.\");\n\t\t\t\t\t}\n\t\t\t\t\tif (!Allowed.Contains(value))\n\t\t\t\t\t{\n\t\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"Slot {_index} does not allow {value}.\");\n\t\t\t\t\t}\n\t\t\t\t\tif (value != PBEMove.None)\n\t\t\t\t\t{\n\t\t\t\t\t\t// If \"move\" is in another slot, place \"slotIndex\"'s old move at the other slot\n\t\t\t\t\t\tfor (int i = 0; i < _parent.Settings.NumMoves; i++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (i != _index)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tPBELegalMovesetSlot iSlot = _parent[i];\n\t\t\t\t\t\t\t\tif (iSlot.Move == value)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t// If slot 0 is Snore and slot 3 is None but is trying to become Snore, do nothing because the first Snore is in an earlier slot and swapping None to an earlier slot makes no sense\n\t\t\t\t\t\t\t\t\tif (old == PBEMove.None && i < _index)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tgoto finish;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tUpdateMove(value);\n\t\t\t\t\t\t\t\t\t\tiSlot.UpdateMove(old);\n\t\t\t\t\t\t\t\t\t\tgoto editables;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t// If \"move\" is None and a slot after \"slotIndex\" is not None, then place None at the other slot instead and place the other slot's move at \"slotIndex\"\n\t\t\t\t\t\tfor (int i = _parent.Settings.NumMoves - 1; i > _index; i--)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tPBELegalMovesetSlot iSlot = _parent[i];\n\t\t\t\t\t\t\tif (iSlot.Move != PBEMove.None)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tUpdateMove(iSlot.Move);\n\t\t\t\t\t\t\t\tiSlot.UpdateMove(PBEMove.None);\n\t\t\t\t\t\t\t\tgoto editables;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// This gets reached if:\n\t\t\t\t\t// \"move\" is not None and there is no other slot with \"move\"\n\t\t\t\t\t// \"move\" is None and there is no slot after \"slotIndex\" with a move\n\t\t\t\t\tUpdateMove(value);\n\t\t\t\teditables:\n\t\t\t\t\t_parent.SetEditables();\n\t\t\t\tfinish:\n\t\t\t\t\t;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tprivate bool _isMoveEditable;\n\t\tpublic bool IsMoveEditable\n\t\t{\n\t\t\tget => _isMoveEditable;\n\t\t\tinternal set\n\t\t\t{\n\t\t\t\tif (_isMoveEditable != value)\n\t\t\t\t{\n\t\t\t\t\t_isMoveEditable = value;\n\t\t\t\t\tOnPropertyChanged(nameof(IsMoveEditable));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tprivate byte _ppUps;\n\t\tpublic byte PPUps\n\t\t{\n\t\t\tget => _ppUps;\n\t\t\tset\n\t\t\t{\n\t\t\t\tif (_ppUps != value)\n\t\t\t\t{\n\t\t\t\t\tif (value > _parent.Settings.MaxPPUps)\n\t\t\t\t\t{\n\t\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"\\\"{nameof(value)}\\\" cannot exceed \\\"{nameof(_parent.Settings.MaxPPUps)}\\\" ({_parent.Settings.MaxPPUps}).\");\n\t\t\t\t\t}\n\t\t\t\t\tif (!IsPPUpsEditable)\n\t\t\t\t\t{\n\t\t\t\t\t\tthrow new InvalidOperationException($\"Slot {_index}'s PP-Ups cannot be changed because it has no move.\");\n\t\t\t\t\t}\n\t\t\t\t\tUpdatePPUps(value);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tprivate bool _isPPUpsEditable;\n\t\tpublic bool IsPPUpsEditable\n\t\t{\n\t\t\tget => _isPPUpsEditable;\n\t\t\tprivate set\n\t\t\t{\n\t\t\t\tif (_isPPUpsEditable != value)\n\t\t\t\t{\n\t\t\t\t\t_isPPUpsEditable = value;\n\t\t\t\t\tOnPropertyChanged(nameof(IsPPUpsEditable));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate static readonly PBEMove[] _none = new PBEMove[1] { PBEMove.None };\n\t\tinternal PBELegalMovesetSlot(PBELegalMoveset parent, int index)\n\t\t{\n\t\t\t_parent = parent;\n\t\t\t_index = index;\n\t\t\t_isMoveEditable = index < 2;\n\t\t\tAllowed = new PBEAlphabeticalList<PBEMove>(_none);\n\t\t}\n\n\t\tprivate void UpdateMove(PBEMove move)\n\t\t{\n\t\t\tif (_move != move)\n\t\t\t{\n\t\t\t\t_move = move;\n\t\t\t\tOnPropertyChanged(nameof(Move));\n\t\t\t\tif (_move == PBEMove.None)\n\t\t\t\t{\n\t\t\t\t\tUpdatePPUps(0);\n\t\t\t\t}\n\t\t\t\tIsPPUpsEditable = _move != PBEMove.None;\n\t\t\t}\n\t\t}\n\t\tprivate void UpdatePPUps(byte ppUps)\n\t\t{\n\t\t\tif (_ppUps != ppUps)\n\t\t\t{\n\t\t\t\t_ppUps = ppUps;\n\t\t\t\tOnPropertyChanged(nameof(PPUps));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void OnPropertyChanged(string property)\n\t{\n\t\tPropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));\n\t}\n\tpublic event PropertyChangedEventHandler? PropertyChanged;\n\n\tprivate PBESpecies _species;\n\tpublic PBESpecies Species\n\t{\n\t\tget => _species;\n\t\tset\n\t\t{\n\t\t\tif (_species != value)\n\t\t\t{\n\t\t\t\tPBEDataUtils.ValidateSpecies(value, 0, true);\n\t\t\t\t_species = value;\n\t\t\t\t_form = 0;\n\t\t\t\tOnPropertyChanged(nameof(Species));\n\t\t\t\tSetAlloweds();\n\t\t\t}\n\t\t}\n\t}\n\tprivate PBEForm _form;\n\tpublic PBEForm Form\n\t{\n\t\tget => _form;\n\t\tset\n\t\t{\n\t\t\tif (_form != value)\n\t\t\t{\n\t\t\t\tPBEDataUtils.ValidateSpecies(_species, value, true);\n\t\t\t\t_form = value;\n\t\t\t\tOnPropertyChanged(nameof(Form));\n\t\t\t\tSetAlloweds();\n\t\t\t}\n\t\t}\n\t}\n\tprivate byte _level;\n\tpublic byte Level\n\t{\n\t\tget => _level;\n\t\tset\n\t\t{\n\t\t\tif (_level != value)\n\t\t\t{\n\t\t\t\tPBEDataUtils.ValidateLevel(value, Settings);\n\t\t\t\t_level = value;\n\t\t\t\tOnPropertyChanged(nameof(Level));\n\t\t\t\tSetAlloweds();\n\t\t\t}\n\t\t}\n\t}\n\tpublic PBESettings Settings { get; }\n\tprivate readonly PBELegalMovesetSlot[] _list;\n\tpublic int Count => Settings.NumMoves;\n\tpublic PBELegalMovesetSlot this[int index]\n\t{\n\t\tget\n\t\t{\n\t\t\tif (index >= Settings.NumMoves)\n\t\t\t{\n\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(index));\n\t\t\t}\n\t\t\treturn _list[index];\n\t\t}\n\t}\n\tIPBEMovesetSlot IReadOnlyList<IPBEMovesetSlot>.this[int index] => this[index];\n\tpublic PBELegalMovesetSlot? this[PBEMove move]\n\t{\n\t\tget\n\t\t{\n\t\t\tfor (int i = 0; i < Settings.NumMoves; i++)\n\t\t\t{\n\t\t\t\tPBELegalMovesetSlot slot = _list[i];\n\t\t\t\tif (slot.Move == move)\n\t\t\t\t{\n\t\t\t\t\treturn slot;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tinternal PBELegalMoveset(PBELegalMoveset other)\n\t{\n\t\t_species = other._species;\n\t\t_form = other._form;\n\t\t_level = other._level;\n\t\tSettings = other.Settings;\n\t\tint count = Settings.NumMoves;\n\t\t_list = new PBELegalMovesetSlot[count];\n\t\tfor (int i = 0; i < count; i++)\n\t\t{\n\t\t\t_list[i] = new PBELegalMovesetSlot(this, i);\n\t\t}\n\t\tSetAlloweds();\n\t\tfor (int i = 0; i < count; i++)\n\t\t{\n\t\t\tPBELegalMovesetSlot slot = _list[i];\n\t\t\tPBELegalMovesetSlot oSlot = other[i];\n\t\t\tslot.Move = oSlot.Move;\n\t\t\tslot.PPUps = oSlot.PPUps;\n\t\t}\n\t}\n\tinternal PBELegalMoveset(PBESpecies species, PBEForm form, byte level, PBESettings settings, IPBEMoveset other)\n\t{\n\t\tint count = other.Count;\n\t\tif (count != settings.NumMoves)\n\t\t{\n\t\t\tthrow new InvalidDataException($\"Moveset count must be equal to \\\"{nameof(settings.NumMoves)}\\\" ({settings.NumMoves}).\");\n\t\t}\n\t\t_species = species;\n\t\t_form = form;\n\t\t_level = level;\n\t\tSettings = settings;\n\t\t_list = new PBELegalMovesetSlot[count];\n\t\tfor (int i = 0; i < count; i++)\n\t\t{\n\t\t\t_list[i] = new PBELegalMovesetSlot(this, i);\n\t\t}\n\t\tSetAlloweds();\n\t\tfor (int i = 0; i < count; i++)\n\t\t{\n\t\t\tIPBEMovesetSlot oSlot = other[i];\n\t\t\tPBELegalMovesetSlot slot = _list[i];\n\t\t\tPBEMove move = oSlot.Move;\n\t\t\tslot.Move = move;\n\t\t\tif (slot.Move != move)\n\t\t\t{\n\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(IPBEPokemon.Moveset), \"Invalid moves.\");\n\t\t\t}\n\t\t\tbyte ppUps = oSlot.PPUps;\n\t\t\tslot.PPUps = ppUps;\n\t\t\tif (slot.PPUps != ppUps)\n\t\t\t{\n\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(IPBEPokemon.Moveset), \"Invalid PP-Ups.\");\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>Creates a new <see cref=\"PBELegalMoveset\"/> object with the specified traits.</summary>\n\t/// <param name=\"species\">The species of the Pokémon that this moveset will be built for.</param>\n\t/// <param name=\"form\">The form of the Pokémon that this moveset will be built for.</param>\n\t/// <param name=\"level\">The level of the Pokémon that this moveset will be built for.</param>\n\t/// <param name=\"settings\">The settings that will be used to evaluate the <see cref=\"PBELegalMoveset\"/>.</param>\n\t/// <param name=\"randomize\">True if <see cref=\"Randomize\"/> should be called, False if the move slots use their default values.</param>\n\tpublic PBELegalMoveset(PBESpecies species, PBEForm form, byte level, PBESettings settings, bool randomize)\n\t{\n\t\tsettings.ShouldBeReadOnly(nameof(settings));\n\t\tPBEDataUtils.ValidateSpecies(species, form, true);\n\t\tPBEDataUtils.ValidateLevel(level, settings);\n\t\t_level = level;\n\t\t_species = species;\n\t\t_form = form;\n\t\tSettings = settings;\n\t\tint count = settings.NumMoves;\n\t\t_list = new PBELegalMovesetSlot[count];\n\t\tfor (int i = 0; i < count; i++)\n\t\t{\n\t\t\t_list[i] = new PBELegalMovesetSlot(this, i);\n\t\t}\n\t\tSetAlloweds();\n\t\tif (randomize)\n\t\t{\n\t\t\tRandomize();\n\t\t}\n\t}\n\n\tprivate static readonly PBEAlphabeticalList<PBEMove> secretSwordArray = new(new PBEMove[1] { PBEMove.SecretSword });\n\tprivate void SetAlloweds()\n\t{\n\t\t// Set alloweds\n\t\tint i;\n\t\tIReadOnlyCollection<PBEMove> legalMoves = PBEDataProvider.Instance.GetLegalMoves(_species, _form, _level);\n\t\tvar allowed = new List<PBEMove>(legalMoves.Count + 1);\n\t\tallowed.AddRange(legalMoves);\n\t\tif (_species == PBESpecies.Keldeo && _form == PBEForm.Keldeo_Resolute)\n\t\t{\n\t\t\t_list[0].Allowed.Reset(secretSwordArray);\n\t\t\tallowed.Remove(PBEMove.SecretSword);\n\t\t\ti = 1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\ti = 0;\n\t\t}\n\t\tfor (; i < Settings.NumMoves; i++)\n\t\t{\n\t\t\tif (i == 1)\n\t\t\t{\n\t\t\t\tallowed.Insert(0, PBEMove.None);\n\t\t\t}\n\t\t\t_list[i].Allowed.Reset(allowed);\n\t\t}\n\t\t// Remove unalloweds (slot.Move setter will automatically sort PBEMove.None)\n\t\twhile (true)\n\t\t{\n\t\t\tint bad = Array.FindIndex(_list, s => !s.Allowed.Contains(s.Move));\n\t\t\tif (bad == -1)\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tPBELegalMovesetSlot slot = _list[bad];\n\t\t\t\tslot.Move = slot.Allowed[0];\n\t\t\t}\n\t\t}\n\t\tSetEditables();\n\t}\n\tprivate void SetEditables()\n\t{\n\t\tfor (int i = 2; i < Settings.NumMoves; i++)\n\t\t{\n\t\t\t_list[i].IsMoveEditable = _list[i - 1].Move != PBEMove.None;\n\t\t}\n\t}\n\n\t/// <summary>Sets every move slot excluding the first to <see cref=\"PBEMove.None\"/> with 0 PP-Ups.</summary>\n\tpublic void Clear()\n\t{\n\t\tfor (int i = 1; i < Settings.NumMoves; i++)\n\t\t{\n\t\t\t_list[i].Move = PBEMove.None;\n\t\t}\n\t\tSetEditables();\n\t}\n\tpublic bool Contains(PBEMove move)\n\t{\n\t\treturn this[move] is not null;\n\t}\n\t/// <summary>Randomizes the move and PP-Ups in each slot without creating duplicate moves.</summary>\n\tpublic void Randomize()\n\t{\n\t\tvar blacklist = new List<PBEMove>(Settings.NumMoves) { PBEMove.None };\n\t\tfor (int i = 0; i < Settings.NumMoves; i++)\n\t\t{\n\t\t\tPBELegalMovesetSlot slot = _list[i];\n\t\t\tPBEMove[] allowed = slot.Allowed.Except(blacklist).ToArray();\n\t\t\tif (allowed.Length == 0)\n\t\t\t{\n\t\t\t\tfor (int j = i; j < Settings.NumMoves; j++)\n\t\t\t\t{\n\t\t\t\t\t_list[j].Move = PBEMove.None;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tPBEMove move = PBEDataProvider.GlobalRandom.RandomElement(allowed);\n\t\t\t\tif (i < Settings.NumMoves - 1)\n\t\t\t\t{\n\t\t\t\t\tblacklist.Add(move);\n\t\t\t\t}\n\t\t\t\tslot.Move = move;\n\t\t\t\tslot.PPUps = (byte)PBEDataProvider.GlobalRandom.RandomInt(0, Settings.MaxPPUps);\n\t\t\t}\n\t\t}\n\t\tSetEditables();\n\t}\n\n\tpublic IEnumerator<PBELegalMovesetSlot> GetEnumerator()\n\t{\n\t\tfor (int i = 0; i < Settings.NumMoves; i++)\n\t\t{\n\t\t\tyield return _list[i];\n\t\t}\n\t}\n\tIEnumerator<IPBEMovesetSlot> IEnumerable<IPBEMovesetSlot>.GetEnumerator()\n\t{\n\t\treturn GetEnumerator();\n\t}\n\tIEnumerator IEnumerable.GetEnumerator()\n\t{\n\t\treturn GetEnumerator();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Data/Legality/LegalPokemon.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Data.Utils;\nusing Kermalis.PokemonBattleEngine.Utils;\nusing System;\nusing System.ComponentModel;\nusing System.Diagnostics.CodeAnalysis;\nusing System.IO;\nusing System.Text.Json.Nodes;\n\nnamespace Kermalis.PokemonBattleEngine.Data.Legality;\n\npublic sealed class PBELegalPokemon : IPBEPokemon, INotifyPropertyChanged\n{\n\tprivate void OnPropertyChanged(string property)\n\t{\n\t\tPropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));\n\t}\n\tpublic event PropertyChangedEventHandler? PropertyChanged;\n\n\tpublic PBESettings Settings { get; }\n\n\tprivate IPBEPokemonData _pData;\n\tpublic PBEAlphabeticalList<PBEAbility> SelectableAbilities { get; } = new();\n\tpublic PBEAlphabeticalList<PBEForm> SelectableForms { get; } = new();\n\tpublic PBEAlphabeticalList<PBEGender> SelectableGenders { get; } = new();\n\tpublic PBEAlphabeticalList<PBEItem> SelectableItems { get; } = new();\n\n\tpublic bool PBEIgnore => false;\n\n\tprivate PBESpecies _species;\n\tpublic PBESpecies Species\n\t{\n\t\tget => _species;\n\t\tset\n\t\t{\n\t\t\tif (_species != value)\n\t\t\t{\n\t\t\t\tPBEDataUtils.ValidateSpecies(value, 0, true);\n\t\t\t\tPBESpecies oldSpecies = _species;\n\t\t\t\t_species = value;\n\t\t\t\t_form = 0;\n\t\t\t\tOnPropertyChanged(nameof(Species));\n\t\t\t\tOnSpeciesChanged(oldSpecies);\n\t\t\t\tOnPropertyChanged(nameof(Form));\n\t\t\t}\n\t\t}\n\t}\n\tprivate PBEForm _form;\n\tpublic PBEForm Form\n\t{\n\t\tget => _form;\n\t\tset\n\t\t{\n\t\t\tif (_form != value)\n\t\t\t{\n\t\t\t\tPBEDataUtils.ValidateSpecies(_species, value, true);\n\t\t\t\t_form = value;\n\t\t\t\tOnPropertyChanged(nameof(Form));\n\t\t\t\tOnFormChanged();\n\t\t\t}\n\t\t}\n\t}\n\tprivate string _nickname;\n\tpublic string Nickname\n\t{\n\t\tget => _nickname;\n\t\tset\n\t\t{\n\t\t\tif (_nickname != value)\n\t\t\t{\n\t\t\t\tPBEDataUtils.ValidateNickname(value, Settings);\n\t\t\t\t_nickname = value;\n\t\t\t\tOnPropertyChanged(nameof(Nickname));\n\t\t\t}\n\t\t}\n\t}\n\tprivate byte _level;\n\tpublic byte Level\n\t{\n\t\tget => _level;\n\t\tset\n\t\t{\n\t\t\tif (_level != value)\n\t\t\t{\n\t\t\t\tPBEDataUtils.ValidateLevel(value, Settings);\n\t\t\t\t_level = value;\n\t\t\t\tOnPropertyChanged(nameof(Level));\n\t\t\t\tMoveset.Level = value;\n\t\t\t\tEXP = PBEDataProvider.Instance.GetEXPRequired(_pData.GrowthRate, value);\n\t\t\t}\n\t\t}\n\t}\n\tprivate uint _exp;\n\tpublic uint EXP\n\t{\n\t\tget => _exp;\n\t\tset\n\t\t{\n\t\t\tif (_exp != value)\n\t\t\t{\n\t\t\t\t_level = PBEDataProvider.Instance.GetEXPLevel(_pData.GrowthRate, value);\n\t\t\t\t_exp = value;\n\t\t\t\tOnPropertyChanged(nameof(EXP));\n\t\t\t\tOnPropertyChanged(nameof(Level));\n\t\t\t\tMoveset.Level = _level;\n\t\t\t}\n\t\t}\n\t}\n\tprivate byte _friendship;\n\tpublic byte Friendship\n\t{\n\t\tget => _friendship;\n\t\tset\n\t\t{\n\t\t\tif (value != _friendship)\n\t\t\t{\n\t\t\t\t_friendship = value;\n\t\t\t\tOnPropertyChanged(nameof(Friendship));\n\t\t\t}\n\t\t}\n\t}\n\tprivate bool _shiny;\n\tpublic bool Shiny\n\t{\n\t\tget => _shiny;\n\t\tset\n\t\t{\n\t\t\tif (value != _shiny)\n\t\t\t{\n\t\t\t\t_shiny = value;\n\t\t\t\tOnPropertyChanged(nameof(Shiny));\n\t\t\t}\n\t\t}\n\t}\n\tprivate bool _pokerus;\n\tpublic bool Pokerus\n\t{\n\t\tget => _pokerus;\n\t\tset\n\t\t{\n\t\t\tif (value != _pokerus)\n\t\t\t{\n\t\t\t\t_pokerus = value;\n\t\t\t\tOnPropertyChanged(nameof(Pokerus));\n\t\t\t}\n\t\t}\n\t}\n\tprivate PBEAbility _ability;\n\tpublic PBEAbility Ability\n\t{\n\t\tget => _ability;\n\t\tset\n\t\t{\n\t\t\tif (value != _ability)\n\t\t\t{\n\t\t\t\tPBEDataUtils.ValidateAbility(SelectableAbilities, value);\n\t\t\t\t_ability = value;\n\t\t\t\tOnPropertyChanged(nameof(Ability));\n\t\t\t}\n\t\t}\n\t}\n\tprivate PBENature _nature;\n\tpublic PBENature Nature\n\t{\n\t\tget => _nature;\n\t\tset\n\t\t{\n\t\t\tif (value != _nature)\n\t\t\t{\n\t\t\t\tPBEDataUtils.ValidateNature(value);\n\t\t\t\t_nature = value;\n\t\t\t\tOnPropertyChanged(nameof(Nature));\n\t\t\t}\n\t\t}\n\t}\n\tprivate PBEItem _caughtBall;\n\tpublic PBEItem CaughtBall\n\t{\n\t\tget => _caughtBall;\n\t\tset\n\t\t{\n\t\t\tif (value != _caughtBall)\n\t\t\t{\n\t\t\t\tPBEDataUtils.ValidateCaughtBall(value);\n\t\t\t\t_caughtBall = value;\n\t\t\t\tOnPropertyChanged(nameof(CaughtBall));\n\t\t\t}\n\t\t}\n\t}\n\tprivate PBEGender _gender;\n\tpublic PBEGender Gender\n\t{\n\t\tget => _gender;\n\t\tset\n\t\t{\n\t\t\tif (value != _gender)\n\t\t\t{\n\t\t\t\tPBEDataUtils.ValidateGender(SelectableGenders, value);\n\t\t\t\t_gender = value;\n\t\t\t\tOnPropertyChanged(nameof(Gender));\n\t\t\t}\n\t\t}\n\t}\n\tprivate PBEItem _item;\n\tpublic PBEItem Item\n\t{\n\t\tget => _item;\n\t\tset\n\t\t{\n\t\t\tif (value != _item)\n\t\t\t{\n\t\t\t\tPBEDataUtils.ValidateItem(SelectableItems, value);\n\t\t\t\t_item = value;\n\t\t\t\tOnPropertyChanged(nameof(Item));\n\t\t\t}\n\t\t}\n\t}\n\tpublic PBELegalEffortValues EffortValues { get; }\n\tIPBEStatCollection IPBEPokemon.EffortValues => EffortValues;\n\tpublic PBELegalIndividualValues IndividualValues { get; }\n\tIPBEReadOnlyStatCollection IPBEPokemon.IndividualValues => IndividualValues;\n\tpublic PBELegalMoveset Moveset { get; }\n\tIPBEMoveset IPBEPokemon.Moveset => Moveset;\n\n\tinternal PBELegalPokemon(PBESettings settings, EndianBinaryReader r)\n\t{\n\t\tSettings = settings;\n\t\tPBESpecies species = r.ReadEnum<PBESpecies>();\n\t\tPBEForm form = r.ReadEnum<PBEForm>();\n\t\tPBEDataUtils.ValidateSpecies(species, form, true);\n\t\t_species = species;\n\t\t_form = form;\n\t\tSetSelectable();\n\t\tstring nickname = r.ReadString_NullTerminated();\n\t\tPBEDataUtils.ValidateNickname(nickname, Settings);\n\t\t_nickname = nickname;\n\t\tbyte level = r.ReadByte();\n\t\tPBEDataUtils.ValidateLevel(level, Settings);\n\t\t_level = level;\n\t\tuint exp = r.ReadUInt32();\n\t\tPBEDataUtils.ValidateEXP(_pData!.GrowthRate, exp, level);\n\t\t_exp = exp;\n\t\t_friendship = r.ReadByte();\n\t\t_shiny = r.ReadBoolean();\n\t\t_pokerus = r.ReadBoolean();\n\t\tPBEAbility ability = r.ReadEnum<PBEAbility>();\n\t\tPBEDataUtils.ValidateAbility(SelectableAbilities, ability);\n\t\t_ability = ability;\n\t\tPBENature nature = r.ReadEnum<PBENature>();\n\t\tPBEDataUtils.ValidateNature(nature);\n\t\t_nature = nature;\n\t\tPBEItem caughtBall = r.ReadEnum<PBEItem>();\n\t\tPBEDataUtils.ValidateCaughtBall(caughtBall);\n\t\t_caughtBall = caughtBall;\n\t\tPBEGender gender = r.ReadEnum<PBEGender>();\n\t\tPBEDataUtils.ValidateGender(SelectableGenders, gender);\n\t\t_gender = gender;\n\t\tPBEItem item = r.ReadEnum<PBEItem>();\n\t\tPBEDataUtils.ValidateItem(SelectableItems, item);\n\t\t_item = item;\n\t\tEffortValues = new PBELegalEffortValues(Settings, r);\n\t\tIndividualValues = new PBELegalIndividualValues(Settings, r);\n\t\tMoveset = new PBELegalMoveset(species, form, level, Settings, new PBEReadOnlyMoveset(r));\n\t}\n\tinternal PBELegalPokemon(PBESettings settings, JsonObject jObj)\n\t{\n\t\tSettings = settings;\n\t\t_friendship = jObj.GetSafe(nameof(Friendship)).GetValue<byte>();\n\t\t_shiny = jObj.GetSafe(nameof(Shiny)).GetValue<bool>();\n\t\t_pokerus = jObj.GetSafe(nameof(Pokerus)).GetValue<bool>();\n\t\tbyte level = jObj.GetSafe(nameof(Level)).GetValue<byte>();\n\t\tPBEDataUtils.ValidateLevel(level, Settings);\n\t\t_level = level;\n\t\tstring nickname = jObj.GetSafe(nameof(Nickname)).GetValue<string>();\n\t\tPBEDataUtils.ValidateNickname(nickname, Settings);\n\t\t_nickname = nickname;\n\t\tif (!PBEDataProvider.Instance.GetNatureByName(jObj.GetSafe(nameof(Nature)).GetValue<string>(), out PBENature? nature))\n\t\t{\n\t\t\tthrow new InvalidDataException(\"Invalid nature\");\n\t\t}\n\t\tPBEDataUtils.ValidateNature(nature.Value);\n\t\t_nature = nature.Value;\n\t\tif (!PBEDataProvider.Instance.GetItemByName(jObj.GetSafe(nameof(CaughtBall)).GetValue<string>(), out PBEItem? caughtBall))\n\t\t{\n\t\t\tthrow new InvalidDataException(\"Invalid caught ball\");\n\t\t}\n\t\tPBEDataUtils.ValidateCaughtBall(caughtBall.Value);\n\t\t_caughtBall = caughtBall.Value;\n\t\tif (!PBEDataProvider.Instance.GetSpeciesByName(jObj.GetSafe(nameof(Species)).GetValue<string>(), out PBESpecies? species))\n\t\t{\n\t\t\tthrow new InvalidDataException(\"Invalid species\");\n\t\t}\n\t\tPBEForm form;\n\t\tif (PBEDataUtils.HasForms(species.Value, true))\n\t\t{\n\t\t\tform = Enum.Parse<PBEForm>(jObj.GetSafe(nameof(Form)).GetValue<string>());\n\t\t}\n\t\telse\n\t\t{\n\t\t\tform = 0;\n\t\t}\n\t\tPBEDataUtils.ValidateSpecies(species.Value, form, true);\n\t\t_species = species.Value;\n\t\t_form = form;\n\t\tSetSelectable();\n\t\tuint exp = jObj.GetSafe(nameof(EXP)).GetValue<uint>();\n\t\tPBEDataUtils.ValidateEXP(_pData.GrowthRate, exp, level);\n\t\t_exp = exp;\n\t\tif (!PBEDataProvider.Instance.GetAbilityByName(jObj.GetSafe(nameof(Ability)).GetValue<string>(), out PBEAbility? ability))\n\t\t{\n\t\t\tthrow new InvalidDataException(\"Invalid ability\");\n\t\t}\n\t\tPBEDataUtils.ValidateAbility(SelectableAbilities, ability.Value);\n\t\t_ability = ability.Value;\n\t\tif (!PBEDataProvider.Instance.GetGenderByName(jObj.GetSafe(nameof(Gender)).GetValue<string>(), out PBEGender? gender))\n\t\t{\n\t\t\tthrow new InvalidDataException(\"Invalid gender\");\n\t\t}\n\t\tPBEDataUtils.ValidateGender(SelectableGenders, gender.Value);\n\t\t_gender = gender.Value;\n\t\tif (!PBEDataProvider.Instance.GetItemByName(jObj.GetSafe(nameof(Item)).GetValue<string>(), out PBEItem? item))\n\t\t{\n\t\t\tthrow new InvalidDataException(\"Invalid item\");\n\t\t}\n\t\tPBEDataUtils.ValidateItem(SelectableItems, item.Value);\n\t\t_item = item.Value;\n\t\tEffortValues = new PBELegalEffortValues(Settings, jObj.GetSafe(nameof(EffortValues)).AsObject());\n\t\tIndividualValues = new PBELegalIndividualValues(Settings, jObj.GetSafe(nameof(IndividualValues)).AsObject());\n\t\tMoveset = new PBELegalMoveset(_species, form, level, Settings, new PBEReadOnlyMoveset(jObj.GetSafe(nameof(Moveset)).AsArray()));\n\t}\n\tpublic PBELegalPokemon(PBESpecies species, PBEForm form, byte level, uint exp, PBESettings settings)\n\t{\n\t\tsettings.ShouldBeReadOnly(nameof(settings));\n\t\tPBEDataUtils.ValidateSpecies(species, form, true);\n\t\tPBEDataUtils.ValidateLevel(level, settings);\n\t\tPBEDataUtils.ValidateEXP(PBEDataProvider.Instance.GetPokemonData(species, form).GrowthRate, exp, level);\n\t\tSettings = settings;\n\t\t_species = species;\n\t\t_form = form;\n\t\t_level = level;\n\t\t_exp = exp;\n\t\t_friendship = (byte)PBEDataProvider.GlobalRandom.RandomInt(0, byte.MaxValue);\n\t\t_shiny = PBEDataProvider.GlobalRandom.RandomShiny();\n\t\t_nature = PBEDataProvider.GlobalRandom.RandomElement(PBEDataUtils.AllNatures);\n\t\t_caughtBall = PBEDataProvider.GlobalRandom.RandomElement(PBEDataUtils.AllBalls);\n\t\tEffortValues = new PBELegalEffortValues(Settings, true);\n\t\tIndividualValues = new PBELegalIndividualValues(Settings, true);\n\t\tMoveset = new PBELegalMoveset(_species, _form, _level, Settings, true);\n\t\tSetSelectable();\n\t\t_nickname = GetTruncatedNickname();\n\t\tUpdateAbility();\n\t\tUpdateGender();\n\t\tUpdateItem();\n\t}\n\t[MemberNotNull(nameof(_pData))]\n\tprivate void SetSelectable()\n\t{\n\t\t_pData = PBEDataProvider.Instance.GetPokemonData(_species, _form);\n\t\tSelectableAbilities.Reset(_pData.Abilities);\n\t\tSelectableForms.Reset(PBEDataUtils.GetForms(_species, true));\n\t\tSelectableGenders.Reset(PBEDataUtils.GetValidGenders(_pData.GenderRatio));\n\t\tSelectableItems.Reset(PBEDataUtils.GetValidItems(_species, _form));\n\t}\n\tprivate void OnFormChanged()\n\t{\n\t\tSetSelectable();\n\t\tif (!SelectableAbilities.Contains(_ability))\n\t\t{\n\t\t\tAbility = PBEDataProvider.GlobalRandom.RandomElement(SelectableAbilities);\n\t\t}\n\t\tif (!SelectableItems.Contains(_item))\n\t\t{\n\t\t\tItem = PBEDataProvider.GlobalRandom.RandomElement(SelectableItems);\n\t\t}\n\t\tMoveset.Form = _form;\n\t}\n\tprivate string GetTruncatedNickname()\n\t{\n\t\tstring newNickname = PBEDataProvider.Instance.GetSpeciesName(_species).FromGlobalLanguage();\n\t\tif (newNickname.Length > Settings.MaxPokemonNameLength)\n\t\t{\n\t\t\tnewNickname = newNickname.Substring(0, Settings.MaxPokemonNameLength);\n\t\t}\n\t\treturn newNickname;\n\t}\n\tprivate void UpdateAbility()\n\t{\n\t\tAbility = PBEDataProvider.GlobalRandom.RandomElement(SelectableAbilities);\n\t}\n\tprivate void UpdateGender()\n\t{\n\t\tGender = PBEDataProvider.GlobalRandom.RandomGender(_pData.GenderRatio);\n\t}\n\tprivate void UpdateItem()\n\t{\n\t\tItem = PBEDataProvider.GlobalRandom.RandomElement(SelectableItems);\n\t}\n\tprivate void OnSpeciesChanged(PBESpecies oldSpecies)\n\t{\n\t\tSetSelectable();\n\t\tif (_nickname == PBEDataProvider.Instance.GetSpeciesName(oldSpecies).FromGlobalLanguage())\n\t\t{\n\t\t\tNickname = GetTruncatedNickname();\n\t\t}\n\t\tif (!SelectableAbilities.Contains(_ability))\n\t\t{\n\t\t\tUpdateAbility();\n\t\t}\n\t\tif (!SelectableGenders.Contains(_gender))\n\t\t{\n\t\t\tUpdateGender();\n\t\t}\n\t\tif (!SelectableItems.Contains(_item))\n\t\t{\n\t\t\tUpdateItem();\n\t\t}\n\t\tMoveset.Species = _species;\n\t\tMoveset.Form = _form;\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Data/Legality/LegalPokemonCollection.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Data.Utils;\nusing Kermalis.PokemonBattleEngine.Utils;\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Collections.Specialized;\nusing System.ComponentModel;\nusing System.IO;\nusing System.Text.Json;\nusing System.Text.Json.Nodes;\n\nnamespace Kermalis.PokemonBattleEngine.Data.Legality;\n\npublic sealed class PBELegalPokemonCollection : IPBEPokemonCollection, IPBEPokemonCollection<PBELegalPokemon>, INotifyCollectionChanged, INotifyPropertyChanged\n{\n\tprivate void OnCollectionChanged(NotifyCollectionChangedEventArgs e)\n\t{\n\t\tCollectionChanged?.Invoke(this, e);\n\t}\n\tprivate void OnPropertyChanged(string property)\n\t{\n\t\tPropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));\n\t}\n\tpublic event NotifyCollectionChangedEventHandler? CollectionChanged;\n\tpublic event PropertyChangedEventHandler? PropertyChanged;\n\n\tprivate readonly List<PBELegalPokemon> _list;\n\tpublic int Count => _list.Count;\n\tpublic PBELegalPokemon this[int index]\n\t{\n\t\tget\n\t\t{\n\t\t\tif (index >= _list.Count)\n\t\t\t{\n\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(index));\n\t\t\t}\n\t\t\treturn _list[index];\n\t\t}\n\t\tset => ReplaceAt(value, index);\n\t}\n\tIPBEPokemon IReadOnlyList<IPBEPokemon>.this[int index] => this[index];\n\n\tpublic PBESettings Settings { get; }\n\n\tinternal PBELegalPokemonCollection(PBESettings settings, EndianBinaryReader r)\n\t{\n\t\tbyte count = r.ReadByte();\n\t\tif (count < 1 || count > settings.MaxPartySize)\n\t\t{\n\t\t\tthrow new InvalidDataException();\n\t\t}\n\t\tSettings = settings;\n\t\t_list = new List<PBELegalPokemon>(Settings.MaxPartySize);\n\t\tfor (int i = 0; i < count; i++)\n\t\t{\n\t\t\tInsertWithEvents(false, new PBELegalPokemon(Settings, r), i);\n\t\t}\n\t}\n\tpublic PBELegalPokemonCollection(string path)\n\t{\n\t\tJsonObject jObj = JsonNode.Parse(File.ReadAllText(path))!.AsObject();\n\n\t\tSettings = new PBESettings(jObj.GetSafe(nameof(Settings)).GetValue<string>());\n\t\tSettings.MakeReadOnly();\n\n\t\tJsonArray jArray = jObj.GetSafe(\"Party\").AsArray();\n\t\tif (jArray.Count < 1 || jArray.Count > Settings.MaxPartySize)\n\t\t{\n\t\t\tthrow new InvalidDataException(\"Invalid party size.\");\n\t\t}\n\n\t\t_list = new List<PBELegalPokemon>(Settings.MaxPartySize);\n\t\tfor (int i = 0; i < jArray.Count; i++)\n\t\t{\n\t\t\tInsertWithEvents(false, new PBELegalPokemon(Settings, jArray.GetSafe(i).AsObject()), i);\n\t\t}\n\t}\n\tpublic PBELegalPokemonCollection(PBESettings settings)\n\t{\n\t\tsettings.ShouldBeReadOnly(nameof(settings));\n\t\tSettings = settings;\n\t\t_list = new List<PBELegalPokemon>(Settings.MaxPartySize);\n\t}\n\tpublic PBELegalPokemonCollection(PBESettings settings, int numPkmnToGenerate, bool setToMaxLevel)\n\t{\n\t\tsettings.ShouldBeReadOnly(nameof(settings));\n\t\tif (numPkmnToGenerate < 1 || numPkmnToGenerate > settings.MaxPartySize)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(numPkmnToGenerate));\n\t\t}\n\t\tSettings = settings;\n\t\t_list = new List<PBELegalPokemon>(Settings.MaxPartySize);\n\t\tfor (int i = 0; i < numPkmnToGenerate; i++)\n\t\t{\n\t\t\tInsertRandom(setToMaxLevel, false, i);\n\t\t}\n\t}\n\n\tprivate void InsertRandom(bool setToMaxLevel, bool fireEvent, int index)\n\t{\n\t\t(PBESpecies species, PBEForm form) = PBEDataProvider.GlobalRandom.RandomSpecies(true);\n\t\tbyte level = setToMaxLevel ? Settings.MaxLevel : PBEDataProvider.GlobalRandom.RandomLevel(Settings);\n\t\tInsert(species, form, level, PBEDataProvider.Instance.GetEXPRequired(PBEDataProvider.Instance.GetPokemonData(species, form).GrowthRate, level), fireEvent, index);\n\t}\n\tprivate void Insert(PBESpecies species, PBEForm form, byte level, uint exp, bool fireEvent, int index)\n\t{\n\t\tInsertWithEvents(fireEvent, new PBELegalPokemon(species, form, level, exp, Settings), index);\n\t}\n\tprivate void InsertWithEvents(bool fireEvent, PBELegalPokemon item, int index)\n\t{\n\t\t_list.Insert(index, item);\n\t\tif (fireEvent)\n\t\t{\n\t\t\tOnPropertyChanged(nameof(Count));\n\t\t\tOnPropertyChanged(\"Item[]\");\n\t\t\tOnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));\n\t\t}\n\t}\n\tprivate void RemoveWithEvents(PBELegalPokemon item, int index)\n\t{\n\t\t_list.RemoveAt(index);\n\t\tNotifyCollectionChangedEventArgs e;\n\t\tif (_list.Count == 0)\n\t\t{\n\t\t\tInsertRandom(false, false, 0);\n\t\t\te = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, _list[0], item, 0);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tOnPropertyChanged(nameof(Count));\n\t\t\te = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index);\n\t\t}\n\t\tOnPropertyChanged(\"Item[]\");\n\t\tOnCollectionChanged(e);\n\t}\n\n\tprivate void ExceedException()\n\t{\n\t\tthrow new InvalidOperationException($\"Party size cannot exceed \\\"{nameof(Settings.MaxPartySize)}\\\" ({Settings.MaxPartySize}).\");\n\t}\n\tpublic void AddRandom(bool setToMaxLevel)\n\t{\n\t\tif (_list.Count < Settings.MaxPartySize)\n\t\t{\n\t\t\tInsertRandom(setToMaxLevel, true, _list.Count);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tExceedException();\n\t\t}\n\t}\n\tpublic void Add(PBESpecies species, PBEForm form, byte level, uint exp)\n\t{\n\t\tPBEDataUtils.ValidateSpecies(species, form, true);\n\t\tPBEDataUtils.ValidateLevel(level, Settings);\n\t\tPBEDataUtils.ValidateEXP(PBEDataProvider.Instance.GetPokemonData(species, form).GrowthRate, exp, level);\n\t\tif (_list.Count < Settings.MaxPartySize)\n\t\t{\n\t\t\tInsert(species, form, level, exp, true, _list.Count);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tExceedException();\n\t\t}\n\t}\n\tpublic void Add(PBELegalPokemon item)\n\t{\n\t\tif (!Settings.Equals(item.Settings))\n\t\t{\n\t\t\tthrow new ArgumentException(\"Settings must be equal.\", nameof(item));\n\t\t}\n\t\tif (_list.Count < Settings.MaxPartySize)\n\t\t{\n\t\t\tInsertWithEvents(true, item, _list.Count);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tExceedException();\n\t\t}\n\t}\n\tpublic void InsertRandom(bool setToMaxLevel, int index)\n\t{\n\t\tif (_list.Count < Settings.MaxPartySize)\n\t\t{\n\t\t\tInsertRandom(setToMaxLevel, true, index);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tExceedException();\n\t\t}\n\t}\n\tpublic void Insert(PBESpecies species, PBEForm form, byte level, uint exp, int index)\n\t{\n\t\tPBEDataUtils.ValidateSpecies(species, form, true);\n\t\tPBEDataUtils.ValidateLevel(level, Settings);\n\t\tif (_list.Count < Settings.MaxPartySize)\n\t\t{\n\t\t\tInsert(species, form, level, exp, true, index);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tExceedException();\n\t\t}\n\t}\n\tpublic void Insert(PBELegalPokemon item, int index)\n\t{\n\t\tif (!Settings.Equals(item.Settings))\n\t\t{\n\t\t\tthrow new ArgumentException(\"Settings must be equal.\", nameof(item));\n\t\t}\n\t\tif (_list.Count < Settings.MaxPartySize)\n\t\t{\n\t\t\tInsertWithEvents(true, item, index);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tExceedException();\n\t\t}\n\t}\n\tpublic void Clear()\n\t{\n\t\tint oldCount = _list.Count;\n\t\t_list.Clear();\n\t\tInsertRandom(false, false, 0);\n\t\tif (oldCount != 1)\n\t\t{\n\t\t\tOnPropertyChanged(nameof(Count));\n\t\t}\n\t\tOnPropertyChanged(\"Item[]\");\n\t\tOnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));\n\t}\n\tpublic bool Remove(PBELegalPokemon item)\n\t{\n\t\tint index = _list.IndexOf(item);\n\t\tbool b = index != -1;\n\t\tif (b)\n\t\t{\n\t\t\tRemoveWithEvents(item, index);\n\t\t}\n\t\treturn b;\n\t}\n\tpublic void RemoveAt(int index)\n\t{\n\t\tif (index < 0 || index >= _list.Count)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(index));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tRemoveWithEvents(_list[index], index);\n\t\t}\n\t}\n\tpublic void ReplaceAt(PBELegalPokemon item, int index)\n\t{\n\t\tif (index >= _list.Count)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(index));\n\t\t}\n\t\tif (!Settings.Equals(item.Settings))\n\t\t{\n\t\t\tthrow new ArgumentException(\"Settings must be equal.\", nameof(item));\n\t\t}\n\t\tPBELegalPokemon old = _list[index];\n\t\t_list[index] = item;\n\t\tOnPropertyChanged(\"Item[]\");\n\t\tOnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, item, old, index));\n\t}\n\n\tpublic bool Contains(PBELegalPokemon item)\n\t{\n\t\treturn _list.IndexOf(item) != -1;\n\t}\n\tpublic int IndexOf(PBELegalPokemon item)\n\t{\n\t\treturn _list.IndexOf(item);\n\t}\n\n\tpublic IEnumerator<PBELegalPokemon> GetEnumerator()\n\t{\n\t\treturn _list.GetEnumerator();\n\t}\n\tIEnumerator IEnumerable.GetEnumerator()\n\t{\n\t\treturn _list.GetEnumerator();\n\t}\n\tIEnumerator<IPBEPokemon> IEnumerable<IPBEPokemon>.GetEnumerator()\n\t{\n\t\treturn _list.GetEnumerator();\n\t}\n\n\tpublic void ToJsonFile(string path)\n\t{\n\t\tusing (FileStream fs = File.OpenWrite(path))\n\t\tusing (var w = new Utf8JsonWriter(fs, options: new JsonWriterOptions { Indented = true }))\n\t\t{\n\t\t\tw.WriteStartObject();\n\n\t\t\tw.WriteString(nameof(Settings), Settings.ToString());\n\n\t\t\tw.WriteStartArray(\"Party\");\n\t\t\tfor (int i = 0; i < _list.Count; i++)\n\t\t\t{\n\t\t\t\t_list[i].ToJson(w);\n\t\t\t}\n\t\t\tw.WriteEndArray();\n\n\t\t\tw.WriteEndObject();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Data/PBEAlphabeticalList.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Collections.Specialized;\nusing System.ComponentModel;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq;\n\nnamespace Kermalis.PokemonBattleEngine.Data;\n\npublic sealed class PBEAlphabeticalList<T> : INotifyCollectionChanged, INotifyPropertyChanged, IReadOnlyList<T>\n{\n\tprivate sealed class PBEAlphabeticalListEntry\n\t{\n\t\tpublic T Key { get; }\n\t\tpublic IPBEReadOnlyLocalizedString Value { get; }\n\n\t\tpublic PBEAlphabeticalListEntry(T key, object? parameter)\n\t\t{\n\t\t\tswitch (key)\n\t\t\t{\n\t\t\t\tcase PBEAbility ability: Value = PBEDataProvider.Instance.GetAbilityName(ability); break;\n\t\t\t\tcase PBEForm form: Value = PBEDataProvider.Instance.GetFormName((PBESpecies)parameter!, form); break;\n\t\t\t\tcase PBEGender gender: Value = PBEDataProvider.Instance.GetGenderName(gender); break;\n\t\t\t\tcase PBEItem item: Value = PBEDataProvider.Instance.GetItemName(item); break;\n\t\t\t\tcase PBEMove move: Value = PBEDataProvider.Instance.GetMoveName(move); break;\n\t\t\t\tcase PBENature nature: Value = PBEDataProvider.Instance.GetNatureName(nature); break;\n\t\t\t\tcase PBESpecies species: Value = PBEDataProvider.Instance.GetSpeciesName(species); break;\n\t\t\t\tcase PBEStat stat: Value = PBEDataProvider.Instance.GetStatName(stat); break;\n\t\t\t\tcase PBEType type: Value = PBEDataProvider.Instance.GetTypeName(type); break;\n\t\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(key));\n\t\t\t}\n\t\t\tKey = key;\n\t\t}\n\t}\n\n\tprivate void OnCollectionChanged(NotifyCollectionChangedEventArgs e)\n\t{\n\t\tCollectionChanged?.Invoke(this, e);\n\t}\n\tprivate void OnPropertyChanged(string property)\n\t{\n\t\tPropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));\n\t}\n\tpublic event NotifyCollectionChangedEventHandler? CollectionChanged;\n\tpublic event PropertyChangedEventHandler? PropertyChanged;\n\n\tprivate PBEAlphabeticalListEntry[] _list;\n\tpublic int Count => _list.Length;\n\tpublic T this[int index]\n\t{\n\t\tget\n\t\t{\n\t\t\tif (index >= _list.Length)\n\t\t\t{\n\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(index));\n\t\t\t}\n\t\t\treturn _list[index].Key;\n\t\t}\n\t}\n\n\tinternal PBEAlphabeticalList()\n\t{\n\t\t_list = Array.Empty<PBEAlphabeticalListEntry>();\n\t}\n\tinternal PBEAlphabeticalList(IEnumerable<T> collection, object? parameter = null)\n\t{\n\t\tReset(collection, parameter: parameter);\n\t}\n\n\tprivate void Sort(PBEAlphabeticalListEntry[]? old)\n\t{\n\t\tif (old is null || old == _list)\n\t\t{\n\t\t\told = (PBEAlphabeticalListEntry[])_list.Clone();\n\t\t}\n\t\tArray.Sort(_list, (x, y) => x.Value.FromGlobalLanguage().CompareTo(y.Value.FromGlobalLanguage()));\n\t\tif (!_list.SequenceEqual(old))\n\t\t{\n\t\t\tOnPropertyChanged(\"Item[]\");\n\t\t\tOnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));\n\t\t}\n\t}\n\n\t[MemberNotNull(nameof(_list))]\n\tinternal void Reset(IEnumerable<T> collection, object? parameter = null)\n\t{\n\t\tPBEAlphabeticalListEntry[]? old = _list;\n\t\tif (collection is PBEAlphabeticalList<T> other)\n\t\t{\n\t\t\t_list = (PBEAlphabeticalListEntry[])other._list.Clone();\n\t\t}\n\t\telse\n\t\t{\n\t\t\t_list = collection.Select(t => new PBEAlphabeticalListEntry(t, parameter)).ToArray();\n\t\t}\n\t\tif (old is not null && old.Length != _list.Length)\n\t\t{\n\t\t\tOnPropertyChanged(nameof(Count));\n\t\t}\n\t\tSort(old);\n\t}\n\n\tpublic bool Contains(T? item)\n\t{\n\t\treturn IndexOf(item) != -1;\n\t}\n\tpublic List<T> FindAll(Predicate<T> match)\n\t{\n\t\tvar results = new List<T>(_list.Length);\n\t\tfor (int i = 0; i < _list.Length; i++)\n\t\t{\n\t\t\tT key = _list[i].Key;\n\t\t\tif (match(key))\n\t\t\t{\n\t\t\t\tresults.Add(key);\n\t\t\t}\n\t\t}\n\t\treturn results;\n\t}\n\tpublic int IndexOf(T? item)\n\t{\n\t\tif (item is not null)\n\t\t{\n\t\t\tfor (int i = 0; i < _list.Length; i++)\n\t\t\t{\n\t\t\t\tif (item.Equals(_list[i].Key))\n\t\t\t\t{\n\t\t\t\t\treturn i;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n\n\tpublic IEnumerator<T> GetEnumerator()\n\t{\n\t\tfor (int i = 0; i < _list.Length; i++)\n\t\t{\n\t\t\tyield return _list[i].Key;\n\t\t}\n\t}\n\tIEnumerator IEnumerable.GetEnumerator()\n\t{\n\t\treturn GetEnumerator();\n\t}\n\n\tpublic ReadOnlyCollection<T> AsReadOnly()\n\t{\n\t\treturn ToList().AsReadOnly();\n\t}\n\tpublic T[] ToArray()\n\t{\n\t\tvar arr = new T[_list.Length];\n\t\tfor (int i = 0; i < _list.Length; i++)\n\t\t{\n\t\t\tarr[i] = _list[i].Key;\n\t\t}\n\t\treturn arr;\n\t}\n\tpublic List<T> ToList()\n\t{\n\t\tvar list = new List<T>(_list.Length);\n\t\tfor (int i = 0; i < _list.Length; i++)\n\t\t{\n\t\t\tlist.Add(_list[i].Key);\n\t\t}\n\t\treturn list;\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Data/PBEList.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Collections.Specialized;\nusing System.ComponentModel;\nusing System.Linq;\n\nnamespace Kermalis.PokemonBattleEngine.Data;\n\npublic sealed class PBEList<T> : INotifyCollectionChanged, INotifyPropertyChanged, IReadOnlyList<T>\n{\n\tprivate void OnCollectionChanged(NotifyCollectionChangedEventArgs e)\n\t{\n\t\tCollectionChanged?.Invoke(this, e);\n\t}\n\tprivate void OnPropertyChanged(string property)\n\t{\n\t\tPropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));\n\t}\n\tpublic event NotifyCollectionChangedEventHandler? CollectionChanged;\n\tpublic event PropertyChangedEventHandler? PropertyChanged;\n\n\tprivate readonly List<T> _list;\n\tpublic int Count => _list.Count;\n\tpublic T this[int index]\n\t{\n\t\tget\n\t\t{\n\t\t\tif (index >= _list.Count)\n\t\t\t{\n\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(index));\n\t\t\t}\n\t\t\treturn _list[index];\n\t\t}\n\t}\n\n\tinternal PBEList()\n\t{\n\t\t_list = new List<T>();\n\t}\n\tinternal PBEList(int capacity)\n\t{\n\t\t_list = new List<T>(capacity);\n\t}\n\tinternal PBEList(IEnumerable<T> collection)\n\t{\n\t\t_list = new List<T>(collection);\n\t}\n\n\tinternal void Add(T item)\n\t{\n\t\tint index = _list.Count;\n\t\t_list.Insert(index, item);\n\t\tOnPropertyChanged(nameof(Count));\n\t\tOnPropertyChanged(\"Item[]\");\n\t\tOnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));\n\t}\n\tinternal void Insert(int index, T item)\n\t{\n\t\t_list.Insert(index, item);\n\t\tOnPropertyChanged(nameof(Count));\n\t\tOnPropertyChanged(\"Item[]\");\n\t\tOnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));\n\t}\n\tinternal bool Remove(T item)\n\t{\n\t\tint index = _list.IndexOf(item);\n\t\tbool b = index != -1;\n\t\tif (b)\n\t\t{\n\t\t\t_list.RemoveAt(index);\n\t\t\tOnPropertyChanged(nameof(Count));\n\t\t\tOnPropertyChanged(\"Item[]\");\n\t\t\tOnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));\n\t\t}\n\t\treturn b;\n\t}\n\tinternal void RemoveAt(int index)\n\t{\n\t\tif (index < 0 || index >= _list.Count)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(index));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tT item = _list[index];\n\t\t\t_list.RemoveAt(index);\n\t\t\tOnPropertyChanged(nameof(Count));\n\t\t\tOnPropertyChanged(\"Item[]\");\n\t\t\tOnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));\n\t\t}\n\t}\n\tinternal void Reset(IEnumerable<T> collection)\n\t{\n\t\tint oldCount = _list.Count;\n\t\tif (!_list.SequenceEqual(collection))\n\t\t{\n\t\t\t_list.Clear();\n\t\t\t_list.AddRange(collection);\n\t\t\tif (oldCount != _list.Count)\n\t\t\t{\n\t\t\t\tOnPropertyChanged(nameof(Count));\n\t\t\t}\n\t\t\tOnPropertyChanged(\"Item[]\");\n\t\t\tOnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));\n\t\t}\n\t}\n\tinternal void Swap(T a, T b)\n\t{\n\t\tint aIndex = IndexOf(a);\n\t\tif (aIndex == -1)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(a));\n\t\t}\n\t\tint bIndex = IndexOf(b);\n\t\tif (bIndex == -1)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(b));\n\t\t}\n\t\t_list[aIndex] = b;\n\t\t_list[bIndex] = a;\n\t\tOnPropertyChanged(\"Item[]\");\n\t\tOnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, a, b, bIndex));\n\t\tOnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, b, a, aIndex));\n\t}\n\n\tpublic bool Contains(T item)\n\t{\n\t\treturn _list.IndexOf(item) != -1;\n\t}\n\tpublic List<T> FindAll(Predicate<T> match)\n\t{\n\t\treturn _list.FindAll(match);\n\t}\n\tpublic int IndexOf(T item)\n\t{\n\t\treturn _list.IndexOf(item);\n\t}\n\n\tpublic T[] ToArray()\n\t{\n\t\treturn _list.ToArray();\n\t}\n\n\tpublic IEnumerator<T> GetEnumerator()\n\t{\n\t\tfor (int i = 0; i < _list.Count; i++)\n\t\t{\n\t\t\tyield return _list[i];\n\t\t}\n\t}\n\tIEnumerator IEnumerable.GetEnumerator()\n\t{\n\t\treturn ((IEnumerable<T>)this).GetEnumerator();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Data/ReadOnlyLocalizedString.cs",
    "content": "﻿namespace Kermalis.PokemonBattleEngine.Data;\n\npublic sealed class PBEReadOnlyLocalizedString : IPBEReadOnlyLocalizedString\n{\n\tpublic string English { get; }\n\tpublic string French { get; }\n\tpublic string German { get; }\n\tpublic string Italian { get; }\n\tpublic string Japanese_Kana { get; }\n\tpublic string Japanese_Kanji { get; }\n\tpublic string Korean { get; }\n\tpublic string Spanish { get; }\n\n\tpublic PBEReadOnlyLocalizedString(IPBEReadOnlyLocalizedString other)\n\t{\n\t\tEnglish = other.English;\n\t\tFrench = other.French;\n\t\tGerman = other.German;\n\t\tItalian = other.Italian;\n\t\tJapanese_Kana = other.Japanese_Kana;\n\t\tJapanese_Kanji = other.Japanese_Kanji;\n\t\tKorean = other.Korean;\n\t\tSpanish = other.Spanish;\n\t}\n\n\tpublic override string ToString()\n\t{\n\t\treturn this.FromGlobalLanguage();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Data/ReadOnlyMoveset.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Data.Utils;\nusing Kermalis.PokemonBattleEngine.Utils;\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Text.Json.Nodes;\n\nnamespace Kermalis.PokemonBattleEngine.Data;\n\npublic sealed class PBEReadOnlyMoveset : IPBEMoveset, IPBEMoveset<PBEReadOnlyMoveset.PBEReadOnlyMovesetSlot>\n{\n\tpublic sealed class PBEReadOnlyMovesetSlot : IPBEMovesetSlot\n\t{\n\t\tpublic PBEMove Move { get; }\n\t\tpublic byte PPUps { get; }\n\n\t\tinternal PBEReadOnlyMovesetSlot(PBEMove move, byte ppUps)\n\t\t{\n\t\t\tMove = move;\n\t\t\tPPUps = ppUps;\n\t\t}\n\t}\n\n\tprivate readonly PBEReadOnlyMovesetSlot[] _list;\n\tpublic int Count => _list.Length;\n\tpublic PBEReadOnlyMovesetSlot this[int index]\n\t{\n\t\tget\n\t\t{\n\t\t\tif (index >= _list.Length)\n\t\t\t{\n\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(index));\n\t\t\t}\n\t\t\treturn _list[index];\n\t\t}\n\t}\n\tIPBEMovesetSlot IReadOnlyList<IPBEMovesetSlot>.this[int index] => this[index];\n\n\tinternal PBEReadOnlyMoveset(EndianBinaryReader r)\n\t{\n\t\tint count = r.ReadByte();\n\t\t_list = new PBEReadOnlyMovesetSlot[count];\n\t\tfor (int i = 0; i < count; i++)\n\t\t{\n\t\t\t_list[i] = new PBEReadOnlyMovesetSlot(r.ReadEnum<PBEMove>(), r.ReadByte());\n\t\t}\n\t}\n\tinternal PBEReadOnlyMoveset(JsonArray jArray)\n\t{\n\t\tint count = jArray.Count;\n\t\t_list = new PBEReadOnlyMovesetSlot[count];\n\t\tfor (int i = 0; i < count; i++)\n\t\t{\n\t\t\tJsonObject jObj = jArray.GetSafe(i).AsObject();\n\t\t\tif (!PBEDataProvider.Instance.GetMoveByName(jObj.GetSafe(nameof(IPBEMovesetSlot.Move)).GetValue<string>(), out PBEMove? move))\n\t\t\t{\n\t\t\t\tthrow new InvalidDataException(\"Invalid move\");\n\t\t\t}\n\n\t\t\tbyte ppUps = jObj.GetSafe(nameof(IPBEMovesetSlot.PPUps)).GetValue<byte>();\n\t\t\t_list[i] = new PBEReadOnlyMovesetSlot(move.Value, ppUps);\n\t\t}\n\t}\n\tpublic PBEReadOnlyMoveset(IPBEMoveset other)\n\t{\n\t\tint count = other.Count;\n\t\t_list = new PBEReadOnlyMovesetSlot[count];\n\t\tfor (int i = 0; i < count; i++)\n\t\t{\n\t\t\tIPBEMovesetSlot oSlot = other[i];\n\t\t\t_list[i] = new PBEReadOnlyMovesetSlot(oSlot.Move, oSlot.PPUps);\n\t\t}\n\t}\n\n\tpublic IEnumerator<PBEReadOnlyMovesetSlot> GetEnumerator()\n\t{\n\t\tfor (int i = 0; i < _list.Length; i++)\n\t\t{\n\t\t\tyield return _list[i];\n\t\t}\n\t}\n\tIEnumerator<IPBEMovesetSlot> IEnumerable<IPBEMovesetSlot>.GetEnumerator()\n\t{\n\t\treturn GetEnumerator();\n\t}\n\tIEnumerator IEnumerable.GetEnumerator()\n\t{\n\t\treturn GetEnumerator();\n\t}\n}\n\npublic sealed class PBEReadOnlyPartyMoveset : IPBEPartyMoveset, IPBEPartyMoveset<PBEReadOnlyPartyMoveset.PBEReadOnlyPartyMovesetSlot>\n{\n\tpublic sealed class PBEReadOnlyPartyMovesetSlot : IPBEPartyMovesetSlot\n\t{\n\t\tpublic PBEMove Move { get; }\n\t\tpublic int PP { get; }\n\t\tpublic byte PPUps { get; }\n\n\t\tinternal PBEReadOnlyPartyMovesetSlot(PBESettings settings, PBEMove move, byte ppUps)\n\t\t{\n\t\t\tMove = move;\n\t\t\tPP = PBEDataUtils.CalcMaxPP(move, ppUps, settings);\n\t\t\tPPUps = ppUps;\n\t\t}\n\t\tinternal PBEReadOnlyPartyMovesetSlot(PBEMove move, int pp, byte ppUps)\n\t\t{\n\t\t\tMove = move;\n\t\t\tPP = pp;\n\t\t\tPPUps = ppUps;\n\t\t}\n\t}\n\n\tprivate readonly PBEReadOnlyPartyMovesetSlot[] _list;\n\tpublic int Count => _list.Length;\n\tpublic PBEReadOnlyPartyMovesetSlot this[int index]\n\t{\n\t\tget\n\t\t{\n\t\t\tif (index >= _list.Length)\n\t\t\t{\n\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(index));\n\t\t\t}\n\t\t\treturn _list[index];\n\t\t}\n\t}\n\tIPBEPartyMovesetSlot IReadOnlyList<IPBEPartyMovesetSlot>.this[int index] => this[index];\n\n\tinternal PBEReadOnlyPartyMoveset(EndianBinaryReader r)\n\t{\n\t\tint count = r.ReadByte();\n\t\t_list = new PBEReadOnlyPartyMovesetSlot[count];\n\t\tfor (int i = 0; i < count; i++)\n\t\t{\n\t\t\t_list[i] = new PBEReadOnlyPartyMovesetSlot(r.ReadEnum<PBEMove>(), r.ReadInt32(), r.ReadByte());\n\t\t}\n\t}\n\tinternal PBEReadOnlyPartyMoveset(JsonArray jArray)\n\t{\n\t\tint count = jArray.Count;\n\t\t_list = new PBEReadOnlyPartyMovesetSlot[count];\n\t\tfor (int i = 0; i < count; i++)\n\t\t{\n\t\t\tJsonObject jObj = jArray.GetSafe(i).AsObject();\n\t\t\tif (!PBEDataProvider.Instance.GetMoveByName(jObj.GetSafe(nameof(IPBEMovesetSlot.Move)).GetValue<string>(), out PBEMove? move))\n\t\t\t{\n\t\t\t\tthrow new InvalidDataException(\"Invalid move\");\n\t\t\t}\n\n\t\t\tint pp = jObj.GetSafe(nameof(IPBEPartyMovesetSlot.PP)).GetValue<int>();\n\t\t\tbyte ppUps = jObj.GetSafe(nameof(IPBEPartyMovesetSlot.PPUps)).GetValue<byte>();\n\t\t\t_list[i] = new PBEReadOnlyPartyMovesetSlot(move.Value, pp, ppUps);\n\t\t}\n\t}\n\tpublic PBEReadOnlyPartyMoveset(PBESettings settings, IPBEMoveset other)\n\t{\n\t\tsettings.ShouldBeReadOnly(nameof(settings));\n\t\tint count = other.Count;\n\t\t_list = new PBEReadOnlyPartyMovesetSlot[count];\n\t\tfor (int i = 0; i < count; i++)\n\t\t{\n\t\t\tIPBEMovesetSlot oSlot = other[i];\n\t\t\t_list[i] = new PBEReadOnlyPartyMovesetSlot(settings, oSlot.Move, oSlot.PPUps);\n\t\t}\n\t}\n\tpublic PBEReadOnlyPartyMoveset(IPBEPartyMoveset other)\n\t{\n\t\tint count = other.Count;\n\t\t_list = new PBEReadOnlyPartyMovesetSlot[count];\n\t\tfor (int i = 0; i < count; i++)\n\t\t{\n\t\t\tIPBEPartyMovesetSlot oSlot = other[i];\n\t\t\t_list[i] = new PBEReadOnlyPartyMovesetSlot(oSlot.Move, oSlot.PP, oSlot.PPUps);\n\t\t}\n\t}\n\n\tpublic IEnumerator<PBEReadOnlyPartyMovesetSlot> GetEnumerator()\n\t{\n\t\tfor (int i = 0; i < _list.Length; i++)\n\t\t{\n\t\t\tyield return _list[i];\n\t\t}\n\t}\n\tIEnumerator<IPBEPartyMovesetSlot> IEnumerable<IPBEPartyMovesetSlot>.GetEnumerator()\n\t{\n\t\treturn GetEnumerator();\n\t}\n\tIEnumerator IEnumerable.GetEnumerator()\n\t{\n\t\treturn GetEnumerator();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Data/ReadOnlyPokemon.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\n\nnamespace Kermalis.PokemonBattleEngine.Data;\n\npublic sealed class PBEReadOnlyPokemon : IPBEPokemon\n{\n\tpublic bool PBEIgnore => false;\n\tpublic PBESpecies Species { get; }\n\tpublic PBEForm Form { get; }\n\tpublic PBEGender Gender { get; }\n\tpublic string Nickname { get; }\n\tpublic bool Shiny { get; }\n\tpublic byte Level { get; }\n\tpublic uint EXP { get; }\n\tpublic bool Pokerus { get; }\n\tpublic PBEItem Item { get; }\n\tpublic byte Friendship { get; }\n\tpublic PBEAbility Ability { get; }\n\tpublic PBENature Nature { get; }\n\tpublic PBEItem CaughtBall { get; }\n\tpublic IPBEStatCollection EffortValues { get; }\n\tpublic IPBEReadOnlyStatCollection IndividualValues { get; }\n\tpublic IPBEMoveset Moveset { get; }\n\n\tinternal PBEReadOnlyPokemon(EndianBinaryReader r)\n\t{\n\t\tSpecies = r.ReadEnum<PBESpecies>();\n\t\tForm = r.ReadEnum<PBEForm>();\n\t\tNickname = r.ReadString_NullTerminated();\n\t\tLevel = r.ReadByte();\n\t\tEXP = r.ReadUInt32();\n\t\tFriendship = r.ReadByte();\n\t\tShiny = r.ReadBoolean();\n\t\tPokerus = r.ReadBoolean();\n\t\tAbility = r.ReadEnum<PBEAbility>();\n\t\tNature = r.ReadEnum<PBENature>();\n\t\tCaughtBall = r.ReadEnum<PBEItem>();\n\t\tGender = r.ReadEnum<PBEGender>();\n\t\tItem = r.ReadEnum<PBEItem>();\n\t\tEffortValues = new PBEStatCollection(r);\n\t\tIndividualValues = new PBEReadOnlyStatCollection(r);\n\t\tMoveset = new PBEReadOnlyMoveset(r);\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Data/ReadOnlyPokemonCollection.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\n\nnamespace Kermalis.PokemonBattleEngine.Data;\n\npublic sealed class PBEReadOnlyPokemonCollection : IPBEPokemonCollection, IPBEPokemonCollection<PBEReadOnlyPokemon>\n{\n\tprivate readonly PBEReadOnlyPokemon[] _list;\n\tpublic int Count => _list.Length;\n\tpublic PBEReadOnlyPokemon this[int index]\n\t{\n\t\tget\n\t\t{\n\t\t\tif (index >= _list.Length)\n\t\t\t{\n\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(index));\n\t\t\t}\n\t\t\treturn _list[index];\n\t\t}\n\t}\n\tIPBEPokemon IReadOnlyList<IPBEPokemon>.this[int index] => this[index];\n\n\tinternal PBEReadOnlyPokemonCollection(EndianBinaryReader r)\n\t{\n\t\tbyte count = r.ReadByte();\n\t\t_list = new PBEReadOnlyPokemon[count];\n\t\tfor (int i = 0; i < count; i++)\n\t\t{\n\t\t\t_list[i] = new PBEReadOnlyPokemon(r);\n\t\t}\n\t}\n\n\tIEnumerator IEnumerable.GetEnumerator()\n\t{\n\t\treturn _list.GetEnumerator();\n\t}\n\tIEnumerator<IPBEPokemon> IEnumerable<IPBEPokemon>.GetEnumerator()\n\t{\n\t\treturn ((IEnumerable<PBEReadOnlyPokemon>)_list).GetEnumerator();\n\t}\n\tpublic IEnumerator<PBEReadOnlyPokemon> GetEnumerator()\n\t{\n\t\treturn ((IEnumerable<PBEReadOnlyPokemon>)_list).GetEnumerator();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Data/Settings.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing System;\nusing System.ComponentModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Data;\n\n#pragma warning disable CS0618 // Type or member is obsolete\n/// <summary>The various engine settings.</summary>\npublic sealed class PBESettings : INotifyPropertyChanged\n{\n\tprivate void OnPropertyChanged(string property)\n\t{\n\t\tPropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));\n\t}\n\t/// <summary>Fires whenever a property changes.</summary>\n\tpublic event PropertyChangedEventHandler? PropertyChanged;\n\n\tprivate bool _isReadOnly;\n\t/// <summary>Gets a value that indicates whether this <see cref=\"PBESettings\"/> object is read-only.</summary>\n\tpublic bool IsReadOnly\n\t{\n\t\tget => _isReadOnly;\n\t\tprivate set\n\t\t{\n\t\t\tif (_isReadOnly != value)\n\t\t\t{\n\t\t\t\t_isReadOnly = value;\n\t\t\t\tOnPropertyChanged(nameof(IsReadOnly));\n\t\t\t}\n\t\t}\n\t}\n\n\t/// <summary>The default settings used in official games.</summary>\n\tpublic static PBESettings DefaultSettings { get; }\n\n\tstatic PBESettings()\n\t{\n\t\tDefaultSettings = new PBESettings();\n\t\tDefaultSettings.MakeReadOnly();\n\t}\n\n\t#region Properties\n\n\t/// <summary>The default value of <see cref=\"MaxLevel\"/>.</summary>\n\tpublic const byte DefaultMaxLevel = 100;\n\tprivate byte _maxLevel = DefaultMaxLevel;\n\t/// <summary>The maximum level a Pokémon can be. Not used in stat/damage calculation.</summary>\n\tpublic byte MaxLevel\n\t{\n\t\tget => _maxLevel;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_maxLevel != value)\n\t\t\t{\n\t\t\t\tif (value < _minLevel)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"{nameof(MaxLevel)} must be at least {nameof(MinLevel)} ({_minLevel}).\");\n\t\t\t\t}\n\t\t\t\t_maxLevel = value;\n\t\t\t\tOnPropertyChanged(nameof(MaxLevel));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"MinLevel\"/>.</summary>\n\tpublic const byte DefaultMinLevel = 1;\n\tprivate byte _minLevel = DefaultMinLevel;\n\t/// <summary>The minimum level a Pokémon can be.</summary>\n\tpublic byte MinLevel\n\t{\n\t\tget => _minLevel;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_minLevel != value)\n\t\t\t{\n\t\t\t\tif (value < 1 || value > _maxLevel)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"{nameof(MinLevel)} must be at least 1 and cannot exceed {nameof(MaxLevel)} ({_maxLevel}).\");\n\t\t\t\t}\n\t\t\t\t_minLevel = value;\n\t\t\t\tOnPropertyChanged(nameof(MinLevel));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"MaxPartySize\"/>.</summary>\n\tpublic const byte DefaultMaxPartySize = 6;\n\tprivate byte _maxPartySize = DefaultMaxPartySize;\n\t/// <summary>The maximum amount of Pokémon each trainer can bring into a battle.</summary>\n\tpublic byte MaxPartySize\n\t{\n\t\tget => _maxPartySize;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_maxPartySize != value)\n\t\t\t{\n\t\t\t\tif (value < 1)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"{nameof(MaxPartySize)} must be at least 1.\");\n\t\t\t\t}\n\t\t\t\t_maxPartySize = value;\n\t\t\t\tOnPropertyChanged(nameof(MaxPartySize));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"MaxPokemonNameLength\"/>.</summary>\n\tpublic const byte DefaultMaxPokemonNameLength = 10;\n\tprivate byte _maxPokemonNameLength = DefaultMaxPokemonNameLength;\n\t/// <summary>The maximum amount of characters a Pokémon nickname can have.</summary>\n\tpublic byte MaxPokemonNameLength\n\t{\n\t\tget => _maxPokemonNameLength;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_maxPokemonNameLength != value)\n\t\t\t{\n\t\t\t\tif (value < 1)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"{nameof(MaxPokemonNameLength)} must be at least 1.\");\n\t\t\t\t}\n\t\t\t\t_maxPokemonNameLength = value;\n\t\t\t\tOnPropertyChanged(nameof(MaxPokemonNameLength));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"MaxTrainerNameLength\"/>. This value is different in non-English games.</summary>\n\tpublic const byte DefaultMaxTrainerNameLength = 7;\n\tprivate byte _maxTrainerNameLength = DefaultMaxTrainerNameLength;\n\t/// <summary>The maximum amount of characters a trainer's name can have.</summary>\n\t[Obsolete(\"Currently not used anywhere.\")]\n\tpublic byte MaxTrainerNameLength\n\t{\n\t\tget => _maxTrainerNameLength;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_maxTrainerNameLength != value)\n\t\t\t{\n\t\t\t\tif (value < 1)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"{nameof(MaxTrainerNameLength)} must be at least 1.\");\n\t\t\t\t}\n\t\t\t\t_maxTrainerNameLength = value;\n\t\t\t\tOnPropertyChanged(nameof(MaxTrainerNameLength));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"MaxTotalEVs\"/>.</summary>\n\tpublic const ushort DefaultMaxTotalEVs = 510;\n\tprivate ushort _maxTotalEVs = DefaultMaxTotalEVs;\n\t/// <summary>The maximum sum of a Pokémon's EVs.</summary>\n\tpublic ushort MaxTotalEVs\n\t{\n\t\tget => _maxTotalEVs;\n\t\tset\n\t\t{\n\t\t\tconst int max = byte.MaxValue * 6;\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_maxTotalEVs != value)\n\t\t\t{\n\t\t\t\tif (value > max)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"{nameof(MaxTotalEVs)} must not exceed {max}.\");\n\t\t\t\t}\n\t\t\t\t_maxTotalEVs = value;\n\t\t\t\tOnPropertyChanged(nameof(MaxTotalEVs));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"MaxIVs\"/>.</summary>\n\tpublic const byte DefaultMaxIVs = 31;\n\tprivate byte _maxIVs = DefaultMaxIVs;\n\t/// <summary>The maximum amount of IVs Pokémon can have in each stat. Raising this will not affect <see cref=\"PBEMoveEffect.HiddenPower\"/>.</summary>\n\tpublic byte MaxIVs\n\t{\n\t\tget => _maxIVs;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_maxIVs != value)\n\t\t\t{\n\t\t\t\t_maxIVs = value;\n\t\t\t\tOnPropertyChanged(nameof(MaxIVs));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"NatureStatBoost\"/>.</summary>\n\tpublic const float DefaultNatureStatBoost = 0.1f;\n\tprivate float _natureStatBoost = DefaultNatureStatBoost;\n\t/// <summary>The amount of influence a Pokémon's <see cref=\"PBENature\"/> has on its stats.</summary>\n\tpublic float NatureStatBoost\n\t{\n\t\tget => _natureStatBoost;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_natureStatBoost != value)\n\t\t\t{\n\t\t\t\tif (value < 0)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"{nameof(NatureStatBoost)} must be at least 0.\");\n\t\t\t\t}\n\t\t\t\t_natureStatBoost = value;\n\t\t\t\tOnPropertyChanged(nameof(NatureStatBoost));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"MaxStatChange\"/>.</summary>\n\tpublic const sbyte DefaultMaxStatChange = 6;\n\tprivate sbyte _maxStatChange = DefaultMaxStatChange;\n\t/// <summary>The maximum change a stat can have in the negative and positive direction.</summary>\n\tpublic sbyte MaxStatChange\n\t{\n\t\tget => _maxStatChange;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_maxStatChange != value)\n\t\t\t{\n\t\t\t\t_maxStatChange = value;\n\t\t\t\tOnPropertyChanged(nameof(MaxStatChange));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"NumMoves\"/>.</summary>\n\tpublic const byte DefaultNumMoves = 4;\n\tprivate byte _numMoves = DefaultNumMoves;\n\t/// <summary>The maximum amount of moves a specific Pokémon can remember at once.</summary>\n\tpublic byte NumMoves\n\t{\n\t\tget => _numMoves;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_numMoves != value)\n\t\t\t{\n\t\t\t\tif (value < 1)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"{nameof(NumMoves)} must be at least 1.\");\n\t\t\t\t}\n\t\t\t\t_numMoves = value;\n\t\t\t\tOnPropertyChanged(nameof(NumMoves));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"PPMultiplier\"/>.</summary>\n\tpublic const byte DefaultPPMultiplier = 5;\n\tprivate byte _ppMultiplier = DefaultPPMultiplier;\n\t/// <summary>This affects the base PP of each move and the boost PP-Ups give. The formulas that determine PP are at <see cref=\"PBEBattleMoveset.GetNonTransformPP(PBESettings, PBEMove, byte)\"/> and <see cref=\"PBEBattleMoveset.GetTransformPP(PBESettings, PBEMove)\"/>.</summary>\n\tpublic byte PPMultiplier\n\t{\n\t\tget => _ppMultiplier;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_ppMultiplier != value)\n\t\t\t{\n\t\t\t\tif (value < 1)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"{nameof(PPMultiplier)} must be at least 1.\");\n\t\t\t\t}\n\t\t\t\t_ppMultiplier = value;\n\t\t\t\tOnPropertyChanged(nameof(PPMultiplier));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"MaxPPUps\"/>.</summary>\n\tpublic const byte DefaultMaxPPUps = 3;\n\tprivate byte _maxPPUps = DefaultMaxPPUps;\n\t/// <summary>The maximum amount of PP-Ups that can be used on each of a Pokémon's moves.</summary>\n\tpublic byte MaxPPUps\n\t{\n\t\tget => _maxPPUps;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_maxPPUps != value)\n\t\t\t{\n\t\t\t\t_maxPPUps = value;\n\t\t\t\tOnPropertyChanged(nameof(MaxPPUps));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"CritMultiplier\"/>.</summary>\n\tpublic const float DefaultCritMultiplier = 2.0f;\n\tprivate float _critMultiplier = DefaultCritMultiplier;\n\t/// <summary>The damage boost awarded by critical hits.</summary>\n\tpublic float CritMultiplier\n\t{\n\t\tget => _critMultiplier;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_critMultiplier != value)\n\t\t\t{\n\t\t\t\t_critMultiplier = value;\n\t\t\t\tOnPropertyChanged(nameof(CritMultiplier));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"ConfusionMaxTurns\"/>.</summary>\n\tpublic const byte DefaultConfusionMaxTurns = 4;\n\tprivate byte _confusionMaxTurns = DefaultConfusionMaxTurns;\n\t/// <summary>The maximum amount of turns a Pokémon can be <see cref=\"PBEStatus2.Confused\"/>.</summary>\n\tpublic byte ConfusionMaxTurns\n\t{\n\t\tget => _confusionMaxTurns;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_confusionMaxTurns != value)\n\t\t\t{\n\t\t\t\tif (value < _confusionMinTurns)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"{nameof(ConfusionMaxTurns)} must be at least {nameof(ConfusionMinTurns)} ({_confusionMinTurns}).\");\n\t\t\t\t}\n\t\t\t\t_confusionMaxTurns = value;\n\t\t\t\tOnPropertyChanged(nameof(ConfusionMaxTurns));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"ConfusionMinTurns\"/>.</summary>\n\tpublic const byte DefaultConfusionMinTurns = 1;\n\tprivate byte _confusionMinTurns = DefaultConfusionMinTurns;\n\t/// <summary>The minimum amount of turns a Pokémon can be <see cref=\"PBEStatus2.Confused\"/>.</summary>\n\tpublic byte ConfusionMinTurns\n\t{\n\t\tget => _confusionMinTurns;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_confusionMinTurns != value)\n\t\t\t{\n\t\t\t\tif (value > _confusionMaxTurns)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"{nameof(ConfusionMinTurns)} cannot exceed {nameof(ConfusionMaxTurns)} ({_confusionMaxTurns}).\");\n\t\t\t\t}\n\t\t\t\t_confusionMinTurns = value;\n\t\t\t\tOnPropertyChanged(nameof(ConfusionMinTurns));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"SleepMaxTurns\"/>.</summary>\n\tpublic const byte DefaultSleepMaxTurns = 3;\n\tprivate byte _sleepMaxTurns = DefaultSleepMaxTurns;\n\t/// <summary>The maximum amount of turns a Pokémon can be <see cref=\"PBEStatus1.Asleep\"/>. <see cref=\"PBEMoveEffect.Rest\"/> will always sleep for <see cref=\"SleepMaxTurns\"/> turns.</summary>\n\tpublic byte SleepMaxTurns\n\t{\n\t\tget => _sleepMaxTurns;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_sleepMaxTurns != value)\n\t\t\t{\n\t\t\t\tif (value < _sleepMinTurns)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"{nameof(SleepMaxTurns)} must be at least {nameof(SleepMinTurns)} ({_sleepMinTurns}).\");\n\t\t\t\t}\n\t\t\t\t_sleepMaxTurns = value;\n\t\t\t\tOnPropertyChanged(nameof(SleepMaxTurns));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"SleepMinTurns\"/>.</summary>\n\tpublic const byte DefaultSleepMinTurns = 1;\n\tprivate byte _sleepMinTurns = DefaultSleepMinTurns;\n\t/// <summary>The minimum amount of turns a Pokémon can be <see cref=\"PBEStatus1.Asleep\"/>. <see cref=\"PBEMoveEffect.Rest\"/> will ignore this value and always sleep for <see cref=\"SleepMaxTurns\"/> turns.</summary>\n\tpublic byte SleepMinTurns\n\t{\n\t\tget => _sleepMinTurns;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_sleepMinTurns != value)\n\t\t\t{\n\t\t\t\tif (value > _sleepMaxTurns)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"{nameof(SleepMinTurns)} cannot exceed {nameof(SleepMaxTurns)} ({_sleepMaxTurns}).\");\n\t\t\t\t}\n\t\t\t\t_sleepMinTurns = value;\n\t\t\t\tOnPropertyChanged(nameof(SleepMinTurns));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"BurnDamageDenominator\"/>.</summary>\n\tpublic const byte DefaultBurnDamageDenominator = 8;\n\tprivate byte _burnDamageDenominator = DefaultBurnDamageDenominator;\n\t/// <summary>A Pokémon with <see cref=\"PBEStatus1.Burned\"/> loses (1/this) of its HP at the end of every turn.</summary>\n\tpublic byte BurnDamageDenominator\n\t{\n\t\tget => _burnDamageDenominator;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_burnDamageDenominator != value)\n\t\t\t{\n\t\t\t\tif (value < 1)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"{nameof(BurnDamageDenominator)} must be at least 1.\");\n\t\t\t\t}\n\t\t\t\t_burnDamageDenominator = value;\n\t\t\t\tOnPropertyChanged(nameof(BurnDamageDenominator));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"PoisonDamageDenominator\"/>.</summary>\n\tpublic const byte DefaultPoisonDamageDenominator = 8;\n\tprivate byte _poisonDamageDenominator = DefaultPoisonDamageDenominator;\n\t/// <summary>A Pokémon with <see cref=\"PBEStatus1.Poisoned\"/> loses (1/this) of its HP at the end of every turn.</summary>\n\tpublic byte PoisonDamageDenominator\n\t{\n\t\tget => _poisonDamageDenominator;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_poisonDamageDenominator != value)\n\t\t\t{\n\t\t\t\tif (value < 1)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"{nameof(PoisonDamageDenominator)} must be at least 1.\");\n\t\t\t\t}\n\t\t\t\t_poisonDamageDenominator = value;\n\t\t\t\tOnPropertyChanged(nameof(PoisonDamageDenominator));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"ToxicDamageDenominator\"/>.</summary>\n\tpublic const byte DefaultToxicDamageDenominator = 16;\n\tprivate byte _toxicDamageDenominator = DefaultToxicDamageDenominator;\n\t/// <summary>A Pokémon with <see cref=\"PBEStatus1.BadlyPoisoned\"/> loses (<see cref=\"PBEBattlePokemon.Status1Counter\"/>/this) of its HP at the end of every turn.</summary>\n\tpublic byte ToxicDamageDenominator\n\t{\n\t\tget => _toxicDamageDenominator;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_toxicDamageDenominator != value)\n\t\t\t{\n\t\t\t\tif (value < 1)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"{nameof(ToxicDamageDenominator)} must be at least 1.\");\n\t\t\t\t}\n\t\t\t\t_toxicDamageDenominator = value;\n\t\t\t\tOnPropertyChanged(nameof(ToxicDamageDenominator));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"LeechSeedDenominator\"/>.</summary>\n\tpublic const byte DefaultLeechSeedDenominator = 8;\n\tprivate byte _leechSeedDenominator = DefaultLeechSeedDenominator;\n\t/// <summary>A Pokémon with <see cref=\"PBEStatus2.LeechSeed\"/> loses (1/this) of its HP at the end of every turn and the Pokémon at <see cref=\"PBEBattlePokemon.SeededPosition\"/> on <see cref=\"PBEBattlePokemon.SeededTeam\"/> restores the lost HP.</summary>\n\tpublic byte LeechSeedDenominator\n\t{\n\t\tget => _leechSeedDenominator;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_leechSeedDenominator != value)\n\t\t\t{\n\t\t\t\tif (value < 1)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"{nameof(LeechSeedDenominator)} must be at least 1.\");\n\t\t\t\t}\n\t\t\t\t_leechSeedDenominator = value;\n\t\t\t\tOnPropertyChanged(nameof(LeechSeedDenominator));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"CurseDenominator\"/>.</summary>\n\tpublic const byte DefaultCurseDenominator = 4;\n\tprivate byte _curseDenominator = DefaultCurseDenominator;\n\t/// <summary>A Pokémon with <see cref=\"PBEStatus2.Cursed\"/> loses (1/this) of its HP at the end of every turn.</summary>\n\tpublic byte CurseDenominator\n\t{\n\t\tget => _curseDenominator;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_curseDenominator != value)\n\t\t\t{\n\t\t\t\tif (value < 1)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"{nameof(CurseDenominator)} must be at least 1.\");\n\t\t\t\t}\n\t\t\t\t_curseDenominator = value;\n\t\t\t\tOnPropertyChanged(nameof(CurseDenominator));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"LeftoversHealDenominator\"/>.</summary>\n\tpublic const byte DefaultLeftoversHealDenominator = 16;\n\tprivate byte _leftoversHealDenominator = DefaultLeftoversHealDenominator;\n\t/// <summary>A Pokémon holding a <see cref=\"PBEItem.Leftovers\"/> restores (1/this) of its HP at the end of every turn.</summary>\n\tpublic byte LeftoversHealDenominator\n\t{\n\t\tget => _leftoversHealDenominator;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_leftoversHealDenominator != value)\n\t\t\t{\n\t\t\t\tif (value < 1)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"{nameof(LeftoversHealDenominator)} must be at least 1.\");\n\t\t\t\t}\n\t\t\t\t_leftoversHealDenominator = value;\n\t\t\t\tOnPropertyChanged(nameof(LeftoversHealDenominator));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"BlackSludgeDamageDenominator\"/>.</summary>\n\tpublic const byte DefaultBlackSludgeDamageDenominator = 8;\n\tprivate byte _blackSludgeDamageDenominator = DefaultBlackSludgeDamageDenominator;\n\t/// <summary>A Pokémon holding a <see cref=\"PBEItem.BlackSludge\"/> without <see cref=\"PBEType.Poison\"/> loses (1/this) of its HP at the end of every turn.</summary>\n\tpublic byte BlackSludgeDamageDenominator\n\t{\n\t\tget => _blackSludgeDamageDenominator;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_blackSludgeDamageDenominator != value)\n\t\t\t{\n\t\t\t\tif (value < 1)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"{nameof(BlackSludgeDamageDenominator)} must be at least 1.\");\n\t\t\t\t}\n\t\t\t\t_blackSludgeDamageDenominator = value;\n\t\t\t\tOnPropertyChanged(nameof(BlackSludgeDamageDenominator));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"BlackSludgeHealDenominator\"/>.</summary>\n\tpublic const byte DefaultBlackSludgeHealDenominator = 16;\n\tprivate byte _blackSludgeHealDenominator = DefaultBlackSludgeHealDenominator;\n\t/// <summary>A Pokémon holding a <see cref=\"PBEItem.BlackSludge\"/> with <see cref=\"PBEType.Poison\"/> restores (1/this) of its HP at the end of every turn.</summary>\n\tpublic byte BlackSludgeHealDenominator\n\t{\n\t\tget => _blackSludgeHealDenominator;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_blackSludgeHealDenominator != value)\n\t\t\t{\n\t\t\t\tif (value < 1)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"{nameof(BlackSludgeHealDenominator)} must be at least 1.\");\n\t\t\t\t}\n\t\t\t\t_blackSludgeHealDenominator = value;\n\t\t\t\tOnPropertyChanged(nameof(BlackSludgeHealDenominator));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"ReflectTurns\"/>.</summary>\n\tpublic const byte DefaultReflectTurns = 5;\n\tprivate byte _reflectTurns = DefaultReflectTurns;\n\t/// <summary>The amount of turns <see cref=\"PBEMoveEffect.Reflect\"/> lasts.</summary>\n\tpublic byte ReflectTurns\n\t{\n\t\tget => _reflectTurns;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_reflectTurns != value)\n\t\t\t{\n\t\t\t\tif (value < 1)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"{nameof(ReflectTurns)} must be at least 1.\");\n\t\t\t\t}\n\t\t\t\t_reflectTurns = value;\n\t\t\t\tOnPropertyChanged(nameof(ReflectTurns));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"LightScreenTurns\"/>.</summary>\n\tpublic const byte DefaultLightScreenTurns = 5;\n\tprivate byte _lightScreenTurns = DefaultLightScreenTurns;\n\t/// <summary>The amount of turns <see cref=\"PBEMoveEffect.LightScreen\"/> lasts.</summary>\n\tpublic byte LightScreenTurns\n\t{\n\t\tget => _lightScreenTurns;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_lightScreenTurns != value)\n\t\t\t{\n\t\t\t\tif (value < 1)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"{nameof(LightScreenTurns)} must be at least 1.\");\n\t\t\t\t}\n\t\t\t\t_lightScreenTurns = value;\n\t\t\t\tOnPropertyChanged(nameof(LightScreenTurns));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"LightClayTurnExtension\"/>.</summary>\n\tpublic const byte DefaultLightClayTurnExtension = 3;\n\tprivate byte _lightClayTurnExtension = DefaultLightClayTurnExtension;\n\t/// <summary>The amount of turns added to <see cref=\"ReflectTurns\"/> and <see cref=\"LightScreenTurns\"/> when the user is holding a <see cref=\"PBEItem.LightClay\"/>.</summary>\n\tpublic byte LightClayTurnExtension\n\t{\n\t\tget => _lightClayTurnExtension;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_lightClayTurnExtension != value)\n\t\t\t{\n\t\t\t\t_lightClayTurnExtension = value;\n\t\t\t\tOnPropertyChanged(nameof(LightClayTurnExtension));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"HailTurns\"/>.</summary>\n\tpublic const byte DefaultHailTurns = 5;\n\tprivate byte _hailTurns = DefaultHailTurns;\n\t/// <summary>The amount of turns <see cref=\"PBEWeather.Hailstorm\"/> lasts. For infinite turns, set <see cref=\"IcyRockTurnExtension\"/> to 0 first, then this to 0.</summary>\n\tpublic byte HailTurns\n\t{\n\t\tget => _hailTurns;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_hailTurns != value)\n\t\t\t{\n\t\t\t\tif (value == 0 && _icyRockTurnExtension != 0)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"For infinite turns, set {nameof(IcyRockTurnExtension)} to 0 first, then {nameof(HailTurns)} to 0.\");\n\t\t\t\t}\n\t\t\t\t_hailTurns = value;\n\t\t\t\tOnPropertyChanged(nameof(HailTurns));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"HailDamageDenominator\"/>.</summary>\n\tpublic const byte DefaultHailDamageDenominator = 16;\n\tprivate byte _hailDamageDenominator = DefaultHailDamageDenominator;\n\t/// <summary>A Pokémon in <see cref=\"PBEWeather.Hailstorm\"/> loses (1/this) of its HP at the end of every turn.</summary>\n\tpublic byte HailDamageDenominator\n\t{\n\t\tget => _hailDamageDenominator;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_hailDamageDenominator != value)\n\t\t\t{\n\t\t\t\tif (value < 1)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"{nameof(HailDamageDenominator)} must be at least 1.\");\n\t\t\t\t}\n\t\t\t\t_hailDamageDenominator = value;\n\t\t\t\tOnPropertyChanged(nameof(HailDamageDenominator));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"IcyRockTurnExtension\"/>.</summary>\n\tpublic const byte DefaultIcyRockTurnExtension = 3;\n\tprivate byte _icyRockTurnExtension = DefaultIcyRockTurnExtension;\n\t/// <summary>The amount of turns added to <see cref=\"HailTurns\"/> when the user is holding a <see cref=\"PBEItem.IcyRock\"/>. If <see cref=\"HailTurns\"/> is 0 (infinite turns), this must also be 0.</summary>\n\tpublic byte IcyRockTurnExtension\n\t{\n\t\tget => _icyRockTurnExtension;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_icyRockTurnExtension != value)\n\t\t\t{\n\t\t\t\tif (value != 0 && _hailTurns == 0)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"If {nameof(HailTurns)} is 0 (infinite turns), {nameof(IcyRockTurnExtension)} must also be 0.\");\n\t\t\t\t}\n\t\t\t\t_icyRockTurnExtension = value;\n\t\t\t\tOnPropertyChanged(nameof(IcyRockTurnExtension));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"IceBodyHealDenominator\"/>.</summary>\n\tpublic const byte DefaultIceBodyHealDenominator = 16;\n\tprivate byte _iceBodyHealDenominator = DefaultIceBodyHealDenominator;\n\t/// <summary>A Pokémon with <see cref=\"PBEAbility.IceBody\"/> in <see cref=\"PBEWeather.Hailstorm\"/> restores (1/this) of its HP at the end of every turn.</summary>\n\tpublic byte IceBodyHealDenominator\n\t{\n\t\tget => _iceBodyHealDenominator;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_iceBodyHealDenominator != value)\n\t\t\t{\n\t\t\t\tif (value < 1)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"{nameof(IceBodyHealDenominator)} must be at least 1.\");\n\t\t\t\t}\n\t\t\t\t_iceBodyHealDenominator = value;\n\t\t\t\tOnPropertyChanged(nameof(IceBodyHealDenominator));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"RainTurns\"/>.</summary>\n\tpublic const byte DefaultRainTurns = 5;\n\tprivate byte _rainTurns = DefaultRainTurns;\n\t/// <summary>The amount of turns <see cref=\"PBEWeather.Rain\"/> lasts. For infinite turns, set <see cref=\"DampRockTurnExtension\"/> to 0 first, then this to 0.</summary>\n\tpublic byte RainTurns\n\t{\n\t\tget => _rainTurns;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_rainTurns != value)\n\t\t\t{\n\t\t\t\tif (value == 0 && _dampRockTurnExtension != 0)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"For infinite turns, set {nameof(DampRockTurnExtension)} to 0 first, then {nameof(RainTurns)} to 0.\");\n\t\t\t\t}\n\t\t\t\t_rainTurns = value;\n\t\t\t\tOnPropertyChanged(nameof(RainTurns));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"DampRockTurnExtension\"/>.</summary>\n\tpublic const byte DefaultDampRockTurnExtension = 3;\n\tprivate byte _dampRockTurnExtension = DefaultDampRockTurnExtension;\n\t/// <summary>The amount of turns added to <see cref=\"RainTurns\"/> when the user is holding a <see cref=\"PBEItem.DampRock\"/>. If <see cref=\"RainTurns\"/> is 0 (infinite turns), this must also be 0.</summary>\n\tpublic byte DampRockTurnExtension\n\t{\n\t\tget => _dampRockTurnExtension;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_dampRockTurnExtension != value)\n\t\t\t{\n\t\t\t\tif (value != 0 && _rainTurns == 0)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"If {nameof(RainTurns)} is 0 (infinite turns), {nameof(DampRockTurnExtension)} must also be 0.\");\n\t\t\t\t}\n\t\t\t\t_dampRockTurnExtension = value;\n\t\t\t\tOnPropertyChanged(nameof(DampRockTurnExtension));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"SandstormTurns\"/>.</summary>\n\tpublic const byte DefaultSandstormTurns = 5;\n\tprivate byte _sandstormTurns = DefaultSandstormTurns;\n\t/// <summary>The amount of turns <see cref=\"PBEWeather.Sandstorm\"/> lasts. For infinite turns, set <see cref=\"SmoothRockTurnExtension\"/> to 0 first, then this to 0.</summary>\n\tpublic byte SandstormTurns\n\t{\n\t\tget => _sandstormTurns;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_sandstormTurns != value)\n\t\t\t{\n\t\t\t\tif (value == 0 && _smoothRockTurnExtension != 0)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"For infinite turns, set {nameof(SmoothRockTurnExtension)} to 0 first, then {nameof(SandstormTurns)} to 0.\");\n\t\t\t\t}\n\t\t\t\t_sandstormTurns = value;\n\t\t\t\tOnPropertyChanged(nameof(SandstormTurns));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"SandstormDamageDenominator\"/>.</summary>\n\tpublic const byte DefaultSandstormDamageDenominator = 16;\n\tprivate byte _sandstormDamageDenominator = DefaultSandstormDamageDenominator;\n\t/// <summary>A Pokémon in <see cref=\"PBEWeather.Sandstorm\"/> loses (1/this) of its HP at the end of every turn.</summary>\n\tpublic byte SandstormDamageDenominator\n\t{\n\t\tget => _sandstormDamageDenominator;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_sandstormDamageDenominator != value)\n\t\t\t{\n\t\t\t\tif (value < 1)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"{nameof(SandstormDamageDenominator)} must be at least 1.\");\n\t\t\t\t}\n\t\t\t\t_sandstormDamageDenominator = value;\n\t\t\t\tOnPropertyChanged(nameof(SandstormDamageDenominator));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"SmoothRockTurnExtension\"/>.</summary>\n\tpublic const byte DefaultSmoothRockTurnExtension = 3;\n\tprivate byte _smoothRockTurnExtension = DefaultSmoothRockTurnExtension;\n\t/// <summary>The amount of turns added to <see cref=\"SandstormTurns\"/> when the user is holding a <see cref=\"PBEItem.SmoothRock\"/>. If <see cref=\"SandstormTurns\"/> is 0 (infinite turns), this must also be 0.</summary>\n\tpublic byte SmoothRockTurnExtension\n\t{\n\t\tget => _smoothRockTurnExtension;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_smoothRockTurnExtension != value)\n\t\t\t{\n\t\t\t\tif (value != 0 && _sandstormTurns == 0)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"If {nameof(SandstormTurns)} is 0 (infinite turns), {nameof(SmoothRockTurnExtension)} must also be 0.\");\n\t\t\t\t}\n\t\t\t\t_smoothRockTurnExtension = value;\n\t\t\t\tOnPropertyChanged(nameof(SmoothRockTurnExtension));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"SunTurns\"/>.</summary>\n\tpublic const byte DefaultSunTurns = 5;\n\tprivate byte _sunTurns = DefaultSunTurns;\n\t/// <summary>The amount of turns <see cref=\"PBEWeather.HarshSunlight\"/> lasts. For infinite turns, set <see cref=\"HeatRockTurnExtension\"/> to 0 first, then this to 0.</summary>\n\tpublic byte SunTurns\n\t{\n\t\tget => _sunTurns;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_sunTurns != value)\n\t\t\t{\n\t\t\t\tif (value == 0 && _heatRockTurnExtension != 0)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"For infinite turns, set {nameof(HeatRockTurnExtension)} to 0 first, then {nameof(SunTurns)} to 0.\");\n\t\t\t\t}\n\t\t\t\t_sunTurns = value;\n\t\t\t\tOnPropertyChanged(nameof(SunTurns));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"HeatRockTurnExtension\"/>.</summary>\n\tpublic const byte DefaultHeatRockTurnExtension = 3;\n\tprivate byte _heatRockTurnExtension = DefaultHeatRockTurnExtension;\n\t/// <summary>The amount of turns added to <see cref=\"SunTurns\"/> when the user is holding a <see cref=\"PBEItem.HeatRock\"/>. If <see cref=\"SunTurns\"/> is 0 (infinite turns), this must also be 0.</summary>\n\tpublic byte HeatRockTurnExtension\n\t{\n\t\tget => _heatRockTurnExtension;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_heatRockTurnExtension != value)\n\t\t\t{\n\t\t\t\tif (value != 0 && _sunTurns == 0)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"If {nameof(SunTurns)} is 0 (infinite turns), {nameof(HeatRockTurnExtension)} must also be 0.\");\n\t\t\t\t}\n\t\t\t\t_heatRockTurnExtension = value;\n\t\t\t\tOnPropertyChanged(nameof(HeatRockTurnExtension));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"HiddenPowerMax\"/>.</summary>\n\tpublic const byte DefaultHiddenPowerMax = 70;\n\tprivate byte _hiddenPowerMax = DefaultHiddenPowerMax;\n\t/// <summary>The maximum base power of <see cref=\"PBEMoveEffect.HiddenPower\"/>.</summary>\n\tpublic byte HiddenPowerMax\n\t{\n\t\tget => _hiddenPowerMax;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_hiddenPowerMax != value)\n\t\t\t{\n\t\t\t\tif (value < _hiddenPowerMin)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"{nameof(HiddenPowerMax)} must be at least {nameof(HiddenPowerMin)} ({_hiddenPowerMin}).\");\n\t\t\t\t}\n\t\t\t\t_hiddenPowerMax = value;\n\t\t\t\tOnPropertyChanged(nameof(HiddenPowerMax));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"HiddenPowerMin\"/>.</summary>\n\tpublic const byte DefaultHiddenPowerMin = 30;\n\tprivate byte _hiddenPowerMin = DefaultHiddenPowerMin;\n\t/// <summary>The minimum base power of <see cref=\"PBEMoveEffect.HiddenPower\"/>.</summary>\n\tpublic byte HiddenPowerMin\n\t{\n\t\tget => _hiddenPowerMin;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_hiddenPowerMin != value)\n\t\t\t{\n\t\t\t\tif (value == 0 || value > _hiddenPowerMax)\n\t\t\t\t{\n\t\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"{nameof(HiddenPowerMin)} must be at least 1 and cannot exceed {nameof(HiddenPowerMax)} ({_hiddenPowerMax}).\");\n\t\t\t\t}\n\t\t\t\t_hiddenPowerMin = value;\n\t\t\t\tOnPropertyChanged(nameof(HiddenPowerMin));\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>The default value of <see cref=\"BugFix\"/>.</summary>\n\tpublic const bool DefaultBugFix = false;\n\tprivate bool _bugFix = DefaultBugFix;\n\t/// <summary>Whether bugfixes should be applied or not.</summary>\n\tpublic bool BugFix\n\t{\n\t\tget => _bugFix;\n\t\tset\n\t\t{\n\t\t\tShouldNotBeReadOnly();\n\t\t\tif (_bugFix != value)\n\t\t\t{\n\t\t\t\t_bugFix = value;\n\t\t\t\tOnPropertyChanged(nameof(BugFix));\n\t\t\t}\n\t\t}\n\t}\n\n\t#endregion\n\n\t/// <summary>Creates a new <see cref=\"PBESettings\"/> object where every setting is pre-set to the values used in official games.</summary>\n\tpublic PBESettings() { }\n\t/// <summary>Creates a new <see cref=\"PBESettings\"/> object with the specified code <see cref=\"string\"/>.</summary>\n\t/// <param name=\"code\">The code <see cref=\"string\"/> to use.</param>\n\tpublic PBESettings(string code)\n\t{\n\t\tusing (var ms = new MemoryStream(Convert.FromBase64String(code)))\n\t\t{\n\t\t\tFromBytes(new EndianBinaryReader(ms));\n\t\t}\n\t}\n\t/// <summary>Creates a new <see cref=\"PBESettings\"/> object which copies the settings from the specified <see cref=\"PBESettings\"/> object. <see cref=\"IsReadOnly\"/> and <see cref=\"PropertyChanged\"/> are not copied.</summary>\n\t/// <param name=\"other\">The <see cref=\"PBESettings\"/> object to copy settings from.</param>\n\tpublic PBESettings(PBESettings other)\n\t{\n\t\tother.ShouldBeReadOnly(nameof(other));\n\n\t\tMaxLevel = other._maxLevel;\n\t\tMinLevel = other._minLevel;\n\t\tMaxPartySize = other._maxPartySize;\n\t\tMaxPokemonNameLength = other._maxPokemonNameLength;\n\t\tMaxTrainerNameLength = other._maxTrainerNameLength;\n\t\tMaxTotalEVs = other._maxTotalEVs;\n\t\tMaxIVs = other._maxIVs;\n\t\tNatureStatBoost = other._natureStatBoost;\n\t\tMaxStatChange = other._maxStatChange;\n\t\tNumMoves = other._numMoves;\n\t\tPPMultiplier = other._ppMultiplier;\n\t\tMaxPPUps = other._maxPPUps;\n\t\tCritMultiplier = other._critMultiplier;\n\t\tConfusionMaxTurns = other._confusionMaxTurns;\n\t\tConfusionMinTurns = other._confusionMinTurns;\n\t\tSleepMaxTurns = other._sleepMaxTurns;\n\t\tSleepMinTurns = other._sleepMinTurns;\n\t\tBurnDamageDenominator = other._burnDamageDenominator;\n\t\tPoisonDamageDenominator = other._poisonDamageDenominator;\n\t\tToxicDamageDenominator = other._toxicDamageDenominator;\n\t\tLeechSeedDenominator = other._leechSeedDenominator;\n\t\tCurseDenominator = other._curseDenominator;\n\t\tLeftoversHealDenominator = other._leftoversHealDenominator;\n\t\tBlackSludgeDamageDenominator = other._blackSludgeDamageDenominator;\n\t\tBlackSludgeHealDenominator = other._blackSludgeHealDenominator;\n\t\tReflectTurns = other._reflectTurns;\n\t\tLightScreenTurns = other._lightScreenTurns;\n\t\tLightClayTurnExtension = other._lightClayTurnExtension;\n\t\tHailTurns = other._hailTurns;\n\t\tHailDamageDenominator = other._hailDamageDenominator;\n\t\tIcyRockTurnExtension = other._icyRockTurnExtension;\n\t\tIceBodyHealDenominator = other._iceBodyHealDenominator;\n\t\tRainTurns = other._rainTurns;\n\t\tDampRockTurnExtension = other._dampRockTurnExtension;\n\t\tSandstormTurns = other._sandstormTurns;\n\t\tSandstormDamageDenominator = other._sandstormDamageDenominator;\n\t\tSmoothRockTurnExtension = other._smoothRockTurnExtension;\n\t\tSunTurns = other._sunTurns;\n\t\tHeatRockTurnExtension = other._heatRockTurnExtension;\n\t\tHiddenPowerMax = other._hiddenPowerMax;\n\t\tHiddenPowerMin = other._hiddenPowerMin;\n\t\tBugFix = other._bugFix;\n\t}\n\tpublic PBESettings(EndianBinaryReader r)\n\t{\n\t\tFromBytes(r);\n\t}\n\n\tprivate void ShouldNotBeReadOnly()\n\t{\n\t\tif (_isReadOnly)\n\t\t{\n\t\t\tthrow new InvalidOperationException($\"This {nameof(PBESettings)} is marked as read-only.\");\n\t\t}\n\t}\n\tpublic void ShouldBeReadOnly(string nameOf)\n\t{\n\t\tif (!_isReadOnly)\n\t\t{\n\t\t\tthrow new ArgumentException(\"Settings must be read-only.\", nameOf);\n\t\t}\n\t}\n\t/// <summary>Marks this <see cref=\"PBESettings\"/> object as read-only and clears <see cref=\"PropertyChanged\"/>.</summary>\n\tpublic void MakeReadOnly()\n\t{\n\t\tif (!_isReadOnly)\n\t\t{\n\t\t\tIsReadOnly = true;\n\t\t\tOnPropertyChanged(nameof(IsReadOnly));\n\t\t\tPropertyChanged = null;\n\t\t}\n\t}\n\n\tpublic override int GetHashCode()\n\t{\n\t\tvar hash = new HashCode();\n\t\thash.Add(_maxLevel);\n\t\thash.Add(_minLevel);\n\t\thash.Add(_maxPartySize);\n\t\thash.Add(_maxPokemonNameLength);\n\t\thash.Add(_maxTrainerNameLength);\n\t\thash.Add(_maxTotalEVs);\n\t\thash.Add(_maxIVs);\n\t\thash.Add(_natureStatBoost);\n\t\thash.Add(_maxStatChange);\n\t\thash.Add(_numMoves);\n\t\thash.Add(_ppMultiplier);\n\t\thash.Add(_maxPPUps);\n\t\thash.Add(_critMultiplier);\n\t\thash.Add(_confusionMaxTurns);\n\t\thash.Add(_confusionMinTurns);\n\t\thash.Add(_sleepMaxTurns);\n\t\thash.Add(_sleepMinTurns);\n\t\thash.Add(_burnDamageDenominator);\n\t\thash.Add(_poisonDamageDenominator);\n\t\thash.Add(_toxicDamageDenominator);\n\t\thash.Add(_leechSeedDenominator);\n\t\thash.Add(_curseDenominator);\n\t\thash.Add(_leftoversHealDenominator);\n\t\thash.Add(_blackSludgeDamageDenominator);\n\t\thash.Add(_blackSludgeHealDenominator);\n\t\thash.Add(_reflectTurns);\n\t\thash.Add(_lightScreenTurns);\n\t\thash.Add(_lightClayTurnExtension);\n\t\thash.Add(_hailTurns);\n\t\thash.Add(_hailDamageDenominator);\n\t\thash.Add(_icyRockTurnExtension);\n\t\thash.Add(_iceBodyHealDenominator);\n\t\thash.Add(_rainTurns);\n\t\thash.Add(_dampRockTurnExtension);\n\t\thash.Add(_sandstormTurns);\n\t\thash.Add(_sandstormDamageDenominator);\n\t\thash.Add(_smoothRockTurnExtension);\n\t\thash.Add(_sunTurns);\n\t\thash.Add(_heatRockTurnExtension);\n\t\thash.Add(_hiddenPowerMax);\n\t\thash.Add(_hiddenPowerMin);\n\t\thash.Add(_bugFix);\n\t\treturn hash.ToHashCode();\n\t}\n\n\t/// <summary>Returns a value indicating whether a code <see cref=\"string\"/> or another <see cref=\"PBESettings\"/> object represents the same settings as this <see cref=\"PBESettings\"/> object.</summary>\n\t/// <param name=\"obj\">The code <see cref=\"string\"/> or the <see cref=\"PBESettings\"/> object to check for equality.</param>\n\tpublic override bool Equals(object? obj)\n\t{\n\t\tif (obj is null)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\tif (ReferenceEquals(obj, this))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t\tif (obj is string str)\n\t\t{\n\t\t\tPBESettings ps;\n\t\t\ttry\n\t\t\t{\n\t\t\t\tps = new PBESettings(str);\n\t\t\t}\n\t\t\tcatch\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn ps.Equals(this);\n\t\t}\n\t\tif (obj is PBESettings other)\n\t\t{\n\t\t\treturn other._maxLevel.Equals(_maxLevel)\n\t\t\t\t&& other._minLevel.Equals(_minLevel)\n\t\t\t\t&& other._maxPartySize.Equals(_maxPartySize)\n\t\t\t\t&& other._maxPokemonNameLength.Equals(_maxPokemonNameLength)\n\t\t\t\t&& other._maxTrainerNameLength.Equals(_maxTrainerNameLength)\n\t\t\t\t&& other._maxTotalEVs.Equals(_maxTotalEVs)\n\t\t\t\t&& other._maxIVs.Equals(_maxIVs)\n\t\t\t\t&& other._natureStatBoost.Equals(_natureStatBoost)\n\t\t\t\t&& other._maxStatChange.Equals(_maxStatChange)\n\t\t\t\t&& other._numMoves.Equals(_numMoves)\n\t\t\t\t&& other._ppMultiplier.Equals(_ppMultiplier)\n\t\t\t\t&& other._maxPPUps.Equals(_maxPPUps)\n\t\t\t\t&& other._critMultiplier.Equals(_critMultiplier)\n\t\t\t\t&& other._confusionMaxTurns.Equals(_confusionMaxTurns)\n\t\t\t\t&& other._confusionMinTurns.Equals(_confusionMinTurns)\n\t\t\t\t&& other._sleepMaxTurns.Equals(_sleepMaxTurns)\n\t\t\t\t&& other._sleepMinTurns.Equals(_sleepMinTurns)\n\t\t\t\t&& other._burnDamageDenominator.Equals(_burnDamageDenominator)\n\t\t\t\t&& other._poisonDamageDenominator.Equals(_poisonDamageDenominator)\n\t\t\t\t&& other._toxicDamageDenominator.Equals(_toxicDamageDenominator)\n\t\t\t\t&& other._leechSeedDenominator.Equals(_leechSeedDenominator)\n\t\t\t\t&& other._curseDenominator.Equals(_curseDenominator)\n\t\t\t\t&& other._leftoversHealDenominator.Equals(_leftoversHealDenominator)\n\t\t\t\t&& other._blackSludgeDamageDenominator.Equals(_blackSludgeDamageDenominator)\n\t\t\t\t&& other._blackSludgeHealDenominator.Equals(_blackSludgeHealDenominator)\n\t\t\t\t&& other._reflectTurns.Equals(_reflectTurns)\n\t\t\t\t&& other._lightScreenTurns.Equals(_lightScreenTurns)\n\t\t\t\t&& other._lightClayTurnExtension.Equals(_lightClayTurnExtension)\n\t\t\t\t&& other._hailTurns.Equals(_hailTurns)\n\t\t\t\t&& other._hailDamageDenominator.Equals(_hailDamageDenominator)\n\t\t\t\t&& other._icyRockTurnExtension.Equals(_icyRockTurnExtension)\n\t\t\t\t&& other._iceBodyHealDenominator.Equals(_iceBodyHealDenominator)\n\t\t\t\t&& other._rainTurns.Equals(_rainTurns)\n\t\t\t\t&& other._dampRockTurnExtension.Equals(_dampRockTurnExtension)\n\t\t\t\t&& other._sandstormTurns.Equals(_sandstormTurns)\n\t\t\t\t&& other._sandstormDamageDenominator.Equals(_sandstormDamageDenominator)\n\t\t\t\t&& other._smoothRockTurnExtension.Equals(_smoothRockTurnExtension)\n\t\t\t\t&& other._sunTurns.Equals(_sunTurns)\n\t\t\t\t&& other._heatRockTurnExtension.Equals(_heatRockTurnExtension)\n\t\t\t\t&& other._hiddenPowerMax.Equals(_hiddenPowerMax)\n\t\t\t\t&& other._hiddenPowerMin.Equals(_hiddenPowerMin)\n\t\t\t\t&& other._bugFix.Equals(_bugFix);\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate enum PBESettingID : ushort\n\t{\n\t\tMaxLevel,\n\t\tMinLevel,\n\t\tMaxPartySize,\n\t\tMaxPokemonNameLength,\n\t\tMaxTrainerNameLength,\n\t\tMaxTotalEVs,\n\t\tMaxIVs,\n\t\tNatureStatBoost,\n\t\tMaxStatChange,\n\t\tNumMoves,\n\t\tPPMultiplier,\n\t\tMaxPPUps,\n\t\tCritMultiplier,\n\t\tConfusionMaxTurns,\n\t\tConfusionMinTurns,\n\t\tSleepMaxTurns,\n\t\tSleepMinTurns,\n\t\tBurnDamageDenominator,\n\t\tPoisonDamageDenominator,\n\t\tToxicDamageDenominator,\n\t\tLeechSeedDenominator,\n\t\tCurseDenominator,\n\t\tLeftoversHealDenominator,\n\t\tBlackSludgeDamageDenominator,\n\t\tBlackSludgeHealDenominator,\n\t\tReflectTurns,\n\t\tLightScreenTurns,\n\t\tLightClayTurnExtension,\n\t\tHailTurns,\n\t\tHailDamageDenominator,\n\t\tIcyRockTurnExtension,\n\t\tIceBodyHealDenominator,\n\t\tRainTurns,\n\t\tDampRockTurnExtension,\n\t\tSandstormTurns,\n\t\tSandstormDamageDenominator,\n\t\tSmoothRockTurnExtension,\n\t\tSunTurns,\n\t\tHeatRockTurnExtension,\n\t\tHiddenPowerMax,\n\t\tHiddenPowerMin,\n\t\tBugFix\n\t}\n\n\t/// <summary>Converts this <see cref=\"PBESettings\"/> object into a unique code <see cref=\"string\"/>.</summary>\n\tpublic override string ToString()\n\t{\n\t\treturn Convert.ToBase64String(ToBytes());\n\t}\n\n\tpublic byte[] ToBytes()\n\t{\n\t\tbyte[] data;\n\t\tushort numChanged = 0;\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tvar w = new EndianBinaryWriter(ms);\n\n\t\t\tif (_maxLevel != DefaultMaxLevel)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.MaxLevel);\n\t\t\t\tw.WriteByte(_maxLevel);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_minLevel != DefaultMinLevel)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.MinLevel);\n\t\t\t\tw.WriteByte(_minLevel);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_maxPartySize != DefaultMaxPartySize)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.MaxPartySize);\n\t\t\t\tw.WriteByte(_maxPartySize);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_maxPokemonNameLength != DefaultMaxPokemonNameLength)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.MaxPokemonNameLength);\n\t\t\t\tw.WriteByte(_maxPokemonNameLength);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_maxTrainerNameLength != DefaultMaxTrainerNameLength)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.MaxTrainerNameLength);\n\t\t\t\tw.WriteByte(_maxTrainerNameLength);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_maxTotalEVs != DefaultMaxTotalEVs)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.MaxTotalEVs);\n\t\t\t\tw.WriteUInt16(_maxTotalEVs);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_maxIVs != DefaultMaxIVs)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.MaxIVs);\n\t\t\t\tw.WriteByte(_maxIVs);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_natureStatBoost != DefaultNatureStatBoost)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.NatureStatBoost);\n\t\t\t\tw.WriteSingle(_natureStatBoost);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_maxStatChange != DefaultMaxStatChange)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.MaxStatChange);\n\t\t\t\tw.WriteSByte(_maxStatChange);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_numMoves != DefaultNumMoves)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.NumMoves);\n\t\t\t\tw.WriteByte(_numMoves);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_ppMultiplier != DefaultPPMultiplier)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.PPMultiplier);\n\t\t\t\tw.WriteByte(_ppMultiplier);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_maxPPUps != DefaultMaxPPUps)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.MaxPPUps);\n\t\t\t\tw.WriteByte(_maxPPUps);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_critMultiplier != DefaultCritMultiplier)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.CritMultiplier);\n\t\t\t\tw.WriteSingle(_critMultiplier);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_confusionMaxTurns != DefaultConfusionMaxTurns)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.ConfusionMaxTurns);\n\t\t\t\tw.WriteByte(_confusionMaxTurns);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_confusionMinTurns != DefaultConfusionMinTurns)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.ConfusionMinTurns);\n\t\t\t\tw.WriteByte(_confusionMinTurns);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_sleepMaxTurns != DefaultSleepMaxTurns)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.SleepMaxTurns);\n\t\t\t\tw.WriteByte(_sleepMaxTurns);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_sleepMinTurns != DefaultSleepMinTurns)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.SleepMinTurns);\n\t\t\t\tw.WriteByte(_sleepMinTurns);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_burnDamageDenominator != DefaultBurnDamageDenominator)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.BurnDamageDenominator);\n\t\t\t\tw.WriteByte(_burnDamageDenominator);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_poisonDamageDenominator != DefaultPoisonDamageDenominator)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.PoisonDamageDenominator);\n\t\t\t\tw.WriteByte(_poisonDamageDenominator);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_toxicDamageDenominator != DefaultToxicDamageDenominator)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.ToxicDamageDenominator);\n\t\t\t\tw.WriteByte(_toxicDamageDenominator);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_leechSeedDenominator != DefaultLeechSeedDenominator)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.LeechSeedDenominator);\n\t\t\t\tw.WriteByte(_leechSeedDenominator);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_curseDenominator != DefaultCurseDenominator)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.CurseDenominator);\n\t\t\t\tw.WriteByte(_curseDenominator);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_leftoversHealDenominator != DefaultLeftoversHealDenominator)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.LeftoversHealDenominator);\n\t\t\t\tw.WriteByte(_leftoversHealDenominator);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_blackSludgeDamageDenominator != DefaultBlackSludgeDamageDenominator)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.BlackSludgeDamageDenominator);\n\t\t\t\tw.WriteByte(_blackSludgeDamageDenominator);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_blackSludgeHealDenominator != DefaultBlackSludgeHealDenominator)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.BlackSludgeHealDenominator);\n\t\t\t\tw.WriteByte(_blackSludgeHealDenominator);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_reflectTurns != DefaultReflectTurns)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.ReflectTurns);\n\t\t\t\tw.WriteByte(_reflectTurns);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_lightScreenTurns != DefaultLightScreenTurns)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.LightScreenTurns);\n\t\t\t\tw.WriteByte(_lightScreenTurns);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_lightClayTurnExtension != DefaultLightClayTurnExtension)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.LightClayTurnExtension);\n\t\t\t\tw.WriteByte(_lightClayTurnExtension);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_hailTurns != DefaultHailTurns)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.HailTurns);\n\t\t\t\tw.WriteByte(_hailTurns);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_hailDamageDenominator != DefaultHailDamageDenominator)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.HailDamageDenominator);\n\t\t\t\tw.WriteByte(_hailDamageDenominator);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_icyRockTurnExtension != DefaultIcyRockTurnExtension)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.IcyRockTurnExtension);\n\t\t\t\tw.WriteByte(_icyRockTurnExtension);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_iceBodyHealDenominator != DefaultIceBodyHealDenominator)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.IceBodyHealDenominator);\n\t\t\t\tw.WriteByte(_iceBodyHealDenominator);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_rainTurns != DefaultRainTurns)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.RainTurns);\n\t\t\t\tw.WriteByte(_rainTurns);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_dampRockTurnExtension != DefaultDampRockTurnExtension)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.DampRockTurnExtension);\n\t\t\t\tw.WriteByte(_dampRockTurnExtension);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_sandstormTurns != DefaultSandstormTurns)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.SandstormTurns);\n\t\t\t\tw.WriteByte(_sandstormTurns);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_sandstormDamageDenominator != DefaultSandstormDamageDenominator)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.SandstormDamageDenominator);\n\t\t\t\tw.WriteByte(_sandstormDamageDenominator);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_smoothRockTurnExtension != DefaultSmoothRockTurnExtension)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.SmoothRockTurnExtension);\n\t\t\t\tw.WriteByte(_smoothRockTurnExtension);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_sunTurns != DefaultSunTurns)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.SunTurns);\n\t\t\t\tw.WriteByte(_sunTurns);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_heatRockTurnExtension != DefaultHeatRockTurnExtension)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.HeatRockTurnExtension);\n\t\t\t\tw.WriteByte(_heatRockTurnExtension);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_hiddenPowerMax != DefaultHiddenPowerMax)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.HiddenPowerMax);\n\t\t\t\tw.WriteByte(_hiddenPowerMax);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_hiddenPowerMin != DefaultHiddenPowerMin)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.HiddenPowerMin);\n\t\t\t\tw.WriteByte(_hiddenPowerMin);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tif (_bugFix != DefaultBugFix)\n\t\t\t{\n\t\t\t\tw.WriteEnum(PBESettingID.BugFix);\n\t\t\t\tw.WriteBoolean(_bugFix);\n\t\t\t\tnumChanged++;\n\t\t\t}\n\t\t\tdata = ms.ToArray();\n\t\t}\n\t\tbyte[] ret = new byte[data.Length + 2];\n\t\tEndianBinaryPrimitives.WriteInt16(ret.AsSpan(0, 2), (short)numChanged, Endianness.LittleEndian);\n\t\tArray.Copy(data, 0, ret, 2, data.Length);\n\t\treturn ret;\n\t}\n\tprivate void FromBytes(EndianBinaryReader r)\n\t{\n\t\tushort numChanged = r.ReadUInt16();\n\t\tfor (ushort i = 0; i < numChanged; i++)\n\t\t{\n\t\t\tswitch (r.ReadEnum<PBESettingID>())\n\t\t\t{\n\t\t\t\tcase PBESettingID.MaxLevel: MaxLevel = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.MinLevel: MinLevel = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.MaxPartySize: MaxPartySize = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.MaxPokemonNameLength: MaxPokemonNameLength = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.MaxTrainerNameLength: MaxTrainerNameLength = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.MaxTotalEVs: MaxTotalEVs = r.ReadUInt16(); break;\n\t\t\t\tcase PBESettingID.MaxIVs: MaxIVs = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.NatureStatBoost: NatureStatBoost = r.ReadSingle(); break;\n\t\t\t\tcase PBESettingID.MaxStatChange: MaxStatChange = r.ReadSByte(); break;\n\t\t\t\tcase PBESettingID.NumMoves: NumMoves = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.PPMultiplier: PPMultiplier = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.MaxPPUps: MaxPPUps = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.CritMultiplier: CritMultiplier = r.ReadSingle(); break;\n\t\t\t\tcase PBESettingID.ConfusionMaxTurns: ConfusionMaxTurns = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.ConfusionMinTurns: ConfusionMinTurns = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.SleepMaxTurns: SleepMaxTurns = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.SleepMinTurns: SleepMinTurns = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.BurnDamageDenominator: BurnDamageDenominator = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.PoisonDamageDenominator: PoisonDamageDenominator = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.ToxicDamageDenominator: ToxicDamageDenominator = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.LeechSeedDenominator: LeechSeedDenominator = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.CurseDenominator: CurseDenominator = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.LeftoversHealDenominator: LeftoversHealDenominator = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.BlackSludgeDamageDenominator: BlackSludgeDamageDenominator = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.BlackSludgeHealDenominator: BlackSludgeHealDenominator = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.ReflectTurns: ReflectTurns = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.LightScreenTurns: LightScreenTurns = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.LightClayTurnExtension: LightClayTurnExtension = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.HailTurns: HailTurns = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.HailDamageDenominator: HailDamageDenominator = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.IcyRockTurnExtension: IcyRockTurnExtension = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.IceBodyHealDenominator: IceBodyHealDenominator = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.RainTurns: RainTurns = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.DampRockTurnExtension: DampRockTurnExtension = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.SandstormTurns: SandstormTurns = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.SandstormDamageDenominator: SandstormDamageDenominator = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.SmoothRockTurnExtension: SmoothRockTurnExtension = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.SunTurns: SunTurns = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.HeatRockTurnExtension: HeatRockTurnExtension = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.HiddenPowerMax: HiddenPowerMax = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.HiddenPowerMin: HiddenPowerMin = r.ReadByte(); break;\n\t\t\t\tcase PBESettingID.BugFix: BugFix = r.ReadBoolean(); break;\n\t\t\t\tdefault: throw new InvalidDataException();\n\t\t\t}\n\t\t}\n\t}\n}\n#pragma warning restore CS0618 // Type or member is obsolete\n\n"
  },
  {
    "path": "PokemonBattleEngine/Data/StatCollection.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\n\nnamespace Kermalis.PokemonBattleEngine.Data;\n\npublic sealed class PBEReadOnlyStatCollection : IPBEReadOnlyStatCollection\n{\n\tpublic byte HP { get; }\n\tpublic byte Attack { get; }\n\tpublic byte Defense { get; }\n\tpublic byte SpAttack { get; }\n\tpublic byte SpDefense { get; }\n\tpublic byte Speed { get; }\n\n\tpublic PBEReadOnlyStatCollection(byte hp, byte attack, byte defense, byte spAttack, byte spDefense, byte speed)\n\t{\n\t\tHP = hp;\n\t\tAttack = attack;\n\t\tDefense = defense;\n\t\tSpAttack = spAttack;\n\t\tSpDefense = spDefense;\n\t\tSpeed = speed;\n\t}\n\tinternal PBEReadOnlyStatCollection(EndianBinaryReader r)\n\t\t: this(r.ReadByte(), r.ReadByte(), r.ReadByte(), r.ReadByte(), r.ReadByte(), r.ReadByte()) { }\n\tpublic PBEReadOnlyStatCollection(IPBEReadOnlyStatCollection stats)\n\t\t: this(stats.HP, stats.Attack, stats.Defense, stats.SpAttack, stats.SpDefense, stats.Speed) { }\n\n\tpublic byte this[PBEStat stat] => this.GetStat(stat);\n}\npublic sealed class PBEStatCollection : IPBEStatCollection\n{\n\tpublic byte HP { get; set; }\n\tpublic byte Attack { get; set; }\n\tpublic byte Defense { get; set; }\n\tpublic byte SpAttack { get; set; }\n\tpublic byte SpDefense { get; set; }\n\tpublic byte Speed { get; set; }\n\n\tpublic PBEStatCollection(byte hp, byte attack, byte defense, byte spAttack, byte spDefense, byte speed)\n\t{\n\t\tHP = hp;\n\t\tAttack = attack;\n\t\tDefense = defense;\n\t\tSpAttack = spAttack;\n\t\tSpDefense = spDefense;\n\t\tSpeed = speed;\n\t}\n\tinternal PBEStatCollection(EndianBinaryReader r)\n\t\t: this(r.ReadByte(), r.ReadByte(), r.ReadByte(), r.ReadByte(), r.ReadByte(), r.ReadByte()) { }\n\tpublic PBEStatCollection(IPBEReadOnlyStatCollection stats)\n\t\t: this(stats.HP, stats.Attack, stats.Defense, stats.SpAttack, stats.SpDefense, stats.Speed) { }\n\n\tpublic byte this[PBEStat stat]\n\t{\n\t\tget => this.GetStat(stat);\n\t\tset => this.SetStat(stat, value);\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Data/Utils/DataUtils_Effects.cs",
    "content": "﻿namespace Kermalis.PokemonBattleEngine.Data.Utils;\n\npublic static partial class PBEDataUtils\n{\n\t#region Static Collections\n\n\tpublic static PBEAlphabeticalList<PBEStat> MoodyStats { get; } = new(new[] { PBEStat.Attack, PBEStat.Defense, PBEStat.SpAttack, PBEStat.SpDefense, PBEStat.Speed, PBEStat.Accuracy, PBEStat.Evasion });\n\tpublic static PBEAlphabeticalList<PBEStat> StarfBerryStats { get; } = new(new[] { PBEStat.Attack, PBEStat.Defense, PBEStat.SpAttack, PBEStat.SpDefense, PBEStat.Speed });\n\n\t#endregion\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Data/Utils/DataUtils_Forms.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Utils;\nusing System;\nusing System.Collections.Generic;\n\nnamespace Kermalis.PokemonBattleEngine.Data.Utils;\n\npublic static partial class PBEDataUtils\n{\n\t#region Static Collections\n\n\tpublic static PBEAlphabeticalList<PBESpecies> AllSpecies { get; } = new(Enum.GetValues<PBESpecies>().ExceptOne(PBESpecies.MAX));\n\tpublic static PBEAlphabeticalList<PBESpecies> FullyEvolvedSpecies { get; } = new(AllSpecies.FindAll(s => !PBEDataProvider.Instance.HasEvolutions(s, 0)));\n\n\t#region Forms\n\n\tprivate static readonly PBEAlphabeticalList<PBEForm> _arceus = new(new[] { PBEForm.Arceus, PBEForm.Arceus_Bug, PBEForm.Arceus_Dark,\n\t\t\tPBEForm.Arceus_Dragon, PBEForm.Arceus_Electric, PBEForm.Arceus_Fighting, PBEForm.Arceus_Fire, PBEForm.Arceus_Flying, PBEForm.Arceus_Ghost, PBEForm.Arceus_Grass,\n\t\t\tPBEForm.Arceus_Ground, PBEForm.Arceus_Ice, PBEForm.Arceus_Poison, PBEForm.Arceus_Psychic, PBEForm.Arceus_Rock, PBEForm.Arceus_Steel, PBEForm.Arceus_Water }, PBESpecies.Arceus);\n\tprivate static readonly PBEAlphabeticalList<PBEForm> _basculin = new(new[] { PBEForm.Basculin_Blue, PBEForm.Basculin_Red }, PBESpecies.Basculin);\n\tprivate static readonly PBEAlphabeticalList<PBEForm> _burmy = new(new[] { PBEForm.Burmy_Plant, PBEForm.Burmy_Sandy, PBEForm.Burmy_Trash }, PBESpecies.Burmy);\n\tprivate static readonly PBEAlphabeticalList<PBEForm> _castform = new(new[] { PBEForm.Castform, PBEForm.Castform_Rainy, PBEForm.Castform_Snowy,\n\t\t\tPBEForm.Castform_Sunny }, PBESpecies.Castform);\n\tprivate static readonly PBEAlphabeticalList<PBEForm> _cherrim = new(new[] { PBEForm.Cherrim, PBEForm.Cherrim_Sunshine }, PBESpecies.Cherrim);\n\tprivate static readonly PBEAlphabeticalList<PBEForm> _darmanitan = new(new[] { PBEForm.Darmanitan, PBEForm.Darmanitan_Zen }, PBESpecies.Darmanitan);\n\tprivate static readonly PBEAlphabeticalList<PBEForm> _deerling = new(new[] { PBEForm.Deerling_Autumn, PBEForm.Deerling_Spring, PBEForm.Deerling_Summer,\n\t\t\tPBEForm.Deerling_Winter }, PBESpecies.Deerling);\n\tprivate static readonly PBEAlphabeticalList<PBEForm> _deoxys = new(new[] { PBEForm.Deoxys, PBEForm.Deoxys_Attack, PBEForm.Deoxys_Defense,\n\t\t\tPBEForm.Deoxys_Speed }, PBESpecies.Deoxys);\n\tprivate static readonly PBEAlphabeticalList<PBEForm> _gastrodon = new(new[] { PBEForm.Gastrodon_East, PBEForm.Gastrodon_West }, PBESpecies.Gastrodon);\n\tprivate static readonly PBEAlphabeticalList<PBEForm> _genesect = new(new[] { PBEForm.Genesect, PBEForm.Genesect_Burn, PBEForm.Genesect_Chill,\n\t\t\tPBEForm.Genesect_Douse, PBEForm.Genesect_Shock }, PBESpecies.Genesect);\n\tprivate static readonly PBEAlphabeticalList<PBEForm> _giratina = new(new[] { PBEForm.Giratina, PBEForm.Giratina_Origin }, PBESpecies.Giratina);\n\tprivate static readonly PBEAlphabeticalList<PBEForm> _keldeo = new(new[] { PBEForm.Keldeo, PBEForm.Keldeo_Resolute }, PBESpecies.Keldeo);\n\tprivate static readonly PBEAlphabeticalList<PBEForm> _kyurem = new(new[] { PBEForm.Kyurem, PBEForm.Kyurem_Black, PBEForm.Kyurem_White }, PBESpecies.Kyurem);\n\tprivate static readonly PBEAlphabeticalList<PBEForm> _landorus = new(new[] { PBEForm.Landorus, PBEForm.Landorus_Therian }, PBESpecies.Landorus);\n\tprivate static readonly PBEAlphabeticalList<PBEForm> _meloetta = new(new[] { PBEForm.Meloetta, PBEForm.Meloetta_Pirouette }, PBESpecies.Meloetta);\n\tprivate static readonly PBEAlphabeticalList<PBEForm> _rotom = new(new[] { PBEForm.Rotom, PBEForm.Rotom_Fan, PBEForm.Rotom_Frost, PBEForm.Rotom_Heat,\n\t\t\tPBEForm.Rotom_Mow, PBEForm.Rotom_Wash }, PBESpecies.Rotom);\n\tprivate static readonly PBEAlphabeticalList<PBEForm> _sawsbuck = new(new[] { PBEForm.Sawsbuck_Autumn, PBEForm.Sawsbuck_Spring, PBEForm.Sawsbuck_Summer,\n\t\t\tPBEForm.Sawsbuck_Winter }, PBESpecies.Sawsbuck);\n\tprivate static readonly PBEAlphabeticalList<PBEForm> _shaymin = new(new[] { PBEForm.Shaymin, PBEForm.Shaymin_Sky }, PBESpecies.Shaymin);\n\tprivate static readonly PBEAlphabeticalList<PBEForm> _shellos = new(new[] { PBEForm.Shellos_East, PBEForm.Shellos_West }, PBESpecies.Shellos);\n\tprivate static readonly PBEAlphabeticalList<PBEForm> _thundurus = new(new[] { PBEForm.Thundurus, PBEForm.Thundurus_Therian }, PBESpecies.Thundurus);\n\tprivate static readonly PBEAlphabeticalList<PBEForm> _tornadus = new(new[] { PBEForm.Tornadus, PBEForm.Tornadus_Therian }, PBESpecies.Tornadus);\n\tprivate static readonly PBEAlphabeticalList<PBEForm> _unown = new(new[] { PBEForm.Unown_A, PBEForm.Unown_B, PBEForm.Unown_C, PBEForm.Unown_D,\n\t\t\tPBEForm.Unown_E, PBEForm.Unown_Exclamation, PBEForm.Unown_F, PBEForm.Unown_G, PBEForm.Unown_H, PBEForm.Unown_I, PBEForm.Unown_J, PBEForm.Unown_K, PBEForm.Unown_L, PBEForm.Unown_M,\n\t\t\tPBEForm.Unown_N, PBEForm.Unown_O, PBEForm.Unown_P, PBEForm.Unown_Q, PBEForm.Unown_Question, PBEForm.Unown_R, PBEForm.Unown_S, PBEForm.Unown_T, PBEForm.Unown_U, PBEForm.Unown_V,\n\t\t\tPBEForm.Unown_W, PBEForm.Unown_X, PBEForm.Unown_Y, PBEForm.Unown_Z }, PBESpecies.Unown);\n\tprivate static readonly PBEAlphabeticalList<PBEForm> _wormadam = new(new[] { PBEForm.Wormadam_Plant, PBEForm.Wormadam_Sandy, PBEForm.Wormadam_Trash },\n\t\t\tPBESpecies.Wormadam);\n\n\t#endregion\n\n\t#endregion\n\n\tpublic static bool CanChangeForm(PBESpecies species, bool requireUsableOutsideOfBattle)\n\t{\n\t\tif (species <= 0 || species >= PBESpecies.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(species));\n\t\t}\n\t\tswitch (species)\n\t\t{\n\t\t\tcase PBESpecies.Arceus:\n\t\t\tcase PBESpecies.Burmy:\n\t\t\tcase PBESpecies.Deerling:\n\t\t\tcase PBESpecies.Deoxys:\n\t\t\tcase PBESpecies.Genesect:\n\t\t\tcase PBESpecies.Giratina:\n\t\t\tcase PBESpecies.Keldeo:\n\t\t\tcase PBESpecies.Kyurem:\n\t\t\tcase PBESpecies.Landorus:\n\t\t\tcase PBESpecies.Rotom:\n\t\t\tcase PBESpecies.Sawsbuck:\n\t\t\tcase PBESpecies.Shaymin:\n\t\t\tcase PBESpecies.Thundurus:\n\t\t\tcase PBESpecies.Tornadus: return true;\n\t\t\tcase PBESpecies.Castform:\n\t\t\tcase PBESpecies.Cherrim:\n\t\t\tcase PBESpecies.Darmanitan:\n\t\t\tcase PBESpecies.Meloetta: return !requireUsableOutsideOfBattle;\n\t\t\tdefault: return false;\n\t\t}\n\t}\n\tpublic static bool HasForms(PBESpecies species, bool requireUsableOutsideOfBattle)\n\t{\n\t\tif (species <= 0 || species >= PBESpecies.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(species));\n\t\t}\n\t\tswitch (species)\n\t\t{\n\t\t\tcase PBESpecies.Arceus:\n\t\t\tcase PBESpecies.Basculin:\n\t\t\tcase PBESpecies.Burmy:\n\t\t\tcase PBESpecies.Deerling:\n\t\t\tcase PBESpecies.Deoxys:\n\t\t\tcase PBESpecies.Gastrodon:\n\t\t\tcase PBESpecies.Genesect:\n\t\t\tcase PBESpecies.Giratina:\n\t\t\tcase PBESpecies.Keldeo:\n\t\t\tcase PBESpecies.Kyurem:\n\t\t\tcase PBESpecies.Landorus:\n\t\t\tcase PBESpecies.Rotom:\n\t\t\tcase PBESpecies.Sawsbuck:\n\t\t\tcase PBESpecies.Shaymin:\n\t\t\tcase PBESpecies.Shellos:\n\t\t\tcase PBESpecies.Thundurus:\n\t\t\tcase PBESpecies.Tornadus:\n\t\t\tcase PBESpecies.Unown:\n\t\t\tcase PBESpecies.Wormadam: return true;\n\t\t\tcase PBESpecies.Castform:\n\t\t\tcase PBESpecies.Cherrim:\n\t\t\tcase PBESpecies.Darmanitan:\n\t\t\tcase PBESpecies.Meloetta: return !requireUsableOutsideOfBattle;\n\t\t\tdefault: return false;\n\t\t}\n\t}\n\n\tpublic static string? GetNameOfForm(PBESpecies species, PBEForm form)\n\t{\n\t\tValidateSpecies(species, form, false);\n\t\tstring[] names = Enum.GetNames<PBEForm>();\n\t\tPBEForm[] forms = Enum.GetValues<PBEForm>();\n\t\tDictionary<PBEForm, string> combo = new();\n\t\tfor (int i = 0; i < names.Length; i++)\n\t\t{\n\t\t\tPBEForm f = forms[i];\n\t\t\tstring name = names[i];\n\t\t\tif (name.StartsWith(species.ToString()))\n\t\t\t{\n\t\t\t\tcombo.Add(f, name);\n\t\t\t}\n\t\t}\n\t\tif (combo.Count == 0)\n\t\t{\n\t\t\treturn null;\n\t\t}\n\t\treturn combo[form];\n\t}\n\n\tpublic static IReadOnlyList<PBEForm> GetForms(PBESpecies species, bool requireUsableOutsideOfBattle)\n\t{\n\t\tif (species <= 0 || species >= PBESpecies.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(species));\n\t\t}\n\t\tswitch (species)\n\t\t{\n\t\t\tcase PBESpecies.Arceus: return _arceus;\n\t\t\tcase PBESpecies.Basculin: return _basculin;\n\t\t\tcase PBESpecies.Burmy: return _burmy;\n\t\t\tcase PBESpecies.Castform: return requireUsableOutsideOfBattle ? Array.Empty<PBEForm>() : _castform;\n\t\t\tcase PBESpecies.Cherrim: return requireUsableOutsideOfBattle ? Array.Empty<PBEForm>() : _cherrim;\n\t\t\tcase PBESpecies.Darmanitan: return requireUsableOutsideOfBattle ? Array.Empty<PBEForm>() : _darmanitan;\n\t\t\tcase PBESpecies.Deerling: return _deerling;\n\t\t\tcase PBESpecies.Deoxys: return _deoxys;\n\t\t\tcase PBESpecies.Gastrodon: return _gastrodon;\n\t\t\tcase PBESpecies.Genesect: return _genesect;\n\t\t\tcase PBESpecies.Giratina: return _giratina;\n\t\t\tcase PBESpecies.Keldeo: return _keldeo;\n\t\t\tcase PBESpecies.Kyurem: return _kyurem;\n\t\t\tcase PBESpecies.Landorus: return _landorus;\n\t\t\tcase PBESpecies.Meloetta: return requireUsableOutsideOfBattle ? Array.Empty<PBEForm>() : _meloetta;\n\t\t\tcase PBESpecies.Rotom: return _rotom;\n\t\t\tcase PBESpecies.Sawsbuck: return _sawsbuck;\n\t\t\tcase PBESpecies.Shaymin: return _shaymin;\n\t\t\tcase PBESpecies.Shellos: return _shellos;\n\t\t\tcase PBESpecies.Thundurus: return _thundurus;\n\t\t\tcase PBESpecies.Tornadus: return _tornadus;\n\t\t\tcase PBESpecies.Unown: return _unown;\n\t\t\tcase PBESpecies.Wormadam: return _wormadam;\n\t\t\tdefault: return Array.Empty<PBEForm>();\n\t\t}\n\t}\n\n\tpublic static IReadOnlyList<PBEItem> GetValidItems(PBESpecies species, PBEForm form)\n\t{\n\t\tValidateSpecies(species, form, false);\n\t\tswitch (species)\n\t\t{\n\t\t\tcase PBESpecies.Arceus:\n\t\t\t{\n\t\t\t\tswitch (form)\n\t\t\t\t{\n\t\t\t\t\tcase PBEForm.Arceus: return _arceusItems;\n\t\t\t\t\tcase PBEForm.Arceus_Bug: return _arceusBugItems;\n\t\t\t\t\tcase PBEForm.Arceus_Dark: return _arceusDarkItems;\n\t\t\t\t\tcase PBEForm.Arceus_Dragon: return _arceusDragonItems;\n\t\t\t\t\tcase PBEForm.Arceus_Electric: return _arceusElectricItems;\n\t\t\t\t\tcase PBEForm.Arceus_Fighting: return _arceusFightingItems;\n\t\t\t\t\tcase PBEForm.Arceus_Fire: return _arceusFireItems;\n\t\t\t\t\tcase PBEForm.Arceus_Flying: return _arceusFlyingItems;\n\t\t\t\t\tcase PBEForm.Arceus_Ghost: return _arceusGhostItems;\n\t\t\t\t\tcase PBEForm.Arceus_Grass: return _arceusGrassItems;\n\t\t\t\t\tcase PBEForm.Arceus_Ground: return _arceusGroundItems;\n\t\t\t\t\tcase PBEForm.Arceus_Ice: return _arceusIceItems;\n\t\t\t\t\tcase PBEForm.Arceus_Poison: return _arceusPoisonItems;\n\t\t\t\t\tcase PBEForm.Arceus_Psychic: return _arceusPsychicItems;\n\t\t\t\t\tcase PBEForm.Arceus_Rock: return _arceusRockItems;\n\t\t\t\t\tcase PBEForm.Arceus_Steel: return _arceusSteelItems;\n\t\t\t\t\tcase PBEForm.Arceus_Water: return _arceusWaterItems;\n\t\t\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(form));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcase PBESpecies.Genesect:\n\t\t\t{\n\t\t\t\tswitch (form)\n\t\t\t\t{\n\t\t\t\t\tcase PBEForm.Genesect: return _genesectItems;\n\t\t\t\t\tcase PBEForm.Genesect_Burn: return _genesectBurnItems;\n\t\t\t\t\tcase PBEForm.Genesect_Chill: return _genesectChillItems;\n\t\t\t\t\tcase PBEForm.Genesect_Douse: return _genesectDouseItems;\n\t\t\t\t\tcase PBEForm.Genesect_Shock: return _genesectShockItems;\n\t\t\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(form));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcase PBESpecies.Giratina:\n\t\t\t{\n\t\t\t\tswitch (form)\n\t\t\t\t{\n\t\t\t\t\tcase PBEForm.Giratina: return _giratinaItems;\n\t\t\t\t\tcase PBEForm.Giratina_Origin: return _giratinaOriginItems;\n\t\t\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(form));\n\t\t\t\t}\n\t\t\t}\n\t\t\tdefault: return AllItems;\n\t\t}\n\t}\n\n\tpublic static bool IsValidForm(PBESpecies species, PBEForm form, bool requireUsableOutsideOfBattle)\n\t{\n\t\tif (species <= 0 || species >= PBESpecies.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(species));\n\t\t}\n\t\tswitch (species)\n\t\t{\n\t\t\tcase PBESpecies.Arceus: return form <= PBEForm.Arceus_Dark;\n\t\t\tcase PBESpecies.Basculin: return form <= PBEForm.Basculin_Blue;\n\t\t\tcase PBESpecies.Burmy: return form <= PBEForm.Burmy_Trash;\n\t\t\tcase PBESpecies.Castform: return form <= (requireUsableOutsideOfBattle ? 0 : PBEForm.Castform_Snowy);\n\t\t\tcase PBESpecies.Cherrim: return form <= (requireUsableOutsideOfBattle ? 0 : PBEForm.Cherrim_Sunshine);\n\t\t\tcase PBESpecies.Darmanitan: return form <= (requireUsableOutsideOfBattle ? 0 : PBEForm.Darmanitan_Zen);\n\t\t\tcase PBESpecies.Deerling: return form <= PBEForm.Deerling_Winter;\n\t\t\tcase PBESpecies.Deoxys: return form <= PBEForm.Deoxys_Speed;\n\t\t\tcase PBESpecies.Gastrodon: return form <= PBEForm.Gastrodon_East;\n\t\t\tcase PBESpecies.Genesect: return form <= PBEForm.Genesect_Chill;\n\t\t\tcase PBESpecies.Giratina: return form <= PBEForm.Giratina_Origin;\n\t\t\tcase PBESpecies.Keldeo: return form <= PBEForm.Keldeo_Resolute;\n\t\t\tcase PBESpecies.Kyurem: return form <= PBEForm.Kyurem_Black;\n\t\t\tcase PBESpecies.Landorus: return form <= PBEForm.Landorus_Therian;\n\t\t\tcase PBESpecies.Meloetta: return form <= (requireUsableOutsideOfBattle ? 0 : PBEForm.Meloetta_Pirouette);\n\t\t\tcase PBESpecies.Rotom: return form <= PBEForm.Rotom_Mow;\n\t\t\tcase PBESpecies.Sawsbuck: return form <= PBEForm.Sawsbuck_Winter;\n\t\t\tcase PBESpecies.Shaymin: return form <= PBEForm.Shaymin_Sky;\n\t\t\tcase PBESpecies.Shellos: return form <= PBEForm.Shellos_East;\n\t\t\tcase PBESpecies.Thundurus: return form <= PBEForm.Thundurus_Therian;\n\t\t\tcase PBESpecies.Tornadus: return form <= PBEForm.Tornadus_Therian;\n\t\t\tcase PBESpecies.Unown: return form <= PBEForm.Unown_Question;\n\t\t\tcase PBESpecies.Wormadam: return form <= PBEForm.Wormadam_Trash;\n\t\t\tdefault: return form <= 0;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Data/Utils/DataUtils_Items.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Utils;\nusing System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Linq;\n\nnamespace Kermalis.PokemonBattleEngine.Data.Utils;\n\npublic static partial class PBEDataUtils\n{\n\t#region Static Collections\n\n\tpublic static PBEAlphabeticalList<PBEItem> AllItems { get; } = new(Enum.GetValues<PBEItem>());\n\tpublic static PBEAlphabeticalList<PBEItem> AllBalls { get; } = new(new[] { PBEItem.MasterBall, PBEItem.UltraBall, PBEItem.GreatBall, PBEItem.PokeBall,\n\t\t\tPBEItem.SafariBall, PBEItem.NetBall, PBEItem.DiveBall, PBEItem.NestBall, PBEItem.RepeatBall, PBEItem.TimerBall, PBEItem.LuxuryBall, PBEItem.PremierBall, PBEItem.DuskBall, PBEItem.HealBall,\n\t\t\tPBEItem.QuickBall, PBEItem.CherishBall, PBEItem.FastBall, PBEItem.LevelBall, PBEItem.LureBall, PBEItem.HeavyBall, PBEItem.LoveBall, PBEItem.FriendBall, PBEItem.MoonBall, PBEItem.SportBall,\n\t\t\tPBEItem.ParkBall, PBEItem.DreamBall });\n\tpublic static ReadOnlyDictionary<PBEType, PBEItem> TypeToGem { get; } = new(new Dictionary<PBEType, PBEItem>()\n\t\t{\n\t\t\t{ PBEType.Bug, PBEItem.BugGem },\n\t\t\t{ PBEType.Dark, PBEItem.DarkGem },\n\t\t\t{ PBEType.Dragon, PBEItem.DragonGem },\n\t\t\t{ PBEType.Electric, PBEItem.ElectricGem },\n\t\t\t{ PBEType.Fighting, PBEItem.FightingGem },\n\t\t\t{ PBEType.Fire, PBEItem.FireGem },\n\t\t\t{ PBEType.Flying, PBEItem.FlyingGem },\n\t\t\t{ PBEType.Ghost, PBEItem.GhostGem },\n\t\t\t{ PBEType.Grass, PBEItem.GrassGem },\n\t\t\t{ PBEType.Ground, PBEItem.GroundGem },\n\t\t\t{ PBEType.Ice, PBEItem.IceGem },\n\t\t\t{ PBEType.Normal, PBEItem.NormalGem },\n\t\t\t{ PBEType.Poison, PBEItem.PoisonGem },\n\t\t\t{ PBEType.Psychic, PBEItem.PsychicGem },\n\t\t\t{ PBEType.Rock, PBEItem.RockGem },\n\t\t\t{ PBEType.Steel, PBEItem.SteelGem },\n\t\t\t{ PBEType.Water, PBEItem.WaterGem }\n\t\t});\n\n\t#region Form Items\n\n\tprivate static readonly PBEAlphabeticalList<PBEItem> _arceusItems = new(AllItems.Except(new[] { PBEItem.DracoPlate, PBEItem.DreadPlate, PBEItem.EarthPlate,\n\t\t\tPBEItem.FistPlate, PBEItem.FlamePlate, PBEItem.IciclePlate, PBEItem.InsectPlate, PBEItem.IronPlate, PBEItem.MeadowPlate, PBEItem.MindPlate, PBEItem.SkyPlate, PBEItem.SplashPlate,\n\t\t\tPBEItem.SpookyPlate, PBEItem.StonePlate, PBEItem.ToxicPlate, PBEItem.ZapPlate }));\n\tprivate static readonly PBEAlphabeticalList<PBEItem> _arceusBugItems = new(new[] { PBEItem.InsectPlate });\n\tprivate static readonly PBEAlphabeticalList<PBEItem> _arceusDarkItems = new(new[] { PBEItem.DreadPlate });\n\tprivate static readonly PBEAlphabeticalList<PBEItem> _arceusDragonItems = new(new[] { PBEItem.DracoPlate });\n\tprivate static readonly PBEAlphabeticalList<PBEItem> _arceusElectricItems = new(new[] { PBEItem.ZapPlate });\n\tprivate static readonly PBEAlphabeticalList<PBEItem> _arceusFightingItems = new(new[] { PBEItem.FistPlate });\n\tprivate static readonly PBEAlphabeticalList<PBEItem> _arceusFireItems = new(new[] { PBEItem.FlamePlate });\n\tprivate static readonly PBEAlphabeticalList<PBEItem> _arceusFlyingItems = new(new[] { PBEItem.SkyPlate });\n\tprivate static readonly PBEAlphabeticalList<PBEItem> _arceusGhostItems = new(new[] { PBEItem.SpookyPlate });\n\tprivate static readonly PBEAlphabeticalList<PBEItem> _arceusGrassItems = new(new[] { PBEItem.MeadowPlate });\n\tprivate static readonly PBEAlphabeticalList<PBEItem> _arceusGroundItems = new(new[] { PBEItem.EarthPlate });\n\tprivate static readonly PBEAlphabeticalList<PBEItem> _arceusIceItems = new(new[] { PBEItem.IciclePlate });\n\tprivate static readonly PBEAlphabeticalList<PBEItem> _arceusPoisonItems = new(new[] { PBEItem.ToxicPlate });\n\tprivate static readonly PBEAlphabeticalList<PBEItem> _arceusPsychicItems = new(new[] { PBEItem.MindPlate });\n\tprivate static readonly PBEAlphabeticalList<PBEItem> _arceusRockItems = new(new[] { PBEItem.StonePlate });\n\tprivate static readonly PBEAlphabeticalList<PBEItem> _arceusSteelItems = new(new[] { PBEItem.IronPlate });\n\tprivate static readonly PBEAlphabeticalList<PBEItem> _arceusWaterItems = new(new[] { PBEItem.SplashPlate });\n\tprivate static readonly PBEAlphabeticalList<PBEItem> _genesectItems = new(AllItems.Except(new[] { PBEItem.BurnDrive, PBEItem.ChillDrive, PBEItem.DouseDrive,\n\t\t\tPBEItem.ShockDrive }));\n\tprivate static readonly PBEAlphabeticalList<PBEItem> _genesectBurnItems = new(new[] { PBEItem.BurnDrive });\n\tprivate static readonly PBEAlphabeticalList<PBEItem> _genesectChillItems = new(new[] { PBEItem.ChillDrive });\n\tprivate static readonly PBEAlphabeticalList<PBEItem> _genesectDouseItems = new(new[] { PBEItem.DouseDrive });\n\tprivate static readonly PBEAlphabeticalList<PBEItem> _genesectShockItems = new(new[] { PBEItem.ShockDrive });\n\tprivate static readonly PBEAlphabeticalList<PBEItem> _giratinaItems = new(AllItems.ExceptOne(PBEItem.GriseousOrb));\n\tprivate static readonly PBEAlphabeticalList<PBEItem> _giratinaOriginItems = new(new[] { PBEItem.GriseousOrb });\n\n\t#endregion\n\n\t#endregion\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Data/Utils/DataUtils_Moves.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace Kermalis.PokemonBattleEngine.Data.Utils;\n\npublic static partial class PBEDataUtils\n{\n\t#region Static Collections\n\n\tpublic static PBEAlphabeticalList<PBEMove> AllMoves { get; } = new(Enum.GetValues<PBEMove>().Except(new[] { PBEMove.None, PBEMove.MAX }));\n\tpublic static PBEAlphabeticalList<PBEMove> MetronomeMoves { get; } = new(GetMovesWithoutFlag(PBEMoveFlag.BlockedFromMetronome));\n\tpublic static PBEAlphabeticalList<PBEMove> SketchLegalMoves { get; } = new(GetMovesWithoutFlag(PBEMoveFlag.BlockedFromSketch, exception: PBEMoveEffect.Sketch));\n\n\t#endregion\n\n\tprivate static List<PBEMove> GetMovesWithoutFlag(PBEMoveFlag flag, PBEMoveEffect? exception = null)\n\t{\n\t\treturn AllMoves.FindAll(m =>\n\t\t{\n\t\t\tIPBEMoveData mData = PBEDataProvider.Instance.GetMoveData(m, cache: false);\n\t\t\tif (!mData.IsMoveUsable())\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (exception is not null && mData.Effect == exception.Value)\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn !mData.Flags.HasFlag(flag);\n\t\t});\n\t}\n\n\tpublic static bool HasSecondaryEffects(PBEMoveEffect effect, PBESettings settings)\n\t{\n\t\tsettings.ShouldBeReadOnly(nameof(settings));\n\t\tswitch (effect)\n\t\t{\n\t\t\tcase PBEMoveEffect.Hit__MaybeBurn:\n\t\t\tcase PBEMoveEffect.Hit__MaybeBurn__10PercentFlinch:\n\t\t\tcase PBEMoveEffect.Hit__MaybeConfuse:\n\t\t\tcase PBEMoveEffect.Hit__MaybeFlinch:\n\t\t\tcase PBEMoveEffect.Hit__MaybeFreeze:\n\t\t\tcase PBEMoveEffect.Hit__MaybeFreeze__10PercentFlinch:\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerTarget_ACC_By1:\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerTarget_ATK_By1:\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerTarget_DEF_By1:\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerTarget_SPATK_By1:\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerTarget_SPDEF_By1:\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerTarget_SPDEF_By2:\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerTarget_SPE_By1:\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerUser_ATK_DEF_By1:\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerUser_DEF_SPDEF_By1:\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerUser_SPATK_By2:\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerUser_SPE_By1:\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerUser_SPE_DEF_SPDEF_By1:\n\t\t\tcase PBEMoveEffect.Hit__MaybeParalyze:\n\t\t\tcase PBEMoveEffect.Hit__MaybeParalyze__10PercentFlinch:\n\t\t\tcase PBEMoveEffect.Hit__MaybePoison:\n\t\t\tcase PBEMoveEffect.Hit__MaybeRaiseUser_ATK_By1:\n\t\t\tcase PBEMoveEffect.Hit__MaybeRaiseUser_ATK_DEF_SPATK_SPDEF_SPE_By1:\n\t\t\tcase PBEMoveEffect.Hit__MaybeRaiseUser_DEF_By1:\n\t\t\tcase PBEMoveEffect.Hit__MaybeRaiseUser_SPATK_By1:\n\t\t\tcase PBEMoveEffect.Hit__MaybeRaiseUser_SPE_By1:\n\t\t\tcase PBEMoveEffect.Hit__MaybeToxic:\n\t\t\tcase PBEMoveEffect.Snore: return true;\n\t\t\t// BUG: SecretPower is unaffected by SereneGrace and the Rainbow\n\t\t\tcase PBEMoveEffect.SecretPower: return settings.BugFix;\n\t\t\tdefault: return false;\n\t\t}\n\t}\n\tpublic static bool IsHPDrainMove(PBEMoveEffect effect)\n\t{\n\t\tswitch (effect)\n\t\t{\n\t\t\tcase PBEMoveEffect.HPDrain:\n\t\t\tcase PBEMoveEffect.HPDrain__RequireSleep: return true;\n\t\t\tdefault: return false;\n\t\t}\n\t}\n\tpublic static bool IsHPRestoreMove(PBEMoveEffect effect)\n\t{\n\t\tswitch (effect)\n\t\t{\n\t\t\tcase PBEMoveEffect.Rest:\n\t\t\tcase PBEMoveEffect.RestoreTargetHP: return true;\n\t\t\tdefault: return false;\n\t\t}\n\t}\n\tpublic static bool IsMultiHitMove(PBEMoveEffect effect) // TODO: TripleKick\n\t{\n\t\tswitch (effect)\n\t\t{\n\t\t\tcase PBEMoveEffect.Hit__2Times:\n\t\t\tcase PBEMoveEffect.Hit__2Times__MaybePoison:\n\t\t\tcase PBEMoveEffect.Hit__2To5Times: return true;\n\t\t\tdefault: return false;\n\t\t}\n\t}\n\tpublic static bool IsRecoilMove(PBEMoveEffect effect) // TODO: JumpKick/HiJumpKick\n\t{\n\t\tswitch (effect)\n\t\t{\n\t\t\tcase PBEMoveEffect.Recoil:\n\t\t\tcase PBEMoveEffect.Recoil__10PercentBurn:\n\t\t\tcase PBEMoveEffect.Recoil__10PercentParalyze: return true;\n\t\t\tdefault: return false;\n\t\t}\n\t}\n\tpublic static bool IsSetDamageMove(PBEMoveEffect effect)\n\t{\n\t\tswitch (effect)\n\t\t{\n\t\t\tcase PBEMoveEffect.Endeavor:\n\t\t\tcase PBEMoveEffect.FinalGambit:\n\t\t\tcase PBEMoveEffect.OneHitKnockout:\n\t\t\tcase PBEMoveEffect.Psywave:\n\t\t\tcase PBEMoveEffect.SeismicToss:\n\t\t\tcase PBEMoveEffect.SetDamage:\n\t\t\tcase PBEMoveEffect.SuperFang: return true;\n\t\t\tdefault: return false;\n\t\t}\n\t}\n\tpublic static bool IsSpreadMove(PBEMoveTarget targets)\n\t{\n\t\tswitch (targets)\n\t\t{\n\t\t\tcase PBEMoveTarget.All:\n\t\t\tcase PBEMoveTarget.AllFoes:\n\t\t\tcase PBEMoveTarget.AllFoesSurrounding:\n\t\t\tcase PBEMoveTarget.AllSurrounding:\n\t\t\tcase PBEMoveTarget.AllTeam: return true;\n\t\t\tdefault: return false;\n\t\t}\n\t}\n\tpublic static bool IsWeatherMove(PBEMoveEffect effect)\n\t{\n\t\tswitch (effect)\n\t\t{\n\t\t\tcase PBEMoveEffect.Hail:\n\t\t\tcase PBEMoveEffect.RainDance:\n\t\t\tcase PBEMoveEffect.Sandstorm:\n\t\t\tcase PBEMoveEffect.SunnyDay: return true;\n\t\t\tdefault: return false;\n\t\t}\n\t}\n\n\t/// <summary>Temporary check to see if a move is usable, can be removed once all moves are added</summary>\n\tpublic static bool IsMoveUsable(PBEMove move)\n\t{\n\t\treturn PBEDataProvider.Instance.GetMoveData(move, cache: false).IsMoveUsable();\n\t}\n\t/// <summary>Temporary check to see if a move is usable, can be removed once all moves are added</summary>\n\tpublic static bool IsMoveUsable(PBEMoveEffect effect)\n\t{\n\t\treturn effect != PBEMoveEffect.TODOMOVE && effect != PBEMoveEffect.Sketch;\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Data/Utils/DataUtils_Stats.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Utils;\nusing System;\nusing System.Collections.Generic;\n\nnamespace Kermalis.PokemonBattleEngine.Data.Utils;\n\npublic static partial class PBEDataUtils\n{\n\t#region Static Collections\n\n\tpublic static PBEAlphabeticalList<PBENature> AllNatures { get; } = new(Enum.GetValues<PBENature>().ExceptOne(PBENature.MAX));\n\tpublic static PBEAlphabeticalList<PBESpecies> MoonStoneSpecies { get; } = new(new[] { PBESpecies.Nidoran_F, PBESpecies.Nidorina, PBESpecies.Nidoqueen,\n\t\t\tPBESpecies.Nidoran_M, PBESpecies.Nidorino, PBESpecies.Nidoking, PBESpecies.Cleffa, PBESpecies.Clefairy, PBESpecies.Clefable, PBESpecies.Igglybuff, PBESpecies.Jigglypuff,\n\t\t\tPBESpecies.Wigglytuff, PBESpecies.Skitty, PBESpecies.Delcatty, PBESpecies.Munna, PBESpecies.Musharna });\n\tprivate static readonly Dictionary<PBENature, (PBEFlavor Favored, PBEFlavor Disliked)> _natureBoosts = new()\n\t\t{\n\t\t\t{ PBENature.Adamant, (PBEFlavor.Spicy, PBEFlavor.Dry) },\n\t\t\t{ PBENature.Bold, (PBEFlavor.Sour, PBEFlavor.Spicy) },\n\t\t\t{ PBENature.Brave, (PBEFlavor.Spicy, PBEFlavor.Sweet) },\n\t\t\t{ PBENature.Calm, (PBEFlavor.Bitter, PBEFlavor.Spicy) },\n\t\t\t{ PBENature.Careful, (PBEFlavor.Bitter, PBEFlavor.Dry) },\n\t\t\t{ PBENature.Gentle, (PBEFlavor.Bitter, PBEFlavor.Sour) },\n\t\t\t{ PBENature.Hasty, (PBEFlavor.Sweet, PBEFlavor.Sour) },\n\t\t\t{ PBENature.Impish, (PBEFlavor.Sour, PBEFlavor.Dry) },\n\t\t\t{ PBENature.Jolly, (PBEFlavor.Sweet, PBEFlavor.Dry) },\n\t\t\t{ PBENature.Lax, (PBEFlavor.Sour, PBEFlavor.Bitter) },\n\t\t\t{ PBENature.Lonely, (PBEFlavor.Spicy, PBEFlavor.Sour) },\n\t\t\t{ PBENature.Mild, (PBEFlavor.Dry, PBEFlavor.Sour) },\n\t\t\t{ PBENature.Modest, (PBEFlavor.Dry, PBEFlavor.Spicy) },\n\t\t\t{ PBENature.Naive, (PBEFlavor.Sweet, PBEFlavor.Bitter) },\n\t\t\t{ PBENature.Naughty, (PBEFlavor.Spicy, PBEFlavor.Bitter) },\n\t\t\t{ PBENature.Quiet, (PBEFlavor.Dry, PBEFlavor.Sweet) },\n\t\t\t{ PBENature.Rash, (PBEFlavor.Dry, PBEFlavor.Bitter) },\n\t\t\t{ PBENature.Relaxed, (PBEFlavor.Sour, PBEFlavor.Sweet) },\n\t\t\t{ PBENature.Sassy, (PBEFlavor.Bitter, PBEFlavor.Sweet) },\n\t\t\t{ PBENature.Timid, (PBEFlavor.Sweet, PBEFlavor.Spicy) }\n\t\t};\n\tprivate static readonly PBEType[] _hiddenPowerTypes = new PBEType[]\n\t\t{\n\t\t\tPBEType.Fighting, // 7.8125 %\n            PBEType.Flying,   // 6.2500 %\n            PBEType.Poison,   // 6.2500 %\n            PBEType.Ground,   // 6.2500 %\n            PBEType.Rock,     // 6.2500 %\n            PBEType.Bug,      // 7.8125 %\n            PBEType.Ghost,    // 6.2500 %\n            PBEType.Steel,    // 6.2500 %\n            PBEType.Fire,     // 6.2500 %\n            PBEType.Water,    // 6.2500 %\n            PBEType.Grass,    // 7.8125 %\n            PBEType.Electric, // 6.2500 %\n            PBEType.Psychic,  // 6.2500 %\n            PBEType.Ice,      // 6.2500 %\n            PBEType.Dragon,   // 6.2500 %\n            PBEType.Dark      // 1.5625 %\n        };\n\n\t#region Genders\n\n\tprivate static readonly PBEAlphabeticalList<PBEGender> _genderless = new(new[] { PBEGender.Genderless });\n\tprivate static readonly PBEAlphabeticalList<PBEGender> _male = new(new[] { PBEGender.Male });\n\tprivate static readonly PBEAlphabeticalList<PBEGender> _female = new(new[] { PBEGender.Female });\n\tprivate static readonly PBEAlphabeticalList<PBEGender> _maleFemale = new(new[] { PBEGender.Male, PBEGender.Female });\n\n\t#endregion\n\n\t#endregion\n\n\tpublic static sbyte GetRelationshipToFlavor(this PBENature nature, PBEFlavor flavor)\n\t{\n\t\tif (nature >= PBENature.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(nature));\n\t\t}\n\t\tif (flavor >= PBEFlavor.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(flavor));\n\t\t}\n\n\t\tif (_natureBoosts.TryGetValue(nature, out (PBEFlavor Favored, PBEFlavor Disliked) t))\n\t\t{\n\t\t\tif (t.Favored == flavor)\n\t\t\t{\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\telse if (t.Disliked == flavor)\n\t\t\t{\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\tpublic static sbyte GetRelationshipToStat(this PBENature nature, PBEStat stat)\n\t{\n\t\tif (nature >= PBENature.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(nature));\n\t\t}\n\t\tif (stat < PBEStat.Attack || stat > PBEStat.Speed)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(stat));\n\t\t}\n\n\t\treturn nature.GetRelationshipToFlavor((PBEFlavor)(stat - 1));\n\t}\n\tpublic static PBEFlavor? GetLikedFlavor(this PBENature nature)\n\t{\n\t\tif (nature >= PBENature.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(nature));\n\t\t}\n\t\tif (_natureBoosts.TryGetValue(nature, out (PBEFlavor Favored, PBEFlavor Disliked) t))\n\t\t{\n\t\t\treturn t.Favored;\n\t\t}\n\t\treturn null;\n\t}\n\tpublic static PBEFlavor? GetDislikedFlavor(this PBENature nature)\n\t{\n\t\tif (nature >= PBENature.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(nature));\n\t\t}\n\t\tif (_natureBoosts.TryGetValue(nature, out (PBEFlavor Favored, PBEFlavor Disliked) t))\n\t\t{\n\t\t\treturn t.Disliked;\n\t\t}\n\t\treturn null;\n\t}\n\tpublic static PBEStat? GetLikedStat(this PBENature nature)\n\t{\n\t\tif (nature >= PBENature.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(nature));\n\t\t}\n\t\tif (_natureBoosts.TryGetValue(nature, out (PBEFlavor Favored, PBEFlavor Disliked) t))\n\t\t{\n\t\t\treturn (PBEStat)(t.Favored + 1);\n\t\t}\n\t\treturn null;\n\t}\n\tpublic static PBEStat? GetDislikedStat(this PBENature nature)\n\t{\n\t\tif (nature >= PBENature.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(nature));\n\t\t}\n\t\tif (_natureBoosts.TryGetValue(nature, out (PBEFlavor Favored, PBEFlavor Disliked) t))\n\t\t{\n\t\t\treturn (PBEStat)(t.Disliked + 1);\n\t\t}\n\t\treturn null;\n\t}\n\tpublic static bool IsNeutralNature(this PBENature nature)\n\t{\n\t\tif (nature >= PBENature.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(nature));\n\t\t}\n\t\treturn !_natureBoosts.ContainsKey(nature);\n\t}\n\n\tpublic static int CalcMaxPP(byte ppTier, byte ppUps, PBESettings settings)\n\t{\n\t\tsettings.ShouldBeReadOnly(nameof(settings));\n\t\treturn Math.Max(1, (ppTier * settings.PPMultiplier) + (ppTier * ppUps));\n\t}\n\tpublic static int CalcMaxPP(PBEMove move, byte ppUps, PBESettings settings)\n\t{\n\t\tsettings.ShouldBeReadOnly(nameof(settings));\n\t\tif (move >= PBEMove.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(move));\n\t\t}\n\t\tif (move != PBEMove.None)\n\t\t{\n\t\t\treturn CalcMaxPP(PBEDataProvider.Instance.GetMoveData(move).PPTier, ppUps, settings);\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tprivate static ushort CalcHP(PBESpecies species, IPBEReadOnlyStatCollection baseStats, byte evs, byte ivs, byte level)\n\t{\n\t\treturn (ushort)(species == PBESpecies.Shedinja ? 1 : ((((2 * baseStats.HP) + ivs + (evs / 4)) * level / 100) + level + 10));\n\t}\n\tprivate static ushort CalcOtherStat(IPBEReadOnlyStatCollection baseStats, PBEStat stat, sbyte statRelationship, byte evs, byte ivs, byte level, PBESettings settings)\n\t{\n\t\tfloat natureMultiplier = 1 + (statRelationship * settings.NatureStatBoost);\n\t\treturn (ushort)(((((2 * baseStats.GetStat(stat)) + ivs + (evs / 4)) * level / 100) + 5) * natureMultiplier);\n\t}\n\tpublic static ushort CalculateStat(PBESpecies species, IPBEReadOnlyStatCollection baseStats, PBEStat stat, PBENature nature, byte evs, byte ivs, byte level, PBESettings settings)\n\t{\n\t\tsettings.ShouldBeReadOnly(nameof(settings));\n\t\tValidateLevel(level, settings);\n\t\tif (ivs > settings.MaxIVs)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(ivs));\n\t\t}\n\n\t\tswitch (stat)\n\t\t{\n\t\t\tcase PBEStat.HP:\n\t\t\t{\n\t\t\t\treturn CalcHP(species, baseStats, evs, ivs, level);\n\t\t\t}\n\t\t\tcase PBEStat.Attack:\n\t\t\tcase PBEStat.Defense:\n\t\t\tcase PBEStat.SpAttack:\n\t\t\tcase PBEStat.SpDefense:\n\t\t\tcase PBEStat.Speed:\n\t\t\t{\n\t\t\t\treturn CalcOtherStat(baseStats, stat, nature.GetRelationshipToStat(stat), evs, ivs, level, settings);\n\t\t\t}\n\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(stat));\n\t\t}\n\t}\n\tpublic static ushort CalculateStat(IPBEPokemonData pData, PBEStat stat, PBENature nature, byte evs, byte ivs, byte level, PBESettings settings)\n\t{\n\t\treturn CalculateStat(pData.Species, pData.BaseStats, stat, nature, evs, ivs, level, settings);\n\t}\n\tpublic static ushort CalculateStat(IPBESpeciesForm pkmn, PBEStat stat, PBENature nature, byte evs, byte ivs, byte level, PBESettings settings)\n\t{\n\t\treturn CalculateStat(pkmn.Species, pkmn.Form, stat, nature, evs, ivs, level, settings);\n\t}\n\tpublic static ushort CalculateStat(PBESpecies species, PBEForm form, PBEStat stat, PBENature nature, byte evs, byte ivs, byte level, PBESettings settings)\n\t{\n\t\tValidateSpecies(species, form, false);\n\t\treturn CalculateStat(PBEDataProvider.Instance.GetPokemonData(species, form), stat, nature, evs, ivs, level, settings);\n\t}\n\tpublic static void GetStatRange(PBESpecies species, IPBEReadOnlyStatCollection baseStats, PBEStat stat, byte level, PBESettings settings, out ushort low, out ushort high)\n\t{\n\t\tsettings.ShouldBeReadOnly(nameof(settings));\n\t\tValidateLevel(level, settings);\n\t\tswitch (stat)\n\t\t{\n\t\t\tcase PBEStat.HP:\n\t\t\t{\n\t\t\t\tlow = CalcHP(species, baseStats, 0, 0, level);\n\t\t\t\thigh = CalcHP(species, baseStats, byte.MaxValue, settings.MaxIVs, level);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEStat.Attack:\n\t\t\tcase PBEStat.Defense:\n\t\t\tcase PBEStat.SpAttack:\n\t\t\tcase PBEStat.SpDefense:\n\t\t\tcase PBEStat.Speed:\n\t\t\t{\n\t\t\t\tlow = CalcOtherStat(baseStats, stat, -1, 0, 0, level, settings);\n\t\t\t\thigh = CalcOtherStat(baseStats, stat, +1, byte.MaxValue, settings.MaxIVs, level, settings);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(stat));\n\t\t}\n\t}\n\tpublic static void GetStatRange(IPBEPokemonData pData, PBEStat stat, byte level, PBESettings settings, out ushort low, out ushort high)\n\t{\n\t\tGetStatRange(pData.Species, pData.BaseStats, stat, level, settings, out low, out high);\n\t}\n\tpublic static void GetStatRange(IPBESpeciesForm pkmn, PBEStat stat, byte level, PBESettings settings, out ushort low, out ushort high)\n\t{\n\t\tGetStatRange(pkmn.Species, pkmn.Form, stat, level, settings, out low, out high);\n\t}\n\tpublic static void GetStatRange(PBESpecies species, PBEForm form, PBEStat stat, byte level, PBESettings settings, out ushort low, out ushort high)\n\t{\n\t\tValidateSpecies(species, form, false);\n\t\tGetStatRange(PBEDataProvider.Instance.GetPokemonData(species, form), stat, level, settings, out low, out high);\n\t}\n\n\tpublic static PBEType GetHiddenPowerType(byte hpIV, byte attackIV, byte defenseIV, byte spAttackIV, byte spDefenseIV, byte speedIV)\n\t{\n\t\tint a = hpIV & 1,\n\t\t\t\tb = attackIV & 1,\n\t\t\t\tc = defenseIV & 1,\n\t\t\t\td = speedIV & 1,\n\t\t\t\te = spAttackIV & 1,\n\t\t\t\tf = spDefenseIV & 1;\n\t\treturn _hiddenPowerTypes[(((1 << 0) * a) + ((1 << 1) * b) + ((1 << 2) * c) + ((1 << 3) * d) + ((1 << 4) * e) + ((1 << 5) * f)) * (_hiddenPowerTypes.Length - 1) / ((1 << 6) - 1)];\n\t}\n\tpublic static byte GetHiddenPowerBasePower(byte hpIV, byte attackIV, byte defenseIV, byte spAttackIV, byte spDefenseIV, byte speedIV, PBESettings settings)\n\t{\n\t\tsettings.ShouldBeReadOnly(nameof(settings));\n\t\tint a = (hpIV & 2) == 2 ? 1 : 0,\n\t\t\t\tb = (attackIV & 2) == 2 ? 1 : 0,\n\t\t\t\tc = (defenseIV & 2) == 2 ? 1 : 0,\n\t\t\t\td = (speedIV & 2) == 2 ? 1 : 0,\n\t\t\t\te = (spAttackIV & 2) == 2 ? 1 : 0,\n\t\t\t\tf = (spDefenseIV & 2) == 2 ? 1 : 0;\n\t\tbyte mininumBasePower = settings.HiddenPowerMin,\n\t\t\t\tmaximumBasePower = settings.HiddenPowerMax;\n\t\treturn (byte)(((((1 << 0) * a) + ((1 << 1) * b) + ((1 << 2) * c) + ((1 << 3) * d) + ((1 << 4) * e) + ((1 << 5) * f)) * (maximumBasePower - mininumBasePower) / ((1 << 6) - 1)) + mininumBasePower);\n\t}\n\n\tpublic static IReadOnlyList<PBEGender> GetValidGenders(PBEGenderRatio genderRatio)\n\t{\n\t\tswitch (genderRatio)\n\t\t{\n\t\t\tcase PBEGenderRatio.M0_F0: return _genderless;\n\t\t\tcase PBEGenderRatio.M1_F0: return _male;\n\t\t\tcase PBEGenderRatio.M0_F1: return _female;\n\t\t\tdefault: return _maleFemale;\n\t\t}\n\t}\n\tpublic static bool IsOppositeGender(this PBEGender gender, PBEGender otherGender)\n\t{\n\t\treturn gender != PBEGender.Genderless && otherGender != PBEGender.Genderless && gender != otherGender;\n\t}\n\tpublic static string ToSymbol(this PBEGender gender)\n\t{\n\t\treturn gender == PBEGender.Female ? \"♀\" : gender == PBEGender.Male ? \"♂\" : string.Empty;\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Data/Utils/DataUtils_Validate.cs",
    "content": "﻿using System;\n\nnamespace Kermalis.PokemonBattleEngine.Data.Utils;\n\npublic static partial class PBEDataUtils\n{\n\tpublic static void ValidateSpecies(PBESpecies species, PBEForm form, bool requireUsableOutsideOfBattle)\n\t{\n\t\tif (!IsValidForm(species, form, requireUsableOutsideOfBattle))\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(form));\n\t\t}\n\t}\n\tpublic static void ValidateNickname(string value, PBESettings settings)\n\t{\n\t\tif (string.IsNullOrWhiteSpace(value))\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(value));\n\t\t}\n\t\tif (value.Length > settings.MaxPokemonNameLength)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"{nameof(value)} cannot have more than {nameof(settings.MaxPokemonNameLength)} ({settings.MaxPokemonNameLength}) characters.\");\n\t\t}\n\t}\n\tpublic static void ValidateLevel(byte value, PBESettings settings)\n\t{\n\t\tif (value < settings.MinLevel || value > settings.MaxLevel)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(value), $\"{nameof(value)} must be at least {nameof(settings.MinLevel)} ({settings.MinLevel}) and cannot exceed {nameof(settings.MaxLevel)} ({settings.MaxLevel}).\");\n\t\t}\n\t}\n\tpublic static void ValidateEXP(PBEGrowthRate type, uint value, byte level)\n\t{\n\t\tuint requiredForLevel = PBEDataProvider.Instance.GetEXPRequired(type, level);\n\t\tif (value < requiredForLevel)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(value));\n\t\t}\n\t\tif (level < 100)\n\t\t{\n\t\t\tuint requiredForNextLevel = PBEDataProvider.Instance.GetEXPRequired(type, (byte)(level + 1));\n\t\t\tif (value >= requiredForNextLevel)\n\t\t\t{\n\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(value));\n\t\t\t}\n\t\t}\n\t}\n\tpublic static void ValidateAbility(PBEAlphabeticalList<PBEAbility> valid, PBEAbility value)\n\t{\n\t\tif (!valid.Contains(value))\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(value));\n\t\t}\n\t}\n\tpublic static void ValidateNature(PBENature value)\n\t{\n\t\tif (!AllNatures.Contains(value))\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(value));\n\t\t}\n\t}\n\tpublic static void ValidateGender(PBEAlphabeticalList<PBEGender> valid, PBEGender value)\n\t{\n\t\tif (!valid.Contains(value))\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(value));\n\t\t}\n\t}\n\tpublic static void ValidateItem(PBEAlphabeticalList<PBEItem> valid, PBEItem value)\n\t{\n\t\tif (!valid.Contains(value))\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(value));\n\t\t}\n\t}\n\tpublic static void ValidateCaughtBall(PBEItem value)\n\t{\n\t\tif (!AllBalls.Contains(value))\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(value));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Network/Client.cs",
    "content": "﻿/* EasyTcp\n * \n * Copyright (c) 2019 henkje\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Packets;\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq;\nusing System.Net;\nusing System.Net.Sockets;\n\nnamespace Kermalis.PokemonBattleEngine.Network;\n\npublic sealed class PBEClient : IDisposable\n{\n\tpublic PBEBattle? Battle { get; set; }\n\n\tprivate Socket? _socket;\n\tprivate PBEEncryption? _encryption;\n\tprivate PBEPacketProcessor _packetProcessor = null!; // Set in Connect()\n\tprivate byte[]? _buffer;\n\n\tpublic IPEndPoint? RemoteIP { get; private set; }\n\t[MemberNotNullWhen(true, nameof(_socket), nameof(RemoteIP))]\n\tpublic bool IsConnected { get; private set; }\n\n\tpublic event EventHandler? Disconnected;\n\tpublic event EventHandler<Exception>? Error;\n\tpublic event EventHandler<IPBEPacket>? PacketReceived;\n\n\tpublic bool Connect(IPEndPoint ip, int millisecondsTimeout, PBEPacketProcessor packetProcessor, PBEEncryption? encryption = null)\n\t{\n\t\tif (millisecondsTimeout < -1)\n\t\t{\n\t\t\tthrow new ArgumentException($\"\\\"{nameof(millisecondsTimeout)}\\\" is invalid.\");\n\t\t}\n\n\t\tDisconnect(true);\n\t\t_socket = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp);\n\n\t\ttry\n\t\t{\n\t\t\tif (_socket.BeginConnect(ip, null, null).AsyncWaitHandle.WaitOne(millisecondsTimeout))\n\t\t\t{\n\t\t\t\tIsConnected = true;\n\t\t\t\tRemoteIP = ip;\n\t\t\t\t_encryption = encryption;\n\t\t\t\t_packetProcessor = packetProcessor;\n\t\t\t\tBeginReceive();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex)\n\t\t{\n\t\t\tNotifyError(ex);\n\t\t}\n\t\tDisconnect(false);\n\t\treturn false;\n\t}\n\tpublic void Disconnect(bool notify)\n\t{\n\t\tif (!IsConnected)\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\tIsConnected = false;\n\t\tRemoteIP = null;\n\t\t_encryption = null;\n\t\ttry\n\t\t{\n\t\t\t_socket.Shutdown(SocketShutdown.Both);\n\t\t}\n\t\tcatch (Exception ex)\n\t\t{\n\t\t\tNotifyError(ex);\n\t\t}\n\t\t_socket.Dispose();\n\t\t_socket = null;\n\t\tif (notify)\n\t\t{\n\t\t\tDisconnected?.Invoke(this, EventArgs.Empty);\n\t\t}\n\t}\n\n\tpublic void Send(IPBEPacket packet)\n\t{\n\t\tif (!IsConnected)\n\t\t{\n\t\t\tthrow new InvalidOperationException(\"Socket not connected.\");\n\t\t}\n\t\tbyte[] data = packet.Data.ToArray();\n\t\tif (_encryption is not null)\n\t\t{\n\t\t\tdata = _encryption.Encrypt(data);\n\t\t}\n\t\tPBENetworkUtils.Send(data, _socket);\n\t}\n\n\tprivate void BeginReceive()\n\t{\n\t\t_socket!.BeginReceive(_buffer = new byte[2], 0, 2, SocketFlags.None, OnReceiveLength, null);\n\t}\n\tprivate void OnReceiveLength(IAsyncResult ar)\n\t{\n\t\tif (!IsConnected)\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\ttry\n\t\t{\n\t\t\tif (_socket.Poll(0, SelectMode.SelectRead) && _socket.Available <= 0)\n\t\t\t{\n\t\t\t\tDisconnect(true);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tushort dataLength = (ushort)(_buffer![0] | (_buffer[1] << 8));\n\t\t\t\tif (dataLength <= 0)\n\t\t\t\t{\n\t\t\t\t\tDisconnect(true);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t_socket.BeginReceive(_buffer = new byte[dataLength], 0, dataLength, SocketFlags.None, OnReceiveData, null);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex)\n\t\t{\n\t\t\tNotifyError(ex);\n\t\t\tDisconnect(true);\n\t\t}\n\t}\n\tprivate void OnReceiveData(IAsyncResult ar)\n\t{\n\t\tif (!IsConnected)\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\ttry\n\t\t{\n\t\t\tif (_socket.Poll(0, SelectMode.SelectRead) && _socket.Available <= 0)\n\t\t\t{\n\t\t\t\tDisconnect(true);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tbyte[] data = _buffer!;\n\t\t\t\tif (_encryption is not null)\n\t\t\t\t{\n\t\t\t\t\tdata = _encryption.Decrypt(data);\n\t\t\t\t}\n\t\t\t\tPacketReceived?.Invoke(this, _packetProcessor.CreatePacket(data, Battle));\n\t\t\t\tBeginReceive();\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex)\n\t\t{\n\t\t\tNotifyError(ex);\n\t\t\tDisconnect(true);\n\t\t}\n\t}\n\n\tprivate void NotifyError(Exception ex)\n\t{\n\t\tif (Error is not null)\n\t\t{\n\t\t\tError.Invoke(this, ex);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\tpublic void Dispose()\n\t{\n\t\tDisconnect(true);\n\t\tDisconnected = null;\n\t\tError = null;\n\t\tPacketReceived = null;\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Network/Encryption.cs",
    "content": "﻿/* EasyTcp\n * \n * Copyright (c) 2019 henkje\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\nusing System;\nusing System.IO;\nusing System.Security.Cryptography;\n\nnamespace Kermalis.PokemonBattleEngine.Network;\n\npublic sealed class PBEEncryption\n{\n\tprivate readonly SymmetricAlgorithm _algorithm;\n\n\tpublic PBEEncryption(SymmetricAlgorithm algorithm)\n\t{\n\t\t_algorithm = algorithm;\n\t}\n\n\tpublic byte[] Encrypt(byte[] data)\n\t{\n\t\t_algorithm.GenerateIV();\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tms.Write(_algorithm.IV, 0, _algorithm.IV.Length);\n\t\t\tusing (var cs = new CryptoStream(ms, _algorithm.CreateEncryptor(_algorithm.Key, _algorithm.IV), CryptoStreamMode.Write))\n\t\t\t{\n\t\t\t\tcs.Write(data, 0, data.Length);\n\t\t\t\tcs.FlushFinalBlock();\n\t\t\t}\n\t\t\treturn ms.ToArray();\n\t\t}\n\t}\n\tpublic byte[] Decrypt(byte[] data)\n\t{\n\t\tusing (var ms = new MemoryStream(data))\n\t\t{\n\t\t\tbyte[] iv = new byte[_algorithm.IV.Length];\n\t\t\tms.Read(iv);\n\n\t\t\t_algorithm.IV = iv;\n\n\t\t\tusing (var cs = new CryptoStream(ms, _algorithm.CreateDecryptor(_algorithm.Key, _algorithm.IV), CryptoStreamMode.Read))\n\t\t\t{\n\t\t\t\tbyte[] decrypted = new byte[data.Length];\n\t\t\t\tint byteCount = cs.Read(decrypted, 0, decrypted.Length);\n\t\t\t\tbyte[] ret = new byte[byteCount];\n\t\t\t\tArray.Copy(decrypted, 0, ret, 0, byteCount);\n\t\t\t\treturn ret;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Network/NetworkUtils.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing System;\nusing System.Collections.Generic;\nusing System.Net.Sockets;\n\nnamespace Kermalis.PokemonBattleEngine.Network;\n\ninternal static class PBENetworkUtils\n{\n\tpublic static void Send(byte[] data, Socket socket)\n\t{\n\t\tusing (SocketAsyncEventArgs e = CreateArgs(data))\n\t\t{\n\t\t\tsocket.SendAsync(e);\n\t\t}\n\t}\n\tpublic static void Send(byte[] data, IEnumerable<Socket> sockets)\n\t{\n\t\tusing (SocketAsyncEventArgs e = CreateArgs(data))\n\t\t{\n\t\t\tforeach (Socket socket in sockets)\n\t\t\t{\n\t\t\t\tsocket.SendAsync(e);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static SocketAsyncEventArgs CreateArgs(byte[] data)\n\t{\n\t\tint len = data.Length;\n\t\tif (len <= 0 || len > ushort.MaxValue)\n\t\t{\n\t\t\tthrow new ArgumentException($\"Data length must be greater than 0 bytes and must not exceed {ushort.MaxValue} bytes.\");\n\t\t}\n\n\t\tbyte[] message = new byte[len + 2];\n\t\tEndianBinaryPrimitives.WriteInt16(message.AsSpan(0, 2), (short)len, Endianness.LittleEndian);\n\t\tArray.Copy(data, 0, message, 2, len);\n\t\tvar e = new SocketAsyncEventArgs();\n\t\te.SetBuffer(message, 0, message.Length);\n\t\treturn e;\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Network/Server.cs",
    "content": "﻿/* EasyTcp\n * \n * Copyright (c) 2019 henkje\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Packets;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Net;\nusing System.Net.Sockets;\n\nnamespace Kermalis.PokemonBattleEngine.Network;\n\npublic sealed class PBEServer : IDisposable\n{\n\tpublic PBEBattle? Battle { get; set; }\n\n\tprivate Socket? _listener;\n\tprivate PBEEncryption? _encryption;\n\tprivate PBEPacketProcessor _packetProcessor = null!; // Set in Start()\n\tprivate int _maxConnections;\n\n\t[MemberNotNullWhen(true, nameof(_listener))]\n\tpublic bool IsRunning => _listener is not null;\n\tprivate readonly HashSet<IPEndPoint> _bannedIPs = new();\n\tprivate readonly HashSet<PBEServerClient> _connectedClients = new();\n\n\tpublic event EventHandler<PBEServerClient>? ClientConnected;\n\tpublic event EventHandler<PBEServerClient>? ClientDisconnected;\n\tpublic event EventHandler<Exception>? Error;\n\tpublic delegate void PBEClientRefusedEventHandler(object? sender, IPEndPoint refusedIP, bool refusedForBan);\n\tpublic event PBEClientRefusedEventHandler? ClientRefused;\n\n\tpublic void Start(IPEndPoint ip, int maxConnections, PBEPacketProcessor packetProcessor, PBEEncryption? encryption = null, bool dualMode = false)\n\t{\n\t\tif (IsRunning)\n\t\t{\n\t\t\tthrow new InvalidOperationException(\"Server is already running.\");\n\t\t}\n\t\tif (maxConnections <= 0)\n\t\t{\n\t\t\tthrow new ArgumentException($\"\\\"{nameof(maxConnections)}\\\" must be greater than 0.\");\n\t\t}\n\n\t\t_listener = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp);\n\t\tif (ip.AddressFamily == AddressFamily.InterNetworkV6)\n\t\t{\n\t\t\t_listener.DualMode = dualMode;\n\t\t}\n\t\t_listener.Bind(ip);\n\t\t_encryption = encryption;\n\t\t_packetProcessor = packetProcessor;\n\n\t\ttry\n\t\t{\n\t\t\t_maxConnections = maxConnections;\n\t\t\t_listener.Listen(maxConnections);\n\t\t\t_listener.BeginAccept(OnClientConnected, null);\n\t\t}\n\t\tcatch (Exception ex)\n\t\t{\n\t\t\tNotifyError(ex);\n\t\t\tStop();\n\t\t}\n\t}\n\tpublic void Stop()\n\t{\n\t\tif (!IsRunning)\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\ttry\n\t\t{\n\t\t\t_listener.Shutdown(SocketShutdown.Both);\n\t\t}\n\t\tcatch (Exception ex)\n\t\t{\n\t\t\tNotifyError(ex);\n\t\t}\n\t\t_listener.Dispose();\n\t\t_listener = null;\n\t}\n\n\tpublic void SendToAll(IPBEPacket packet)\n\t{\n\t\tlock (_connectedClients)\n\t\t{\n\t\t\tforeach (PBEServerClient client in _connectedClients)\n\t\t\t{\n\t\t\t\tclient.Send(packet);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void OnClientConnected(IAsyncResult ar)\n\t{\n\t\tif (!IsRunning)\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\tPBEServerClient? client = null;\n\t\ttry\n\t\t{\n\t\t\tclient = new PBEServerClient(_listener.EndAccept(ar), _encryption);\n\t\t\tbool isBanned;\n\t\t\tlock (_bannedIPs)\n\t\t\t{\n\t\t\t\tisBanned = _bannedIPs.Contains(client.IP);\n\t\t\t}\n\t\t\tif (isBanned)\n\t\t\t{\n\t\t\t\tRefuseClient(client, true);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tint count;\n\t\t\t\tlock (_connectedClients)\n\t\t\t\t{\n\t\t\t\t\tcount = _connectedClients.Count;\n\t\t\t\t}\n\t\t\t\tif (count >= _maxConnections)\n\t\t\t\t{\n\t\t\t\t\tRefuseClient(client, false);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tlock (_connectedClients)\n\t\t\t\t\t{\n\t\t\t\t\t\t_connectedClients.Add(client);\n\t\t\t\t\t}\n\t\t\t\t\tclient.IsConnected = true;\n\t\t\t\t\tClientConnected?.Invoke(this, client);\n\t\t\t\t\tBeginReceive(client);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex)\n\t\t{\n\t\t\tNotifyError(ex);\n\t\t\tif (client is not null)\n\t\t\t{\n\t\t\t\tDisconnectClient(client);\n\t\t\t}\n\t\t}\n\t\t_listener.BeginAccept(OnClientConnected, _listener);\n\t}\n\n\tprivate void BeginReceive(PBEServerClient client)\n\t{\n\t\tbyte[] buffer = new byte[2];\n\t\tclient.Buffer = buffer;\n\t\tclient.Socket.BeginReceive(buffer, 0, 2, SocketFlags.None, OnReceiveLength, client);\n\t}\n\tprivate void OnReceiveLength(IAsyncResult ar)\n\t{\n\t\tvar client = (PBEServerClient)ar.AsyncState!;\n\t\tif (!client.IsConnected)\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\ttry\n\t\t{\n\t\t\tif (client.Socket.Poll(0, SelectMode.SelectRead) && client.Socket.Available <= 0)\n\t\t\t{\n\t\t\t\tDisconnectClient(client);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tushort dataLength = (ushort)(client.Buffer![0] | (client.Buffer[1] << 8));\n\t\t\t\tif (dataLength <= 0)\n\t\t\t\t{\n\t\t\t\t\tDisconnectClient(client);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tclient.Socket.BeginReceive(client.Buffer = new byte[dataLength], 0, dataLength, SocketFlags.None, OnReceiveData, client);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex)\n\t\t{\n\t\t\tNotifyError(ex);\n\t\t\tDisconnectClient(client);\n\t\t}\n\t}\n\tprivate void OnReceiveData(IAsyncResult ar)\n\t{\n\t\tvar client = (PBEServerClient)ar.AsyncState!;\n\t\tif (!client.IsConnected)\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\ttry\n\t\t{\n\t\t\tif (client.Socket.Poll(0, SelectMode.SelectRead) && client.Socket.Available <= 0)\n\t\t\t{\n\t\t\t\tDisconnectClient(client);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tbyte[] data = client.Buffer!;\n\t\t\t\tif (_encryption is not null)\n\t\t\t\t{\n\t\t\t\t\tdata = _encryption.Decrypt(data);\n\t\t\t\t}\n\t\t\t\tclient.FirePacketReceived(_packetProcessor.CreatePacket(data, Battle));\n\t\t\t\tBeginReceive(client);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex)\n\t\t{\n\t\t\tNotifyError(ex);\n\t\t\tDisconnectClient(client);\n\t\t}\n\t}\n\n\tprivate void RefuseClient(PBEServerClient client, bool isBanned)\n\t{\n\t\tclient.Socket.Shutdown(SocketShutdown.Both);\n\t\tclient.Socket.Dispose();\n\t\tClientRefused?.Invoke(this, client.IP, isBanned);\n\t}\n\tpublic bool DisconnectClient(PBEServerClient client)\n\t{\n\t\tbool b;\n\t\tlock (_connectedClients)\n\t\t{\n\t\t\tb = _connectedClients.Remove(client);\n\t\t}\n\t\tif (b)\n\t\t{\n\t\t\tclient.IsConnected = false;\n\t\t\tclient.Socket.Shutdown(SocketShutdown.Both);\n\t\t\tclient.Socket.Dispose();\n\t\t\tClientDisconnected?.Invoke(this, client);\n\t\t}\n\t\treturn b;\n\t}\n\n\tprivate void NotifyError(Exception ex)\n\t{\n\t\tif (Error is not null)\n\t\t{\n\t\t\tError.Invoke(this, ex);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\tpublic void Dispose()\n\t{\n\t\tStop();\n\t\tClientConnected = null;\n\t\tClientDisconnected = null;\n\t\tClientRefused = null;\n\t\tError = null;\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Network/ServerClient.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Packets;\nusing System;\nusing System.Linq;\nusing System.Net;\nusing System.Net.Sockets;\n\nnamespace Kermalis.PokemonBattleEngine.Network;\n\npublic sealed class PBEServerClient\n{\n\tinternal readonly Socket Socket;\n\tinternal byte[]? Buffer;\n\n\tprivate readonly PBEEncryption? _encryption;\n\n\tpublic IPEndPoint IP { get; }\n\tpublic bool IsConnected { get; internal set; }\n\n\tpublic event EventHandler<IPBEPacket>? PacketReceived;\n\n\tinternal PBEServerClient(Socket socket, PBEEncryption? encryption)\n\t{\n\t\tSocket = socket;\n\t\tIP = (IPEndPoint)socket.RemoteEndPoint!;\n\t\t_encryption = encryption;\n\t}\n\n\tpublic void Send(IPBEPacket packet)\n\t{\n\t\tbyte[] data = packet.Data.ToArray();\n\t\tif (_encryption is not null)\n\t\t{\n\t\t\tdata = _encryption.Encrypt(data);\n\t\t}\n\t\tPBENetworkUtils.Send(data, Socket);\n\t}\n\n\tinternal void FirePacketReceived(IPBEPacket packet)\n\t{\n\t\tPacketReceived?.Invoke(this, packet);\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/ActionsRequestPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing System.Collections.ObjectModel;\nusing System.IO;\nusing System.Linq;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEActionsRequestPacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x07;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer Trainer { get; }\n\tpublic ReadOnlyCollection<PBEFieldPosition> Pokemon { get; }\n\n\tinternal PBEActionsRequestPacket(PBETrainer trainer)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((Trainer = trainer).Id);\n\t\t\tbyte count = (byte)(Pokemon = new ReadOnlyCollection<PBEFieldPosition>(trainer.ActionsRequired.Select(p => p.FieldPosition).ToArray())).Count;\n\t\t\tw.WriteByte(count);\n\t\t\tfor (int i = 0; i < count; i++)\n\t\t\t{\n\t\t\t\tw.WriteEnum(Pokemon[i]);\n\t\t\t}\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEActionsRequestPacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tTrainer = battle.Trainers[r.ReadByte()];\n\t\tvar pkmn = new PBEFieldPosition[r.ReadByte()];\n\t\tfor (int i = 0; i < pkmn.Length; i++)\n\t\t{\n\t\t\tpkmn[i] = r.ReadEnum<PBEFieldPosition>();\n\t\t}\n\t\tPokemon = new ReadOnlyCollection<PBEFieldPosition>(pkmn);\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/ActionsResponsePacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEActionsResponsePacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x08;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic ReadOnlyCollection<PBETurnAction> Actions { get; }\n\n\tpublic PBEActionsResponsePacket(IList<PBETurnAction> actions)\n\t{\n\t\tif (actions.Count == 0)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(actions));\n\t\t}\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tbyte count = (byte)(Actions = new ReadOnlyCollection<PBETurnAction>(actions)).Count;\n\t\t\tw.WriteByte(count);\n\t\t\tfor (int i = 0; i < count; i++)\n\t\t\t{\n\t\t\t\tActions[i].ToBytes(w);\n\t\t\t}\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEActionsResponsePacket(byte[] data, EndianBinaryReader r)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tvar actions = new PBETurnAction[r.ReadByte()];\n\t\tfor (int i = 0; i < actions.Length; i++)\n\t\t{\n\t\t\tactions[i] = new PBETurnAction(r);\n\t\t}\n\t\tActions = new ReadOnlyCollection<PBETurnAction>(actions);\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/AutoCenterPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic interface IPBEAutoCenterPacket : IPBEPacket\n{\n\tPBETrainer Pokemon0Trainer { get; }\n\tPBEFieldPosition Pokemon0OldPosition { get; }\n\tPBETrainer Pokemon1Trainer { get; }\n\tPBEFieldPosition Pokemon1OldPosition { get; }\n}\npublic interface IPBEAutoCenterPacket_0 : IPBEAutoCenterPacket\n{\n\tbyte Pokemon0 { get; }\n}\npublic interface IPBEAutoCenterPacket_1 : IPBEAutoCenterPacket\n{\n\tbyte Pokemon1 { get; }\n}\npublic sealed class PBEAutoCenterPacket : IPBEAutoCenterPacket_0, IPBEAutoCenterPacket_1\n{\n\tpublic const ushort ID = 0x2A;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer Pokemon0Trainer { get; }\n\tpublic byte Pokemon0 { get; }\n\tpublic PBEFieldPosition Pokemon0OldPosition { get; }\n\tpublic PBETrainer Pokemon1Trainer { get; }\n\tpublic byte Pokemon1 { get; }\n\tpublic PBEFieldPosition Pokemon1OldPosition { get; }\n\n\tinternal PBEAutoCenterPacket(PBEBattlePokemon pokemon0, PBEFieldPosition pokemon0OldPosition, PBEBattlePokemon pokemon1, PBEFieldPosition pokemon1OldPosition)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((Pokemon0Trainer = pokemon0.Trainer).Id);\n\t\t\tw.WriteByte(Pokemon0 = pokemon0.Id);\n\t\t\tw.WriteEnum(Pokemon0OldPosition = pokemon0OldPosition);\n\t\t\tw.WriteByte((Pokemon1Trainer = pokemon1.Trainer).Id);\n\t\t\tw.WriteByte(Pokemon1 = pokemon1.Id);\n\t\t\tw.WriteEnum(Pokemon1OldPosition = pokemon1OldPosition);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEAutoCenterPacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tPokemon0Trainer = battle.Trainers[r.ReadByte()];\n\t\tPokemon0 = r.ReadByte();\n\t\tPokemon0OldPosition = r.ReadEnum<PBEFieldPosition>();\n\t\tPokemon1Trainer = battle.Trainers[r.ReadByte()];\n\t\tPokemon1 = r.ReadByte();\n\t\tPokemon1OldPosition = r.ReadEnum<PBEFieldPosition>();\n\t}\n}\npublic sealed class PBEAutoCenterPacket_Hidden0 : IPBEAutoCenterPacket_1\n{\n\tpublic const ushort ID = 0x30;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer Pokemon0Trainer { get; }\n\tpublic PBEFieldPosition Pokemon0OldPosition { get; }\n\tpublic PBETrainer Pokemon1Trainer { get; }\n\tpublic byte Pokemon1 { get; }\n\tpublic PBEFieldPosition Pokemon1OldPosition { get; }\n\n\tpublic PBEAutoCenterPacket_Hidden0(PBEAutoCenterPacket other)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((Pokemon0Trainer = other.Pokemon0Trainer).Id);\n\t\t\tw.WriteEnum(Pokemon0OldPosition = other.Pokemon0OldPosition);\n\t\t\tw.WriteByte((Pokemon1Trainer = other.Pokemon1Trainer).Id);\n\t\t\tw.WriteByte(Pokemon1 = other.Pokemon1);\n\t\t\tw.WriteEnum(Pokemon1OldPosition = other.Pokemon1OldPosition);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEAutoCenterPacket_Hidden0(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tPokemon0Trainer = battle.Trainers[r.ReadByte()];\n\t\tPokemon0OldPosition = r.ReadEnum<PBEFieldPosition>();\n\t\tPokemon1Trainer = battle.Trainers[r.ReadByte()];\n\t\tPokemon1 = r.ReadByte();\n\t\tPokemon1OldPosition = r.ReadEnum<PBEFieldPosition>();\n\t}\n}\npublic sealed class PBEAutoCenterPacket_Hidden1 : IPBEAutoCenterPacket_0\n{\n\tpublic const ushort ID = 0x31;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer Pokemon0Trainer { get; }\n\tpublic byte Pokemon0 { get; }\n\tpublic PBEFieldPosition Pokemon0OldPosition { get; }\n\tpublic PBETrainer Pokemon1Trainer { get; }\n\tpublic PBEFieldPosition Pokemon1OldPosition { get; }\n\n\tpublic PBEAutoCenterPacket_Hidden1(PBEAutoCenterPacket other)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((Pokemon0Trainer = other.Pokemon0Trainer).Id);\n\t\t\tw.WriteByte(Pokemon0 = other.Pokemon0);\n\t\t\tw.WriteEnum(Pokemon0OldPosition = other.Pokemon0OldPosition);\n\t\t\tw.WriteByte((Pokemon1Trainer = other.Pokemon1Trainer).Id);\n\t\t\tw.WriteEnum(Pokemon1OldPosition = other.Pokemon1OldPosition);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEAutoCenterPacket_Hidden1(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tPokemon0Trainer = battle.Trainers[r.ReadByte()];\n\t\tPokemon0 = r.ReadByte();\n\t\tPokemon0OldPosition = r.ReadEnum<PBEFieldPosition>();\n\t\tPokemon1Trainer = battle.Trainers[r.ReadByte()];\n\t\tPokemon1OldPosition = r.ReadEnum<PBEFieldPosition>();\n\t}\n}\npublic sealed class PBEAutoCenterPacket_Hidden01 : IPBEAutoCenterPacket\n{\n\tpublic const ushort ID = 0x32;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer Pokemon0Trainer { get; }\n\tpublic PBEFieldPosition Pokemon0OldPosition { get; }\n\tpublic PBETrainer Pokemon1Trainer { get; }\n\tpublic PBEFieldPosition Pokemon1OldPosition { get; }\n\n\tpublic PBEAutoCenterPacket_Hidden01(PBEAutoCenterPacket other)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((Pokemon0Trainer = other.Pokemon0Trainer).Id);\n\t\t\tw.WriteEnum(Pokemon0OldPosition = other.Pokemon0OldPosition);\n\t\t\tw.WriteByte((Pokemon1Trainer = other.Pokemon1Trainer).Id);\n\t\t\tw.WriteEnum(Pokemon1OldPosition = other.Pokemon1OldPosition);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEAutoCenterPacket_Hidden01(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tPokemon0Trainer = battle.Trainers[r.ReadByte()];\n\t\tPokemon0OldPosition = r.ReadEnum<PBEFieldPosition>();\n\t\tPokemon1Trainer = battle.Trainers[r.ReadByte()];\n\t\tPokemon1OldPosition = r.ReadEnum<PBEFieldPosition>();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/BattlePacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Utils;\nusing System.Collections.ObjectModel;\nusing System.IO;\nusing System.Linq;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEBattlePacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x05;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic sealed class PBETeamInfo\n\t{\n\t\tpublic sealed class PBETrainerInfo\n\t\t{\n\t\t\tpublic sealed class PBEBattlePokemonInfo // SleepTurns would be too much info for a client to have\n\t\t\t{\n\t\t\t\tpublic byte Id { get; }\n\t\t\t\tpublic PBESpecies Species { get; }\n\t\t\t\tpublic PBEForm Form { get; }\n\t\t\t\tpublic string Nickname { get; }\n\t\t\t\tpublic byte Level { get; }\n\t\t\t\tpublic uint EXP { get; }\n\t\t\t\tpublic bool Pokerus { get; }\n\t\t\t\tpublic byte Friendship { get; }\n\t\t\t\tpublic bool Shiny { get; }\n\t\t\t\tpublic PBEAbility Ability { get; }\n\t\t\t\tpublic PBENature Nature { get; }\n\t\t\t\tpublic PBEGender Gender { get; }\n\t\t\t\tpublic PBEItem Item { get; }\n\t\t\t\tpublic PBEItem CaughtBall { get; }\n\t\t\t\tpublic PBEStatus1 Status1 { get; }\n\t\t\t\tpublic PBEReadOnlyStatCollection EffortValues { get; }\n\t\t\t\tpublic PBEReadOnlyStatCollection IndividualValues { get; }\n\t\t\t\tpublic PBEReadOnlyPartyMoveset Moveset { get; }\n\n\t\t\t\tinternal PBEBattlePokemonInfo(PBEBattlePokemon pkmn)\n\t\t\t\t{\n\t\t\t\t\tId = pkmn.Id;\n\t\t\t\t\tSpecies = pkmn.OriginalSpecies;\n\t\t\t\t\tForm = pkmn.OriginalForm;\n\t\t\t\t\tNickname = pkmn.Nickname;\n\t\t\t\t\tLevel = pkmn.OriginalLevel;\n\t\t\t\t\tEXP = pkmn.OriginalEXP;\n\t\t\t\t\tFriendship = pkmn.Friendship;\n\t\t\t\t\tShiny = pkmn.Shiny;\n\t\t\t\t\tPokerus = pkmn.Pokerus;\n\t\t\t\t\tAbility = pkmn.OriginalAbility;\n\t\t\t\t\tNature = pkmn.Nature;\n\t\t\t\t\tGender = pkmn.Gender;\n\t\t\t\t\tItem = pkmn.OriginalItem;\n\t\t\t\t\tCaughtBall = pkmn.OriginalCaughtBall;\n\t\t\t\t\tStatus1 = pkmn.OriginalStatus1;\n\t\t\t\t\tEffortValues = pkmn.OriginalEffortValues!;\n\t\t\t\t\tIndividualValues = pkmn.IndividualValues!;\n\t\t\t\t\tMoveset = pkmn.OriginalMoveset!;\n\t\t\t\t}\n\t\t\t\tinternal PBEBattlePokemonInfo(EndianBinaryReader r)\n\t\t\t\t{\n\t\t\t\t\tId = r.ReadByte();\n\t\t\t\t\tSpecies = r.ReadEnum<PBESpecies>();\n\t\t\t\t\tForm = r.ReadEnum<PBEForm>();\n\t\t\t\t\tNickname = r.ReadString_NullTerminated();\n\t\t\t\t\tLevel = r.ReadByte();\n\t\t\t\t\tEXP = r.ReadUInt32();\n\t\t\t\t\tFriendship = r.ReadByte();\n\t\t\t\t\tShiny = r.ReadBoolean();\n\t\t\t\t\tPokerus = r.ReadBoolean();\n\t\t\t\t\tAbility = r.ReadEnum<PBEAbility>();\n\t\t\t\t\tNature = r.ReadEnum<PBENature>();\n\t\t\t\t\tGender = r.ReadEnum<PBEGender>();\n\t\t\t\t\tItem = r.ReadEnum<PBEItem>();\n\t\t\t\t\tCaughtBall = r.ReadEnum<PBEItem>();\n\t\t\t\t\tStatus1 = r.ReadEnum<PBEStatus1>();\n\t\t\t\t\tEffortValues = new PBEReadOnlyStatCollection(r);\n\t\t\t\t\tIndividualValues = new PBEReadOnlyStatCollection(r);\n\t\t\t\t\tMoveset = new PBEReadOnlyPartyMoveset(r);\n\t\t\t\t}\n\n\t\t\t\tinternal void ToBytes(EndianBinaryWriter w)\n\t\t\t\t{\n\t\t\t\t\tw.WriteByte(Id);\n\t\t\t\t\tw.WriteEnum(Species);\n\t\t\t\t\tw.WriteEnum(Form);\n\t\t\t\t\tw.WriteChars_NullTerminated(Nickname);\n\t\t\t\t\tw.WriteByte(Level);\n\t\t\t\t\tw.WriteUInt32(EXP);\n\t\t\t\t\tw.WriteByte(Friendship);\n\t\t\t\t\tw.WriteBoolean(Shiny);\n\t\t\t\t\tw.WriteBoolean(Pokerus);\n\t\t\t\t\tw.WriteEnum(Ability);\n\t\t\t\t\tw.WriteEnum(Nature);\n\t\t\t\t\tw.WriteEnum(Gender);\n\t\t\t\t\tw.WriteEnum(Item);\n\t\t\t\t\tw.WriteEnum(CaughtBall);\n\t\t\t\t\tw.WriteEnum(Status1);\n\t\t\t\t\tEffortValues.ToBytes(w);\n\t\t\t\t\tIndividualValues.ToBytes(w);\n\t\t\t\t\tMoveset.ToBytes(w);\n\t\t\t\t}\n\t\t\t}\n\t\t\tpublic sealed class PBEInventorySlotInfo\n\t\t\t{\n\t\t\t\tpublic PBEItem Item { get; }\n\t\t\t\tpublic uint Quantity { get; }\n\n\t\t\t\tinternal PBEInventorySlotInfo(PBEBattleInventory.PBEBattleInventorySlot slot)\n\t\t\t\t{\n\t\t\t\t\tItem = slot.Item;\n\t\t\t\t\tQuantity = slot.Quantity;\n\t\t\t\t}\n\t\t\t\tinternal PBEInventorySlotInfo(EndianBinaryReader r)\n\t\t\t\t{\n\t\t\t\t\tItem = r.ReadEnum<PBEItem>();\n\t\t\t\t\tQuantity = r.ReadUInt32();\n\t\t\t\t}\n\n\t\t\t\tinternal void ToBytes(EndianBinaryWriter w)\n\t\t\t\t{\n\t\t\t\t\tw.WriteEnum(Item);\n\t\t\t\t\tw.WriteUInt32(Quantity);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic byte Id { get; }\n\t\t\tpublic string Name { get; }\n\t\t\tpublic ReadOnlyCollection<PBEInventorySlotInfo> Inventory { get; }\n\t\t\tpublic ReadOnlyCollection<PBEBattlePokemonInfo> Party { get; }\n\n\t\t\tinternal PBETrainerInfo(PBETrainer trainer)\n\t\t\t{\n\t\t\t\tId = trainer.Id;\n\t\t\t\tif (trainer.IsWild)\n\t\t\t\t{\n\t\t\t\t\tName = string.Empty;\n\t\t\t\t\tInventory = PBEEmptyReadOnlyCollection<PBEInventorySlotInfo>.Value;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tName = trainer.Name;\n\t\t\t\t\tInventory = trainer.Inventory.Count == 0\n\t\t\t\t\t\t? PBEEmptyReadOnlyCollection<PBEInventorySlotInfo>.Value\n\t\t\t\t\t\t: new ReadOnlyCollection<PBEInventorySlotInfo>(trainer.Inventory.Values.Select(s => new PBEInventorySlotInfo(s)).ToArray());\n\t\t\t\t}\n\t\t\t\tParty = new ReadOnlyCollection<PBEBattlePokemonInfo>(trainer.Party.Select(p => new PBEBattlePokemonInfo(p)).ToArray());\n\t\t\t}\n\t\t\tinternal PBETrainerInfo(EndianBinaryReader r)\n\t\t\t{\n\t\t\t\tId = r.ReadByte();\n\t\t\t\tName = r.ReadString_NullTerminated();\n\t\t\t\tint count = r.ReadUInt16();\n\t\t\t\tif (count == 0)\n\t\t\t\t{\n\t\t\t\t\tInventory = PBEEmptyReadOnlyCollection<PBEInventorySlotInfo>.Value;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tvar inv = new PBEInventorySlotInfo[count];\n\t\t\t\t\tfor (int i = 0; i < count; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tinv[i] = new PBEInventorySlotInfo(r);\n\t\t\t\t\t}\n\t\t\t\t\tInventory = new ReadOnlyCollection<PBEInventorySlotInfo>(inv);\n\t\t\t\t}\n\t\t\t\tcount = r.ReadByte();\n\t\t\t\tif (count == 0)\n\t\t\t\t{\n\t\t\t\t\tParty = PBEEmptyReadOnlyCollection<PBEBattlePokemonInfo>.Value;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tvar party = new PBEBattlePokemonInfo[count];\n\t\t\t\t\tfor (int i = 0; i < count; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tparty[i] = new PBEBattlePokemonInfo(r);\n\t\t\t\t\t}\n\t\t\t\t\tParty = new ReadOnlyCollection<PBEBattlePokemonInfo>(party);\n\t\t\t\t}\n\t\t\t}\n\t\t\tinternal PBETrainerInfo(PBETrainerInfo other, byte? onlyForTrainer)\n\t\t\t{\n\t\t\t\tId = other.Id;\n\t\t\t\tName = other.Name;\n\t\t\t\tif (onlyForTrainer is not null && onlyForTrainer.Value == Id)\n\t\t\t\t{\n\t\t\t\t\tInventory = other.Inventory;\n\t\t\t\t\tParty = other.Party;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tInventory = PBEEmptyReadOnlyCollection<PBEInventorySlotInfo>.Value;\n\t\t\t\t\tParty = PBEEmptyReadOnlyCollection<PBEBattlePokemonInfo>.Value;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tinternal void ToBytes(EndianBinaryWriter w)\n\t\t\t{\n\t\t\t\tw.WriteByte(Id);\n\t\t\t\tw.WriteChars_NullTerminated(Name);\n\t\t\t\tushort icount = (ushort)Inventory.Count;\n\t\t\t\tw.WriteUInt16(icount);\n\t\t\t\tfor (int i = 0; i < icount; i++)\n\t\t\t\t{\n\t\t\t\t\tInventory[i].ToBytes(w);\n\t\t\t\t}\n\t\t\t\tbyte pcount = (byte)Party.Count;\n\t\t\t\tw.WriteByte(pcount);\n\t\t\t\tfor (int i = 0; i < pcount; i++)\n\t\t\t\t{\n\t\t\t\t\tParty[i].ToBytes(w);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic byte Id { get; }\n\t\tpublic ReadOnlyCollection<PBETrainerInfo> Trainers { get; }\n\n\t\tinternal PBETeamInfo(PBETeam team)\n\t\t{\n\t\t\tId = team.Id;\n\t\t\tTrainers = new ReadOnlyCollection<PBETrainerInfo>(team.Trainers.Select(t => new PBETrainerInfo(t)).ToArray());\n\t\t}\n\t\tinternal PBETeamInfo(EndianBinaryReader r)\n\t\t{\n\t\t\tId = r.ReadByte();\n\t\t\tvar trainers = new PBETrainerInfo[r.ReadByte()];\n\t\t\tfor (int i = 0; i < trainers.Length; i++)\n\t\t\t{\n\t\t\t\ttrainers[i] = new PBETrainerInfo(r);\n\t\t\t}\n\t\t\tTrainers = new ReadOnlyCollection<PBETrainerInfo>(trainers);\n\t\t}\n\t\tinternal PBETeamInfo(PBETeamInfo other, byte? onlyForTrainer)\n\t\t{\n\t\t\tId = other.Id;\n\t\t\tTrainers = new ReadOnlyCollection<PBETrainerInfo>(other.Trainers.Select(t => new PBETrainerInfo(t, onlyForTrainer)).ToArray());\n\t\t}\n\n\t\tinternal void ToBytes(EndianBinaryWriter w)\n\t\t{\n\t\t\tw.WriteByte(Id);\n\t\t\tbyte count = (byte)Trainers.Count;\n\t\t\tw.WriteByte(count);\n\t\t\tfor (int i = 0; i < count; i++)\n\t\t\t{\n\t\t\t\tTrainers[i].ToBytes(w);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic PBEBattleType BattleType { get; }\n\tpublic PBEBattleFormat BattleFormat { get; }\n\tpublic PBEBattleTerrain BattleTerrain { get; }\n\tpublic PBEWeather Weather { get; }\n\tpublic PBESettings Settings { get; }\n\tpublic ReadOnlyCollection<PBETeamInfo> Teams { get; }\n\n\tinternal PBEBattlePacket(PBEBattle battle)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteEnum(BattleType = battle.BattleType);\n\t\t\tw.WriteEnum(BattleFormat = battle.BattleFormat);\n\t\t\tw.WriteEnum(BattleTerrain = battle.BattleTerrain);\n\t\t\tw.WriteEnum(Weather = battle.Weather);\n\t\t\tw.WriteBytes((Settings = battle.Settings).ToBytes());\n\t\t\tTeams = new ReadOnlyCollection<PBETeamInfo>(battle.Teams.Select(t => new PBETeamInfo(t)).ToArray());\n\t\t\tfor (int i = 0; i < 2; i++)\n\t\t\t{\n\t\t\t\tTeams[i].ToBytes(w);\n\t\t\t}\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEBattlePacket(byte[] data, EndianBinaryReader r)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tBattleType = r.ReadEnum<PBEBattleType>();\n\t\tBattleFormat = r.ReadEnum<PBEBattleFormat>();\n\t\tBattleTerrain = r.ReadEnum<PBEBattleTerrain>();\n\t\tWeather = r.ReadEnum<PBEWeather>();\n\t\tSettings = new PBESettings(r);\n\t\tSettings.MakeReadOnly();\n\t\tvar teams = new PBETeamInfo[2];\n\t\tfor (int i = 0; i < 2; i++)\n\t\t{\n\t\t\tteams[i] = new PBETeamInfo(r);\n\t\t}\n\t\tTeams = new ReadOnlyCollection<PBETeamInfo>(teams);\n\t}\n\tpublic PBEBattlePacket(PBEBattlePacket other, byte? onlyForTrainer)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteEnum(BattleType = other.BattleType);\n\t\t\tw.WriteEnum(BattleFormat = other.BattleFormat);\n\t\t\tw.WriteEnum(BattleTerrain = other.BattleTerrain);\n\t\t\tw.WriteEnum(Weather = other.Weather);\n\t\t\tw.WriteBytes((Settings = other.Settings).ToBytes());\n\t\t\tTeams = new ReadOnlyCollection<PBETeamInfo>(other.Teams.Select(t => new PBETeamInfo(t, onlyForTrainer)).ToArray());\n\t\t\tfor (int i = 0; i < 2; i++)\n\t\t\t{\n\t\t\t\tTeams[i].ToBytes(w);\n\t\t\t}\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/BattleResultPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEBattleResultPacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x26;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBEBattleResult BattleResult { get; }\n\n\tinternal PBEBattleResultPacket(PBEBattleResult battleResult)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteEnum(BattleResult = battleResult);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEBattleResultPacket(byte[] data, EndianBinaryReader r)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tBattleResult = r.ReadEnum<PBEBattleResult>();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/FleeResponsePacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEFleeResponsePacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x38;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBEFleeResponsePacket()\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEFleeResponsePacket(byte[] data)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/HazePacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEHazePacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x0B;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tinternal PBEHazePacket()\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEHazePacket(byte[] data)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/MatchCancelledPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEMatchCancelledPacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x02;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBEMatchCancelledPacket()\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEMatchCancelledPacket(byte[] data)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/PartyRequestPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEPartyRequestPacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x03;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic byte BattleId { get; }\n\tpublic bool RequireLegal { get; }\n\n\tpublic PBEPartyRequestPacket(byte battleId, bool requireLegal)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte(BattleId = battleId);\n\t\t\tw.WriteBoolean(RequireLegal = requireLegal);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEPartyRequestPacket(byte[] data, EndianBinaryReader r)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tBattleId = r.ReadByte();\n\t\tRequireLegal = r.ReadBoolean();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/PartyResponsePacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Data.Legality;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEPartyResponsePacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x04;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic IPBEPokemonCollection Party { get; }\n\n\tpublic PBEPartyResponsePacket(IPBEPokemonCollection party)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\t(Party = party).ToBytes(w);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEPartyResponsePacket(byte[] data, EndianBinaryReader r)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tParty = new PBEReadOnlyPokemonCollection(r);\n\t}\n}\npublic sealed class PBELegalPartyResponsePacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x2D;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBELegalPokemonCollection Party { get; }\n\n\tpublic PBELegalPartyResponsePacket(PBELegalPokemonCollection party)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteBytes(party.Settings.ToBytes());\n\t\t\t(Party = party).ToBytes(w);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBELegalPartyResponsePacket(byte[] data, EndianBinaryReader r)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tvar s = new PBESettings(r);\n\t\ts.MakeReadOnly();\n\t\tParty = new PBELegalPokemonCollection(s, r);\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/PlayerJoinedPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing System;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEPlayerJoinedPacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x01;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic string TrainerName { get; }\n\n\tpublic PBEPlayerJoinedPacket(string trainerName)\n\t{\n\t\tif (string.IsNullOrWhiteSpace(trainerName))\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(trainerName));\n\t\t}\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteChars_NullTerminated(TrainerName = trainerName);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEPlayerJoinedPacket(byte[] data, EndianBinaryReader r)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tTrainerName = r.ReadString_NullTerminated();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/ResponsePacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEResponsePacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x00;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBEResponsePacket()\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEResponsePacket(byte[] data)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/SwitchInRequestPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBESwitchInRequestPacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x23;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer Trainer { get; }\n\tpublic byte Amount { get; }\n\n\tinternal PBESwitchInRequestPacket(PBETrainer trainer)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((Trainer = trainer).Id);\n\t\t\tw.WriteByte(Amount = trainer.SwitchInsRequired);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBESwitchInRequestPacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tTrainer = battle.Trainers[r.ReadByte()];\n\t\tAmount = r.ReadByte();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/SwitchInResponsePacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBESwitchInResponsePacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x24;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic ReadOnlyCollection<PBESwitchIn> Switches { get; }\n\n\tpublic PBESwitchInResponsePacket(IList<PBESwitchIn> switches)\n\t{\n\t\tif (switches.Count == 0)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(switches));\n\t\t}\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tbyte count = (byte)(Switches = new ReadOnlyCollection<PBESwitchIn>(switches)).Count;\n\t\t\tw.WriteByte(count);\n\t\t\tfor (int i = 0; i < count; i++)\n\t\t\t{\n\t\t\t\tSwitches[i].ToBytes(w);\n\t\t\t}\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBESwitchInResponsePacket(byte[] data, EndianBinaryReader r)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tvar switches = new PBESwitchIn[r.ReadByte()];\n\t\tfor (int i = 0; i < switches.Length; i++)\n\t\t{\n\t\t\tswitches[i] = new PBESwitchIn(r);\n\t\t}\n\t\tSwitches = new ReadOnlyCollection<PBESwitchIn>(switches);\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/TurnBeganPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBETurnBeganPacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x27;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic ushort TurnNumber { get; }\n\n\tinternal PBETurnBeganPacket(ushort turnNumber)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteUInt16(TurnNumber = turnNumber);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBETurnBeganPacket(byte[] data, EndianBinaryReader r)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tTurnNumber = r.ReadUInt16();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_AbilityPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEAbilityPacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x19;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer AbilityOwnerTrainer { get; }\n\tpublic PBEFieldPosition AbilityOwner { get; }\n\tpublic PBETrainer Pokemon2Trainer { get; }\n\tpublic PBEFieldPosition Pokemon2 { get; }\n\tpublic PBEAbility Ability { get; }\n\tpublic PBEAbilityAction AbilityAction { get; }\n\n\tinternal PBEAbilityPacket(PBEBattlePokemon abilityOwner, PBEBattlePokemon pokemon2, PBEAbility ability, PBEAbilityAction abilityAction)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((AbilityOwnerTrainer = abilityOwner.Trainer).Id);\n\t\t\tw.WriteEnum(AbilityOwner = abilityOwner.FieldPosition);\n\t\t\tw.WriteByte((Pokemon2Trainer = pokemon2.Trainer).Id);\n\t\t\tw.WriteEnum(Pokemon2 = pokemon2.FieldPosition);\n\t\t\tw.WriteEnum(Ability = ability);\n\t\t\tw.WriteEnum(AbilityAction = abilityAction);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEAbilityPacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tAbilityOwnerTrainer = battle.Trainers[r.ReadByte()];\n\t\tAbilityOwner = r.ReadEnum<PBEFieldPosition>();\n\t\tPokemon2Trainer = battle.Trainers[r.ReadByte()];\n\t\tPokemon2 = r.ReadEnum<PBEFieldPosition>();\n\t\tAbility = r.ReadEnum<PBEAbility>();\n\t\tAbilityAction = r.ReadEnum<PBEAbilityAction>();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_AbilityReplacedPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEAbilityReplacedPacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x2C;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer AbilityOwnerTrainer { get; }\n\tpublic PBEFieldPosition AbilityOwner { get; }\n\tpublic PBEAbility? OldAbility { get; }\n\tpublic PBEAbility NewAbility { get; }\n\n\tinternal PBEAbilityReplacedPacket(PBEBattlePokemon abilityOwner, PBEAbility? oldAbility, PBEAbility newAbility)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((AbilityOwnerTrainer = abilityOwner.Trainer).Id);\n\t\t\tw.WriteEnum(AbilityOwner = abilityOwner.FieldPosition);\n\t\t\tw.WriteBoolean(oldAbility is not null);\n\t\t\tif (oldAbility is not null)\n\t\t\t{\n\t\t\t\tw.WriteEnum((OldAbility = oldAbility).Value);\n\t\t\t}\n\t\t\tw.WriteEnum(NewAbility = newAbility);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEAbilityReplacedPacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tAbilityOwnerTrainer = battle.Trainers[r.ReadByte()];\n\t\tAbilityOwner = r.ReadEnum<PBEFieldPosition>();\n\t\tif (r.ReadBoolean())\n\t\t{\n\t\t\tOldAbility = r.ReadEnum<PBEAbility>();\n\t\t}\n\t\tNewAbility = r.ReadEnum<PBEAbility>();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_BattleStatusPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEBattleStatusPacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x21;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBEBattleStatus BattleStatus { get; }\n\tpublic PBEBattleStatusAction BattleStatusAction { get; }\n\n\tinternal PBEBattleStatusPacket(PBEBattleStatus battleStatus, PBEBattleStatusAction battleStatusAction)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteEnum(BattleStatus = battleStatus);\n\t\t\tw.WriteEnum(BattleStatusAction = battleStatusAction);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEBattleStatusPacket(byte[] data, EndianBinaryReader r)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tBattleStatus = r.ReadEnum<PBEBattleStatus>();\n\t\tBattleStatusAction = r.ReadEnum<PBEBattleStatusAction>();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_CapturePacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBECapturePacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x3B;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer PokemonTrainer { get; }\n\tpublic PBEFieldPosition Pokemon { get; }\n\tpublic PBEItem Ball { get; }\n\tpublic byte NumShakes { get; }\n\tpublic bool Success { get; }\n\tpublic bool Critical { get; }\n\n\tinternal PBECapturePacket(PBEBattlePokemon pokemon, PBEItem ball, byte numShakes, bool success, bool critical)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((PokemonTrainer = pokemon.Trainer).Id);\n\t\t\tw.WriteEnum(Pokemon = pokemon.FieldPosition);\n\t\t\tw.WriteEnum(Ball = ball);\n\t\t\tw.WriteByte(NumShakes = numShakes);\n\t\t\tw.WriteBoolean(Success = success);\n\t\t\tw.WriteBoolean(Critical = critical);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBECapturePacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tPokemonTrainer = battle.Trainers[r.ReadByte()];\n\t\tPokemon = r.ReadEnum<PBEFieldPosition>();\n\t\tBall = r.ReadEnum<PBEItem>();\n\t\tNumShakes = r.ReadByte();\n\t\tSuccess = r.ReadBoolean();\n\t\tCritical = r.ReadBoolean();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_FleeFailedPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEFleeFailedPacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x39;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer PokemonTrainer { get; }\n\tpublic PBEFieldPosition Pokemon { get; }\n\n\tinternal PBEFleeFailedPacket(PBEBattlePokemon pokemon)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((PokemonTrainer = pokemon.Trainer).Id);\n\t\t\tw.WriteEnum(Pokemon = pokemon.FieldPosition);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEFleeFailedPacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tPokemonTrainer = battle.Trainers[r.ReadByte()];\n\t\tPokemon = r.ReadEnum<PBEFieldPosition>();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_IllusionPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEIllusionPacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x25;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer PokemonTrainer { get; }\n\tpublic PBEFieldPosition Pokemon { get; }\n\tpublic PBEGender ActualGender { get; }\n\tpublic PBEItem ActualCaughtBall { get; }\n\tpublic bool ActualShiny { get; }\n\tpublic string ActualNickname { get; }\n\tpublic PBESpecies ActualSpecies { get; }\n\tpublic PBEForm ActualForm { get; }\n\tpublic PBEType ActualType1 { get; }\n\tpublic PBEType ActualType2 { get; }\n\tpublic float ActualWeight { get; }\n\n\tinternal PBEIllusionPacket(PBEBattlePokemon pokemon)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((PokemonTrainer = pokemon.Trainer).Id);\n\t\t\tw.WriteEnum(Pokemon = pokemon.FieldPosition);\n\t\t\tw.WriteEnum(ActualGender = pokemon.Gender);\n\t\t\tw.WriteEnum(ActualCaughtBall = pokemon.CaughtBall);\n\t\t\tw.WriteChars_NullTerminated(ActualNickname = pokemon.Nickname);\n\t\t\tw.WriteBoolean(ActualShiny = pokemon.Shiny);\n\t\t\tw.WriteEnum(ActualSpecies = pokemon.Species);\n\t\t\tw.WriteEnum(ActualForm = pokemon.Form);\n\t\t\tw.WriteEnum(ActualType1 = pokemon.Type1);\n\t\t\tw.WriteEnum(ActualType2 = pokemon.Type2);\n\t\t\tw.WriteSingle(ActualWeight = pokemon.Weight);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEIllusionPacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tPokemonTrainer = battle.Trainers[r.ReadByte()];\n\t\tPokemon = r.ReadEnum<PBEFieldPosition>();\n\t\tActualGender = r.ReadEnum<PBEGender>();\n\t\tActualCaughtBall = r.ReadEnum<PBEItem>();\n\t\tActualNickname = r.ReadString_NullTerminated();\n\t\tActualShiny = r.ReadBoolean();\n\t\tActualSpecies = r.ReadEnum<PBESpecies>();\n\t\tActualForm = r.ReadEnum<PBEForm>();\n\t\tActualType1 = r.ReadEnum<PBEType>();\n\t\tActualType2 = r.ReadEnum<PBEType>();\n\t\tActualWeight = r.ReadSingle();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_ItemPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEItemPacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x16;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer ItemHolderTrainer { get; }\n\tpublic PBEFieldPosition ItemHolder { get; }\n\tpublic PBETrainer Pokemon2Trainer { get; }\n\tpublic PBEFieldPosition Pokemon2 { get; }\n\tpublic PBEItem Item { get; }\n\tpublic PBEItemAction ItemAction { get; }\n\n\tinternal PBEItemPacket(PBEBattlePokemon itemHolder, PBEBattlePokemon pokemon2, PBEItem item, PBEItemAction itemAction)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((ItemHolderTrainer = itemHolder.Trainer).Id);\n\t\t\tw.WriteEnum(ItemHolder = itemHolder.FieldPosition);\n\t\t\tw.WriteByte((Pokemon2Trainer = pokemon2.Trainer).Id);\n\t\t\tw.WriteEnum(Pokemon2 = pokemon2.FieldPosition);\n\t\t\tw.WriteEnum(Item = item);\n\t\t\tw.WriteEnum(ItemAction = itemAction);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEItemPacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tItemHolderTrainer = battle.Trainers[r.ReadByte()];\n\t\tItemHolder = r.ReadEnum<PBEFieldPosition>();\n\t\tPokemon2Trainer = battle.Trainers[r.ReadByte()];\n\t\tPokemon2 = r.ReadEnum<PBEFieldPosition>();\n\t\tItem = r.ReadEnum<PBEItem>();\n\t\tItemAction = r.ReadEnum<PBEItemAction>();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_ItemTurnPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEItemTurnPacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x3A;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer ItemUserTrainer { get; }\n\tpublic PBEFieldPosition ItemUser { get; }\n\tpublic PBEItem Item { get; }\n\tpublic PBEItemTurnAction ItemAction { get; }\n\n\tinternal PBEItemTurnPacket(PBEBattlePokemon itemUserHolder, PBEItem item, PBEItemTurnAction itemAction)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((ItemUserTrainer = itemUserHolder.Trainer).Id);\n\t\t\tw.WriteEnum(ItemUser = itemUserHolder.FieldPosition);\n\t\t\tw.WriteEnum(Item = item);\n\t\t\tw.WriteEnum(ItemAction = itemAction);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEItemTurnPacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tItemUserTrainer = battle.Trainers[r.ReadByte()];\n\t\tItemUser = r.ReadEnum<PBEFieldPosition>();\n\t\tItem = r.ReadEnum<PBEItem>();\n\t\tItemAction = r.ReadEnum<PBEItemTurnAction>();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_MoveCritPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEMoveCritPacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x0F;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer VictimTrainer { get; }\n\tpublic PBEFieldPosition Victim { get; }\n\n\tinternal PBEMoveCritPacket(PBEBattlePokemon victim)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((VictimTrainer = victim.Trainer).Id);\n\t\t\tw.WriteEnum(Victim = victim.FieldPosition);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEMoveCritPacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tVictimTrainer = battle.Trainers[r.ReadByte()];\n\t\tVictim = r.ReadEnum<PBEFieldPosition>();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_MoveLockPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEMoveLockPacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x28;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer MoveUserTrainer { get; }\n\tpublic PBEFieldPosition MoveUser { get; }\n\tpublic PBEMoveLockType MoveLockType { get; }\n\tpublic PBEMove LockedMove { get; }\n\tpublic PBETurnTarget? LockedTargets { get; }\n\n\tinternal PBEMoveLockPacket(PBEBattlePokemon moveUser, PBEMoveLockType moveLockType, PBEMove lockedMove, PBETurnTarget? lockedTargets = null)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((MoveUserTrainer = moveUser.Trainer).Id);\n\t\t\tw.WriteEnum(MoveUser = moveUser.FieldPosition);\n\t\t\tw.WriteEnum(MoveLockType = moveLockType);\n\t\t\tw.WriteEnum(LockedMove = lockedMove);\n\t\t\tw.WriteBoolean(lockedTargets is not null);\n\t\t\tif (lockedTargets is not null)\n\t\t\t{\n\t\t\t\tw.WriteEnum((LockedTargets = lockedTargets).Value);\n\t\t\t}\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEMoveLockPacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tMoveUserTrainer = battle.Trainers[r.ReadByte()];\n\t\tMoveUser = r.ReadEnum<PBEFieldPosition>();\n\t\tMoveLockType = r.ReadEnum<PBEMoveLockType>();\n\t\tLockedMove = r.ReadEnum<PBEMove>();\n\t\tif (r.ReadBoolean())\n\t\t{\n\t\t\tLockedTargets = r.ReadEnum<PBETurnTarget>();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_MovePPChangedPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEMovePPChangedPacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x17;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer MoveUserTrainer { get; }\n\tpublic PBEFieldPosition MoveUser { get; }\n\tpublic PBEMove Move { get; }\n\tpublic int AmountReduced { get; }\n\n\tinternal PBEMovePPChangedPacket(PBEBattlePokemon moveUser, PBEMove move, int amountReduced)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((MoveUserTrainer = moveUser.Trainer).Id);\n\t\t\tw.WriteEnum(MoveUser = moveUser.FieldPosition);\n\t\t\tw.WriteEnum(Move = move);\n\t\t\tw.WriteInt32(AmountReduced = amountReduced);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEMovePPChangedPacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tMoveUserTrainer = battle.Trainers[r.ReadByte()];\n\t\tMoveUser = r.ReadEnum<PBEFieldPosition>();\n\t\tMove = r.ReadEnum<PBEMove>();\n\t\tAmountReduced = r.ReadInt32();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_MoveResultPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEMoveResultPacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x15;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer MoveUserTrainer { get; }\n\tpublic PBEFieldPosition MoveUser { get; }\n\tpublic PBETrainer Pokemon2Trainer { get; }\n\tpublic PBEFieldPosition Pokemon2 { get; }\n\tpublic PBEResult Result { get; }\n\n\tinternal PBEMoveResultPacket(PBEBattlePokemon moveUser, PBEBattlePokemon pokemon2, PBEResult result)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((MoveUserTrainer = moveUser.Trainer).Id);\n\t\t\tw.WriteEnum(MoveUser = moveUser.FieldPosition);\n\t\t\tw.WriteByte((Pokemon2Trainer = pokemon2.Trainer).Id);\n\t\t\tw.WriteEnum(Pokemon2 = pokemon2.FieldPosition);\n\t\t\tw.WriteEnum(Result = result);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEMoveResultPacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tMoveUserTrainer = battle.Trainers[r.ReadByte()];\n\t\tMoveUser = r.ReadEnum<PBEFieldPosition>();\n\t\tPokemon2Trainer = battle.Trainers[r.ReadByte()];\n\t\tPokemon2 = r.ReadEnum<PBEFieldPosition>();\n\t\tResult = r.ReadEnum<PBEResult>();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_MoveUsedPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEMoveUsedPacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x09;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer MoveUserTrainer { get; }\n\tpublic PBEFieldPosition MoveUser { get; }\n\tpublic PBEMove Move { get; }\n\tpublic bool Owned { get; }\n\n\tinternal PBEMoveUsedPacket(PBEBattlePokemon moveUser, PBEMove move, bool owned)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((MoveUserTrainer = moveUser.Trainer).Id);\n\t\t\tw.WriteEnum(MoveUser = moveUser.FieldPosition);\n\t\t\tw.WriteEnum(Move = move);\n\t\t\tw.WriteBoolean(Owned = owned);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEMoveUsedPacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tMoveUserTrainer = battle.Trainers[r.ReadByte()];\n\t\tMoveUser = r.ReadEnum<PBEFieldPosition>();\n\t\tMove = r.ReadEnum<PBEMove>();\n\t\tOwned = r.ReadBoolean();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_PkmnEXPChangedPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEPkmnEXPChangedPacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x3D;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer PokemonTrainer { get; }\n\tpublic byte Pokemon { get; }\n\tpublic uint OldEXP { get; }\n\tpublic uint NewEXP { get; }\n\n\tinternal PBEPkmnEXPChangedPacket(PBEBattlePokemon pokemon, uint oldEXP)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((PokemonTrainer = pokemon.Trainer).Id);\n\t\t\tw.WriteByte(Pokemon = pokemon.Id);\n\t\t\tw.WriteUInt32(OldEXP = oldEXP);\n\t\t\tw.WriteUInt32(NewEXP = pokemon.EXP);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEPkmnEXPChangedPacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tPokemonTrainer = battle.Trainers[r.ReadByte()];\n\t\tPokemon = r.ReadByte();\n\t\tOldEXP = r.ReadUInt32();\n\t\tNewEXP = r.ReadUInt32();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_PkmnEXPEarnedPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEPkmnEXPEarnedPacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x3E;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer PokemonTrainer { get; }\n\tpublic byte Pokemon { get; }\n\tpublic uint Earned { get; }\n\n\tinternal PBEPkmnEXPEarnedPacket(PBEBattlePokemon pokemon, uint earned)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((PokemonTrainer = pokemon.Trainer).Id);\n\t\t\tw.WriteByte(Pokemon = pokemon.Id);\n\t\t\tw.WriteUInt32(Earned = earned);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEPkmnEXPEarnedPacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tPokemonTrainer = battle.Trainers[r.ReadByte()];\n\t\tPokemon = r.ReadByte();\n\t\tEarned = r.ReadUInt32();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_PkmnFaintedPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic interface IPBEPkmnFaintedPacket : IPBEPacket\n{\n\tPBETrainer PokemonTrainer { get; }\n\tPBEFieldPosition OldPosition { get; }\n}\npublic sealed class PBEPkmnFaintedPacket : IPBEPkmnFaintedPacket\n{\n\tpublic const ushort ID = 0x0E;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer PokemonTrainer { get; }\n\tpublic byte Pokemon { get; }\n\tpublic PBEFieldPosition OldPosition { get; }\n\n\tinternal PBEPkmnFaintedPacket(PBEBattlePokemon pokemon, PBEFieldPosition oldPosition)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((PokemonTrainer = pokemon.Trainer).Id);\n\t\t\tw.WriteByte(Pokemon = pokemon.Id);\n\t\t\tw.WriteEnum(OldPosition = oldPosition);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEPkmnFaintedPacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tPokemonTrainer = battle.Trainers[r.ReadByte()];\n\t\tPokemon = r.ReadByte();\n\t\tOldPosition = r.ReadEnum<PBEFieldPosition>();\n\t}\n}\npublic sealed class PBEPkmnFaintedPacket_Hidden : IPBEPkmnFaintedPacket\n{\n\tpublic const ushort ID = 0x2F;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer PokemonTrainer { get; }\n\tpublic PBEFieldPosition OldPosition { get; }\n\n\tpublic PBEPkmnFaintedPacket_Hidden(PBEPkmnFaintedPacket other)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((PokemonTrainer = other.PokemonTrainer).Id);\n\t\t\tw.WriteEnum(OldPosition = other.OldPosition);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEPkmnFaintedPacket_Hidden(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tPokemonTrainer = battle.Trainers[r.ReadByte()];\n\t\tOldPosition = r.ReadEnum<PBEFieldPosition>();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_PkmnFormChangedPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic interface IPBEPkmnFormChangedPacket : IPBEPacket\n{\n\tPBETrainer PokemonTrainer { get; }\n\tPBEFieldPosition Pokemon { get; }\n\tfloat NewHPPercentage { get; }\n\tPBEAbility NewKnownAbility { get; }\n\tPBEForm NewForm { get; }\n\tPBEType NewType1 { get; }\n\tPBEType NewType2 { get; }\n\tfloat NewWeight { get; }\n\tbool IsRevertForm { get; }\n}\npublic sealed class PBEPkmnFormChangedPacket : IPBEPkmnFormChangedPacket\n{\n\tpublic const ushort ID = 0x29;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer PokemonTrainer { get; }\n\tpublic PBEFieldPosition Pokemon { get; }\n\tpublic ushort NewHP { get; }\n\tpublic ushort NewMaxHP { get; }\n\tpublic float NewHPPercentage { get; }\n\tpublic ushort NewAttack { get; }\n\tpublic ushort NewDefense { get; }\n\tpublic ushort NewSpAttack { get; }\n\tpublic ushort NewSpDefense { get; }\n\tpublic ushort NewSpeed { get; }\n\tpublic PBEAbility NewAbility { get; }\n\tpublic PBEAbility NewKnownAbility { get; }\n\tpublic PBEForm NewForm { get; }\n\tpublic PBEType NewType1 { get; }\n\tpublic PBEType NewType2 { get; }\n\tpublic float NewWeight { get; }\n\tpublic bool IsRevertForm { get; }\n\n\tinternal PBEPkmnFormChangedPacket(PBEBattlePokemon pokemon, bool isRevertForm)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((PokemonTrainer = pokemon.Trainer).Id);\n\t\t\tw.WriteEnum(Pokemon = pokemon.FieldPosition);\n\t\t\tw.WriteUInt16(NewHP = pokemon.HP);\n\t\t\tw.WriteUInt16(NewMaxHP = pokemon.MaxHP);\n\t\t\tw.WriteSingle(NewHPPercentage = pokemon.HPPercentage);\n\t\t\tw.WriteUInt16(NewAttack = pokemon.Attack);\n\t\t\tw.WriteUInt16(NewDefense = pokemon.Defense);\n\t\t\tw.WriteUInt16(NewSpAttack = pokemon.SpAttack);\n\t\t\tw.WriteUInt16(NewSpDefense = pokemon.SpDefense);\n\t\t\tw.WriteUInt16(NewSpeed = pokemon.Speed);\n\t\t\tw.WriteEnum(NewAbility = pokemon.Ability);\n\t\t\tw.WriteEnum(NewKnownAbility = pokemon.KnownAbility);\n\t\t\tw.WriteEnum(NewForm = pokemon.Form);\n\t\t\tw.WriteEnum(NewType1 = pokemon.Type1);\n\t\t\tw.WriteEnum(NewType2 = pokemon.Type2);\n\t\t\tw.WriteSingle(NewWeight = pokemon.Weight);\n\t\t\tw.WriteBoolean(IsRevertForm = isRevertForm);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEPkmnFormChangedPacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tPokemonTrainer = battle.Trainers[r.ReadByte()];\n\t\tPokemon = r.ReadEnum<PBEFieldPosition>();\n\t\tNewHP = r.ReadUInt16();\n\t\tNewMaxHP = r.ReadUInt16();\n\t\tNewHPPercentage = r.ReadSingle();\n\t\tNewAttack = r.ReadUInt16();\n\t\tNewDefense = r.ReadUInt16();\n\t\tNewSpAttack = r.ReadUInt16();\n\t\tNewSpDefense = r.ReadUInt16();\n\t\tNewSpeed = r.ReadUInt16();\n\t\tNewAbility = r.ReadEnum<PBEAbility>();\n\t\tNewKnownAbility = r.ReadEnum<PBEAbility>();\n\t\tNewForm = r.ReadEnum<PBEForm>();\n\t\tNewType1 = r.ReadEnum<PBEType>();\n\t\tNewType2 = r.ReadEnum<PBEType>();\n\t\tNewWeight = r.ReadSingle();\n\t}\n}\npublic sealed class PBEPkmnFormChangedPacket_Hidden : IPBEPkmnFormChangedPacket\n{\n\tpublic const ushort ID = 0x34;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer PokemonTrainer { get; }\n\tpublic PBEFieldPosition Pokemon { get; }\n\tpublic float NewHPPercentage { get; }\n\tpublic PBEAbility NewKnownAbility { get; }\n\tpublic PBEForm NewForm { get; }\n\tpublic PBEType NewType1 { get; }\n\tpublic PBEType NewType2 { get; }\n\tpublic float NewWeight { get; }\n\tpublic bool IsRevertForm { get; }\n\n\tpublic PBEPkmnFormChangedPacket_Hidden(PBEPkmnFormChangedPacket other)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((PokemonTrainer = other.PokemonTrainer).Id);\n\t\t\tw.WriteEnum(Pokemon = other.Pokemon);\n\t\t\tw.WriteSingle(NewHPPercentage = other.NewHPPercentage);\n\t\t\tw.WriteEnum(NewKnownAbility = other.NewKnownAbility);\n\t\t\tw.WriteEnum(NewForm = other.NewForm);\n\t\t\tw.WriteEnum(NewType1 = other.NewType1);\n\t\t\tw.WriteEnum(NewType2 = other.NewType2);\n\t\t\tw.WriteSingle(NewWeight = other.NewWeight);\n\t\t\tw.WriteBoolean(IsRevertForm = other.IsRevertForm);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEPkmnFormChangedPacket_Hidden(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tPokemonTrainer = battle.Trainers[r.ReadByte()];\n\t\tPokemon = r.ReadEnum<PBEFieldPosition>();\n\t\tNewHPPercentage = r.ReadSingle();\n\t\tNewKnownAbility = r.ReadEnum<PBEAbility>();\n\t\tNewForm = r.ReadEnum<PBEForm>();\n\t\tNewType1 = r.ReadEnum<PBEType>();\n\t\tNewType2 = r.ReadEnum<PBEType>();\n\t\tNewWeight = r.ReadSingle();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_PkmnHPChangedPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic interface IPBEPkmnHPChangedPacket : IPBEPacket\n{\n\tPBETrainer PokemonTrainer { get; }\n\tPBEFieldPosition Pokemon { get; }\n\tfloat OldHPPercentage { get; }\n\tfloat NewHPPercentage { get; }\n}\npublic sealed class PBEPkmnHPChangedPacket : IPBEPkmnHPChangedPacket\n{\n\tpublic const ushort ID = 0x0A;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer PokemonTrainer { get; }\n\tpublic PBEFieldPosition Pokemon { get; }\n\tpublic ushort OldHP { get; }\n\tpublic ushort NewHP { get; }\n\tpublic float OldHPPercentage { get; }\n\tpublic float NewHPPercentage { get; }\n\n\tinternal PBEPkmnHPChangedPacket(PBEBattlePokemon pokemon, ushort oldHP, float oldHPPercentage)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((PokemonTrainer = pokemon.Trainer).Id);\n\t\t\tw.WriteEnum(Pokemon = pokemon.FieldPosition);\n\t\t\tw.WriteUInt16(OldHP = oldHP);\n\t\t\tw.WriteUInt16(NewHP = pokemon.HP);\n\t\t\tw.WriteSingle(OldHPPercentage = oldHPPercentage);\n\t\t\tw.WriteSingle(NewHPPercentage = pokemon.HPPercentage);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEPkmnHPChangedPacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tPokemonTrainer = battle.Trainers[r.ReadByte()];\n\t\tPokemon = r.ReadEnum<PBEFieldPosition>();\n\t\tOldHP = r.ReadUInt16();\n\t\tNewHP = r.ReadUInt16();\n\t\tOldHPPercentage = r.ReadSingle();\n\t\tNewHPPercentage = r.ReadSingle();\n\t}\n}\npublic sealed class PBEPkmnHPChangedPacket_Hidden : IPBEPkmnHPChangedPacket\n{\n\tpublic const ushort ID = 0x35;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer PokemonTrainer { get; }\n\tpublic PBEFieldPosition Pokemon { get; }\n\tpublic float OldHPPercentage { get; }\n\tpublic float NewHPPercentage { get; }\n\n\tpublic PBEPkmnHPChangedPacket_Hidden(PBEPkmnHPChangedPacket other)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((PokemonTrainer = other.PokemonTrainer).Id);\n\t\t\tw.WriteEnum(Pokemon = other.Pokemon);\n\t\t\tw.WriteSingle(OldHPPercentage = other.OldHPPercentage);\n\t\t\tw.WriteSingle(NewHPPercentage = other.NewHPPercentage);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEPkmnHPChangedPacket_Hidden(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tPokemonTrainer = battle.Trainers[r.ReadByte()];\n\t\tPokemon = r.ReadEnum<PBEFieldPosition>();\n\t\tOldHPPercentage = r.ReadSingle();\n\t\tNewHPPercentage = r.ReadSingle();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_PkmnLevelChangedPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEPkmnLevelChangedPacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x3F;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer PokemonTrainer { get; }\n\tpublic byte Pokemon { get; }\n\tpublic byte NewLevel { get; }\n\n\tinternal PBEPkmnLevelChangedPacket(PBEBattlePokemon pokemon)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((PokemonTrainer = pokemon.Trainer).Id);\n\t\t\tw.WriteByte(Pokemon = pokemon.Id);\n\t\t\tw.WriteByte(NewLevel = pokemon.Level);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEPkmnLevelChangedPacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tPokemonTrainer = battle.Trainers[r.ReadByte()];\n\t\tPokemon = r.ReadByte();\n\t\tNewLevel = r.ReadByte();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_PkmnStatChangedPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEPkmnStatChangedPacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x10;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer PokemonTrainer { get; }\n\tpublic PBEFieldPosition Pokemon { get; }\n\tpublic PBEStat Stat { get; }\n\tpublic sbyte OldValue { get; }\n\tpublic sbyte NewValue { get; }\n\n\tinternal PBEPkmnStatChangedPacket(PBEBattlePokemon pokemon, PBEStat stat, sbyte oldValue, sbyte newValue)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((PokemonTrainer = pokemon.Trainer).Id);\n\t\t\tw.WriteEnum(Pokemon = pokemon.FieldPosition);\n\t\t\tw.WriteEnum(Stat = stat);\n\t\t\tw.WriteSByte(OldValue = oldValue);\n\t\t\tw.WriteSByte(NewValue = newValue);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEPkmnStatChangedPacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tPokemonTrainer = battle.Trainers[r.ReadByte()];\n\t\tPokemon = r.ReadEnum<PBEFieldPosition>();\n\t\tStat = r.ReadEnum<PBEStat>();\n\t\tOldValue = r.ReadSByte();\n\t\tNewValue = r.ReadSByte();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_PkmnSwitchInPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic interface IPBEPkmnSwitchInInfo_Hidden : IPBEPkmnAppearedInfo_Hidden\n{\n\tPBEItem CaughtBall { get; }\n}\npublic interface IPBEPkmnSwitchInPacket : IPBEPacket\n{\n\tPBETrainer Trainer { get; }\n\tIReadOnlyList<IPBEPkmnSwitchInInfo_Hidden> SwitchIns { get; }\n\tbool Forced { get; }\n\tPBETrainer? ForcedByPokemonTrainer { get; }\n\tPBEFieldPosition ForcedByPokemon { get; }\n}\npublic sealed class PBEPkmnSwitchInPacket : IPBEPkmnSwitchInPacket\n{\n\tpublic const ushort ID = 0x06;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer Trainer { get; }\n\tpublic ReadOnlyCollection<PBEPkmnAppearedInfo> SwitchIns { get; }\n\tIReadOnlyList<IPBEPkmnSwitchInInfo_Hidden> IPBEPkmnSwitchInPacket.SwitchIns => SwitchIns;\n\tpublic bool Forced { get; }\n\tpublic PBETrainer? ForcedByPokemonTrainer { get; }\n\tpublic PBEFieldPosition ForcedByPokemon { get; }\n\n\tinternal PBEPkmnSwitchInPacket(PBETrainer trainer, IList<PBEPkmnAppearedInfo> switchIns, PBEBattlePokemon? forcedByPokemon = null)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((Trainer = trainer).Id);\n\t\t\tbyte count = (byte)(SwitchIns = new ReadOnlyCollection<PBEPkmnAppearedInfo>(switchIns)).Count;\n\t\t\tw.WriteByte(count);\n\t\t\tfor (int i = 0; i < count; i++)\n\t\t\t{\n\t\t\t\tSwitchIns[i].ToBytes(w);\n\t\t\t}\n\t\t\tw.WriteBoolean(Forced = forcedByPokemon is not null);\n\t\t\tif (forcedByPokemon is not null)\n\t\t\t{\n\t\t\t\tw.WriteByte((ForcedByPokemonTrainer = forcedByPokemon.Trainer).Id);\n\t\t\t\tw.WriteEnum(ForcedByPokemon = forcedByPokemon.FieldPosition);\n\t\t\t}\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEPkmnSwitchInPacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tTrainer = battle.Trainers[r.ReadByte()];\n\t\tvar switches = new PBEPkmnAppearedInfo[r.ReadByte()];\n\t\tfor (int i = 0; i < switches.Length; i++)\n\t\t{\n\t\t\tswitches[i] = new PBEPkmnAppearedInfo(r);\n\t\t}\n\t\tSwitchIns = new ReadOnlyCollection<PBEPkmnAppearedInfo>(switches);\n\t\tForced = r.ReadBoolean();\n\t\tif (Forced)\n\t\t{\n\t\t\tForcedByPokemonTrainer = battle.Trainers[r.ReadByte()];\n\t\t\tForcedByPokemon = r.ReadEnum<PBEFieldPosition>();\n\t\t}\n\t}\n}\npublic sealed class PBEPkmnSwitchInPacket_Hidden : IPBEPkmnSwitchInPacket\n{\n\tpublic const ushort ID = 0x36;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic sealed class PBEPkmnSwitchInInfo : IPBEPkmnSwitchInInfo_Hidden\n\t{\n\t\tpublic PBESpecies Species { get; }\n\t\tpublic PBEForm Form { get; }\n\t\tpublic string Nickname { get; }\n\t\tpublic byte Level { get; }\n\t\tpublic bool Shiny { get; }\n\t\tpublic PBEGender Gender { get; }\n\t\tpublic PBEItem CaughtBall { get; }\n\t\tpublic float HPPercentage { get; }\n\t\tpublic PBEStatus1 Status1 { get; }\n\t\tpublic PBEFieldPosition FieldPosition { get; }\n\n\t\tinternal PBEPkmnSwitchInInfo(PBEPkmnAppearedInfo other)\n\t\t{\n\t\t\tSpecies = other.Species;\n\t\t\tForm = other.Form;\n\t\t\tNickname = other.Nickname;\n\t\t\tLevel = other.Level;\n\t\t\tShiny = other.Shiny;\n\t\t\tGender = other.Gender;\n\t\t\tCaughtBall = other.CaughtBall;\n\t\t\tHPPercentage = other.HPPercentage;\n\t\t\tStatus1 = other.Status1;\n\t\t\tFieldPosition = other.FieldPosition;\n\t\t}\n\t\tinternal PBEPkmnSwitchInInfo(EndianBinaryReader r)\n\t\t{\n\t\t\tSpecies = r.ReadEnum<PBESpecies>();\n\t\t\tForm = r.ReadEnum<PBEForm>();\n\t\t\tNickname = r.ReadString_NullTerminated();\n\t\t\tLevel = r.ReadByte();\n\t\t\tShiny = r.ReadBoolean();\n\t\t\tGender = r.ReadEnum<PBEGender>();\n\t\t\tCaughtBall = r.ReadEnum<PBEItem>();\n\t\t\tHPPercentage = r.ReadSingle();\n\t\t\tStatus1 = r.ReadEnum<PBEStatus1>();\n\t\t\tFieldPosition = r.ReadEnum<PBEFieldPosition>();\n\t\t}\n\n\t\tinternal void ToBytes(EndianBinaryWriter w)\n\t\t{\n\t\t\tw.WriteEnum(Species);\n\t\t\tw.WriteEnum(Form);\n\t\t\tw.WriteChars_NullTerminated(Nickname);\n\t\t\tw.WriteByte(Level);\n\t\t\tw.WriteBoolean(Shiny);\n\t\t\tw.WriteEnum(Gender);\n\t\t\tw.WriteEnum(CaughtBall);\n\t\t\tw.WriteSingle(HPPercentage);\n\t\t\tw.WriteEnum(Status1);\n\t\t\tw.WriteEnum(FieldPosition);\n\t\t}\n\t}\n\n\tpublic PBETrainer Trainer { get; }\n\tpublic ReadOnlyCollection<PBEPkmnSwitchInInfo> SwitchIns { get; }\n\tIReadOnlyList<IPBEPkmnSwitchInInfo_Hidden> IPBEPkmnSwitchInPacket.SwitchIns => SwitchIns;\n\tpublic bool Forced { get; }\n\tpublic PBETrainer? ForcedByPokemonTrainer { get; }\n\tpublic PBEFieldPosition ForcedByPokemon { get; }\n\n\tpublic PBEPkmnSwitchInPacket_Hidden(PBEPkmnSwitchInPacket other)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((Trainer = other.Trainer).Id);\n\t\t\tvar switchIns = new PBEPkmnSwitchInInfo[other.SwitchIns.Count];\n\t\t\tfor (int i = 0; i < switchIns.Length; i++)\n\t\t\t{\n\t\t\t\tswitchIns[i] = new PBEPkmnSwitchInInfo(other.SwitchIns[i]);\n\t\t\t}\n\t\t\tbyte count = (byte)(SwitchIns = new ReadOnlyCollection<PBEPkmnSwitchInInfo>(switchIns)).Count;\n\t\t\tw.WriteByte(count);\n\t\t\tfor (int i = 0; i < count; i++)\n\t\t\t{\n\t\t\t\tSwitchIns[i].ToBytes(w);\n\t\t\t}\n\t\t\tw.WriteBoolean(Forced = other.Forced);\n\t\t\tif (Forced)\n\t\t\t{\n\t\t\t\tw.WriteByte((ForcedByPokemonTrainer = other.ForcedByPokemonTrainer!).Id);\n\t\t\t\tw.WriteEnum(ForcedByPokemon = other.ForcedByPokemon);\n\t\t\t}\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEPkmnSwitchInPacket_Hidden(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tTrainer = battle.Trainers[r.ReadByte()];\n\t\tvar switches = new PBEPkmnSwitchInInfo[r.ReadByte()];\n\t\tfor (int i = 0; i < switches.Length; i++)\n\t\t{\n\t\t\tswitches[i] = new PBEPkmnSwitchInInfo(r);\n\t\t}\n\t\tSwitchIns = new ReadOnlyCollection<PBEPkmnSwitchInInfo>(switches);\n\t\tForced = r.ReadBoolean();\n\t\tif (Forced)\n\t\t{\n\t\t\tForcedByPokemonTrainer = battle.Trainers[r.ReadByte()];\n\t\t\tForcedByPokemon = r.ReadEnum<PBEFieldPosition>();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_PkmnSwitchOutPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic interface IPBEPkmnSwitchOutPacket : IPBEPacket\n{\n\tPBETrainer PokemonTrainer { get; }\n\tPBEFieldPosition OldPosition { get; }\n\tbool Forced { get; }\n\tPBETrainer? ForcedByPokemonTrainer { get; }\n\tPBEFieldPosition ForcedByPokemon { get; }\n}\npublic sealed class PBEPkmnSwitchOutPacket : IPBEPkmnSwitchOutPacket\n{\n\tpublic const ushort ID = 0x0C;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer PokemonTrainer { get; }\n\tpublic byte Pokemon { get; }\n\tpublic PBEFieldPosition OldPosition { get; }\n\tpublic bool Forced { get; }\n\tpublic PBETrainer? ForcedByPokemonTrainer { get; }\n\tpublic PBEFieldPosition ForcedByPokemon { get; }\n\n\tinternal PBEPkmnSwitchOutPacket(PBEBattlePokemon pokemon, PBEFieldPosition oldPosition, PBEBattlePokemon? forcedByPokemon = null)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((PokemonTrainer = pokemon.Trainer).Id);\n\t\t\tw.WriteByte(Pokemon = pokemon.Id);\n\t\t\tw.WriteEnum(OldPosition = oldPosition);\n\t\t\tw.WriteBoolean(Forced = forcedByPokemon is not null);\n\t\t\tif (forcedByPokemon is not null)\n\t\t\t{\n\t\t\t\tw.WriteByte((ForcedByPokemonTrainer = forcedByPokemon.Trainer).Id);\n\t\t\t\tw.WriteEnum(ForcedByPokemon = forcedByPokemon.FieldPosition);\n\t\t\t}\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEPkmnSwitchOutPacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tPokemonTrainer = battle.Trainers[r.ReadByte()];\n\t\tPokemon = r.ReadByte();\n\t\tOldPosition = r.ReadEnum<PBEFieldPosition>();\n\t\tForced = r.ReadBoolean();\n\t\tif (Forced)\n\t\t{\n\t\t\tForcedByPokemonTrainer = battle.Trainers[r.ReadByte()];\n\t\t\tForcedByPokemon = r.ReadEnum<PBEFieldPosition>();\n\t\t}\n\t}\n}\npublic sealed class PBEPkmnSwitchOutPacket_Hidden : IPBEPkmnSwitchOutPacket\n{\n\tpublic const ushort ID = 0x37;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer PokemonTrainer { get; }\n\tpublic PBEFieldPosition OldPosition { get; }\n\tpublic bool Forced { get; }\n\tpublic PBETrainer? ForcedByPokemonTrainer { get; }\n\tpublic PBEFieldPosition ForcedByPokemon { get; }\n\n\tpublic PBEPkmnSwitchOutPacket_Hidden(PBEPkmnSwitchOutPacket other)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((PokemonTrainer = other.PokemonTrainer).Id);\n\t\t\tw.WriteEnum(OldPosition = other.OldPosition);\n\t\t\tw.WriteBoolean(Forced = other.Forced);\n\t\t\tif (Forced)\n\t\t\t{\n\t\t\t\tw.WriteByte((ForcedByPokemonTrainer = other.ForcedByPokemonTrainer!).Id);\n\t\t\t\tw.WriteEnum(ForcedByPokemon = other.ForcedByPokemon);\n\t\t\t}\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEPkmnSwitchOutPacket_Hidden(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tPokemonTrainer = battle.Trainers[r.ReadByte()];\n\t\tOldPosition = r.ReadEnum<PBEFieldPosition>();\n\t\tForced = r.ReadBoolean();\n\t\tif (Forced)\n\t\t{\n\t\t\tForcedByPokemonTrainer = battle.Trainers[r.ReadByte()];\n\t\t\tForcedByPokemon = r.ReadEnum<PBEFieldPosition>();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_PsychUpPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEPsychUpPacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x22;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer UserTrainer { get; }\n\tpublic PBEFieldPosition User { get; }\n\tpublic PBETrainer TargetTrainer { get; }\n\tpublic PBEFieldPosition Target { get; }\n\tpublic sbyte AttackChange { get; }\n\tpublic sbyte DefenseChange { get; }\n\tpublic sbyte SpAttackChange { get; }\n\tpublic sbyte SpDefenseChange { get; }\n\tpublic sbyte SpeedChange { get; }\n\tpublic sbyte AccuracyChange { get; }\n\tpublic sbyte EvasionChange { get; }\n\n\tinternal PBEPsychUpPacket(PBEBattlePokemon user, PBEBattlePokemon target)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((UserTrainer = user.Trainer).Id);\n\t\t\tw.WriteEnum(User = user.FieldPosition);\n\t\t\tw.WriteByte((TargetTrainer = target.Trainer).Id);\n\t\t\tw.WriteEnum(Target = target.FieldPosition);\n\t\t\tw.WriteSByte(AttackChange = target.AttackChange);\n\t\t\tw.WriteSByte(DefenseChange = target.DefenseChange);\n\t\t\tw.WriteSByte(SpAttackChange = target.SpAttackChange);\n\t\t\tw.WriteSByte(SpDefenseChange = target.SpDefenseChange);\n\t\t\tw.WriteSByte(SpeedChange = target.SpeedChange);\n\t\t\tw.WriteSByte(AccuracyChange = target.AccuracyChange);\n\t\t\tw.WriteSByte(EvasionChange = target.EvasionChange);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEPsychUpPacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tUserTrainer = battle.Trainers[r.ReadByte()];\n\t\tUser = r.ReadEnum<PBEFieldPosition>();\n\t\tTargetTrainer = battle.Trainers[r.ReadByte()];\n\t\tTarget = r.ReadEnum<PBEFieldPosition>();\n\t\tAttackChange = r.ReadSByte();\n\t\tDefenseChange = r.ReadSByte();\n\t\tSpAttackChange = r.ReadSByte();\n\t\tSpDefenseChange = r.ReadSByte();\n\t\tSpeedChange = r.ReadSByte();\n\t\tAccuracyChange = r.ReadSByte();\n\t\tEvasionChange = r.ReadSByte();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_ReflectTypePacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEReflectTypePacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x2E;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer UserTrainer { get; }\n\tpublic PBEFieldPosition User { get; }\n\tpublic PBETrainer TargetTrainer { get; }\n\tpublic PBEFieldPosition Target { get; }\n\tpublic PBEType Type1 { get; }\n\tpublic PBEType Type2 { get; }\n\n\tinternal PBEReflectTypePacket(PBEBattlePokemon user, PBEBattlePokemon target)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((UserTrainer = user.Trainer).Id);\n\t\t\tw.WriteEnum(User = user.FieldPosition);\n\t\t\tw.WriteByte((TargetTrainer = target.Trainer).Id);\n\t\t\tw.WriteEnum(Target = target.FieldPosition);\n\t\t\tw.WriteEnum(Type1 = target.Type1);\n\t\t\tw.WriteEnum(Type2 = target.Type2);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEReflectTypePacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tUserTrainer = battle.Trainers[r.ReadByte()];\n\t\tUser = r.ReadEnum<PBEFieldPosition>();\n\t\tTargetTrainer = battle.Trainers[r.ReadByte()];\n\t\tTarget = r.ReadEnum<PBEFieldPosition>();\n\t\tType1 = r.ReadEnum<PBEType>();\n\t\tType2 = r.ReadEnum<PBEType>();\n\t}\n}\npublic sealed class PBEReflectTypePacket_Hidden : IPBEPacket\n{\n\tpublic const ushort ID = 0x33;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer UserTrainer { get; }\n\tpublic PBEFieldPosition User { get; }\n\tpublic PBETrainer TargetTrainer { get; }\n\tpublic PBEFieldPosition Target { get; }\n\n\tpublic PBEReflectTypePacket_Hidden(PBEReflectTypePacket other)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((UserTrainer = other.UserTrainer).Id);\n\t\t\tw.WriteEnum(User = other.User);\n\t\t\tw.WriteByte((TargetTrainer = other.TargetTrainer).Id);\n\t\t\tw.WriteEnum(Target = other.Target);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEReflectTypePacket_Hidden(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tUserTrainer = battle.Trainers[r.ReadByte()];\n\t\tUser = r.ReadEnum<PBEFieldPosition>();\n\t\tTargetTrainer = battle.Trainers[r.ReadByte()];\n\t\tTarget = r.ReadEnum<PBEFieldPosition>();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_SpecialMessagePacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBESpecialMessagePacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x20;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBESpecialMessage Message { get; }\n\tpublic ReadOnlyCollection<object> Params { get; }\n\n\tinternal PBESpecialMessagePacket(PBESpecialMessage message, params object[] parameters)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteEnum(Message = message);\n\t\t\tvar par = new List<object>();\n\t\t\tswitch (Message)\n\t\t\t{\n\t\t\t\tcase PBESpecialMessage.DraggedOut:\n\t\t\t\tcase PBESpecialMessage.Endure:\n\t\t\t\tcase PBESpecialMessage.HPDrained:\n\t\t\t\tcase PBESpecialMessage.Recoil:\n\t\t\t\tcase PBESpecialMessage.Struggle:\n\t\t\t\t{\n\t\t\t\t\tvar p0 = (PBEBattlePokemon)parameters[0];\n\t\t\t\t\tpar.Add(p0.Trainer);\n\t\t\t\t\tpar.Add(p0.FieldPosition);\n\t\t\t\t\tw.WriteByte(p0.Trainer.Id);\n\t\t\t\t\tw.WriteEnum(p0.FieldPosition);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBESpecialMessage.Magnitude:\n\t\t\t\tcase PBESpecialMessage.MultiHit:\n\t\t\t\t{\n\t\t\t\t\tbyte p0 = (byte)parameters[0];\n\t\t\t\t\tpar.Add(p0);\n\t\t\t\t\tw.WriteByte(p0);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBESpecialMessage.PainSplit:\n\t\t\t\t{\n\t\t\t\t\tvar p0 = (PBEBattlePokemon)parameters[0];\n\t\t\t\t\tvar p1 = (PBEBattlePokemon)parameters[1];\n\t\t\t\t\tpar.Add(p0.Trainer);\n\t\t\t\t\tpar.Add(p0.FieldPosition);\n\t\t\t\t\tpar.Add(p1.Trainer);\n\t\t\t\t\tpar.Add(p1.FieldPosition);\n\t\t\t\t\tw.WriteByte(p1.Trainer.Id);\n\t\t\t\t\tw.WriteEnum(p1.FieldPosition);\n\t\t\t\t\tw.WriteByte(p1.Trainer.Id);\n\t\t\t\t\tw.WriteEnum(p1.FieldPosition);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tParams = new ReadOnlyCollection<object>(par);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBESpecialMessagePacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tMessage = r.ReadEnum<PBESpecialMessage>();\n\t\tswitch (Message)\n\t\t{\n\t\t\tcase PBESpecialMessage.DraggedOut:\n\t\t\tcase PBESpecialMessage.Endure:\n\t\t\tcase PBESpecialMessage.HPDrained:\n\t\t\tcase PBESpecialMessage.Recoil:\n\t\t\tcase PBESpecialMessage.Struggle:\n\t\t\t{\n\t\t\t\tParams = new ReadOnlyCollection<object>(new object[] { battle.Trainers[r.ReadByte()], r.ReadEnum<PBEFieldPosition>() });\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBESpecialMessage.Magnitude:\n\t\t\tcase PBESpecialMessage.MultiHit:\n\t\t\t{\n\t\t\t\tParams = new ReadOnlyCollection<object>(new object[] { r.ReadByte() });\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBESpecialMessage.NothingHappened:\n\t\t\tcase PBESpecialMessage.OneHitKnockout:\n\t\t\tcase PBESpecialMessage.PayDay:\n\t\t\t{\n\t\t\t\tParams = new ReadOnlyCollection<object>(Array.Empty<object>());\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBESpecialMessage.PainSplit:\n\t\t\t{\n\t\t\t\tParams = new ReadOnlyCollection<object>(new object[] { battle.Trainers[r.ReadByte()], r.ReadEnum<PBEFieldPosition>(), battle.Trainers[r.ReadByte()], r.ReadEnum<PBEFieldPosition>() });\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: throw new InvalidDataException();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_Status1Packet.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEStatus1Packet : IPBEPacket\n{\n\tpublic const ushort ID = 0x11;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer Status1ReceiverTrainer { get; }\n\tpublic PBEFieldPosition Status1Receiver { get; }\n\tpublic PBETrainer Pokemon2Trainer { get; }\n\tpublic PBEFieldPosition Pokemon2 { get; }\n\tpublic PBEStatus1 Status1 { get; }\n\tpublic PBEStatusAction StatusAction { get; }\n\n\tinternal PBEStatus1Packet(PBEBattlePokemon status1Receiver, PBEBattlePokemon pokemon2, PBEStatus1 status1, PBEStatusAction statusAction)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((Status1ReceiverTrainer = status1Receiver.Trainer).Id);\n\t\t\tw.WriteEnum(Status1Receiver = status1Receiver.FieldPosition);\n\t\t\tw.WriteByte((Pokemon2Trainer = pokemon2.Trainer).Id);\n\t\t\tw.WriteEnum(Pokemon2 = pokemon2.FieldPosition);\n\t\t\tw.WriteEnum(Status1 = status1);\n\t\t\tw.WriteEnum(StatusAction = statusAction);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEStatus1Packet(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tStatus1ReceiverTrainer = battle.Trainers[r.ReadByte()];\n\t\tStatus1Receiver = r.ReadEnum<PBEFieldPosition>();\n\t\tPokemon2Trainer = battle.Trainers[r.ReadByte()];\n\t\tPokemon2 = r.ReadEnum<PBEFieldPosition>();\n\t\tStatus1 = r.ReadEnum<PBEStatus1>();\n\t\tStatusAction = r.ReadEnum<PBEStatusAction>();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_Status2Packet.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEStatus2Packet : IPBEPacket\n{\n\tpublic const ushort ID = 0x12;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer Status2ReceiverTrainer { get; }\n\tpublic PBEFieldPosition Status2Receiver { get; }\n\tpublic PBETrainer Pokemon2Trainer { get; }\n\tpublic PBEFieldPosition Pokemon2 { get; }\n\tpublic PBEStatus2 Status2 { get; }\n\tpublic PBEStatusAction StatusAction { get; }\n\n\tinternal PBEStatus2Packet(PBEBattlePokemon status2Receiver, PBEBattlePokemon pokemon2, PBEStatus2 status2, PBEStatusAction statusAction)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((Status2ReceiverTrainer = status2Receiver.Trainer).Id);\n\t\t\tw.WriteEnum(Status2Receiver = status2Receiver.FieldPosition);\n\t\t\tw.WriteByte((Pokemon2Trainer = pokemon2.Trainer).Id);\n\t\t\tw.WriteEnum(Pokemon2 = pokemon2.FieldPosition);\n\t\t\tw.WriteEnum(Status2 = status2);\n\t\t\tw.WriteEnum(StatusAction = statusAction);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEStatus2Packet(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tStatus2ReceiverTrainer = battle.Trainers[r.ReadByte()];\n\t\tStatus2Receiver = r.ReadEnum<PBEFieldPosition>();\n\t\tPokemon2Trainer = battle.Trainers[r.ReadByte()];\n\t\tPokemon2 = r.ReadEnum<PBEFieldPosition>();\n\t\tStatus2 = r.ReadEnum<PBEStatus2>();\n\t\tStatusAction = r.ReadEnum<PBEStatusAction>();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_TeamStatusDamagePacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBETeamStatusDamagePacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x41;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETeam Team { get; }\n\tpublic PBETeamStatus TeamStatus { get; }\n\tpublic PBETrainer DamageVictimTrainer { get; }\n\tpublic PBEFieldPosition DamageVictim { get; }\n\n\tinternal PBETeamStatusDamagePacket(PBETeam team, PBETeamStatus teamStatus, PBEBattlePokemon damageVictim)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((Team = team).Id);\n\t\t\tw.WriteEnum(TeamStatus = teamStatus);\n\t\t\tw.WriteByte((DamageVictimTrainer = damageVictim.Trainer).Id);\n\t\t\tw.WriteEnum(DamageVictim = damageVictim.FieldPosition);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBETeamStatusDamagePacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tTeam = battle.Teams[r.ReadByte()];\n\t\tTeamStatus = r.ReadEnum<PBETeamStatus>();\n\t\tDamageVictimTrainer = battle.Trainers[r.ReadByte()];\n\t\tDamageVictim = r.ReadEnum<PBEFieldPosition>();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_TeamStatusPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBETeamStatusPacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x13;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETeam Team { get; }\n\tpublic PBETeamStatus TeamStatus { get; }\n\tpublic PBETeamStatusAction TeamStatusAction { get; }\n\n\tinternal PBETeamStatusPacket(PBETeam team, PBETeamStatus teamStatus, PBETeamStatusAction teamStatusAction)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((Team = team).Id);\n\t\t\tw.WriteEnum(TeamStatus = teamStatus);\n\t\t\tw.WriteEnum(TeamStatusAction = teamStatusAction);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBETeamStatusPacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tTeam = battle.Teams[r.ReadByte()];\n\t\tTeamStatus = r.ReadEnum<PBETeamStatus>();\n\t\tTeamStatusAction = r.ReadEnum<PBETeamStatusAction>();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_TransformPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBETransformPacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x18;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer UserTrainer { get; }\n\tpublic PBEFieldPosition User { get; }\n\tpublic PBETrainer TargetTrainer { get; }\n\tpublic PBEFieldPosition Target { get; }\n\tpublic ushort TargetAttack { get; }\n\tpublic ushort TargetDefense { get; }\n\tpublic ushort TargetSpAttack { get; }\n\tpublic ushort TargetSpDefense { get; }\n\tpublic ushort TargetSpeed { get; }\n\tpublic sbyte TargetAttackChange { get; }\n\tpublic sbyte TargetDefenseChange { get; }\n\tpublic sbyte TargetSpAttackChange { get; }\n\tpublic sbyte TargetSpDefenseChange { get; }\n\tpublic sbyte TargetSpeedChange { get; }\n\tpublic sbyte TargetAccuracyChange { get; }\n\tpublic sbyte TargetEvasionChange { get; }\n\tpublic PBEAbility TargetAbility { get; }\n\tpublic PBESpecies TargetSpecies { get; }\n\tpublic PBEForm TargetForm { get; }\n\tpublic PBEType TargetType1 { get; }\n\tpublic PBEType TargetType2 { get; }\n\tpublic float TargetWeight { get; }\n\tpublic ReadOnlyCollection<PBEMove> TargetMoves { get; }\n\n\tinternal PBETransformPacket(PBEBattlePokemon user, PBEBattlePokemon target)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((UserTrainer = user.Trainer).Id);\n\t\t\tw.WriteEnum(User = user.FieldPosition);\n\t\t\tw.WriteByte((TargetTrainer = target.Trainer).Id);\n\t\t\tw.WriteEnum(Target = target.FieldPosition);\n\t\t\tw.WriteUInt16(TargetAttack = target.Attack);\n\t\t\tw.WriteUInt16(TargetDefense = target.Defense);\n\t\t\tw.WriteUInt16(TargetSpAttack = target.SpAttack);\n\t\t\tw.WriteUInt16(TargetSpDefense = target.SpDefense);\n\t\t\tw.WriteUInt16(TargetSpeed = target.Speed);\n\t\t\tw.WriteSByte(TargetAttackChange = target.AttackChange);\n\t\t\tw.WriteSByte(TargetDefenseChange = target.DefenseChange);\n\t\t\tw.WriteSByte(TargetSpAttackChange = target.SpAttackChange);\n\t\t\tw.WriteSByte(TargetSpDefenseChange = target.SpDefenseChange);\n\t\t\tw.WriteSByte(TargetSpeedChange = target.SpeedChange);\n\t\t\tw.WriteSByte(TargetAccuracyChange = target.AccuracyChange);\n\t\t\tw.WriteSByte(TargetEvasionChange = target.EvasionChange);\n\t\t\tw.WriteEnum(TargetAbility = target.Ability);\n\t\t\tw.WriteEnum(TargetSpecies = target.Species);\n\t\t\tw.WriteEnum(TargetForm = target.Form);\n\t\t\tw.WriteEnum(TargetType1 = target.Type1);\n\t\t\tw.WriteEnum(TargetType2 = target.Type2);\n\t\t\tw.WriteSingle(TargetWeight = target.Weight);\n\t\t\tTargetMoves = target.Moves.ForTransformPacket();\n\t\t\tfor (int i = 0; i < TargetMoves.Count; i++)\n\t\t\t{\n\t\t\t\tw.WriteEnum(TargetMoves[i]);\n\t\t\t}\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBETransformPacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tUserTrainer = battle.Trainers[r.ReadByte()];\n\t\tUser = r.ReadEnum<PBEFieldPosition>();\n\t\tTargetTrainer = battle.Trainers[r.ReadByte()];\n\t\tTarget = r.ReadEnum<PBEFieldPosition>();\n\t\tTargetAttack = r.ReadUInt16();\n\t\tTargetDefense = r.ReadUInt16();\n\t\tTargetSpAttack = r.ReadUInt16();\n\t\tTargetSpDefense = r.ReadUInt16();\n\t\tTargetSpeed = r.ReadUInt16();\n\t\tTargetAttackChange = r.ReadSByte();\n\t\tTargetDefenseChange = r.ReadSByte();\n\t\tTargetSpAttackChange = r.ReadSByte();\n\t\tTargetSpDefenseChange = r.ReadSByte();\n\t\tTargetSpeedChange = r.ReadSByte();\n\t\tTargetAccuracyChange = r.ReadSByte();\n\t\tTargetEvasionChange = r.ReadSByte();\n\t\tTargetAbility = r.ReadEnum<PBEAbility>();\n\t\tTargetSpecies = r.ReadEnum<PBESpecies>();\n\t\tTargetForm = r.ReadEnum<PBEForm>();\n\t\tTargetType1 = r.ReadEnum<PBEType>();\n\t\tTargetType2 = r.ReadEnum<PBEType>();\n\t\tTargetWeight = r.ReadSingle();\n\t\tvar moves = new PBEMove[battle.Settings.NumMoves];\n\t\tfor (int i = 0; i < moves.Length; i++)\n\t\t{\n\t\t\tmoves[i] = r.ReadEnum<PBEMove>();\n\t\t}\n\t\tTargetMoves = new ReadOnlyCollection<PBEMove>(moves);\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_TypeChangedPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBETypeChangedPacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x2B;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBETrainer PokemonTrainer { get; }\n\tpublic PBEFieldPosition Pokemon { get; }\n\tpublic PBEType Type1 { get; }\n\tpublic PBEType Type2 { get; }\n\n\tinternal PBETypeChangedPacket(PBEBattlePokemon pokemon, PBEType type1, PBEType type2)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteByte((PokemonTrainer = pokemon.Trainer).Id);\n\t\t\tw.WriteEnum(Pokemon = pokemon.FieldPosition);\n\t\t\tw.WriteEnum(Type1 = type1);\n\t\t\tw.WriteEnum(Type2 = type2);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBETypeChangedPacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tPokemonTrainer = battle.Trainers[r.ReadByte()];\n\t\tPokemon = r.ReadEnum<PBEFieldPosition>();\n\t\tType1 = r.ReadEnum<PBEType>();\n\t\tType2 = r.ReadEnum<PBEType>();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_WeatherDamagePacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEWeatherDamagePacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x40;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBEWeather Weather { get; }\n\tpublic PBETrainer DamageVictimTrainer { get; }\n\tpublic PBEFieldPosition DamageVictim { get; }\n\n\tinternal PBEWeatherDamagePacket(PBEWeather weather, PBEBattlePokemon damageVictim)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteEnum(Weather = weather);\n\t\t\tw.WriteByte((DamageVictimTrainer = damageVictim.Trainer).Id);\n\t\t\tw.WriteEnum(DamageVictim = damageVictim.FieldPosition);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEWeatherDamagePacket(byte[] data, EndianBinaryReader r, PBEBattle battle)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tWeather = r.ReadEnum<PBEWeather>();\n\t\tDamageVictimTrainer = battle.Trainers[r.ReadByte()];\n\t\tDamageVictim = r.ReadEnum<PBEFieldPosition>();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_WeatherPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic sealed class PBEWeatherPacket : IPBEPacket\n{\n\tpublic const ushort ID = 0x14;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic PBEWeather Weather { get; }\n\tpublic PBEWeatherAction WeatherAction { get; }\n\n\tinternal PBEWeatherPacket(PBEWeather weather, PBEWeatherAction weatherAction)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tw.WriteEnum(Weather = weather);\n\t\t\tw.WriteEnum(WeatherAction = weatherAction);\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEWeatherPacket(byte[] data, EndianBinaryReader r)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tWeather = r.ReadEnum<PBEWeather>();\n\t\tWeatherAction = r.ReadEnum<PBEWeatherAction>();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/_WildPkmnAppearedPacket.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic interface IPBEPkmnAppearedInfo_Hidden : IPBESpeciesForm\n{\n\tstring Nickname { get; }\n\tbyte Level { get; }\n\tbool Shiny { get; }\n\tPBEGender Gender { get; }\n\tfloat HPPercentage { get; }\n\tPBEStatus1 Status1 { get; }\n\tPBEFieldPosition FieldPosition { get; }\n}\npublic interface IPBEWildPkmnAppearedPacket : IPBEPacket\n{\n\tIReadOnlyList<IPBEPkmnAppearedInfo_Hidden> Pokemon { get; }\n}\npublic sealed class PBEPkmnAppearedInfo : IPBEPkmnSwitchInInfo_Hidden\n{\n\tpublic byte Pokemon { get; }\n\tpublic bool IsDisguised { get; }\n\tpublic PBESpecies Species { get; }\n\tpublic PBEForm Form { get; }\n\tpublic string Nickname { get; }\n\tpublic byte Level { get; }\n\tpublic bool Shiny { get; }\n\tpublic PBEGender Gender { get; }\n\tpublic PBEItem CaughtBall { get; }\n\tpublic ushort HP { get; }\n\tpublic ushort MaxHP { get; }\n\tpublic float HPPercentage { get; }\n\tpublic PBEStatus1 Status1 { get; }\n\tpublic PBEFieldPosition FieldPosition { get; }\n\n\tinternal PBEPkmnAppearedInfo(PBEBattlePokemon pkmn)\n\t{\n\t\tPokemon = pkmn.Id;\n\t\tIsDisguised = pkmn.Status2.HasFlag(PBEStatus2.Disguised);\n\t\tSpecies = pkmn.KnownSpecies;\n\t\tForm = pkmn.KnownForm;\n\t\tNickname = pkmn.KnownNickname;\n\t\tLevel = pkmn.Level;\n\t\tShiny = pkmn.KnownShiny;\n\t\tGender = pkmn.KnownGender;\n\t\tCaughtBall = pkmn.KnownCaughtBall;\n\t\tHP = pkmn.HP;\n\t\tMaxHP = pkmn.MaxHP;\n\t\tHPPercentage = pkmn.HPPercentage;\n\t\tStatus1 = pkmn.Status1;\n\t\tFieldPosition = pkmn.FieldPosition;\n\t}\n\tinternal PBEPkmnAppearedInfo(EndianBinaryReader r)\n\t{\n\t\tPokemon = r.ReadByte();\n\t\tIsDisguised = r.ReadBoolean();\n\t\tSpecies = r.ReadEnum<PBESpecies>();\n\t\tForm = r.ReadEnum<PBEForm>();\n\t\tNickname = r.ReadString_NullTerminated();\n\t\tLevel = r.ReadByte();\n\t\tShiny = r.ReadBoolean();\n\t\tGender = r.ReadEnum<PBEGender>();\n\t\tCaughtBall = r.ReadEnum<PBEItem>();\n\t\tHP = r.ReadUInt16();\n\t\tMaxHP = r.ReadUInt16();\n\t\tHPPercentage = r.ReadSingle();\n\t\tStatus1 = r.ReadEnum<PBEStatus1>();\n\t\tFieldPosition = r.ReadEnum<PBEFieldPosition>();\n\t}\n\n\tinternal void ToBytes(EndianBinaryWriter w)\n\t{\n\t\tw.WriteByte(Pokemon);\n\t\tw.WriteBoolean(IsDisguised);\n\t\tw.WriteEnum(Species);\n\t\tw.WriteEnum(Form);\n\t\tw.WriteChars_NullTerminated(Nickname);\n\t\tw.WriteByte(Level);\n\t\tw.WriteBoolean(Shiny);\n\t\tw.WriteEnum(Gender);\n\t\tw.WriteEnum(CaughtBall);\n\t\tw.WriteUInt16(HP);\n\t\tw.WriteUInt16(MaxHP);\n\t\tw.WriteSingle(HPPercentage);\n\t\tw.WriteEnum(Status1);\n\t\tw.WriteEnum(FieldPosition);\n\t}\n}\npublic sealed class PBEWildPkmnAppearedPacket : IPBEWildPkmnAppearedPacket\n{\n\tpublic const ushort ID = 0x0D;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic ReadOnlyCollection<PBEPkmnAppearedInfo> Pokemon { get; }\n\tIReadOnlyList<IPBEPkmnAppearedInfo_Hidden> IPBEWildPkmnAppearedPacket.Pokemon => Pokemon;\n\n\tinternal PBEWildPkmnAppearedPacket(IList<PBEPkmnAppearedInfo> pokemon)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tbyte count = (byte)(Pokemon = new ReadOnlyCollection<PBEPkmnAppearedInfo>(pokemon)).Count;\n\t\t\tw.WriteByte(count);\n\t\t\tfor (int i = 0; i < count; i++)\n\t\t\t{\n\t\t\t\tPokemon[i].ToBytes(w);\n\t\t\t}\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEWildPkmnAppearedPacket(byte[] data, EndianBinaryReader r)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tvar pokemon = new PBEPkmnAppearedInfo[r.ReadByte()];\n\t\tfor (int i = 0; i < pokemon.Length; i++)\n\t\t{\n\t\t\tpokemon[i] = new PBEPkmnAppearedInfo(r);\n\t\t}\n\t\tPokemon = new ReadOnlyCollection<PBEPkmnAppearedInfo>(pokemon);\n\t}\n}\npublic sealed class PBEWildPkmnAppearedPacket_Hidden : IPBEWildPkmnAppearedPacket\n{\n\tpublic const ushort ID = 0x3C;\n\tpublic ReadOnlyCollection<byte> Data { get; }\n\n\tpublic sealed class PBEWildPkmnInfo : IPBEPkmnAppearedInfo_Hidden\n\t{\n\t\tpublic PBESpecies Species { get; }\n\t\tpublic PBEForm Form { get; }\n\t\tpublic string Nickname { get; }\n\t\tpublic byte Level { get; }\n\t\tpublic bool Shiny { get; }\n\t\tpublic PBEGender Gender { get; }\n\t\tpublic float HPPercentage { get; }\n\t\tpublic PBEStatus1 Status1 { get; }\n\t\tpublic PBEFieldPosition FieldPosition { get; }\n\n\t\tinternal PBEWildPkmnInfo(PBEPkmnAppearedInfo other)\n\t\t{\n\t\t\tSpecies = other.Species;\n\t\t\tForm = other.Form;\n\t\t\tNickname = other.Nickname;\n\t\t\tLevel = other.Level;\n\t\t\tShiny = other.Shiny;\n\t\t\tGender = other.Gender;\n\t\t\tHPPercentage = other.HPPercentage;\n\t\t\tStatus1 = other.Status1;\n\t\t\tFieldPosition = other.FieldPosition;\n\t\t}\n\t\tinternal PBEWildPkmnInfo(EndianBinaryReader r)\n\t\t{\n\t\t\tSpecies = r.ReadEnum<PBESpecies>();\n\t\t\tForm = r.ReadEnum<PBEForm>();\n\t\t\tNickname = r.ReadString_NullTerminated();\n\t\t\tLevel = r.ReadByte();\n\t\t\tShiny = r.ReadBoolean();\n\t\t\tGender = r.ReadEnum<PBEGender>();\n\t\t\tHPPercentage = r.ReadSingle();\n\t\t\tStatus1 = r.ReadEnum<PBEStatus1>();\n\t\t\tFieldPosition = r.ReadEnum<PBEFieldPosition>();\n\t\t}\n\n\t\tinternal void ToBytes(EndianBinaryWriter w)\n\t\t{\n\t\t\tw.WriteEnum(Species);\n\t\t\tw.WriteEnum(Form);\n\t\t\tw.WriteChars_NullTerminated(Nickname);\n\t\t\tw.WriteByte(Level);\n\t\t\tw.WriteBoolean(Shiny);\n\t\t\tw.WriteEnum(Gender);\n\t\t\tw.WriteSingle(HPPercentage);\n\t\t\tw.WriteEnum(Status1);\n\t\t\tw.WriteEnum(FieldPosition);\n\t\t}\n\t}\n\n\tpublic ReadOnlyCollection<PBEWildPkmnInfo> Pokemon { get; }\n\tIReadOnlyList<IPBEPkmnAppearedInfo_Hidden> IPBEWildPkmnAppearedPacket.Pokemon => Pokemon;\n\n\tpublic PBEWildPkmnAppearedPacket_Hidden(PBEWildPkmnAppearedPacket other)\n\t{\n\t\tusing (var ms = new MemoryStream())\n\t\t{\n\t\t\tEndianBinaryWriter w = PBEPacketProcessor.WritePacketID(ms, ID);\n\n\t\t\tvar pokemon = new PBEWildPkmnInfo[other.Pokemon.Count];\n\t\t\tfor (int i = 0; i < pokemon.Length; i++)\n\t\t\t{\n\t\t\t\tpokemon[i] = new PBEWildPkmnInfo(other.Pokemon[i]);\n\t\t\t}\n\t\t\tbyte count = (byte)(Pokemon = new ReadOnlyCollection<PBEWildPkmnInfo>(pokemon)).Count;\n\t\t\tw.WriteByte(count);\n\t\t\tfor (int i = 0; i < count; i++)\n\t\t\t{\n\t\t\t\tPokemon[i].ToBytes(w);\n\t\t\t}\n\n\t\t\tData = new ReadOnlyCollection<byte>(ms.ToArray());\n\t\t}\n\t}\n\tinternal PBEWildPkmnAppearedPacket_Hidden(byte[] data, EndianBinaryReader r)\n\t{\n\t\tData = new ReadOnlyCollection<byte>(data);\n\n\t\tvar pokemon = new PBEWildPkmnInfo[r.ReadByte()];\n\t\tfor (int i = 0; i < pokemon.Length; i++)\n\t\t{\n\t\t\tpokemon[i] = new PBEWildPkmnInfo(r);\n\t\t}\n\t\tPokemon = new ReadOnlyCollection<PBEWildPkmnInfo>(pokemon);\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/__Packet.cs",
    "content": "﻿using System.Collections.ObjectModel;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic interface IPBEPacket\n{\n\tReadOnlyCollection<byte> Data { get; }\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Packets/__PacketProcessor.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.Packets;\n\npublic class PBEPacketProcessor\n{\n\tprotected static void CheckNull([NotNull] PBEBattle? battle)\n\t{\n\t\tif (battle is null)\n\t\t{\n\t\t\tthrow new ArgumentNullException(nameof(battle));\n\t\t}\n\t}\n\n\tpublic IPBEPacket CreatePacket(byte[] data, PBEBattle? battle)\n\t{\n\t\tif (data.Length < 2)\n\t\t{\n\t\t\tthrow new InvalidDataException();\n\t\t}\n\n\t\tusing (var ms = new MemoryStream(data))\n\t\t{\n\t\t\tvar r = new EndianBinaryReader(ms);\n\t\t\tushort id = r.ReadUInt16();\n\t\t\tIPBEPacket? ret = TryCreatePacket(data, battle, r, id);\n\t\t\tif (ret is null)\n\t\t\t{\n\t\t\t\tthrow new InvalidDataException($\"Invalid packet ID ({id})\");\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\t}\n\n\tpublic static EndianBinaryWriter WritePacketID(MemoryStream ms, ushort id)\n\t{\n\t\tvar w = new EndianBinaryWriter(ms);\n\t\tw.WriteUInt16(id);\n\t\treturn w;\n\t}\n\n\tprotected virtual IPBEPacket? TryCreatePacket(byte[] data, PBEBattle? battle, EndianBinaryReader r, ushort id)\n\t{\n\t\tswitch (id)\n\t\t{\n\t\t\tcase PBEResponsePacket.ID: return new PBEResponsePacket(data);\n\t\t\tcase PBEPlayerJoinedPacket.ID: return new PBEPlayerJoinedPacket(data, r);\n\t\t\tcase PBEMatchCancelledPacket.ID: return new PBEMatchCancelledPacket(data);\n\t\t\tcase PBEPartyRequestPacket.ID: return new PBEPartyRequestPacket(data, r);\n\t\t\tcase PBEPartyResponsePacket.ID: return new PBEPartyResponsePacket(data, r);\n\t\t\tcase PBEBattlePacket.ID: return new PBEBattlePacket(data, r);\n\t\t\tcase PBEPkmnSwitchInPacket.ID: CheckNull(battle); return new PBEPkmnSwitchInPacket(data, r, battle);\n\t\t\tcase PBEActionsRequestPacket.ID: CheckNull(battle); return new PBEActionsRequestPacket(data, r, battle);\n\t\t\tcase PBEActionsResponsePacket.ID: return new PBEActionsResponsePacket(data, r);\n\t\t\tcase PBEMoveUsedPacket.ID: CheckNull(battle); return new PBEMoveUsedPacket(data, r, battle);\n\t\t\tcase PBEPkmnHPChangedPacket.ID: CheckNull(battle); return new PBEPkmnHPChangedPacket(data, r, battle);\n\t\t\tcase PBEHazePacket.ID: return new PBEHazePacket(data);\n\t\t\tcase PBEPkmnSwitchOutPacket.ID: CheckNull(battle); return new PBEPkmnSwitchOutPacket(data, r, battle);\n\t\t\tcase PBEWildPkmnAppearedPacket.ID: return new PBEWildPkmnAppearedPacket(data, r);\n\t\t\tcase PBEPkmnFaintedPacket.ID: CheckNull(battle); return new PBEPkmnFaintedPacket(data, r, battle);\n\t\t\tcase PBEMoveCritPacket.ID: CheckNull(battle); return new PBEMoveCritPacket(data, r, battle);\n\t\t\tcase PBEPkmnStatChangedPacket.ID: CheckNull(battle); return new PBEPkmnStatChangedPacket(data, r, battle);\n\t\t\tcase PBEStatus1Packet.ID: CheckNull(battle); return new PBEStatus1Packet(data, r, battle);\n\t\t\tcase PBEStatus2Packet.ID: CheckNull(battle); return new PBEStatus2Packet(data, r, battle);\n\t\t\tcase PBETeamStatusPacket.ID: CheckNull(battle); return new PBETeamStatusPacket(data, r, battle);\n\t\t\tcase PBEWeatherPacket.ID: CheckNull(battle); return new PBEWeatherPacket(data, r);\n\t\t\tcase PBEMoveResultPacket.ID: CheckNull(battle); return new PBEMoveResultPacket(data, r, battle);\n\t\t\tcase PBEItemPacket.ID: CheckNull(battle); return new PBEItemPacket(data, r, battle);\n\t\t\tcase PBEMovePPChangedPacket.ID: CheckNull(battle); return new PBEMovePPChangedPacket(data, r, battle);\n\t\t\tcase PBETransformPacket.ID: CheckNull(battle); return new PBETransformPacket(data, r, battle);\n\t\t\tcase PBEAbilityPacket.ID: CheckNull(battle); return new PBEAbilityPacket(data, r, battle);\n\t\t\tcase PBESpecialMessagePacket.ID: CheckNull(battle); return new PBESpecialMessagePacket(data, r, battle);\n\t\t\tcase PBEBattleStatusPacket.ID: return new PBEBattleStatusPacket(data, r);\n\t\t\tcase PBEPsychUpPacket.ID: CheckNull(battle); return new PBEPsychUpPacket(data, r, battle);\n\t\t\tcase PBESwitchInRequestPacket.ID: CheckNull(battle); return new PBESwitchInRequestPacket(data, r, battle);\n\t\t\tcase PBESwitchInResponsePacket.ID: return new PBESwitchInResponsePacket(data, r);\n\t\t\tcase PBEIllusionPacket.ID: CheckNull(battle); return new PBEIllusionPacket(data, r, battle);\n\t\t\tcase PBEBattleResultPacket.ID: return new PBEBattleResultPacket(data, r);\n\t\t\tcase PBETurnBeganPacket.ID: return new PBETurnBeganPacket(data, r);\n\t\t\tcase PBEMoveLockPacket.ID: CheckNull(battle); return new PBEMoveLockPacket(data, r, battle);\n\t\t\tcase PBEPkmnFormChangedPacket.ID: CheckNull(battle); return new PBEPkmnFormChangedPacket(data, r, battle);\n\t\t\tcase PBEAutoCenterPacket.ID: CheckNull(battle); return new PBEAutoCenterPacket(data, r, battle);\n\t\t\tcase PBETypeChangedPacket.ID: CheckNull(battle); return new PBETypeChangedPacket(data, r, battle);\n\t\t\tcase PBEAbilityReplacedPacket.ID: CheckNull(battle); return new PBEAbilityReplacedPacket(data, r, battle);\n\t\t\tcase PBELegalPartyResponsePacket.ID: return new PBELegalPartyResponsePacket(data, r);\n\t\t\tcase PBEReflectTypePacket.ID: CheckNull(battle); return new PBEReflectTypePacket(data, r, battle);\n\t\t\tcase PBEPkmnFaintedPacket_Hidden.ID: CheckNull(battle); return new PBEPkmnFaintedPacket_Hidden(data, r, battle);\n\t\t\tcase PBEAutoCenterPacket_Hidden0.ID: CheckNull(battle); return new PBEAutoCenterPacket_Hidden0(data, r, battle);\n\t\t\tcase PBEAutoCenterPacket_Hidden1.ID: CheckNull(battle); return new PBEAutoCenterPacket_Hidden1(data, r, battle);\n\t\t\tcase PBEAutoCenterPacket_Hidden01.ID: CheckNull(battle); return new PBEAutoCenterPacket_Hidden01(data, r, battle);\n\t\t\tcase PBEReflectTypePacket_Hidden.ID: CheckNull(battle); return new PBEReflectTypePacket_Hidden(data, r, battle);\n\t\t\tcase PBEPkmnFormChangedPacket_Hidden.ID: CheckNull(battle); return new PBEPkmnFormChangedPacket_Hidden(data, r, battle);\n\t\t\tcase PBEPkmnHPChangedPacket_Hidden.ID: CheckNull(battle); return new PBEPkmnHPChangedPacket_Hidden(data, r, battle);\n\t\t\tcase PBEPkmnSwitchInPacket_Hidden.ID: CheckNull(battle); return new PBEPkmnSwitchInPacket_Hidden(data, r, battle);\n\t\t\tcase PBEPkmnSwitchOutPacket_Hidden.ID: CheckNull(battle); return new PBEPkmnSwitchOutPacket_Hidden(data, r, battle);\n\t\t\tcase PBEFleeResponsePacket.ID: return new PBEFleeResponsePacket(data);\n\t\t\tcase PBEFleeFailedPacket.ID: CheckNull(battle); return new PBEFleeFailedPacket(data, r, battle);\n\t\t\tcase PBEItemTurnPacket.ID: CheckNull(battle); return new PBEItemTurnPacket(data, r, battle);\n\t\t\tcase PBECapturePacket.ID: CheckNull(battle); return new PBECapturePacket(data, r, battle);\n\t\t\tcase PBEWildPkmnAppearedPacket_Hidden.ID: return new PBEWildPkmnAppearedPacket_Hidden(data, r);\n\t\t\tcase PBEPkmnEXPChangedPacket.ID: CheckNull(battle); return new PBEPkmnEXPChangedPacket(data, r, battle);\n\t\t\tcase PBEPkmnEXPEarnedPacket.ID: CheckNull(battle); return new PBEPkmnEXPEarnedPacket(data, r, battle);\n\t\t\tcase PBEPkmnLevelChangedPacket.ID: CheckNull(battle); return new PBEPkmnLevelChangedPacket(data, r, battle);\n\t\t\tcase PBEWeatherDamagePacket.ID: CheckNull(battle); return new PBEWeatherDamagePacket(data, r, battle);\n\t\t\tcase PBETeamStatusDamagePacket.ID: CheckNull(battle); return new PBETeamStatusDamagePacket(data, r, battle);\n\t\t\tdefault: return null;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/PokemonBattleEngine.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n\t<PropertyGroup>\n\t\t<TargetFramework>net7.0</TargetFramework>\n\t\t<OutputType>Library</OutputType>\n\t\t<RootNamespace>Kermalis.PokemonBattleEngine</RootNamespace>\n\t\t<Authors>Kermalis</Authors>\n\t\t<Copyright>Kermalis</Copyright>\n\t\t<RepositoryUrl>https://github.com/Kermalis/PokemonBattleEngine</RepositoryUrl>\n\t\t<DocumentationFile>bin\\$(Configuration)\\$(TargetFramework)\\$(AssemblyName).xml</DocumentationFile>\n\t\t<NoWarn>1591</NoWarn>\n\t\t<Nullable>enable</Nullable>\n\t</PropertyGroup>\n\n\t<ItemGroup>\n\t\t<PackageReference Include=\"EndianBinaryIO\" Version=\"2.1.0\" />\n\t</ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "PokemonBattleEngine/Utils/EmptyCollections.cs",
    "content": "﻿using System;\nusing System.Collections.ObjectModel;\n\nnamespace Kermalis.PokemonBattleEngine.Utils;\n\ninternal static class PBEEmptyReadOnlyCollection<T>\n{\n\tpublic static readonly ReadOnlyCollection<T> Value = new(Array.Empty<T>());\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Utils/Random.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Data.Utils;\nusing System;\nusing System.Collections.Generic;\n\nnamespace Kermalis.PokemonBattleEngine.Utils;\n\n/// <summary>A random helper. This class is thread-safe.</summary>\npublic class PBERandom\n{\n\tprotected readonly object _randLockObj = new();\n\tprotected Random _rand;\n\tprotected int _seed;\n\t/// <summary>Gets or sets the seed of this <see cref=\"PBERandom\"/>. The chain will be reset even if the seed is the same as the previous seed.</summary>\n\tpublic int Seed\n\t{\n\t\tget => _seed;\n\t\tset\n\t\t{\n\t\t\tlock (_randLockObj)\n\t\t\t{\n\t\t\t\t_rand = new Random(value);\n\t\t\t\t_seed = value;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic PBERandom() : this(Environment.TickCount) { }\n\tpublic PBERandom(int? seed) : this(seed ?? Environment.TickCount) { }\n\tpublic PBERandom(int seed)\n\t{\n\t\t_rand = new Random(seed);\n\t\t_seed = seed;\n\t}\n\n\tpublic PBEBattleTerrain RandomBattleTerrain()\n\t{\n\t\treturn (PBEBattleTerrain)RandomInt(0, (int)PBEBattleTerrain.MAX - 1);\n\t}\n\tpublic bool RandomBool()\n\t{\n\t\treturn RandomInt(0, 1) == 1;\n\t}\n\tpublic bool RandomBool(int chanceNumerator, int chanceDenominator)\n\t{\n\t\tif (chanceDenominator < 1)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(chanceDenominator), $\"\\\"{nameof(chanceDenominator)}\\\" must be at least 1.\");\n\t\t}\n\t\tif (chanceNumerator >= chanceDenominator)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t\tif (chanceNumerator == 0)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\treturn RandomInt(0, chanceDenominator - 1) < chanceNumerator;\n\t}\n\tpublic T RandomElement<T>(IReadOnlyList<T> source)\n\t{\n\t\tint count = source.Count - 1;\n\t\tif (count == 0)\n\t\t{\n\t\t\treturn source[0];\n\t\t}\n\t\tif (count < 0)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(source), $\"\\\"{nameof(source)}\\\" must have at least one element.\");\n\t\t}\n\t\treturn source[RandomInt(0, count)];\n\t}\n\t/// <summary>Returns a random <see cref=\"PBEGender\"/> for the given <paramref name=\"genderRatio\"/>.</summary>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Thrown when <paramref name=\"genderRatio\"/> is invalid.</exception>\n\tpublic PBEGender RandomGender(PBEGenderRatio genderRatio)\n\t{\n\t\tswitch (genderRatio)\n\t\t{\n\t\t\tcase PBEGenderRatio.M7_F1: return RandomBool(875, 1000) ? PBEGender.Male : PBEGender.Female;\n\t\t\tcase PBEGenderRatio.M3_F1: return RandomBool(750, 1000) ? PBEGender.Male : PBEGender.Female;\n\t\t\tcase PBEGenderRatio.M1_F1: return RandomBool(500, 1000) ? PBEGender.Male : PBEGender.Female;\n\t\t\tcase PBEGenderRatio.M1_F3: return RandomBool(250, 1000) ? PBEGender.Male : PBEGender.Female;\n\t\t\tcase PBEGenderRatio.M0_F1: return PBEGender.Female;\n\t\t\tcase PBEGenderRatio.M0_F0: return PBEGender.Genderless;\n\t\t\tcase PBEGenderRatio.M1_F0: return PBEGender.Male;\n\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(genderRatio));\n\t\t}\n\t}\n\tpublic int RandomInt()\n\t{\n\t\treturn RandomInt(int.MinValue, int.MaxValue);\n\t}\n\t/// <summary>Returns a random <see cref=\"int\"/> value between the inclusive <paramref name=\"minValue\"/> and inclusive <paramref name=\"maxValue\"/>.</summary>\n\tpublic int RandomInt(int minValue, int maxValue)\n\t{\n\t\tif (minValue == maxValue)\n\t\t{\n\t\t\treturn minValue;\n\t\t}\n\t\tif (minValue > maxValue)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(minValue), $\"\\\"{nameof(minValue)}\\\" cannot exceed \\\"{nameof(maxValue)}\\\".\");\n\t\t}\n\t\tSpan<byte> bytes = stackalloc byte[sizeof(uint)];\n\t\tuint scale;\n\t\tdo\n\t\t{\n\t\t\tlock (_randLockObj)\n\t\t\t{\n\t\t\t\t_rand.NextBytes(bytes);\n\t\t\t}\n\t\t\tscale = EndianBinaryPrimitives.ReadUInt32_Unsafe(bytes, Endianness.LittleEndian);\n\t\t} while (scale == uint.MaxValue); // \"d\" should not be 1.0\n\t\tfloat d = scale / (float)uint.MaxValue;\n\t\treturn (int)(minValue + (((long)maxValue + 1 - minValue) * d)); // Remove \"+ 1\" for exclusive maxValue\n\t}\n\t/// <summary>Returns a random <see cref=\"byte\"/> value that is between <paramref name=\"settings\"/>'s <see cref=\"PBESettings.MinLevel\"/> and <see cref=\"PBESettings.MaxLevel\"/>.</summary>\n\t/// <param name=\"settings\">The <see cref=\"PBESettings\"/> object to use.</param>\n\tpublic byte RandomLevel(PBESettings settings)\n\t{\n\t\tsettings.ShouldBeReadOnly(nameof(settings));\n\t\treturn (byte)RandomInt(settings.MinLevel, settings.MaxLevel);\n\t}\n\t/// <summary>Returns a random <see cref=\"bool\"/> value that represents shininess using shiny odds.</summary>\n\tpublic bool RandomShiny()\n\t{\n\t\treturn RandomBool(8, 65536);\n\t}\n\t/// <summary>Returns a random <see cref=\"PBESpecies\"/> with a random <see cref=\"PBEForm\"/>.</summary>\n\tpublic (PBESpecies, PBEForm) RandomSpecies(bool requireUsableOutsideOfBattle)\n\t{\n\t\treturn RandomSpecies(PBEDataUtils.AllSpecies, requireUsableOutsideOfBattle);\n\t}\n\tpublic (PBESpecies, PBEForm) RandomSpecies(IReadOnlyList<PBESpecies> eligible, bool requireUsableOutsideOfBattle)\n\t{\n\t\tPBESpecies species = RandomElement(eligible);\n\t\tIReadOnlyList<PBEForm> forms = PBEDataUtils.GetForms(species, requireUsableOutsideOfBattle);\n\t\tPBEForm form = forms.Count > 0 ? RandomElement(forms) : 0;\n\t\treturn (species, form);\n\t}\n\t/// <summary>Shuffles the items in <paramref name=\"source\"/> using the Fisher-Yates Shuffle algorithm.</summary>\n\tpublic void Shuffle<T>(IList<T> source)\n\t{\n\t\tint count = source.Count - 1;\n\t\tif (count < 0)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(source), $\"\\\"{nameof(source)}\\\" must have at least one element.\");\n\t\t}\n\t\tfor (int a = 0; a < count; a++)\n\t\t{\n\t\t\tint b = RandomInt(a, count);\n\t\t\t(source[b], source[a]) = (source[a], source[b]);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine/Utils/Utils.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.IO;\nusing System.Text.Json.Nodes;\n\nnamespace Kermalis.PokemonBattleEngine.Utils;\n\n/// <summary>A static class that provides utilities that are used throughout the battle engine.</summary>\npublic static class PBEUtils\n{\n\t/// <summary>Returns a <see cref=\"string\"/> that combines <paramref name=\"source\"/>'s elements' string representations using \"and\" with commas.</summary>\n\t/// <typeparam name=\"T\">The type of the elements of <paramref name=\"source\"/>.</typeparam>\n\t/// <param name=\"source\">An <see cref=\"IReadOnlyList{T}\"/> to create a string from.</param>\n\tpublic static string Andify<T>(this IReadOnlyList<T> source)\n\t{\n\t\tstring str = source[0]?.ToString() ?? string.Empty;\n\t\tfor (int i = 1; i < source.Count; i++)\n\t\t{\n\t\t\tif (i == source.Count - 1)\n\t\t\t{\n\t\t\t\tif (source.Count > 2)\n\t\t\t\t{\n\t\t\t\t\tstr += ',';\n\t\t\t\t}\n\t\t\t\tstr += \" and \";\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tstr += \", \";\n\t\t\t}\n\t\t\tstr += source[i]?.ToString() ?? string.Empty;\n\t\t}\n\t\treturn str;\n\t}\n\tpublic static IEnumerable<T> ExceptOne<T>(this IEnumerable<T> source, T one)\n\t{\n\t\tforeach (T t in source)\n\t\t{\n\t\t\tif (!Equals(t, one))\n\t\t\t{\n\t\t\t\tyield return t;\n\t\t\t}\n\t\t}\n\t}\n\t/// <summary>Removes all invalid file name characters from <paramref name=\"fileName\"/>.</summary>\n\tinternal static string ToSafeFileName(string fileName)\n\t{\n\t\tchar[] invalid = Path.GetInvalidFileNameChars();\n\t\tfor (int i = 0; i < invalid.Length; i++)\n\t\t{\n\t\t\tfileName = fileName.Replace(invalid[i], '-');\n\t\t}\n\t\treturn fileName;\n\t}\n\n\tinternal static JsonNode GetSafe(this JsonArray j, int index)\n\t{\n\t\tJsonNode? ret = j[index];\n\t\tif (ret is null)\n\t\t{\n\t\t\tthrow new InvalidDataException($\"JSON array index \\\"{index}\\\" was not found\");\n\t\t}\n\t\treturn ret;\n\t}\n\tinternal static JsonNode GetSafe(this JsonObject j, string key)\n\t{\n\t\tJsonNode? ret = j[key];\n\t\tif (ret is null)\n\t\t{\n\t\t\tthrow new InvalidDataException($\"JSON object key \\\"{key}\\\" was not found\");\n\t\t}\n\t\treturn ret;\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine.DefaultData/AI/AI.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing System;\nusing System.Collections.Generic;\n\nnamespace Kermalis.PokemonBattleEngine.DefaultData.AI;\n\n/// <summary>Creates valid decisions for a team in a battle. Decisions may not be valid for custom settings and/or move changes.</summary>\npublic partial class PBEDDAI\n{\n\t// TODO: Control multiple trainers of a multi battle team\n\tpublic PBETrainer Trainer { get; }\n\n\tpublic PBEDDAI(PBETrainer trainer)\n\t{\n\t\tif (trainer.IsWild)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(trainer), $\"Cannot use this AI type with a wild trainer. Use {nameof(PBEDDWildAI)} or another type of AI.\");\n\t\t}\n\t\tTrainer = trainer;\n\t}\n\n\t/// <summary>Creates valid actions for a battle turn for a specific team.</summary>\n\t/// <exception cref=\"InvalidOperationException\">Thrown when <see name=\"Trainer\"/> has no active battlers or <see name=\"Trainer\"/>'s <see cref=\"PBETrainer.Battle\"/>'s <see cref=\"PBEBattle.BattleState\"/> is not <see cref=\"PBEBattleState.WaitingForActions\"/>.</exception>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Thrown when a Pokémon has no moves, the AI tries to use a move with invalid targets, or <see name=\"Trainer\"/>'s <see cref=\"PBETrainer.Battle\"/>'s <see cref=\"PBEBattle.BattleFormat\"/> is invalid.</exception>\n\tpublic void CreateActions()\n\t{\n\t\tif (Trainer.Battle.BattleState != PBEBattleState.WaitingForActions)\n\t\t{\n\t\t\tthrow new InvalidOperationException($\"{nameof(Trainer.Battle.BattleState)} must be {PBEBattleState.WaitingForActions} to create actions.\");\n\t\t}\n\t\tint count = Trainer.ActionsRequired.Count;\n\t\tvar actions = new List<PBETurnAction>(count);\n\t\tvar standBy = new List<PBEBattlePokemon>();\n\t\tfor (int i = 0; i < count; i++)\n\t\t{\n\t\t\tPBEBattlePokemon user = Trainer.ActionsRequired[i];\n\t\t\t// If a Pokémon is forced to struggle, it is best that it just stays in until it faints\n\t\t\tif (user.IsForcedToStruggle())\n\t\t\t{\n\t\t\t\tactions.Add(new PBETurnAction(user, PBEMove.Struggle, PBEBattleUtils.GetPossibleTargets(user, user.GetMoveTargets(PBEMove.Struggle))[0]));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// If a Pokémon has a temp locked move (Dig, Dive, ShadowForce) it must be used\n\t\t\telse if (user.TempLockedMove != PBEMove.None)\n\t\t\t{\n\t\t\t\tactions.Add(new PBETurnAction(user, user.TempLockedMove, user.TempLockedTargets));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// The Pokémon is free to switch or fight (unless it cannot switch due to Magnet Pull etc)\n\t\t\tPBETurnAction a = DecideAction(user, actions, standBy);\n\t\t\t// Action was chosen, finish up for this Pokémon\n\t\t\tif (a.Decision == PBETurnDecision.SwitchOut)\n\t\t\t{\n\t\t\t\tstandBy.Add(Trainer.GetPokemon(a.SwitchPokemonId));\n\t\t\t}\n\t\t\tactions.Add(a);\n\t\t}\n\t\tif (!Trainer.SelectActionsIfValid(actions, out string? valid))\n\t\t{\n\t\t\tthrow new Exception(\"AI created bad actions. - \" + valid);\n\t\t}\n\t}\n\n\t/// <summary>Creates valid switches for a battle for a specific team.</summary>\n\t/// <exception cref=\"InvalidOperationException\">Thrown when <see name=\"Trainer\"/> does not require switch-ins or <see name=\"Trainer\"/>'s <see cref=\"PBETrainer.Battle\"/>'s <see cref=\"PBEBattle.BattleState\"/> is not <see cref=\"PBEBattleState.WaitingForActions\"/>.</exception>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Thrown when <see name=\"Trainer\"/>'s <see cref=\"PBETrainer.Battle\"/>'s <see cref=\"PBEBattle.BattleFormat\"/> is invalid.</exception>\n\tpublic void CreateSwitches()\n\t{\n\t\tif (Trainer.Battle.BattleState != PBEBattleState.WaitingForSwitchIns)\n\t\t{\n\t\t\tthrow new InvalidOperationException($\"{nameof(Trainer.Battle.BattleState)} must be {PBEBattleState.WaitingForSwitchIns} to create switch-ins.\");\n\t\t}\n\t\tif (Trainer.SwitchInsRequired == 0)\n\t\t{\n\t\t\tthrow new InvalidOperationException($\"{nameof(Trainer)} must require switch-ins.\");\n\t\t}\n\t\tList<PBEBattlePokemon> available = Trainer.Party.FindAll(p => p.FieldPosition == PBEFieldPosition.None && p.CanBattle);\n\t\tPBEDataProvider.GlobalRandom.Shuffle(available);\n\t\tvar availablePositions = new List<PBEFieldPosition>();\n\t\tswitch (Trainer.Battle.BattleFormat)\n\t\t{\n\t\t\tcase PBEBattleFormat.Single:\n\t\t\t{\n\t\t\t\tavailablePositions.Add(PBEFieldPosition.Center);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEBattleFormat.Double:\n\t\t\t{\n\t\t\t\tif (Trainer.OwnsSpot(PBEFieldPosition.Left) && !Trainer.IsSpotOccupied(PBEFieldPosition.Left))\n\t\t\t\t{\n\t\t\t\t\tavailablePositions.Add(PBEFieldPosition.Left);\n\t\t\t\t}\n\t\t\t\tif (Trainer.OwnsSpot(PBEFieldPosition.Right) && !Trainer.IsSpotOccupied(PBEFieldPosition.Right))\n\t\t\t\t{\n\t\t\t\t\tavailablePositions.Add(PBEFieldPosition.Right);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEBattleFormat.Triple:\n\t\t\tcase PBEBattleFormat.Rotation:\n\t\t\t{\n\t\t\t\tif (Trainer.OwnsSpot(PBEFieldPosition.Left) && !Trainer.IsSpotOccupied(PBEFieldPosition.Left))\n\t\t\t\t{\n\t\t\t\t\tavailablePositions.Add(PBEFieldPosition.Left);\n\t\t\t\t}\n\t\t\t\tif (Trainer.OwnsSpot(PBEFieldPosition.Center) && !Trainer.IsSpotOccupied(PBEFieldPosition.Center))\n\t\t\t\t{\n\t\t\t\t\tavailablePositions.Add(PBEFieldPosition.Center);\n\t\t\t\t}\n\t\t\t\tif (Trainer.OwnsSpot(PBEFieldPosition.Right) && !Trainer.IsSpotOccupied(PBEFieldPosition.Right))\n\t\t\t\t{\n\t\t\t\t\tavailablePositions.Add(PBEFieldPosition.Right);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: throw new InvalidOperationException(nameof(Trainer.Battle.BattleFormat));\n\t\t}\n\t\tvar switches = new PBESwitchIn[Trainer.SwitchInsRequired];\n\t\tfor (int i = 0; i < Trainer.SwitchInsRequired; i++)\n\t\t{\n\t\t\tswitches[i] = new PBESwitchIn(available[i], availablePositions[i]);\n\t\t}\n\t\tif (!Trainer.SelectSwitchesIfValid(out string? valid, switches))\n\t\t{\n\t\t\tthrow new Exception(\"AI created bad switches. - \" + valid);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine.DefaultData/AI/AIDecisions.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Data.Utils;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Linq;\n\nnamespace Kermalis.PokemonBattleEngine.DefaultData.AI;\n\npublic partial class PBEDDAI\n{\n\tprivate PBETurnAction DecideAction(PBEBattlePokemon user, List<PBETurnAction> actions, List<PBEBattlePokemon> standBy)\n\t{\n\t\t// Gather all options of switching and moves\n\t\tPBEMove[] usableMoves = user.GetUsableMoves();\n\t\tvar possibleActions = new List<(PBETurnAction Action, float Score)>();\n\t\tfor (int m = 0; m < usableMoves.Length; m++) // Score moves\n\t\t{\n\t\t\tPBEMove move = usableMoves[m];\n\t\t\tPBEType moveType = user.GetMoveType(move);\n\t\t\tPBEMoveTarget moveTargets = user.GetMoveTargets(move);\n\t\t\tPBETurnTarget[] possibleTargets = PBEDataUtils.IsSpreadMove(moveTargets)\n\t\t\t\t\t\t\t? new PBETurnTarget[] { PBEBattleUtils.GetSpreadMoveTargets(user, moveTargets) }\n\t\t\t\t\t\t\t: PBEBattleUtils.GetPossibleTargets(user, moveTargets);\n\t\t\tforeach (PBETurnTarget possibleTarget in possibleTargets)\n\t\t\t{\n\t\t\t\t// TODO: RandomFoeSurrounding (probably just account for the specific effects that use this target type)\n\t\t\t\t// TODO: Don't queue up to do the same thing (two trying to afflict the same target when there are multiple targets)\n\t\t\t\tvar targets = new List<PBEBattlePokemon>();\n\t\t\t\tif (possibleTarget.HasFlag(PBETurnTarget.AllyLeft))\n\t\t\t\t{\n\t\t\t\t\tTrainer.Team.TryAddPokemonToCollection(PBEFieldPosition.Left, targets);\n\t\t\t\t}\n\t\t\t\tif (possibleTarget.HasFlag(PBETurnTarget.AllyCenter))\n\t\t\t\t{\n\t\t\t\t\tTrainer.Team.TryAddPokemonToCollection(PBEFieldPosition.Center, targets);\n\t\t\t\t}\n\t\t\t\tif (possibleTarget.HasFlag(PBETurnTarget.AllyRight))\n\t\t\t\t{\n\t\t\t\t\tTrainer.Team.TryAddPokemonToCollection(PBEFieldPosition.Right, targets);\n\t\t\t\t}\n\t\t\t\tif (possibleTarget.HasFlag(PBETurnTarget.FoeLeft))\n\t\t\t\t{\n\t\t\t\t\tTrainer.Team.OpposingTeam.TryAddPokemonToCollection(PBEFieldPosition.Left, targets);\n\t\t\t\t}\n\t\t\t\tif (possibleTarget.HasFlag(PBETurnTarget.FoeCenter))\n\t\t\t\t{\n\t\t\t\t\tTrainer.Team.OpposingTeam.TryAddPokemonToCollection(PBEFieldPosition.Center, targets);\n\t\t\t\t}\n\t\t\t\tif (possibleTarget.HasFlag(PBETurnTarget.FoeRight))\n\t\t\t\t{\n\t\t\t\t\tTrainer.Team.OpposingTeam.TryAddPokemonToCollection(PBEFieldPosition.Right, targets);\n\t\t\t\t}\n\t\t\t\tfloat score = ScoreMove(targets, user, move, moveType, actions);\n\t\t\t\tpossibleActions.Add((new PBETurnAction(user, move, possibleTarget), score));\n\t\t\t}\n\t\t}\n\t\tif (user.CanSwitchOut())\n\t\t{\n\t\t\tPBEBattlePokemon[] availableForSwitch = Trainer.Party.Except(standBy).Where(p => p.FieldPosition == PBEFieldPosition.None && p.CanBattle).ToArray();\n\t\t\tfor (int s = 0; s < availableForSwitch.Length; s++) // Score switches\n\t\t\t{\n\t\t\t\tPBEBattlePokemon switchPkmn = availableForSwitch[s];\n\t\t\t\t// TODO: Entry hazards\n\t\t\t\t// TODO: Known moves of active battlers\n\t\t\t\t// TODO: Type effectiveness\n\t\t\t\tfloat score = -10;\n\t\t\t\tpossibleActions.Add((new PBETurnAction(user, switchPkmn), score));\n\t\t\t}\n\t\t}\n\n\t\tIOrderedEnumerable<(PBETurnAction Action, float Score)> byScore = possibleActions.OrderByDescending(t => t.Score);\n\t\tDebug_LogGeneratedActions(user, byScore);\n\t\tfloat bestScore = byScore.First().Score;\n\t\treturn PBEDataProvider.GlobalRandom.RandomElement(byScore.Where(t => t.Score == bestScore).ToArray()).Action; // Pick random action of the ones that tied for best score\n\t}\n\tprivate void Debug_LogGeneratedActions(PBEBattlePokemon user, IOrderedEnumerable<(PBETurnAction Action, float Score)> byScore)\n\t{\n\t\tstring ToDebugString((PBETurnAction Action, float Score) t)\n\t\t{\n\t\t\tstring str = \"{\";\n\t\t\tif (t.Action.Decision == PBETurnDecision.Fight)\n\t\t\t{\n\t\t\t\tstr += string.Format(\"Fight {0} {1}\", t.Action.FightMove, t.Action.FightTargets);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tstr += string.Format(\"Switch {0}\", Trainer.GetPokemon(t.Action.SwitchPokemonId).Nickname);\n\t\t\t}\n\t\t\tstr += \" [\" + t.Score + \"]}\";\n\t\t\treturn str;\n\t\t}\n\t\tDebug.WriteLine(\"{0}'s possible actions: {1}\", user.Nickname, \"( \" + string.Join(\", \", byScore.Select(t => ToDebugString(t))) + \" )\");\n\t}\n\tprivate float ScoreMove(List<PBEBattlePokemon> targets, PBEBattlePokemon user, PBEMove move, PBEType moveType, List<PBETurnAction> actions)\n\t{\n\t\tif (targets.Count == 0)\n\t\t{\n\t\t\treturn -100;\n\t\t}\n\t\tfloat score = 0;\n\t\tIPBEMoveData mData = PBEDataProvider.Instance.GetMoveData(move);\n\t\tif (!mData.IsMoveUsable())\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(move), $\"{move} is not yet implemented in Pokémon Battle Engine.\");\n\t\t}\n\t\tswitch (mData.Effect)\n\t\t{\n\t\t\tcase PBEMoveEffect.Acrobatics:\n\t\t\tcase PBEMoveEffect.Bounce:\n\t\t\tcase PBEMoveEffect.BrickBreak:\n\t\t\tcase PBEMoveEffect.Brine:\n\t\t\tcase PBEMoveEffect.ChipAway:\n\t\t\tcase PBEMoveEffect.CrushGrip:\n\t\t\tcase PBEMoveEffect.Dig:\n\t\t\tcase PBEMoveEffect.Dive:\n\t\t\tcase PBEMoveEffect.Eruption:\n\t\t\tcase PBEMoveEffect.Facade:\n\t\t\tcase PBEMoveEffect.Feint:\n\t\t\tcase PBEMoveEffect.Flail:\n\t\t\tcase PBEMoveEffect.Fly:\n\t\t\tcase PBEMoveEffect.FoulPlay:\n\t\t\tcase PBEMoveEffect.Frustration:\n\t\t\tcase PBEMoveEffect.GrassKnot:\n\t\t\tcase PBEMoveEffect.HeatCrash:\n\t\t\tcase PBEMoveEffect.Hex:\n\t\t\tcase PBEMoveEffect.HiddenPower:\n\t\t\tcase PBEMoveEffect.Hit:\n\t\t\tcase PBEMoveEffect.Hit__2Times:\n\t\t\tcase PBEMoveEffect.Hit__2Times__MaybePoison:\n\t\t\tcase PBEMoveEffect.Hit__2To5Times:\n\t\t\tcase PBEMoveEffect.Hit__MaybeBurn:\n\t\t\tcase PBEMoveEffect.Hit__MaybeBurn__10PercentFlinch:\n\t\t\tcase PBEMoveEffect.Hit__MaybeBurnFreezeParalyze:\n\t\t\tcase PBEMoveEffect.Hit__MaybeConfuse:\n\t\t\tcase PBEMoveEffect.Hit__MaybeFlinch:\n\t\t\tcase PBEMoveEffect.Hit__MaybeFreeze:\n\t\t\tcase PBEMoveEffect.Hit__MaybeFreeze__10PercentFlinch:\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerTarget_ACC_By1:\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerTarget_ATK_By1:\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerTarget_DEF_By1:\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerTarget_SPATK_By1:\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerTarget_SPDEF_By1:\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerTarget_SPDEF_By2:\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerTarget_SPE_By1:\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerUser_ATK_DEF_By1:\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerUser_DEF_SPDEF_By1:\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerUser_SPATK_By2:\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerUser_SPE_By1:\n\t\t\tcase PBEMoveEffect.Hit__MaybeLowerUser_SPE_DEF_SPDEF_By1:\n\t\t\tcase PBEMoveEffect.Hit__MaybeParalyze:\n\t\t\tcase PBEMoveEffect.Hit__MaybeParalyze__10PercentFlinch:\n\t\t\tcase PBEMoveEffect.Hit__MaybePoison:\n\t\t\tcase PBEMoveEffect.Hit__MaybeRaiseUser_ATK_By1:\n\t\t\tcase PBEMoveEffect.Hit__MaybeRaiseUser_ATK_DEF_SPATK_SPDEF_SPE_By1:\n\t\t\tcase PBEMoveEffect.Hit__MaybeRaiseUser_DEF_By1:\n\t\t\tcase PBEMoveEffect.Hit__MaybeRaiseUser_SPATK_By1:\n\t\t\tcase PBEMoveEffect.Hit__MaybeRaiseUser_SPE_By1:\n\t\t\tcase PBEMoveEffect.Hit__MaybeToxic:\n\t\t\tcase PBEMoveEffect.HPDrain:\n\t\t\tcase PBEMoveEffect.Judgment:\n\t\t\tcase PBEMoveEffect.Magnitude:\n\t\t\tcase PBEMoveEffect.Payback:\n\t\t\tcase PBEMoveEffect.PayDay:\n\t\t\tcase PBEMoveEffect.Psyshock:\n\t\t\tcase PBEMoveEffect.Punishment:\n\t\t\tcase PBEMoveEffect.Recoil:\n\t\t\tcase PBEMoveEffect.Recoil__10PercentBurn:\n\t\t\tcase PBEMoveEffect.Recoil__10PercentParalyze:\n\t\t\tcase PBEMoveEffect.Retaliate:\n\t\t\tcase PBEMoveEffect.Return:\n\t\t\tcase PBEMoveEffect.SecretPower:\n\t\t\tcase PBEMoveEffect.ShadowForce:\n\t\t\tcase PBEMoveEffect.SmellingSalt:\n\t\t\tcase PBEMoveEffect.StoredPower:\n\t\t\tcase PBEMoveEffect.TechnoBlast:\n\t\t\tcase PBEMoveEffect.Venoshock:\n\t\t\tcase PBEMoveEffect.WakeUpSlap:\n\t\t\tcase PBEMoveEffect.WeatherBall:\n\t\t\t{\n\t\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t\t{\n\t\t\t\t\t// TODO: Favor hitting ally with move if waterabsorb/voltabsorb etc\n\t\t\t\t\t// TODO: Liquid ooze\n\t\t\t\t\t// TODO: Check items\n\t\t\t\t\t// TODO: Stat changes and accuracy (even thunder/guillotine accuracy)\n\t\t\t\t\t// TODO: Check base power specifically against hp remaining (include spread move damage reduction)\n\t\t\t\t\tPBETypeEffectiveness.IsAffectedByAttack(user, target, moveType, out float damageMultiplier, useKnownInfo: true);\n\t\t\t\t\tif (damageMultiplier <= 0) // (-infinity, 0.0] Ineffective\n\t\t\t\t\t{\n\t\t\t\t\t\tscore += target.Team == Trainer.Team ? 0 : -60;\n\t\t\t\t\t}\n\t\t\t\t\telse if (damageMultiplier <= 0.25) // (0.0, 0.25] NotVeryEffective\n\t\t\t\t\t{\n\t\t\t\t\t\tscore += target.Team == Trainer.Team ? -5 : -30;\n\t\t\t\t\t}\n\t\t\t\t\telse if (damageMultiplier < 1) // (0.25, 1.0) NotVeryEffective\n\t\t\t\t\t{\n\t\t\t\t\t\tscore += target.Team == Trainer.Team ? -10 : -10;\n\t\t\t\t\t}\n\t\t\t\t\telse if (damageMultiplier == 1) // [1.0, 1.0] Normal\n\t\t\t\t\t{\n\t\t\t\t\t\tscore += target.Team == Trainer.Team ? -15 : +10;\n\t\t\t\t\t}\n\t\t\t\t\telse if (damageMultiplier < 4) // (1.0, 4.0) SuperEffective\n\t\t\t\t\t{\n\t\t\t\t\t\tscore += target.Team == Trainer.Team ? -20 : +25;\n\t\t\t\t\t}\n\t\t\t\t\telse // [4.0, infinity) SuperEffective\n\t\t\t\t\t{\n\t\t\t\t\t\tscore += target.Team == Trainer.Team ? -30 : +40;\n\t\t\t\t\t}\n\t\t\t\t\tif (user.ReceivesSTAB(moveType) && damageMultiplier > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tscore += (user.Ability == PBEAbility.Adaptability ? 7 : 5) * (target.Team == Trainer.Team ? -1 : +1);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.Attract:\n\t\t\t{\n\t\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t\t{\n\t\t\t\t\t// TODO: Destiny knot\n\t\t\t\t\tif (target.IsAttractionPossible(user, useKnownInfo: true) == PBEResult.Success)\n\t\t\t\t\t{\n\t\t\t\t\t\tscore += target.Team == Trainer.Team ? -20 : +40;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tscore += target.Team == Trainer.Team ? 0 : -60;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.Burn:\n\t\t\t{\n\t\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t\t{\n\t\t\t\t\t// TODO: Heatproof, physical attacker\n\t\t\t\t\tif (target.IsBurnPossible(user, useKnownInfo: true) == PBEResult.Success)\n\t\t\t\t\t{\n\t\t\t\t\t\tscore += target.Team == Trainer.Team ? -20 : +40;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tscore += target.Team == Trainer.Team ? 0 : -60;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.ChangeTarget_ACC:\n\t\t\t{\n\t\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t\t{\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.Accuracy, mData.EffectParam, ref score);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.ChangeTarget_ATK:\n\t\t\t{\n\t\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t\t{\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.Attack, mData.EffectParam, ref score);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.ChangeTarget_DEF:\n\t\t\t{\n\t\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t\t{\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.Defense, mData.EffectParam, ref score);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.ChangeTarget_EVA:\n\t\t\tcase PBEMoveEffect.Minimize:\n\t\t\t{\n\t\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t\t{\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.Evasion, mData.EffectParam, ref score);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.ChangeTarget_SPATK:\n\t\t\t{\n\t\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t\t{\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.SpAttack, mData.EffectParam, ref score);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.ChangeTarget_SPDEF:\n\t\t\t{\n\t\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t\t{\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.SpDefense, mData.EffectParam, ref score);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.ChangeTarget_SPE:\n\t\t\t{\n\t\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t\t{\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.Speed, mData.EffectParam, ref score);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.Confuse:\n\t\t\tcase PBEMoveEffect.Flatter:\n\t\t\tcase PBEMoveEffect.Swagger:\n\t\t\t{\n\t\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t\t{\n\t\t\t\t\t// TODO: Only swagger/flatter if the opponent most likely won't use it against you\n\t\t\t\t\tif (target.IsConfusionPossible(user, useKnownInfo: true) == PBEResult.Success)\n\t\t\t\t\t{\n\t\t\t\t\t\tscore += target.Team == Trainer.Team ? -20 : +40;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tscore += target.Team == Trainer.Team ? 0 : -60;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.Growth:\n\t\t\t{\n\t\t\t\tint change = Trainer.Battle.WillLeafGuardActivate() ? +2 : +1;\n\t\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t\t{\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.Attack, change, ref score);\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.SpAttack, change, ref score);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.LeechSeed:\n\t\t\t{\n\t\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t\t{\n\t\t\t\t\tif (target.IsLeechSeedPossible(useKnownInfo: true) == PBEResult.Success)\n\t\t\t\t\t{\n\t\t\t\t\t\tscore += target.Team == Trainer.Team ? -20 : +40;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tscore += target.Team == Trainer.Team ? 0 : -60;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.LightScreen:\n\t\t\t{\n\t\t\t\tscore += Trainer.Team.TeamStatus.HasFlag(PBETeamStatus.LightScreen) || IsTeammateUsingEffect(actions, PBEMoveEffect.LightScreen) ? -100 : +40;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.LowerTarget_ATK_DEF_By1:\n\t\t\t{\n\t\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t\t{\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.Attack, -1, ref score);\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.Defense, -1, ref score);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.LowerTarget_DEF_SPDEF_By1_Raise_ATK_SPATK_SPE_By2:\n\t\t\t{\n\t\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t\t{\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.Defense, -1, ref score);\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.SpDefense, -1, ref score);\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.Attack, +2, ref score);\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.SpAttack, +2, ref score);\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.Speed, +2, ref score);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.LuckyChant:\n\t\t\t{\n\t\t\t\tscore += Trainer.Team.TeamStatus.HasFlag(PBETeamStatus.LuckyChant) || IsTeammateUsingEffect(actions, PBEMoveEffect.LuckyChant) ? -100 : +40;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.Moonlight:\n\t\t\tcase PBEMoveEffect.Rest:\n\t\t\tcase PBEMoveEffect.RestoreTargetHP:\n\t\t\tcase PBEMoveEffect.Roost:\n\t\t\t{\n\t\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t\t{\n\t\t\t\t\tif (target.Team == Trainer.Team)\n\t\t\t\t\t{\n\t\t\t\t\t\tscore += HPAware(target.HPPercentage, +45, -15);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tscore -= 100;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.Nothing:\n\t\t\tcase PBEMoveEffect.Teleport:\n\t\t\t{\n\t\t\t\tscore -= 100;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.Paralyze:\n\t\t\tcase PBEMoveEffect.ThunderWave:\n\t\t\t{\n\t\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t\t{\n\t\t\t\t\tbool tw = mData.Effect != PBEMoveEffect.ThunderWave || PBETypeEffectiveness.ThunderWaveTypeCheck(user, target, move, useKnownInfo: true) == PBEResult.Success;\n\t\t\t\t\tif (tw && target.IsParalysisPossible(user, useKnownInfo: true) == PBEResult.Success)\n\t\t\t\t\t{\n\t\t\t\t\t\tscore += target.Team == Trainer.Team ? -20 : +40;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tscore += target.Team == Trainer.Team ? 0 : -60;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.Poison:\n\t\t\tcase PBEMoveEffect.Toxic:\n\t\t\t{\n\t\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t\t{\n\t\t\t\t\t// TODO: Poison Heal\n\t\t\t\t\tif (target.IsPoisonPossible(user, useKnownInfo: true) == PBEResult.Success)\n\t\t\t\t\t{\n\t\t\t\t\t\tscore += target.Team == Trainer.Team ? -20 : +40;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tscore += target.Team == Trainer.Team ? 0 : -60;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.RaiseTarget_ATK_ACC_By1:\n\t\t\t{\n\t\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t\t{\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.Attack, +1, ref score);\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.Accuracy, +1, ref score);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.RaiseTarget_ATK_DEF_By1:\n\t\t\t{\n\t\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t\t{\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.Attack, +1, ref score);\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.Defense, +1, ref score);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.RaiseTarget_ATK_DEF_ACC_By1:\n\t\t\t{\n\t\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t\t{\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.Attack, +1, ref score);\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.Defense, +1, ref score);\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.Accuracy, +1, ref score);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.RaiseTarget_ATK_SPATK_By1:\n\t\t\t{\n\t\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t\t{\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.Attack, +1, ref score);\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.SpAttack, +1, ref score);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.RaiseTarget_ATK_SPE_By1:\n\t\t\t{\n\t\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t\t{\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.Attack, +1, ref score);\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.Speed, +1, ref score);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.RaiseTarget_DEF_SPDEF_By1:\n\t\t\t{\n\t\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t\t{\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.Defense, +1, ref score);\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.SpDefense, +1, ref score);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.RaiseTarget_SPATK_SPDEF_By1:\n\t\t\t{\n\t\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t\t{\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.SpAttack, +1, ref score);\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.SpDefense, +1, ref score);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.RaiseTarget_SPATK_SPDEF_SPE_By1:\n\t\t\t{\n\t\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t\t{\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.SpAttack, +1, ref score);\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.SpDefense, +1, ref score);\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.Speed, +1, ref score);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.RaiseTarget_SPE_By2_ATK_By1:\n\t\t\t{\n\t\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t\t{\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.Speed, +2, ref score);\n\t\t\t\t\tScoreStatChange(user, target, PBEStat.Attack, +1, ref score);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.Reflect:\n\t\t\t{\n\t\t\t\tscore += Trainer.Team.TeamStatus.HasFlag(PBETeamStatus.Reflect) || IsTeammateUsingEffect(actions, PBEMoveEffect.Reflect) ? -100 : +40;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.Safeguard:\n\t\t\t{\n\t\t\t\tscore += Trainer.Team.TeamStatus.HasFlag(PBETeamStatus.Safeguard) || IsTeammateUsingEffect(actions, PBEMoveEffect.Safeguard) ? -100 : +40;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.Sleep:\n\t\t\t{\n\t\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t\t{\n\t\t\t\t\t// TODO: Bad Dreams\n\t\t\t\t\tif (target.IsSleepPossible(user, useKnownInfo: true) == PBEResult.Success)\n\t\t\t\t\t{\n\t\t\t\t\t\tscore += target.Team == Trainer.Team ? -20 : +40;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tscore += target.Team == Trainer.Team ? 0 : -60;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.Substitute:\n\t\t\t{\n\t\t\t\tforeach (PBEBattlePokemon target in targets)\n\t\t\t\t{\n\t\t\t\t\tif (target.IsSubstitutePossible() == PBEResult.Success)\n\t\t\t\t\t{\n\t\t\t\t\t\tscore += target.Team == Trainer.Team ? HPAware(target.HPPercentage, -30, +50) : -60;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tscore += target.Team == Trainer.Team ? 0 : -20;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveEffect.BellyDrum:\n\t\t\tcase PBEMoveEffect.Camouflage:\n\t\t\tcase PBEMoveEffect.ChangeTarget_SPATK__IfAttractionPossible:\n\t\t\tcase PBEMoveEffect.Conversion:\n\t\t\tcase PBEMoveEffect.Curse:\n\t\t\tcase PBEMoveEffect.Endeavor:\n\t\t\tcase PBEMoveEffect.Entrainment:\n\t\t\tcase PBEMoveEffect.FinalGambit:\n\t\t\tcase PBEMoveEffect.FocusEnergy:\n\t\t\tcase PBEMoveEffect.Foresight:\n\t\t\tcase PBEMoveEffect.GastroAcid:\n\t\t\tcase PBEMoveEffect.Hail:\n\t\t\tcase PBEMoveEffect.Haze:\n\t\t\tcase PBEMoveEffect.HelpingHand:\n\t\t\tcase PBEMoveEffect.HPDrain__RequireSleep:\n\t\t\tcase PBEMoveEffect.LockOn:\n\t\t\tcase PBEMoveEffect.MagnetRise:\n\t\t\tcase PBEMoveEffect.Metronome:\n\t\t\tcase PBEMoveEffect.MiracleEye:\n\t\t\tcase PBEMoveEffect.Nightmare:\n\t\t\tcase PBEMoveEffect.OneHitKnockout:\n\t\t\tcase PBEMoveEffect.PainSplit:\n\t\t\tcase PBEMoveEffect.PowerTrick:\n\t\t\tcase PBEMoveEffect.Protect:\n\t\t\tcase PBEMoveEffect.PsychUp:\n\t\t\tcase PBEMoveEffect.Psywave:\n\t\t\tcase PBEMoveEffect.QuickGuard:\n\t\t\tcase PBEMoveEffect.RainDance:\n\t\t\tcase PBEMoveEffect.ReflectType:\n\t\t\tcase PBEMoveEffect.Refresh:\n\t\t\tcase PBEMoveEffect.RolePlay:\n\t\t\tcase PBEMoveEffect.Sandstorm:\n\t\t\tcase PBEMoveEffect.SeismicToss:\n\t\t\tcase PBEMoveEffect.Selfdestruct:\n\t\t\tcase PBEMoveEffect.SetDamage:\n\t\t\tcase PBEMoveEffect.SimpleBeam:\n\t\t\tcase PBEMoveEffect.Sketch:\n\t\t\tcase PBEMoveEffect.Snore:\n\t\t\tcase PBEMoveEffect.Soak:\n\t\t\tcase PBEMoveEffect.Spikes:\n\t\t\tcase PBEMoveEffect.StealthRock:\n\t\t\tcase PBEMoveEffect.SuckerPunch:\n\t\t\tcase PBEMoveEffect.SunnyDay:\n\t\t\tcase PBEMoveEffect.SuperFang:\n\t\t\tcase PBEMoveEffect.Tailwind:\n\t\t\tcase PBEMoveEffect.ToxicSpikes:\n\t\t\tcase PBEMoveEffect.Transform:\n\t\t\tcase PBEMoveEffect.TrickRoom:\n\t\t\tcase PBEMoveEffect.Whirlwind:\n\t\t\tcase PBEMoveEffect.WideGuard:\n\t\t\tcase PBEMoveEffect.WorrySeed:\n\t\t\t{\n\t\t\t\t// TODO\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: throw new InvalidDataException(nameof(IPBEMoveData.Effect));\n\t\t}\n\t\treturn score;\n\t}\n\tprivate static void ScoreStatChange(PBEBattlePokemon user, PBEBattlePokemon target, PBEStat stat, int change, ref float score)\n\t{\n\t\t// TODO: Do we need the stat change? Physical vs special vs status users, and base stats/transform stats/power trick stats\n\t\tsbyte original = target.GetStatChange(stat);\n\t\tsbyte maxStatChange = user.Battle.Settings.MaxStatChange;\n\t\tchange = Math.Max(-maxStatChange, Math.Min(maxStatChange, original + change)) - original;\n\t\tif (change != 0)\n\t\t{\n\t\t\tscore += (user.Team == target.Team ? +1 : -1) * change * 10;\n\t\t\tscore += HPAware(target.HPPercentage, -20, +10);\n\t\t}\n\t}\n\tprivate static bool IsTeammateUsingEffect(List<PBETurnAction> actions, PBEMoveEffect effect)\n\t{\n\t\treturn actions.FindIndex(a => a.Decision == PBETurnDecision.Fight && PBEDataProvider.Instance.GetMoveData(a.FightMove).Effect == effect) != -1;\n\t}\n\tprivate static float HPAware(float hpPercentage, float zeroPercentScore, float hundredPercentScore)\n\t{\n\t\treturn ((-zeroPercentScore + hundredPercentScore) * hpPercentage) + zeroPercentScore;\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine.DefaultData/AI/WildAI.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing System;\n\nnamespace Kermalis.PokemonBattleEngine.DefaultData.AI;\n\n// Wild Pokémon always select a random usable move (unless they are forced to use a move)\n// They will flee randomly based on their IPBEPokemonData.FleeRate only if it's a single battle and they are allowed to flee\npublic sealed class PBEDDWildAI\n{\n\tpublic PBETrainer Trainer { get; }\n\n\tpublic PBEDDWildAI(PBETrainer trainer)\n\t{\n\t\tTrainer = trainer;\n\t}\n\n\tpublic void CreateActions(bool allowFlee)\n\t{\n\t\tif (Trainer.Battle.BattleState != PBEBattleState.WaitingForActions)\n\t\t{\n\t\t\tthrow new InvalidOperationException($\"{nameof(Trainer.Battle.BattleState)} must be {PBEBattleState.WaitingForActions} to create actions.\");\n\t\t}\n\t\t// Try to flee if it's a single wild battle and the Pokémon is a runner\n\t\tif (allowFlee && Trainer.IsWild && Trainer.Battle.BattleFormat == PBEBattleFormat.Single && Trainer.IsFleeValid(out _))\n\t\t{\n\t\t\tPBEBattlePokemon user = Trainer.ActionsRequired[0];\n\t\t\tIPBEPokemonData pData = PBEDataProvider.Instance.GetPokemonData(user);\n\t\t\tif (PBEDataProvider.GlobalRandom.RandomBool(pData.FleeRate, byte.MaxValue))\n\t\t\t{\n\t\t\t\tif (!Trainer.SelectFleeIfValid(out string? valid))\n\t\t\t\t{\n\t\t\t\t\tthrow new Exception(\"Wild AI tried to flee but couldn't. - \" + valid);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t// Select a move\n\t\tvar actions = new PBETurnAction[Trainer.ActionsRequired.Count];\n\t\tfor (int i = 0; i < actions.Length; i++)\n\t\t{\n\t\t\tPBEBattlePokemon user = Trainer.ActionsRequired[i];\n\t\t\t// If a Pokémon is forced to struggle, it must use Struggle\n\t\t\tif (user.IsForcedToStruggle())\n\t\t\t{\n\t\t\t\tactions[i] = new PBETurnAction(user, PBEMove.Struggle, PBEBattleUtils.GetPossibleTargets(user, user.GetMoveTargets(PBEMove.Struggle))[0]);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// If a Pokémon has a temp locked move (Dig, Dive, ShadowForce) it must be used\n\t\t\tif (user.TempLockedMove != PBEMove.None)\n\t\t\t{\n\t\t\t\tactions[i] = new PBETurnAction(user, user.TempLockedMove, user.TempLockedTargets);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// The Pokémon is free to fight, so pick a random move\n\t\t\tPBEMove[] usableMoves = user.GetUsableMoves();\n\t\t\tPBEMove move = PBEDataProvider.GlobalRandom.RandomElement(usableMoves);\n\t\t\tactions[i] = new PBETurnAction(user, move, PBEBattle.GetRandomTargetForMetronome(user, move, PBEDataProvider.GlobalRandom));\n\t\t}\n\t\tif (!Trainer.SelectActionsIfValid(out string? valid2, actions))\n\t\t{\n\t\t\tthrow new Exception(\"Wild AI created bad actions. - \" + valid2);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine.DefaultData/Data/BerryData.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data;\n\nnamespace Kermalis.PokemonBattleEngine.DefaultData.Data;\n\npublic sealed partial class PBEDDBerryData : IPBEBerryData\n{\n\tpublic byte Bitterness { get; }\n\tpublic byte Dryness { get; }\n\tpublic byte Sourness { get; }\n\tpublic byte Spicyness { get; }\n\tpublic byte Sweetness { get; }\n\tpublic byte NaturalGiftPower { get; }\n\tpublic PBEType NaturalGiftType { get; }\n\n\tprivate PBEDDBerryData(byte naturalGiftPower, PBEType naturalGiftType,\n\t\tbyte bitterness = 0, byte dryness = 0, byte sourness = 0, byte spicyness = 0, byte sweetness = 0)\n\t{\n\t\tBitterness = bitterness;\n\t\tDryness = dryness;\n\t\tSourness = sourness;\n\t\tSpicyness = spicyness;\n\t\tSweetness = sweetness;\n\t\tNaturalGiftPower = naturalGiftPower;\n\t\tNaturalGiftType = naturalGiftType;\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine.DefaultData/Data/BerryData_Data.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\n\nnamespace Kermalis.PokemonBattleEngine.DefaultData.Data;\n\npublic sealed partial class PBEDDBerryData\n{\n\tpublic static ReadOnlyDictionary<PBEItem, PBEDDBerryData> Data { get; } = new(new Dictionary<PBEItem, PBEDDBerryData>\n\t{\n\t\t{ PBEItem.AguavBerry, new PBEDDBerryData(60, PBEType.Dragon, bitterness: 15) },\n\t\t{ PBEItem.ApicotBerry, new PBEDDBerryData(80, PBEType.Ground, spicyness: 10, dryness: 30, sourness: 30) },\n\t\t{ PBEItem.AspearBerry, new PBEDDBerryData(60, PBEType.Ice, sourness: 10) },\n\t\t{ PBEItem.BabiriBerry, new PBEDDBerryData(60, PBEType.Steel, spicyness: 25, dryness: 10) },\n\t\t{ PBEItem.BelueBerry, new PBEDDBerryData(80, PBEType.Electric, spicyness: 10, sourness: 30) },\n\t\t{ PBEItem.BlukBerry, new PBEDDBerryData(70, PBEType.Fire, dryness: 10, sweetness: 10) },\n\t\t{ PBEItem.ChartiBerry, new PBEDDBerryData(60, PBEType.Rock, spicyness: 10, dryness: 20) },\n\t\t{ PBEItem.CheriBerry, new PBEDDBerryData(60, PBEType.Fire, spicyness: 10) },\n\t\t{ PBEItem.ChestoBerry, new PBEDDBerryData(60, PBEType.Water, dryness: 10) },\n\t\t{ PBEItem.ChilanBerry, new PBEDDBerryData(60, PBEType.Normal, dryness: 25, sweetness: 10) },\n\t\t{ PBEItem.ChopleBerry, new PBEDDBerryData(60, PBEType.Fighting, spicyness: 15, bitterness: 10) },\n\t\t{ PBEItem.CobaBerry, new PBEDDBerryData(60, PBEType.Flying, dryness: 10, bitterness: 15) },\n\t\t{ PBEItem.ColburBerry, new PBEDDBerryData(60, PBEType.Dark, bitterness: 10, sourness: 20) },\n\t\t{ PBEItem.CornnBerry, new PBEDDBerryData(70, PBEType.Bug, dryness: 20, sweetness: 10) },\n\t\t{ PBEItem.CustapBerry, new PBEDDBerryData(80, PBEType.Ghost, sweetness: 40, bitterness: 10) },\n\t\t{ PBEItem.DurinBerry, new PBEDDBerryData(80, PBEType.Water, bitterness: 30, sourness: 10) },\n\t\t{ PBEItem.EnigmaBerry, new PBEDDBerryData(80, PBEType.Bug, spicyness: 40, dryness: 10) },\n\t\t{ PBEItem.FigyBerry, new PBEDDBerryData(60, PBEType.Bug, spicyness: 15) },\n\t\t{ PBEItem.GanlonBerry, new PBEDDBerryData(80, PBEType.Ice, dryness: 30, sweetness: 10, bitterness: 30) },\n\t\t{ PBEItem.GrepaBerry, new PBEDDBerryData(70, PBEType.Flying, dryness: 10, sweetness: 10, sourness: 10) },\n\t\t{ PBEItem.HabanBerry, new PBEDDBerryData(60, PBEType.Dragon, sweetness: 10, bitterness: 20) },\n\t\t{ PBEItem.HondewBerry, new PBEDDBerryData(70, PBEType.Ground, spicyness: 10, dryness: 10, bitterness: 10) },\n\t\t{ PBEItem.IapapaBerry, new PBEDDBerryData(60, PBEType.Dark, sourness: 15) },\n\t\t{ PBEItem.JabocaBerry, new PBEDDBerryData(80, PBEType.Dragon, bitterness: 40, sourness: 10) },\n\t\t{ PBEItem.KasibBerry, new PBEDDBerryData(60, PBEType.Ghost, dryness: 10, sweetness: 20) },\n\t\t{ PBEItem.KebiaBerry, new PBEDDBerryData(60, PBEType.Poison, dryness: 15, sourness: 10) },\n\t\t{ PBEItem.KelpsyBerry, new PBEDDBerryData(70, PBEType.Fighting, dryness: 10, bitterness: 10, sourness: 10) },\n\t\t{ PBEItem.LansatBerry, new PBEDDBerryData(80, PBEType.Flying, spicyness: 30, dryness: 10, sweetness: 30, bitterness: 10, sourness: 30) },\n\t\t{ PBEItem.LeppaBerry, new PBEDDBerryData(60, PBEType.Fighting, spicyness: 10, sweetness: 10, bitterness: 10, sourness: 10) },\n\t\t{ PBEItem.LiechiBerry, new PBEDDBerryData(80, PBEType.Grass, spicyness: 30, dryness: 10, sweetness: 30) },\n\t\t{ PBEItem.LumBerry, new PBEDDBerryData(60, PBEType.Flying, spicyness: 10, dryness: 10, sweetness: 10, bitterness: 10) },\n\t\t{ PBEItem.MagoBerry, new PBEDDBerryData(60, PBEType.Ghost, sweetness: 15) },\n\t\t{ PBEItem.MagostBerry, new PBEDDBerryData(70, PBEType.Rock, sweetness: 20, bitterness: 10) },\n\t\t{ PBEItem.MicleBerry, new PBEDDBerryData(80, PBEType.Rock, dryness: 40, sweetness: 10) },\n\t\t{ PBEItem.NanabBerry, new PBEDDBerryData(70, PBEType.Water, sweetness: 10, bitterness: 10) },\n\t\t{ PBEItem.NomelBerry, new PBEDDBerryData(70, PBEType.Dragon, spicyness: 10, sourness: 20) },\n\t\t{ PBEItem.OccaBerry, new PBEDDBerryData(60, PBEType.Fire, spicyness: 15, sweetness: 10) },\n\t\t{ PBEItem.OranBerry, new PBEDDBerryData(60, PBEType.Poison, spicyness: 10, dryness: 10, bitterness: 10, sourness: 10) },\n\t\t{ PBEItem.PamtreBerry, new PBEDDBerryData(70, PBEType.Steel, dryness: 30, sweetness: 10) },\n\t\t{ PBEItem.PasshoBerry, new PBEDDBerryData(60, PBEType.Water, dryness: 15, bitterness: 10) },\n\t\t{ PBEItem.PayapaBerry, new PBEDDBerryData(60, PBEType.Psychic, sweetness: 10, sourness: 15) },\n\t\t{ PBEItem.PechaBerry, new PBEDDBerryData(60, PBEType.Electric, sweetness: 10) },\n\t\t{ PBEItem.PersimBerry, new PBEDDBerryData(60, PBEType.Ground, spicyness: 10, dryness: 10, sweetness: 10, sourness: 10) },\n\t\t{ PBEItem.PetayaBerry, new PBEDDBerryData(80, PBEType.Poison, spicyness: 30, bitterness: 30, sourness: 10) },\n\t\t{ PBEItem.PinapBerry, new PBEDDBerryData(70, PBEType.Grass, spicyness: 10, sourness: 10) },\n\t\t{ PBEItem.PomegBerry, new PBEDDBerryData(70, PBEType.Ice, spicyness: 10, sweetness: 10, bitterness: 10) },\n\t\t{ PBEItem.QualotBerry, new PBEDDBerryData(70, PBEType.Poison, spicyness: 10, sweetness: 10, sourness: 10) },\n\t\t{ PBEItem.RabutaBerry, new PBEDDBerryData(70, PBEType.Ghost, bitterness: 20, sourness: 10) },\n\t\t{ PBEItem.RawstBerry, new PBEDDBerryData(60, PBEType.Grass, bitterness: 10) },\n\t\t{ PBEItem.RazzBerry, new PBEDDBerryData(60, PBEType.Steel, spicyness: 10, dryness: 10) },\n\t\t{ PBEItem.RindoBerry, new PBEDDBerryData(60, PBEType.Grass, spicyness: 10, bitterness: 15) },\n\t\t{ PBEItem.RowapBerry, new PBEDDBerryData(80, PBEType.Dark, spicyness: 10, sourness: 40) },\n\t\t{ PBEItem.SalacBerry, new PBEDDBerryData(80, PBEType.Fighting, sweetness: 30, bitterness: 10, sourness: 30) },\n\t\t{ PBEItem.ShucaBerry, new PBEDDBerryData(60, PBEType.Ground, spicyness: 10, sweetness: 15) },\n\t\t{ PBEItem.SitrusBerry, new PBEDDBerryData(60, PBEType.Psychic, dryness: 10, sweetness: 10, bitterness: 10, sourness: 10) },\n\t\t{ PBEItem.SpelonBerry, new PBEDDBerryData(70, PBEType.Dark, spicyness: 30, dryness: 10) },\n\t\t{ PBEItem.StarfBerry, new PBEDDBerryData(80, PBEType.Psychic, spicyness: 30, dryness: 10, sweetness: 30, bitterness: 10, sourness: 30) },\n\t\t{ PBEItem.TamatoBerry, new PBEDDBerryData(70, PBEType.Psychic, spicyness: 20, dryness: 10) },\n\t\t{ PBEItem.TangaBerry, new PBEDDBerryData(60, PBEType.Bug, spicyness: 20, sourness: 10) },\n\t\t{ PBEItem.WacanBerry, new PBEDDBerryData(60, PBEType.Electric, sweetness: 15, sourness: 10) },\n\t\t{ PBEItem.WatmelBerry, new PBEDDBerryData(80, PBEType.Fire, sweetness: 30, bitterness: 10) },\n\t\t{ PBEItem.WepearBerry, new PBEDDBerryData(70, PBEType.Electric, bitterness: 10, sourness: 10) },\n\t\t{ PBEItem.WikiBerry, new PBEDDBerryData(60, PBEType.Rock, dryness: 15) },\n\t\t{ PBEItem.YacheBerry, new PBEDDBerryData(60, PBEType.Ice, dryness: 10, sourness: 15) }\n\t});\n}\n"
  },
  {
    "path": "PokemonBattleEngine.DefaultData/Data/EXPTables.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data;\nusing System;\n\nnamespace Kermalis.PokemonBattleEngine.DefaultData.Data;\n\npublic static class PBEDDEXPTables\n{\n\t#region Tables\n\n\tprivate static ReadOnlySpan<uint> ErraticTable => new uint[100]\n\t{\n\t\t0,\n\t\t15,\n\t\t52,\n\t\t122,\n\t\t237,\n\t\t406,\n\t\t637,\n\t\t942,\n\t\t1_326,\n\t\t1_800,\n\t\t2_369,\n\t\t3_041,\n\t\t3_822,\n\t\t4_719,\n\t\t5_737,\n\t\t6_881,\n\t\t8_155,\n\t\t9_564,\n\t\t11_111,\n\t\t12_800,\n\t\t14_632,\n\t\t16_610,\n\t\t18_737,\n\t\t21_012,\n\t\t23_437,\n\t\t26_012,\n\t\t28_737,\n\t\t31_610,\n\t\t34_632,\n\t\t37_800,\n\t\t41_111,\n\t\t44_564,\n\t\t48_155,\n\t\t51_881,\n\t\t55_737,\n\t\t59_719,\n\t\t63_822,\n\t\t68_041,\n\t\t72_369,\n\t\t76_800,\n\t\t81_326,\n\t\t85_942,\n\t\t90_637,\n\t\t95_406,\n\t\t100_237,\n\t\t105_122,\n\t\t110_052,\n\t\t115_015,\n\t\t120_001,\n\t\t125_000,\n\t\t131_324,\n\t\t137_795,\n\t\t144_410,\n\t\t151_165,\n\t\t158_056,\n\t\t165_079,\n\t\t172_229,\n\t\t179_503,\n\t\t186_894,\n\t\t194_400,\n\t\t202_013,\n\t\t209_728,\n\t\t217_540,\n\t\t225_443,\n\t\t233_431,\n\t\t241_496,\n\t\t249_633,\n\t\t257_834,\n\t\t267_406,\n\t\t276_458,\n\t\t286_328,\n\t\t296_358,\n\t\t305_767,\n\t\t316_074,\n\t\t326_531,\n\t\t336_255,\n\t\t346_965,\n\t\t357_812,\n\t\t367_807,\n\t\t378_880,\n\t\t390_077,\n\t\t400_293,\n\t\t411_686,\n\t\t423_190,\n\t\t433_572,\n\t\t445_239,\n\t\t457_001,\n\t\t467_489,\n\t\t479_378,\n\t\t491_346,\n\t\t501_878,\n\t\t513_934,\n\t\t526_049,\n\t\t536_557,\n\t\t548_720,\n\t\t560_922,\n\t\t571_333,\n\t\t583_539,\n\t\t591_882,\n\t\t600_000\n\t};\n\n\tprivate static ReadOnlySpan<uint> FastTable => new uint[100]\n\t{\n\t\t0,\n\t\t6,\n\t\t21,\n\t\t51,\n\t\t100,\n\t\t172,\n\t\t274,\n\t\t409,\n\t\t583,\n\t\t800,\n\t\t1_064,\n\t\t1_382,\n\t\t1_757,\n\t\t2_195,\n\t\t2_700,\n\t\t3_276,\n\t\t3_930,\n\t\t4_665,\n\t\t5_487,\n\t\t6_400,\n\t\t7_408,\n\t\t8_518,\n\t\t9_733,\n\t\t11_059,\n\t\t12_500,\n\t\t14_060,\n\t\t15_746,\n\t\t17_561,\n\t\t19_511,\n\t\t21_600,\n\t\t23_832,\n\t\t26_214,\n\t\t28_749,\n\t\t31_443,\n\t\t34_300,\n\t\t37_324,\n\t\t40_522,\n\t\t43_897,\n\t\t47_455,\n\t\t51_200,\n\t\t55_136,\n\t\t59_270,\n\t\t63_605,\n\t\t68_147,\n\t\t72_900,\n\t\t77_868,\n\t\t83_058,\n\t\t88_473,\n\t\t94_119,\n\t\t100_000,\n\t\t106_120,\n\t\t112_486,\n\t\t119_101,\n\t\t125_971,\n\t\t133_100,\n\t\t140_492,\n\t\t148_154,\n\t\t156_089,\n\t\t164_303,\n\t\t172_800,\n\t\t181_584,\n\t\t190_662,\n\t\t200_037,\n\t\t209_715,\n\t\t219_700,\n\t\t229_996,\n\t\t240_610,\n\t\t251_545,\n\t\t262_807,\n\t\t274_400,\n\t\t286_328,\n\t\t298_598,\n\t\t311_213,\n\t\t324_179,\n\t\t337_500,\n\t\t351_180,\n\t\t365_226,\n\t\t379_641,\n\t\t394_431,\n\t\t409_600,\n\t\t425_152,\n\t\t441_094,\n\t\t457_429,\n\t\t474_163,\n\t\t491_300,\n\t\t508_844,\n\t\t526_802,\n\t\t545_177,\n\t\t563_975,\n\t\t583_200,\n\t\t602_856,\n\t\t622_950,\n\t\t643_485,\n\t\t664_467,\n\t\t685_900,\n\t\t707_788,\n\t\t730_138,\n\t\t752_953,\n\t\t776_239,\n\t\t800_000\n\t};\n\n\tprivate static ReadOnlySpan<uint> MediumFastTable => new uint[100]\n\t{\n\t\t0,\n\t\t8,\n\t\t27,\n\t\t64,\n\t\t125,\n\t\t216,\n\t\t343,\n\t\t512,\n\t\t729,\n\t\t1_000,\n\t\t1_331,\n\t\t1_728,\n\t\t2_197,\n\t\t2_744,\n\t\t3_375,\n\t\t4_096,\n\t\t4_913,\n\t\t5_832,\n\t\t6_859,\n\t\t8_000,\n\t\t9_261,\n\t\t10_648,\n\t\t12_167,\n\t\t13_824,\n\t\t15_625,\n\t\t17_576,\n\t\t19_683,\n\t\t21_952,\n\t\t24_389,\n\t\t27_000,\n\t\t29_791,\n\t\t32_768,\n\t\t35_937,\n\t\t39_304,\n\t\t42_875,\n\t\t46_656,\n\t\t50_653,\n\t\t54_872,\n\t\t59_319,\n\t\t64_000,\n\t\t68_921,\n\t\t74_088,\n\t\t79_507,\n\t\t85_184,\n\t\t91_125,\n\t\t97_336,\n\t\t103_823,\n\t\t110_592,\n\t\t117_649,\n\t\t125_000,\n\t\t132_651,\n\t\t140_608,\n\t\t148_877,\n\t\t157_464,\n\t\t166_375,\n\t\t175_616,\n\t\t185_193,\n\t\t195_112,\n\t\t205_379,\n\t\t216_000,\n\t\t226_981,\n\t\t238_328,\n\t\t250_047,\n\t\t262_144,\n\t\t274_625,\n\t\t287_496,\n\t\t300_763,\n\t\t314_432,\n\t\t328_509,\n\t\t343_000,\n\t\t357_911,\n\t\t373_248,\n\t\t389_017,\n\t\t405_224,\n\t\t421_875,\n\t\t438_976,\n\t\t456_533,\n\t\t474_552,\n\t\t493_039,\n\t\t512_000,\n\t\t531_441,\n\t\t551_368,\n\t\t571_787,\n\t\t592_704,\n\t\t614_125,\n\t\t636_056,\n\t\t658_503,\n\t\t681_472,\n\t\t704_969,\n\t\t729_000,\n\t\t753_571,\n\t\t778_688,\n\t\t804_357,\n\t\t830_584,\n\t\t857_375,\n\t\t884_736,\n\t\t912_673,\n\t\t941_192,\n\t\t970_299,\n\t\t1_000_000\n\t};\n\n\tprivate static ReadOnlySpan<uint> MediumSlowTable => new uint[100]\n\t{\n\t\t0,\n\t\t9,\n\t\t57,\n\t\t96,\n\t\t135,\n\t\t179,\n\t\t236,\n\t\t314,\n\t\t419,\n\t\t560,\n\t\t742,\n\t\t973,\n\t\t1_261,\n\t\t1_612,\n\t\t2_035,\n\t\t2_535,\n\t\t3_120,\n\t\t3_798,\n\t\t4_575,\n\t\t5_460,\n\t\t6_458,\n\t\t7_577,\n\t\t8_825,\n\t\t10_208,\n\t\t11_735,\n\t\t13_411,\n\t\t15_244,\n\t\t17_242,\n\t\t19_411,\n\t\t21_760,\n\t\t24_294,\n\t\t27_021,\n\t\t29_949,\n\t\t33_084,\n\t\t36_435,\n\t\t40_007,\n\t\t43_808,\n\t\t47_846,\n\t\t52_127,\n\t\t56_660,\n\t\t61_450,\n\t\t66_505,\n\t\t71_833,\n\t\t77_440,\n\t\t83_335,\n\t\t89_523,\n\t\t96_012,\n\t\t102_810,\n\t\t109_923,\n\t\t117_360,\n\t\t125_126,\n\t\t133_229,\n\t\t141_677,\n\t\t150_476,\n\t\t159_635,\n\t\t169_159,\n\t\t179_056,\n\t\t189_334,\n\t\t199_999,\n\t\t211_060,\n\t\t222_522,\n\t\t234_393,\n\t\t246_681,\n\t\t259_392,\n\t\t272_535,\n\t\t286_115,\n\t\t300_140,\n\t\t314_618,\n\t\t329_555,\n\t\t344_960,\n\t\t360_838,\n\t\t377_197,\n\t\t394_045,\n\t\t411_388,\n\t\t429_235,\n\t\t447_591,\n\t\t466_464,\n\t\t485_862,\n\t\t505_791,\n\t\t526_260,\n\t\t547_274,\n\t\t568_841,\n\t\t590_969,\n\t\t613_664,\n\t\t636_935,\n\t\t660_787,\n\t\t685_228,\n\t\t710_266,\n\t\t735_907,\n\t\t762_160,\n\t\t789_030,\n\t\t816_525,\n\t\t844_653,\n\t\t873_420,\n\t\t902_835,\n\t\t932_903,\n\t\t963_632,\n\t\t995_030,\n\t\t1_027_103,\n\t\t1_059_860\n\t};\n\n\tprivate static ReadOnlySpan<uint> SlowTable => new uint[100]\n\t{\n\t\t0,\n\t\t10,\n\t\t33,\n\t\t80,\n\t\t156,\n\t\t270,\n\t\t428,\n\t\t640,\n\t\t911,\n\t\t1_250,\n\t\t1_663,\n\t\t2_160,\n\t\t2_746,\n\t\t3_430,\n\t\t4_218,\n\t\t5_120,\n\t\t6_141,\n\t\t7_290,\n\t\t8_573,\n\t\t10_000,\n\t\t11_576,\n\t\t13_310,\n\t\t15_208,\n\t\t17_280,\n\t\t19_531,\n\t\t21_970,\n\t\t24_603,\n\t\t27_440,\n\t\t30_486,\n\t\t33_750,\n\t\t37_238,\n\t\t40_960,\n\t\t44_921,\n\t\t49_130,\n\t\t53_593,\n\t\t58_320,\n\t\t63_316,\n\t\t68_590,\n\t\t74_148,\n\t\t80_000,\n\t\t86_151,\n\t\t92_610,\n\t\t99_383,\n\t\t106_480,\n\t\t113_906,\n\t\t121_670,\n\t\t129_778,\n\t\t138_240,\n\t\t147_061,\n\t\t156_250,\n\t\t165_813,\n\t\t175_760,\n\t\t186_096,\n\t\t196_830,\n\t\t207_968,\n\t\t219_520,\n\t\t231_491,\n\t\t243_890,\n\t\t256_723,\n\t\t270_000,\n\t\t283_726,\n\t\t297_910,\n\t\t312_558,\n\t\t327_680,\n\t\t343_281,\n\t\t359_370,\n\t\t375_953,\n\t\t393_040,\n\t\t410_636,\n\t\t428_750,\n\t\t447_388,\n\t\t466_560,\n\t\t486_271,\n\t\t506_530,\n\t\t527_343,\n\t\t548_720,\n\t\t570_666,\n\t\t593_190,\n\t\t616_298,\n\t\t640_000,\n\t\t664_301,\n\t\t689_210,\n\t\t714_733,\n\t\t740_880,\n\t\t767_656,\n\t\t795_070,\n\t\t823_128,\n\t\t851_840,\n\t\t881_211,\n\t\t911_250,\n\t\t941_963,\n\t\t973_360,\n\t\t1_005_446,\n\t\t1_038_230,\n\t\t1_071_718,\n\t\t1_105_920,\n\t\t1_140_841,\n\t\t1_176_490,\n\t\t1_212_873,\n\t\t1_250_000\n\t};\n\n\tprivate static ReadOnlySpan<uint> FluctuatingTable => new uint[100]\n\t{\n\t\t0,\n\t\t4,\n\t\t13,\n\t\t32,\n\t\t65,\n\t\t112,\n\t\t178,\n\t\t276,\n\t\t393,\n\t\t540,\n\t\t745,\n\t\t967,\n\t\t1_230,\n\t\t1_591,\n\t\t1_957,\n\t\t2_457,\n\t\t3_046,\n\t\t3_732,\n\t\t4_526,\n\t\t5_440,\n\t\t6_482,\n\t\t7_666,\n\t\t9_003,\n\t\t10_506,\n\t\t12_187,\n\t\t14_060,\n\t\t16_140,\n\t\t18_439,\n\t\t20_974,\n\t\t23_760,\n\t\t26_811,\n\t\t30_146,\n\t\t33_780,\n\t\t37_731,\n\t\t42_017,\n\t\t46_656,\n\t\t50_653,\n\t\t55_969,\n\t\t60_505,\n\t\t66_560,\n\t\t71_677,\n\t\t78_533,\n\t\t84_277,\n\t\t91_998,\n\t\t98_415,\n\t\t107_069,\n\t\t114_205,\n\t\t123_863,\n\t\t131_766,\n\t\t142_500,\n\t\t151_222,\n\t\t163_105,\n\t\t172_697,\n\t\t185_807,\n\t\t196_322,\n\t\t210_739,\n\t\t222_231,\n\t\t238_036,\n\t\t250_562,\n\t\t267_840,\n\t\t281_456,\n\t\t300_293,\n\t\t315_059,\n\t\t335_544,\n\t\t351_520,\n\t\t373_744,\n\t\t390_991,\n\t\t415_050,\n\t\t433_631,\n\t\t459_620,\n\t\t479_600,\n\t\t507_617,\n\t\t529_063,\n\t\t559_209,\n\t\t582_187,\n\t\t614_566,\n\t\t639_146,\n\t\t673_863,\n\t\t700_115,\n\t\t737_280,\n\t\t765_275,\n\t\t804_997,\n\t\t834_809,\n\t\t877_201,\n\t\t908_905,\n\t\t954_084,\n\t\t987_754,\n\t\t1_035_837,\n\t\t1_071_552,\n\t\t1_122_660,\n\t\t1_160_499,\n\t\t1_214_753,\n\t\t1_254_796,\n\t\t1_312_322,\n\t\t1_354_652,\n\t\t1_415_577,\n\t\t1_460_276,\n\t\t1_524_731,\n\t\t1_571_884,\n\t\t1_640_000\n\t};\n\n\t#endregion\n\n\tpublic static uint GetEXPRequired(PBEGrowthRate type, byte level)\n\t{\n\t\tif (type >= PBEGrowthRate.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(type));\n\t\t}\n\t\tif (level < 1 || level > 100)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(level));\n\t\t}\n\n\t\treturn GetTable(type)[level - 1];\n\t}\n\n\tpublic static byte GetEXPLevel(PBEGrowthRate type, uint exp)\n\t{\n\t\tif (type >= PBEGrowthRate.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(type));\n\t\t}\n\n\t\tReadOnlySpan<uint> table = GetTable(type);\n\t\tif (exp > table[100 - 1] || exp < table[1 - 1])\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(exp));\n\t\t}\n\n\t\tfor (byte i = 0; i < 99; i++)\n\t\t{\n\t\t\tuint cur = table[i];\n\t\t\tuint next = table[i + 1];\n\t\t\tif (exp >= cur && exp < next)\n\t\t\t{\n\t\t\t\treturn (byte)(i + 1);\n\t\t\t}\n\t\t}\n\t\treturn 100;\n\t}\n\n\tprivate static ReadOnlySpan<uint> GetTable(PBEGrowthRate type)\n\t{\n\t\tswitch (type)\n\t\t{\n\t\t\tcase PBEGrowthRate.Erratic: return ErraticTable;\n\t\t\tcase PBEGrowthRate.Fast: return FastTable;\n\t\t\tcase PBEGrowthRate.Fluctuating: return FluctuatingTable;\n\t\t\tcase PBEGrowthRate.MediumFast: return MediumFastTable;\n\t\t\tcase PBEGrowthRate.MediumSlow: return MediumSlowTable;\n\t\t\tcase PBEGrowthRate.Slow: return SlowTable;\n\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(type));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine.DefaultData/Data/EventPokemon.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\n\nnamespace Kermalis.PokemonBattleEngine.DefaultData.Data;\n\npublic sealed partial class PBEDDEventPokemon\n{\n\tpublic ReadOnlyCollection<byte> Generations { get; }\n\tpublic PBESpecies Species { get; }\n\tpublic byte Level { get; }\n\t/// <summary><see langword=\"null\"/> means the Pokémon can be shiny or not shiny</summary>\n\tpublic bool? Shiny { get; }\n\t/// <summary><see langword=\"null\"/> means the Pokémon can be male or female</summary>\n\tpublic PBEGender? Gender { get; }\n\tpublic ReadOnlyCollection<PBEAbility> PossibleAbilities { get; }\n\tpublic ReadOnlyCollection<PBENature> PossibleNatures { get; }\n\tpublic ReadOnlyCollection<byte?> IndividualValues { get; } // A stat being \"null\" means that stat is random\n\tpublic ReadOnlyCollection<PBEMove> Moves { get; }\n\n\tprivate PBEDDEventPokemon(IList<byte> generations, PBESpecies species, byte level, bool? shiny, PBEGender? gender, PBEAbility[] possibleAbilities, ReadOnlyCollection<PBENature> possibleNatures, byte?[] ivs, PBEMove[] moves)\n\t{\n\t\tGenerations = new ReadOnlyCollection<byte>(generations);\n\t\tSpecies = species;\n\t\tLevel = level;\n\t\tShiny = shiny;\n\t\tGender = gender;\n\t\tPossibleAbilities = new ReadOnlyCollection<PBEAbility>(possibleAbilities);\n\t\tPossibleNatures = possibleNatures;\n\t\tIndividualValues = new ReadOnlyCollection<byte?>(ivs);\n\t\tMoves = new ReadOnlyCollection<PBEMove>(moves);\n\t}\n\tprivate PBEDDEventPokemon(IList<byte> generations, PBESpecies species, byte level, bool? shiny, PBEGender? gender, PBEAbility[] possibleAbilities, PBENature[] possibleNatures, byte?[] ivs, PBEMove[] moves)\n\t\t: this(generations, species, level, shiny, gender, possibleAbilities, new ReadOnlyCollection<PBENature>(possibleNatures), ivs, moves)\n\t{\n\t\t//\n\t}\n\tprivate PBEDDEventPokemon(IList<byte> generations, PBESpecies species, byte level, bool? shiny, PBEGender? gender, PBEAbility[] possibleAbilities, PBEAlphabeticalList<PBENature> possibleNatures, byte?[] ivs, PBEMove[] moves)\n\t\t: this(generations, species, level, shiny, gender, possibleAbilities, possibleNatures.AsReadOnly(), ivs, moves)\n\t{\n\t\t//\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine.DefaultData/Data/EventPokemon_Data.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Data.Utils;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\n\nnamespace Kermalis.PokemonBattleEngine.DefaultData.Data;\n\npublic sealed partial class PBEDDEventPokemon\n{\n\tpublic static ReadOnlyDictionary<PBESpecies, ReadOnlyCollection<PBEDDEventPokemon>> Events { get; } = new(new Dictionary<PBESpecies, ReadOnlyCollection<PBEDDEventPokemon>>\n\t{\n\t\t{\n\t\t\tPBESpecies.Absol,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // 5th anniversary egg\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Absol, 5, null, null, new PBEAbility[] { PBEAbility.Pressure }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Scratch, PBEMove.Leer, PBEMove.Spite, PBEMove.None }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // 5th anniversary egg | Pokémon Stamp RS magazine raffle\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Absol, 5, null, null, new PBEAbility[] { PBEAbility.Pressure }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Scratch, PBEMove.Leer, PBEMove.Wish, PBEMove.None }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Pokémon Box promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Absol, 35, false, null, new PBEAbility[] { PBEAbility.Pressure }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.RazorWind, PBEMove.Bite, PBEMove.SwordsDance, PBEMove.Spite }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Pokémon Box promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Absol, 35, false, null, new PBEAbility[] { PBEAbility.Pressure }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.RazorWind, PBEMove.Bite, PBEMove.SwordsDance, PBEMove.Wish }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Journey across America | Party of the decade\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Absol, 70, false, null, new PBEAbility[] { PBEAbility.Pressure }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.DoubleTeam, PBEMove.Slash, PBEMove.FutureSight, PBEMove.PerishSong }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Arceus,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Jewel of Life promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 4 }, PBESpecies.Arceus, 100, false, PBEGender.Genderless, new PBEAbility[] { PBEAbility.Multitype }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Judgment, PBEMove.RoarOfTime, PBEMove.SpacialRend, PBEMove.ShadowForce }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Global Link promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Arceus, 100, false, PBEGender.Genderless, new PBEAbility[] { PBEAbility.Multitype }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Recover, PBEMove.HyperBeam, PBEMove.PerishSong, PBEMove.Judgment }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Audino,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // 2010 birthday\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Audino, 30, false, PBEGender.Female, new PBEAbility[] { PBEAbility.Healer }, new PBENature[] { PBENature.Calm },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.HealPulse, PBEMove.HelpingHand, PBEMove.Refresh, PBEMove.DoubleSlap }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // 2011 birthday | 2012 birthday\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Audino, 30, false, PBEGender.Female, new PBEAbility[] { PBEAbility.Healer }, new PBENature[] { PBENature.Jolly, PBENature.Serious },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.HealPulse, PBEMove.HelpingHand, PBEMove.Refresh, PBEMove.Present }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Axew,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Iris's Axew\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Axew, 1, null, PBEGender.Male, new PBEAbility[] { PBEAbility.MoldBreaker }, new PBENature[] { PBENature.Naive },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, 31 }, new PBEMove[] { PBEMove.Scratch, PBEMove.DragonRage, PBEMove.None, PBEMove.None }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Pokémon Searcher BW promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Axew, 10, false, null, new PBEAbility[] { PBEAbility.MoldBreaker }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.DragonRage, PBEMove.Return, PBEMove.Endure, PBEMove.DragonClaw }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Best Wishes Iris's Axew\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Axew, 30, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Rivalry }, new PBENature[] { PBENature.Naive },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.DragonRage, PBEMove.Scratch, PBEMove.Outrage, PBEMove.GigaImpact }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Azurill,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // PCNY\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Azurill, 5, false, null, new PBEAbility[] { PBEAbility.HugePower, PBEAbility.ThickFat }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Splash, PBEMove.Charm, PBEMove.None, PBEMove.None }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Blastoise,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Journey across America | Party of the decade\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Blastoise, 70, false, null, new PBEAbility[] { PBEAbility.Torrent }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Protect, PBEMove.RainDance, PBEMove.SkullBash, PBEMove.HydroPump }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Blaziken,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Journey across America | Party of the decade\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Blaziken, 70, false, null, new PBEAbility[] { PBEAbility.Blaze }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.BlazeKick, PBEMove.Slash, PBEMove.MirrorMove, PBEMove.SkyUppercut }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Train station\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Blaziken, 50, null, null, new PBEAbility[] { PBEAbility.Blaze }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.FlareBlitz, PBEMove.HiJumpKick, PBEMove.ThunderPunch, PBEMove.StoneEdge }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Bulbasaur,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Gather more Pokémon\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Bulbasaur, 10, false, null, new PBEAbility[] { PBEAbility.Overgrow }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Tackle, PBEMove.Growl, PBEMove.LeechSeed, PBEMove.VineWhip }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Journey across America\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Bulbasaur, 70, false, null, new PBEAbility[] { PBEAbility.Overgrow }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.SweetScent, PBEMove.Growth, PBEMove.Synthesis, PBEMove.SolarBeam }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Kanto starter egg\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Bulbasaur, 1, null, null, new PBEAbility[] { PBEAbility.Overgrow }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, 31, null, null, null }, new PBEMove[] { PBEMove.FalseSwipe, PBEMove.Block, PBEMove.FrenzyPlant, PBEMove.WeatherBall }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Global Link promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Bulbasaur, 10, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Chlorophyll }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Tackle, PBEMove.Growl, PBEMove.LeechSeed, PBEMove.VineWhip }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Chandelure,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Powerful Tag\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Chandelure, 50, false, PBEGender.Female, new PBEAbility[] { PBEAbility.FlashFire }, new PBENature[] { PBENature.Modest },\n\t\t\t\t\tnew byte?[] { null, null, null, 31, null, null }, new PBEMove[] { PBEMove.HeatWave, PBEMove.ShadowBall, PBEMove.EnergyBall, PBEMove.Psychic }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Chansey,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // PCNY\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Chansey, 5, null, PBEGender.Female, new PBEAbility[] { PBEAbility.NaturalCure, PBEAbility.SereneGrace }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.SweetScent, PBEMove.Wish, PBEMove.None, PBEMove.None }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Gather more Pokémon\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Chansey, 10, false, PBEGender.Female, new PBEAbility[] { PBEAbility.NaturalCure, PBEAbility.SereneGrace }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Pound, PBEMove.Growl, PBEMove.TailWhip, PBEMove.Refresh }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Charizard,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Journey across America | Top 10 distribution | Party of the decade\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Charizard, 70, false, null, new PBEAbility[] { PBEAbility.Blaze }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.WingAttack, PBEMove.Slash, PBEMove.DragonRage, PBEMove.FireSpin }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Charmander,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Gather more Pokémon\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Charmander, 10, false, null, new PBEAbility[] { PBEAbility.Blaze }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Scratch, PBEMove.Growl, PBEMove.Ember, PBEMove.None }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // 2007 birthday | 2008 birthday | 2009 birthday | 2010 birthday\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 4 }, PBESpecies.Charmander, 40, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Blaze }, new PBENature[] { PBENature.Hardy, PBENature.Mild, PBENature.Naive, PBENature.Naughty },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Return, PBEMove.HiddenPower, PBEMove.QuickAttack, PBEMove.Howl }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Kanto starter eggs\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Charmander, 1, null, null, new PBEAbility[] { PBEAbility.Blaze }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, 31 }, new PBEMove[] { PBEMove.FalseSwipe, PBEMove.Block, PBEMove.BlastBurn, PBEMove.Acrobatics }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Global Link promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Charmander, 10, false, PBEGender.Male, new PBEAbility[] { PBEAbility.SolarPower }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Scratch, PBEMove.Growl, PBEMove.Ember, PBEMove.SmokeScreen }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Chimchar,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // 2009 birthday | 2010 birthday\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 4 }, PBESpecies.Chimchar, 40, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Blaze }, new PBENature[] { PBENature.Hardy, PBENature.Mild },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Flamethrower, PBEMove.ThunderPunch, PBEMove.GrassKnot, PBEMove.HelpingHand }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Global Link promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Chimchar, 10, false, PBEGender.Male, new PBEAbility[] { PBEAbility.IronFist }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Scratch, PBEMove.Leer, PBEMove.Ember, PBEMove.Taunt }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Global Link promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Chimchar, 10, false, PBEGender.Male, new PBEAbility[] { PBEAbility.IronFist }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Leer, PBEMove.Ember, PBEMove.Taunt, PBEMove.FakeOut }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Cradily,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // PCNY\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Cradily, 40, false, null, new PBEAbility[] { PBEAbility.SuctionCups }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Acid, PBEMove.Ingrain, PBEMove.ConfuseRay, PBEMove.Amnesia }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Cresselia,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // World championships 2013\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Cresselia, 68, false, PBEGender.Female, new PBEAbility[] { PBEAbility.Levitate }, new PBENature[] { PBENature.Modest },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.IceBeam, PBEMove.Psyshock, PBEMove.EnergyBall, PBEMove.HiddenPower }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Crobat,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // World championships 2010\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 4 }, PBESpecies.Crobat, 30, false, PBEGender.Male, new PBEAbility[] { PBEAbility.InnerFocus }, new PBENature[] { PBENature.Timid },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.HeatWave, PBEMove.AirSlash, PBEMove.SludgeBomb, PBEMove.SuperFang }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Cubchoo,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Pokémon Smash! Cubchoo\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Cubchoo, 15, false, null, new PBEAbility[] { PBEAbility.SnowCloak }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.PowderSnow, PBEMove.Growl, PBEMove.Bide, PBEMove.IcyWind }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Darkrai,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // The Rise of Darkrai promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 4 }, PBESpecies.Darkrai, 50, false, PBEGender.Genderless, new PBEAbility[] { PBEAbility.BadDreams }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.RoarOfTime, PBEMove.SpacialRend, PBEMove.Nightmare, PBEMove.Hypnosis }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Almia Darkrai\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 4 }, PBESpecies.Darkrai, 50, false, PBEGender.Genderless, new PBEAbility[] { PBEAbility.BadDreams }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.DarkVoid, PBEMove.DarkPulse, PBEMove.ShadowBall, PBEMove.DoubleTeam }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Winter 2010 | Victini movie promotions | Winter 2011 | May 2012\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Darkrai, 50, false, PBEGender.Genderless, new PBEAbility[] { PBEAbility.BadDreams }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.DarkVoid, PBEMove.OminousWind, PBEMove.FaintAttack, PBEMove.Nightmare }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Deino,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Year of the dragon\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Deino, 1, true, null, new PBEAbility[] { PBEAbility.Hustle }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Tackle, PBEMove.DragonRage, PBEMove.None, PBEMove.None }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Dialga,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // 2013 shiny creation trio\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Dialga, 100, true, PBEGender.Genderless, new PBEAbility[] { PBEAbility.Pressure }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.DragonPulse, PBEMove.DracoMeteor, PBEMove.RoarOfTime, PBEMove.AuraSphere }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Druddigon,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Year of the dragon\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Druddigon, 1, true, null, new PBEAbility[] { PBEAbility.RoughSkin, PBEAbility.SheerForce }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Leer, PBEMove.Scratch, PBEMove.None, PBEMove.None }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Eevee,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Eevee collection promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 4 }, PBESpecies.Eevee, 10, false, PBEGender.Female, new PBEAbility[] { PBEAbility.Adaptability }, new PBENature[] { PBENature.Lonely },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Covet, PBEMove.Bite, PBEMove.HelpingHand, PBEMove.Attract }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // World championships 2010\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 4 }, PBESpecies.Eevee, 50, true, PBEGender.Male, new PBEAbility[] { PBEAbility.Adaptability }, new PBENature[] { PBENature.Hardy },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.IronTail, PBEMove.TrumpCard, PBEMove.Flail, PBEMove.QuickAttack }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Ikimono-gakari promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Eevee, 50, false, PBEGender.Female, new PBEAbility[] { PBEAbility.Adaptability }, new PBENature[] { PBENature.Hardy },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Sing, PBEMove.Return, PBEMove.EchoedVoice, PBEMove.Attract }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Emboar,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Pokémon Center 15th anniversary\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Emboar, 100, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Blaze }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.FlareBlitz, PBEMove.HammerArm, PBEMove.WildCharge, PBEMove.HeadSmash }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Empoleon,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Pokémon Center 15th anniversary\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Empoleon, 100, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Torrent }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.HydroPump, PBEMove.IceBeam, PBEMove.AquaJet, PBEMove.GrassKnot }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Espeon,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Journey across America | Party of the decade\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Espeon, 70, false, null, new PBEAbility[] { PBEAbility.Synchronize }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Psybeam, PBEMove.PsychUp, PBEMove.Psychic, PBEMove.MorningSun }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Global Link promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Espeon, 10, false, PBEGender.Male, new PBEAbility[] { PBEAbility.MagicBounce }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.TailWhip, PBEMove.Tackle, PBEMove.HelpingHand, PBEMove.SandAttack }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Farfetchd,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // PCNY\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Farfetchd, 5, null, null, new PBEAbility[] { PBEAbility.InnerFocus, PBEAbility.KeenEye }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Yawn, PBEMove.Wish, PBEMove.None, PBEMove.None }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Flareon,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Global Link promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Flareon, 10, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Guts }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.TailWhip, PBEMove.Tackle, PBEMove.HelpingHand, PBEMove.SandAttack }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Garchomp,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Strongest class\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Garchomp, 100, false, PBEGender.Male, new PBEAbility[] { PBEAbility.SandVeil }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Outrage, PBEMove.Earthquake, PBEMove.SwordsDance, PBEMove.StoneEdge }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Global Link promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Garchomp, 48, false, PBEGender.Male, new PBEAbility[] { PBEAbility.RoughSkin }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Slash, PBEMove.DragonClaw, PBEMove.Dig, PBEMove.Crunch }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Genesect,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Plasma Genesect\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Genesect, 50, false, PBEGender.Genderless, new PBEAbility[] { PBEAbility.Download }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.TechnoBlast, PBEMove.MagnetBomb, PBEMove.SolarBeam, PBEMove.SignalBeam }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Genesect movie promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Genesect, 100, true, PBEGender.Genderless, new PBEAbility[] { PBEAbility.Download }, new PBENature[] { PBENature.Hasty },\n\t\t\t\t\tnew byte?[] { null, 31, null, null, null, 31 }, new PBEMove[] { PBEMove.ExtremeSpeed, PBEMove.TechnoBlast, PBEMove.BlazeKick, PBEMove.ShiftGear }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Giratina,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // 2013 shiny creation trio\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Giratina, 100, true, PBEGender.Genderless, new PBEAbility[] { PBEAbility.Pressure }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.DragonPulse, PBEMove.DragonClaw, PBEMove.AuraSphere, PBEMove.ShadowForce }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Glaceon,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Global Link promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Glaceon, 10, false, PBEGender.Male, new PBEAbility[] { PBEAbility.IceBody }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.TailWhip, PBEMove.Tackle, PBEMove.HelpingHand, PBEMove.SandAttack }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Golurk,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Victini movies promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Golurk, 70, true, PBEGender.Genderless, new PBEAbility[] { PBEAbility.IronFist }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.ShadowPunch, PBEMove.HyperBeam, PBEMove.GyroBall, PBEMove.HammerArm }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Gorebyss,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // PCNY\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Gorebyss, 20, false, null, new PBEAbility[] { PBEAbility.SwiftSwim }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Whirlpool, PBEMove.Confusion, PBEMove.Agility, PBEMove.None }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Haxorus,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Iris's Haxorus\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Haxorus, 59, false, PBEGender.Female, new PBEAbility[] { PBEAbility.MoldBreaker }, new PBENature[] { PBENature.Naive },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Earthquake, PBEMove.DualChop, PBEMove.XScissor, PBEMove.DragonDance }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Huntail,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // PCNY\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Huntail, 20, false, null, new PBEAbility[] { PBEAbility.SwiftSwim }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Whirlpool, PBEMove.Bite, PBEMove.Screech, PBEMove.None }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Hydreigon,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Victini movies promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Hydreigon, 70, true, PBEGender.Male, new PBEAbility[] { PBEAbility.Levitate }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.HyperVoice, PBEMove.DragonBreath, PBEMove.Flamethrower, PBEMove.FocusBlast }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Infernape,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Pokémon Center 15th anniversary\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Infernape, 100, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Blaze }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.FireBlast, PBEMove.CloseCombat, PBEMove.Uturn, PBEMove.GrassKnot }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Jirachi,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Jirachi Wish Maker promotion | Wishmaker Jirachi | Channel Jirachi | 2004 Jirachi | 2005 Jirachi | 2006 Jirachi | 2007 Jirachi | 2008 Jirachi | 2010 (Korea) Jirachi\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3, 4 }, PBESpecies.Jirachi, 5, null, PBEGender.Genderless, new PBEAbility[] { PBEAbility.SereneGrace }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Wish, PBEMove.Confusion, PBEMove.Rest, PBEMove.None }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // PokéPark Jirachi\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Jirachi, 30, false, PBEGender.Genderless, new PBEAbility[] { PBEAbility.SereneGrace }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Wish, PBEMove.Psychic, PBEMove.HelpingHand, PBEMove.Rest }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // 2009 Jirachi | 2010 (rest of the world) Jirachi\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 4 }, PBESpecies.Jirachi, 5, false, PBEGender.Genderless, new PBEAbility[] { PBEAbility.SereneGrace }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Wish, PBEMove.Confusion, PBEMove.Rest, PBEMove.DracoMeteor }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Decolora Jirachi\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Jirachi, 50, false, PBEGender.Genderless, new PBEAbility[] { PBEAbility.SereneGrace }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.HealingWish, PBEMove.Psychic, PBEMove.Swift, PBEMove.MeteorMash }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Character Fair Jirachi\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Jirachi, 50, false, PBEGender.Genderless, new PBEAbility[] { PBEAbility.SereneGrace }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Wish, PBEMove.HealingWish, PBEMove.CosmicPower, PBEMove.MeteorMash }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // 2013 Jirachi\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Jirachi, 50, false, PBEGender.Genderless, new PBEAbility[] { PBEAbility.SereneGrace }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.DracoMeteor, PBEMove.MeteorMash, PBEMove.Wish, PBEMove.FollowMe }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Chilseok Jirachi\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Jirachi, 50, false, PBEGender.Genderless, new PBEAbility[] { PBEAbility.SereneGrace }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Wish, PBEMove.HealingWish, PBEMove.Swift, PBEMove.Return }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Jolteon,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Global Link promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Jolteon, 10, false, PBEGender.Male, new PBEAbility[] { PBEAbility.QuickFeet }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.TailWhip, PBEMove.Tackle, PBEMove.HelpingHand, PBEMove.SandAttack }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Karrablast,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Trade for Evolution!\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Karrablast, 30, false, null, new PBEAbility[] { PBEAbility.ShedSkin, PBEAbility.Swarm }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.FuryAttack, PBEMove.Headbutt, PBEMove.FalseSwipe, PBEMove.BugBuzz }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Summer 2011\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Karrablast, 50, false, null, new PBEAbility[] { PBEAbility.ShedSkin, PBEAbility.Swarm }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Megahorn, PBEMove.TakeDown, PBEMove.XScissor, PBEMove.Flail }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Keldeo,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Kyurem VS. The Sword of Justice promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Keldeo, 15, false, PBEGender.Genderless, new PBEAbility[] { PBEAbility.Justified }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.AquaJet, PBEMove.Leer, PBEMove.DoubleKick, PBEMove.BubbleBeam }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Winter 2013\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Keldeo, 50, false, PBEGender.Genderless, new PBEAbility[] { PBEAbility.Justified }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.SacredSword, PBEMove.HydroPump, PBEMove.AquaJet, PBEMove.SwordsDance }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Latias,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Journey across America | Top 10 distribution | Party of the decade\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Latias, 70, false, PBEGender.Female, new PBEAbility[] { PBEAbility.Levitate }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.MistBall, PBEMove.Psychic, PBEMove.Recover, PBEMove.Charm }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Latios,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Journey across America | Top 10 distribution | Party of the decade\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Latios, 70, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Levitate }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.LusterPurge, PBEMove.Psychic, PBEMove.Recover, PBEMove.DragonDance }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Leafeon,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Global Link promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Leafeon, 10, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Chlorophyll }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.TailWhip, PBEMove.Tackle, PBEMove.HelpingHand, PBEMove.SandAttack }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Liepard,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Se Jun's Liepard\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Liepard, 20, false, PBEGender.Female, new PBEAbility[] { PBEAbility.Prankster }, new PBENature[] { PBENature.Jolly },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.FakeOut, PBEMove.FoulPlay, PBEMove.Encore, PBEMove.Swagger }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Lileep,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Pokémon Adventure Camp fossil Pokémon\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Lileep, 15, false, null, new PBEAbility[] { PBEAbility.SuctionCups }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Recover, PBEMove.RockSlide, PBEMove.Constrict, PBEMove.Acid }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Lucario,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // PalCity Lucario\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 4 }, PBESpecies.Lucario, 50, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Steadfast }, new PBENature[] { PBENature.Modest },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.AuraSphere, PBEMove.DarkPulse, PBEMove.DragonPulse, PBEMove.WaterPulse }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // World championships 2008\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 4 }, PBESpecies.Lucario, 30, false, PBEGender.Male, new PBEAbility[] { PBEAbility.InnerFocus }, new PBENature[] { PBENature.Adamant },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.ForcePalm, PBEMove.BoneRush, PBEMove.SunnyDay, PBEMove.BlazeKick }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Global Link promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Lucario, 10, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Justified }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Detect, PBEMove.MetalClaw, PBEMove.Counter, PBEMove.BulletPunch }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Powerful Tag\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Lucario, 50, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Justified }, new PBENature[] { PBENature.Naughty },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.BulletPunch, PBEMove.CloseCombat, PBEMove.StoneEdge, PBEMove.ShadowClaw }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Magikarp,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // GTS Ryuuta\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 4 }, PBESpecies.Magikarp, 4, false, PBEGender.Male, new PBEAbility[] { PBEAbility.SwiftSwim }, new PBENature[] { PBENature.Modest },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Splash, PBEMove.None, PBEMove.None, PBEMove.None }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // GTS Nana\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 4 }, PBESpecies.Magikarp, 5, false, PBEGender.Female, new PBEAbility[] { PBEAbility.SwiftSwim }, new PBENature[] { PBENature.Lonely },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Splash, PBEMove.None, PBEMove.None, PBEMove.None }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // GTS Utz\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 4 }, PBESpecies.Magikarp, 5, false, PBEGender.Male, new PBEAbility[] { PBEAbility.SwiftSwim }, new PBENature[] { PBENature.Relaxed },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Splash, PBEMove.None, PBEMove.None, PBEMove.None }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // GTS Ruirui\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 4 }, PBESpecies.Magikarp, 6, false, PBEGender.Female, new PBEAbility[] { PBEAbility.SwiftSwim }, new PBENature[] { PBENature.Rash },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Splash, PBEMove.None, PBEMove.None, PBEMove.None }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // GTS Nory\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 4 }, PBESpecies.Magikarp, 7, false, PBEGender.Female, new PBEAbility[] { PBEAbility.SwiftSwim }, new PBENature[] { PBENature.Hardy },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Splash, PBEMove.None, PBEMove.None, PBEMove.None }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Pokémon Center relocation\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Magikarp, 99, true, null, new PBEAbility[] { PBEAbility.SwiftSwim }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Flail, PBEMove.HydroPump, PBEMove.Bounce, PBEMove.Splash }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Meloetta,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Kyruem VS. The Sword of Justice promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Meloetta, 15, false, PBEGender.Genderless, new PBEAbility[] { PBEAbility.SereneGrace }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Round, PBEMove.QuickAttack, PBEMove.Confusion, PBEMove.None }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Summer 2013\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Meloetta, 50, false, PBEGender.Genderless, new PBEAbility[] { PBEAbility.SereneGrace }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Round, PBEMove.TeeterDance, PBEMove.Psychic, PBEMove.CloseCombat }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Metagross,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Train station\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Metagross, 50, null, PBEGender.Genderless, new PBEAbility[] { PBEAbility.ClearBody }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.MeteorMash, PBEMove.Earthquake, PBEMove.BulletPunch, PBEMove.HammerArm }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Strongest class\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Metagross, 100, false, PBEGender.Genderless, new PBEAbility[] { PBEAbility.ClearBody }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.BulletPunch, PBEMove.ZenHeadbutt, PBEMove.HammerArm, PBEMove.IcePunch }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // World championships 2013\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Metagross, 45, true, PBEGender.Genderless, new PBEAbility[] { PBEAbility.ClearBody }, new PBENature[] { PBENature.Adamant },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.MeteorMash, PBEMove.Earthquake, PBEMove.ZenHeadbutt, PBEMove.Protect }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Global Link promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Metagross, 45, false, PBEGender.Genderless, new PBEAbility[] { PBEAbility.LightMetal }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.IronDefense, PBEMove.Agility, PBEMove.HammerArm, PBEMove.DoubleEdge }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Global Link promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Metagross, 45, false, PBEGender.Genderless, new PBEAbility[] { PBEAbility.LightMetal }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Psychic, PBEMove.MeteorMash, PBEMove.HammerArm, PBEMove.DoubleEdge }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Steven's Metagross\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Metagross, 58, false, PBEGender.Genderless, new PBEAbility[] { PBEAbility.ClearBody }, new PBENature[] { PBENature.Serious },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Earthquake, PBEMove.HyperBeam, PBEMove.Psychic, PBEMove.MeteorMash }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Metang,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Gale of Darkness demo promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Metang, 30, false, PBEGender.Genderless, new PBEAbility[] { PBEAbility.ClearBody }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.TakeDown, PBEMove.Confusion, PBEMove.MetalClaw, PBEMove.Refresh }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Minun,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // PokéPark Minun\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Minun, 5, null, null, new PBEAbility[] { PBEAbility.Minus }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Growl, PBEMove.ThunderWave, PBEMove.MudSport, PBEMove.None }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Gather more Pokémon\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Minun, 10, false, null, new PBEAbility[] { PBEAbility.Minus }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Growl, PBEMove.ThunderWave, PBEMove.QuickAttack, PBEMove.None }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Misdreavus,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Gather more Pokémon\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Misdreavus, 10, false, null, new PBEAbility[] { PBEAbility.Levitate }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Growl, PBEMove.Psywave, PBEMove.Spite, PBEMove.None }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Ninetales,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Powerful Tag\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Ninetales, 50, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Drought }, new PBENature[] { PBENature.Bold },\n\t\t\t\t\tnew byte?[] { null, null, 31, null, null, null }, new PBEMove[] { PBEMove.HeatWave, PBEMove.SolarBeam, PBEMove.Psyshock, PBEMove.WillOWisp }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Palkia,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // 2013 shiny creation trio\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Palkia, 100, true, PBEGender.Genderless, new PBEAbility[] { PBEAbility.Pressure }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.HydroPump, PBEMove.DracoMeteor, PBEMove.SpacialRend, PBEMove.AuraSphere }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Pansage,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Cilan's Pansage\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Pansage, 1, null, PBEGender.Male, new PBEAbility[] { PBEAbility.Gluttony }, new PBENature[] { PBENature.Brave },\n\t\t\t\t\tnew byte?[] { null, 31, null, null, null, null }, new PBEMove[] { PBEMove.BulletSeed, PBEMove.Bite, PBEMove.SolarBeam, PBEMove.Dig }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Best Wishes Cilan's Pansage\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Pansage, 30, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Gluttony }, new PBENature[] { PBENature.Serious },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.BulletSeed, PBEMove.SolarBeam, PBEMove.RockTomb, PBEMove.Dig }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Global Link promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Pansage, 10, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Overgrow }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Leer, PBEMove.Lick, PBEMove.VineWhip, PBEMove.LeafStorm }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Pichu,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // 5th anniversary egg | Pokémon Stamp RS magazine raffle\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Pichu, 5, null, null, new PBEAbility[] { PBEAbility.Static }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.ThunderShock, PBEMove.Charm, PBEMove.TeeterDance, PBEMove.None }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // 5th anniversary egg\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Pichu, 5, null, null, new PBEAbility[] { PBEAbility.Static }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.ThunderShock, PBEMove.Charm, PBEMove.Wish, PBEMove.None }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Pokémon Box bonus egg\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Pichu, 5, null, null, new PBEAbility[] { PBEAbility.Static }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.ThunderShock, PBEMove.Charm, PBEMove.Surf, PBEMove.None }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // PokéPark Pichu\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Pichu, 5, null, null, new PBEAbility[] { PBEAbility.Static }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.ThunderShock, PBEMove.Charm, PBEMove.FollowMe, PBEMove.None }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Red and Green 12th anniversary\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 4 }, PBESpecies.Pichu, 1, null, null, new PBEAbility[] { PBEAbility.Static }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.VoltTackle, PBEMove.Thunderbolt, PBEMove.GrassKnot, PBEMove.Return }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Jewel of Life promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 4 }, PBESpecies.Pichu, 30, true, PBEGender.Male, new PBEAbility[] { PBEAbility.Static }, new PBENature[] { PBENature.Jolly },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Charge, PBEMove.VoltTackle, PBEMove.Endeavor, PBEMove.Endure }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Pidove,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Ash's Pidove\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Pidove, 1, null, PBEGender.Female, new PBEAbility[] { PBEAbility.SuperLuck }, new PBENature[] { PBENature.Hardy },\n\t\t\t\t\tnew byte?[] { null, 31, null, null, null, null }, new PBEMove[] { PBEMove.Gust, PBEMove.QuickAttack, PBEMove.AirCutter, PBEMove.None }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Pikachu,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // PCNY stone promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Pikachu, 50, false, null, new PBEAbility[] { PBEAbility.Static }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Thunderbolt, PBEMove.Agility, PBEMove.Thunder, PBEMove.LightScreen }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // All Nippon Airways Pikachu (Gen 3)\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Pikachu, 10, false, null, new PBEAbility[] { PBEAbility.Static }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Fly, PBEMove.ThunderShock, PBEMove.TailWhip, PBEMove.ThunderWave }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Yokohama Pokémon Center opening\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Pikachu, 10, false, null, new PBEAbility[] { PBEAbility.Static }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.ThunderShock, PBEMove.Growl, PBEMove.ThunderWave, PBEMove.Surf }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // GW Festival | Sapporo Pokémon Center opening\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Pikachu, 10, false, null, new PBEAbility[] { PBEAbility.Static }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Growl, PBEMove.TailWhip, PBEMove.ThunderWave, PBEMove.Fly }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Gather more Pokémon | Colosseum Pikachu\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Pikachu, 10, false, null, new PBEAbility[] { PBEAbility.Static }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.ThunderShock, PBEMove.Growl, PBEMove.TailWhip, PBEMove.ThunderWave }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Journey across America | Top 10 distribution\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Pikachu, 70, false, null, new PBEAbility[] { PBEAbility.Static }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Thunderbolt, PBEMove.Agility, PBEMove.Thunder, PBEMove.LightScreen }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Party of the decade\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Pikachu, 70, false, null, new PBEAbility[] { PBEAbility.Static }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Thunderbolt, PBEMove.Thunder, PBEMove.LightScreen, PBEMove.Fly }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Battle Revolution Pikachu\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 4 }, PBESpecies.Pikachu, 10, false, PBEGender.Female, new PBEAbility[] { PBEAbility.Static }, new PBENature[] { PBENature.Hardy },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.VoltTackle, PBEMove.Surf, PBEMove.TailWhip, PBEMove.ThunderWave }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // TCG world championships 2007\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 4 }, PBESpecies.Pikachu, 50, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Static }, new PBENature[] { PBENature.Hardy },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Surf, PBEMove.Thunderbolt, PBEMove.LightScreen, PBEMove.QuickAttack }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Nintendo Spot promotion | Nintendo Zone promotion | 7-Eleven Pikachu\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 4 }, PBESpecies.Pikachu, 20, false, null, new PBEAbility[] { PBEAbility.Static }, new PBENature[] { PBENature.Bashful, PBENature.Docile },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.QuickAttack, PBEMove.ThunderShock, PBEMove.TailWhip, PBEMove.Present }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Yokohama Pokémon Center reopening | 2008 birthday | 2009 birthday\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 4 }, PBESpecies.Pikachu, 40, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Static }, new PBENature[] { PBENature.Mild, PBENature.Modest },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Surf, PBEMove.Thunder, PBEMove.Protect, PBEMove.None }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Sleeping Pikachu Collection promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 4 }, PBESpecies.Pikachu, 50, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Static }, new PBENature[] { PBENature.Relaxed },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Rest, PBEMove.SleepTalk, PBEMove.Yawn, PBEMove.Snore }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Character Fair Pikachu\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 4 }, PBESpecies.Pikachu, 30, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Static }, new PBENature[] { PBENature.Brave },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.VoltTackle, PBEMove.QuickAttack, PBEMove.Thunderbolt, PBEMove.IronTail }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Kyoto Cross Media Experience Pikachu\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 4 }, PBESpecies.Pikachu, 30, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Static }, new PBENature[] { PBENature.Naughty },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.LastResort, PBEMove.Present, PBEMove.Thunderbolt, PBEMove.QuickAttack }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Ario Pikachu\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 4 }, PBESpecies.Pikachu, 20, false, PBEGender.Female, new PBEAbility[] { PBEAbility.Static }, new PBENature[] { PBENature.Bashful },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Present, PBEMove.QuickAttack, PBEMove.ThunderWave, PBEMove.TailWhip }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Ash's Pikachu\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 4 }, PBESpecies.Pikachu, 50, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Static }, new PBENature[] { PBENature.Naughty },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.VoltTackle, PBEMove.IronTail, PBEMove.QuickAttack, PBEMove.Thunderbolt }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // All Nippon Airways Pikachu (Gen 5)\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Pikachu, 50, false, null, new PBEAbility[] { PBEAbility.Static }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Fly, PBEMove.IronTail, PBEMove.ElectroBall, PBEMove.QuickAttack }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Singing Pikachu\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Pikachu, 30, false, PBEGender.Female, new PBEAbility[] { PBEAbility.Lightningrod }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Sing, PBEMove.TeeterDance, PBEMove.Encore, PBEMove.ElectroBall }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // ExtremeSpeed Pikachu\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Pikachu, 50, null, PBEGender.Female, new PBEAbility[] { PBEAbility.Static }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.ExtremeSpeed, PBEMove.Thunderbolt, PBEMove.GrassKnot, PBEMove.BrickBreak }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Pikachu Festival | Pika Pika Carnival | Summer 2012 Pikachu | Strongest class\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Pikachu, 100, null, PBEGender.Female, new PBEAbility[] { PBEAbility.Static }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Thunder, PBEMove.VoltTackle, PBEMove.GrassKnot, PBEMove.QuickAttack }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // World championships 2012\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Pikachu, 50, false, PBEGender.Female, new PBEAbility[] { PBEAbility.Lightningrod }, new PBENature[] { PBENature.Timid },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Fly, PBEMove.Thunderbolt, PBEMove.GrassKnot, PBEMove.Protect }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Pokémon Center 15th anniversary\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Pikachu, 100, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Lightningrod }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.VoltTackle, PBEMove.QuickAttack, PBEMove.Feint, PBEMove.VoltSwitch }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Best Wishes Ash's Pikachu\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Pikachu, 50, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Static }, new PBENature[] { PBENature.Brave },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Thunderbolt, PBEMove.QuickAttack, PBEMove.IronTail, PBEMove.ElectroBall }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Global Link promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Pikachu, 10, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Lightningrod }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.ThunderShock, PBEMove.TailWhip, PBEMove.ThunderWave, PBEMove.Headbutt }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Piplup,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Pokémon Searcher BW promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Piplup, 15, null, null, new PBEAbility[] { PBEAbility.Torrent }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.HydroPump, PBEMove.FeatherDance, PBEMove.WaterSport, PBEMove.Peck }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Dawn's Piplup\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Piplup, 15, false, null, new PBEAbility[] { PBEAbility.Torrent }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Sing, PBEMove.Round, PBEMove.FeatherDance, PBEMove.Peck }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Global Link promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Piplup, 10, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Defiant }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Pound, PBEMove.Growl, PBEMove.Bubble, PBEMove.None }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Global Link promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Piplup, 10, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Defiant }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Pound, PBEMove.Growl, PBEMove.Bubble, PBEMove.FeatherDance }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Plusle,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // PokéPark Plusle\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Plusle, 5, null, null, new PBEAbility[] { PBEAbility.Plus }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Growl, PBEMove.ThunderWave, PBEMove.WaterSport, PBEMove.None }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Gather more Pokémon\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Plusle, 10, false, null, new PBEAbility[] { PBEAbility.Plus }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Growl, PBEMove.ThunderWave, PBEMove.QuickAttack, PBEMove.None }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Politoed,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Yamamoto's Tournament\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Politoed, 50, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Drizzle }, new PBENature[] { PBENature.Calm },\n\t\t\t\t\tnew byte?[] { 31, 13, 31, 5, 31, 5 }, new PBEMove[] { PBEMove.Scald, PBEMove.IceBeam, PBEMove.PerishSong, PBEMove.Protect }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Poliwag,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Egg Pokémon Present\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Poliwag, 5, null, null, new PBEAbility[] { PBEAbility.Damp, PBEAbility.WaterAbsorb }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Bubble, PBEMove.SweetKiss, PBEMove.None, PBEMove.None }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Psyduck,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Pokémon Trade and Battle Day\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Psyduck, 27, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Damp }, new PBENature[] { PBENature.Lax },\n\t\t\t\t\tnew byte?[] { 31, 16, 12, 29, 31, 14 }, new PBEMove[] { PBEMove.TailWhip, PBEMove.Disable, PBEMove.Confusion, PBEMove.Screech }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // PokéPark Psyduck\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Psyduck, 5, null, null, new PBEAbility[] { PBEAbility.CloudNine, PBEAbility.Damp }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.WaterSport, PBEMove.Scratch, PBEMove.TailWhip, PBEMove.MudSport }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // GTS Psyducks\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 4 }, PBESpecies.Psyduck, 1, false, null, new PBEAbility[] { PBEAbility.CloudNine, PBEAbility.Damp }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Psychic, PBEMove.ConfuseRay, PBEMove.Yawn, PBEMove.MudBomb }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Regirock,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Lucario and the Mystery of Mew promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Regirock, 40, false, PBEGender.Genderless, new PBEAbility[] { PBEAbility.ClearBody }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Curse, PBEMove.Superpower, PBEMove.AncientPower, PBEMove.HyperBeam }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Reshiram,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Victini movies promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Reshiram, 100, false, PBEGender.Genderless, new PBEAbility[] { PBEAbility.Turboblaze }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.BlueFlare, PBEMove.FusionFlare, PBEMove.Mist, PBEMove.DracoMeteor }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Riolu,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Pokémon Ranger\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 4 }, PBESpecies.Riolu, 40, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Steadfast }, new PBENature[] { PBENature.Serious },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.AuraSphere, PBEMove.ShadowClaw, PBEMove.BulletPunch, PBEMove.DrainPunch }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Rotom,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Best Wishes Professor Oak's Rotom\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Rotom, 10, false, PBEGender.Genderless, new PBEAbility[] { PBEAbility.Levitate }, new PBENature[] { PBENature.Naughty },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Uproar, PBEMove.Astonish, PBEMove.Trick, PBEMove.ThunderShock }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Samurott,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Pokémon Center 15th anniversary\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Samurott, 100, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Torrent }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.HydroPump, PBEMove.IceBeam, PBEMove.Megahorn, PBEMove.Superpower }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Scrafty,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // World championships 2011\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Scrafty, 50, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Moxie }, new PBENature[] { PBENature.Brave },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.FirePunch, PBEMove.Payback, PBEMove.DrainPunch, PBEMove.Substitute }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Scraggy,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Ash's Scraggy\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Scraggy, 1, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Moxie }, new PBENature[] { PBENature.Adamant },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Leer, PBEMove.LowKick, PBEMove.Headbutt, PBEMove.HiJumpKick }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Serperior,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Pokémon Center 15th anniversary\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Serperior, 100, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Torrent }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.LeafStorm, PBEMove.Substitute, PBEMove.GigaDrain, PBEMove.LeechSeed }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Shedinja,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // PCNY Monster Week 1\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Shedinja, 50, false, PBEGender.Genderless, new PBEAbility[] { PBEAbility.WonderGuard }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Spite, PBEMove.ConfuseRay, PBEMove.ShadowBall, PBEMove.Grudge }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Shelmet,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Trade for Evolution!\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Shelmet, 30, false, null, new PBEAbility[] { PBEAbility.Hydration, PBEAbility.ShellArmor }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.StruggleBug, PBEMove.MegaDrain, PBEMove.Yawn, PBEMove.Protect }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Summer 2011\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Shelmet, 50, false, null, new PBEAbility[] { PBEAbility.Hydration, PBEAbility.ShellArmor }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Encore, PBEMove.GigaDrain, PBEMove.BodySlam, PBEMove.BugBuzz }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Skitty,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // PokéPark Skitty\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Skitty, 5, null, null, new PBEAbility[] { PBEAbility.CuteCharm }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Growl, PBEMove.Tackle, PBEMove.TailWhip, PBEMove.Rollout }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Gather more Pokémon\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Skitty, 10, false, null, new PBEAbility[] { PBEAbility.CuteCharm }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Growl, PBEMove.Tackle, PBEMove.TailWhip, PBEMove.Attract }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Pokémon Box bonus egg\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Skitty, 5, null, null, new PBEAbility[] { PBEAbility.CuteCharm }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Tackle, PBEMove.Growl, PBEMove.TailWhip, PBEMove.PayDay }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Smeargle,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Gather more Pokémon\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Smeargle, 10, false, null, new PBEAbility[] { PBEAbility.OwnTempo }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Sketch, PBEMove.None, PBEMove.None, PBEMove.None }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // World championships 2013\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Smeargle, 50, false, PBEGender.Female, new PBEAbility[] { PBEAbility.Technician }, new PBENature[] { PBENature.Jolly },\n\t\t\t\t\tnew byte?[] { null, 31, null, null, null, 31 }, new PBEMove[] { PBEMove.FalseSwipe, PBEMove.Spore, PBEMove.OdorSleuth, PBEMove.MeanLook }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Snivy,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Pokémon Center Tohoku Snivy\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Snivy, 5, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Overgrow }, new PBENature[] { PBENature.Hardy },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Growth, PBEMove.Synthesis, PBEMove.EnergyBall, PBEMove.Aromatherapy }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Squirtle,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Gather more Pokémon\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Squirtle, 10, false, null, new PBEAbility[] { PBEAbility.Torrent }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Tackle, PBEMove.TailWhip, PBEMove.Bubble, PBEMove.Withdraw }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Kanto starter egg\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Squirtle, 1, null, null, new PBEAbility[] { PBEAbility.Torrent }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.FalseSwipe, PBEMove.Block, PBEMove.HydroCannon, PBEMove.FollowMe }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Global Link promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Squirtle, 10, false, PBEGender.Male, new PBEAbility[] { PBEAbility.RainDish }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Tackle, PBEMove.TailWhip, PBEMove.Bubble, PBEMove.Withdraw }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Thundurus,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Milos Island Pokémon\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Thundurus, 70, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Prankster }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Thunder, PBEMove.HammerArm, PBEMove.FocusBlast, PBEMove.WildCharge }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Tirtouga,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Pokémon Adventure Camp fossil Pokémon\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Tirtouga, 15, false, null, new PBEAbility[] { PBEAbility.SolidRock, PBEAbility.Sturdy }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Bite, PBEMove.Protect, PBEMove.AquaJet, PBEMove.BodySlam }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Torchic,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Gather more Pokémon\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Torchic, 10, false, null, new PBEAbility[] { PBEAbility.Blaze }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Scratch, PBEMove.Growl, PBEMove.FocusEnergy, PBEMove.Ember }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Global Link promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Torchic, 10, false, PBEGender.Male, new PBEAbility[] { PBEAbility.SpeedBoost }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Scratch, PBEMove.Growl, PBEMove.FocusEnergy, PBEMove.Ember }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Tornadus,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Milos Island Pokémon\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Tornadus, 70, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Prankster }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Hurricane, PBEMove.HammerArm, PBEMove.AirSlash, PBEMove.HiddenPower }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Torterra,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Pokémon Center 15th anniversary\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Torterra, 100, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Overgrow }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.WoodHammer, PBEMove.Earthquake, PBEMove.Outrage, PBEMove.StoneEdge }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Tropius,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // PCNY\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Tropius, 30, false, null, new PBEAbility[] { PBEAbility.Chlorophyll }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.RazorLeaf, PBEMove.Stomp, PBEMove.SweetScent, PBEMove.Whirlwind }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Pokémon Sunday promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 4 }, PBESpecies.Tropius, 53, false, PBEGender.Female, new PBEAbility[] { PBEAbility.Chlorophyll }, new PBENature[] { PBENature.Jolly },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.AirSlash, PBEMove.Synthesis, PBEMove.SunnyDay, PBEMove.SolarBeam }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Umbreon,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Journey across America | Party of the decade\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Umbreon, 70, false, null, new PBEAbility[] { PBEAbility.Synchronize }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.FaintAttack, PBEMove.MeanLook, PBEMove.Screech, PBEMove.Moonlight }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Global Link promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Umbreon, 10, false, PBEGender.Male, new PBEAbility[] { PBEAbility.InnerFocus }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.TailWhip, PBEMove.Tackle, PBEMove.HelpingHand, PBEMove.SandAttack }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Vaporeon,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Global Link promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Vaporeon, 10, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Hydration }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.TailWhip, PBEMove.Tackle, PBEMove.HelpingHand, PBEMove.SandAttack }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Victini,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // M14 promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Victini, 50, false, PBEGender.Genderless, new PBEAbility[] { PBEAbility.VictoryStar }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.VCreate, PBEMove.FusionFlare, PBEMove.FusionBolt, PBEMove.SearingShot }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Pokémon Center Tohoku Victini\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Victini, 100, false, PBEGender.Genderless, new PBEAbility[] { PBEAbility.VictoryStar }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.VCreate, PBEMove.BlueFlare, PBEMove.BoltStrike, PBEMove.Glaciate }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Volcarona,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Korean Nationals 2012\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Volcarona, 100, true, PBEGender.Male, new PBEAbility[] { PBEAbility.FlameBody }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.QuiverDance, PBEMove.BugBuzz, PBEMove.FieryDance, PBEMove.MorningSun }\n\t\t\t\t),\n\t\t\t\tnew PBEDDEventPokemon // Alder's Volcarona\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Volcarona, 77, false, PBEGender.Male, new PBEAbility[] { PBEAbility.FlameBody }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.BugBuzz, PBEMove.Overheat, PBEMove.HyperBeam, PBEMove.QuiverDance }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Vulpix,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Pokémon Trade and Battle Day\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 3 }, PBESpecies.Vulpix, 18, false, PBEGender.Female, new PBEAbility[] { PBEAbility.FlashFire }, new PBENature[] { PBENature.Quirky },\n\t\t\t\t\tnew byte?[] { 15, 6, 3, 25, 13, 22 }, new PBEMove[] { PBEMove.TailWhip, PBEMove.Roar, PBEMove.QuickAttack, PBEMove.WillOWisp }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Whimsicott,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Powerful Tag\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Whimsicott, 50, false, PBEGender.Female, new PBEAbility[] { PBEAbility.Prankster }, new PBENature[] { PBENature.Timid },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, 31 }, new PBEMove[] { PBEMove.Swagger, PBEMove.GigaDrain, PBEMove.BeatUp, PBEMove.HelpingHand }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Zekrom,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // Victini movies promotion\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Zekrom, 100, false, PBEGender.Genderless, new PBEAbility[] { PBEAbility.Teravolt }, PBEDataUtils.AllNatures,\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.BoltStrike, PBEMove.FusionBolt, PBEMove.Haze, PBEMove.Outrage }\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\t{\n\t\t\tPBESpecies.Zoroark,\n\t\t\tnew ReadOnlyCollection<PBEDDEventPokemon>(new[]\n\t\t\t{\n\t\t\t\tnew PBEDDEventPokemon // 2011\n\t\t\t\t(\n\t\t\t\t\tnew byte[] { 5 }, PBESpecies.Zoroark, 50, false, PBEGender.Male, new PBEAbility[] { PBEAbility.Illusion }, new PBENature[] { PBENature.Quirky },\n\t\t\t\t\tnew byte?[] { null, null, null, null, null, null }, new PBEMove[] { PBEMove.Agility, PBEMove.Embargo, PBEMove.Punishment, PBEMove.Snarl }\n\t\t\t\t)\n\t\t\t})\n\t\t}\n\t});\n}\n"
  },
  {
    "path": "PokemonBattleEngine.DefaultData/Data/ItemData.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data;\n\nnamespace Kermalis.PokemonBattleEngine.DefaultData.Data;\n\npublic sealed partial class PBEDDItemData : IPBEItemData\n{\n\tpublic byte FlingPower { get; }\n\n\tprivate PBEDDItemData(byte flingPower = 0)\n\t{\n\t\tFlingPower = flingPower;\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine.DefaultData/Data/ItemData_Data.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\n\nnamespace Kermalis.PokemonBattleEngine.DefaultData.Data;\n\npublic sealed partial class PBEDDItemData\n{\n\tpublic static ReadOnlyDictionary<PBEItem, PBEDDItemData> Data { get; } = new(new Dictionary<PBEItem, PBEDDItemData>\n\t{\n\t\t{ PBEItem.AbsorbBulb, new PBEDDItemData(30) },\n\t\t{ PBEItem.AdamantOrb, new PBEDDItemData(60) },\n\t\t{ PBEItem.AguavBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.AirBalloon, new PBEDDItemData(10) },\n\t\t{ PBEItem.AmuletCoin, new PBEDDItemData(30) },\n\t\t{ PBEItem.Antidote, new PBEDDItemData(30) },\n\t\t{ PBEItem.ApicotBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.ArmorFossil, new PBEDDItemData(100) },\n\t\t{ PBEItem.AspearBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.Awakening, new PBEDDItemData(30) },\n\t\t{ PBEItem.BabiriBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.BalmMushroom, new PBEDDItemData(30) },\n\t\t{ PBEItem.BelueBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.BerryJuice, new PBEDDItemData(30) },\n\t\t{ PBEItem.BigMushroom, new PBEDDItemData(30) },\n\t\t{ PBEItem.BigNugget, new PBEDDItemData(30) },\n\t\t{ PBEItem.BigPearl, new PBEDDItemData(30) },\n\t\t{ PBEItem.BigRoot, new PBEDDItemData(10) },\n\t\t{ PBEItem.BindingBand, new PBEDDItemData(30) },\n\t\t{ PBEItem.BlkApricorn, new PBEDDItemData() },\n\t\t{ PBEItem.BlackBelt, new PBEDDItemData(30) },\n\t\t{ PBEItem.BlackFlute, new PBEDDItemData(30) },\n\t\t{ PBEItem.BlackGlasses, new PBEDDItemData(30) },\n\t\t{ PBEItem.BlackSludge, new PBEDDItemData(30) },\n\t\t{ PBEItem.BluApricorn, new PBEDDItemData() },\n\t\t{ PBEItem.BlueFlute, new PBEDDItemData(30) },\n\t\t{ PBEItem.BlueScarf, new PBEDDItemData(10) },\n\t\t{ PBEItem.BlueShard, new PBEDDItemData(30) },\n\t\t{ PBEItem.BlukBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.BridgeMailD, new PBEDDItemData() },\n\t\t{ PBEItem.BridgeMailM, new PBEDDItemData() },\n\t\t{ PBEItem.BridgeMailS, new PBEDDItemData() },\n\t\t{ PBEItem.BridgeMailT, new PBEDDItemData() },\n\t\t{ PBEItem.BridgeMailV, new PBEDDItemData() },\n\t\t{ PBEItem.BrightPowder, new PBEDDItemData(10) },\n\t\t{ PBEItem.BugGem, new PBEDDItemData() },\n\t\t{ PBEItem.BurnDrive, new PBEDDItemData(70) },\n\t\t{ PBEItem.BurnHeal, new PBEDDItemData(30) },\n\t\t{ PBEItem.Calcium, new PBEDDItemData(30) },\n\t\t{ PBEItem.Carbos, new PBEDDItemData(30) },\n\t\t{ PBEItem.Casteliacone, new PBEDDItemData(30) },\n\t\t{ PBEItem.CellBattery, new PBEDDItemData(30) },\n\t\t{ PBEItem.Charcoal, new PBEDDItemData(30) },\n\t\t{ PBEItem.ChartiBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.CheriBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.CherishBall, new PBEDDItemData() },\n\t\t{ PBEItem.ChestoBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.ChilanBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.ChillDrive, new PBEDDItemData(70) },\n\t\t{ PBEItem.ChoiceBand, new PBEDDItemData(10) },\n\t\t{ PBEItem.ChoiceScarf, new PBEDDItemData(10) },\n\t\t{ PBEItem.ChoiceSpecs, new PBEDDItemData(10) },\n\t\t{ PBEItem.ChopleBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.ClawFossil, new PBEDDItemData(100) },\n\t\t{ PBEItem.CleanseTag, new PBEDDItemData(30) },\n\t\t{ PBEItem.CleverWing, new PBEDDItemData(20) },\n\t\t{ PBEItem.CobaBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.ColburBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.CometShard, new PBEDDItemData(30) },\n\t\t{ PBEItem.CornnBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.CoverFossil, new PBEDDItemData(100) },\n\t\t{ PBEItem.CustapBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.DampMulch, new PBEDDItemData(30) },\n\t\t{ PBEItem.DampRock, new PBEDDItemData(60) },\n\t\t{ PBEItem.DarkGem, new PBEDDItemData() },\n\t\t{ PBEItem.DawnStone, new PBEDDItemData(80) },\n\t\t{ PBEItem.DeepSeaScale, new PBEDDItemData(30) },\n\t\t{ PBEItem.DeepSeaTooth, new PBEDDItemData(90) },\n\t\t{ PBEItem.DestinyKnot, new PBEDDItemData(10) },\n\t\t{ PBEItem.DireHit, new PBEDDItemData(30) },\n\t\t{ PBEItem.DiveBall, new PBEDDItemData() },\n\t\t{ PBEItem.DomeFossil, new PBEDDItemData(100) },\n\t\t{ PBEItem.DouseDrive, new PBEDDItemData(70) },\n\t\t{ PBEItem.DracoPlate, new PBEDDItemData(90) },\n\t\t{ PBEItem.DragonFang, new PBEDDItemData(70) },\n\t\t{ PBEItem.DragonGem, new PBEDDItemData() },\n\t\t{ PBEItem.DragonScale, new PBEDDItemData(30) },\n\t\t{ PBEItem.DreadPlate, new PBEDDItemData(90) },\n\t\t{ PBEItem.DreamBall, new PBEDDItemData() },\n\t\t{ PBEItem.DubiousDisc, new PBEDDItemData(50) },\n\t\t{ PBEItem.DurinBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.DuskBall, new PBEDDItemData() },\n\t\t{ PBEItem.DuskStone, new PBEDDItemData(80) },\n\t\t{ PBEItem.EarthPlate, new PBEDDItemData(90) },\n\t\t{ PBEItem.EjectButton, new PBEDDItemData(30) },\n\t\t{ PBEItem.Electirizer, new PBEDDItemData(80) },\n\t\t{ PBEItem.ElectricGem, new PBEDDItemData() },\n\t\t{ PBEItem.Elixir, new PBEDDItemData(30) },\n\t\t{ PBEItem.EnergyPowder, new PBEDDItemData(30) },\n\t\t{ PBEItem.EnergyRoot, new PBEDDItemData(30) },\n\t\t{ PBEItem.EnigmaBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.EscapeRope, new PBEDDItemData(30) },\n\t\t{ PBEItem.Ether, new PBEDDItemData(30) },\n\t\t{ PBEItem.Everstone, new PBEDDItemData(30) },\n\t\t{ PBEItem.Eviolite, new PBEDDItemData(40) },\n\t\t{ PBEItem.ExpertBelt, new PBEDDItemData(10) },\n\t\t{ PBEItem.ExpShare, new PBEDDItemData(30) },\n\t\t{ PBEItem.FastBall, new PBEDDItemData() },\n\t\t{ PBEItem.FavoredMail, new PBEDDItemData() },\n\t\t{ PBEItem.FightingGem, new PBEDDItemData() },\n\t\t{ PBEItem.FigyBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.FireGem, new PBEDDItemData() },\n\t\t{ PBEItem.FireStone, new PBEDDItemData(30) },\n\t\t{ PBEItem.FistPlate, new PBEDDItemData(90) },\n\t\t{ PBEItem.FlameOrb, new PBEDDItemData(30) },\n\t\t{ PBEItem.FlamePlate, new PBEDDItemData(90) },\n\t\t{ PBEItem.FloatStone, new PBEDDItemData(30) },\n\t\t{ PBEItem.FluffyTail, new PBEDDItemData(30) },\n\t\t{ PBEItem.FlyingGem, new PBEDDItemData() },\n\t\t{ PBEItem.FocusBand, new PBEDDItemData(10) },\n\t\t{ PBEItem.FocusSash, new PBEDDItemData(10) },\n\t\t{ PBEItem.FreshWater, new PBEDDItemData(30) },\n\t\t{ PBEItem.FriendBall, new PBEDDItemData() },\n\t\t{ PBEItem.FullHeal, new PBEDDItemData(30) },\n\t\t{ PBEItem.FullIncense, new PBEDDItemData(10) },\n\t\t{ PBEItem.FullRestore, new PBEDDItemData(30) },\n\t\t{ PBEItem.GanlonBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.GeniusWing, new PBEDDItemData(20) },\n\t\t{ PBEItem.GhostGem, new PBEDDItemData() },\n\t\t{ PBEItem.GooeyMulch, new PBEDDItemData(30) },\n\t\t{ PBEItem.GrassGem, new PBEDDItemData() },\n\t\t{ PBEItem.GreatBall, new PBEDDItemData() },\n\t\t{ PBEItem.GrnApricorn, new PBEDDItemData() },\n\t\t{ PBEItem.GreenScarf, new PBEDDItemData(10) },\n\t\t{ PBEItem.GreenShard, new PBEDDItemData(30) },\n\t\t{ PBEItem.GreetMail, new PBEDDItemData() },\n\t\t{ PBEItem.GrepaBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.GripClaw, new PBEDDItemData(90) },\n\t\t{ PBEItem.GriseousOrb, new PBEDDItemData(60) },\n\t\t{ PBEItem.GroundGem, new PBEDDItemData() },\n\t\t{ PBEItem.GrowthMulch, new PBEDDItemData(30) },\n\t\t{ PBEItem.GuardSpec, new PBEDDItemData(30) },\n\t\t{ PBEItem.HabanBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.HardStone, new PBEDDItemData(100) },\n\t\t{ PBEItem.HealBall, new PBEDDItemData() },\n\t\t{ PBEItem.HealPowder, new PBEDDItemData(30) },\n\t\t{ PBEItem.HealthWing, new PBEDDItemData(20) },\n\t\t{ PBEItem.HeartScale, new PBEDDItemData(30) },\n\t\t{ PBEItem.HeatRock, new PBEDDItemData(60) },\n\t\t{ PBEItem.HeavyBall, new PBEDDItemData() },\n\t\t{ PBEItem.HelixFossil, new PBEDDItemData(100) },\n\t\t{ PBEItem.HondewBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.Honey, new PBEDDItemData(30) },\n\t\t{ PBEItem.HPUp, new PBEDDItemData(30) },\n\t\t{ PBEItem.HyperPotion, new PBEDDItemData(30) },\n\t\t{ PBEItem.IapapaBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.IceGem, new PBEDDItemData() },\n\t\t{ PBEItem.IceHeal, new PBEDDItemData(30) },\n\t\t{ PBEItem.IciclePlate, new PBEDDItemData(90) },\n\t\t{ PBEItem.IcyRock, new PBEDDItemData(40) },\n\t\t{ PBEItem.InquiryMail, new PBEDDItemData() },\n\t\t{ PBEItem.InsectPlate, new PBEDDItemData(90) },\n\t\t{ PBEItem.Iron, new PBEDDItemData(30) },\n\t\t{ PBEItem.IronBall, new PBEDDItemData(130) },\n\t\t{ PBEItem.IronPlate, new PBEDDItemData(90) },\n\t\t{ PBEItem.JabocaBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.KasibBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.KebiaBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.KelpsyBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.KingsRock, new PBEDDItemData(30) },\n\t\t{ PBEItem.LaggingTail, new PBEDDItemData(10) },\n\t\t{ PBEItem.LansatBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.LavaCookie, new PBEDDItemData(30) },\n\t\t{ PBEItem.LaxIncense, new PBEDDItemData(10) },\n\t\t{ PBEItem.LeafStone, new PBEDDItemData(30) },\n\t\t{ PBEItem.Leftovers, new PBEDDItemData(10) },\n\t\t{ PBEItem.Lemonade, new PBEDDItemData(30) },\n\t\t{ PBEItem.LeppaBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.LevelBall, new PBEDDItemData() },\n\t\t{ PBEItem.LiechiBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.LifeOrb, new PBEDDItemData(30) },\n\t\t{ PBEItem.LightBall, new PBEDDItemData(30) },\n\t\t{ PBEItem.LightClay, new PBEDDItemData(30) },\n\t\t{ PBEItem.LikeMail, new PBEDDItemData() },\n\t\t{ PBEItem.LoveBall, new PBEDDItemData() },\n\t\t{ PBEItem.LuckIncense, new PBEDDItemData(10) },\n\t\t{ PBEItem.LuckyEgg, new PBEDDItemData(30) },\n\t\t{ PBEItem.LuckyPunch, new PBEDDItemData(40) },\n\t\t{ PBEItem.LumBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.LureBall, new PBEDDItemData() },\n\t\t{ PBEItem.LustrousOrb, new PBEDDItemData(60) },\n\t\t{ PBEItem.LuxuryBall, new PBEDDItemData() },\n\t\t{ PBEItem.MachoBrace, new PBEDDItemData(60) },\n\t\t{ PBEItem.Magmarizer, new PBEDDItemData(80) },\n\t\t{ PBEItem.Magnet, new PBEDDItemData(30) },\n\t\t{ PBEItem.MagoBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.MagostBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.MasterBall, new PBEDDItemData() },\n\t\t{ PBEItem.MaxElixir, new PBEDDItemData(30) },\n\t\t{ PBEItem.MaxEther, new PBEDDItemData(30) },\n\t\t{ PBEItem.MaxPotion, new PBEDDItemData(30) },\n\t\t{ PBEItem.MaxRepel, new PBEDDItemData(30) },\n\t\t{ PBEItem.MaxRevive, new PBEDDItemData(30) },\n\t\t{ PBEItem.MeadowPlate, new PBEDDItemData(90) },\n\t\t{ PBEItem.MentalHerb, new PBEDDItemData(10) },\n\t\t{ PBEItem.MetalCoat, new PBEDDItemData(30) },\n\t\t{ PBEItem.MetalPowder, new PBEDDItemData(10) },\n\t\t{ PBEItem.Metronome, new PBEDDItemData(30) },\n\t\t{ PBEItem.MicleBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.MindPlate, new PBEDDItemData(90) },\n\t\t{ PBEItem.MiracleSeed, new PBEDDItemData(30) },\n\t\t{ PBEItem.MoomooMilk, new PBEDDItemData(30) },\n\t\t{ PBEItem.MoonBall, new PBEDDItemData() },\n\t\t{ PBEItem.MoonStone, new PBEDDItemData(30) },\n\t\t{ PBEItem.MuscleBand, new PBEDDItemData(10) },\n\t\t{ PBEItem.MuscleWing, new PBEDDItemData(20) },\n\t\t{ PBEItem.MysticWater, new PBEDDItemData(30) },\n\t\t{ PBEItem.NanabBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.NestBall, new PBEDDItemData() },\n\t\t{ PBEItem.NetBall, new PBEDDItemData() },\n\t\t{ PBEItem.NeverMeltIce, new PBEDDItemData(30) },\n\t\t{ PBEItem.NomelBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.NormalGem, new PBEDDItemData() },\n\t\t{ PBEItem.Nugget, new PBEDDItemData(30) },\n\t\t{ PBEItem.OccaBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.OddIncense, new PBEDDItemData(10) },\n\t\t{ PBEItem.OddKeystone, new PBEDDItemData(80) },\n\t\t{ PBEItem.OldAmber, new PBEDDItemData(100) },\n\t\t{ PBEItem.OldGateau, new PBEDDItemData(30) },\n\t\t{ PBEItem.OranBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.OvalStone, new PBEDDItemData(80) },\n\t\t{ PBEItem.PamtreBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.ParalyzHeal, new PBEDDItemData(30) },\n\t\t{ PBEItem.ParkBall, new PBEDDItemData() },\n\t\t{ PBEItem.PasshoBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.PassOrb, new PBEDDItemData(30) },\n\t\t{ PBEItem.PayapaBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.Pearl, new PBEDDItemData(30) },\n\t\t{ PBEItem.PearlString, new PBEDDItemData(30) },\n\t\t{ PBEItem.PechaBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.PersimBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.PetayaBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.PinapBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.PnkApricorn, new PBEDDItemData() },\n\t\t{ PBEItem.PinkScarf, new PBEDDItemData(10) },\n\t\t{ PBEItem.PlumeFossil, new PBEDDItemData(100) },\n\t\t{ PBEItem.PoisonBarb, new PBEDDItemData(70) },\n\t\t{ PBEItem.PoisonGem, new PBEDDItemData() },\n\t\t{ PBEItem.PokeBall, new PBEDDItemData() },\n\t\t{ PBEItem.PokeDoll, new PBEDDItemData(30) },\n\t\t{ PBEItem.PokeToy, new PBEDDItemData(30) },\n\t\t{ PBEItem.PomegBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.Potion, new PBEDDItemData(30) },\n\t\t{ PBEItem.PowerAnklet, new PBEDDItemData(70) },\n\t\t{ PBEItem.PowerBand, new PBEDDItemData(70) },\n\t\t{ PBEItem.PowerBelt, new PBEDDItemData(70) },\n\t\t{ PBEItem.PowerBracer, new PBEDDItemData(70) },\n\t\t{ PBEItem.PowerHerb, new PBEDDItemData(10) },\n\t\t{ PBEItem.PowerLens, new PBEDDItemData(70) },\n\t\t{ PBEItem.PowerWeight, new PBEDDItemData(70) },\n\t\t{ PBEItem.PPMax, new PBEDDItemData(30) },\n\t\t{ PBEItem.PPUp, new PBEDDItemData(30) },\n\t\t{ PBEItem.PremierBall, new PBEDDItemData() },\n\t\t{ PBEItem.PrettyWing, new PBEDDItemData(30) },\n\t\t{ PBEItem.PrismScale, new PBEDDItemData(30) },\n\t\t{ PBEItem.Protector, new PBEDDItemData(80) },\n\t\t{ PBEItem.Protein, new PBEDDItemData(30) },\n\t\t{ PBEItem.PsychicGem, new PBEDDItemData() },\n\t\t{ PBEItem.PureIncense, new PBEDDItemData(10) },\n\t\t{ PBEItem.QualotBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.QuickBall, new PBEDDItemData() },\n\t\t{ PBEItem.QuickClaw, new PBEDDItemData(80) },\n\t\t{ PBEItem.QuickPowder, new PBEDDItemData(10) },\n\t\t{ PBEItem.RabutaBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.RageCandyBar, new PBEDDItemData(30) },\n\t\t{ PBEItem.RareBone, new PBEDDItemData(100) },\n\t\t{ PBEItem.RareCandy, new PBEDDItemData(30) },\n\t\t{ PBEItem.RawstBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.RazorClaw, new PBEDDItemData(80) },\n\t\t{ PBEItem.RazorFang, new PBEDDItemData(30) },\n\t\t{ PBEItem.RazzBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.ReaperCloth, new PBEDDItemData(10) },\n\t\t{ PBEItem.RedApricorn, new PBEDDItemData() },\n\t\t{ PBEItem.RedCard, new PBEDDItemData(10) },\n\t\t{ PBEItem.RedFlute, new PBEDDItemData(30) },\n\t\t{ PBEItem.RedScarf, new PBEDDItemData(10) },\n\t\t{ PBEItem.RedShard, new PBEDDItemData(30) },\n\t\t{ PBEItem.RelicBand, new PBEDDItemData(30) },\n\t\t{ PBEItem.RelicCopper, new PBEDDItemData(30) },\n\t\t{ PBEItem.RelicCrown, new PBEDDItemData(30) },\n\t\t{ PBEItem.RelicGold, new PBEDDItemData(30) },\n\t\t{ PBEItem.RelicSilver, new PBEDDItemData(30) },\n\t\t{ PBEItem.RelicStatue, new PBEDDItemData(30) },\n\t\t{ PBEItem.RelicVase, new PBEDDItemData(30) },\n\t\t{ PBEItem.RepeatBall, new PBEDDItemData() },\n\t\t{ PBEItem.Repel, new PBEDDItemData(30) },\n\t\t{ PBEItem.ReplyMail, new PBEDDItemData() },\n\t\t{ PBEItem.ResistWing, new PBEDDItemData(20) },\n\t\t{ PBEItem.RevivalHerb, new PBEDDItemData(30) },\n\t\t{ PBEItem.Revive, new PBEDDItemData(30) },\n\t\t{ PBEItem.RindoBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.RingTarget, new PBEDDItemData(10) },\n\t\t{ PBEItem.RockGem, new PBEDDItemData() },\n\t\t{ PBEItem.RockIncense, new PBEDDItemData(10) },\n\t\t{ PBEItem.RockyHelmet, new PBEDDItemData(60) },\n\t\t{ PBEItem.RootFossil, new PBEDDItemData(100) },\n\t\t{ PBEItem.RoseIncense, new PBEDDItemData(10) },\n\t\t{ PBEItem.RowapBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.RSVPMail, new PBEDDItemData() },\n\t\t{ PBEItem.SacredAsh, new PBEDDItemData(30) },\n\t\t{ PBEItem.SafariBall, new PBEDDItemData() },\n\t\t{ PBEItem.SalacBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.ScopeLens, new PBEDDItemData(30) },\n\t\t{ PBEItem.SeaIncense, new PBEDDItemData(10) },\n\t\t{ PBEItem.SharpBeak, new PBEDDItemData(50) },\n\t\t{ PBEItem.ShedShell, new PBEDDItemData(10) },\n\t\t{ PBEItem.ShellBell, new PBEDDItemData(30) },\n\t\t{ PBEItem.ShinyStone, new PBEDDItemData(80) },\n\t\t{ PBEItem.ShoalSalt, new PBEDDItemData(30) },\n\t\t{ PBEItem.ShoalShell, new PBEDDItemData(30) },\n\t\t{ PBEItem.ShockDrive, new PBEDDItemData(70) },\n\t\t{ PBEItem.ShucaBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.SilkScarf, new PBEDDItemData(10) },\n\t\t{ PBEItem.SilverPowder, new PBEDDItemData(10) },\n\t\t{ PBEItem.SitrusBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.SkullFossil, new PBEDDItemData(100) },\n\t\t{ PBEItem.SkyPlate, new PBEDDItemData(90) },\n\t\t{ PBEItem.SmokeBall, new PBEDDItemData(30) },\n\t\t{ PBEItem.SmoothRock, new PBEDDItemData(10) },\n\t\t{ PBEItem.SodaPop, new PBEDDItemData(30) },\n\t\t{ PBEItem.SoftSand, new PBEDDItemData(10) },\n\t\t{ PBEItem.SootheBell, new PBEDDItemData(10) },\n\t\t{ PBEItem.SoulDew, new PBEDDItemData(30) },\n\t\t{ PBEItem.SpellTag, new PBEDDItemData(30) },\n\t\t{ PBEItem.SpelonBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.SplashPlate, new PBEDDItemData(90) },\n\t\t{ PBEItem.SpookyPlate, new PBEDDItemData(90) },\n\t\t{ PBEItem.SportBall, new PBEDDItemData() },\n\t\t{ PBEItem.StableMulch, new PBEDDItemData(30) },\n\t\t{ PBEItem.Stardust, new PBEDDItemData(30) },\n\t\t{ PBEItem.StarfBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.StarPiece, new PBEDDItemData(30) },\n\t\t{ PBEItem.SteelGem, new PBEDDItemData() },\n\t\t{ PBEItem.Stick, new PBEDDItemData(60) },\n\t\t{ PBEItem.StickyBarb, new PBEDDItemData(80) },\n\t\t{ PBEItem.StonePlate, new PBEDDItemData(90) },\n\t\t{ PBEItem.SunStone, new PBEDDItemData(30) },\n\t\t{ PBEItem.SuperPotion, new PBEDDItemData(30) },\n\t\t{ PBEItem.SuperRepel, new PBEDDItemData(30) },\n\t\t{ PBEItem.SweetHeart, new PBEDDItemData(30) },\n\t\t{ PBEItem.SwiftWing, new PBEDDItemData(20) },\n\t\t{ PBEItem.TamatoBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.TangaBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.ThanksMail, new PBEDDItemData() },\n\t\t{ PBEItem.ThickClub, new PBEDDItemData(90) },\n\t\t{ PBEItem.Thunderstone, new PBEDDItemData(30) },\n\t\t{ PBEItem.TimerBall, new PBEDDItemData() },\n\t\t{ PBEItem.TinyMushroom, new PBEDDItemData(30) },\n\t\t{ PBEItem.ToxicOrb, new PBEDDItemData(30) },\n\t\t{ PBEItem.ToxicPlate, new PBEDDItemData(90) },\n\t\t{ PBEItem.TwistedSpoon, new PBEDDItemData(30) },\n\t\t{ PBEItem.UltraBall, new PBEDDItemData() },\n\t\t{ PBEItem.UpGrade, new PBEDDItemData(30) },\n\t\t{ PBEItem.WacanBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.WaterGem, new PBEDDItemData() },\n\t\t{ PBEItem.WaterStone, new PBEDDItemData(30) },\n\t\t{ PBEItem.WatmelBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.WaveIncense, new PBEDDItemData(10) },\n\t\t{ PBEItem.WepearBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.WhtApricorn, new PBEDDItemData() },\n\t\t{ PBEItem.WhiteFlute, new PBEDDItemData(30) },\n\t\t{ PBEItem.WhiteHerb, new PBEDDItemData(10) },\n\t\t{ PBEItem.WideLens, new PBEDDItemData(10) },\n\t\t{ PBEItem.WikiBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.WiseGlasses, new PBEDDItemData(10) },\n\t\t{ PBEItem.XAccuracy, new PBEDDItemData(30) },\n\t\t{ PBEItem.XAttack, new PBEDDItemData(30) },\n\t\t{ PBEItem.XDefend, new PBEDDItemData(30) },\n\t\t{ PBEItem.XSpDef, new PBEDDItemData(30) },\n\t\t{ PBEItem.XSpecial, new PBEDDItemData(30) },\n\t\t{ PBEItem.XSpeed, new PBEDDItemData(30) },\n\t\t{ PBEItem.YacheBerry, new PBEDDItemData(10) },\n\t\t{ PBEItem.YlwApricorn, new PBEDDItemData() },\n\t\t{ PBEItem.YellowFlute, new PBEDDItemData(30) },\n\t\t{ PBEItem.YellowScarf, new PBEDDItemData(10) },\n\t\t{ PBEItem.YellowShard, new PBEDDItemData(30) },\n\t\t{ PBEItem.ZapPlate, new PBEDDItemData(90) },\n\t\t{ PBEItem.Zinc, new PBEDDItemData(30) },\n\t\t{ PBEItem.ZoomLens, new PBEDDItemData(10) }\n\t});\n}\n"
  },
  {
    "path": "PokemonBattleEngine.DefaultData/Data/LegalityChecker.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Data.Utils;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Linq;\n\nnamespace Kermalis.PokemonBattleEngine.DefaultData.Data;\n\npublic static class PBEDDLegalityChecker\n{\n\tprivate static List<(PBESpecies, PBEForm)> GetSpecies(PBESpecies species, PBEForm form)\n\t{\n\t\t// Recursion BAYBEE\n\t\t// IDK what to name these functions so enjoy Add1 and Add2\n\t\tvar list = new List<(PBESpecies, PBEForm)>();\n\t\tvoid Add1(PBESpecies s, PBEForm f)\n\t\t{\n\t\t\t// Do not take forms if unable to change into them (Wormadam)\n\t\t\tif (PBEDataUtils.CanChangeForm(s, true))\n\t\t\t{\n\t\t\t\tforeach (PBEForm cf in PBEDataUtils.GetForms(s, true))\n\t\t\t\t{\n\t\t\t\t\tAdd2(s, cf);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tAdd2(s, f);\n\t\t\t}\n\t\t}\n\t\tvoid Add2(PBESpecies s, PBEForm f)\n\t\t{\n\t\t\tforeach ((PBESpecies cs, PBEForm cf) in PBEDefaultDataProvider.Instance.GetPokemonDataExtended(s, f).PreEvolutions)\n\t\t\t{\n\t\t\t\tAdd1(cs, cf);\n\t\t\t}\n\t\t\tlist.Add((s, f));\n\t\t}\n\t\tAdd1(species, form);\n\t\treturn list;\n\t}\n\n\tpublic static IReadOnlyCollection<PBEMove> GetLegalMoves(PBESpecies species, PBEForm form, byte level)\n\t{\n\t\tList<(PBESpecies, PBEForm)> speciesToStealFrom = GetSpecies(species, form);\n\n\t\tvar moves = new List<PBEMove>();\n\t\tforeach ((PBESpecies spe, PBEForm fo) in speciesToStealFrom)\n\t\t{\n\t\t\tIPBEDDPokemonDataExtended pData = PBEDefaultDataProvider.Instance.GetPokemonDataExtended(spe, fo);\n\t\t\t// Disallow moves learned after the current level\n\t\t\tmoves.AddRange(pData.LevelUpMoves.Where(t => t.Level <= level).Select(t => t.Move));\n\t\t\t// Disallow form-specific moves from other forms (Rotom)\n\t\t\tmoves.AddRange(pData.OtherMoves.Where(t => (spe == species && fo == form) || t.ObtainMethod != PBEDDMoveObtainMethod.Form).Select(t => t.Move));\n\t\t\t// Event Pokémon checking is extremely basic and wrong, but the goal is not to be super restricting or accurate\n\t\t\tif (PBEDDEventPokemon.Events.TryGetValue(spe, out ReadOnlyCollection<PBEDDEventPokemon>? events))\n\t\t\t{\n\t\t\t\t// Disallow moves learned after the current level\n\t\t\t\tmoves.AddRange(events.Where(e => e.Level <= level).SelectMany(e => e.Moves).Where(m => m != PBEMove.None));\n\t\t\t}\n\t\t\tif (moves.FindIndex(m => PBEDataProvider.Instance.GetMoveData(m, cache: false).Effect == PBEMoveEffect.Sketch) != -1)\n\t\t\t{\n\t\t\t\treturn PBEDataUtils.SketchLegalMoves;\n\t\t\t}\n\t\t}\n\t\treturn moves.Distinct().Where(m => PBEDataUtils.IsMoveUsable(m)).ToArray();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine.DefaultData/Data/MoveData.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data;\nusing System;\nusing System.Text;\n\nnamespace Kermalis.PokemonBattleEngine.DefaultData.Data;\n\npublic sealed partial class PBEDDMoveData : IPBEMoveData\n{\n\tpublic PBEType Type { get; }\n\tpublic PBEMoveCategory Category { get; }\n\tpublic sbyte Priority { get; }\n\tpublic byte PPTier { get; }\n\tpublic byte Power { get; }\n\tpublic byte Accuracy { get; }\n\tpublic PBEMoveEffect Effect { get; }\n\tpublic int EffectParam { get; }\n\tpublic PBEMoveTarget Targets { get; }\n\tpublic PBEMoveFlag Flags { get; }\n\n\tprivate PBEDDMoveData(PBEType type, PBEMoveCategory category, sbyte priority, byte ppTier, byte power, byte accuracy,\n\t\tPBEMoveEffect effect, int effectParam, PBEMoveTarget targets,\n\t\tPBEMoveFlag flags)\n\t{\n\t\tType = type; Category = category; Priority = priority; PPTier = ppTier; Power = power; Accuracy = accuracy;\n\t\tEffect = effect; EffectParam = effectParam; Targets = targets;\n\t\tFlags = flags;\n\t}\n\n\tpublic override string ToString()\n\t{\n\t\tvar sb = new StringBuilder();\n\n\t\tsb.AppendLine($\"Type: {Type}\");\n\t\tsb.AppendLine($\"Category: {Category}\");\n\t\tsb.AppendLine($\"Priority: {Priority}\");\n\t\tsb.AppendLine($\"PP: {Math.Max(1, PPTier * PBESettings.DefaultPPMultiplier)}\");\n\t\tsb.AppendLine($\"Power: {(Power == 0 ? \"―\" : Power.ToString())}\");\n\t\tsb.AppendLine($\"Accuracy: {(Accuracy == 0 ? \"―\" : Accuracy.ToString())}\");\n\t\tsb.AppendLine($\"Effect: {Effect}\");\n\t\tsb.AppendLine($\"Effect Parameter: {EffectParam}\");\n\t\tsb.AppendLine($\"Targets: {Targets}\");\n\t\tsb.Append($\"Flags: {Flags}\");\n\n\t\treturn sb.ToString();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine.DefaultData/Data/MoveData_Data.cs",
    "content": "using Kermalis.PokemonBattleEngine.Data;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\n\nnamespace Kermalis.PokemonBattleEngine.DefaultData.Data;\n\npublic sealed partial class PBEDDMoveData\n{\n\tpublic static ReadOnlyDictionary<PBEMove, PBEDDMoveData> Data { get; } = new(new Dictionary<PBEMove, PBEDDMoveData>\n\t{\n\t\t{\n\t\t\tPBEMove.Absorb,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Special, 0, 5, 20, 100,\n\t\t\t\tPBEMoveEffect.HPDrain, 50, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Acid,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Poison, PBEMoveCategory.Special, 0, 6, 40, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_SPDEF_By1, 10, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.AcidArmor,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Poison, PBEMoveCategory.Status, 0, 8, 0, 0,\n\t\t\t\tPBEMoveEffect.ChangeTarget_DEF, +2, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.AcidSpray,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Poison, PBEMoveCategory.Special, 0, 4, 40, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_SPDEF_By2, 100, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Acrobatics,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Flying, PBEMoveCategory.Physical, 0, 3, 55, 100,\n\t\t\t\tPBEMoveEffect.Acrobatics, 0, PBEMoveTarget.SingleNotSelf,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Acupressure,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 6, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleAllySurrounding,\n\t\t\t\tPBEMoveFlag.None\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.AerialAce,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Flying, PBEMoveCategory.Physical, 0, 4, 60, 0,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleNotSelf,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Aeroblast,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Flying, PBEMoveCategory.Special, 0, 1, 100, 95,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleNotSelf,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.HighCritChance\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.AfterYou,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 3, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.BlockedFromMetronome\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Agility,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, 0, 6, 0, 0,\n\t\t\t\tPBEMoveEffect.ChangeTarget_SPE, +2, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.AirCutter,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Flying, PBEMoveCategory.Special, 0, 5, 55, 95,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.HighCritChance\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.AirSlash,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Flying, PBEMoveCategory.Special, 0, 4, 75, 95,\n\t\t\t\tPBEMoveEffect.Hit__MaybeFlinch, 30, PBEMoveTarget.SingleNotSelf,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.AllySwitch,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, +1, 3, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.None\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Amnesia,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, 0, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.ChangeTarget_SPDEF, +2, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.AncientPower,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Rock, PBEMoveCategory.Special, 0, 1, 60, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeRaiseUser_ATK_DEF_SPATK_SPDEF_SPE_By1, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.AquaJet,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Water, PBEMoveCategory.Physical, +1, 4, 40, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.AquaRing,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Water, PBEMoveCategory.Status, 0, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.AquaTail,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Water, PBEMoveCategory.Physical, 0, 2, 90, 90,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.ArmThrust,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Physical, 0, 4, 15, 100,\n\t\t\t\tPBEMoveEffect.Hit__2To5Times, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Aromatherapy,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Status, 0, 1, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.AllTeam,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Assist,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.BlockedFromAssist | PBEMoveFlag.BlockedFromCopycat | PBEMoveFlag.BlockedFromMetronome | PBEMoveFlag.BlockedFromSleepTalk\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Assurance,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dark, PBEMoveCategory.Physical, 0, 2, 50, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Astonish,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ghost, PBEMoveCategory.Physical, 0, 3, 30, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeFlinch, 30, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.AttackOrder,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Bug, PBEMoveCategory.Physical, 0, 3, 90, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.HighCritChance\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Attract,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 3, 0, 100,\n\t\t\t\tPBEMoveEffect.Attract, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.AuraSphere,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Special, 0, 4, 90, 0,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleNotSelf,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.AuroraBeam,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ice, PBEMoveCategory.Special, 0, 4, 65, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_ATK_By1, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Autotomize,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Steel, PBEMoveCategory.Status, 0, 3, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Avalanche,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ice, PBEMoveCategory.Physical, -4, 2, 60, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Barrage,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 4, 15, 85,\n\t\t\t\tPBEMoveEffect.Hit__2To5Times, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Barrier,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, 0, 6, 0, 0,\n\t\t\t\tPBEMoveEffect.ChangeTarget_DEF, +2, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.BatonPass,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 8, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.None\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.BeatUp,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dark, PBEMoveCategory.Physical, 0, 2, 0, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.BellyDrum,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.BellyDrum, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Bestow,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 3, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.BlockedFromAssist | PBEMoveFlag.BlockedFromCopycat | PBEMoveFlag.BlockedFromMetronome\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Bide,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, +1, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedByProtect | PBEMoveFlag.BlockedFromSleepTalk | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Bind,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 4, 15, 85,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Bite,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dark, PBEMoveCategory.Physical, 0, 5, 60, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeFlinch, 30, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.BlastBurn,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fire, PBEMoveCategory.Special, 0, 1, 150, 90,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.BlazeKick,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fire, PBEMoveCategory.Physical, 0, 2, 85, 90,\n\t\t\t\tPBEMoveEffect.Hit__MaybeBurn, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.HighCritChance | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Blizzard,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ice, PBEMoveCategory.Special, 0, 1, 120, 70,\n\t\t\t\tPBEMoveEffect.Hit__MaybeFreeze, 10, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.NeverMissHail\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Block,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 1, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.BlueFlare,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fire, PBEMoveCategory.Special, 0, 1, 130, 85,\n\t\t\t\tPBEMoveEffect.Hit__MaybeBurn, 20, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.BrickBreak,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Physical, 0, 3, 75, 100,\n\t\t\t\tPBEMoveEffect.BrickBreak, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Brine,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Water, PBEMoveCategory.Special, 0, 2, 65, 100,\n\t\t\t\tPBEMoveEffect.Brine, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.BodySlam,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 3, 85, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeParalyze, 30, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.BoltStrike,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Electric, PBEMoveCategory.Physical, 0, 1, 130, 85,\n\t\t\t\tPBEMoveEffect.Hit__MaybeParalyze, 20, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.BoneClub,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ground, PBEMoveCategory.Physical, 0, 4, 65, 85,\n\t\t\t\tPBEMoveEffect.Hit__MaybeFlinch, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Bonemerang,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ground, PBEMoveCategory.Physical, 0, 2, 50, 90,\n\t\t\t\tPBEMoveEffect.Hit__2Times, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.BoneRush,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ground, PBEMoveCategory.Physical, 0, 2, 25, 90,\n\t\t\t\tPBEMoveEffect.Hit__2To5Times, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Bounce,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Flying, PBEMoveCategory.Physical, 0, 1, 85, 85,\n\t\t\t\tPBEMoveEffect.Bounce, 30, PBEMoveTarget.SingleNotSelf,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.BlockedByGravity | PBEMoveFlag.BlockedFromSleepTalk | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.BraveBird,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Flying, PBEMoveCategory.Physical, 0, 3, 120, 100,\n\t\t\t\tPBEMoveEffect.Recoil, 3, PBEMoveTarget.SingleNotSelf,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.AffectedByReckless | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Bubble,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Water, PBEMoveCategory.Special, 0, 6, 20, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_SPE_By1, 10, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.BubbleBeam,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Water, PBEMoveCategory.Special, 0, 4, 65, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_SPE_By1, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.BugBite,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Bug, PBEMoveCategory.Physical, 0, 4, 60, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.BugBuzz,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Bug, PBEMoveCategory.Special, 0, 2, 90, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_SPDEF_By1, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.AffectedBySoundproof\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.BulkUp,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Status, 0, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.RaiseTarget_ATK_DEF_By1, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Bulldoze,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ground, PBEMoveCategory.Physical, 0, 4, 60, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_SPE_By1, 100, PBEMoveTarget.AllSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.BulletPunch,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Steel, PBEMoveCategory.Physical, +1, 6, 40, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByIronFist | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.BulletSeed,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Physical, 0, 6, 25, 100,\n\t\t\t\tPBEMoveEffect.Hit__2To5Times, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.CalmMind,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, 0, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.RaiseTarget_SPATK_SPDEF_By1, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Camouflage,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.Camouflage, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Captivate,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 4, 0, 100,\n\t\t\t\tPBEMoveEffect.ChangeTarget_SPATK__IfAttractionPossible, -2, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Charge,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Electric, PBEMoveCategory.Status, 0, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.ChargeBeam,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Electric, PBEMoveCategory.Special, 0, 2, 50, 90,\n\t\t\t\tPBEMoveEffect.Hit__MaybeRaiseUser_SPATK_By1, 70, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Charm,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.ChangeTarget_ATK, -2, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Chatter,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Flying, PBEMoveCategory.Special, 0, 4, 60, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeConfuse, 0, PBEMoveTarget.SingleNotSelf,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.AffectedBySoundproof | PBEMoveFlag.BlockedFromAssist | PBEMoveFlag.BlockedFromCopycat | PBEMoveFlag.BlockedFromMeFirst | PBEMoveFlag.BlockedFromMetronome | PBEMoveFlag.BlockedFromSketch | PBEMoveFlag.BlockedFromMimic\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.ChipAway,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 4, 70, 100,\n\t\t\t\tPBEMoveEffect.ChipAway, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.CircleThrow,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Physical, -6, 2, 60, 90,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.BlockedFromAssist | PBEMoveFlag.BlockedFromCopycat | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Clamp,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Water, PBEMoveCategory.Physical, 0, 3, 35, 85,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.ClearSmog,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Poison, PBEMoveCategory.Special, 0, 3, 50, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.CloseCombat,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Physical, 0, 1, 120, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerUser_DEF_SPDEF_By1, 100, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Coil,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Poison, PBEMoveCategory.Status, 0, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.RaiseTarget_ATK_DEF_ACC_By1, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.CometPunch,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 3, 18, 85,\n\t\t\t\tPBEMoveEffect.Hit__2To5Times, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByIronFist | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.ConfuseRay,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ghost, PBEMoveCategory.Status, 0, 2, 0, 100,\n\t\t\t\tPBEMoveEffect.Confuse, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Confusion,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Special, 0, 5, 50, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeConfuse, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Constrict,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 7, 10, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_SPE_By1, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Conversion,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 6, 0, 0,\n\t\t\t\tPBEMoveEffect.Conversion, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Conversion2,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 6, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.None\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Copycat,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.BlockedFromAssist | PBEMoveFlag.BlockedFromCopycat | PBEMoveFlag.BlockedFromMetronome | PBEMoveFlag.BlockedFromSleepTalk\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.CosmicPower,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, 0, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.RaiseTarget_DEF_SPDEF_By1, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.CottonGuard,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.ChangeTarget_DEF, +3, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.CottonSpore,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Status, 0, 8, 0, 100,\n\t\t\t\tPBEMoveEffect.ChangeTarget_SPE, -2, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Counter,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Physical, -5, 4, 0, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedByProtect | PBEMoveFlag.BlockedFromAssist | PBEMoveFlag.BlockedFromCopycat | PBEMoveFlag.BlockedFromMeFirst | PBEMoveFlag.BlockedFromMetronome | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Covet,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 8, 60, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.BlockedFromAssist | PBEMoveFlag.BlockedFromCopycat | PBEMoveFlag.BlockedFromMeFirst | PBEMoveFlag.BlockedFromMetronome | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Crabhammer,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Water, PBEMoveCategory.Physical, 0, 2, 90, 90,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.HighCritChance | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.CrossChop,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Physical, 0, 1, 100, 80,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.HighCritChance | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.CrossPoison,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Poison, PBEMoveCategory.Physical, 0, 4, 70, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybePoison, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.HighCritChance | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Crunch,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dark, PBEMoveCategory.Physical, 0, 3, 80, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_DEF_By1, 20, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.CrushClaw,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 2, 75, 95,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_DEF_By1, 50, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.CrushGrip,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 1, 120, 100,\n\t\t\t\tPBEMoveEffect.CrushGrip, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Curse,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ghost, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.Curse, 0, PBEMoveTarget.Varies,\n\t\t\t\tPBEMoveFlag.None\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Cut,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 6, 50, 95,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.DarkPulse,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dark, PBEMoveCategory.Special, 0, 3, 80, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeFlinch, 20, PBEMoveTarget.SingleNotSelf,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.DarkVoid,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dark, PBEMoveCategory.Status, 0, 2, 0, 80,\n\t\t\t\tPBEMoveEffect.Sleep, 0, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.DefendOrder,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Bug, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.RaiseTarget_DEF_SPDEF_By1, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.DefenseCurl,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 8, 0, 0,\n\t\t\t\tPBEMoveEffect.ChangeTarget_DEF, +1, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Defog,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Flying, PBEMoveCategory.Status, 0, 3, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.DestinyBond,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ghost, PBEMoveCategory.Status, 0, 1, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.BlockedFromAssist | PBEMoveFlag.BlockedFromCopycat | PBEMoveFlag.BlockedFromMetronome\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Detect,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Status, +4, 1, 0, 0,\n\t\t\t\tPBEMoveEffect.Protect, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.BlockedFromAssist | PBEMoveFlag.BlockedFromCopycat | PBEMoveFlag.BlockedFromMetronome\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Dig,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ground, PBEMoveCategory.Physical, 0, 2, 80, 100,\n\t\t\t\tPBEMoveEffect.Dig, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.BlockedFromSleepTalk | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Disable,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 4, 0, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Discharge,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Electric, PBEMoveCategory.Special, 0, 3, 80, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeParalyze, 30, PBEMoveTarget.AllSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Dive,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Water, PBEMoveCategory.Physical, 0, 2, 80, 100,\n\t\t\t\tPBEMoveEffect.Dive, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.BlockedFromSleepTalk | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.DizzyPunch,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 2, 70, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeConfuse, 20, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByIronFist | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.DoomDesire,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Steel, PBEMoveCategory.Special, 0, 1, 140, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.DoubleEdge,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 3, 120, 100,\n\t\t\t\tPBEMoveEffect.Recoil, 3, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.AffectedByReckless | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.DoubleHit,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 2, 35, 90,\n\t\t\t\tPBEMoveEffect.Hit__2Times, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.DoubleKick,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Physical, 0, 6, 30, 100,\n\t\t\t\tPBEMoveEffect.Hit__2Times, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.DoubleSlap,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 2, 15, 85,\n\t\t\t\tPBEMoveEffect.Hit__2To5Times, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.DoubleTeam,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 3, 0, 0,\n\t\t\t\tPBEMoveEffect.ChangeTarget_EVA, +1, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.DracoMeteor,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dragon, PBEMoveCategory.Special, 0, 1, 140, 90,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerUser_SPATK_By2, 100, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.DragonBreath,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dragon, PBEMoveCategory.Special, 0, 4, 60, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeParalyze, 30, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.DragonClaw,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dragon, PBEMoveCategory.Physical, 0, 3, 80, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.DragonDance,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dragon, PBEMoveCategory.Status, 0, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.RaiseTarget_ATK_SPE_By1, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.DragonPulse,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dragon, PBEMoveCategory.Special, 0, 2, 90, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleNotSelf,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.DragonRage,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dragon, PBEMoveCategory.Special, 0, 2, 0, 100,\n\t\t\t\tPBEMoveEffect.SetDamage, 40, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.DragonRush,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dragon, PBEMoveCategory.Physical, 0, 2, 100, 75,\n\t\t\t\tPBEMoveEffect.Hit__MaybeFlinch, 20, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.DragonTail,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dragon, PBEMoveCategory.Physical, -6, 2, 60, 90,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.BlockedFromAssist | PBEMoveFlag.BlockedFromCopycat | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.DrainPunch,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Physical, 0, 2, 75, 100,\n\t\t\t\tPBEMoveEffect.HPDrain, 50, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByIronFist | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.DreamEater,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Special, 0, 3, 100, 100,\n\t\t\t\tPBEMoveEffect.HPDrain__RequireSleep, 50, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.DrillPeck,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Flying, PBEMoveCategory.Physical, 0, 4, 80, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleNotSelf,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.DrillRun,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ground, PBEMoveCategory.Physical, 0, 2, 80, 95,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.HighCritChance | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.DualChop,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dragon, PBEMoveCategory.Physical, 0, 3, 40, 90,\n\t\t\t\tPBEMoveEffect.Hit__2Times, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.DynamicPunch,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Physical, 0, 1, 100, 50,\n\t\t\t\tPBEMoveEffect.Hit__MaybeConfuse, 100, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByIronFist | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.EarthPower,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ground, PBEMoveCategory.Special, 0, 2, 90, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_SPDEF_By1, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Earthquake,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ground, PBEMoveCategory.Physical, 0, 2, 100, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.AllSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.DoubleDamageUnderground\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.EchoedVoice,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Special, 0, 3, 40, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.AffectedBySoundproof\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.EggBomb,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 2, 100, 75,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.ElectroBall,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Electric, PBEMoveCategory.Special, 0, 2, 0, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Electroweb,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Electric, PBEMoveCategory.Special, 0, 3, 55, 95,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_SPE_By1, 100, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Embargo,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dark, PBEMoveCategory.Status, 0, 3, 0, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Ember,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fire, PBEMoveCategory.Special, 0, 5, 40, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeBurn, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Encore,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 1, 0, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Endeavor,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 1, 0, 100,\n\t\t\t\tPBEMoveEffect.Endeavor, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Endure,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, +4, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.BlockedFromAssist | PBEMoveFlag.BlockedFromCopycat | PBEMoveFlag.BlockedFromMetronome\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.EnergyBall,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Special, 0, 2, 80, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_SPDEF_By1, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Entrainment,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 3, 0, 100,\n\t\t\t\tPBEMoveEffect.Entrainment, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Eruption,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fire, PBEMoveCategory.Special, 0, 1, 150, 100,\n\t\t\t\tPBEMoveEffect.Eruption, 0, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Explosion,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 1, 250, 100,\n\t\t\t\tPBEMoveEffect.Selfdestruct, 0, PBEMoveTarget.AllSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Extrasensory,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Special, 0, 6, 80, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeFlinch, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.ExtremeSpeed,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, +2, 1, 80, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Facade,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 4, 70, 100,\n\t\t\t\tPBEMoveEffect.Facade, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.FaintAttack,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dark, PBEMoveCategory.Physical, 0, 4, 60, 0,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.FakeOut,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, +3, 2, 40, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.FakeTears,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dark, PBEMoveCategory.Status, 0, 4, 0, 100,\n\t\t\t\tPBEMoveEffect.ChangeTarget_SPDEF, -2, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.FalseSwipe,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 8, 40, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.FeatherDance,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Flying, PBEMoveCategory.Status, 0, 3, 0, 100,\n\t\t\t\tPBEMoveEffect.ChangeTarget_ATK, -2, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Feint,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, +2, 2, 30, 100,\n\t\t\t\tPBEMoveEffect.Feint, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.BlockedFromAssist | PBEMoveFlag.BlockedFromCopycat | PBEMoveFlag.BlockedFromMetronome\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.FieryDance,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fire, PBEMoveCategory.Special, 0, 2, 80, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeRaiseUser_SPATK_By1, 50, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.FinalGambit,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Special, 0, 1, 0, 100,\n\t\t\t\tPBEMoveEffect.FinalGambit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.FireBlast,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fire, PBEMoveCategory.Special, 0, 1, 120, 85,\n\t\t\t\tPBEMoveEffect.Hit__MaybeBurn, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.FireFang,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fire, PBEMoveCategory.Physical, 0, 3, 65, 95,\n\t\t\t\tPBEMoveEffect.Hit__MaybeBurn__10PercentFlinch, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.FirePledge,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fire, PBEMoveCategory.Special, 0, 2, 50, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.UnaffectedByGems\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.FirePunch,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fire, PBEMoveCategory.Physical, 0, 3, 75, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeBurn, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByIronFist | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.FireSpin,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fire, PBEMoveCategory.Special, 0, 3, 35, 85,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Fissure,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ground, PBEMoveCategory.Physical, 0, 1, 0, 30,\n\t\t\t\tPBEMoveEffect.OneHitKnockout, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByProtect | PBEMoveFlag.HitsUnderground\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Flail,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 3, 0, 100,\n\t\t\t\tPBEMoveEffect.Flail, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.FlameBurst,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fire, PBEMoveCategory.Special, 0, 3, 70, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.FlameCharge,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fire, PBEMoveCategory.Physical, 0, 4, 50, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeRaiseUser_SPE_By1, 100, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Flamethrower,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fire, PBEMoveCategory.Special, 0, 3, 95, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeBurn, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.FlameWheel,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fire, PBEMoveCategory.Physical, 0, 5, 60, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeBurn, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.DefrostsUser | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.FlareBlitz,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fire, PBEMoveCategory.Physical, 0, 3, 120, 100,\n\t\t\t\tPBEMoveEffect.Recoil__10PercentBurn, 3, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.AffectedByReckless | PBEMoveFlag.DefrostsUser | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Flash,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 4, 0, 100,\n\t\t\t\tPBEMoveEffect.ChangeTarget_ACC, -1, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.FlashCannon,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Steel, PBEMoveCategory.Special, 0, 2, 80, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_SPDEF_By1, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Flatter,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dark, PBEMoveCategory.Status, 0, 3, 0, 100,\n\t\t\t\tPBEMoveEffect.Flatter, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Fling,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dark, PBEMoveCategory.Physical, 0, 2, 0, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Fly,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Flying, PBEMoveCategory.Physical, 0, 3, 90, 95,\n\t\t\t\tPBEMoveEffect.Fly, 0, PBEMoveTarget.SingleNotSelf,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.BlockedByGravity | PBEMoveFlag.BlockedFromSleepTalk | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.FocusBlast,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Special, 0, 1, 120, 70,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_SPDEF_By1, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.FocusEnergy,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 6, 0, 0,\n\t\t\t\tPBEMoveEffect.FocusEnergy, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.FocusPunch,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Physical, -3, 4, 150, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByIronFist | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.BlockedFromAssist | PBEMoveFlag.BlockedFromCopycat | PBEMoveFlag.BlockedFromMeFirst | PBEMoveFlag.BlockedFromMetronome | PBEMoveFlag.BlockedFromSleepTalk | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.FollowMe,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, +3, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.BlockedFromAssist | PBEMoveFlag.BlockedFromCopycat | PBEMoveFlag.BlockedFromMetronome\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.ForcePalm,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Physical, 0, 2, 60, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeParalyze, 30, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Foresight,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 8, 0, 0,\n\t\t\t\tPBEMoveEffect.Foresight, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.FoulPlay,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dark, PBEMoveCategory.Physical, 0, 3, 95, 100,\n\t\t\t\tPBEMoveEffect.FoulPlay, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.FreezeShock,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ice, PBEMoveCategory.Physical, 0, 1, 140, 90,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 30, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.BlockedFromSleepTalk | PBEMoveFlag.BlockedFromMetronome\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.FrenzyPlant,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Special, 0, 1, 150, 90,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.FrostBreath,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ice, PBEMoveCategory.Special, 0, 2, 40, 90,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.AlwaysCrit\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Frustration,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 4, 0, 100,\n\t\t\t\tPBEMoveEffect.Frustration, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.FuryAttack,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 3, 15, 85,\n\t\t\t\tPBEMoveEffect.Hit__2To5Times, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.FuryCutter,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Bug, PBEMoveCategory.Physical, 0, 4, 20, 95,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.FurySwipes,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 3, 18, 85,\n\t\t\t\tPBEMoveEffect.Hit__2To5Times, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.FusionBolt,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Electric, PBEMoveCategory.Physical, 0, 1, 100, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.FusionFlare,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fire, PBEMoveCategory.Special, 0, 1, 100, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.FutureSight,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Special, 0, 2, 100, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.None\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.GastroAcid,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Poison, PBEMoveCategory.Status, 0, 2, 0, 100,\n\t\t\t\tPBEMoveEffect.GastroAcid, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.GearGrind,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Steel, PBEMoveCategory.Physical, 0, 3, 50, 85,\n\t\t\t\tPBEMoveEffect.Hit__2Times, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.GigaDrain,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Special, 0, 2, 75, 100,\n\t\t\t\tPBEMoveEffect.HPDrain, 50, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.GigaImpact,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 1, 150, 90,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Glaciate,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ice, PBEMoveCategory.Special, 0, 2, 65, 95,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_SPE_By1, 100, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Glare,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 6, 0, 90,\n\t\t\t\tPBEMoveEffect.Paralyze, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.GrassKnot,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Special, 0, 4, 0, 100,\n\t\t\t\tPBEMoveEffect.GrassKnot, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.GrassPledge,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Special, 0, 2, 50, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.UnaffectedByGems\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.GrassWhistle,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Status, 0, 3, 0, 55,\n\t\t\t\tPBEMoveEffect.Sleep, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.AffectedBySoundproof\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Gravity,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, 0, 1, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.All,\n\t\t\t\tPBEMoveFlag.None\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Growl,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 8, 0, 100,\n\t\t\t\tPBEMoveEffect.ChangeTarget_ATK, -1, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.AffectedBySoundproof\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Growth,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Status, 0, 8, 0, 0,\n\t\t\t\tPBEMoveEffect.Growth, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Grudge,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ghost, PBEMoveCategory.Status, 0, 1, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.None\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.GuardSplit,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.GuardSwap,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Guillotine,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 1, 0, 30,\n\t\t\t\tPBEMoveEffect.OneHitKnockout, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.GunkShot,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Poison, PBEMoveCategory.Physical, 0, 1, 120, 70,\n\t\t\t\tPBEMoveEffect.Hit__MaybePoison, 30, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Gust,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Flying, PBEMoveCategory.Special, 0, 7, 40, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleNotSelf,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.DoubleDamageAirborne\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.GyroBall,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Steel, PBEMoveCategory.Physical, 0, 1, 0, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Hail,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ice, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.Hail, 0, PBEMoveTarget.All,\n\t\t\t\tPBEMoveFlag.None\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.HammerArm,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Physical, 0, 2, 100, 90,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerUser_SPE_By1, 100, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByIronFist | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Harden,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 6, 0, 0,\n\t\t\t\tPBEMoveEffect.ChangeTarget_DEF, +1, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Haze,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ice, PBEMoveCategory.Status, 0, 6, 0, 0,\n\t\t\t\tPBEMoveEffect.Haze, 0, PBEMoveTarget.All,\n\t\t\t\tPBEMoveFlag.None\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Headbutt,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 3, 70, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeFlinch, 30, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.HeadCharge,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 3, 120, 100,\n\t\t\t\tPBEMoveEffect.Recoil, 4, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.AffectedByReckless | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.HeadSmash,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Rock, PBEMoveCategory.Physical, 0, 1, 150, 80,\n\t\t\t\tPBEMoveEffect.Recoil, 2, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.AffectedByReckless | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.HealBell,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 1, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.AllTeam,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.HealBlock,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, 0, 3, 0, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.HealingWish,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.HealOrder,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Bug, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.RestoreTargetHP, 50, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.HealPulse,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.RestoreTargetHP, 50, PBEMoveTarget.SingleNotSelf,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.HeartStamp,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Physical, 0, 5, 60, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeFlinch, 30, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.HeartSwap,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.HeatCrash,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fire, PBEMoveCategory.Physical, 0, 2, 0, 100,\n\t\t\t\tPBEMoveEffect.HeatCrash, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.HeatWave,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fire, PBEMoveCategory.Special, 0, 2, 100, 90,\n\t\t\t\tPBEMoveEffect.Hit__MaybeBurn, 10, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.HeavySlam,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Steel, PBEMoveCategory.Physical, 0, 2, 0, 100,\n\t\t\t\tPBEMoveEffect.HeatCrash, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.HelpingHand,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, +5, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.HelpingHand, 0, PBEMoveTarget.SingleAllySurrounding,\n\t\t\t\tPBEMoveFlag.BlockedFromAssist | PBEMoveFlag.BlockedFromCopycat | PBEMoveFlag.BlockedFromMetronome\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Hex,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ghost, PBEMoveCategory.Special, 0, 2, 50, 100,\n\t\t\t\tPBEMoveEffect.Hex, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.HiddenPower,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Special, 0, 3, 0, 100,\n\t\t\t\tPBEMoveEffect.HiddenPower, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.HiJumpKick,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Physical, 0, 2, 130, 90,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.AffectedByReckless | PBEMoveFlag.BlockedByGravity | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.HoneClaws,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dark, PBEMoveCategory.Status, 0, 3, 0, 0,\n\t\t\t\tPBEMoveEffect.RaiseTarget_ATK_ACC_By1, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.HornAttack,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 5, 65, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.HornDrill,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 1, 0, 30,\n\t\t\t\tPBEMoveEffect.OneHitKnockout, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.HornLeech,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Physical, 0, 2, 75, 100,\n\t\t\t\tPBEMoveEffect.HPDrain, 50, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Howl,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 8, 0, 0,\n\t\t\t\tPBEMoveEffect.ChangeTarget_ATK, +1, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Hurricane,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Flying, PBEMoveCategory.Special, 0, 2, 120, 70,\n\t\t\t\tPBEMoveEffect.Hit__MaybeConfuse, 30, PBEMoveTarget.SingleNotSelf,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.HitsAirborne | PBEMoveFlag.NeverMissRain\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.HydroCannon,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Water, PBEMoveCategory.Special, 0, 1, 150, 90,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.HydroPump,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Water, PBEMoveCategory.Special, 0, 1, 120, 80,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.HyperBeam,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Special, 0, 1, 150, 90,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.HyperFang,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 3, 80, 90,\n\t\t\t\tPBEMoveEffect.Hit__MaybeFlinch, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.HyperVoice,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Special, 0, 2, 90, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.AffectedBySoundproof\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Hypnosis,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, 0, 4, 0, 60,\n\t\t\t\tPBEMoveEffect.Sleep, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.IceBall,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ice, PBEMoveCategory.Physical, 0, 4, 30, 90,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.DoubleDamageUserDefenseCurl | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.IceBeam,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ice, PBEMoveCategory.Special, 0, 2, 95, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeFreeze, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.IceBurn,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ice, PBEMoveCategory.Special, 0, 1, 140, 90,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 30, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.BlockedFromSleepTalk | PBEMoveFlag.BlockedFromMetronome\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.IceFang,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ice, PBEMoveCategory.Physical, 0, 3, 65, 95,\n\t\t\t\tPBEMoveEffect.Hit__MaybeFreeze__10PercentFlinch, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.IcePunch,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ice, PBEMoveCategory.Physical, 0, 3, 75, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeFreeze, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByIronFist | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.IceShard,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ice, PBEMoveCategory.Physical, +1, 6, 40, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.IcicleCrash,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ice, PBEMoveCategory.Physical, 0, 2, 85, 90,\n\t\t\t\tPBEMoveEffect.Hit__MaybeFlinch, 30, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.IcicleSpear,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ice, PBEMoveCategory.Physical, 0, 6, 25, 100,\n\t\t\t\tPBEMoveEffect.Hit__2To5Times, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.IcyWind,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ice, PBEMoveCategory.Special, 0, 3, 55, 95,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_SPE_By1, 100, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Imprison,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Incinerate,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fire, PBEMoveCategory.Special, 0, 3, 30, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Inferno,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fire, PBEMoveCategory.Special, 0, 1, 100, 50,\n\t\t\t\tPBEMoveEffect.Hit__MaybeBurn, 100, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Ingrain,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Status, 0, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.IronDefense,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Steel, PBEMoveCategory.Status, 0, 3, 0, 0,\n\t\t\t\tPBEMoveEffect.ChangeTarget_DEF, +2, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.IronHead,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Steel, PBEMoveCategory.Physical, 0, 3, 80, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeFlinch, 30, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.IronTail,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Steel, PBEMoveCategory.Physical, 0, 3, 100, 75,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_DEF_By1, 30, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Judgment,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Special, 0, 2, 100, 100,\n\t\t\t\tPBEMoveEffect.Judgment, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.JumpKick,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Physical, 0, 2, 100, 95,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.AffectedByReckless | PBEMoveFlag.BlockedByGravity | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.KarateChop,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Physical, 0, 5, 50, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.HighCritChance | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Kinesis,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, 0, 3, 0, 80,\n\t\t\t\tPBEMoveEffect.ChangeTarget_ACC, -1, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.KnockOff,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dark, PBEMoveCategory.Physical, 0, 4, 20, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.LastResort,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 1, 140, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.LavaPlume,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fire, PBEMoveCategory.Special, 0, 3, 80, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeBurn, 30, PBEMoveTarget.AllSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.LeafBlade,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Physical, 0, 3, 90, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.HighCritChance | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.LeafStorm,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Special, 0, 1, 140, 90,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerUser_SPATK_By2, 100, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.LeafTornado,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Special, 0, 2, 65, 90,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_ACC_By1, 50, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.LeechLife,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Bug, PBEMoveCategory.Physical, 0, 3, 20, 100,\n\t\t\t\tPBEMoveEffect.HPDrain, 50, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.LeechSeed,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Status, 0, 2, 0, 90,\n\t\t\t\tPBEMoveEffect.LeechSeed, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Leer,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 6, 0, 100,\n\t\t\t\tPBEMoveEffect.ChangeTarget_DEF, -1, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Lick,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ghost, PBEMoveCategory.Physical, 0, 6, 20, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeParalyze, 30, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.LightScreen,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, 0, 6, 0, 0,\n\t\t\t\tPBEMoveEffect.LightScreen, 0, PBEMoveTarget.AllTeam,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.LockOn,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 1, 0, 0,\n\t\t\t\tPBEMoveEffect.LockOn, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.LovelyKiss,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 2, 0, 75,\n\t\t\t\tPBEMoveEffect.Sleep, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.LowKick,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Physical, 0, 4, 0, 100,\n\t\t\t\tPBEMoveEffect.GrassKnot, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.LowSweep,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Physical, 0, 4, 60, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_SPE_By1, 100, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.LuckyChant,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 6, 0, 0,\n\t\t\t\tPBEMoveEffect.LuckyChant, 0, PBEMoveTarget.AllTeam,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.LunarDance,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.LusterPurge,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Special, 0, 1, 70, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_SPDEF_By1, 50, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.MachPunch,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Physical, +1, 6, 40, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByIronFist | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.MagicalLeaf,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Special, 0, 4, 60, 0,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.MagicCoat,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, +4, 3, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.None\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.MagicRoom,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, -7, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.All,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.MagmaStorm,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fire, PBEMoveCategory.Special, 0, 1, 120, 75,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.MagnetBomb,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Steel, PBEMoveCategory.Physical, 0, 4, 60, 0,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.MagnetRise,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Electric, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.MagnetRise, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch | PBEMoveFlag.BlockedByGravity\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Magnitude,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ground, PBEMoveCategory.Physical, 0, 6, 0, 100,\n\t\t\t\tPBEMoveEffect.Magnitude, 0, PBEMoveTarget.AllSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.DoubleDamageUnderground\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.MeanLook,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 1, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Meditate,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, 0, 8, 0, 0,\n\t\t\t\tPBEMoveEffect.ChangeTarget_ATK, +1, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.MeFirst,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleFoeSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByProtect | PBEMoveFlag.BlockedFromAssist | PBEMoveFlag.BlockedFromCopycat | PBEMoveFlag.BlockedFromMetronome | PBEMoveFlag.BlockedFromSleepTalk\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.MegaDrain,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Special, 0, 3, 40, 100,\n\t\t\t\tPBEMoveEffect.HPDrain, 50, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Megahorn,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Bug, PBEMoveCategory.Physical, 0, 2, 120, 85,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.MegaKick,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 1, 120, 75,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.MegaPunch,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 4, 80, 85,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByIronFist | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Memento,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dark, PBEMoveCategory.Status, 0, 2, 0, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.MetalBurst,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Steel, PBEMoveCategory.Physical, 0, 2, 0, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.BlockedFromMeFirst\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.MetalClaw,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Steel, PBEMoveCategory.Physical, 0, 7, 50, 95,\n\t\t\t\tPBEMoveEffect.Hit__MaybeRaiseUser_ATK_By1, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.MetalSound,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Steel, PBEMoveCategory.Status, 0, 8, 0, 85,\n\t\t\t\tPBEMoveEffect.ChangeTarget_SPDEF, -2, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.AffectedBySoundproof\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.MeteorMash,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Steel, PBEMoveCategory.Special, 0, 2, 100, 85,\n\t\t\t\tPBEMoveEffect.Hit__MaybeRaiseUser_ATK_By1, 20, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByIronFist | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Metronome,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.Metronome, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.BlockedFromAssist | PBEMoveFlag.BlockedFromCopycat | PBEMoveFlag.BlockedFromMetronome | PBEMoveFlag.BlockedFromMimic | PBEMoveFlag.BlockedFromSketchWhenSuccessful | PBEMoveFlag.BlockedFromSleepTalk\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.MilkDrink,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.RestoreTargetHP, 50, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Mimic,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByProtect | PBEMoveFlag.BlockedFromAssist | PBEMoveFlag.BlockedFromCopycat | PBEMoveFlag.BlockedFromMetronome | PBEMoveFlag.BlockedFromSketchWhenSuccessful | PBEMoveFlag.BlockedFromSleepTalk\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.MindReader,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 1, 0, 0,\n\t\t\t\tPBEMoveEffect.LockOn, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Minimize,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.Minimize, +2, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.MiracleEye,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, 0, 8, 0, 0,\n\t\t\t\tPBEMoveEffect.MiracleEye, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.MirrorCoat,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Special, -5, 4, 0, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedByProtect | PBEMoveFlag.BlockedFromAssist | PBEMoveFlag.BlockedFromCopycat | PBEMoveFlag.BlockedFromMeFirst | PBEMoveFlag.BlockedFromMetronome\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.MirrorMove,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Flying, PBEMoveCategory.Status, 0, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.BlockedFromAssist | PBEMoveFlag.BlockedFromCopycat | PBEMoveFlag.BlockedFromMetronome | PBEMoveFlag.BlockedFromSleepTalk\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.MirrorShot,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Steel, PBEMoveCategory.Special, 0, 2, 65, 85,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_ACC_By1, 30, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Mist,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ice, PBEMoveCategory.Status, 0, 6, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.AllTeam,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.MistBall,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Special, 0, 1, 70, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_SPATK_By1, 50, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Moonlight,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 1, 0, 0,\n\t\t\t\tPBEMoveEffect.Moonlight, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.MorningSun,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 1, 0, 0,\n\t\t\t\tPBEMoveEffect.Moonlight, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.MudBomb,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ground, PBEMoveCategory.Special, 0, 2, 65, 85,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_ACC_By1, 30, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.MuddyWater,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Water, PBEMoveCategory.Special, 0, 2, 95, 85,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_ACC_By1, 30, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.MudSlap,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ground, PBEMoveCategory.Special, 0, 2, 20, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_ACC_By1, 100, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.MudSport,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ground, PBEMoveCategory.Status, 0, 3, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.All,\n\t\t\t\tPBEMoveFlag.None\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.MudShot,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ground, PBEMoveCategory.Special, 0, 3, 55, 95,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_ACC_By1, 100, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.NastyPlot,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dark, PBEMoveCategory.Status, 0, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.ChangeTarget_SPATK, +2, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.NaturalGift,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 3, 0, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.NaturePower,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.BlockedFromMetronome\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.NeedleArm,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Physical, 0, 3, 60, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeFlinch, 30, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.NightDaze,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dark, PBEMoveCategory.Special, 0, 2, 85, 95,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_ACC_By1, 40, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Nightmare,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ghost, PBEMoveCategory.Status, 0, 3, 0, 100,\n\t\t\t\tPBEMoveEffect.Nightmare, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.NightShade,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ghost, PBEMoveCategory.Special, 0, 3, 0, 100,\n\t\t\t\tPBEMoveEffect.SeismicToss, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.NightSlash,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dark, PBEMoveCategory.Physical, 0, 3, 70, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.HighCritChance | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Octazooka,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Water, PBEMoveCategory.Special, 0, 2, 65, 85,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_ACC_By1, 50, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.OdorSleuth,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 8, 0, 0,\n\t\t\t\tPBEMoveEffect.Foresight, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.OminousWind,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ghost, PBEMoveCategory.Special, 0, 1, 60, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeRaiseUser_ATK_DEF_SPATK_SPDEF_SPE_By1, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Outrage,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dragon, PBEMoveCategory.Physical, 0, 2, 120, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.RandomFoeSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Overheat,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fire, PBEMoveCategory.Special, 0, 1, 140, 90,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerUser_SPATK_By2, 100, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.PainSplit,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.PainSplit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Payback,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dark, PBEMoveCategory.Physical, 0, 2, 50, 100,\n\t\t\t\tPBEMoveEffect.Payback, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.PayDay,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 4, 40, 100,\n\t\t\t\tPBEMoveEffect.PayDay, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Peck,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Flying, PBEMoveCategory.Physical, 0, 7, 35, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleNotSelf,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.PerishSong,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 1, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.All,\n\t\t\t\tPBEMoveFlag.AffectedBySoundproof\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.PetalDance,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Special, 0, 2, 120, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.RandomFoeSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.PinMissile,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Bug, PBEMoveCategory.Physical, 0, 4, 14, 85,\n\t\t\t\tPBEMoveEffect.Hit__2To5Times, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Pluck,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Flying, PBEMoveCategory.Physical, 0, 4, 60, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleNotSelf,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.PoisonFang,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Poison, PBEMoveCategory.Physical, 0, 3, 50, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeToxic, 30, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.PoisonGas,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Poison, PBEMoveCategory.Status, 0, 8, 0, 80,\n\t\t\t\tPBEMoveEffect.Poison, 0, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.PoisonJab,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Poison, PBEMoveCategory.Physical, 0, 4, 80, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybePoison, 30, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.PoisonPowder,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Poison, PBEMoveCategory.Status, 0, 7, 0, 75,\n\t\t\t\tPBEMoveEffect.Poison, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.PoisonSting,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Poison, PBEMoveCategory.Physical, 0, 7, 15, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybePoison, 30, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.PoisonTail,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Poison, PBEMoveCategory.Physical, 0, 5, 50, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybePoison, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.HighCritChance | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Pound,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 7, 40, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.PowderSnow,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ice, PBEMoveCategory.Special, 0, 5, 40, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeFreeze, 10, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.PowerGem,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Rock, PBEMoveCategory.Special, 0, 4, 70, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.PowerSplit,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.PowerSwap,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.PowerTrick,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.PowerTrick, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.PowerWhip,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Physical, 0, 2, 120, 85,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Present,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 3, 0, 90,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Protect,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, +4, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.Protect, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.BlockedFromAssist | PBEMoveFlag.BlockedFromCopycat | PBEMoveFlag.BlockedFromMetronome\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Psybeam,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Special, 0, 4, 65, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeConfuse, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Psychic,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Special, 0, 2, 90, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_SPDEF_By1, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.PsychoBoost,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Special, 0, 1, 140, 90,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerUser_SPATK_By2, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.PsychoCut,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Physical, 0, 4, 70, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.HighCritChance\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.PsychoShift,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, 0, 2, 0, 90,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.PsychUp,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.PsychUp, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.None\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Psyshock,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Special, 0, 2, 80, 100,\n\t\t\t\tPBEMoveEffect.Psyshock, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Psystrike,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Special, 0, 2, 100, 100,\n\t\t\t\tPBEMoveEffect.Psyshock, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Psywave,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Special, 0, 3, 0, 80,\n\t\t\t\tPBEMoveEffect.Psywave, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Punishment,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dark, PBEMoveCategory.Physical, 0, 1, 60, 100,\n\t\t\t\tPBEMoveEffect.Punishment, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Pursuit,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dark, PBEMoveCategory.Physical, 0, 4, 40, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Quash,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dark, PBEMoveCategory.Status, 0, 3, 0, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.BlockedFromMetronome\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.QuickAttack,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, +1, 6, 40, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.QuickGuard,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Status, +3, 3, 0, 0,\n\t\t\t\tPBEMoveEffect.QuickGuard, 0, PBEMoveTarget.AllTeam,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch | PBEMoveFlag.BlockedFromMetronome\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.QuiverDance,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Bug, PBEMoveCategory.Status, 0, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.RaiseTarget_SPATK_SPDEF_SPE_By1, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Rage,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 4, 20, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.RagePowder,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Bug, PBEMoveCategory.Status, +3, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.BlockedFromAssist | PBEMoveFlag.BlockedFromCopycat | PBEMoveFlag.BlockedFromMetronome\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.RainDance,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Water, PBEMoveCategory.Status, 0, 1, 0, 0,\n\t\t\t\tPBEMoveEffect.RainDance, 0, PBEMoveTarget.All,\n\t\t\t\tPBEMoveFlag.None\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.RapidSpin,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 8, 20, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.RazorLeaf,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Physical, 0, 5, 55, 95,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.HighCritChance\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.RazorShell,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Water, PBEMoveCategory.Physical, 0, 2, 75, 95,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_DEF_By1, 50, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.RazorWind,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Special, 0, 2, 80, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.BlockedFromSleepTalk | PBEMoveFlag.HighCritChance\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Recover,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.RestoreTargetHP, 50, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Recycle,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Reflect,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, 0, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.Reflect, 0, PBEMoveTarget.AllTeam,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.ReflectType,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 3, 0, 0,\n\t\t\t\tPBEMoveEffect.ReflectType, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Refresh,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.Refresh, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.RelicSong,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Special, 0, 2, 75, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.AffectedBySoundproof | PBEMoveFlag.BlockedFromMetronome\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Rest,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.Rest, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Retaliate,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 1, 70, 100,\n\t\t\t\tPBEMoveEffect.Retaliate, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Return,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 4, 0, 100,\n\t\t\t\tPBEMoveEffect.Return, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Revenge,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Physical, -4, 2, 60, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Reversal,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Physical, 0, 3, 0, 100,\n\t\t\t\tPBEMoveEffect.Flail, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Roar,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, -6, 4, 0, 100,\n\t\t\t\tPBEMoveEffect.Whirlwind, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.AffectedBySoundproof\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.RoarOfTime,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dragon, PBEMoveCategory.Special, 0, 1, 150, 90,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.RockBlast,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Rock, PBEMoveCategory.Physical, 0, 2, 25, 90,\n\t\t\t\tPBEMoveEffect.Hit__2To5Times, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.RockClimb,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 4, 90, 85,\n\t\t\t\tPBEMoveEffect.Hit__MaybeConfuse, 20, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.RockPolish,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Rock, PBEMoveCategory.Status, 0, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.ChangeTarget_SPE, +2, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.RockSlide,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Rock, PBEMoveCategory.Physical, 0, 2, 75, 90,\n\t\t\t\tPBEMoveEffect.Hit__MaybeFlinch, 30, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.RockSmash,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Physical, 0, 3, 40, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.RockThrow,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Rock, PBEMoveCategory.Physical, 0, 3, 50, 90,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.RockTomb,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Rock, PBEMoveCategory.Physical, 0, 2, 50, 80,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_SPE_By1, 100, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.RockWrecker,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Rock, PBEMoveCategory.Physical, 0, 1, 150, 90,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.RolePlay,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.RolePlay, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.None\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.RollingKick,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Physical, 0, 3, 60, 85,\n\t\t\t\tPBEMoveEffect.Hit__MaybeFlinch, 30, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Rollout,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Rock, PBEMoveCategory.Physical, 0, 4, 30, 90,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.DoubleDamageUserDefenseCurl | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Roost,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Flying, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.Roost, 50, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Round,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Special, 0, 3, 60, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.AffectedBySoundproof\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SacredFire,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fire, PBEMoveCategory.Physical, 0, 1, 100, 95,\n\t\t\t\tPBEMoveEffect.Hit__MaybeBurn, 50, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.DefrostsUser\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SacredSword,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Physical, 0, 4, 90, 100,\n\t\t\t\tPBEMoveEffect.ChipAway, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Safeguard,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 5, 0, 0,\n\t\t\t\tPBEMoveEffect.Safeguard, 0, PBEMoveTarget.AllTeam,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SandAttack,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ground, PBEMoveCategory.Status, 0, 3, 0, 100,\n\t\t\t\tPBEMoveEffect.ChangeTarget_ACC, -1, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Sandstorm,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Rock, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.Sandstorm, 0, PBEMoveTarget.All,\n\t\t\t\tPBEMoveFlag.None\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SandTomb,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ground, PBEMoveCategory.Physical, 0, 3, 35, 85,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Scald,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Water, PBEMoveCategory.Special, 0, 3, 80, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeBurn, 30, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.DefrostsUser\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.ScaryFace,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 2, 0, 100,\n\t\t\t\tPBEMoveEffect.ChangeTarget_SPE, -2, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Scratch,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 8, 40, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Screech,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 8, 0, 85,\n\t\t\t\tPBEMoveEffect.ChangeTarget_DEF, -2, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.AffectedBySoundproof\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SearingShot,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fire, PBEMoveCategory.Special, 0, 1, 100, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeBurn, 30, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SecretPower,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 4, 70, 100,\n\t\t\t\tPBEMoveEffect.SecretPower, 30, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SecretSword,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Special, 0, 2, 85, 100,\n\t\t\t\tPBEMoveEffect.Psyshock, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.BlockedFromMetronome\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SeedBomb,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Physical, 0, 3, 80, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SeedFlare,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Special, 0, 1, 120, 85,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_SPDEF_By2, 40, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SeismicToss,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Physical, 0, 4, 0, 100,\n\t\t\t\tPBEMoveEffect.SeismicToss, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Selfdestruct,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 1, 200, 100,\n\t\t\t\tPBEMoveEffect.Selfdestruct, 0, PBEMoveTarget.AllSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.ShadowBall,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ghost, PBEMoveCategory.Special, 0, 3, 80, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_SPDEF_By1, 20, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.ShadowClaw,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ghost, PBEMoveCategory.Physical, 0, 3, 70, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.HighCritChance | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.ShadowForce,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ghost, PBEMoveCategory.Physical, 0, 1, 120, 100,\n\t\t\t\tPBEMoveEffect.ShadowForce, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.BlockedFromSleepTalk | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.ShadowPunch,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ghost, PBEMoveCategory.Physical, 0, 4, 60, 0,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByIronFist | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.ShadowSneak,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ghost, PBEMoveCategory.Physical, +1, 6, 40, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Sharpen,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 6, 0, 0,\n\t\t\t\tPBEMoveEffect.ChangeTarget_ATK, +1, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SheerCold,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ice, PBEMoveCategory.Special, 0, 1, 0, 30,\n\t\t\t\tPBEMoveEffect.OneHitKnockout, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.ShellSmash,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 3, 0, 0,\n\t\t\t\tPBEMoveEffect.LowerTarget_DEF_SPDEF_By1_Raise_ATK_SPATK_SPE_By2, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.ShiftGear,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Steel, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.RaiseTarget_SPE_By2_ATK_By1, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SignalBeam,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Bug, PBEMoveCategory.Special, 0, 3, 75, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeConfuse, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SilverWind,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Bug, PBEMoveCategory.Special, 0, 1, 60, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeRaiseUser_ATK_DEF_SPATK_SPDEF_SPE_By1, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SimpleBeam,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 3, 0, 100,\n\t\t\t\tPBEMoveEffect.SimpleBeam, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Sing,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 3, 0, 55,\n\t\t\t\tPBEMoveEffect.Sleep, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.AffectedBySoundproof\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Sketch,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 0, 0, 0,\n\t\t\t\tPBEMoveEffect.Sketch, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.BlockedFromAssist | PBEMoveFlag.BlockedFromCopycat | PBEMoveFlag.BlockedFromMetronome | PBEMoveFlag.BlockedFromMimic | PBEMoveFlag.BlockedFromSketch | PBEMoveFlag.BlockedFromSleepTalk\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SkillSwap,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SkullBash,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 3, 100, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.BlockedFromSleepTalk | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SkyAttack,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Flying, PBEMoveCategory.Physical, 0, 1, 140, 90,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleNotSelf,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.BlockedFromSleepTalk\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SkyDrop,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Flying, PBEMoveCategory.Physical, 0, 2, 60, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleNotSelf,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.BlockedByGravity | PBEMoveFlag.BlockedFromSleepTalk | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.ShockWave,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Electric, PBEMoveCategory.Special, 0, 4, 60, 0,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SkyUppercut,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Physical, 0, 3, 85, 90,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByIronFist | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.HitsAirborne | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SlackOff,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.RestoreTargetHP, 50, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Slam,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 4, 80, 75,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Slash,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 4, 70, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.HighCritChance | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SleepPowder,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Status, 0, 3, 0, 75,\n\t\t\t\tPBEMoveEffect.Sleep, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SleepTalk,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.BlockedFromAssist | PBEMoveFlag.BlockedFromCopycat | PBEMoveFlag.BlockedFromMetronome | PBEMoveFlag.BlockedFromSleepTalk\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Sludge,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Poison, PBEMoveCategory.Special, 0, 4, 65, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybePoison, 30, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SludgeBomb,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Poison, PBEMoveCategory.Special, 0, 2, 90, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybePoison, 30, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SmackDown,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Rock, PBEMoveCategory.Physical, 0, 3, 50, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.HitsAirborne\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SmellingSalt,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 2, 60, 100,\n\t\t\t\tPBEMoveEffect.SmellingSalt, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SludgeWave,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Poison, PBEMoveCategory.Special, 0, 2, 95, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybePoison, 10, PBEMoveTarget.AllSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Smog,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Poison, PBEMoveCategory.Special, 0, 4, 20, 70,\n\t\t\t\tPBEMoveEffect.Hit__MaybePoison, 40, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SmokeScreen,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 4, 0, 100,\n\t\t\t\tPBEMoveEffect.ChangeTarget_ACC, -1, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Snarl,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dark, PBEMoveCategory.Special, 0, 3, 55, 95,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_SPATK_By1, 100, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.AffectedBySoundproof | PBEMoveFlag.BlockedFromMetronome\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Snatch,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dark, PBEMoveCategory.Status, +4, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.BlockedFromAssist | PBEMoveFlag.BlockedFromCopycat | PBEMoveFlag.BlockedFromMetronome\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Snore,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Special, 0, 3, 40, 100,\n\t\t\t\tPBEMoveEffect.Snore, 30, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.AffectedBySoundproof | PBEMoveFlag.BlockedFromMetronome\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Soak,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Water, PBEMoveCategory.Status, 0, 4, 0, 100,\n\t\t\t\tPBEMoveEffect.Soak, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Softboiled,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.RestoreTargetHP, 50, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SolarBeam,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Special, 0, 2, 120, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.BlockedFromSleepTalk\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SonicBoom,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Special, 0, 4, 0, 90,\n\t\t\t\tPBEMoveEffect.SetDamage, 20, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SpacialRend,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dragon, PBEMoveCategory.Special, 0, 1, 100, 95,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.HighCritChance\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Spark,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Electric, PBEMoveCategory.Physical, 0, 4, 65, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeParalyze, 30, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SpiderWeb,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Bug, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SpikeCannon,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 3, 20, 100,\n\t\t\t\tPBEMoveEffect.Hit__2To5Times, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Spikes,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ground, PBEMoveCategory.Status, 0, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.Spikes, 0, PBEMoveTarget.AllFoes,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Spite,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Ghost, PBEMoveCategory.Status, 0, 2, 0, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SpitUp,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Special, 0, 2, 0, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Splash,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 8, 0, 0,\n\t\t\t\tPBEMoveEffect.Nothing, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.BlockedByGravity\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Spore,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Status, 0, 3, 0, 100,\n\t\t\t\tPBEMoveEffect.Sleep, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.StealthRock,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Rock, PBEMoveCategory.Status, 0, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.StealthRock, 0, PBEMoveTarget.AllFoes,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Steamroller,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Bug, PBEMoveCategory.Physical, 0, 4, 65, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeFlinch, 30, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.DoubleDamageMinimized | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SteelWing,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Steel, PBEMoveCategory.Physical, 0, 5, 70, 90,\n\t\t\t\tPBEMoveEffect.Hit__MaybeRaiseUser_DEF_By1, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Stockpile,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Stomp,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 4, 65, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.DoubleDamageMinimized | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.StoneEdge,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Rock, PBEMoveCategory.Physical, 0, 1, 100, 80,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.HighCritChance\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.StoredPower,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Special, 0, 2, 20, 100,\n\t\t\t\tPBEMoveEffect.StoredPower, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.StormThrow,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Physical, 0, 2, 40, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.AlwaysCrit | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Strength,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 3, 80, 90,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.StringShot,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Bug, PBEMoveCategory.Status, 0, 8, 0, 95,\n\t\t\t\tPBEMoveEffect.ChangeTarget_SPE, -1, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Struggle,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 0, 50, 0,\n\t\t\t\tPBEMoveEffect.Struggle, 4, PBEMoveTarget.RandomFoeSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByProtect | PBEMoveFlag.BlockedFromAssist | PBEMoveFlag.BlockedFromCopycat | PBEMoveFlag.BlockedFromMeFirst | PBEMoveFlag.BlockedFromMetronome | PBEMoveFlag.BlockedFromMimic | PBEMoveFlag.BlockedFromSketch | PBEMoveFlag.MakesContact | PBEMoveFlag.UnaffectedByGems\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.StruggleBug,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Bug, PBEMoveCategory.Special, 0, 4, 30, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerTarget_SPATK_By1, 100, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.StunSpore,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Status, 0, 6, 0, 75,\n\t\t\t\tPBEMoveEffect.Paralyze, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Submission,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Physical, 0, 5, 80, 80,\n\t\t\t\tPBEMoveEffect.Recoil, 4, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.AffectedByReckless | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Substitute,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.Substitute, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SuckerPunch,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dark, PBEMoveCategory.Physical, +1, 1, 80, 100,\n\t\t\t\tPBEMoveEffect.SuckerPunch, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SunnyDay,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fire, PBEMoveCategory.Status, 0, 1, 0, 0,\n\t\t\t\tPBEMoveEffect.SunnyDay, 0, PBEMoveTarget.All,\n\t\t\t\tPBEMoveFlag.None\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SuperFang,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 2, 0, 90,\n\t\t\t\tPBEMoveEffect.SuperFang, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Superpower,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Physical, 0, 1, 120, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerUser_ATK_DEF_By1, 100, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Supersonic,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 4, 0, 55,\n\t\t\t\tPBEMoveEffect.Confuse, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.AffectedBySoundproof\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Surf,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Water, PBEMoveCategory.Special, 0, 3, 95, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.AllSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.DoubleDamageUnderwater\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Swagger,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 3, 0, 90,\n\t\t\t\tPBEMoveEffect.Swagger, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Swallow,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SweetKiss,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 2, 0, 75,\n\t\t\t\tPBEMoveEffect.Confuse, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SweetScent,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 5, 0, 100,\n\t\t\t\tPBEMoveEffect.ChangeTarget_EVA, -1, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Swift,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Special, 0, 4, 60, 0,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Switcheroo,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dark, PBEMoveCategory.Status, 0, 2, 0, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.BlockedFromAssist | PBEMoveFlag.BlockedFromCopycat | PBEMoveFlag.BlockedFromMetronome\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.SwordsDance,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 6, 0, 0,\n\t\t\t\tPBEMoveEffect.ChangeTarget_ATK, +2, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Synchronoise,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Special, 0, 3, 70, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.AllSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Synthesis,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Status, 0, 1, 0, 0,\n\t\t\t\tPBEMoveEffect.Moonlight, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Tackle,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 7, 50, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.TailGlow,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Bug, PBEMoveCategory.Status, 0, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.ChangeTarget_SPATK, +3, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.TailSlap,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 2, 25, 85,\n\t\t\t\tPBEMoveEffect.Hit__2To5Times, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.TailWhip,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 6, 0, 100,\n\t\t\t\tPBEMoveEffect.ChangeTarget_DEF, -1, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Tailwind,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Flying, PBEMoveCategory.Status, 0, 6, 0, 0,\n\t\t\t\tPBEMoveEffect.Tailwind, 0, PBEMoveTarget.AllTeam,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.TakeDown,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 4, 90, 85,\n\t\t\t\tPBEMoveEffect.Recoil, 4, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.AffectedByReckless | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Taunt,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dark, PBEMoveCategory.Status, 0, 4, 0, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.TechnoBlast,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Special, 0, 1, 85, 100,\n\t\t\t\tPBEMoveEffect.TechnoBlast, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.BlockedFromMetronome\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.TeeterDance,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 4, 0, 100,\n\t\t\t\tPBEMoveEffect.Confuse, 0, PBEMoveTarget.AllSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Telekinesis,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, 0, 3, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.BlockedByGravity\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Teleport,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, 0, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.Teleport, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.None\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Thief,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dark, PBEMoveCategory.Physical, 0, 2, 40, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.BlockedFromAssist | PBEMoveFlag.BlockedFromCopycat | PBEMoveFlag.BlockedFromMeFirst | PBEMoveFlag.BlockedFromMetronome | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Thrash,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 2, 120, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.RandomFoeSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Thunder,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Electric, PBEMoveCategory.Special, 0, 2, 120, 70,\n\t\t\t\tPBEMoveEffect.Hit__MaybeParalyze, 30, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.HitsAirborne | PBEMoveFlag.NeverMissRain\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Thunderbolt,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Electric, PBEMoveCategory.Special, 0, 3, 95, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeParalyze, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.ThunderFang,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Electric, PBEMoveCategory.Physical, 0, 3, 65, 95,\n\t\t\t\tPBEMoveEffect.Hit__MaybeParalyze__10PercentFlinch, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.ThunderPunch,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Electric, PBEMoveCategory.Physical, 0, 3, 75, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeParalyze, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByIronFist | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.ThunderShock,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Electric, PBEMoveCategory.Special, 0, 6, 40, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeParalyze, 10, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.ThunderWave,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Electric, PBEMoveCategory.Status, 0, 4, 0, 100,\n\t\t\t\tPBEMoveEffect.ThunderWave, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Tickle,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 4, 0, 100,\n\t\t\t\tPBEMoveEffect.LowerTarget_ATK_DEF_By1, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Torment,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dark, PBEMoveCategory.Status, 0, 3, 0, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Toxic,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Poison, PBEMoveCategory.Status, 0, 2, 0, 90,\n\t\t\t\tPBEMoveEffect.Toxic, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.ToxicSpikes,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Poison, PBEMoveCategory.Status, 0, 4, 0, 0,\n\t\t\t\tPBEMoveEffect.ToxicSpikes, 0, PBEMoveTarget.AllFoes,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Transform,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.Transform, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.BlockedFromAssist | PBEMoveFlag.BlockedFromCopycat | PBEMoveFlag.BlockedFromMetronome | PBEMoveFlag.BlockedFromMimic | PBEMoveFlag.BlockedFromSketchWhenSuccessful\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.TriAttack,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Special, 0, 2, 80, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeBurnFreezeParalyze, 20, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Trick,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, 0, 2, 0, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.BlockedFromAssist | PBEMoveFlag.BlockedFromCopycat | PBEMoveFlag.BlockedFromMetronome\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.TrickRoom,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, -7, 1, 0, 0,\n\t\t\t\tPBEMoveEffect.TrickRoom, 0, PBEMoveTarget.All,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.TripleKick,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Physical, 0, 2, 10, 90,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.TrumpCard,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Special, 0, 1, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Twineedle,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Bug, PBEMoveCategory.Physical, 0, 4, 25, 100,\n\t\t\t\tPBEMoveEffect.Hit__2Times__MaybePoison, 20, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Twister,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Dragon, PBEMoveCategory.Special, 0, 4, 40, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeFlinch, 20, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.DoubleDamageAirborne\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Uproar,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Special, 0, 2, 90, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.RandomFoeSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.AffectedBySoundproof | PBEMoveFlag.BlockedFromSleepTalk\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Uturn,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Bug, PBEMoveCategory.Physical, 0, 4, 70, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.VacuumWave,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Special, +1, 6, 40, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.VCreate,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fire, PBEMoveCategory.Physical, 0, 1, 180, 95,\n\t\t\t\tPBEMoveEffect.Hit__MaybeLowerUser_SPE_DEF_SPDEF_By1, 100, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.BlockedFromMetronome | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Venoshock,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Poison, PBEMoveCategory.Special, 0, 2, 65, 100,\n\t\t\t\tPBEMoveEffect.Venoshock, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.ViceGrip,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 6, 55, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.VineWhip,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Physical, 0, 3, 35, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.VitalThrow,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Physical, -1, 2, 70, 0,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.VoltSwitch,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Electric, PBEMoveCategory.Special, 0, 4, 70, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.VoltTackle,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Electric, PBEMoveCategory.Physical, 0, 3, 120, 100,\n\t\t\t\tPBEMoveEffect.Recoil__10PercentParalyze, 3, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.AffectedByReckless | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.WakeUpSlap,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fighting, PBEMoveCategory.Physical, 0, 2, 60, 100,\n\t\t\t\tPBEMoveEffect.WakeUpSlap, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Waterfall,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Water, PBEMoveCategory.Physical, 0, 3, 80, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeFlinch, 20, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.WaterGun,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Water, PBEMoveCategory.Special, 0, 5, 40, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.WaterPledge,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Water, PBEMoveCategory.Special, 0, 2, 50, 100,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.UnaffectedByGems\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.WaterPulse,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Water, PBEMoveCategory.Special, 0, 4, 60, 100,\n\t\t\t\tPBEMoveEffect.Hit__MaybeConfuse, 20, PBEMoveTarget.SingleNotSelf,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.WaterSport,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Water, PBEMoveCategory.Status, 0, 3, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.All,\n\t\t\t\tPBEMoveFlag.None\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.WaterSpout,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Water, PBEMoveCategory.Special, 0, 1, 150, 100,\n\t\t\t\tPBEMoveEffect.Eruption, 0, PBEMoveTarget.AllFoesSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.WeatherBall,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Special, 0, 2, 50, 100,\n\t\t\t\tPBEMoveEffect.WeatherBall, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Whirlpool,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Water, PBEMoveCategory.Special, 0, 3, 35, 85,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.DoubleDamageUnderwater\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Whirlwind,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, -6, 4, 0, 100,\n\t\t\t\tPBEMoveEffect.Whirlwind, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.WideGuard,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Rock, PBEMoveCategory.Status, +3, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.WideGuard, 0, PBEMoveTarget.AllTeam,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch | PBEMoveFlag.BlockedFromMetronome\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.WildCharge,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Electric, PBEMoveCategory.Physical, 0, 3, 90, 100,\n\t\t\t\tPBEMoveEffect.Recoil, 4, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.AffectedByReckless | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.WillOWisp,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Fire, PBEMoveCategory.Status, 0, 3, 0, 75,\n\t\t\t\tPBEMoveEffect.Burn, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.WingAttack,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Flying, PBEMoveCategory.Physical, 0, 7, 60, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleNotSelf,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Wish,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Withdraw,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Water, PBEMoveCategory.Status, 0, 8, 0, 0,\n\t\t\t\tPBEMoveEffect.ChangeTarget_DEF, +1, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.WonderRoom,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Status, -7, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.All,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.WoodHammer,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Physical, 0, 3, 120, 100,\n\t\t\t\tPBEMoveEffect.Recoil, 3, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.AffectedByReckless | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.WorkUp,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 6, 0, 0,\n\t\t\t\tPBEMoveEffect.RaiseTarget_ATK_SPATK_By1, 0, PBEMoveTarget.Self,\n\t\t\t\tPBEMoveFlag.AffectedBySnatch\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.WorrySeed,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Grass, PBEMoveCategory.Status, 0, 2, 0, 100,\n\t\t\t\tPBEMoveEffect.WorrySeed, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Wrap,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Physical, 0, 4, 15, 90,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.WringOut,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Special, 0, 1, 120, 100,\n\t\t\t\tPBEMoveEffect.CrushGrip, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.XScissor,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Bug, PBEMoveCategory.Physical, 0, 3, 80, 100,\n\t\t\t\tPBEMoveEffect.Hit, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.Yawn,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Normal, PBEMoveCategory.Status, 0, 2, 0, 0,\n\t\t\t\tPBEMoveEffect.TODOMOVE, 0, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMagicCoat | PBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.ZapCannon,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Electric, PBEMoveCategory.Special, 0, 1, 120, 50,\n\t\t\t\tPBEMoveEffect.Hit__MaybeParalyze, 100, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect\n\t\t\t)\n\t\t},\n\t\t{\n\t\t\tPBEMove.ZenHeadbutt,\n\t\t\tnew PBEDDMoveData\n\t\t\t(\n\t\t\t\tPBEType.Psychic, PBEMoveCategory.Physical, 0, 3, 80, 90,\n\t\t\t\tPBEMoveEffect.Hit__MaybeFlinch, 20, PBEMoveTarget.SingleSurrounding,\n\t\t\t\tPBEMoveFlag.AffectedByMirrorMove | PBEMoveFlag.AffectedByProtect | PBEMoveFlag.MakesContact\n\t\t\t)\n\t\t}\n\t});\n}\n"
  },
  {
    "path": "PokemonBattleEngine.DefaultData/Data/PokemonData.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Data.Utils;\nusing System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.DefaultData.Data;\n\npublic sealed class PBEDDPokemonData : IPBEDDPokemonDataExtended\n{\n\tpublic PBESpecies Species { get; }\n\tpublic PBEForm Form { get; }\n\tpublic PBEReadOnlyStatCollection BaseStats { get; }\n\tIPBEReadOnlyStatCollection IPBEPokemonData.BaseStats => BaseStats;\n\tpublic PBEType Type1 { get; }\n\tpublic PBEType Type2 { get; }\n\tpublic PBEGenderRatio GenderRatio { get; }\n\tpublic PBEGrowthRate GrowthRate { get; }\n\tpublic ushort BaseEXPYield { get; }\n\tpublic byte CatchRate { get; }\n\tpublic byte FleeRate { get; }\n\t/// <summary>Weight in Kilograms</summary>\n\tpublic float Weight { get; }\n\tpublic ReadOnlyCollection<PBEAbility> Abilities { get; }\n\tIReadOnlyList<PBEAbility> IPBEPokemonData.Abilities => Abilities;\n\tpublic ReadOnlyCollection<(PBESpecies Species, PBEForm Form)> PreEvolutions { get; }\n\tIReadOnlyList<(PBESpecies Species, PBEForm Form)> IPBEDDPokemonDataExtended.PreEvolutions => PreEvolutions;\n\tpublic ReadOnlyCollection<(PBESpecies Species, PBEForm Form)> Evolutions { get; }\n\tIReadOnlyList<(PBESpecies Species, PBEForm Form)> IPBEDDPokemonDataExtended.Evolutions => Evolutions;\n\tpublic ReadOnlyCollection<(PBEMove Move, byte Level, PBEDDMoveObtainMethod ObtainMethod)> LevelUpMoves { get; }\n\tIReadOnlyList<(PBEMove Move, byte Level, PBEDDMoveObtainMethod ObtainMethod)> IPBEDDPokemonDataExtended.LevelUpMoves => LevelUpMoves;\n\tpublic ReadOnlyCollection<(PBEMove Move, PBEDDMoveObtainMethod ObtainMethod)> OtherMoves { get; }\n\tIReadOnlyList<(PBEMove Move, PBEDDMoveObtainMethod ObtainMethod)> IPBEDDPokemonDataExtended.OtherMoves => OtherMoves;\n\n\tprivate PBEDDPokemonData(SearchResult result)\n\t{\n\t\tBaseStats = new PBEReadOnlyStatCollection(result);\n\t\tType1 = (PBEType)result.Type1;\n\t\tType2 = (PBEType)result.Type2;\n\t\tGenderRatio = (PBEGenderRatio)result.GenderRatio;\n\t\tGrowthRate = (PBEGrowthRate)result.GrowthRate;\n\t\tBaseEXPYield = result.BaseEXPYield;\n\t\tCatchRate = result.CatchRate;\n\t\tFleeRate = result.FleeRate;\n\t\tWeight = result.Weight;\n\n\t\tconst char Split1Char = '+';\n\t\tconst char Split2Char = '|';\n\n\t\tstring[] split1 = result.PreEvolutions.Split(Split1Char, StringSplitOptions.RemoveEmptyEntries);\n\t\tvar preEvolutions = new (PBESpecies, PBEForm)[split1.Length];\n\t\tfor (int i = 0; i < preEvolutions.Length; i++)\n\t\t{\n\t\t\tstring[] split2 = split1[i].Split(Split2Char);\n\t\t\tpreEvolutions[i] = (Enum.Parse<PBESpecies>(split2[0]), Enum.Parse<PBEForm>(split2[1]));\n\t\t}\n\t\tPreEvolutions = new ReadOnlyCollection<(PBESpecies, PBEForm)>(preEvolutions);\n\n\t\tsplit1 = result.Evolutions.Split(Split1Char, StringSplitOptions.RemoveEmptyEntries);\n\t\tvar evolutions = new (PBESpecies, PBEForm)[split1.Length];\n\t\tfor (int i = 0; i < evolutions.Length; i++)\n\t\t{\n\t\t\tstring[] split2 = split1[i].Split(Split2Char);\n\t\t\tevolutions[i] = (Enum.Parse<PBESpecies>(split2[0]), Enum.Parse<PBEForm>(split2[1]));\n\t\t}\n\t\tEvolutions = new ReadOnlyCollection<(PBESpecies, PBEForm)>(evolutions);\n\n\t\tsplit1 = result.Abilities.Split(Split1Char, StringSplitOptions.RemoveEmptyEntries);\n\t\tvar abilities = new PBEAbility[split1.Length];\n\t\tfor (int i = 0; i < abilities.Length; i++)\n\t\t{\n\t\t\tabilities[i] = Enum.Parse<PBEAbility>(split1[i]);\n\t\t}\n\t\tAbilities = new ReadOnlyCollection<PBEAbility>(abilities);\n\n\t\tsplit1 = result.LevelUpMoves.Split(Split1Char, StringSplitOptions.RemoveEmptyEntries);\n\t\tvar levelUpMoves = new (PBEMove, byte, PBEDDMoveObtainMethod)[split1.Length];\n\t\tfor (int i = 0; i < levelUpMoves.Length; i++)\n\t\t{\n\t\t\tstring[] split2 = split1[i].Split(Split2Char);\n\t\t\tlevelUpMoves[i] = (Enum.Parse<PBEMove>(split2[0]), byte.Parse(split2[1]), Enum.Parse<PBEDDMoveObtainMethod>(split2[2]));\n\t\t}\n\t\tLevelUpMoves = new ReadOnlyCollection<(PBEMove, byte, PBEDDMoveObtainMethod)>(levelUpMoves);\n\n\t\tsplit1 = result.OtherMoves.Split(Split1Char, StringSplitOptions.RemoveEmptyEntries);\n\t\tvar otherMoves = new (PBEMove, PBEDDMoveObtainMethod)[split1.Length];\n\t\tfor (int i = 0; i < otherMoves.Length; i++)\n\t\t{\n\t\t\tstring[] split2 = split1[i].Split(Split2Char);\n\t\t\totherMoves[i] = (Enum.Parse<PBEMove>(split2[0]), Enum.Parse<PBEDDMoveObtainMethod>(split2[1]));\n\t\t}\n\t\tOtherMoves = new ReadOnlyCollection<(PBEMove, PBEDDMoveObtainMethod)>(otherMoves);\n\t}\n\n\t#region Database Querying\n\n#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.\n\tprivate class SearchResult : IPBEStatCollection\n\t{\n\t\tpublic string Species { get; set; }\n\t\tpublic string Form { get; set; }\n\t\tpublic byte HP { get; set; }\n\t\tpublic byte Attack { get; set; }\n\t\tpublic byte Defense { get; set; }\n\t\tpublic byte SpAttack { get; set; }\n\t\tpublic byte SpDefense { get; set; }\n\t\tpublic byte Speed { get; set; }\n\t\tpublic byte Type1 { get; set; }\n\t\tpublic byte Type2 { get; set; }\n\t\tpublic byte GenderRatio { get; set; }\n\t\tpublic byte GrowthRate { get; set; }\n\t\tpublic ushort BaseEXPYield { get; set; }\n\t\tpublic byte CatchRate { get; set; }\n\t\tpublic byte FleeRate { get; set; }\n\t\tpublic float Weight { get; set; }\n\t\tpublic string PreEvolutions { get; set; }\n\t\tpublic string Evolutions { get; set; }\n\t\tpublic string Abilities { get; set; }\n\t\tpublic string LevelUpMoves { get; set; }\n\t\tpublic string OtherMoves { get; set; }\n\t}\n#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.\n\n#pragma warning disable IDE0060 // Remove unused parameter\n\tpublic static PBEDDPokemonData GetData(PBESpecies species, PBEForm form, bool cache = true)\n#pragma warning restore IDE0060 // Remove unused parameter\n\t{\n\t\tPBEDataUtils.ValidateSpecies(species, form, false);\n\t\tList<SearchResult> results = PBEDefaultDataProvider.Instance.QueryDatabase<SearchResult>($\"SELECT * FROM PokemonData WHERE Species='{species}' AND Form='{PBEDataUtils.GetNameOfForm(species, form) ?? \"0\"}'\");\n\t\tif (results.Count == 1)\n\t\t{\n\t\t\treturn new PBEDDPokemonData(results[0]);\n\t\t}\n\t\tthrow new InvalidDataException();\n\t}\n\n\t#endregion\n}\n"
  },
  {
    "path": "PokemonBattleEngine.DefaultData/DefaultDataProvider.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Data.Utils;\nusing Kermalis.PokemonBattleEngine.DefaultData.Data;\nusing Kermalis.PokemonBattleEngine.Utils;\nusing Microsoft.Data.Sqlite;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Globalization;\nusing System.IO;\nusing System.Reflection;\n\nnamespace Kermalis.PokemonBattleEngine.DefaultData;\n\npublic class PBEDefaultDataProvider : PBEDataProvider\n{\n\tpublic static new PBEDefaultDataProvider Instance => (PBEDefaultDataProvider)PBEDataProvider.Instance;\n\n\tprivate readonly object _databaseConnectLockObj = new();\n\tprivate readonly SqliteConnection _databaseConnection;\n\n\tprotected PBEDefaultDataProvider(string databasePath, PBELanguage language, PBERandom rand)\n\t\t: base(language, rand)\n\t{\n\t\tSQLitePCL.Batteries_V2.Init();\n\t\t_databaseConnection = new SqliteConnection($\"Filename={Path.Combine(databasePath, \"PokemonBattleEngine.db\")};Mode=ReadOnly;\");\n\t\t_databaseConnection.Open();\n\t\t_databaseConnection.CreateFunction(\"StrCmp\", (Func<object, object, bool>)StrCmp);\n\t}\n\tpublic static void InitEngine(string databasePath, int? randomSeed = null)\n\t{\n\t\tvar cultureInfo = CultureInfo.ReadOnly(CultureInfo.CurrentUICulture);\n\t\tif (!cultureInfo.ToPBELanguage(out PBELanguage? lang))\n\t\t{\n\t\t\tlang = PBELanguage.English;\n\t\t}\n\t\t_ = new PBEDefaultDataProvider(databasePath, lang.Value, new PBERandom(randomSeed));\n\t}\n\tpublic static void InitEngine(string databasePath, PBELanguage language, int? randomSeed = null)\n\t{\n\t\tif (language >= PBELanguage.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(language));\n\t\t}\n\t\t_ = new PBEDefaultDataProvider(databasePath, language, new PBERandom(randomSeed));\n\t}\n\n\tprivate static bool StrCmp(object arg0, object arg1)\n\t{\n\t\tif (Convert.IsDBNull(arg0) || Convert.IsDBNull(arg1))\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\treturn string.Equals(Convert.ToString(arg0), Convert.ToString(arg1), StringComparison.InvariantCultureIgnoreCase);\n\t}\n\n\t// TODO: Keep this internal version and make a public version that only allows operations that retrieve data?\n\tinternal List<T> QueryDatabase<T>(string commandText) where T : new()\n\t{\n\t\tvar list = new List<T>();\n\t\tType type = typeof(T);\n\t\tlock (_databaseConnectLockObj)\n\t\t{\n\t\t\tusing (SqliteCommand cmd = _databaseConnection.CreateCommand())\n\t\t\t{\n\t\t\t\tcmd.CommandText = commandText;\n\t\t\t\tusing (SqliteDataReader r = cmd.ExecuteReader())\n\t\t\t\t{\n\t\t\t\t\twhile (r.Read())\n\t\t\t\t\t{\n\t\t\t\t\t\tT obj = Activator.CreateInstance<T>();\n\t\t\t\t\t\tfor (int i = 0; i < r.FieldCount; i++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tPropertyInfo? property = type.GetProperty(r.GetName(i));\n\t\t\t\t\t\t\tproperty?.SetValue(obj, Convert.ChangeType(r.GetValue(i), property.PropertyType));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tlist.Add(obj);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn list;\n\t}\n\n\t#region Data\n\n\tpublic override bool IsBerry(PBEItem item)\n\t{\n\t\treturn PBEDDBerryData.Data.ContainsKey(item);\n\t}\n\tpublic override IPBEBerryData GetBerryData(PBEItem item, bool cache = true)\n\t{\n\t\treturn PBEDDBerryData.Data[item];\n\t}\n\tpublic override bool TryGetBerryData(PBEItem item, [NotNullWhen(true)] out IPBEBerryData? bData, bool cache = true)\n\t{\n\t\tif (IsBerry(item))\n\t\t{\n\t\t\tbData = GetBerryData(item, cache: cache);\n\t\t\treturn true;\n\t\t}\n\t\tbData = null;\n\t\treturn false;\n\t}\n\tpublic override IPBEItemData GetItemData(PBEItem item, bool cache = true)\n\t{\n\t\treturn PBEDDItemData.Data[item];\n\t}\n\tpublic override IPBEMoveData GetMoveData(PBEMove move, bool cache = true)\n\t{\n\t\treturn PBEDDMoveData.Data[move];\n\t}\n\tpublic override bool HasEvolutions(PBESpecies species, PBEForm form, bool cache = true)\n\t{\n\t\treturn PBEDDPokemonData.GetData(species, form, cache: cache).HasEvolutions();\n\t}\n\tpublic override IPBEPokemonData GetPokemonData(PBESpecies species, PBEForm form, bool cache = true)\n\t{\n\t\treturn PBEDDPokemonData.GetData(species, form, cache: cache);\n\t}\n\tpublic virtual IPBEDDPokemonDataExtended GetPokemonDataExtended(PBESpecies species, PBEForm form, bool cache = true)\n\t{\n\t\treturn PBEDDPokemonData.GetData(species, form, cache: cache);\n\t}\n\n\tpublic override int GetSpeciesCaught()\n\t{\n\t\treturn 300;\n\t}\n\n\tpublic override IReadOnlyCollection<PBEMove> GetLegalMoves(PBESpecies species, PBEForm form, byte level)\n\t{\n\t\treturn PBEDDLegalityChecker.GetLegalMoves(species, form, level);\n\t}\n\n\tpublic virtual IPBEDDPokemonDataExtended GetPokemonDataExtended(IPBESpeciesForm pkmn, bool cache = true)\n\t{\n\t\treturn GetPokemonDataExtended(pkmn.Species, pkmn.Form, cache: cache);\n\t}\n\n\t#endregion\n\n\t#region EXP\n\n\tpublic override uint GetEXPRequired(PBEGrowthRate type, byte level)\n\t{\n\t\treturn PBEDDEXPTables.GetEXPRequired(type, level);\n\t}\n\tpublic override byte GetEXPLevel(PBEGrowthRate type, uint exp)\n\t{\n\t\treturn PBEDDEXPTables.GetEXPLevel(type, exp);\n\t}\n\tpublic override float GetEXPModifier(PBEBattle battle)\n\t{\n\t\treturn 1;\n\t}\n\tpublic override float GetEXPTradeModifier(PBEBattlePokemon pkmn)\n\t{\n\t\treturn 1;\n\t}\n\n\t#endregion\n\n\t#region Catching\n\n\tpublic override bool IsDarkGrass(PBEBattle battle)\n\t{\n\t\treturn false;\n\t}\n\tpublic override bool IsDuskBallSetting(PBEBattle battle)\n\t{\n\t\treturn battle.BattleTerrain == PBEBattleTerrain.Cave;\n\t}\n\tpublic override bool IsFishing(PBEBattle battle)\n\t{\n\t\treturn false;\n\t}\n\tpublic override bool IsGuaranteedCapture(PBEBattle battle, IPBESpeciesForm pkmn)\n\t{\n\t\treturn IsGuaranteedCapture(battle, pkmn.Species, pkmn.Form);\n\t}\n\tpublic override bool IsGuaranteedCapture(PBEBattle battle, PBESpecies species, PBEForm form)\n\t{\n\t\treturn false;\n\t}\n\tpublic override bool IsMoonBallFamily(IPBESpeciesForm pkmn)\n\t{\n\t\treturn IsMoonBallFamily(pkmn.Species, pkmn.Form);\n\t}\n\tpublic override bool IsMoonBallFamily(PBESpecies species, PBEForm form)\n\t{\n\t\treturn PBEDataUtils.MoonStoneSpecies.Contains(species);\n\t}\n\tpublic override bool IsRepeatBallSpecies(PBESpecies species)\n\t{\n\t\treturn false;\n\t}\n\tpublic override bool IsSurfing(PBEBattle battle)\n\t{\n\t\treturn battle.BattleTerrain == PBEBattleTerrain.Water;\n\t}\n\tpublic override bool IsUnderwater(PBEBattle battle)\n\t{\n\t\treturn false;\n\t}\n\tpublic override float GetCatchRateModifier(PBEBattle battle)\n\t{\n\t\treturn 1;\n\t}\n\n\t#endregion\n\n\t#region LocalizedString\n\n\tpublic override bool GetAbilityByName(string abilityName, [NotNullWhen(true)] out PBEAbility? ability)\n\t{\n\t\treturn PBEDDLocalizedString.GetAbilityByName(abilityName, out ability);\n\t}\n\tpublic virtual IPBEReadOnlyLocalizedString GetAbilityDescription(PBEAbility ability)\n\t{\n\t\treturn PBEDDLocalizedString.GetAbilityDescription(ability);\n\t}\n\tpublic override IPBEReadOnlyLocalizedString GetAbilityName(PBEAbility ability)\n\t{\n\t\treturn PBEDDLocalizedString.GetAbilityName(ability);\n\t}\n\tpublic override bool GetFormByName(PBESpecies species, string formName, [NotNullWhen(true)] out PBEForm? form)\n\t{\n\t\treturn PBEDDLocalizedString.GetFormByName(species, formName, out form);\n\t}\n\tpublic override IPBEReadOnlyLocalizedString GetFormName(PBESpecies species, PBEForm form)\n\t{\n\t\treturn PBEDDLocalizedString.GetFormName(species, form);\n\t}\n\tpublic override bool GetGenderByName(string genderName, [NotNullWhen(true)] out PBEGender? gender)\n\t{\n\t\treturn PBEDDLocalizedString.GetGenderByName(genderName, out gender);\n\t}\n\tpublic override IPBEReadOnlyLocalizedString GetGenderName(PBEGender gender)\n\t{\n\t\treturn PBEDDLocalizedString.GetGenderName(gender);\n\t}\n\tpublic override bool GetItemByName(string itemName, [NotNullWhen(true)] out PBEItem? item)\n\t{\n\t\treturn PBEDDLocalizedString.GetItemByName(itemName, out item);\n\t}\n\tpublic virtual IPBEReadOnlyLocalizedString GetItemDescription(PBEItem item)\n\t{\n\t\treturn PBEDDLocalizedString.GetItemDescription(item);\n\t}\n\tpublic override IPBEReadOnlyLocalizedString GetItemName(PBEItem item)\n\t{\n\t\treturn PBEDDLocalizedString.GetItemName(item);\n\t}\n\tpublic override bool GetMoveByName(string moveName, [NotNullWhen(true)] out PBEMove? move)\n\t{\n\t\treturn PBEDDLocalizedString.GetMoveByName(moveName, out move);\n\t}\n\tpublic virtual IPBEReadOnlyLocalizedString GetMoveDescription(PBEMove move)\n\t{\n\t\treturn PBEDDLocalizedString.GetMoveDescription(move);\n\t}\n\tpublic override IPBEReadOnlyLocalizedString GetMoveName(PBEMove move)\n\t{\n\t\treturn PBEDDLocalizedString.GetMoveName(move);\n\t}\n\tpublic override bool GetNatureByName(string natureName, [NotNullWhen(true)] out PBENature? nature)\n\t{\n\t\treturn PBEDDLocalizedString.GetNatureByName(natureName, out nature);\n\t}\n\tpublic override IPBEReadOnlyLocalizedString GetNatureName(PBENature nature)\n\t{\n\t\treturn PBEDDLocalizedString.GetNatureName(nature);\n\t}\n\tpublic override bool GetSpeciesByName(string speciesName, [NotNullWhen(true)] out PBESpecies? species)\n\t{\n\t\treturn PBEDDLocalizedString.GetSpeciesByName(speciesName, out species);\n\t}\n\tpublic virtual IPBEReadOnlyLocalizedString GetSpeciesCategory(PBESpecies species)\n\t{\n\t\treturn PBEDDLocalizedString.GetSpeciesCategory(species);\n\t}\n\tpublic virtual IPBEReadOnlyLocalizedString GetSpeciesEntry(PBESpecies species)\n\t{\n\t\treturn PBEDDLocalizedString.GetSpeciesEntry(species);\n\t}\n\tpublic override IPBEReadOnlyLocalizedString GetSpeciesName(PBESpecies species)\n\t{\n\t\treturn PBEDDLocalizedString.GetSpeciesName(species);\n\t}\n\tpublic override bool GetStatByName(string statName, [NotNullWhen(true)] out PBEStat? stat)\n\t{\n\t\treturn PBEDDLocalizedString.GetStatByName(statName, out stat);\n\t}\n\tpublic override IPBEReadOnlyLocalizedString GetStatName(PBEStat stat)\n\t{\n\t\treturn PBEDDLocalizedString.GetStatName(stat);\n\t}\n\tpublic override bool GetTypeByName(string typeName, [NotNullWhen(true)] out PBEType? type)\n\t{\n\t\treturn PBEDDLocalizedString.GetTypeByName(typeName, out type);\n\t}\n\tpublic override IPBEReadOnlyLocalizedString GetTypeName(PBEType type)\n\t{\n\t\treturn PBEDDLocalizedString.GetTypeName(type);\n\t}\n\n\t#endregion\n}\n"
  },
  {
    "path": "PokemonBattleEngine.DefaultData/Enums.cs",
    "content": "﻿using System;\n\nnamespace Kermalis.PokemonBattleEngine.DefaultData;\n\n[Flags]\npublic enum PBEDDMoveObtainMethod : ulong\n{\n\t/// <summary>There is no way to learn this move.</summary>\n\tNone,\n\t/// <summary>The move can be learned by levelling up a Pokémon in Pokémon Ruby Version and Pokémon Sapphire Version.</summary>\n\tLevelUp_RSColoXD = 1uL << 0,\n\t/// <summary>The move can be learned by levelling up a Pokémon in Pokémon Fire Red Version.</summary>\n\tLevelUp_FR = 1uL << 1,\n\t/// <summary>The move can be learned by levelling up a Pokémon in Pokémon Leaf Green Version.</summary>\n\tLevelUp_LG = 1uL << 2,\n\t/// <summary>The move can be learned by levelling up a Pokémon in Pokémon Emerald Version.</summary>\n\tLevelUp_E = 1uL << 3,\n\t/// <summary>The move can be learned by levelling up a Pokémon in Pokémon Diamond Version and Pokémon Pearl Version.</summary>\n\tLevelUp_DP = 1uL << 4,\n\t/// <summary>The move can be learned by levelling up a Pokémon in Pokémon Platinum Version.</summary>\n\tLevelUp_Pt = 1uL << 5,\n\t/// <summary>The move can be learned by levelling up a Pokémon in Pokémon HeartGold Version and Pokémon SoulSilver Version.</summary>\n\tLevelUp_HGSS = 1uL << 6,\n\t/// <summary>The move can be learned by levelling up a Pokémon in Pokémon Black Version and Pokémon White Version.</summary>\n\tLevelUp_BW = 1uL << 7,\n\t/// <summary>The move can be learned by levelling up a Pokémon in Pokémon Black Version 2 and Pokémon White Version 2.</summary>\n\tLevelUp_B2W2 = 1uL << 8,\n\t/// <summary>The move can be learned by using a technical machine on a Pokémon in Pokémon Ruby Version, Pokémon Sapphire Version, Pokémon Fire Red Version, Pokémon Leaf Green Version, Pokémon Emerald Version, Pokémon Colosseum, and Pokémon XD: Gale of Darkness.</summary>\n\tTM_RSFRLGEColoXD = 1uL << 9,\n\t/// <summary>The move can be learned by using a technical machine on a Pokémon in Pokémon Diamond Version, Pokémon Pearl Version, and Pokémon Platinum Version.</summary>\n\tTM_DPPt = 1uL << 10,\n\t/// <summary>The move can be learned by using a technical machine on a Pokémon in Pokémon HeartGold Version and Pokémon SoulSilver Version.</summary>\n\tTM_HGSS = 1uL << 11,\n\t/// <summary>The move can be learned by using a technical machine on a Pokémon in Pokémon Black Version and Pokémon White Version.</summary>\n\tTM_BW = 1uL << 12,\n\t/// <summary>The move can be learned by using a technical machine on a Pokémon in Pokémon Black Version 2 and Pokémon White Version 2.</summary>\n\tTM_B2W2 = 1uL << 13,\n\t/// <summary>The move can be learned by using a hidden machine on a Pokémon in Pokémon Ruby Version, Pokémon Sapphire Version, Pokémon Fire Red Version, Pokémon Leaf Green Version, Pokémon Emerald Version, Pokémon Colosseum, and Pokémon XD: Gale of Darkness.</summary>\n\tHM_RSFRLGEColoXD = 1uL << 14,\n\t/// <summary>The move can be learned by using a hidden machine on a Pokémon in Pokémon Diamond Version, Pokémon Pearl Version, and Pokémon Platinum Version.</summary>\n\tHM_DPPt = 1uL << 15,\n\t/// <summary>The move can be learned by using a hidden machine on a Pokémon in Pokémon HeartGold Version and Pokémon SoulSilver Version.</summary>\n\tHM_HGSS = 1uL << 16,\n\t/// <summary>The move can be learned by using a hidden machine on a Pokémon in Pokémon Black Version, Pokémon White Version, Pokémon Black Version 2, and Pokémon White Version 2.</summary>\n\tHM_BWB2W2 = 1uL << 17,\n\t/// <summary>The move can be taught to a Pokémon by a move tutor in Pokémon Fire Red Version and Pokémon Leaf Green Version.</summary>\n\tMoveTutor_FRLG = 1uL << 18,\n\t/// <summary>The move can be taught to a Pokémon by a move tutor in Pokémon Emerald Version.</summary>\n\tMoveTutor_E = 1uL << 19,\n\t/// <summary>The move can be taught to a Pokémon by a move tutor in Pokémon XD: Gale of Darkness.</summary>\n\tMoveTutor_XD = 1uL << 20,\n\t/// <summary>The move can be taught to a Pokémon by a move tutor in Pokémon Diamond Version and Pokémon Pearl Version.</summary>\n\tMoveTutor_DP = 1uL << 21,\n\t/// <summary>The move can be taught to a Pokémon by a move tutor in Pokémon Platinum Version.</summary>\n\tMoveTutor_Pt = 1uL << 22,\n\t/// <summary>The move can be taught to a Pokémon by a move tutor in Pokémon HeartGold Version and Pokémon SoulSilver Version.</summary>\n\tMoveTutor_HGSS = 1uL << 23,\n\t/// <summary>The move can be taught to a Pokémon by a move tutor in Pokémon Black Version and Pokémon White Version.</summary>\n\tMoveTutor_BW = 1uL << 24,\n\t/// <summary>The move can be taught to a Pokémon by a move tutor in Pokémon Black Version 2 and Pokémon White Version 2.</summary>\n\tMoveTutor_B2W2 = 1uL << 25,\n\t/// <summary>The move can be learned by hatching a Pokémon egg in Pokémon Ruby Version, Pokémon Sapphire Version, Pokémon Fire Red Version, Pokémon Leaf Green Version, and Pokémon Emerald Version.</summary>\n\tEggMove_RSFRLGE = 1uL << 26,\n\t/// <summary>The move can be learned by hatching a Pokémon egg in Pokémon Diamond Version, Pokémon Pearl Version, and Pokémon Platinum Version.</summary>\n\tEggMove_DPPt = 1uL << 27,\n\t/// <summary>The move can be learned by hatching a Pokémon egg in Pokémon HeartGold Version and Pokémon SoulSilver Version.</summary>\n\tEggMove_HGSS = 1uL << 28,\n\t/// <summary>The move can be learned by hatching a Pokémon egg in Pokémon Black Version, Pokémon White Version, Pokémon Black Version 2, and Pokémon White Version 2.</summary>\n\tEggMove_BWB2W2 = 1uL << 29,\n\t/// <summary>The move is known by a Pokémon when found in the Dream World with Pokémon Black Version and Pokémon White Version.</summary>\n\tDreamWorld_BW = 1uL << 30,\n\t/// <summary>The move is known by a Pokémon when found in the Dream World with Pokémon Black Version 2 and Pokémon White Version 2.</summary>\n\tDreamWorld_B2W2 = 1uL << 31,\n\t/// <summary>The move can be learned by hatching a Pokémon egg under special conditions.</summary>\n\tEggMove_Special = 1uL << 32,\n\t/// <summary>The move is learned by a Pokémon when changing forms. The move cannot be used by other forms if this is the only flag or if the species cannot change forms.</summary>\n\tForm = 1uL << 33\n}\n"
  },
  {
    "path": "PokemonBattleEngine.DefaultData/IPokemonDataExtended.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data;\nusing System.Collections.Generic;\n\nnamespace Kermalis.PokemonBattleEngine.DefaultData;\n\npublic interface IPBEDDPokemonDataExtended : IPBEPokemonData\n{\n\tIReadOnlyList<(PBESpecies Species, PBEForm Form)> PreEvolutions { get; }\n\tIReadOnlyList<(PBESpecies Species, PBEForm Form)> Evolutions { get; }\n\tIReadOnlyList<(PBEMove Move, byte Level, PBEDDMoveObtainMethod ObtainMethod)> LevelUpMoves { get; }\n\tIReadOnlyList<(PBEMove Move, PBEDDMoveObtainMethod ObtainMethod)> OtherMoves { get; }\n}\n\npublic static class PBEDDPokemonDataExtensions\n{\n\tpublic static bool HasEvolutions(this IPBEDDPokemonDataExtended pData)\n\t{\n\t\treturn pData.Evolutions.Count > 0;\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine.DefaultData/LocalizedString.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Data.Utils;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngine.DefaultData;\n\npublic static class PBEDDLocalizedString\n{\n#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.\n\tprivate sealed class SearchResult : IPBELocalizedString\n\t{\n\t\tpublic string Id { get; set; }\n\t\tpublic string English { get; set; }\n\t\tpublic string French { get; set; }\n\t\tpublic string German { get; set; }\n\t\tpublic string Italian { get; set; }\n\t\tpublic string Japanese_Kana { get; set; }\n\t\tpublic string Japanese_Kanji { get; set; }\n\t\tpublic string Korean { get; set; }\n\t\tpublic string Spanish { get; set; }\n\t}\n\tprivate sealed class FormNameSearchResult : IPBELocalizedString\n\t{\n\t\tpublic string Species { get; set; }\n\t\tpublic string Form { get; set; }\n\t\tpublic string English { get; set; }\n\t\tpublic string French { get; set; }\n\t\tpublic string German { get; set; }\n\t\tpublic string Italian { get; set; }\n\t\tpublic string Japanese_Kana { get; set; }\n\t\tpublic string Japanese_Kanji { get; set; }\n\t\tpublic string Korean { get; set; }\n\t\tpublic string Spanish { get; set; }\n\t}\n#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.\n\n\tprivate const string QueryText = \"SELECT * FROM {0} WHERE StrCmp(English,'{1}') OR StrCmp(French,'{1}') OR StrCmp(German,'{1}') OR StrCmp(Italian,'{1}') OR StrCmp(Japanese_Kana,'{1}') OR StrCmp(Japanese_Kanji,'{1}') OR StrCmp(Korean,'{1}') OR StrCmp(Spanish,'{1}')\";\n\tprivate const string QueryId = \"SELECT * FROM {0} WHERE Id='{1}'\";\n\tprivate const string QuerySpeciesAndText = \"SELECT * FROM {0} WHERE (StrCmp(English,'{1}') OR StrCmp(French,'{1}') OR StrCmp(German,'{1}') OR StrCmp(Italian,'{1}') OR StrCmp(Japanese_Kana,'{1}') OR StrCmp(Japanese_Kanji,'{1}') OR StrCmp(Korean,'{1}') OR StrCmp(Spanish,'{1}')) AND (Species='{2}')\";\n\tprivate const string QuerySpecies = \"SELECT * FROM {0} WHERE Species='{1}' AND Form='{2}'\";\n\tprivate static bool GetEnumValue<TEnum>(string value, [NotNullWhen(true)] out TEnum? result) where TEnum : struct, Enum\n\t{\n\t\tforeach (TEnum v in Enum.GetValues<TEnum>())\n\t\t{\n\t\t\tif (v.ToString().Equals(value, StringComparison.InvariantCultureIgnoreCase))\n\t\t\t{\n\t\t\t\tresult = v;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tresult = null;\n\t\treturn false;\n\t}\n\n\tpublic static bool GetAbilityByName(string abilityName, [NotNullWhen(true)] out PBEAbility? ability)\n\t{\n\t\tList<SearchResult> results = PBEDefaultDataProvider.Instance.QueryDatabase<SearchResult>(string.Format(QueryText, \"AbilityNames\", abilityName));\n\t\tif (results.Count == 1)\n\t\t{\n\t\t\tability = Enum.Parse<PBEAbility>(results[0].Id);\n\t\t}\n\t\telse if (!GetEnumValue(abilityName, out ability) || ability == PBEAbility.MAX)\n\t\t{\n\t\t\tability = null;\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\tpublic static PBEReadOnlyLocalizedString GetAbilityDescription(PBEAbility ability)\n\t{\n\t\tif (ability >= PBEAbility.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(ability));\n\t\t}\n\t\tList<SearchResult> results = PBEDefaultDataProvider.Instance.QueryDatabase<SearchResult>(string.Format(QueryId, \"AbilityDescriptions\", ability));\n\t\tif (results.Count == 1)\n\t\t{\n\t\t\treturn new PBEReadOnlyLocalizedString(results[0]);\n\t\t}\n\t\tthrow new InvalidDataException();\n\t}\n\tpublic static PBEReadOnlyLocalizedString GetAbilityName(PBEAbility ability)\n\t{\n\t\tif (ability >= PBEAbility.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(ability));\n\t\t}\n\t\tList<SearchResult> results = PBEDefaultDataProvider.Instance.QueryDatabase<SearchResult>(string.Format(QueryId, \"AbilityNames\", ability));\n\t\tif (results.Count == 1)\n\t\t{\n\t\t\treturn new PBEReadOnlyLocalizedString(results[0]);\n\t\t}\n\t\tthrow new InvalidDataException();\n\t}\n\tpublic static bool GetFormByName(PBESpecies species, string formName, [NotNullWhen(true)] out PBEForm? form)\n\t{\n\t\tList<FormNameSearchResult> results = PBEDefaultDataProvider.Instance.QueryDatabase<FormNameSearchResult>(string.Format(QuerySpeciesAndText, \"FormNames\", formName, species));\n\t\tif (results.Count == 1)\n\t\t{\n\t\t\tform = Enum.Parse<PBEForm>(results[0].Form);\n\t\t}\n\t\telse if (!GetEnumValue(formName, out form))\n\t\t{\n\t\t\tform = null;\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\tpublic static PBEReadOnlyLocalizedString GetFormName(PBESpecies species, PBEForm form)\n\t{\n\t\tPBEDataUtils.ValidateSpecies(species, form, false);\n\t\tList<FormNameSearchResult> results = PBEDefaultDataProvider.Instance.QueryDatabase<FormNameSearchResult>(string.Format(QuerySpecies, \"FormNames\", species, PBEDataUtils.GetNameOfForm(species, form) ?? \"0\"));\n\t\tif (results.Count == 1)\n\t\t{\n\t\t\treturn new PBEReadOnlyLocalizedString(results[0]);\n\t\t}\n\t\tthrow new InvalidDataException();\n\t}\n\tpublic static bool GetGenderByName(string genderName, [NotNullWhen(true)] out PBEGender? gender)\n\t{\n\t\tList<SearchResult> results = PBEDefaultDataProvider.Instance.QueryDatabase<SearchResult>(string.Format(QueryText, \"GenderNames\", genderName));\n\t\tif (results.Count == 1)\n\t\t{\n\t\t\tgender = Enum.Parse<PBEGender>(results[0].Id);\n\t\t}\n\t\telse if (!GetEnumValue(genderName, out gender) || gender == PBEGender.MAX)\n\t\t{\n\t\t\tgender = null;\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\tpublic static PBEReadOnlyLocalizedString GetGenderName(PBEGender gender)\n\t{\n\t\tif (gender >= PBEGender.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(gender));\n\t\t}\n\t\tList<SearchResult> results = PBEDefaultDataProvider.Instance.QueryDatabase<SearchResult>(string.Format(QueryId, \"GenderNames\", gender));\n\t\tif (results.Count == 1)\n\t\t{\n\t\t\treturn new PBEReadOnlyLocalizedString(results[0]);\n\t\t}\n\t\tthrow new InvalidDataException();\n\t}\n\tpublic static bool GetItemByName(string itemName, [NotNullWhen(true)] out PBEItem? item)\n\t{\n\t\tList<SearchResult> results = PBEDefaultDataProvider.Instance.QueryDatabase<SearchResult>(string.Format(QueryText, \"ItemNames\", itemName));\n\t\tif (results.Count == 1)\n\t\t{\n\t\t\titem = Enum.Parse<PBEItem>(results[0].Id);\n\t\t}\n\t\telse if (!GetEnumValue(itemName, out item))\n\t\t{\n\t\t\titem = null;\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\tpublic static PBEReadOnlyLocalizedString GetItemDescription(PBEItem item)\n\t{\n\t\tif (!Enum.IsDefined(item))\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(item));\n\t\t}\n\t\tList<SearchResult> results = PBEDefaultDataProvider.Instance.QueryDatabase<SearchResult>(string.Format(QueryId, \"ItemDescriptions\", item));\n\t\tif (results.Count == 1)\n\t\t{\n\t\t\treturn new PBEReadOnlyLocalizedString(results[0]);\n\t\t}\n\t\tthrow new InvalidDataException();\n\t}\n\tpublic static PBEReadOnlyLocalizedString GetItemName(PBEItem item)\n\t{\n\t\tif (!Enum.IsDefined(item))\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(item));\n\t\t}\n\t\tList<SearchResult> results = PBEDefaultDataProvider.Instance.QueryDatabase<SearchResult>(string.Format(QueryId, \"ItemNames\", item));\n\t\tif (results.Count == 1)\n\t\t{\n\t\t\treturn new PBEReadOnlyLocalizedString(results[0]);\n\t\t}\n\t\tthrow new InvalidDataException();\n\t}\n\tpublic static bool GetMoveByName(string moveName, [NotNullWhen(true)] out PBEMove? move)\n\t{\n\t\tList<SearchResult> results = PBEDefaultDataProvider.Instance.QueryDatabase<SearchResult>(string.Format(QueryText, \"MoveNames\", moveName));\n\t\tif (results.Count == 1)\n\t\t{\n\t\t\tmove = Enum.Parse<PBEMove>(results[0].Id);\n\t\t}\n\t\telse if (!GetEnumValue(moveName, out move) || move == PBEMove.MAX)\n\t\t{\n\t\t\tmove = null;\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\tpublic static PBEReadOnlyLocalizedString GetMoveDescription(PBEMove move)\n\t{\n\t\tif (move >= PBEMove.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(move));\n\t\t}\n\t\tList<SearchResult> results = PBEDefaultDataProvider.Instance.QueryDatabase<SearchResult>(string.Format(QueryId, \"MoveDescriptions\", move));\n\t\tif (results.Count == 1)\n\t\t{\n\t\t\treturn new PBEReadOnlyLocalizedString(results[0]);\n\t\t}\n\t\tthrow new InvalidDataException();\n\t}\n\tpublic static PBEReadOnlyLocalizedString GetMoveName(PBEMove move)\n\t{\n\t\tif (move >= PBEMove.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(move));\n\t\t}\n\t\tList<SearchResult> results = PBEDefaultDataProvider.Instance.QueryDatabase<SearchResult>(string.Format(QueryId, \"MoveNames\", move));\n\t\tif (results.Count == 1)\n\t\t{\n\t\t\treturn new PBEReadOnlyLocalizedString(results[0]);\n\t\t}\n\t\tthrow new InvalidDataException();\n\t}\n\tpublic static bool GetNatureByName(string natureName, [NotNullWhen(true)] out PBENature? nature)\n\t{\n\t\tList<SearchResult> results = PBEDefaultDataProvider.Instance.QueryDatabase<SearchResult>(string.Format(QueryText, \"NatureNames\", natureName));\n\t\tif (results.Count == 1)\n\t\t{\n\t\t\tnature = Enum.Parse<PBENature>(results[0].Id);\n\t\t}\n\t\telse if (!GetEnumValue(natureName, out nature) || nature == PBENature.MAX)\n\t\t{\n\t\t\tnature = null;\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\tpublic static PBEReadOnlyLocalizedString GetNatureName(PBENature nature)\n\t{\n\t\tif (nature >= PBENature.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(nature));\n\t\t}\n\t\tList<SearchResult> results = PBEDefaultDataProvider.Instance.QueryDatabase<SearchResult>(string.Format(QueryId, \"NatureNames\", nature));\n\t\tif (results.Count == 1)\n\t\t{\n\t\t\treturn new PBEReadOnlyLocalizedString(results[0]);\n\t\t}\n\t\tthrow new InvalidDataException();\n\t}\n\tpublic static bool GetSpeciesByName(string speciesName, [NotNullWhen(true)] out PBESpecies? species)\n\t{\n\t\tList<SearchResult> results = PBEDefaultDataProvider.Instance.QueryDatabase<SearchResult>(string.Format(QueryText, \"SpeciesNames\", speciesName));\n\t\tif (results.Count == 1)\n\t\t{\n\t\t\tspecies = Enum.Parse<PBESpecies>(results[0].Id);\n\t\t}\n\t\telse if (!GetEnumValue(speciesName, out species))\n\t\t{\n\t\t\tspecies = null;\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\tpublic static PBEReadOnlyLocalizedString GetSpeciesCategory(PBESpecies species)\n\t{\n\t\tif (species <= 0 || species >= PBESpecies.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(species));\n\t\t}\n\t\tList<SearchResult> results = PBEDefaultDataProvider.Instance.QueryDatabase<SearchResult>(string.Format(QueryId, \"SpeciesCategories\", species));\n\t\tif (results.Count == 1)\n\t\t{\n\t\t\treturn new PBEReadOnlyLocalizedString(results[0]);\n\t\t}\n\t\tthrow new InvalidDataException();\n\t}\n\tpublic static PBEReadOnlyLocalizedString GetSpeciesEntry(PBESpecies species)\n\t{\n\t\tif (species <= 0 || species >= PBESpecies.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(species));\n\t\t}\n\t\tList<SearchResult> results = PBEDefaultDataProvider.Instance.QueryDatabase<SearchResult>(string.Format(QueryId, \"SpeciesEntries\", species));\n\t\tif (results.Count == 1)\n\t\t{\n\t\t\treturn new PBEReadOnlyLocalizedString(results[0]);\n\t\t}\n\t\tthrow new InvalidDataException();\n\t}\n\tpublic static PBEReadOnlyLocalizedString GetSpeciesName(PBESpecies species)\n\t{\n\t\tif (species <= 0 || species >= PBESpecies.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(species));\n\t\t}\n\t\tList<SearchResult> results = PBEDefaultDataProvider.Instance.QueryDatabase<SearchResult>(string.Format(QueryId, \"SpeciesNames\", species));\n\t\tif (results.Count == 1)\n\t\t{\n\t\t\treturn new PBEReadOnlyLocalizedString(results[0]);\n\t\t}\n\t\tthrow new InvalidDataException();\n\t}\n\tpublic static bool GetStatByName(string statName, [NotNullWhen(true)] out PBEStat? stat)\n\t{\n\t\tList<SearchResult> results = PBEDefaultDataProvider.Instance.QueryDatabase<SearchResult>(string.Format(QueryText, \"StatNames\", statName));\n\t\tif (results.Count == 1)\n\t\t{\n\t\t\tstat = Enum.Parse<PBEStat>(results[0].Id);\n\t\t}\n\t\telse if (!GetEnumValue(statName, out stat))\n\t\t{\n\t\t\tstat = null;\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\tpublic static PBEReadOnlyLocalizedString GetStatName(PBEStat stat)\n\t{\n\t\tif (!Enum.IsDefined(stat))\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(stat));\n\t\t}\n\t\tList<SearchResult> results = PBEDefaultDataProvider.Instance.QueryDatabase<SearchResult>(string.Format(QueryId, \"StatNames\", stat));\n\t\tif (results.Count == 1)\n\t\t{\n\t\t\treturn new PBEReadOnlyLocalizedString(results[0]);\n\t\t}\n\t\tthrow new InvalidDataException();\n\t}\n\tpublic static bool GetTypeByName(string typeName, [NotNullWhen(true)] out PBEType? type)\n\t{\n\t\tList<SearchResult> results = PBEDefaultDataProvider.Instance.QueryDatabase<SearchResult>(string.Format(QueryText, \"TypeNames\", typeName));\n\t\tif (results.Count == 1)\n\t\t{\n\t\t\ttype = Enum.Parse<PBEType>(results[0].Id);\n\t\t}\n\t\telse if (!GetEnumValue(typeName, out type))\n\t\t{\n\t\t\ttype = null;\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\tpublic static PBEReadOnlyLocalizedString GetTypeName(PBEType type)\n\t{\n\t\tif (type >= PBEType.MAX)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(type));\n\t\t}\n\t\tList<SearchResult> results = PBEDefaultDataProvider.Instance.QueryDatabase<SearchResult>(string.Format(QueryId, \"TypeNames\", type));\n\t\tif (results.Count == 1)\n\t\t{\n\t\t\treturn new PBEReadOnlyLocalizedString(results[0]);\n\t\t}\n\t\tthrow new InvalidDataException();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine.DefaultData/PokemonBattleEngine.DefaultData.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n\t<PropertyGroup>\n\t\t<TargetFramework>net7.0</TargetFramework>\n\t\t<OutputType>Library</OutputType>\n\t\t<RootNamespace>Kermalis.PokemonBattleEngine.DefaultData</RootNamespace>\n\t\t<Authors>Kermalis</Authors>\n\t\t<Copyright>Kermalis</Copyright>\n\t\t<RepositoryUrl>https://github.com/Kermalis/PokemonBattleEngine</RepositoryUrl>\n\t\t<DocumentationFile>bin\\$(Configuration)\\$(TargetFramework)\\$(AssemblyName).xml</DocumentationFile>\n\t\t<NoWarn>1591</NoWarn>\n\t\t<Nullable>enable</Nullable>\n\t</PropertyGroup>\n\n\t<ItemGroup>\n\t\t<PackageReference Include=\"Microsoft.Data.Sqlite\" Version=\"7.0.2\" />\n\t\t<PackageReference Include=\"SQLitePCLRaw.bundle_e_sqlite3\" Version=\"2.1.4\" />\n\t\t<ProjectReference Include=\"..\\PokemonBattleEngine\\PokemonBattleEngine.csproj\" />\n\t\t<None Update=\"PokemonBattleEngine.db\">\n\t\t\t<CopyToOutputDirectory>Always</CopyToOutputDirectory>\n\t\t</None>\n\t</ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "PokemonBattleEngine.DefaultData/RandomTeamGenerator.cs",
    "content": "﻿// This file is adapted from Pokémon Showdown (MIT License): https://github.com/smogon/pokemon-showdown/blob/master/data/mods/gen5/random-teams.ts\n// Those guys know what they're doing!\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Data.Legality;\nusing Kermalis.PokemonBattleEngine.Data.Utils;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace Kermalis.PokemonBattleEngine.DefaultData;\n\npublic static class PBEDDRandomTeamGenerator\n{\n\tprivate sealed class TeamDetails\n\t{\n\t\tpublic bool Hail;\n\t\tpublic bool HarshSunlight;\n\t\tpublic bool Rain;\n\t\tpublic bool Sandstorm;\n\t\tpublic bool StealthRock;\n\t\tpublic bool ToxicSpikes;\n\t\tpublic bool RapidSpin;\n\t}\n\n\tprivate sealed class Counter\n\t{\n\t\tpublic int Damage;\n\t\tpublic int Recovery;\n\t\tpublic int STAB;\n\t\tpublic int Inaccurate;\n\t\tpublic int Priority;\n\t\tpublic int Recoil;\n\t\tpublic int Drain;\n\t\tpublic int Sound;\n\t\tpublic int PhysicalSetup;\n\t\tpublic int SpecialSetup;\n\t\tpublic int MixedSetup;\n\t\tpublic int SpeedSetup;\n\t\tpublic int PhysicalPool;\n\t\tpublic int SpecialPool;\n\t\tpublic int Hazards;\n\t\tpublic int DamagingMoves;\n\t\tpublic char SetupCategory; // 'N', 'M', 'P', 'S' - None, Mixed, Physical, Special\n\t\tprivate readonly Dictionary<PBEMoveCategory, float> _categories = new();\n\t\tprivate readonly Dictionary<PBEType, int> _types = new();\n\t\tprivate readonly Dictionary<PBEAbility, int> _abilities = new();\n\n\t\tpublic Counter()\n\t\t{\n\t\t\tfor (PBEMoveCategory c = 0; c < PBEMoveCategory.MAX; c++)\n\t\t\t{\n\t\t\t\t_categories.Add(c, 0);\n\t\t\t}\n\t\t\tfor (PBEType t = PBEType.None + 1; t < PBEType.MAX; t++)\n\t\t\t{\n\t\t\t\t_types.Add(t, 0);\n\t\t\t}\n\t\t\tforeach (PBEAbility a in _counterAbilities)\n\t\t\t{\n\t\t\t\t_abilities.Add(a, 0);\n\t\t\t}\n\t\t}\n\n\t\tpublic float this[PBEMoveCategory category]\n\t\t{\n\t\t\tget => _categories[category];\n\t\t\tset => _categories[category] = value;\n\t\t}\n\t\tpublic int this[PBEType type]\n\t\t{\n\t\t\tget => _types[type];\n\t\t\tset => _types[type] = value;\n\t\t}\n\t\tpublic int this[PBEAbility ability]\n\t\t{\n\t\t\tget => _abilities[ability];\n\t\t\tset => _abilities[ability] = value;\n\t\t}\n\t}\n\n\tprivate static readonly PBEMove[] _setupExceptionMoves = new[] { PBEMove.CloseCombat, PBEMove.DracoMeteor, PBEMove.ExtremeSpeed, PBEMove.SuckerPunch, PBEMove.Superpower };\n\tprivate static readonly PBEAbility[] _counterAbilities = new[] { PBEAbility.Adaptability, PBEAbility.Contrary, PBEAbility.Hustle, PBEAbility.IronFist, PBEAbility.SereneGrace, PBEAbility.SheerForce, PBEAbility.SkillLink, PBEAbility.Technician };\n\tprivate static readonly PBEMove[] _recoveryMoves = new[] { PBEMove.HealOrder, PBEMove.MilkDrink, PBEMove.Moonlight, PBEMove.MorningSun, PBEMove.Recover, PBEMove.Roost, PBEMove.SlackOff, PBEMove.Softboiled, PBEMove.Synthesis };\n\tprivate static readonly PBEMove[] _contraryMoves = new[] { PBEMove.CloseCombat, PBEMove.LeafStorm, PBEMove.Overheat, PBEMove.Superpower, PBEMove.VCreate };\n\tprivate static readonly PBEMove[] _physicalSetupMoves = new[] { PBEMove.BellyDrum, PBEMove.BulkUp, PBEMove.Coil, PBEMove.Curse, PBEMove.DragonDance, PBEMove.HoneClaws, PBEMove.Howl, PBEMove.SwordsDance };\n\tprivate static readonly PBEMove[] _specialSetupMoves = new[] { PBEMove.CalmMind, PBEMove.ChargeBeam, PBEMove.NastyPlot, PBEMove.QuiverDance, PBEMove.TailGlow };\n\tprivate static readonly PBEMove[] _mixedSetupMoves = new[] { PBEMove.Growth, PBEMove.ShellSmash, PBEMove.WorkUp };\n\tprivate static readonly PBEMove[] _speedSetupMoves = new[] { PBEMove.Agility, PBEMove.Autotomize, PBEMove.FlameCharge, PBEMove.RockPolish, PBEMove.ShiftGear };\n\tprivate static readonly PBEMove[] _hazardMoves = new[] { PBEMove.Spikes, PBEMove.StealthRock, PBEMove.ToxicSpikes };\n\t// Moves that shouldn't be the only STAB move\n\tprivate static readonly PBEMove[] _noSTABMoves = new[] { PBEMove.AquaJet, PBEMove.Bounce, PBEMove.Explosion, PBEMove.FakeOut, PBEMove.FlameCharge, PBEMove.IceShard, PBEMove.MachPunch, PBEMove.Pluck,\n\t\tPBEMove.Pursuit, PBEMove.QuickAttack, PBEMove.Selfdestruct, PBEMove.SuckerPunch, PBEMove.ClearSmog, PBEMove.Eruption, PBEMove.IcyWind, PBEMove.Incinerate, PBEMove.Snarl, PBEMove.VacuumWave, PBEMove.WaterSpout };\n\n\tprivate static PBEAbility GetAbility(PBESpecies species, List<PBEMove> moves, IPBEDDPokemonDataExtended pData, Counter counter, TeamDetails teamDs)\n\t{\n\t\tvar abilityPool = new List<PBEAbility>(pData.Abilities);\n\t\tPBEAbility ability;\n\t\tdo\n\t\t{\n\t\t\tbool reject = false;\n\t\t\tability = PBEDataProvider.GlobalRandom.RandomElement(abilityPool);\n\t\t\t// Reasons to reject\n\t\t\tswitch (ability)\n\t\t\t{\n\t\t\t\tcase PBEAbility.AngerPoint:\n\t\t\t\tcase PBEAbility.Gluttony:\n\t\t\t\tcase PBEAbility.Moody: reject = true; break;\n\t\t\t\tcase PBEAbility.Blaze: reject = counter[PBEType.Fire] == 0; break;\n\t\t\t\tcase PBEAbility.Chlorophyll: reject = !teamDs.HarshSunlight && !moves.Contains(PBEMove.SunnyDay); break;\n\t\t\t\tcase PBEAbility.Compoundeyes:\n\t\t\t\tcase PBEAbility.NoGuard: reject = counter.Inaccurate == 0; break;\n\t\t\t\tcase PBEAbility.Defiant:\n\t\t\t\tcase PBEAbility.Moxie: reject = counter[PBEMoveCategory.Physical] == 0 && !moves.Contains(PBEMove.BatonPass); break;\n\t\t\t\tcase PBEAbility.Hydration:\n\t\t\t\tcase PBEAbility.RainDish:\n\t\t\t\tcase PBEAbility.SwiftSwim: reject = !teamDs.Rain && !moves.Contains(PBEMove.RainDance); break;\n\t\t\t\tcase PBEAbility.IceBody:\n\t\t\t\tcase PBEAbility.SnowCloak: reject = !teamDs.Hail && !moves.Contains(PBEMove.Hail); break;\n\t\t\t\t// Zangoose\n\t\t\t\tcase PBEAbility.Immunity: reject = pData.Abilities.Contains(PBEAbility.ToxicBoost); break;\n\t\t\t\tcase PBEAbility.Lightningrod: reject = pData.HasType(PBEType.Ground); break;\n\t\t\t\t// Basculin\n\t\t\t\tcase PBEAbility.MoldBreaker: reject = pData.Abilities.Contains(PBEAbility.Adaptability); break;\n\t\t\t\tcase PBEAbility.Overgrow: reject = counter[PBEType.Grass] == 0; break;\n\t\t\t\t// Breloom\n\t\t\t\tcase PBEAbility.PoisonHeal: reject = counter[PBEAbility.Technician] > 0 && pData.Abilities.Contains(PBEAbility.Technician); break;\n\t\t\t\tcase PBEAbility.Prankster: reject = counter[PBEMoveCategory.Status] == 0; break;\n\t\t\t\tcase PBEAbility.Reckless:\n\t\t\t\tcase PBEAbility.RockHead: reject = counter.Recoil == 0; break;\n\t\t\t\t// Solosis, Duosion, Reuniclus\n\t\t\t\tcase PBEAbility.Regenerator: reject = pData.Abilities.Contains(PBEAbility.MagicGuard); break;\n\t\t\t\tcase PBEAbility.SandForce:\n\t\t\t\tcase PBEAbility.SandRush:\n\t\t\t\tcase PBEAbility.SandVeil: reject = !teamDs.Sandstorm && !moves.Contains(PBEMove.Sandstorm); break;\n\t\t\t\tcase PBEAbility.SereneGrace: reject = species == PBESpecies.Blissey || species == PBESpecies.Togetic; break;\n\t\t\t\t// Timburr, Gurdurr, Conkeldurr\n\t\t\t\tcase PBEAbility.SheerForce: reject = (counter[PBEAbility.IronFist] > counter[PBEAbility.SheerForce] && pData.Abilities.Contains(PBEAbility.IronFist)) || moves.Contains(PBEMove.FakeOut); break;\n\t\t\t\tcase PBEAbility.Simple:\n\t\t\t\tcase PBEAbility.WeakArmor: reject = counter.SetupCategory == 'N'; break;\n\t\t\t\tcase PBEAbility.Sturdy: reject = counter.Recoil > 0 && counter.Recovery == 0; break;\n\t\t\t\tcase PBEAbility.Swarm: reject = counter[PBEType.Bug] == 0; break;\n\t\t\t\t// Ambipom, Minccino, Cinccino\n\t\t\t\tcase PBEAbility.Technician: reject = counter[PBEAbility.SkillLink] >= counter[PBEAbility.Technician] && pData.Abilities.Contains(PBEAbility.SkillLink); break;\n\t\t\t\tcase PBEAbility.TintedLens: reject = counter.Damage >= counter.DamagingMoves || (counter[PBEMoveCategory.Status] > 2 && counter.SetupCategory == 'N'); break;\n\t\t\t\tcase PBEAbility.Torrent: reject = counter[PBEType.Water] == 0; break;\n\t\t\t\t// Clefable\n\t\t\t\tcase PBEAbility.Unaware: reject = counter[PBEMoveCategory.Status] < 2 && pData.Abilities.Contains(PBEAbility.MagicGuard); break;\n\t\t\t\tcase PBEAbility.Unburden: reject = pData.BaseStats.Speed > 100; break;\n\t\t\t\t// Chinchou, Lanturn\n\t\t\t\tcase PBEAbility.WaterAbsorb: reject = pData.Abilities.Contains(PBEAbility.VoltAbsorb); break;\n\t\t\t}\n\t\t\t// Reasons to always accept\n\t\t\tif (!reject)\n\t\t\t{\n\t\t\t\tif (_counterAbilities.Contains(ability))\n\t\t\t\t{\n\t\t\t\t\treject = counter[ability] == 0;\n\t\t\t\t\tif (!reject) // If we have an ability that the counter says has good moves, use the ability\n\t\t\t\t\t{\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (ability == PBEAbility.Prankster && counter[PBEMoveCategory.Status] > 1)\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\telse if (ability == PBEAbility.SwiftSwim && moves.Contains(PBEMove.RainDance))\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\telse if ((ability == PBEAbility.Guts || ability == PBEAbility.QuickFeet) && moves.Contains(PBEMove.Facade))\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Ability was rejected\n\t\t\tif (reject)\n\t\t\t{\n\t\t\t\tabilityPool.Remove(ability);\n\t\t\t\tability = PBEAbility.None;\n\t\t\t}\n\t\t} while (ability == PBEAbility.None && abilityPool.Count > 0);\n\t\treturn ability;\n\t}\n\tprivate static PBEItem GetItem(PBESpecies species, PBEForm form, PBEAbility ability, List<PBEMove> moves, IPBEDDPokemonDataExtended pData, Counter counter, bool isLead)\n\t{\n\t\tPBEItem item;\n\t\tvoid GetRandomGem()\n\t\t{\n\t\t\tvar list = new List<PBEType>();\n\t\t\tforeach (PBEMove move in moves)\n\t\t\t{\n\t\t\t\tIPBEMoveData mData = PBEDataProvider.Instance.GetMoveData(move);\n\t\t\t\t//if (!move.basePower && !move.basePowerCallback) continue;\n\t\t\t\t// KERMALIS: Instead gonna check status for now\n\t\t\t\tif (mData.Category != PBEMoveCategory.Status)\n\t\t\t\t{\n\t\t\t\t\tlist.Add(mData.Type);\n\t\t\t\t}\n\t\t\t}\n\t\t\titem = PBEDataUtils.TypeToGem[PBEDataProvider.GlobalRandom.RandomElement(list)];\n\t\t}\n\t\t// First, the extra high-priority items\n\t\tif (species == PBESpecies.Marowak)\n\t\t{\n\t\t\titem = PBEItem.ThickClub;\n\t\t}\n\t\telse if (species == PBESpecies.Deoxys && form == PBEForm.Deoxys_Attack)\n\t\t{\n\t\t\titem = isLead && moves.Contains(PBEMove.StealthRock) ? PBEItem.FocusSash : PBEItem.LifeOrb;\n\t\t}\n\t\telse if (species == PBESpecies.Farfetchd)\n\t\t{\n\t\t\titem = PBEItem.Stick;\n\t\t}\n\t\telse if (species == PBESpecies.Pikachu)\n\t\t{\n\t\t\titem = PBEItem.LightBall;\n\t\t}\n\t\telse if (species == PBESpecies.Shedinja || species == PBESpecies.Smeargle)\n\t\t{\n\t\t\titem = PBEItem.FocusSash;\n\t\t}\n\t\telse if (species == PBESpecies.Unown)\n\t\t{\n\t\t\titem = PBEItem.ChoiceSpecs;\n\t\t}\n\t\telse if (species == PBESpecies.Wobbuffet && moves.Contains(PBEMove.DestinyBond) && PBEDataProvider.GlobalRandom.RandomBool())\n\t\t{\n\t\t\titem = PBEItem.CustapBerry;\n\t\t}\n\t\telse if (ability == PBEAbility.Imposter)\n\t\t{\n\t\t\titem = PBEItem.ChoiceScarf;\n\t\t}\n\t\telse if ((moves.Contains(PBEMove.Switcheroo) || moves.Contains(PBEMove.Trick)) && moves.Contains(PBEMove.GyroBall))\n\t\t{\n\t\t\titem = ability == PBEAbility.Levitate || pData.HasType(PBEType.Flying) ? PBEItem.MachoBrace : PBEItem.IronBall;\n\t\t}\n\t\telse if (moves.Contains(PBEMove.Switcheroo) || moves.Contains(PBEMove.Trick))\n\t\t{\n\t\t\tif (pData.BaseStats.Speed >= 60 && pData.BaseStats.Speed <= 108)\n\t\t\t{\n\t\t\t\titem = PBEItem.ChoiceScarf;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\titem = counter[PBEMoveCategory.Physical] > counter[PBEMoveCategory.Special] ? PBEItem.ChoiceBand : PBEItem.ChoiceSpecs;\n\t\t\t}\n\t\t}\n\t\telse if (pData.HasEvolutions())\n\t\t{\n\t\t\titem = PBEItem.Eviolite;\n\t\t}\n\t\telse if (moves.Contains(PBEMove.ShellSmash))\n\t\t{\n\t\t\titem = PBEItem.WhiteHerb;\n\t\t}\n\t\telse if (ability == PBEAbility.Harvest || moves.Contains(PBEMove.BellyDrum))\n\t\t{\n\t\t\titem = PBEItem.SitrusBerry;\n\t\t}\n\t\telse if ((ability == PBEAbility.MagicGuard || ability == PBEAbility.SheerForce) && counter.DamagingMoves > 1)\n\t\t{\n\t\t\titem = PBEItem.LifeOrb;\n\t\t}\n\t\telse if (ability == PBEAbility.PoisonHeal || ability == PBEAbility.ToxicBoost || moves.Contains(PBEMove.Facade))\n\t\t{\n\t\t\titem = PBEItem.ToxicOrb;\n\t\t}\n\t\telse if (ability != PBEAbility.NaturalCure && ability != PBEAbility.ShedSkin && moves.Contains(PBEMove.Rest) && !moves.Contains(PBEMove.SleepTalk))\n\t\t{\n\t\t\titem = PBEItem.ChestoBerry;\n\t\t}\n\t\telse if (moves.Contains(PBEMove.RainDance))\n\t\t{\n\t\t\titem = PBEItem.DampRock;\n\t\t}\n\t\telse if (moves.Contains(PBEMove.SunnyDay))\n\t\t{\n\t\t\titem = PBEItem.HeatRock;\n\t\t}\n\t\telse if (moves.Contains(PBEMove.LightScreen) || moves.Contains(PBEMove.Reflect))\n\t\t{\n\t\t\titem = PBEItem.LightClay;\n\t\t}\n\t\telse if (moves.Contains(PBEMove.Acrobatics))\n\t\t{\n\t\t\titem = PBEItem.FlyingGem;\n\t\t}\n\t\telse if ((ability == PBEAbility.Guts && !moves.Contains(PBEMove.SleepTalk)) || moves.Contains(PBEMove.PsychoShift))\n\t\t{\n\t\t\titem = moves.Contains(PBEMove.DrainPunch) ? PBEItem.FlameOrb : PBEItem.ToxicOrb;\n\t\t}\n\t\telse if (ability == PBEAbility.Unburden && (counter[PBEMoveCategory.Physical] > 0 || counter[PBEMoveCategory.Special] > 0))\n\t\t{\n\t\t\t// Give Unburden mons a random Gem of the type of one of their damaging moves\n\t\t\tGetRandomGem();\n\t\t}\n\t\t// Medium priority\n\t\telse if (counter[PBEMoveCategory.Status] == 0 && (moves.Contains(PBEMove.Eruption) || moves.Contains(PBEMove.WaterSpout)))\n\t\t{\n\t\t\titem = PBEItem.ChoiceScarf;\n\t\t}\n\t\telse if (ability == PBEAbility.SpeedBoost && counter[PBEMoveCategory.Physical] + counter[PBEMoveCategory.Special] > 2 && !moves.Contains(PBEMove.Substitute))\n\t\t{\n\t\t\titem = PBEItem.LifeOrb;\n\t\t}\n\t\telse if (counter[PBEMoveCategory.Physical] >= PBESettings.DefaultNumMoves && !moves.Contains(PBEMove.DragonTail) && !moves.Contains(PBEMove.FakeOut) && !moves.Contains(PBEMove.FlameCharge)\n\t\t\t&& !moves.Contains(PBEMove.SuckerPunch) && (!moves.Contains(PBEMove.RapidSpin) || PBETypeEffectiveness.GetEffectiveness(PBEType.Rock, pData) < 1))\n\t\t{\n\t\t\titem = (pData.BaseStats.Attack >= 100 || pData.Abilities.Contains(PBEAbility.HugePower)) && pData.BaseStats.Speed >= 60 && pData.BaseStats.Speed <= 108 && counter.Priority == 0 && PBEDataProvider.GlobalRandom.RandomBool(2, 3) ? PBEItem.ChoiceScarf : PBEItem.ChoiceBand;\n\t\t}\n\t\telse if (counter[PBEMoveCategory.Special] >= PBESettings.DefaultNumMoves && !moves.Contains(PBEMove.ClearSmog) && !moves.Contains(PBEMove.FieryDance))\n\t\t{\n\t\t\titem = pData.BaseStats.SpAttack >= 100 && pData.BaseStats.Speed >= 60 && pData.BaseStats.Speed <= 108 && counter.Priority == 0 && PBEDataProvider.GlobalRandom.RandomBool(2, 3) ? PBEItem.ChoiceScarf : PBEItem.ChoiceSpecs;\n\t\t}\n\t\telse if (counter[PBEMoveCategory.Special] >= 3 && moves.Contains(PBEMove.Uturn))\n\t\t{\n\t\t\titem = PBEItem.ChoiceSpecs;\n\t\t}\n\t\telse if (ability != PBEAbility.Levitate && PBETypeEffectiveness.GetEffectiveness(PBEType.Ground, pData) > 1 && !moves.Contains(PBEMove.MagnetRise))\n\t\t{\n\t\t\titem = PBEItem.AirBalloon;\n\t\t}\n\t\telse if (moves.Contains(PBEMove.Substitute) && moves.Contains(PBEMove.Reversal))\n\t\t{\n\t\t\tGetRandomGem();\n\t\t}\n\t\telse if (ability != PBEAbility.Sturdy && (moves.Contains(PBEMove.Flail) || moves.Contains(PBEMove.Reversal)))\n\t\t{\n\t\t\titem = PBEItem.FocusSash;\n\t\t}\n\t\telse if (ability == PBEAbility.SlowStart || moves.Contains(PBEMove.Detect) || moves.Contains(PBEMove.Protect) || moves.Contains(PBEMove.SleepTalk) || moves.Contains(PBEMove.Substitute))\n\t\t{\n\t\t\titem = PBEItem.Leftovers;\n\t\t}\n\t\telse if (ability == PBEAbility.IronBarbs)\n\t\t{\n\t\t\titem = PBEItem.RockyHelmet;\n\t\t}\n\t\telse if (species == PBESpecies.Palkia && (moves.Contains(PBEMove.DracoMeteor) || moves.Contains(PBEMove.SpacialRend)) && moves.Contains(PBEMove.HydroPump))\n\t\t{\n\t\t\titem = PBEItem.LustrousOrb;\n\t\t}\n\t\telse if (pData.BaseStats.HP + pData.BaseStats.Defense + pData.BaseStats.SpDefense > 275)\n\t\t{\n\t\t\titem = PBEItem.Leftovers;\n\t\t}\n\t\telse if (ability != PBEAbility.Sturdy && counter[PBEMoveCategory.Physical] + counter[PBEMoveCategory.Special] >= 3 && counter.SetupCategory != 'N' && !moves.Contains(PBEMove.RapidSpin))\n\t\t{\n\t\t\titem = moves.Contains(PBEMove.Outrage) ? PBEItem.LumBerry : PBEItem.LifeOrb;\n\t\t}\n\t\telse if (counter[PBEMoveCategory.Physical] + counter[PBEMoveCategory.Special] >= PBESettings.DefaultNumMoves)\n\t\t{\n\t\t\titem = counter[PBEType.Normal] > 0 ? PBEItem.LifeOrb : PBEItem.ExpertBelt;\n\t\t}\n\t\telse if (isLead && ability != PBEAbility.Regenerator && ability != PBEAbility.Sturdy && counter.Recoil == 0 && counter.Recovery == 0 && pData.BaseStats.HP + pData.BaseStats.Defense + pData.BaseStats.SpDefense <= 275)\n\t\t{\n\t\t\titem = PBEItem.FocusSash;\n\t\t}\n\t\t// This is the \"REALLY can't think of a good item\" cutoff\n\t\telse if (PBETypeEffectiveness.GetEffectiveness(PBEType.Rock, pData) >= 1 || moves.Contains(PBEMove.DragonTail))\n\t\t{\n\t\t\titem = PBEItem.Leftovers;\n\t\t}\n\t\telse if (ability != PBEAbility.Sturdy && counter[PBEMoveCategory.Status] <= 1 && !moves.Contains(PBEMove.RapidSpin) && !moves.Contains(PBEMove.Uturn))\n\t\t{\n\t\t\titem = PBEItem.LifeOrb;\n\t\t}\n\t\telse\n\t\t{\n\t\t\titem = PBEItem.Leftovers;\n\t\t}\n\n\t\t// For Trick / Switcheroo\n\t\tif (item == PBEItem.Leftovers && pData.HasType(PBEType.Poison))\n\t\t{\n\t\t\titem = PBEItem.BlackSludge;\n\t\t}\n\t\treturn item;\n\t}\n\tprivate static List<PBEMove> GetMoves(PBESpecies species, PBEForm form, IPBEDDPokemonDataExtended pData, bool isLead, TeamDetails teamDs, out Counter counter)\n\t{\n\t\tvar movePool = new List<PBEMove>(PBEDataProvider.Instance.GetLegalMoves(species, form, PBESettings.DefaultMaxLevel));\n\t\tvar moves = new List<PBEMove>(PBESettings.DefaultNumMoves);\n\t\tint startI;\n\t\tif (species == PBESpecies.Keldeo && form == PBEForm.Keldeo_Resolute)\n\t\t{\n\t\t\tmoves.Add(PBEMove.SecretSword);\n\t\t\tmovePool.Remove(PBEMove.SecretSword);\n\t\t\tstartI = 1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstartI = 0;\n\t\t}\n\t\tvar rejectedPool = new List<PBEMove>();\n\n\t\tdo\n\t\t{\n\t\t\t// Choose next moves from learnset/viable moves and add them to moves list:\n\t\t\twhile (moves.Count < PBESettings.DefaultNumMoves && movePool.Count > 0)\n\t\t\t{\n\t\t\t\tPBEMove move = PBEDataProvider.GlobalRandom.RandomElement(movePool);\n\t\t\t\tmovePool.Remove(move);\n\t\t\t\tmoves.Add(move);\n\t\t\t}\n\t\t\twhile (moves.Count < PBESettings.DefaultNumMoves && rejectedPool.Count > 0)\n\t\t\t{\n\t\t\t\tPBEMove move = PBEDataProvider.GlobalRandom.RandomElement(rejectedPool);\n\t\t\t\trejectedPool.Remove(move);\n\t\t\t\tmoves.Add(move);\n\t\t\t}\n\n\t\t\tcounter = QueryMoves(pData, moves, movePool);\n\n\t\t\t// If there are no more moves to choose, there's nothing to cull\n\t\t\tif (movePool.Count == 0 && rejectedPool.Count == 0)\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// Iterate through the moves again, this time to cull them:\n\t\t\tfor (int i = startI; i < moves.Count; i++)\n\t\t\t{\n\t\t\t\tPBEMove move = moves[i];\n\t\t\t\tIPBEMoveData mData = PBEDataProvider.Instance.GetMoveData(move);\n\n\t\t\t\tbool reject = false;\n\t\t\t\tbool isSetup = false;\n\n\t\t\t\tswitch (move)\n\t\t\t\t{\n\t\t\t\t\t// Not very useful without their supporting moves\n\t\t\t\t\tcase PBEMove.BatonPass:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (counter.SetupCategory == 'N' && counter.SpeedSetup == 0 && !moves.Contains(PBEMove.Substitute) && !moves.Contains(PBEMove.Wish) && !pData.HasAbility(PBEAbility.SpeedBoost))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.FocusPunch:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!moves.Contains(PBEMove.Substitute) || counter.DamagingMoves < 2 || moves.Contains(PBEMove.SwordsDance))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.PerishSong:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!moves.Contains(PBEMove.Protect))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.Rest:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!movePool.Contains(PBEMove.SleepTalk))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.SleepTalk:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!moves.Contains(PBEMove.Rest))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tmovePool.Remove(PBEMove.Rest);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.StoredPower:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (counter.SetupCategory == 'N' && !moves.Contains(PBEMove.CosmicPower))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Set up once and only if we have the moves for it\n\t\t\t\t\tcase PBEMove.BellyDrum:\n\t\t\t\t\tcase PBEMove.BulkUp:\n\t\t\t\t\tcase PBEMove.Coil:\n\t\t\t\t\tcase PBEMove.Curse:\n\t\t\t\t\tcase PBEMove.DragonDance:\n\t\t\t\t\tcase PBEMove.HoneClaws:\n\t\t\t\t\tcase PBEMove.SwordsDance:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (counter.SetupCategory != 'P' || counter.PhysicalSetup > 1)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (counter[PBEMoveCategory.Physical] + counter.PhysicalPool < 2 && !moves.Contains(PBEMove.BatonPass) && (!moves.Contains(PBEMove.Rest) || !moves.Contains(PBEMove.SleepTalk)))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tisSetup = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.CalmMind:\n\t\t\t\t\tcase PBEMove.NastyPlot:\n\t\t\t\t\tcase PBEMove.QuiverDance:\n\t\t\t\t\tcase PBEMove.TailGlow:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (counter.SetupCategory != 'S' || counter.SpecialSetup > 1)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (counter[PBEMoveCategory.Special] + counter.SpecialPool < 2 && !moves.Contains(PBEMove.BatonPass) && (!moves.Contains(PBEMove.Rest) || !moves.Contains(PBEMove.SleepTalk)))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tisSetup = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.Growth:\n\t\t\t\t\tcase PBEMove.ShellSmash:\n\t\t\t\t\tcase PBEMove.WorkUp:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (counter.SetupCategory != 'M' || counter.MixedSetup > 1)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (counter.DamagingMoves + counter.PhysicalPool + counter.SpecialPool < 2 && !moves.Contains(PBEMove.BatonPass))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (move == PBEMove.Growth && !moves.Contains(PBEMove.SunnyDay))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tisSetup = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.Agility:\n\t\t\t\t\tcase PBEMove.Autotomize:\n\t\t\t\t\tcase PBEMove.RockPolish:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (counter.DamagingMoves < 2 && counter.SetupCategory == 'N' && !moves.Contains(PBEMove.BatonPass))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (moves.Contains(PBEMove.Rest) && moves.Contains(PBEMove.SleepTalk))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (counter.SetupCategory == 'N')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tisSetup = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Bad after setup\n\t\t\t\t\tcase PBEMove.BulletPunch:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (counter.SpeedSetup > 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.CircleThrow:\n\t\t\t\t\tcase PBEMove.DragonTail:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (counter.SetupCategory != 'N' && ((!moves.Contains(PBEMove.Rest) && !moves.Contains(PBEMove.SleepTalk)) || moves.Contains(PBEMove.StormThrow)))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (counter.SpeedSetup > 0 || moves.Contains(PBEMove.Encore) || moves.Contains(PBEMove.Roar) || moves.Contains(PBEMove.Whirlwind))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.FakeOut:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (counter.SetupCategory != 'N' || moves.Contains(PBEMove.Substitute) || moves.Contains(PBEMove.Switcheroo) || moves.Contains(PBEMove.Trick))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.Haze:\n\t\t\t\t\tcase PBEMove.MagicCoat:\n\t\t\t\t\tcase PBEMove.Pursuit:\n\t\t\t\t\tcase PBEMove.Selfdestruct:\n\t\t\t\t\tcase PBEMove.Spikes:\n\t\t\t\t\tcase PBEMove.WaterSpout:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (counter.SetupCategory != 'N' || counter.SpeedSetup > 0 || (moves.Contains(PBEMove.Rest) && moves.Contains(PBEMove.SleepTalk)))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.HealingWish:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (counter.SetupCategory != 'N' || counter.Recovery > 0 || moves.Contains(PBEMove.Substitute))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.LeechSeed:\n\t\t\t\t\tcase PBEMove.Roar:\n\t\t\t\t\tcase PBEMove.Whirlwind:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (counter.SetupCategory != 'N' || counter.SpeedSetup > 0 || moves.Contains(PBEMove.DragonTail))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.NightShade:\n\t\t\t\t\tcase PBEMove.SeismicToss:\n\t\t\t\t\tcase PBEMove.SuperFang:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (counter.DamagingMoves > 1 || counter.SetupCategory != 'N')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.Protect:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (counter.SetupCategory != 'N' && (pData.HasAbility(PBEAbility.Guts) || pData.HasAbility(PBEAbility.SpeedBoost)) && !moves.Contains(PBEMove.BatonPass))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (moves.Contains(PBEMove.Rest) || (moves.Contains(PBEMove.LightScreen) && moves.Contains(PBEMove.Reflect))) // A typo? light screen and reflect together? not ||?\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.RapidSpin:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (moves.Contains(PBEMove.ShellSmash) || (counter.SetupCategory != 'N' && counter[PBEMoveCategory.Status] >= 2))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.StealthRock:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (counter.SetupCategory != 'N' || counter.SpeedSetup > 0 || moves.Contains(PBEMove.Rest) || teamDs.StealthRock)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.Switcheroo:\n\t\t\t\t\tcase PBEMove.Trick:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (counter[PBEMoveCategory.Physical] + counter[PBEMoveCategory.Special] < 3 || counter.Priority > 0 || moves.Contains(PBEMove.RapidSpin))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.ToxicSpikes:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (counter.SetupCategory != 'N' || teamDs.ToxicSpikes)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.TrickRoom:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (counter.SetupCategory != 'N' || counter.SpeedSetup > 0 || counter.DamagingMoves < 2)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (moves.Contains(PBEMove.LightScreen) || moves.Contains(PBEMove.Reflect))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.Uturn:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (counter.SetupCategory != 'N' || counter.SpeedSetup > 0 || moves.Contains(PBEMove.BatonPass)) // KERMALIS: Should reject if they have volt switch?\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.VoltSwitch:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (counter.SetupCategory != 'N' || counter.SpeedSetup > 0 || moves.Contains(PBEMove.BatonPass) || moves.Contains(PBEMove.MagnetRise) || moves.Contains(PBEMove.Uturn))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Bit redundant to have both\n\t\t\t\t\t// Attacks:\n\t\t\t\t\tcase PBEMove.BugBite:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (moves.Contains(PBEMove.Uturn))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.Crunch:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!pData.HasType(PBEType.Dark) && moves.Contains(PBEMove.SuckerPunch))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.CloseCombat:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (counter.SetupCategory != 'N' && moves.Contains(PBEMove.AuraSphere))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.DragonPulse:\n\t\t\t\t\tcase PBEMove.SpacialRend:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (moves.Contains(PBEMove.DracoMeteor) || moves.Contains(PBEMove.Outrage))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.Thunderbolt:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (moves.Contains(PBEMove.WildCharge))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.AuraSphere:\n\t\t\t\t\tcase PBEMove.HiJumpKick:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (counter.SetupCategory != 'N' && moves.Contains(PBEMove.CloseCombat))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.DrainPunch:\n\t\t\t\t\tcase PBEMove.FocusBlast:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (moves.Contains(PBEMove.CloseCombat) || moves.Contains(PBEMove.CrossChop) || moves.Contains(PBEMove.HiJumpKick) || moves.Contains(PBEMove.LowKick))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.BlueFlare:\n\t\t\t\t\tcase PBEMove.FlareBlitz:\n\t\t\t\t\tcase PBEMove.FieryDance:\n\t\t\t\t\tcase PBEMove.Flamethrower:\n\t\t\t\t\tcase PBEMove.LavaPlume:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (moves.Contains(PBEMove.FireBlast) || moves.Contains(PBEMove.Overheat) || moves.Contains(PBEMove.VCreate))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.AirSlash:\n\t\t\t\t\tcase PBEMove.BraveBird:\n\t\t\t\t\tcase PBEMove.Pluck:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (moves.Contains(PBEMove.Acrobatics) || moves.Contains(PBEMove.Hurricane))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.GigaDrain:\n\t\t\t\t\t{\n\t\t\t\t\t\tif ((counter.SetupCategory != 'N' && moves.Contains(PBEMove.LeafStorm)) || moves.Contains(PBEMove.PetalDance) || moves.Contains(PBEMove.PowerWhip))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.SolarBeam:\n\t\t\t\t\t{\n\t\t\t\t\t\tif ((!pData.HasAbility(PBEAbility.Drought) && !moves.Contains(PBEMove.SunnyDay)) || moves.Contains(PBEMove.GigaDrain) || moves.Contains(PBEMove.LeafStorm))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.LeafStorm:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (counter.SetupCategory != 'N' && moves.Contains(PBEMove.GigaDrain))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.Bonemerang:\n\t\t\t\t\tcase PBEMove.EarthPower:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (moves.Contains(PBEMove.Earthquake))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.Endeavor:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!isLead)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.Facade:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (moves.Contains(PBEMove.SuckerPunch) && !pData.HasType(PBEType.Normal))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.Judgment:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (counter.SetupCategory != 'S' && counter.STAB > 1)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.Return:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (moves.Contains(PBEMove.BodySlam) || moves.Contains(PBEMove.DoubleEdge))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.WeatherBall:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!moves.Contains(PBEMove.SunnyDay))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.PoisonJab:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (moves.Contains(PBEMove.GunkShot))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.Psychic:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (moves.Contains(PBEMove.Psyshock))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.RockBlast:\n\t\t\t\t\tcase PBEMove.RockSlide:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (moves.Contains(PBEMove.HeadSmash) || moves.Contains(PBEMove.StoneEdge))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.StoneEdge:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (moves.Contains(PBEMove.HeadSmash))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.Scald:\n\t\t\t\t\tcase PBEMove.Surf:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (moves.Contains(PBEMove.HydroPump))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.Waterfall:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (moves.Contains(PBEMove.Scald) || (moves.Contains(PBEMove.Rest) && moves.Contains(PBEMove.SleepTalk)))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Status:\n\t\t\t\t\tcase PBEMove.Encore:\n\t\t\t\t\tcase PBEMove.IceShard:\n\t\t\t\t\tcase PBEMove.SuckerPunch:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (moves.Contains(PBEMove.Rest) && moves.Contains(PBEMove.SleepTalk))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.HealBell:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (moves.Contains(PBEMove.MagicCoat))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.Moonlight:\n\t\t\t\t\tcase PBEMove.PainSplit:\n\t\t\t\t\tcase PBEMove.Recover:\n\t\t\t\t\tcase PBEMove.Roost:\n\t\t\t\t\tcase PBEMove.Softboiled:\n\t\t\t\t\tcase PBEMove.Synthesis:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (moves.Contains(PBEMove.LeechSeed) || moves.Contains(PBEMove.Rest) || moves.Contains(PBEMove.Wish))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.Substitute:\n\t\t\t\t\t{\n\t\t\t\t\t\tif ((moves.Contains(PBEMove.DoubleEdge) && !pData.HasAbility(PBEAbility.RockHead)) || moves.Contains(PBEMove.Pursuit) || moves.Contains(PBEMove.Rest) || moves.Contains(PBEMove.Superpower) || moves.Contains(PBEMove.Uturn) || moves.Contains(PBEMove.VoltSwitch))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.ThunderWave:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (counter.SetupCategory != 'N' || counter.SpeedSetup > 0 || (moves.Contains(PBEMove.Rest) && moves.Contains(PBEMove.SleepTalk)))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (moves.Contains(PBEMove.Discharge) || moves.Contains(PBEMove.TrickRoom))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEMove.WillOWisp:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (moves.Contains(PBEMove.LavaPlume) || (moves.Contains(PBEMove.Scald) && !pData.HasType(PBEType.Ghost))) // KERMALIS: why ghost check\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// This move doesn't satisfy our setup requirements:\n\t\t\t\tif ((mData.Category == PBEMoveCategory.Physical && counter.SetupCategory == 'S') || (mData.Category == PBEMoveCategory.Special && counter.SetupCategory == 'P'))\n\t\t\t\t{\n\t\t\t\t\t// Reject STABs last in case the setup type changes later on\n\t\t\t\t\tint stabs = counter[pData.Type1];\n\t\t\t\t\tif (pData.Type2 != PBEType.None)\n\t\t\t\t\t{\n\t\t\t\t\t\tstabs += counter[pData.Type2];\n\t\t\t\t\t}\n\t\t\t\t\tif (!_setupExceptionMoves.Contains(move) && (!pData.HasType(mData.Type) || stabs > 1 || counter[mData.Category] < 2))\n\t\t\t\t\t{\n\t\t\t\t\t\treject = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tPBEMoveCategory c = counter.SetupCategory == 'P' ? PBEMoveCategory.Physical : PBEMoveCategory.Special;\n\t\t\t\tif (counter.SetupCategory != 'N' && !isSetup && counter.SetupCategory != 'M')\n\t\t\t\t{\n\t\t\t\t\tif (mData.Category != c && counter[c] < 2 && !moves.Contains(PBEMove.BatonPass) && (mData.Category != PBEMoveCategory.Status || !mData.IsHPRestoreMove()) && move != PBEMove.SleepTalk)\n\t\t\t\t\t{\n\t\t\t\t\t\t// Mono-attacking with setup and Rest/SleepTalk is allowed\n\t\t\t\t\t\t// Reject status moves only if there is nothing else to reject\n\t\t\t\t\t\tif (mData.Category != PBEMoveCategory.Status || (counter[c] + counter[PBEMoveCategory.Status] > 3 && counter.PhysicalSetup + counter.SpecialSetup < 2))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (counter.SetupCategory == 'S' && move == PBEMove.HiddenPower && pData.Type2 != PBEType.None && counter[PBEMoveCategory.Special] <= 2 && !pData.HasType(mData.Type) && counter[PBEMoveCategory.Physical] == 0 && counter.SpecialPool > 0)\n\t\t\t\t{\n\t\t\t\t\t// Hidden Power isn't good enough\n\t\t\t\t\treject = true;\n\t\t\t\t}\n\n\t\t\t\t// Pokemon should have moves that benefit their Type/Ability/Weather, as well as moves required by its forme\n\t\t\t\tif (!reject)\n\t\t\t\t{\n\t\t\t\t\tif ((\n\t\t\t\t\t\tcounter.PhysicalSetup + counter.SpecialSetup < 2 &&\n\t\t\t\t\t\t(counter.SetupCategory == 'N' || counter.SetupCategory == 'M' || (mData.Category != PBEMoveCategory.Status && mData.Category != c) || counter[c] + counter[PBEMoveCategory.Status] > 3)\n\t\t\t\t\t\t) && (\n\t\t\t\t\t\t(counter.STAB == 0 && counter.Damage == 0 && (pData.Type2 != PBEType.None || (pData.Type1 != PBEType.Normal && pData.Type1 != PBEType.Psychic) || !moves.Contains(PBEMove.IceBeam) || pData.BaseStats.SpAttack >= pData.BaseStats.SpDefense))\n\t\t\t\t\t\t|| (pData.HasType(PBEType.Dark) && counter[PBEType.Dark] == 0)\n\t\t\t\t\t\t|| (pData.HasType(PBEType.Dragon) && counter[PBEType.Dragon] == 0)\n\t\t\t\t\t\t|| (pData.HasType(PBEType.Electric) && counter[PBEType.Electric] == 0)\n\t\t\t\t\t\t|| (pData.HasType(PBEType.Fighting) && counter[PBEType.Fighting] == 0 && (pData.BaseStats.Attack >= 110 || pData.HasAbility(PBEAbility.Justified) || pData.HasAbility(PBEAbility.PurePower) || counter.SetupCategory != 'N' || counter[PBEMoveCategory.Status] == 0))\n\t\t\t\t\t\t|| (pData.HasType(PBEType.Fire) && counter[PBEType.Fire] == 0)\n\t\t\t\t\t\t|| (pData.HasType(PBEType.Flying) && pData.HasType(PBEType.Normal) && counter[PBEType.Flying] == 0)\n\t\t\t\t\t\t|| (pData.HasType(PBEType.Ground) && counter[PBEType.Ground] == 0 && !moves.Contains(PBEMove.Rest) && !moves.Contains(PBEMove.SleepTalk))\n\t\t\t\t\t\t|| (pData.HasType(PBEType.Ice) && counter[PBEType.Ice] == 0)\n\t\t\t\t\t\t|| (pData.HasType(PBEType.Rock) && counter[PBEType.Rock] == 0 && pData.BaseStats.Attack >= 80)\n\t\t\t\t\t\t|| (pData.HasType(PBEType.Steel) && pData.HasAbility(PBEAbility.Technician) && counter[PBEType.Steel] == 0)\n\t\t\t\t\t\t|| (pData.HasType(PBEType.Water) && counter[PBEType.Water] == 0)\n\t\t\t\t\t\t|| (\n\t\t\t\t\t\t(pData.HasAbility(PBEAbility.Adaptability) && counter.SetupCategory == 'N' && pData.Type2 != PBEType.None && (counter[pData.Type1] == 0 || counter[pData.Type2] == 0))\n\t\t\t\t\t\t|| (pData.HasAbility(PBEAbility.BadDreams) && movePool.Contains(PBEMove.DarkVoid))\n\t\t\t\t\t\t|| (pData.HasAbility(PBEAbility.Contrary) && counter[PBEAbility.Contrary] == 0 && species != PBESpecies.Shuckle)\n\t\t\t\t\t\t|| (pData.HasAbility(PBEAbility.Guts) && pData.HasType(PBEType.Normal) && movePool.Contains(PBEMove.Facade))\n\t\t\t\t\t\t|| (pData.HasAbility(PBEAbility.SlowStart) && movePool.Contains(PBEMove.Substitute))\n\t\t\t\t\t\t|| (counter.Recovery == 0 && counter.SetupCategory == 'N' && !moves.Contains(PBEMove.HealingWish) && (\n\t\t\t\t\t\tmovePool.Contains(PBEMove.Recover) || movePool.Contains(PBEMove.Roost) || movePool.Contains(PBEMove.Softboiled)\n\t\t\t\t\t\t) && (counter[PBEMoveCategory.Status] > 1)))\n\t\t\t\t\t\t))\n\t\t\t\t\t{\n\t\t\t\t\t\t// Reject Status or non-STAB\n\t\t\t\t\t\tif (!isSetup && !mData.IsWeatherMove() /*&& !move.damage*/ && (mData.Category != PBEMoveCategory.Status || !mData.IsHPRestoreMove()) && move != PBEMove.Judgment && move != PBEMove.SleepTalk)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (mData.Category == PBEMoveCategory.Status || !pData.HasType(mData.Type) /*|| move.selfSwitch*/ || (mData.Power > 0 && mData.Power < 40 && !mData.IsMultiHitMove()))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treject = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Sleep Talk shouldn't be selected without Rest\n\t\t\t\tif (move == PBEMove.Rest && reject)\n\t\t\t\t{\n\t\t\t\t\tint sleeptalk = moves.IndexOf(PBEMove.SleepTalk);\n\t\t\t\t\tif (sleeptalk >= 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (movePool.Count < 2)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treject = false;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmovePool.RemoveAt(sleeptalk);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Remove rejected moves from the move list\n\t\t\t\tif (reject)\n\t\t\t\t{\n\t\t\t\t\t// Let's say our movePool originally has 4 moves and we reject three, we have one move and three are looping in rejectedPool forever.\n\t\t\t\t\t// This if prevents them from looping through rejectedPool\n\t\t\t\t\tif (movePool.Count > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\trejectedPool.Add(move);\n\t\t\t\t\t}\n\t\t\t\t\tmoves.RemoveAt(i);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t// TODO: Hidden power IVs and types\n\t\t\t}\n\t\t} while (moves.Count < PBESettings.DefaultNumMoves);\n\n\t\treturn moves;\n\t}\n\n\tprivate static Counter QueryMoves(IPBEDDPokemonDataExtended pData, List<PBEMove> moves, List<PBEMove> movePool)\n\t{\n\t\tvar counter = new Counter();\n\n\t\tif (moves.Count == 0)\n\t\t{\n\t\t\treturn counter;\n\t\t}\n\n\t\t// Iterate through all moves we've chosen so far and keep track of what they do:\n\t\tforeach (PBEMove move in moves)\n\t\t{\n\t\t\tIPBEMoveData mData = PBEDataProvider.Instance.GetMoveData(move);\n\t\t\tPBEType moveType = move == PBEMove.Judgment ? pData.Type1 : mData.Type;\n\n\t\t\t// Moves that do a set amount of damage:\n\t\t\tif (mData.IsSetDamageMove())\n\t\t\t{\n\t\t\t\tcounter.Damage++;\n\t\t\t\tcounter.DamagingMoves++;\n\t\t\t}\n\t\t\telse // Are Physical/Special/Status moves:\n\t\t\t{\n\t\t\t\tcounter[mData.Category]++;\n\t\t\t}\n\n\t\t\t// Moves that have a low base power:\n\t\t\tif (move == PBEMove.LowKick || (mData.Power > 0 && mData.Power <= 60 && move != PBEMove.RapidSpin))\n\t\t\t{\n\t\t\t\tcounter[PBEAbility.Technician]++;\n\t\t\t}\n\t\t\t// Moves that hit up to 5 times: (KERMALIS: Also TripleKick)\n\t\t\tif (mData.IsMultiHitMove())\n\t\t\t{\n\t\t\t\tcounter[PBEAbility.SkillLink]++;\n\t\t\t}\n\t\t\tif (mData.IsRecoilMove())\n\t\t\t{\n\t\t\t\tcounter.Recoil++;\n\t\t\t}\n\t\t\tif (mData.IsHPDrainMove())\n\t\t\t{\n\t\t\t\tcounter.Drain++;\n\t\t\t}\n\t\t\t// Moves which have a base power, but aren't super-weak like RapidSpin\n\t\t\tif (move == PBEMove.NaturePower || mData.Power > 30 || mData.IsMultiHitMove()/* || mData.basePowerCallback*/)\n\t\t\t{\n\t\t\t\tcounter[moveType]++;\n\t\t\t\tif (pData.HasType(moveType) || pData.Abilities.Contains(PBEAbility.Normalize))\n\t\t\t\t{\n\t\t\t\t\tcounter[PBEAbility.Adaptability]++;\n\t\t\t\t\t// STAB:\n\t\t\t\t\t// Certain moves aren't acceptable as a Pokémon's only STAB attack\n\t\t\t\t\tif (!_noSTABMoves.Contains(move) && (move != PBEMove.HiddenPower || pData.Type2 == PBEType.None))\n\t\t\t\t\t{\n\t\t\t\t\t\tcounter.STAB++;\n\t\t\t\t\t\t// Ties between Physical and Special setup should broken in favor of STABs\n\t\t\t\t\t\tcounter[mData.Category] += 0.1f;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (mData.Flags.HasFlag(PBEMoveFlag.AffectedByIronFist))\n\t\t\t\t{\n\t\t\t\t\tcounter[PBEAbility.IronFist]++;\n\t\t\t\t}\n\t\t\t\tif (mData.Flags.HasFlag(PBEMoveFlag.AffectedBySoundproof))\n\t\t\t\t{\n\t\t\t\t\tcounter.Sound++;\n\t\t\t\t}\n\t\t\t\tcounter.DamagingMoves++;\n\t\t\t}\n\t\t\t// Moves with secondary effects:\n\t\t\tif (mData.HasSecondaryEffects(PBESettings.DefaultSettings))\n\t\t\t{\n\t\t\t\tcounter[PBEAbility.SheerForce]++;\n\t\t\t\tif (mData.EffectParam >= 20 && mData.EffectParam < 100)\n\t\t\t\t{\n\t\t\t\t\tcounter[PBEAbility.SereneGrace]++;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Moves with low accuracy:\n\t\t\tif (mData.Accuracy != 0 && mData.Accuracy < 90)\n\t\t\t{\n\t\t\t\tcounter.Inaccurate++;\n\t\t\t}\n\t\t\t// Moves with non-zero priority:\n\t\t\tif (mData.Category != PBEMoveCategory.Status && mData.Priority != 0)\n\t\t\t{\n\t\t\t\tcounter.Priority++;\n\t\t\t}\n\t\t\t// Moves that change stats:\n\t\t\tif (_recoveryMoves.Contains(move))\n\t\t\t{\n\t\t\t\tcounter.Recovery++;\n\t\t\t}\n\t\t\tif (_contraryMoves.Contains(move))\n\t\t\t{\n\t\t\t\tcounter[PBEAbility.Contrary]++;\n\t\t\t}\n\t\t\tif (_physicalSetupMoves.Contains(move))\n\t\t\t{\n\t\t\t\tcounter.PhysicalSetup++;\n\t\t\t\tcounter.SetupCategory = 'P';\n\t\t\t}\n\t\t\telse if (_specialSetupMoves.Contains(move))\n\t\t\t{\n\t\t\t\tcounter.SpecialSetup++;\n\t\t\t\tcounter.SetupCategory = 'S';\n\t\t\t}\n\t\t\tif (_mixedSetupMoves.Contains(move))\n\t\t\t{\n\t\t\t\tcounter.MixedSetup++;\n\t\t\t}\n\t\t\tif (_speedSetupMoves.Contains(move))\n\t\t\t{\n\t\t\t\tcounter.SpeedSetup++;\n\t\t\t}\n\t\t\tif (_hazardMoves.Contains(move))\n\t\t\t{\n\t\t\t\tcounter.Hazards++;\n\t\t\t}\n\t\t}\n\n\t\t// Keep track of available moves\n\t\tforeach (PBEMove move in movePool)\n\t\t{\n\t\t\tIPBEMoveData mData = PBEDataProvider.Instance.GetMoveData(move);\n\t\t\tif (!mData.IsSetDamageMove())\n\t\t\t{\n\t\t\t\tif (mData.Category == PBEMoveCategory.Physical)\n\t\t\t\t{\n\t\t\t\t\tcounter.PhysicalPool++;\n\t\t\t\t}\n\t\t\t\telse if (mData.Category == PBEMoveCategory.Special)\n\t\t\t\t{\n\t\t\t\t\tcounter.SpecialPool++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Choose a setup type:\n\t\tif (counter.MixedSetup > 0)\n\t\t{\n\t\t\tcounter.SetupCategory = 'M';\n\t\t}\n\t\telse if (counter.PhysicalSetup > 0 && counter.SpecialSetup > 0)\n\t\t{\n\t\t\tfloat physical = counter[PBEMoveCategory.Physical] + counter.PhysicalPool;\n\t\t\tfloat special = counter[PBEMoveCategory.Special] + counter.SpecialPool;\n\t\t\tif (physical == special)\n\t\t\t{\n\t\t\t\tif (counter[PBEMoveCategory.Physical] > counter[PBEMoveCategory.Special])\n\t\t\t\t{\n\t\t\t\t\tcounter.SetupCategory = 'P';\n\t\t\t\t}\n\t\t\t\tif (counter[PBEMoveCategory.Special] > counter[PBEMoveCategory.Physical])\n\t\t\t\t{\n\t\t\t\t\tcounter.SetupCategory = 'S';\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tcounter.SetupCategory = physical > special ? 'P' : 'S';\n\t\t\t}\n\t\t}\n\t\telse if (counter.SetupCategory == 'P')\n\t\t{\n\t\t\tif (counter[PBEMoveCategory.Physical] < 2 && (counter.STAB == 0 || counter.PhysicalPool == 0) && (!moves.Contains(PBEMove.Rest) || !moves.Contains(PBEMove.SleepTalk)))\n\t\t\t{\n\t\t\t\tcounter.SetupCategory = 'N';\n\t\t\t}\n\t\t}\n\t\telse if (counter.SetupCategory == 'S')\n\t\t{\n\t\t\tif (counter[PBEMoveCategory.Special] < 2 && (counter.STAB == 0 || counter.SpecialPool == 0) && (!moves.Contains(PBEMove.Rest) || !moves.Contains(PBEMove.SleepTalk)))\n\t\t\t{\n\t\t\t\tcounter.SetupCategory = 'N';\n\t\t\t}\n\t\t}\n\n\t\tcounter[PBEMoveCategory.Physical] = (float)Math.Floor(counter[PBEMoveCategory.Physical]);\n\t\tcounter[PBEMoveCategory.Special] = (float)Math.Floor(counter[PBEMoveCategory.Special]);\n\n\t\treturn counter;\n\t}\n\n\t// TODO: Hidden power types\n\tprivate static void GetRandomSet(PBESpecies species, PBEForm form, IPBEDDPokemonDataExtended pData, bool isLead, TeamDetails teamDs, PBELegalPokemon pkmn)\n\t{\n\t\tpkmn.EffortValues.Equalize();\n\t\tList<PBEMove> moves = GetMoves(species, form, pData, isLead, teamDs, out Counter counter);\n\t\t// If Hidden Power has been removed, reset the IVs\n\t\tif (!moves.Contains(PBEMove.HiddenPower))\n\t\t{\n\t\t\tpkmn.IndividualValues.Maximize();\n\t\t}\n\n\t\tif (pkmn.SelectableAbilities.Count > 1)\n\t\t{\n\t\t\tPBEAbility a = GetAbility(species, moves, pData, counter, teamDs);\n\t\t\tif (a != PBEAbility.None)\n\t\t\t{\n\t\t\t\tpkmn.Ability = a;\n\t\t\t}\n\t\t}\n\t\tif (pkmn.SelectableItems.Count > 1)\n\t\t{\n\t\t\tpkmn.Item = GetItem(species, form, pkmn.Ability, moves, pData, counter, isLead);\n\t\t}\n\n\t\t// KERMALIS: This is where showdown does level scaling\n\n\t\t// Minimize confusion damage\n\t\tif (counter[PBEMoveCategory.Physical] == 0 && !moves.Contains(PBEMove.Transform))\n\t\t{\n\t\t\tpkmn.EffortValues.Attack = 0;\n\t\t\tpkmn.IndividualValues.Attack = 0;\n\t\t}\n\t\tif (moves.Contains(PBEMove.GyroBall) || moves.Contains(PBEMove.MetalBurst) || moves.Contains(PBEMove.TrickRoom))\n\t\t{\n\t\t\tpkmn.EffortValues.Speed = 0;\n\t\t\tpkmn.IndividualValues.Speed = 0;\n\t\t}\n\t\tpkmn.Friendship = moves.Contains(PBEMove.Frustration) ? byte.MinValue : byte.MaxValue;\n\t\tfor (int i = 0; i < moves.Count; i++)\n\t\t{\n\t\t\tPBELegalMoveset.PBELegalMovesetSlot slot = pkmn.Moveset[i];\n\t\t\tslot.Move = moves[i];\n\t\t\tif (slot.IsPPUpsEditable)\n\t\t\t{\n\t\t\t\tslot.PPUps = PBESettings.DefaultMaxPPUps;\n\t\t\t}\n\t\t}\n\t}\n\n\t/// <summary>Creates a random team meant for <see cref=\"PBESettings.DefaultSettings\"/>.</summary>\n\t/// <param name=\"numPkmn\">The amount of Pokémon to create in the team./></param>\n\tpublic static PBELegalPokemonCollection CreateRandomTeam(int numPkmn)\n\t{\n\t\treturn CreateRandomTeam(numPkmn, PBEDataUtils.FullyEvolvedSpecies);\n\t}\n\t// TODO: Move Illusion out of the way instead of just yeeting it if it's last\n\t// TODO: Tiers?, level scaling, limit one type combination\n\t// TODO: Hidden Power types\n\t// TODO: Custom settings\n\t// TODO: Non-competitive moves (such as MegaDrain/GigaDrain together, HelpingHand), ban certain species (such as Wobbuffet & Unown, but add certain pre-evolutions such as Pikachu)\n\t// TODO: Prioritize correct attack stat for species, currently will give special moves to a physical attacker, etc\n\t/// <summary>Creates a random team meant for <see cref=\"PBESettings.DefaultSettings\"/>.</summary>\n\t/// <param name=\"numPkmn\">The amount of Pokémon to create in the team./></param>\n\t/// <param name=\"allowedSpecies\">The allowed species to consider.</param>\n\tpublic static PBELegalPokemonCollection CreateRandomTeam(int numPkmn, IEnumerable<PBESpecies> allowedSpecies)\n\t{\n\t\tif (numPkmn < 1 || numPkmn > PBESettings.DefaultMaxPartySize)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(numPkmn));\n\t\t}\n\n\t\tint maxShared = numPkmn / 3;\n\t\tvar speciesPool = new List<PBESpecies>(allowedSpecies);\n\t\tvar teamDs = new TeamDetails();\n\t\tvar usedTypes = new Dictionary<PBEType, int>(numPkmn - 1);\n\t\tvar team = new PBELegalPokemonCollection(PBESettings.DefaultSettings);\n\t\tint currentIndex = 0;\n\t\twhile (speciesPool.Count > 0)\n\t\t{\n\t\t\t(PBESpecies species, PBEForm form) = PBEDataProvider.GlobalRandom.RandomSpecies(speciesPool, true);\n\t\t\tspeciesPool.Remove(species);\n\t\t\t// KERMALIS: Showdown limits {maxShared} per tier\n\t\t\tIPBEDDPokemonDataExtended pData = PBEDefaultDataProvider.Instance.GetPokemonDataExtended(species, form);\n\t\t\tif (ShouldDenyType(usedTypes, pData.Type1, maxShared) || ShouldDenyType(usedTypes, pData.Type2, maxShared))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tvar pkmn = new PBELegalPokemon(species, form, PBESettings.DefaultMaxLevel, PBEDataProvider.Instance.GetEXPRequired(pData.GrowthRate, PBESettings.DefaultMaxLevel), PBESettings.DefaultSettings);\n\t\t\tGetRandomSet(species, form, pData, currentIndex == 0, teamDs, pkmn);\n\n\t\t\t// Illusion shouldn't be the last Pokémon of the team\n\t\t\tif (pkmn.Ability == PBEAbility.Illusion && currentIndex == numPkmn - 1)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// KERMALIS: Showdown limits one type combination, excluding weather ability users\n\n\t\t\t// KERMALIS: Showdown sets Illusion user's level to the level of the last in the party\n\n\t\t\t// Now that our Pokémon has passed all checks, we can increment our counters\n\t\t\tteam.Add(pkmn);\n\t\t\tif (team.Count >= numPkmn || speciesPool.Count == 0)\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcurrentIndex++;\n\t\t\t// Increment our tier counter\n\n\t\t\t// Increment type counters\n\t\t\tAddTypeToDict(usedTypes, pData.Type1);\n\t\t\tAddTypeToDict(usedTypes, pData.Type2);\n\n\t\t\t// Team details\n\t\t\tif (pkmn.Ability == PBEAbility.SnowWarning || pkmn.Moveset.Contains(PBEMove.Hail))\n\t\t\t{\n\t\t\t\tteamDs.Hail = true;\n\t\t\t}\n\t\t\tif (pkmn.Ability == PBEAbility.Drizzle || pkmn.Moveset.Contains(PBEMove.RainDance))\n\t\t\t{\n\t\t\t\tteamDs.Rain = true;\n\t\t\t}\n\t\t\tif (pkmn.Ability == PBEAbility.SandStream || pkmn.Moveset.Contains(PBEMove.Sandstorm))\n\t\t\t{\n\t\t\t\tteamDs.Sandstorm = true;\n\t\t\t}\n\t\t\tif (pkmn.Ability == PBEAbility.Drought || pkmn.Moveset.Contains(PBEMove.SunnyDay))\n\t\t\t{\n\t\t\t\tteamDs.HarshSunlight = true;\n\t\t\t}\n\t\t\tif (pkmn.Moveset.Contains(PBEMove.StealthRock))\n\t\t\t{\n\t\t\t\tteamDs.StealthRock = true;\n\t\t\t}\n\t\t\tif (pkmn.Moveset.Contains(PBEMove.ToxicSpikes))\n\t\t\t{\n\t\t\t\tteamDs.ToxicSpikes = true;\n\t\t\t}\n\t\t\tif (pkmn.Moveset.Contains(PBEMove.RapidSpin))\n\t\t\t{\n\t\t\t\tteamDs.RapidSpin = true;\n\t\t\t}\n\t\t}\n\n\t\tif (team.Count < numPkmn)\n\t\t{\n\t\t\tthrow new Exception(\"Failed to create a random team\");\n\t\t}\n\t\treturn team;\n\t}\n\n\tprivate static void AddTypeToDict(Dictionary<PBEType, int> dict, PBEType type)\n\t{\n\t\tif (type != PBEType.None)\n\t\t{\n\t\t\tif (dict.ContainsKey(type))\n\t\t\t{\n\t\t\t\tdict[type]++;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tdict.Add(type, 1);\n\t\t\t}\n\t\t}\n\t}\n\tprivate static bool ShouldDenyType(Dictionary<PBEType, int> dict, PBEType type, int maxShared)\n\t{\n\t\treturn type != PBEType.None && dict.TryGetValue(type, out int value) && value >= maxShared;\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngine.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 16\nVisualStudioVersion = 16.0.29001.49\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"PokemonBattleEngine\", \"PokemonBattleEngine\\PokemonBattleEngine.csproj\", \"{AC469C92-3871-41DD-AA00-D3E36A5BDA2F}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"PokemonBattleEngineServer\", \"PokemonBattleEngineServer\\PokemonBattleEngineServer.csproj\", \"{BE8F0292-CE0E-4023-984A-17A4016518FA}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"PokemonBattleEngineClient\", \"PokemonBattleEngineClient\\PokemonBattleEngineClient.csproj\", \"{BFADDEA8-741D-439C-82DF-849CB665EF84}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"PokemonBattleEngineDiscord\", \"PokemonBattleEngineDiscord\\PokemonBattleEngineDiscord.csproj\", \"{CCBE5C1D-7C76-4774-B4D1-34659A5C856B}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"PokemonBattleEngineExtras\", \"PokemonBattleEngineExtras\\PokemonBattleEngineExtras.csproj\", \"{31B95673-DFD2-4613-BAD6-7892CB29F4B9}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"PokemonBattleEngineClient.Desktop\", \"PokemonBattleEngineClient.Desktop\\PokemonBattleEngineClient.Desktop.csproj\", \"{CEDCFABE-658C-42AB-BCE2-A96A7B17410E}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"PokemonBattleEngineTests\", \"PokemonBattleEngineTests\\PokemonBattleEngineTests.csproj\", \"{2B15A32D-C213-4C60-981A-350E1569E5DA}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"PokemonBattleEngine.DefaultData\", \"PokemonBattleEngine.DefaultData\\PokemonBattleEngine.DefaultData.csproj\", \"{D49BA669-1AE2-4779-922A-D5C1FF21C2DE}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tAd-Hoc|Any CPU = Ad-Hoc|Any CPU\n\t\tAd-Hoc|iPhone = Ad-Hoc|iPhone\n\t\tAd-Hoc|iPhoneSimulator = Ad-Hoc|iPhoneSimulator\n\t\tAppStore|Any CPU = AppStore|Any CPU\n\t\tAppStore|iPhone = AppStore|iPhone\n\t\tAppStore|iPhoneSimulator = AppStore|iPhoneSimulator\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tDebug|iPhone = Debug|iPhone\n\t\tDebug|iPhoneSimulator = Debug|iPhoneSimulator\n\t\tRelease|Any CPU = Release|Any CPU\n\t\tRelease|iPhone = Release|iPhone\n\t\tRelease|iPhoneSimulator = Release|iPhoneSimulator\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{AC469C92-3871-41DD-AA00-D3E36A5BDA2F}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{AC469C92-3871-41DD-AA00-D3E36A5BDA2F}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU\n\t\t{AC469C92-3871-41DD-AA00-D3E36A5BDA2F}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU\n\t\t{AC469C92-3871-41DD-AA00-D3E36A5BDA2F}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU\n\t\t{AC469C92-3871-41DD-AA00-D3E36A5BDA2F}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU\n\t\t{AC469C92-3871-41DD-AA00-D3E36A5BDA2F}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU\n\t\t{AC469C92-3871-41DD-AA00-D3E36A5BDA2F}.AppStore|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{AC469C92-3871-41DD-AA00-D3E36A5BDA2F}.AppStore|Any CPU.Build.0 = Release|Any CPU\n\t\t{AC469C92-3871-41DD-AA00-D3E36A5BDA2F}.AppStore|iPhone.ActiveCfg = Release|Any CPU\n\t\t{AC469C92-3871-41DD-AA00-D3E36A5BDA2F}.AppStore|iPhone.Build.0 = Release|Any CPU\n\t\t{AC469C92-3871-41DD-AA00-D3E36A5BDA2F}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU\n\t\t{AC469C92-3871-41DD-AA00-D3E36A5BDA2F}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU\n\t\t{AC469C92-3871-41DD-AA00-D3E36A5BDA2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{AC469C92-3871-41DD-AA00-D3E36A5BDA2F}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{AC469C92-3871-41DD-AA00-D3E36A5BDA2F}.Debug|iPhone.ActiveCfg = Debug|Any CPU\n\t\t{AC469C92-3871-41DD-AA00-D3E36A5BDA2F}.Debug|iPhone.Build.0 = Debug|Any CPU\n\t\t{AC469C92-3871-41DD-AA00-D3E36A5BDA2F}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU\n\t\t{AC469C92-3871-41DD-AA00-D3E36A5BDA2F}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU\n\t\t{AC469C92-3871-41DD-AA00-D3E36A5BDA2F}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{AC469C92-3871-41DD-AA00-D3E36A5BDA2F}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{AC469C92-3871-41DD-AA00-D3E36A5BDA2F}.Release|iPhone.ActiveCfg = Release|Any CPU\n\t\t{AC469C92-3871-41DD-AA00-D3E36A5BDA2F}.Release|iPhone.Build.0 = Release|Any CPU\n\t\t{AC469C92-3871-41DD-AA00-D3E36A5BDA2F}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU\n\t\t{AC469C92-3871-41DD-AA00-D3E36A5BDA2F}.Release|iPhoneSimulator.Build.0 = Release|Any CPU\n\t\t{BE8F0292-CE0E-4023-984A-17A4016518FA}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{BE8F0292-CE0E-4023-984A-17A4016518FA}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU\n\t\t{BE8F0292-CE0E-4023-984A-17A4016518FA}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU\n\t\t{BE8F0292-CE0E-4023-984A-17A4016518FA}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU\n\t\t{BE8F0292-CE0E-4023-984A-17A4016518FA}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU\n\t\t{BE8F0292-CE0E-4023-984A-17A4016518FA}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU\n\t\t{BE8F0292-CE0E-4023-984A-17A4016518FA}.AppStore|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{BE8F0292-CE0E-4023-984A-17A4016518FA}.AppStore|Any CPU.Build.0 = Release|Any CPU\n\t\t{BE8F0292-CE0E-4023-984A-17A4016518FA}.AppStore|iPhone.ActiveCfg = Release|Any CPU\n\t\t{BE8F0292-CE0E-4023-984A-17A4016518FA}.AppStore|iPhone.Build.0 = Release|Any CPU\n\t\t{BE8F0292-CE0E-4023-984A-17A4016518FA}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU\n\t\t{BE8F0292-CE0E-4023-984A-17A4016518FA}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU\n\t\t{BE8F0292-CE0E-4023-984A-17A4016518FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{BE8F0292-CE0E-4023-984A-17A4016518FA}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{BE8F0292-CE0E-4023-984A-17A4016518FA}.Debug|iPhone.ActiveCfg = Debug|Any CPU\n\t\t{BE8F0292-CE0E-4023-984A-17A4016518FA}.Debug|iPhone.Build.0 = Debug|Any CPU\n\t\t{BE8F0292-CE0E-4023-984A-17A4016518FA}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU\n\t\t{BE8F0292-CE0E-4023-984A-17A4016518FA}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU\n\t\t{BE8F0292-CE0E-4023-984A-17A4016518FA}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{BE8F0292-CE0E-4023-984A-17A4016518FA}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{BE8F0292-CE0E-4023-984A-17A4016518FA}.Release|iPhone.ActiveCfg = Release|Any CPU\n\t\t{BE8F0292-CE0E-4023-984A-17A4016518FA}.Release|iPhone.Build.0 = Release|Any CPU\n\t\t{BE8F0292-CE0E-4023-984A-17A4016518FA}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU\n\t\t{BE8F0292-CE0E-4023-984A-17A4016518FA}.Release|iPhoneSimulator.Build.0 = Release|Any CPU\n\t\t{BFADDEA8-741D-439C-82DF-849CB665EF84}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{BFADDEA8-741D-439C-82DF-849CB665EF84}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU\n\t\t{BFADDEA8-741D-439C-82DF-849CB665EF84}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU\n\t\t{BFADDEA8-741D-439C-82DF-849CB665EF84}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU\n\t\t{BFADDEA8-741D-439C-82DF-849CB665EF84}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU\n\t\t{BFADDEA8-741D-439C-82DF-849CB665EF84}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU\n\t\t{BFADDEA8-741D-439C-82DF-849CB665EF84}.AppStore|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{BFADDEA8-741D-439C-82DF-849CB665EF84}.AppStore|Any CPU.Build.0 = Release|Any CPU\n\t\t{BFADDEA8-741D-439C-82DF-849CB665EF84}.AppStore|iPhone.ActiveCfg = Release|Any CPU\n\t\t{BFADDEA8-741D-439C-82DF-849CB665EF84}.AppStore|iPhone.Build.0 = Release|Any CPU\n\t\t{BFADDEA8-741D-439C-82DF-849CB665EF84}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU\n\t\t{BFADDEA8-741D-439C-82DF-849CB665EF84}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU\n\t\t{BFADDEA8-741D-439C-82DF-849CB665EF84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{BFADDEA8-741D-439C-82DF-849CB665EF84}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{BFADDEA8-741D-439C-82DF-849CB665EF84}.Debug|iPhone.ActiveCfg = Debug|Any CPU\n\t\t{BFADDEA8-741D-439C-82DF-849CB665EF84}.Debug|iPhone.Build.0 = Debug|Any CPU\n\t\t{BFADDEA8-741D-439C-82DF-849CB665EF84}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU\n\t\t{BFADDEA8-741D-439C-82DF-849CB665EF84}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU\n\t\t{BFADDEA8-741D-439C-82DF-849CB665EF84}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{BFADDEA8-741D-439C-82DF-849CB665EF84}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{BFADDEA8-741D-439C-82DF-849CB665EF84}.Release|iPhone.ActiveCfg = Release|Any CPU\n\t\t{BFADDEA8-741D-439C-82DF-849CB665EF84}.Release|iPhone.Build.0 = Release|Any CPU\n\t\t{BFADDEA8-741D-439C-82DF-849CB665EF84}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU\n\t\t{BFADDEA8-741D-439C-82DF-849CB665EF84}.Release|iPhoneSimulator.Build.0 = Release|Any CPU\n\t\t{CCBE5C1D-7C76-4774-B4D1-34659A5C856B}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{CCBE5C1D-7C76-4774-B4D1-34659A5C856B}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU\n\t\t{CCBE5C1D-7C76-4774-B4D1-34659A5C856B}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU\n\t\t{CCBE5C1D-7C76-4774-B4D1-34659A5C856B}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU\n\t\t{CCBE5C1D-7C76-4774-B4D1-34659A5C856B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU\n\t\t{CCBE5C1D-7C76-4774-B4D1-34659A5C856B}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU\n\t\t{CCBE5C1D-7C76-4774-B4D1-34659A5C856B}.AppStore|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{CCBE5C1D-7C76-4774-B4D1-34659A5C856B}.AppStore|Any CPU.Build.0 = Release|Any CPU\n\t\t{CCBE5C1D-7C76-4774-B4D1-34659A5C856B}.AppStore|iPhone.ActiveCfg = Release|Any CPU\n\t\t{CCBE5C1D-7C76-4774-B4D1-34659A5C856B}.AppStore|iPhone.Build.0 = Release|Any CPU\n\t\t{CCBE5C1D-7C76-4774-B4D1-34659A5C856B}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU\n\t\t{CCBE5C1D-7C76-4774-B4D1-34659A5C856B}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU\n\t\t{CCBE5C1D-7C76-4774-B4D1-34659A5C856B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{CCBE5C1D-7C76-4774-B4D1-34659A5C856B}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{CCBE5C1D-7C76-4774-B4D1-34659A5C856B}.Debug|iPhone.ActiveCfg = Debug|Any CPU\n\t\t{CCBE5C1D-7C76-4774-B4D1-34659A5C856B}.Debug|iPhone.Build.0 = Debug|Any CPU\n\t\t{CCBE5C1D-7C76-4774-B4D1-34659A5C856B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU\n\t\t{CCBE5C1D-7C76-4774-B4D1-34659A5C856B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU\n\t\t{CCBE5C1D-7C76-4774-B4D1-34659A5C856B}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{CCBE5C1D-7C76-4774-B4D1-34659A5C856B}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{CCBE5C1D-7C76-4774-B4D1-34659A5C856B}.Release|iPhone.ActiveCfg = Release|Any CPU\n\t\t{CCBE5C1D-7C76-4774-B4D1-34659A5C856B}.Release|iPhone.Build.0 = Release|Any CPU\n\t\t{CCBE5C1D-7C76-4774-B4D1-34659A5C856B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU\n\t\t{CCBE5C1D-7C76-4774-B4D1-34659A5C856B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU\n\t\t{31B95673-DFD2-4613-BAD6-7892CB29F4B9}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{31B95673-DFD2-4613-BAD6-7892CB29F4B9}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU\n\t\t{31B95673-DFD2-4613-BAD6-7892CB29F4B9}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU\n\t\t{31B95673-DFD2-4613-BAD6-7892CB29F4B9}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU\n\t\t{31B95673-DFD2-4613-BAD6-7892CB29F4B9}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU\n\t\t{31B95673-DFD2-4613-BAD6-7892CB29F4B9}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU\n\t\t{31B95673-DFD2-4613-BAD6-7892CB29F4B9}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{31B95673-DFD2-4613-BAD6-7892CB29F4B9}.AppStore|Any CPU.Build.0 = Debug|Any CPU\n\t\t{31B95673-DFD2-4613-BAD6-7892CB29F4B9}.AppStore|iPhone.ActiveCfg = Debug|Any CPU\n\t\t{31B95673-DFD2-4613-BAD6-7892CB29F4B9}.AppStore|iPhone.Build.0 = Debug|Any CPU\n\t\t{31B95673-DFD2-4613-BAD6-7892CB29F4B9}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU\n\t\t{31B95673-DFD2-4613-BAD6-7892CB29F4B9}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU\n\t\t{31B95673-DFD2-4613-BAD6-7892CB29F4B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{31B95673-DFD2-4613-BAD6-7892CB29F4B9}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{31B95673-DFD2-4613-BAD6-7892CB29F4B9}.Debug|iPhone.ActiveCfg = Debug|Any CPU\n\t\t{31B95673-DFD2-4613-BAD6-7892CB29F4B9}.Debug|iPhone.Build.0 = Debug|Any CPU\n\t\t{31B95673-DFD2-4613-BAD6-7892CB29F4B9}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU\n\t\t{31B95673-DFD2-4613-BAD6-7892CB29F4B9}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU\n\t\t{31B95673-DFD2-4613-BAD6-7892CB29F4B9}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{31B95673-DFD2-4613-BAD6-7892CB29F4B9}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{31B95673-DFD2-4613-BAD6-7892CB29F4B9}.Release|iPhone.ActiveCfg = Debug|Any CPU\n\t\t{31B95673-DFD2-4613-BAD6-7892CB29F4B9}.Release|iPhone.Build.0 = Debug|Any CPU\n\t\t{31B95673-DFD2-4613-BAD6-7892CB29F4B9}.Release|iPhoneSimulator.ActiveCfg = Debug|Any CPU\n\t\t{31B95673-DFD2-4613-BAD6-7892CB29F4B9}.Release|iPhoneSimulator.Build.0 = Debug|Any CPU\n\t\t{CEDCFABE-658C-42AB-BCE2-A96A7B17410E}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{CEDCFABE-658C-42AB-BCE2-A96A7B17410E}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU\n\t\t{CEDCFABE-658C-42AB-BCE2-A96A7B17410E}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU\n\t\t{CEDCFABE-658C-42AB-BCE2-A96A7B17410E}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU\n\t\t{CEDCFABE-658C-42AB-BCE2-A96A7B17410E}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU\n\t\t{CEDCFABE-658C-42AB-BCE2-A96A7B17410E}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU\n\t\t{CEDCFABE-658C-42AB-BCE2-A96A7B17410E}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{CEDCFABE-658C-42AB-BCE2-A96A7B17410E}.AppStore|Any CPU.Build.0 = Debug|Any CPU\n\t\t{CEDCFABE-658C-42AB-BCE2-A96A7B17410E}.AppStore|iPhone.ActiveCfg = Debug|Any CPU\n\t\t{CEDCFABE-658C-42AB-BCE2-A96A7B17410E}.AppStore|iPhone.Build.0 = Debug|Any CPU\n\t\t{CEDCFABE-658C-42AB-BCE2-A96A7B17410E}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU\n\t\t{CEDCFABE-658C-42AB-BCE2-A96A7B17410E}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU\n\t\t{CEDCFABE-658C-42AB-BCE2-A96A7B17410E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{CEDCFABE-658C-42AB-BCE2-A96A7B17410E}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{CEDCFABE-658C-42AB-BCE2-A96A7B17410E}.Debug|iPhone.ActiveCfg = Debug|Any CPU\n\t\t{CEDCFABE-658C-42AB-BCE2-A96A7B17410E}.Debug|iPhone.Build.0 = Debug|Any CPU\n\t\t{CEDCFABE-658C-42AB-BCE2-A96A7B17410E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU\n\t\t{CEDCFABE-658C-42AB-BCE2-A96A7B17410E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU\n\t\t{CEDCFABE-658C-42AB-BCE2-A96A7B17410E}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{CEDCFABE-658C-42AB-BCE2-A96A7B17410E}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{CEDCFABE-658C-42AB-BCE2-A96A7B17410E}.Release|iPhone.ActiveCfg = Release|Any CPU\n\t\t{CEDCFABE-658C-42AB-BCE2-A96A7B17410E}.Release|iPhone.Build.0 = Release|Any CPU\n\t\t{CEDCFABE-658C-42AB-BCE2-A96A7B17410E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU\n\t\t{CEDCFABE-658C-42AB-BCE2-A96A7B17410E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU\n\t\t{2B15A32D-C213-4C60-981A-350E1569E5DA}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{2B15A32D-C213-4C60-981A-350E1569E5DA}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU\n\t\t{2B15A32D-C213-4C60-981A-350E1569E5DA}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU\n\t\t{2B15A32D-C213-4C60-981A-350E1569E5DA}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU\n\t\t{2B15A32D-C213-4C60-981A-350E1569E5DA}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU\n\t\t{2B15A32D-C213-4C60-981A-350E1569E5DA}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU\n\t\t{2B15A32D-C213-4C60-981A-350E1569E5DA}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{2B15A32D-C213-4C60-981A-350E1569E5DA}.AppStore|Any CPU.Build.0 = Debug|Any CPU\n\t\t{2B15A32D-C213-4C60-981A-350E1569E5DA}.AppStore|iPhone.ActiveCfg = Debug|Any CPU\n\t\t{2B15A32D-C213-4C60-981A-350E1569E5DA}.AppStore|iPhone.Build.0 = Debug|Any CPU\n\t\t{2B15A32D-C213-4C60-981A-350E1569E5DA}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU\n\t\t{2B15A32D-C213-4C60-981A-350E1569E5DA}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU\n\t\t{2B15A32D-C213-4C60-981A-350E1569E5DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{2B15A32D-C213-4C60-981A-350E1569E5DA}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{2B15A32D-C213-4C60-981A-350E1569E5DA}.Debug|iPhone.ActiveCfg = Debug|Any CPU\n\t\t{2B15A32D-C213-4C60-981A-350E1569E5DA}.Debug|iPhone.Build.0 = Debug|Any CPU\n\t\t{2B15A32D-C213-4C60-981A-350E1569E5DA}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU\n\t\t{2B15A32D-C213-4C60-981A-350E1569E5DA}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU\n\t\t{2B15A32D-C213-4C60-981A-350E1569E5DA}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{2B15A32D-C213-4C60-981A-350E1569E5DA}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{2B15A32D-C213-4C60-981A-350E1569E5DA}.Release|iPhone.ActiveCfg = Release|Any CPU\n\t\t{2B15A32D-C213-4C60-981A-350E1569E5DA}.Release|iPhone.Build.0 = Release|Any CPU\n\t\t{2B15A32D-C213-4C60-981A-350E1569E5DA}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU\n\t\t{2B15A32D-C213-4C60-981A-350E1569E5DA}.Release|iPhoneSimulator.Build.0 = Release|Any CPU\n\t\t{D49BA669-1AE2-4779-922A-D5C1FF21C2DE}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{D49BA669-1AE2-4779-922A-D5C1FF21C2DE}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU\n\t\t{D49BA669-1AE2-4779-922A-D5C1FF21C2DE}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU\n\t\t{D49BA669-1AE2-4779-922A-D5C1FF21C2DE}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU\n\t\t{D49BA669-1AE2-4779-922A-D5C1FF21C2DE}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU\n\t\t{D49BA669-1AE2-4779-922A-D5C1FF21C2DE}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU\n\t\t{D49BA669-1AE2-4779-922A-D5C1FF21C2DE}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{D49BA669-1AE2-4779-922A-D5C1FF21C2DE}.AppStore|Any CPU.Build.0 = Debug|Any CPU\n\t\t{D49BA669-1AE2-4779-922A-D5C1FF21C2DE}.AppStore|iPhone.ActiveCfg = Debug|Any CPU\n\t\t{D49BA669-1AE2-4779-922A-D5C1FF21C2DE}.AppStore|iPhone.Build.0 = Debug|Any CPU\n\t\t{D49BA669-1AE2-4779-922A-D5C1FF21C2DE}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU\n\t\t{D49BA669-1AE2-4779-922A-D5C1FF21C2DE}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU\n\t\t{D49BA669-1AE2-4779-922A-D5C1FF21C2DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{D49BA669-1AE2-4779-922A-D5C1FF21C2DE}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{D49BA669-1AE2-4779-922A-D5C1FF21C2DE}.Debug|iPhone.ActiveCfg = Debug|Any CPU\n\t\t{D49BA669-1AE2-4779-922A-D5C1FF21C2DE}.Debug|iPhone.Build.0 = Debug|Any CPU\n\t\t{D49BA669-1AE2-4779-922A-D5C1FF21C2DE}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU\n\t\t{D49BA669-1AE2-4779-922A-D5C1FF21C2DE}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU\n\t\t{D49BA669-1AE2-4779-922A-D5C1FF21C2DE}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{D49BA669-1AE2-4779-922A-D5C1FF21C2DE}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{D49BA669-1AE2-4779-922A-D5C1FF21C2DE}.Release|iPhone.ActiveCfg = Release|Any CPU\n\t\t{D49BA669-1AE2-4779-922A-D5C1FF21C2DE}.Release|iPhone.Build.0 = Release|Any CPU\n\t\t{D49BA669-1AE2-4779-922A-D5C1FF21C2DE}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU\n\t\t{D49BA669-1AE2-4779-922A-D5C1FF21C2DE}.Release|iPhoneSimulator.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {26C7F5BF-9040-4BC5-B85A-579E7061A93B}\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "PokemonBattleEngineClient/App.xaml",
    "content": "﻿<Application xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             x:Class=\"Kermalis.PokemonBattleEngineClient.App\">\n\t<Application.Styles>\n\t\t<StyleInclude Source=\"resm:Avalonia.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.Themes.Default\"/>\n\t\t<StyleInclude Source=\"resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default\"/>\n\t</Application.Styles>\n</Application>"
  },
  {
    "path": "PokemonBattleEngineClient/App.xaml.cs",
    "content": "﻿using Avalonia;\nusing Avalonia.Controls.ApplicationLifetimes;\nusing Avalonia.Markup.Xaml;\nusing Kermalis.PokemonBattleEngineClient.Models;\nusing Kermalis.PokemonBattleEngineClient.Views;\nusing System;\n\nnamespace Kermalis.PokemonBattleEngineClient;\n\npublic sealed class App : Application\n{\n\tpublic override void Initialize()\n\t{\n\t\tAvaloniaXamlLoader.Load(this);\n\t}\n\n\tpublic override void OnFrameworkInitializationCompleted()\n\t{\n\t\tMoveInfo.CreateBrushes();\n\t\tHPBarView.CreateResources();\n\t\tFieldView.CreateResources();\n\t\tswitch (ApplicationLifetime)\n\t\t{\n\t\t\tcase null: break;\n\t\t\tcase IClassicDesktopStyleApplicationLifetime desktop: desktop.MainWindow = new MainWindow(); break;\n\t\t\tcase ISingleViewApplicationLifetime singleView: singleView.MainView = new MainView(); break;\n\t\t\tdefault: throw new PlatformNotSupportedException();\n\t\t}\n\t\tbase.OnFrameworkInitializationCompleted();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineClient/Clients/ActionsBuilder.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngineClient.Views;\nusing System;\nusing System.Linq;\n\nnamespace Kermalis.PokemonBattleEngineClient.Clients;\n\ninternal sealed class ActionsBuilder\n{\n\tprivate readonly BattleView _bv;\n\tprivate Action<PBETurnAction[]> _onActionsReady;\n\n\tprivate int _index;\n\tprivate readonly PBEBattlePokemon[] _pkmn;\n\tprivate readonly PBETurnAction[] _actions;\n\tprivate readonly PBEBattlePokemon?[] _standBy;\n\n\tpublic ActionsBuilder(BattleView bv, PBETrainer trainer, Action<PBETurnAction[]> onActionsReady)\n\t{\n\t\t_bv = bv;\n\t\tbv.Actions.ActionsBuilder = this;\n\t\t_onActionsReady = onActionsReady;\n\t\t_pkmn = trainer.ActiveBattlersOrdered.ToArray();\n\t\t_actions = new PBETurnAction[_pkmn.Length];\n\t\t_standBy = new PBEBattlePokemon?[_pkmn.Length];\n\t\tActionsLoop();\n\t}\n\n\tpublic bool IsStandBy(PBEBattlePokemon p)\n\t{\n\t\tint i = Array.IndexOf(_standBy, p);\n\t\treturn i != -1 && i < _index;\n\t}\n\n\tpublic void Pop()\n\t{\n\t\t_index--;\n\t\tActionsLoop();\n\t}\n\tpublic void PushMove(PBEMove move, PBETurnTarget targets)\n\t{\n\t\tPBEBattlePokemon pkmn = _pkmn[_index];\n\t\tvar a = new PBETurnAction(pkmn, move, targets);\n\t\tpkmn.TurnAction = a;\n\t\t_actions[_index] = a;\n\t\t_standBy[_index] = null;\n\t\t_index++;\n\t\tActionsLoop();\n\t}\n\tpublic void PushSwitch(PBEBattlePokemon switcher)\n\t{\n\t\tPBEBattlePokemon pkmn = _pkmn[_index];\n\t\tvar a = new PBETurnAction(pkmn, switcher);\n\t\tpkmn.TurnAction = a;\n\t\t_actions[_index] = a;\n\t\t_standBy[_index] = switcher;\n\t\t_index++;\n\t\tActionsLoop();\n\t}\n\n\tprivate void ActionsLoop()\n\t{\n\t\tif (_index == _pkmn.Length)\n\t\t{\n\t\t\t_bv.Actions.ActionsBuilder = null;\n\t\t\t_onActionsReady(_actions);\n\t\t\t_onActionsReady = null!;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tPBEBattlePokemon pkmn = _pkmn[_index];\n\t\t\t_bv.AddMessage($\"What will {pkmn.Nickname} do?\", messageLog: false);\n\t\t\t_bv.Actions.DisplayActions(_index, pkmn);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineClient/Clients/BattleClient.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Packets;\nusing Kermalis.PokemonBattleEngineClient.Views;\nusing System;\n\nnamespace Kermalis.PokemonBattleEngineClient.Clients;\n\ninternal abstract class BattleClient : IDisposable\n{\n\tprotected const int WaitMilliseconds = 1750;\n\n\tpublic string Name { get; }\n\tpublic abstract PBEBattle Battle { get; }\n\tpublic abstract PBETrainer? Trainer { get; }\n\tpublic abstract BattleView BattleView { get; }\n\tpublic abstract bool HideNonOwned { get; }\n\n\tprotected BattleClient(string name)\n\t{\n\t\tName = name;\n\t}\n\n\tpublic bool ShouldUseKnownInfo(PBETrainer pkmnTrainer)\n\t{\n\t\treturn pkmnTrainer != Trainer && HideNonOwned;\n\t}\n\n\tpublic abstract void Dispose();\n\n\tprotected void ShowAllPokemon()\n\t{\n\t\tforeach (PBEBattlePokemon pkmn in Battle.ActiveBattlers)\n\t\t{\n\t\t\tBattleView.Field.ShowPokemon(pkmn);\n\t\t}\n\t}\n\n\t#region Automatic packet processing\n\t// Returns true if the next packet should be run immediately\n\tprotected virtual bool ProcessPacket(IPBEPacket packet)\n\t{\n\t\tswitch (packet)\n\t\t{\n\t\t\tcase PBEPkmnEXPChangedPacket _:\n\t\t\tcase PBEMoveLockPacket _:\n\t\t\tcase PBEMovePPChangedPacket _:\n\t\t\tcase PBEIllusionPacket _:\n\t\t\tcase PBETransformPacket _:\n\t\t\tcase PBEBattlePacket _:\n\t\t\tcase PBEActionsRequestPacket _:\n\t\t\tcase PBESwitchInRequestPacket _: return true;\n\t\t\t/*case PBEPkmnEXPChangedPacket pecp:\n            {\n                PBEBattlePokemon pokemon = pecp.PokemonTrainer.GetPokemon(pecp.Pokemon);\n                if (pokemon.FieldPosition != PBEFieldPosition.None)\n                {\n                    BattleView.Field.UpdatePokemon(pokemon, true, false);\n                }\n                break;\n            }*/ // Commented out because we don't have EXP bars\n\t\t\tcase PBEPkmnFaintedPacket pfp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon pokemon = pfp.PokemonTrainer.GetPokemon(pfp.Pokemon);\n\t\t\t\tBattleView.Field.HidePokemon(pokemon, pfp.OldPosition);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEPkmnFaintedPacket_Hidden pfph:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon pokemon = pfph.PokemonTrainer.GetPokemon(pfph.OldPosition);\n\t\t\t\tBattleView.Field.HidePokemon(pokemon, pfph.OldPosition);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase IPBEPkmnFormChangedPacket pfcp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon pokemon = pfcp.PokemonTrainer.GetPokemon(pfcp.Pokemon);\n\t\t\t\tBattleView.Field.UpdatePokemon(pokemon, true, true);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase IPBEPkmnHPChangedPacket phcp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon pokemon = phcp.PokemonTrainer.GetPokemon(phcp.Pokemon);\n\t\t\t\tBattleView.Field.UpdatePokemon(pokemon, true, false);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEPkmnLevelChangedPacket plcp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon pokemon = plcp.PokemonTrainer.GetPokemon(plcp.Pokemon);\n\t\t\t\tif (pokemon.FieldPosition != PBEFieldPosition.None)\n\t\t\t\t{\n\t\t\t\t\tBattleView.Field.UpdatePokemon(pokemon, true, false);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase IPBEPkmnSwitchInPacket psip:\n\t\t\t{\n\t\t\t\tif (!psip.Forced) // TODO: Why not forced? bug?\n\t\t\t\t{\n\t\t\t\t\tforeach (IPBEPkmnSwitchInInfo_Hidden info in psip.SwitchIns)\n\t\t\t\t\t{\n\t\t\t\t\t\tBattleView.Field.ShowPokemon(psip.Trainer.GetPokemon(info.FieldPosition));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEPkmnSwitchOutPacket psop:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon pokemon = psop.PokemonTrainer.GetPokemon(psop.Pokemon);\n\t\t\t\tBattleView.Field.HidePokemon(pokemon, psop.OldPosition);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEPkmnSwitchOutPacket_Hidden psoph:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon pokemon = psoph.PokemonTrainer.GetPokemon(psoph.OldPosition);\n\t\t\t\tBattleView.Field.HidePokemon(pokemon, psoph.OldPosition);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEStatus1Packet s1p:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon status1Receiver = s1p.Status1ReceiverTrainer.GetPokemon(s1p.Status1Receiver);\n\t\t\t\tBattleView.Field.UpdatePokemon(status1Receiver, true, false);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEStatus2Packet s2p:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon status2Receiver = s2p.Status2ReceiverTrainer.GetPokemon(s2p.Status2Receiver);\n\t\t\t\tswitch (s2p.Status2)\n\t\t\t\t{\n\t\t\t\t\tcase PBEStatus2.Airborne: BattleView.Field.UpdatePokemon(status2Receiver, false, true); break;\n\t\t\t\t\tcase PBEStatus2.Disguised:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s2p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Ended: BattleView.Field.UpdatePokemon(status2Receiver, true, true); break;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus2.ShadowForce: BattleView.Field.UpdatePokemon(status2Receiver, false, true); break;\n\t\t\t\t\tcase PBEStatus2.Substitute:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s2p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added:\n\t\t\t\t\t\t\tcase PBEStatusAction.Ended: BattleView.Field.UpdatePokemon(status2Receiver, false, true); break;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus2.Transformed:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s2p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added: BattleView.Field.UpdatePokemon(status2Receiver, false, true); break;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus2.Underground: BattleView.Field.UpdatePokemon(status2Receiver, false, true); break;\n\t\t\t\t\tcase PBEStatus2.Underwater: BattleView.Field.UpdatePokemon(status2Receiver, false, true); break;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEWeatherPacket wp:\n\t\t\t{\n\t\t\t\tswitch (wp.WeatherAction)\n\t\t\t\t{\n\t\t\t\t\tcase PBEWeatherAction.Added:\n\t\t\t\t\tcase PBEWeatherAction.Ended: BattleView.Field.UpdateWeather(); break;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase IPBEWildPkmnAppearedPacket wpap:\n\t\t\t{\n\t\t\t\tPBETrainer wildTrainer = Battle.Teams[1].Trainers[0];\n\t\t\t\tforeach (IPBEPkmnAppearedInfo_Hidden info in wpap.Pokemon)\n\t\t\t\t{\n\t\t\t\t\tBattleView.Field.ShowPokemon(wildTrainer.GetPokemon(info.FieldPosition));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase IPBEAutoCenterPacket acp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon pokemon0 = acp is IPBEAutoCenterPacket_0 acp0\n\t\t\t\t\t? acp.Pokemon0Trainer.GetPokemon(acp0.Pokemon0)\n\t\t\t\t\t: acp.Pokemon0Trainer.GetPokemon(acp.Pokemon0OldPosition);\n\t\t\t\tPBEBattlePokemon pokemon1 = acp is IPBEAutoCenterPacket_1 acp1\n\t\t\t\t\t? acp.Pokemon1Trainer.GetPokemon(acp1.Pokemon1)\n\t\t\t\t\t: acp.Pokemon1Trainer.GetPokemon(acp.Pokemon1OldPosition);\n\t\t\t\tBattleView.Field.MovePokemon(pokemon0, acp.Pokemon0OldPosition);\n\t\t\t\tBattleView.Field.MovePokemon(pokemon1, acp.Pokemon1OldPosition);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBETurnBeganPacket tbp:\n\t\t\t{\n\t\t\t\tBattleView.AddMessage($\"Turn {tbp.TurnNumber}\", messageBox: false);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tstring? message = PBEBattle.GetDefaultMessage(Battle, packet, showRawHP: !HideNonOwned, userTrainer: Trainer);\n\t\tif (string.IsNullOrEmpty(message))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t\tBattleView.AddMessage(message);\n\t\treturn false;\n\t}\n\t#endregion\n}\n"
  },
  {
    "path": "PokemonBattleEngineClient/Clients/NetworkClient.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data.Legality;\nusing Kermalis.PokemonBattleEngine.Network;\nusing Kermalis.PokemonBattleEngine.Packets;\nusing Kermalis.PokemonBattleEngineClient.Views;\nusing System;\nusing System.Diagnostics;\nusing System.Net;\n\nnamespace Kermalis.PokemonBattleEngineClient.Clients;\n\ninternal sealed class NetworkClientConnection : IDisposable\n{\n\tprivate readonly PBEClient _client;\n\tprivate readonly PBELegalPokemonCollection _party;\n\tprivate readonly Action<object?> _action;\n\tprivate byte _battleId = byte.MaxValue; // Spectator by default\n\n\tpublic NetworkClientConnection(string host, ushort port, PBELegalPokemonCollection party, Action<object?> action)\n\t{\n\t\t_party = party;\n\t\t_action = action;\n\t\t_client = new PBEClient();\n\t\t_client.Disconnected += OnDisconnected;\n\t\t_client.Error += OnError;\n\t\t_client.PacketReceived += OnPacketReceived;\n\t\tif (_client.Connect(new IPEndPoint(IPAddress.Parse(host), port), 10 * 1000, new PBEPacketProcessor()))\n\t\t{\n\t\t\tOnConnected();\n\t\t}\n\t\telse\n\t\t{\n\t\t\t_client.Dispose();\n\t\t\taction.Invoke(null);\n\t\t}\n\t}\n\n\tprivate void OnConnected()\n\t{\n\t\tDebug.WriteLine($\"Connecting... connected to {_client.RemoteIP}\");\n\t\t_action.Invoke(\"Waiting for players...\");\n\t}\n\tprivate void OnDisconnected(object? sender, EventArgs e)\n\t{\n\t\tDebug.WriteLine(\"Connecting... disconnected from host\");\n\t\t_action.Invoke(null);\n\t}\n\tprivate void OnError(object? sender, Exception ex)\n\t{\n\t\tDebug.WriteLine($\"Connecting... error: {ex}\");\n\t}\n\n\tprivate void OnPacketReceived(object? sender, IPBEPacket packet)\n\t{\n\t\tDebug.WriteLine($\"Connecting... received \\\"{packet.GetType().Name}\\\"\");\n\t\tswitch (packet)\n\t\t{\n\t\t\tcase PBEMatchCancelledPacket _:\n\t\t\t{\n\t\t\t\t_action.Invoke(null);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEPartyRequestPacket prp:\n\t\t\t{\n\t\t\t\t_battleId = prp.BattleId;\n\t\t\t\tif (prp.RequireLegal)\n\t\t\t\t{\n\t\t\t\t\tSend(new PBELegalPartyResponsePacket(_party));\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tSend(new PBEPartyResponsePacket(_party));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEBattlePacket bp:\n\t\t\t{\n\t\t\t\t_client.Disconnected -= OnDisconnected;\n\t\t\t\t_client.Error -= OnError;\n\t\t\t\t_client.PacketReceived -= OnPacketReceived;\n\t\t\t\t_action.Invoke(Tuple.Create(_client, bp, _battleId));\n\t\t\t\t// Response will be sent in NetworkClient constructor so the server doesn't send packets between threads\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(packet));\n\t\t}\n\t}\n\n\tprivate void Send(IPBEPacket packet)\n\t{\n\t\tif (_client.IsConnected)\n\t\t{\n\t\t\t_client.Send(packet);\n\t\t}\n\t}\n\n\tpublic void Dispose()\n\t{\n\t\t_client.Dispose(); // Unsubscribe events\n\t}\n}\ninternal sealed class NetworkClient : NonLocalClient\n{\n\tprivate readonly PBEClient _client;\n\tpublic override PBEBattle Battle { get; }\n\tpublic override PBETrainer? Trainer { get; }\n\tpublic override BattleView BattleView { get; }\n\tpublic override bool HideNonOwned => true;\n\n\tpublic NetworkClient(PBEClient client, PBEBattlePacket bp, byte battleId, string name) : base(name)\n\t{\n\t\tvar b = PBEBattle.CreateRemoteBattle(bp);\n\t\tBattle = b;\n\t\tif (battleId != byte.MaxValue)\n\t\t{\n\t\t\tTrainer = b.Trainers[battleId];\n\t\t}\n\t\tBattleView = new BattleView(this);\n\t\tclient.Battle = b;\n\t\t_client = client;\n\t\tclient.Disconnected += OnDisconnected;\n\t\tclient.Error += OnError;\n\t\tclient.PacketReceived += OnPacketReceived;\n\t\tShowAllPokemon();\n\t\tSend(new PBEResponsePacket());\n\t}\n\n\tprivate void OnDisconnected(object? sender, EventArgs e)\n\t{\n\t\tDebug.WriteLine($\"{Name} disconnected from host\");\n\t\tBattleView.AddMessage(\"Disconnected from host.\", messageBox: false);\n\t}\n\tprivate void OnError(object? sender, Exception ex)\n\t{\n\t\tDebug.WriteLine($\"{Name} error: {ex}\");\n\t}\n\tprivate void OnPacketReceived(object? sender, IPBEPacket packet)\n\t{\n\t\tDebug.WriteLine($\"{Name} received \\\"{packet.GetType().Name}\\\"\");\n\t\tswitch (packet)\n\t\t{\n\t\t\tcase PBEMatchCancelledPacket _:\n\t\t\t{\n\t\t\t\tBattleView.AddMessage(\"Match cancelled!\", messageBox: false);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEPlayerJoinedPacket pjp:\n\t\t\t{\n\t\t\t\tBattleView.AddMessage(string.Format(\"{0} joined the game.\", pjp.TrainerName), messageBox: false);\n\t\t\t\tSend(new PBEResponsePacket());\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEActionsRequestPacket _:\n\t\t\tcase PBESwitchInRequestPacket _:\n\t\t\tcase PBEBattleResultPacket _:\n\t\t\t{\n\t\t\t\tBattle.Events.Add(packet);\n\t\t\t\tSend(new PBEResponsePacket());\n\t\t\t\tStartPacketThread();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\tBattleView.AddMessage(\"Communicating...\", messageLog: false);\n\t\t\t\tBattle.Events.Add(packet);\n\t\t\t\tSend(new PBEResponsePacket());\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void OnActionsReady(PBETurnAction[] acts)\n\t{\n\t\tBattleView.AddMessage(\"Waiting for players...\", messageLog: false);\n\t\tSend(new PBEActionsResponsePacket(acts));\n\t}\n\tprivate void OnSwitchesReady(PBESwitchIn[] switches)\n\t{\n\t\tBattleView.AddMessage(\"Waiting for players...\", messageLog: false);\n\t\tSend(new PBESwitchInResponsePacket(switches));\n\t}\n\n\tprivate void Send(IPBEPacket packet)\n\t{\n\t\tif (_client.IsConnected)\n\t\t{\n\t\t\t_client.Send(packet);\n\t\t}\n\t}\n\n\tpublic override void Dispose()\n\t{\n\t\tbase.Dispose();\n\t\t_client.Dispose(); // Events unsubscribed\n\t}\n\n\tprotected override bool ProcessPacket(IPBEPacket packet)\n\t{\n\t\tswitch (packet)\n\t\t{\n\t\t\tcase PBEMovePPChangedPacket mpcp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon moveUser = mpcp.MoveUserTrainer.GetPokemon(mpcp.MoveUser);\n\t\t\t\tif (moveUser.Trainer == Trainer)\n\t\t\t\t{\n\t\t\t\t\tmoveUser.Moves[mpcp.Move]!.PP -= mpcp.AmountReduced;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEActionsRequestPacket arp:\n\t\t\t{\n\t\t\t\tif (arp.Trainer == Trainer)\n\t\t\t\t{\n\t\t\t\t\t_ = new ActionsBuilder(BattleView, Trainer, OnActionsReady);\n\t\t\t\t}\n\t\t\t\telse if (Trainer is null || Trainer.NumConsciousPkmn == 0) // Spectators/KO'd\n\t\t\t\t{\n\t\t\t\t\tBattleView.AddMessage(\"Waiting for players...\", messageLog: false);\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tcase PBESwitchInRequestPacket sirp:\n\t\t\t{\n\t\t\t\tPBETrainer t = sirp.Trainer;\n\t\t\t\tt.SwitchInsRequired = sirp.Amount;\n\t\t\t\tif (t == Trainer)\n\t\t\t\t{\n\t\t\t\t\t_ = new SwitchesBuilder(BattleView, sirp.Amount, OnSwitchesReady);\n\t\t\t\t}\n\t\t\t\telse if (BattleView.Actions.SwitchesBuilder?.SwitchesRequired == 0) // No need to switch/Spectators/KO'd\n\t\t\t\t{\n\t\t\t\t\tBattleView.AddMessage(\"Waiting for players...\", messageLog: false);\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tcase PBEPkmnFaintedPacket_Hidden pfph:\n\t\t\t{\n\t\t\t\tbool ret = base.ProcessPacket(packet); // Process before removal\n\t\t\t\tPBEBattlePokemon pokemon = pfph.PokemonTrainer.GetPokemon(pfph.OldPosition);\n\t\t\t\tBattle.ActiveBattlers.Remove(pokemon);\n\t\t\t\tpokemon.FieldPosition = PBEFieldPosition.None;\n\t\t\t\tPBETrainer.Remove(pokemon);\n\t\t\t\treturn ret;\n\t\t\t}\n\t\t\tcase PBEPkmnFormChangedPacket_Hidden pfcph:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon pokemon = pfcph.PokemonTrainer.GetPokemon(pfcph.Pokemon);\n\t\t\t\tpokemon.HPPercentage = pfcph.NewHPPercentage;\n\t\t\t\tpokemon.KnownAbility = pfcph.NewKnownAbility;\n\t\t\t\tpokemon.KnownForm = pfcph.NewForm;\n\t\t\t\tpokemon.KnownType1 = pfcph.NewType1;\n\t\t\t\tpokemon.KnownType2 = pfcph.NewType2;\n\t\t\t\tpokemon.KnownWeight = pfcph.NewWeight;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEPkmnHPChangedPacket_Hidden phcph:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon pokemon = phcph.PokemonTrainer.GetPokemon(phcph.Pokemon);\n\t\t\t\tpokemon.HPPercentage = phcph.NewHPPercentage;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEPkmnSwitchInPacket_Hidden psiph:\n\t\t\t{\n\t\t\t\tforeach (PBEPkmnSwitchInPacket_Hidden.PBEPkmnSwitchInInfo info in psiph.SwitchIns)\n\t\t\t\t{\n\t\t\t\t\t_ = new PBEBattlePokemon(psiph.Trainer, info);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEPkmnSwitchOutPacket_Hidden psoph:\n\t\t\t{\n\t\t\t\tbool ret = base.ProcessPacket(packet); // Process before removal\n\t\t\t\tPBEBattlePokemon pokemon = psoph.PokemonTrainer.GetPokemon(psoph.OldPosition);\n\t\t\t\tBattle.ActiveBattlers.Remove(pokemon);\n\t\t\t\tPBETrainer.Remove(pokemon);\n\t\t\t\treturn ret;\n\t\t\t}\n\t\t\tcase PBEReflectTypePacket_Hidden rtph:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon user = rtph.UserTrainer.GetPokemon(rtph.User);\n\t\t\t\tPBEBattlePokemon target = rtph.TargetTrainer.GetPokemon(rtph.Target);\n\t\t\t\tuser.Type1 = user.KnownType1 = target.KnownType1; // Set Type1 and Type2 so Transform works\n\t\t\t\tuser.Type2 = user.KnownType2 = target.KnownType2;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEWildPkmnAppearedPacket wpap:\n\t\t\t{\n\t\t\t\tPBETrainer wildTrainer = Battle.Teams[1].Trainers[0];\n\t\t\t\tforeach (PBEPkmnAppearedInfo info in wpap.Pokemon)\n\t\t\t\t{\n\t\t\t\t\tPBEBattlePokemon pokemon = wildTrainer.GetPokemon(info.Pokemon);\n\t\t\t\t\tpokemon.FieldPosition = info.FieldPosition;\n\t\t\t\t\tBattle.ActiveBattlers.Add(pokemon);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEWildPkmnAppearedPacket_Hidden wpaph:\n\t\t\t{\n\t\t\t\tforeach (PBEWildPkmnAppearedPacket_Hidden.PBEWildPkmnInfo info in wpaph.Pokemon)\n\t\t\t\t{\n\t\t\t\t\t_ = new PBEBattlePokemon(Battle, info);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn base.ProcessPacket(packet);\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineClient/Clients/NonLocalClient.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Packets;\nusing System.IO;\nusing System.Threading;\n\nnamespace Kermalis.PokemonBattleEngineClient.Clients;\n\ninternal abstract class NonLocalClient : BattleClient\n{\n\tprotected NonLocalClient(string name) : base(name) { }\n\n\tpublic override void Dispose()\n\t{\n\t\t_stopPacketThread = true;\n\t}\n\n\t#region Automatic packet processing\n\tprivate int _currentPacket = -1;\n\tprivate Thread? _packetThread;\n\tprivate readonly object _packetThreadLockObj = new();\n\tprivate bool _plsStartPacketThreadForMe = false;\n\tprivate bool _stopPacketThread = false;\n\n\tprivate void CreateThread__Unsafe()\n\t{\n\t\t_packetThread = new Thread(PacketThread) { Name = \"Packet Thread\" };\n\t\t_packetThread.Start();\n\t}\n\tprotected void StartPacketThread()\n\t{\n\t\tlock (_packetThreadLockObj)\n\t\t{\n\t\t\t_stopPacketThread = false;\n\t\t\tif (_packetThread is null)\n\t\t\t{\n\t\t\t\tCreateThread__Unsafe();\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t_plsStartPacketThreadForMe = true;\n\t\t\t}\n\t\t}\n\t}\n\tprivate void PacketThread()\n\t{\n\t\twhile (!_stopPacketThread && _currentPacket < Battle.Events.Count - 1)\n\t\t{\n\t\t\t_plsStartPacketThreadForMe = false;\n\t\t\t_currentPacket++;\n\t\t\tif (!ProcessPacket(Battle.Events[_currentPacket]))\n\t\t\t{\n\t\t\t\tThread.Sleep(WaitMilliseconds);\n\t\t\t}\n\t\t}\n\t\tlock (_packetThreadLockObj)\n\t\t{\n\t\t\tif (_plsStartPacketThreadForMe)\n\t\t\t{\n\t\t\t\tCreateThread__Unsafe();\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t_packetThread = null;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void DoDisguisedAppearance(PBEBattlePokemon pkmn, PBEPkmnAppearedInfo info)\n\t{\n\t\tif (info.IsDisguised)\n\t\t{\n\t\t\tpkmn.Status2 |= PBEStatus2.Disguised;\n\t\t\tpkmn.KnownCaughtBall = info.CaughtBall;\n\t\t\tpkmn.KnownGender = info.Gender;\n\t\t\tpkmn.KnownNickname = info.Nickname;\n\t\t\tpkmn.KnownShiny = info.Shiny;\n\t\t\tpkmn.KnownSpecies = info.Species;\n\t\t\tpkmn.KnownForm = info.Form;\n\t\t\tIPBEPokemonData pData = PBEDataProvider.Instance.GetPokemonData(info);\n\t\t\tpkmn.KnownType1 = pData.Type1;\n\t\t\tpkmn.KnownType2 = pData.Type2;\n\t\t}\n\t}\n\tprotected override bool ProcessPacket(IPBEPacket packet)\n\t{\n\t\tswitch (packet)\n\t\t{\n\t\t\tcase PBEAbilityPacket ap:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon abilityOwner = ap.AbilityOwnerTrainer.GetPokemon(ap.AbilityOwner);\n\t\t\t\tabilityOwner.Ability = ap.Ability;\n\t\t\t\tabilityOwner.KnownAbility = ap.Ability;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEAbilityReplacedPacket arp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon abilityOwner = arp.AbilityOwnerTrainer.GetPokemon(arp.AbilityOwner);\n\t\t\t\tabilityOwner.Ability = arp.NewAbility;\n\t\t\t\tabilityOwner.KnownAbility = arp.NewAbility;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEBattleStatusPacket bsp:\n\t\t\t{\n\t\t\t\tswitch (bsp.BattleStatusAction)\n\t\t\t\t{\n\t\t\t\t\tcase PBEBattleStatusAction.Added: Battle.BattleStatus |= bsp.BattleStatus; break;\n\t\t\t\t\tcase PBEBattleStatusAction.Cleared:\n\t\t\t\t\tcase PBEBattleStatusAction.Ended: Battle.BattleStatus &= ~bsp.BattleStatus; break;\n\t\t\t\t\tdefault: throw new InvalidDataException(nameof(bsp.BattleStatusAction));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBECapturePacket cp:\n\t\t\t{\n\t\t\t\tif (cp.Success)\n\t\t\t\t{\n\t\t\t\t\tPBEBattlePokemon pokemon = cp.PokemonTrainer.GetPokemon(cp.Pokemon);\n\t\t\t\t\tpokemon.CaughtBall = cp.Ball;\n\t\t\t\t\tpokemon.KnownCaughtBall = cp.Ball;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEHazePacket _:\n\t\t\t{\n\t\t\t\tforeach (PBEBattlePokemon pkmn in Battle.ActiveBattlers)\n\t\t\t\t{\n\t\t\t\t\tpkmn.ClearStatChanges();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEIllusionPacket ilp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon pokemon = ilp.PokemonTrainer.GetPokemon(ilp.Pokemon);\n\t\t\t\tpokemon.Ability = pokemon.KnownAbility = PBEAbility.Illusion;\n\t\t\t\tpokemon.Gender = pokemon.KnownGender = ilp.ActualGender;\n\t\t\t\tpokemon.CaughtBall = pokemon.KnownCaughtBall = ilp.ActualCaughtBall;\n\t\t\t\tpokemon.Nickname = pokemon.KnownNickname = ilp.ActualNickname;\n\t\t\t\tpokemon.Shiny = pokemon.KnownShiny = ilp.ActualShiny;\n\t\t\t\tpokemon.Species = pokemon.KnownSpecies = ilp.ActualSpecies;\n\t\t\t\tpokemon.Form = pokemon.KnownForm = ilp.ActualForm;\n\t\t\t\tpokemon.Type1 = pokemon.KnownType1 = ilp.ActualType1;\n\t\t\t\tpokemon.Type2 = pokemon.KnownType2 = ilp.ActualType2;\n\t\t\t\tpokemon.Weight = pokemon.KnownWeight = ilp.ActualWeight;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tcase PBEItemPacket ip:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon itemHolder = ip.ItemHolderTrainer.GetPokemon(ip.ItemHolder);\n\t\t\t\tswitch (ip.ItemAction)\n\t\t\t\t{\n\t\t\t\t\tcase PBEItemAction.Announced:\n\t\t\t\t\tcase PBEItemAction.Damage:\n\t\t\t\t\tcase PBEItemAction.RestoredHP: itemHolder.Item = itemHolder.KnownItem = ip.Item; break;\n\t\t\t\t\tcase PBEItemAction.Consumed: itemHolder.Item = itemHolder.KnownItem = PBEItem.None; break;\n\t\t\t\t\tdefault: throw new InvalidDataException(nameof(ip.ItemAction));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEMoveLockPacket mlp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon moveUser = mlp.MoveUserTrainer.GetPokemon(mlp.MoveUser);\n\t\t\t\tswitch (mlp.MoveLockType)\n\t\t\t\t{\n\t\t\t\t\tcase PBEMoveLockType.ChoiceItem: moveUser.ChoiceLockedMove = mlp.LockedMove; break;\n\t\t\t\t\tcase PBEMoveLockType.Temporary: moveUser.TempLockedMove = mlp.LockedMove; break;\n\t\t\t\t\tdefault: throw new InvalidDataException(nameof(mlp.MoveLockType));\n\t\t\t\t}\n\t\t\t\tif (mlp.LockedTargets is not null)\n\t\t\t\t{\n\t\t\t\t\tmoveUser.TempLockedTargets = mlp.LockedTargets.Value;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tcase PBEMovePPChangedPacket mpcp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon moveUser = mpcp.MoveUserTrainer.GetPokemon(mpcp.MoveUser);\n\t\t\t\tmoveUser.UpdateKnownPP(mpcp.Move, mpcp.AmountReduced);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tcase PBEMoveUsedPacket mup:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon moveUser = mup.MoveUserTrainer.GetPokemon(mup.MoveUser);\n\t\t\t\tif (mup.Owned && !moveUser.KnownMoves.Contains(mup.Move))\n\t\t\t\t{\n\t\t\t\t\tmoveUser.KnownMoves[PBEMove.MAX]!.Move = mup.Move;\n\t\t\t\t\tPBEBattleMoveset.PBEBattleMovesetSlot? slot = moveUser.Moves[PBEMove.MAX];\n\t\t\t\t\tif (slot is not null)\n\t\t\t\t\t{\n\t\t\t\t\t\tslot.Move = mup.Move; // Copy to Moves as well so Transform doesn't break for spectators/allies\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEPkmnEXPChangedPacket pecp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon pokemon = pecp.PokemonTrainer.GetPokemon(pecp.Pokemon);\n\t\t\t\tpokemon.EXP = pecp.NewEXP;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEPkmnFaintedPacket pfp:\n\t\t\t{\n\t\t\t\tbool ret = base.ProcessPacket(packet); // Process before removal\n\t\t\t\tPBEBattlePokemon pokemon = pfp.PokemonTrainer.GetPokemon(pfp.Pokemon);\n\t\t\t\tBattle.ActiveBattlers.Remove(pokemon);\n\t\t\t\tpokemon.ClearForFaint();\n\t\t\t\treturn ret;\n\t\t\t}\n\t\t\tcase PBEPkmnFormChangedPacket pfcp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon pokemon = pfcp.PokemonTrainer.GetPokemon(pfcp.Pokemon);\n\t\t\t\tpokemon.HP = pfcp.NewHP;\n\t\t\t\tpokemon.MaxHP = pfcp.NewMaxHP;\n\t\t\t\tpokemon.HPPercentage = pfcp.NewHPPercentage;\n\t\t\t\tpokemon.Attack = pfcp.NewAttack;\n\t\t\t\tpokemon.Defense = pfcp.NewDefense;\n\t\t\t\tpokemon.SpAttack = pfcp.NewSpAttack;\n\t\t\t\tpokemon.SpDefense = pfcp.NewSpDefense;\n\t\t\t\tpokemon.Speed = pfcp.NewSpeed;\n\t\t\t\tpokemon.Ability = pfcp.NewAbility;\n\t\t\t\tpokemon.KnownAbility = pfcp.NewKnownAbility;\n\t\t\t\tpokemon.Form = pokemon.KnownForm = pfcp.NewForm;\n\t\t\t\tpokemon.Type1 = pokemon.KnownType1 = pfcp.NewType1;\n\t\t\t\tpokemon.Type2 = pokemon.KnownType2 = pfcp.NewType2;\n\t\t\t\tpokemon.Weight = pokemon.KnownWeight = pfcp.NewWeight;\n\t\t\t\tif (pfcp.IsRevertForm)\n\t\t\t\t{\n\t\t\t\t\tpokemon.RevertForm = pfcp.NewForm;\n\t\t\t\t\tpokemon.RevertAbility = pfcp.NewAbility;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEPkmnHPChangedPacket phcp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon pokemon = phcp.PokemonTrainer.GetPokemon(phcp.Pokemon);\n\t\t\t\tpokemon.HP = phcp.NewHP;\n\t\t\t\tpokemon.HPPercentage = phcp.NewHPPercentage;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEPkmnLevelChangedPacket plcp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon pokemon = plcp.PokemonTrainer.GetPokemon(plcp.Pokemon);\n\t\t\t\tpokemon.Level = plcp.NewLevel;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEPkmnStatChangedPacket pscp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon pokemon = pscp.PokemonTrainer.GetPokemon(pscp.Pokemon);\n\t\t\t\tpokemon.SetStatChange(pscp.Stat, pscp.NewValue);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEPkmnSwitchInPacket psip:\n\t\t\t{\n\t\t\t\tforeach (PBEPkmnAppearedInfo info in psip.SwitchIns)\n\t\t\t\t{\n\t\t\t\t\tPBEBattlePokemon pokemon = psip.Trainer.GetPokemon(info.Pokemon);\n\t\t\t\t\tpokemon.FieldPosition = info.FieldPosition;\n\t\t\t\t\tPBETrainer.SwitchTwoPokemon(pokemon, info.FieldPosition);\n\t\t\t\t\tDoDisguisedAppearance(pokemon, info);\n\t\t\t\t\tBattle.ActiveBattlers.Add(pokemon);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEPkmnSwitchOutPacket psop:\n\t\t\t{\n\t\t\t\tbool ret = base.ProcessPacket(packet); // Process before removal\n\t\t\t\tPBEBattlePokemon pokemon = psop.PokemonTrainer.GetPokemon(psop.Pokemon);\n\t\t\t\tBattle.ActiveBattlers.Remove(pokemon);\n\t\t\t\tpokemon.ClearForSwitch();\n\t\t\t\treturn ret;\n\t\t\t}\n\t\t\tcase PBEPsychUpPacket pup:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon user = pup.UserTrainer.GetPokemon(pup.User);\n\t\t\t\tPBEBattlePokemon target = pup.TargetTrainer.GetPokemon(pup.Target);\n\t\t\t\tuser.AttackChange = target.AttackChange = pup.AttackChange;\n\t\t\t\tuser.DefenseChange = target.DefenseChange = pup.DefenseChange;\n\t\t\t\tuser.SpAttackChange = target.SpAttackChange = pup.SpAttackChange;\n\t\t\t\tuser.SpDefenseChange = target.SpDefenseChange = pup.SpDefenseChange;\n\t\t\t\tuser.SpeedChange = target.SpeedChange = pup.SpeedChange;\n\t\t\t\tuser.AccuracyChange = target.AccuracyChange = pup.AccuracyChange;\n\t\t\t\tuser.EvasionChange = target.EvasionChange = pup.EvasionChange;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEReflectTypePacket rtp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon user = rtp.UserTrainer.GetPokemon(rtp.User);\n\t\t\t\tPBEBattlePokemon target = rtp.TargetTrainer.GetPokemon(rtp.Target);\n\t\t\t\tuser.Type1 = user.KnownType1 = target.KnownType1 = target.Type1 = rtp.Type1;\n\t\t\t\tuser.Type2 = user.KnownType2 = target.KnownType2 = target.Type2 = rtp.Type2;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEStatus1Packet s1p:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon status1Receiver = s1p.Status1ReceiverTrainer.GetPokemon(s1p.Status1Receiver);\n\t\t\t\tswitch (s1p.StatusAction)\n\t\t\t\t{\n\t\t\t\t\tcase PBEStatusAction.Added:\n\t\t\t\t\tcase PBEStatusAction.Announced:\n\t\t\t\t\tcase PBEStatusAction.CausedImmobility:\n\t\t\t\t\tcase PBEStatusAction.Damage: status1Receiver.Status1 = s1p.Status1; break;\n\t\t\t\t\tcase PBEStatusAction.Cleared:\n\t\t\t\t\tcase PBEStatusAction.Ended: status1Receiver.Status1 = PBEStatus1.None; break;\n\t\t\t\t\tdefault: throw new InvalidDataException(nameof(s1p.StatusAction));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEStatus2Packet s2p:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon status2Receiver = s2p.Status2ReceiverTrainer.GetPokemon(s2p.Status2Receiver);\n\t\t\t\tPBEBattlePokemon pokemon2 = s2p.Pokemon2Trainer.GetPokemon(s2p.Pokemon2);\n\t\t\t\tswitch (s2p.StatusAction)\n\t\t\t\t{\n\t\t\t\t\tcase PBEStatusAction.Added:\n\t\t\t\t\tcase PBEStatusAction.Announced:\n\t\t\t\t\tcase PBEStatusAction.CausedImmobility:\n\t\t\t\t\tcase PBEStatusAction.Damage: status2Receiver.Status2 |= s2p.Status2; status2Receiver.KnownStatus2 |= s2p.Status2; break;\n\t\t\t\t\tcase PBEStatusAction.Cleared:\n\t\t\t\t\tcase PBEStatusAction.Ended: status2Receiver.Status2 &= ~s2p.Status2; status2Receiver.KnownStatus2 &= ~s2p.Status2; break;\n\t\t\t\t\tdefault: throw new InvalidDataException(nameof(s2p.StatusAction));\n\t\t\t\t}\n\t\t\t\tswitch (s2p.Status2)\n\t\t\t\t{\n\t\t\t\t\tcase PBEStatus2.Infatuated:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s2p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added: status2Receiver.InfatuatedWithPokemon = pokemon2; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Cleared:\n\t\t\t\t\t\t\tcase PBEStatusAction.Ended: status2Receiver.InfatuatedWithPokemon = null; break;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus2.LeechSeed:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s2p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added: status2Receiver.SeededPosition = pokemon2.FieldPosition; status2Receiver.SeededTeam = pokemon2.Team; break;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus2.LockOn:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s2p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added: status2Receiver.LockOnPokemon = pokemon2; break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Ended: status2Receiver.LockOnPokemon = null; break;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus2.PowerTrick:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s2p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added: status2Receiver.ApplyPowerTrickChange(); break;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus2.Roost:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s2p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added: status2Receiver.StartRoost(); break;\n\t\t\t\t\t\t\tcase PBEStatusAction.Ended: status2Receiver.EndRoost(); break;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBEStatus2.Transformed:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (s2p.StatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBEStatusAction.Added: status2Receiver.Transform(pokemon2); break;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBETeamStatusPacket tsp:\n\t\t\t{\n\t\t\t\tPBETeam team = tsp.Team;\n\t\t\t\tswitch (tsp.TeamStatusAction)\n\t\t\t\t{\n\t\t\t\t\tcase PBETeamStatusAction.Added:\n\t\t\t\t\tcase PBETeamStatusAction.Cleared:\n\t\t\t\t\tcase PBETeamStatusAction.Ended: team.TeamStatus &= ~tsp.TeamStatus; break;\n\t\t\t\t\tdefault: throw new InvalidDataException(nameof(tsp.TeamStatusAction));\n\t\t\t\t}\n\t\t\t\tswitch (tsp.TeamStatus)\n\t\t\t\t{\n\t\t\t\t\tcase PBETeamStatus.Spikes:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (tsp.TeamStatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBETeamStatusAction.Added: team.SpikeCount++; break;\n\t\t\t\t\t\t\t\t//case PBETeamStatusAction.Cleared: team.SpikeCount = 0; break;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase PBETeamStatus.ToxicSpikes:\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (tsp.TeamStatusAction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PBETeamStatusAction.Added: team.ToxicSpikeCount++; break;\n\t\t\t\t\t\t\tcase PBETeamStatusAction.Cleared: team.ToxicSpikeCount = 0; break;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBETeamStatusDamagePacket tsdp:\n\t\t\t{\n\t\t\t\ttsdp.Team.TeamStatus |= tsdp.TeamStatus;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBETransformPacket tp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon target = tp.TargetTrainer.GetPokemon(tp.Target);\n\t\t\t\ttarget.Attack = tp.TargetAttack;\n\t\t\t\ttarget.Defense = tp.TargetDefense;\n\t\t\t\ttarget.SpAttack = tp.TargetSpAttack;\n\t\t\t\ttarget.SpDefense = tp.TargetSpDefense;\n\t\t\t\ttarget.Speed = tp.TargetSpeed;\n\t\t\t\ttarget.AttackChange = tp.TargetAttackChange;\n\t\t\t\ttarget.DefenseChange = tp.TargetDefenseChange;\n\t\t\t\ttarget.SpAttackChange = tp.TargetSpAttackChange;\n\t\t\t\ttarget.SpDefenseChange = tp.TargetSpDefenseChange;\n\t\t\t\ttarget.SpeedChange = tp.TargetSpeedChange;\n\t\t\t\ttarget.AccuracyChange = tp.TargetAccuracyChange;\n\t\t\t\ttarget.EvasionChange = tp.TargetEvasionChange;\n\t\t\t\ttarget.Ability = target.KnownAbility = tp.TargetAbility;\n\t\t\t\tfor (int i = 0; i < Battle.Settings.NumMoves; i++)\n\t\t\t\t{\n\t\t\t\t\ttarget.Moves[i].Move = tp.TargetMoves[i];\n\t\t\t\t}\n\t\t\t\ttarget.Species = target.KnownSpecies = tp.TargetSpecies;\n\t\t\t\ttarget.Form = target.KnownForm = tp.TargetForm;\n\t\t\t\ttarget.Type1 = target.KnownType1 = tp.TargetType1;\n\t\t\t\ttarget.Type2 = target.KnownType2 = tp.TargetType2;\n\t\t\t\ttarget.Weight = target.KnownWeight = tp.TargetWeight;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tcase PBETypeChangedPacket tcp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon pokemon = tcp.PokemonTrainer.GetPokemon(tcp.Pokemon);\n\t\t\t\tpokemon.Type1 = pokemon.KnownType1 = tcp.Type1;\n\t\t\t\tpokemon.Type2 = pokemon.KnownType2 = tcp.Type2;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEWeatherPacket wp:\n\t\t\t{\n\t\t\t\tswitch (wp.WeatherAction)\n\t\t\t\t{\n\t\t\t\t\tcase PBEWeatherAction.Added:\n\t\t\t\t\tcase PBEWeatherAction.Ended: Battle.Weather = PBEWeather.None; break;\n\t\t\t\t\tdefault: throw new InvalidDataException(nameof(wp.WeatherAction));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEWeatherDamagePacket wdp:\n\t\t\t{\n\t\t\t\tBattle.Weather = wdp.Weather;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEWildPkmnAppearedPacket wpap:\n\t\t\t{\n\t\t\t\tPBETrainer wildTrainer = Battle.Teams[1].Trainers[0];\n\t\t\t\tforeach (PBEPkmnAppearedInfo info in wpap.Pokemon)\n\t\t\t\t{\n\t\t\t\t\tPBEBattlePokemon pokemon = wildTrainer.GetPokemon(info.Pokemon);\n\t\t\t\t\tDoDisguisedAppearance(pokemon, info);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase IPBEAutoCenterPacket acp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon pokemon0 = acp.Pokemon0Trainer.GetPokemon(acp.Pokemon0OldPosition);\n\t\t\t\tPBEBattlePokemon pokemon1 = acp.Pokemon1Trainer.GetPokemon(acp.Pokemon1OldPosition);\n\t\t\t\tpokemon0.FieldPosition = PBEFieldPosition.Center;\n\t\t\t\tpokemon1.FieldPosition = PBEFieldPosition.Center;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEBattleResultPacket brp:\n\t\t\t{\n\t\t\t\tBattle.BattleResult = brp.BattleResult;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBETurnBeganPacket tbp:\n\t\t\t{\n\t\t\t\tBattle.TurnNumber = tbp.TurnNumber;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn base.ProcessPacket(packet);\n\t}\n\t#endregion\n}\n"
  },
  {
    "path": "PokemonBattleEngineClient/Clients/ReplayClient.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Packets;\nusing Kermalis.PokemonBattleEngineClient.Views;\n\nnamespace Kermalis.PokemonBattleEngineClient.Clients;\n\ninternal sealed class ReplayClient : NonLocalClient\n{\n\tpublic override PBEBattle Battle { get; }\n\tpublic override PBETrainer? Trainer => null;\n\tpublic override BattleView BattleView { get; }\n\tpublic override bool HideNonOwned => false;\n\n\tpublic ReplayClient(string path, string name)\n\t: base(name)\n\t{\n\t\tBattle = PBEBattle.LoadReplay(path, new PBEPacketProcessor());\n\t\tBattleView = new BattleView(this);\n\t\tShowAllPokemon();\n\t\tStartPacketThread();\n\t}\n\n\tprotected override bool ProcessPacket(IPBEPacket packet)\n\t{\n\t\tswitch (packet)\n\t\t{\n\t\t\tcase PBEMovePPChangedPacket mpcp:\n\t\t\t{\n\t\t\t\tPBEBattlePokemon moveUser = mpcp.MoveUserTrainer.GetPokemon(mpcp.MoveUser);\n\t\t\t\tmoveUser.Moves[mpcp.Move]!.PP -= mpcp.AmountReduced;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEActionsRequestPacket _:\n\t\t\tcase PBESwitchInRequestPacket _: return true;\n\t\t}\n\t\treturn base.ProcessPacket(packet);\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineClient/Clients/SinglePlayerClient.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.DefaultData.AI;\nusing Kermalis.PokemonBattleEngine.Packets;\nusing Kermalis.PokemonBattleEngineClient.Views;\nusing System.Threading;\n\nnamespace Kermalis.PokemonBattleEngineClient.Clients;\n\ninternal sealed class SinglePlayerClient : BattleClient\n{\n\tpublic override PBEBattle Battle { get; }\n\tpublic override PBETrainer? Trainer { get; }\n\tpublic override BattleView BattleView { get; }\n\tpublic override bool HideNonOwned => true;\n\tprivate readonly PBEDDAI[] _ais;\n\n\tpublic SinglePlayerClient(PBEBattle b, string name) : base(name)\n\t{\n\t\tBattle = b;\n\t\tTrainer = b.Trainers[0];\n\t\tBattleView = new BattleView(this);\n\t\tb.OnNewEvent += SinglePlayerBattle_OnNewEvent;\n\t\tb.OnStateChanged += SinglePlayerBattle_OnStateChanged;\n\t\t_ais = new PBEDDAI[b.Trainers.Count - 1];\n\t\tfor (int i = 0; i < _ais.Length; i++)\n\t\t{\n\t\t\t_ais[i] = new PBEDDAI(b.Trainers[i + 1]);\n\t\t}\n\t\tShowAllPokemon();\n\t\tCreateBattleThread(b.Begin);\n\t}\n\tprivate static void CreateBattleThread(ThreadStart start)\n\t{\n\t\tnew Thread(start) { Name = \"Battle Thread\" }.Start();\n\t}\n\n\tprivate void SinglePlayerBattle_OnNewEvent(PBEBattle battle, IPBEPacket packet)\n\t{\n\t\tif (!ProcessPacket(packet))\n\t\t{\n\t\t\tThread.Sleep(WaitMilliseconds);\n\t\t}\n\t}\n\tprivate void SinglePlayerBattle_OnStateChanged(PBEBattle battle)\n\t{\n\t\tswitch (battle.BattleState)\n\t\t{\n\t\t\tcase PBEBattleState.Ended: battle.SaveReplay(\"SinglePlayer Battle.pbereplay\"); break;\n\t\t\tcase PBEBattleState.ReadyToRunSwitches: CreateBattleThread(battle.RunSwitches); break;\n\t\t\tcase PBEBattleState.ReadyToRunTurn: CreateBattleThread(battle.RunTurn); break;\n\t\t}\n\t}\n\n\tprivate void OnActionsReady(PBETurnAction[] acts)\n\t{\n\t\tCreateBattleThread(() => Trainer!.SelectActionsIfValid(out _, acts));\n\t}\n\tprivate void OnSwitchesReady(PBESwitchIn[] switches)\n\t{\n\t\tCreateBattleThread(() => Trainer!.SelectSwitchesIfValid(out _, switches));\n\t}\n\n\tprivate PBEDDAI GetAI(PBETrainer t)\n\t{\n\t\treturn _ais[t.Id - 1];\n\t}\n\n\tpublic override void Dispose()\n\t{\n\t\tBattle.SetEnded(); // Events unsubscribed\n\t}\n\n\tprotected override bool ProcessPacket(IPBEPacket packet)\n\t{\n\t\tswitch (packet)\n\t\t{\n\t\t\tcase PBEActionsRequestPacket arp:\n\t\t\t{\n\t\t\t\tPBETrainer t = arp.Trainer;\n\t\t\t\tif (t == Trainer)\n\t\t\t\t{\n\t\t\t\t\t_ = new ActionsBuilder(BattleView, Trainer, OnActionsReady);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tCreateBattleThread(GetAI(t).CreateActions);\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tcase PBESwitchInRequestPacket sirp:\n\t\t\t{\n\t\t\t\tPBETrainer t = sirp.Trainer;\n\t\t\t\tif (t == Trainer)\n\t\t\t\t{\n\t\t\t\t\t_ = new SwitchesBuilder(BattleView, sirp.Amount, OnSwitchesReady);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tCreateBattleThread(GetAI(t).CreateSwitches);\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn base.ProcessPacket(packet);\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineClient/Clients/SwitchesBuilder.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngineClient.Views;\nusing System;\n\nnamespace Kermalis.PokemonBattleEngineClient.Clients;\n\ninternal sealed class SwitchesBuilder\n{\n\tprivate readonly BattleView _bv;\n\tprivate Action<PBESwitchIn[]> _onSwitchesReady;\n\tpublic readonly byte SwitchesRequired;\n\n\tprivate int _index;\n\tprivate readonly PBESwitchIn[] _switches;\n\tprivate readonly PBEBattlePokemon[] _standBy;\n\tprivate readonly PBEFieldPosition[] _positionStandBy;\n\n\tpublic SwitchesBuilder(BattleView bv, byte amount, Action<PBESwitchIn[]> onSwitchesReady)\n\t{\n\t\t_bv = bv;\n\t\tbv.Actions.SwitchesBuilder = this;\n\t\t_onSwitchesReady = onSwitchesReady;\n\t\tSwitchesRequired = amount;\n\t\t_switches = new PBESwitchIn[amount];\n\t\t_standBy = new PBEBattlePokemon[amount];\n\t\t_positionStandBy = new PBEFieldPosition[amount];\n\t\tSwitchesLoop();\n\t}\n\n\tpublic bool IsStandBy(PBEBattlePokemon p)\n\t{\n\t\tint i = Array.IndexOf(_standBy, p);\n\t\treturn i != -1 && i < _index;\n\t}\n\tpublic bool IsStandBy(PBEFieldPosition p)\n\t{\n\t\tint i = Array.IndexOf(_positionStandBy, p);\n\t\treturn i != -1 && i < _index;\n\t}\n\n\tpublic void Pop()\n\t{\n\t\t_index--;\n\t\tSwitchesLoop();\n\t}\n\tpublic void Push(PBEBattlePokemon pkmn, PBEFieldPosition pos)\n\t{\n\t\t_switches[_index] = new PBESwitchIn(pkmn, pos);\n\t\t_standBy[_index] = pkmn;\n\t\t_positionStandBy[_index] = pos;\n\t\t_index++;\n\t\tSwitchesLoop();\n\t}\n\n\tprivate void SwitchesLoop()\n\t{\n\t\tif (_index == SwitchesRequired)\n\t\t{\n\t\t\t_bv.Actions.SwitchesBuilder = null;\n\t\t\t_onSwitchesReady(_switches);\n\t\t\t_onSwitchesReady = null!;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t_bv.AddMessage($\"You must send in {SwitchesRequired - _index} Pokémon.\", messageLog: false);\n\t\t\t_bv.Actions.DisplaySwitches(_index);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineClient/Infrastructure/BetterWrapPanel.cs",
    "content": "﻿// This source file is adapted from the Avalonia project. \n// (https://github.com/AvaloniaUI/Avalonia)\n\nusing Avalonia;\nusing Avalonia.Controls;\nusing Avalonia.Input;\nusing Avalonia.Layout;\nusing Avalonia.Utilities;\nusing static System.Math;\n\nnamespace Kermalis.PokemonBattleEngineClient.Infrastructure;\n\npublic sealed class BetterWrapPanel : Panel, INavigableContainer\n{\n\tpublic static readonly StyledProperty<Orientation> OrientationProperty = AvaloniaProperty.Register<BetterWrapPanel, Orientation>(nameof(Orientation), defaultValue: Orientation.Horizontal);\n\n\tpublic static readonly StyledProperty<HorizontalAlignment> HorizontalContentAlignmentProperty = AvaloniaProperty.Register<BetterWrapPanel, HorizontalAlignment>(nameof(HorizontalContentAlignment), defaultValue: HorizontalAlignment.Left);\n\n\tpublic static readonly StyledProperty<VerticalAlignment> VerticalContentAlignmentProperty = AvaloniaProperty.Register<BetterWrapPanel, VerticalAlignment>(nameof(VerticalContentAlignment), defaultValue: VerticalAlignment.Top);\n\n\tstatic BetterWrapPanel()\n\t{\n\t\tAffectsMeasure<BetterWrapPanel>(OrientationProperty);\n\t\tAffectsArrange<BetterWrapPanel>(HorizontalContentAlignmentProperty,\n\t\t\tVerticalContentAlignmentProperty);\n\t}\n\n\tpublic Orientation Orientation\n\t{\n\t\tget => GetValue(OrientationProperty);\n\t\tset => SetValue(OrientationProperty, value);\n\t}\n\n\tpublic HorizontalAlignment HorizontalContentAlignment\n\t{\n\t\tget => GetValue(HorizontalContentAlignmentProperty);\n\t\tset => SetValue(HorizontalContentAlignmentProperty, value);\n\t}\n\n\tpublic VerticalAlignment VerticalContentAlignment\n\t{\n\t\tget => GetValue(VerticalContentAlignmentProperty);\n\t\tset => SetValue(VerticalContentAlignmentProperty, value);\n\t}\n\n\tIInputElement INavigableContainer.GetControl(NavigationDirection direction, IInputElement from, bool wrap)\n\t{\n\t\tbool horiz = Orientation == Orientation.Horizontal;\n\t\tint index = Children.IndexOf((IControl)from);\n\n\t\tswitch (direction)\n\t\t{\n\t\t\tcase NavigationDirection.First:\n\t\t\t\tindex = 0;\n\t\t\t\tbreak;\n\t\t\tcase NavigationDirection.Last:\n\t\t\t\tindex = Children.Count - 1;\n\t\t\t\tbreak;\n\t\t\tcase NavigationDirection.Next:\n\t\t\t\t++index;\n\t\t\t\tbreak;\n\t\t\tcase NavigationDirection.Previous:\n\t\t\t\t--index;\n\t\t\t\tbreak;\n\t\t\tcase NavigationDirection.Left:\n\t\t\t\tindex = horiz ? index - 1 : -1;\n\t\t\t\tbreak;\n\t\t\tcase NavigationDirection.Right:\n\t\t\t\tindex = horiz ? index + 1 : -1;\n\t\t\t\tbreak;\n\t\t\tcase NavigationDirection.Up:\n\t\t\t\tindex = horiz ? -1 : index - 1;\n\t\t\t\tbreak;\n\t\t\tcase NavigationDirection.Down:\n\t\t\t\tindex = horiz ? -1 : index + 1;\n\t\t\t\tbreak;\n\t\t}\n\n\t\tif (index >= 0 && index < Children.Count)\n\t\t{\n\t\t\treturn Children[index];\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn null!; // ?\n\t\t}\n\t}\n\n\tprotected override Size MeasureOverride(Size constraint)\n\t{\n\t\tvar curLineSize = new UVSize(Orientation);\n\t\tvar panelSize = new UVSize(Orientation);\n\t\tvar uvConstraint = new UVSize(Orientation, constraint.Width, constraint.Height);\n\n\t\tvar childConstraint = new Size(constraint.Width, constraint.Height);\n\n\t\tfor (int i = 0, count = Children.Count; i < count; i++)\n\t\t{\n\t\t\tIControl child = Children[i];\n\t\t\tif (child is null)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t//Flow passes its own constrint to children\n\t\t\tchild.Measure(childConstraint);\n\n\t\t\t//this is the size of the child in UV space\n\t\t\tvar sz = new UVSize(Orientation, child.DesiredSize.Width, child.DesiredSize.Height);\n\n\t\t\tif (MathUtilities.GreaterThan(curLineSize.U + sz.U, uvConstraint.U)) //need to switch to another line\n\t\t\t{\n\t\t\t\tpanelSize.U = Max(curLineSize.U, panelSize.U);\n\t\t\t\tpanelSize.V += curLineSize.V;\n\t\t\t\tcurLineSize = sz;\n\n\t\t\t\tif (MathUtilities.GreaterThan(sz.U, uvConstraint.U)) //the element is wider then the constrint - give it a separate line                    \n\t\t\t\t{\n\t\t\t\t\tpanelSize.U = Max(sz.U, panelSize.U);\n\t\t\t\t\tpanelSize.V += sz.V;\n\t\t\t\t\tcurLineSize = new UVSize(Orientation);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse //continue to accumulate a line\n\t\t\t{\n\t\t\t\tcurLineSize.U += sz.U;\n\t\t\t\tcurLineSize.V = Max(sz.V, curLineSize.V);\n\t\t\t}\n\t\t}\n\n\t\t//the last line size, if any should be added\n\t\tpanelSize.U = Max(curLineSize.U, panelSize.U);\n\t\tpanelSize.V += curLineSize.V;\n\n\t\t//go from UV space to W/H space\n\t\treturn new Size(panelSize.Width, panelSize.Height);\n\t}\n\n\tprotected override Size ArrangeOverride(Size finalSize)\n\t{\n\t\tint firstInLine = 0;\n\t\tdouble accumulatedV = 0;\n\t\tvar curLineSize = new UVSize(Orientation);\n\t\tvar uvFinalSize = new UVSize(Orientation, finalSize.Width, finalSize.Height);\n\n\t\tfor (int i = 0; i < Children.Count; i++)\n\t\t{\n\t\t\tIControl child = Children[i];\n\t\t\tif (child is null)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tvar sz = new UVSize(Orientation, child.DesiredSize.Width, child.DesiredSize.Height);\n\n\t\t\tif (MathUtilities.GreaterThan(curLineSize.U + sz.U, uvFinalSize.U)) //need to switch to another line\n\t\t\t{\n\t\t\t\tArrangeLine(finalSize, accumulatedV, curLineSize, firstInLine, i);\n\n\t\t\t\taccumulatedV += curLineSize.V;\n\t\t\t\tcurLineSize = sz;\n\n\t\t\t\tif (MathUtilities.GreaterThan(sz.U, uvFinalSize.U)) //the element is wider then the constraint - give it a separate line                    \n\t\t\t\t{\n\t\t\t\t\t//switch to next line which only contain one element\n\t\t\t\t\tArrangeLine(finalSize, accumulatedV, sz, i, ++i);\n\n\t\t\t\t\taccumulatedV += sz.V;\n\t\t\t\t\tcurLineSize = new UVSize(Orientation);\n\t\t\t\t}\n\t\t\t\tfirstInLine = i;\n\t\t\t}\n\t\t\telse //continue to accumulate a line\n\t\t\t{\n\t\t\t\tcurLineSize.U += sz.U;\n\t\t\t\tcurLineSize.V = Max(sz.V, curLineSize.V);\n\t\t\t}\n\t\t}\n\n\t\t//arrange the last line, if any\n\t\tif (firstInLine < Children.Count)\n\t\t{\n\t\t\tArrangeLine(finalSize, accumulatedV, curLineSize, firstInLine, Children.Count);\n\t\t}\n\n\t\treturn finalSize;\n\t}\n\n\tprivate void ArrangeLine(Size finalSize, double v, UVSize line, int start, int end)\n\t{\n\t\tdouble u;\n\t\tbool isHorizontal = Orientation == Orientation.Horizontal;\n\n\t\tif (isHorizontal)\n\t\t{\n\t\t\tswitch (HorizontalContentAlignment)\n\t\t\t{\n\t\t\t\tcase HorizontalAlignment.Center:\n\t\t\t\t\tu = (finalSize.Width - line.U) / 2;\n\t\t\t\t\tbreak;\n\t\t\t\tcase HorizontalAlignment.Right:\n\t\t\t\t\tu = finalSize.Width - line.U;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tu = 0;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tswitch (VerticalContentAlignment)\n\t\t\t{\n\t\t\t\tcase VerticalAlignment.Center:\n\t\t\t\t\tu = (finalSize.Height - line.U) / 2;\n\t\t\t\t\tbreak;\n\t\t\t\tcase VerticalAlignment.Bottom:\n\t\t\t\t\tu = finalSize.Height - line.U;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tu = 0;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tfor (int i = start; i < end; i++)\n\t\t{\n\t\t\tIControl child = Children[i];\n\t\t\tif (child is not null)\n\t\t\t{\n\t\t\t\tvar childSize = new UVSize(Orientation, child.DesiredSize.Width, child.DesiredSize.Height);\n\t\t\t\tdouble layoutSlotU = childSize.U;\n\t\t\t\tchild.Arrange(new Rect(\n\t\t\t\t\tisHorizontal ? u : v,\n\t\t\t\t\tisHorizontal ? v : u,\n\t\t\t\t\tisHorizontal ? layoutSlotU : line.V,\n\t\t\t\t\tisHorizontal ? line.V : layoutSlotU));\n\t\t\t\tu += layoutSlotU;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate struct UVSize\n\t{\n\t\tinternal UVSize(Orientation orientation, double width, double height)\n\t\t{\n\t\t\tU = V = 0d;\n\t\t\t_orientation = orientation;\n\t\t\tWidth = width;\n\t\t\tHeight = height;\n\t\t}\n\n\t\tinternal UVSize(Orientation orientation)\n\t\t{\n\t\t\tU = V = 0d;\n\t\t\t_orientation = orientation;\n\t\t}\n\n\t\tinternal double U;\n\t\tinternal double V;\n\t\tprivate readonly Orientation _orientation;\n\n\t\tinternal double Width\n\t\t{\n\t\t\tget => _orientation == Orientation.Horizontal ? U : V;\n\t\t\tset\n\t\t\t{\n\t\t\t\tif (_orientation == Orientation.Horizontal)\n\t\t\t\t{\n\t\t\t\t\tU = value;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tV = value;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tinternal double Height\n\t\t{\n\t\t\tget => _orientation == Orientation.Horizontal ? V : U;\n\t\t\tset\n\t\t\t{\n\t\t\t\tif (_orientation == Orientation.Horizontal)\n\t\t\t\t{\n\t\t\t\t\tV = value;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tU = value;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineClient/Infrastructure/Converters.cs",
    "content": "﻿using Avalonia;\nusing Avalonia.Data.Converters;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Data.Utils;\nusing System;\nusing System.Collections.Generic;\nusing System.Globalization;\n\nnamespace Kermalis.PokemonBattleEngineClient.Infrastructure;\n\npublic sealed class FormToTextBitmapConverter : IMultiValueConverter\n{\n\tpublic static FormToTextBitmapConverter Instance { get; } = new();\n\tpublic object? Convert(IList<object> values, Type? targetType, object? parameter, CultureInfo? culture)\n\t{\n\t\tvar species = (PBESpecies)values[0];\n\t\tif (!PBEDataUtils.HasForms(species, true))\n\t\t{\n\t\t\treturn AvaloniaProperty.UnsetValue;\n\t\t}\n\t\tPBEForm form = true ? 0 : (PBEForm)values[1]; // TODO\n\t\tIPBEReadOnlyLocalizedString localized = PBEDataProvider.Instance.GetFormName(species, form);\n\t\treturn StringRenderer.Render(localized.FromGlobalLanguage(), parameter?.ToString());\n\t}\n}\npublic sealed class ObjectToTextBitmapConverter : IValueConverter\n{\n\tpublic static ObjectToTextBitmapConverter Instance { get; } = new();\n\tpublic object? Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture)\n\t{\n\t\tif (value is null)\n\t\t{\n\t\t\treturn AvaloniaProperty.UnsetValue;\n\t\t}\n\t\tIPBEReadOnlyLocalizedString? localized = null;\n\t\tswitch (value)\n\t\t{\n\t\t\tcase PBEAbility ability: localized = PBEDataProvider.Instance.GetAbilityName(ability); break;\n\t\t\tcase PBEGender gender: localized = PBEDataProvider.Instance.GetGenderName(gender); break;\n\t\t\tcase PBEItem item: localized = PBEDataProvider.Instance.GetItemName(item); break;\n\t\t\tcase IPBEReadOnlyLocalizedString l: localized = l; break;\n\t\t\tcase PBEMove move: localized = PBEDataProvider.Instance.GetMoveName(move); break;\n\t\t\tcase PBENature nature: localized = PBEDataProvider.Instance.GetNatureName(nature); break;\n\t\t\tcase PBESpecies species: localized = PBEDataProvider.Instance.GetSpeciesName(species); break;\n\t\t\tcase PBEStat stat: localized = PBEDataProvider.Instance.GetStatName(stat); break;\n\t\t\tcase PBEType type: localized = PBEDataProvider.Instance.GetTypeName(type); break;\n\t\t}\n\t\treturn StringRenderer.Render(localized is null ? value.ToString() : localized.FromGlobalLanguage(), parameter?.ToString()) ?? AvaloniaProperty.UnsetValue;\n\t}\n\tpublic object? ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)\n\t{\n\t\tthrow new NotImplementedException();\n\t}\n}\npublic sealed class SpeciesToMinispriteConverter : IValueConverter\n{\n\tpublic static SpeciesToMinispriteConverter Instance { get; } = new();\n\tpublic object? Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture)\n\t{\n\t\tPBESpecies species;\n\t\tPBEForm form;\n\t\tPBEGender gender;\n\t\tbool shiny;\n\t\tif (value is PBESpecies s)\n\t\t{\n\t\t\tspecies = s;\n\t\t\tform = 0;\n\t\t\tgender = PBEGender.Male;\n\t\t\tshiny = false;\n\t\t}\n\t\telse if (value is IPBEPokemon pkmn)\n\t\t{\n\t\t\tspecies = pkmn.Species;\n\t\t\tform = pkmn.Form;\n\t\t\tgender = pkmn.Gender;\n\t\t\tshiny = pkmn.Shiny;\n\t\t}\n\t\telse if (value is PBEBattlePokemon bpkmn)\n\t\t{\n\t\t\tbool useKnownInfo = parameter is bool b && b;\n\t\t\tspecies = useKnownInfo ? bpkmn.KnownSpecies : bpkmn.OriginalSpecies;\n\t\t\tform = useKnownInfo ? bpkmn.KnownForm : bpkmn.RevertForm;\n\t\t\tgender = useKnownInfo ? bpkmn.KnownGender : bpkmn.Gender;\n\t\t\tshiny = useKnownInfo ? bpkmn.KnownShiny : bpkmn.Shiny;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(value));\n\t\t}\n\t\treturn Utils.GetMinispriteBitmap(species, form, gender, shiny);\n\t}\n\tpublic object? ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture)\n\t{\n\t\tthrow new NotImplementedException();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineClient/Infrastructure/StringRenderer.cs",
    "content": "﻿using Avalonia;\nusing Avalonia.Media.Imaging;\nusing Avalonia.Platform;\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace Kermalis.PokemonBattleEngineClient.Infrastructure;\n\ninternal static class StringRenderer\n{\n\tprivate interface IStringRenderFont\n\t{\n\t\tstring FontId { get; }\n\t\tint FontHeight { get; }\n\t\tConcurrentDictionary<string, Bitmap> LoadedKeys { get; }\n\t\t(string OldKey, string NewKey)[] OverrideKeys { get; }\n\t\t// TODO: Cached text?\n\t}\n\tprivate class BattleHPFont : IStringRenderFont\n\t{\n\t\tpublic string FontId => \"BattleHP\";\n\t\tpublic int FontHeight => 8;\n\t\tpublic static BattleHPFont Instance { get; } = new();\n\t\tpublic ConcurrentDictionary<string, Bitmap> LoadedKeys { get; } = new();\n\t\tpublic (string OldKey, string NewKey)[] OverrideKeys { get; } = Array.Empty<(string OldKey, string NewKey)>();\n\t}\n\tprivate class BattleLevelFont : IStringRenderFont\n\t{\n\t\tpublic string FontId => \"BattleLevel\";\n\t\tpublic int FontHeight => 10;\n\t\tpublic static BattleLevelFont Instance { get; } = new();\n\t\tpublic ConcurrentDictionary<string, Bitmap> LoadedKeys { get; } = new();\n\t\tpublic (string OldKey, string NewKey)[] OverrideKeys { get; } = new (string OldKey, string NewKey)[]\n\t\t{\n\t\t\t\t(\"[LV]\", \"LV\")\n\t\t};\n\t}\n\tprivate class BattleNameFont : IStringRenderFont\n\t{\n\t\tpublic string FontId => \"BattleName\";\n\t\tpublic int FontHeight => 13;\n\t\tpublic static BattleNameFont Instance { get; } = new();\n\t\tpublic ConcurrentDictionary<string, Bitmap> LoadedKeys { get; } = new();\n\t\tpublic (string OldKey, string NewKey)[] OverrideKeys { get; } = new (string OldKey, string NewKey)[]\n\t\t{\n\t\t\t\t(\"♂\", \"246D\"),\n\t\t\t\t(\"♀\", \"246E\"),\n\t\t\t\t(\"[PK]\", \"2486\"),\n\t\t\t\t(\"[MN]\", \"2487\")\n\t\t};\n\t}\n\tprivate class DefaultFont : IStringRenderFont\n\t{\n\t\tpublic string FontId => \"Default\";\n\t\tpublic int FontHeight => 15;\n\t\tpublic static DefaultFont Instance { get; } = new();\n\t\tpublic ConcurrentDictionary<string, Bitmap> LoadedKeys { get; } = new();\n\t\tpublic (string OldKey, string NewKey)[] OverrideKeys { get; } = new (string OldKey, string NewKey)[]\n\t\t{\n\t\t\t\t(\"♂\", \"246D\"),\n\t\t\t\t(\"♀\", \"246E\"),\n\t\t\t\t(\"[PK]\", \"2486\"),\n\t\t\t\t(\"[MN]\", \"2487\")\n\t\t};\n\t}\n\n\t[return: NotNullIfNotNull(\"str\")]\n\tpublic static unsafe Bitmap? Render(string? str, string? style)\n\t{\n\t\tif (string.IsNullOrWhiteSpace(str))\n\t\t{\n\t\t\treturn null;\n\t\t}\n\t\tIStringRenderFont font;\n\t\tswitch (style)\n\t\t{\n\t\t\tcase \"BattleHP\": font = BattleHPFont.Instance; break;\n\t\t\tcase \"BattleLevel\": font = BattleLevelFont.Instance; break;\n\t\t\tcase \"BattleName\": font = BattleNameFont.Instance; break;\n\t\t\tdefault: font = DefaultFont.Instance; break;\n\t\t}\n\t\tuint primary = 0xFFFFFFFF,\n\t\t\t\tsecondary = 0xFF000000,\n\t\t\t\ttertiary = 0xFF808080;\n\t\tswitch (style)\n\t\t{\n\t\t\tcase \"BattleHP\": primary = 0xFFF7F7F7; secondary = 0xFF101010; tertiary = 0xFF9C9CA5; break;\n\t\t\tcase \"BattleLevel\":\n\t\t\tcase \"BattleName\": primary = 0xFFF7F7F7; secondary = 0xFF181818; break;\n\t\t\t//case \"BattleWhite\": secondary = 0xF0FFFFFF; break; // Looks horrible because of Avalonia's current issues\n\t\t\tcase \"MenuBlack\": primary = 0xFF5A5252; secondary = 0xFFA5A5AD; break;\n\t\t\tdefault: secondary = 0xFF848484; break;\n\t\t}\n\n\t\t// Measure how large the string will end up\n\t\tvar lineWidths = new List<int>(20);\n\t\tint stringWidth = 0,\n\t\t\t\tstringHeight = font.FontHeight,\n\t\t\t\tcurLineWidth = 0;\n\t\tvoid RecordLineWidth()\n\t\t{\n\t\t\tif (curLineWidth > stringWidth)\n\t\t\t{\n\t\t\t\tstringWidth = curLineWidth;\n\t\t\t}\n\t\t\tlineWidths.Add(curLineWidth);\n\t\t}\n\t\tint index = 0;\n\t\tvar keys = new List<(string Key, Bitmap? Bitmap)>();\n\t\twhile (index < str.Length)\n\t\t{\n\t\t\tchar c = str[index];\n\t\t\tif (c == '\\r') // Ignore\n\t\t\t{\n\t\t\t\tindex++;\n\t\t\t}\n\t\t\telse if (c == '\\n')\n\t\t\t{\n\t\t\t\tindex++;\n\t\t\t\tstringHeight += font.FontHeight + 1;\n\t\t\t\tRecordLineWidth();\n\t\t\t\tcurLineWidth = 0;\n\t\t\t\tkeys.Add((\"\\n\", null));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tstring? key = null;\n\t\t\t\tfor (int i = 0; i < font.OverrideKeys.Length; i++)\n\t\t\t\t{\n\t\t\t\t\t(string oldKey, string newKey) = font.OverrideKeys[i];\n\t\t\t\t\tif (index + oldKey.Length <= str.Length && str.Substring(index, oldKey.Length) == oldKey)\n\t\t\t\t\t{\n\t\t\t\t\t\tkey = newKey;\n\t\t\t\t\t\tindex += oldKey.Length;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (key is null)\n\t\t\t\t{\n\t\t\t\t\tkey = ((ushort)str[index]).ToString(\"X4\");\n\t\t\t\t\tindex++;\n\t\t\t\t}\n\t\t\t\tstring resource = \"FONT.\" + font.FontId + \".F_\" + key + \".png\";\n\t\t\t\tif (!Utils.DoesResourceExist(resource))\n\t\t\t\t{\n\t\t\t\t\tkey = \"003F\"; // 003F is '?'\n\t\t\t\t}\n\t\t\t\tif (!font.LoadedKeys.TryGetValue(key, out Bitmap? bmp))\n\t\t\t\t{\n\t\t\t\t\tbmp = new Bitmap(Utils.GetResourceStream(resource));\n\t\t\t\t\tfont.LoadedKeys.TryAdd(key, bmp);\n\t\t\t\t}\n\t\t\t\tcurLineWidth += bmp.PixelSize.Width;\n\t\t\t\tkeys.Add((key, bmp));\n\t\t\t}\n\t\t}\n\t\tRecordLineWidth();\n\n\t\t// Draw the string\n\t\tvar dpi = new Vector(96, 96);\n\t\tvar wb = new WriteableBitmap(new PixelSize(stringWidth, stringHeight), dpi, PixelFormat.Bgra8888, AlphaFormat.Premul);\n\t\tusing (IRenderTarget rtb = Utils.RenderInterface.CreateRenderTarget(new[] { new WriteableBitmapSurface(wb) }))\n\t\tusing (IDrawingContextImpl ctx = rtb.CreateDrawingContext(null))\n\t\t{\n\t\t\tint x = 0,\n\t\t\t\t\ty = 0;\n\t\t\tfor (int i = 0; i < keys.Count; i++)\n\t\t\t{\n\t\t\t\t(string key, Bitmap? bmp) = keys[i];\n\t\t\t\tif (key == \"\\n\")\n\t\t\t\t{\n\t\t\t\t\ty += font.FontHeight + 1;\n\t\t\t\t\tx = 0;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tvar size = new Size(bmp!.PixelSize.Width, bmp.PixelSize.Height); // TODO: Verify and/or try other options with different dpi\n\t\t\t\t\tctx.DrawBitmap(bmp.PlatformImpl, 1d, new Rect(size), new Rect(new Point(x, y), size));\n\t\t\t\t\tx += bmp.PixelSize.Width;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// Edit colors\n\t\tusing (ILockedFramebuffer l = wb.Lock())\n\t\t{\n\t\t\tlong startAddress = l.Address.ToInt64();\n\t\t\tfor (int i = 0; i < lineWidths.Count; i++)\n\t\t\t{\n\t\t\t\tint w = lineWidths[i];\n\t\t\t\tfor (int x = 0; x < w; x++)\n\t\t\t\t{\n\t\t\t\t\tfor (int y = 0; y < font.FontHeight; y++)\n\t\t\t\t\t{\n\t\t\t\t\t\tuint* address = (uint*)(startAddress + (x * sizeof(uint)) + ((y + ((font.FontHeight + 1) * i)) * l.RowBytes));\n\t\t\t\t\t\tuint pixel = *address;\n\t\t\t\t\t\tif (pixel == 0xFFFFFFFF)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t*address = primary;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pixel == 0xFF000000)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t*address = secondary;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pixel == 0xFF808080)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t*address = tertiary;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn wb;\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineClient/Infrastructure/Utils.cs",
    "content": "﻿using Avalonia;\nusing Avalonia.Media.Imaging;\nusing Avalonia.Platform;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Data.Utils;\nusing Kermalis.PokemonBattleEngine.DefaultData;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text;\n\nnamespace Kermalis.PokemonBattleEngineClient.Infrastructure;\n\npublic static class Utils\n{\n\tprivate const string AssemblyPrefix = \"Kermalis.PokemonBattleEngineClient.\";\n\tprivate static readonly Assembly _assembly = Assembly.GetExecutingAssembly();\n\tprivate static readonly string[] _resources = _assembly.GetManifestResourceNames();\n\tprivate static IPlatformRenderInterface? _renderInterface = null;\n\tpublic static IPlatformRenderInterface RenderInterface\n\t{\n\t\tget\n\t\t{\n\t\t\t// This is done because the static constructor of Utils is called (by SetWorkingDirectory) before the Avalonia app is built\n\t\t\tif (_renderInterface is null)\n\t\t\t{\n\t\t\t\t_renderInterface = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();\n\t\t\t}\n\t\t\treturn _renderInterface;\n\t\t}\n\t}\n\n\tstatic Utils()\n\t{\n\t\t_femaleMinispriteLookup = new List<PBESpecies>();\n\t\t_femaleSpriteLookup = new List<PBESpecies>();\n\t\tstatic void Add(string resource, List<PBESpecies> list)\n\t\t{\n\t\t\tusing (var reader = new StreamReader(GetResourceStream(resource)))\n\t\t\t{\n\t\t\t\tstring? line;\n\t\t\t\twhile ((line = reader.ReadLine()) is not null)\n\t\t\t\t{\n\t\t\t\t\tif (!Enum.TryParse(line, out PBESpecies species))\n\t\t\t\t\t{\n\t\t\t\t\t\tthrow new InvalidDataException($\"Failed to parse \\\"{resource}\\\"\");\n\t\t\t\t\t}\n\t\t\t\t\tlist.Add(species);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tAdd(\"PKMN.FemaleMinispriteLookup.txt\", _femaleMinispriteLookup);\n\t\tAdd(\"PKMN.FemaleSpriteLookup.txt\", _femaleSpriteLookup);\n\t}\n\n\tprivate static readonly object _resourceExistsCacheLockObj = new();\n\tprivate static readonly Dictionary<string, bool> _resourceExistsCache = new();\n\tpublic static bool DoesResourceExist(string resource)\n\t{\n\t\tlock (_resourceExistsCacheLockObj)\n\t\t{\n\t\t\tif (!_resourceExistsCache.TryGetValue(resource, out bool value))\n\t\t\t{\n\t\t\t\tvalue = Array.IndexOf(_resources, AssemblyPrefix + resource) != -1;\n\t\t\t\t_resourceExistsCache.Add(resource, value);\n\t\t\t}\n\t\t\treturn value;\n\t\t}\n\t}\n\tpublic static Stream GetResourceStream(string resource)\n\t{\n\t\tStream? ret = _assembly.GetManifestResourceStream(AssemblyPrefix + resource);\n\t\tif (ret is null)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(resource), \"Resource not found: \" + resource);\n\t\t}\n\t\treturn ret;\n\t}\n\tpublic static Uri GetResourceUri(string resource)\n\t{\n\t\treturn new Uri(\"resm:\" + AssemblyPrefix + resource);\n\t}\n\n\tpublic static string WorkingDirectory { get; private set; } = null!;\n\tpublic static void SetWorkingDirectory(string workingDirectory)\n\t{\n\t\tPBEDefaultDataProvider.InitEngine(workingDirectory);\n\t\tWorkingDirectory = workingDirectory;\n\t}\n\n\tprivate static readonly List<PBESpecies> _femaleMinispriteLookup;\n\tprivate static readonly List<PBESpecies> _femaleSpriteLookup;\n\tprivate static bool HasFemaleSprite(PBESpecies species, bool minisprite)\n\t{\n\t\treturn (minisprite ? _femaleMinispriteLookup : _femaleSpriteLookup).Contains(species);\n\t}\n\tpublic static Bitmap GetMinispriteBitmap(PBESpecies species, PBEForm form, PBEGender gender, bool shiny)\n\t{\n\t\tstring speciesStr = PBEDataUtils.GetNameOfForm(species, form) ?? species.ToString();\n\t\tstring genderStr = gender == PBEGender.Female && HasFemaleSprite(species, true) ? \"_F\" : string.Empty;\n\t\treturn new Bitmap(GetResourceStream(\"PKMN.PKMN_\" + speciesStr + (shiny ? \"_S\" : string.Empty) + genderStr + \".png\"));\n\t}\n\tpublic static Uri GetPokemonSpriteUri(PBEBattlePokemon pkmn, bool backSprite)\n\t{\n\t\treturn GetPokemonSpriteUri(pkmn.KnownSpecies, pkmn.KnownForm, pkmn.KnownShiny, pkmn.KnownGender, pkmn.KnownStatus2.HasFlag(PBEStatus2.Substitute), backSprite);\n\t}\n\tpublic static Uri GetPokemonSpriteUri(IPBEPokemon pkmn)\n\t{\n\t\treturn GetPokemonSpriteUri(pkmn.Species, pkmn.Form, pkmn.Shiny, pkmn.Gender, false, false);\n\t}\n\tpublic static Uri GetPokemonSpriteUri(PBESpecies species, PBEForm form, bool shiny, PBEGender gender, bool behindSubstitute, bool backSprite)\n\t{\n\t\tstring orientation = backSprite ? \"_B\" : \"_F\";\n\t\tif (behindSubstitute)\n\t\t{\n\t\t\treturn GetResourceUri(\"PKMN.STATUS2_Substitute\" + orientation + \".gif\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstring speciesStr = PBEDataUtils.GetNameOfForm(species, form) ?? species.ToString();\n\t\t\tstring genderStr = gender == PBEGender.Female && HasFemaleSprite(species, false) ? \"_F\" : string.Empty;\n\t\t\treturn GetResourceUri(\"PKMN.PKMN_\" + speciesStr + orientation + (shiny ? \"_S\" : string.Empty) + genderStr + \".gif\");\n\t\t}\n\t}\n\n\tpublic static string CustomPokemonToString(PBEBattlePokemon pkmn, bool useKnownInfo)\n\t{\n\t\tvar sb = new StringBuilder();\n\n\t\tstring GetTeamNickname(PBEBattlePokemon p)\n\t\t{\n\t\t\treturn $\"{p.Trainer.Name}'s {(useKnownInfo ? p.KnownNickname : p.Nickname)}\";\n\t\t}\n\n\t\tvoid AddStatChanges()\n\t\t{\n\t\t\tPBEStat[] statChanges = pkmn.GetChangedStats();\n\t\t\tif (statChanges.Length > 0)\n\t\t\t{\n\t\t\t\tvar statStrs = new List<string>(7);\n\t\t\t\tif (Array.IndexOf(statChanges, PBEStat.Attack) != -1)\n\t\t\t\t{\n\t\t\t\t\tstatStrs.Add($\"[A] x{PBEBattle.GetStatChangeModifier(pkmn.AttackChange, false):0.00}\");\n\t\t\t\t}\n\t\t\t\tif (Array.IndexOf(statChanges, PBEStat.Defense) != -1)\n\t\t\t\t{\n\t\t\t\t\tstatStrs.Add($\"[D] x{PBEBattle.GetStatChangeModifier(pkmn.DefenseChange, false):0.00}\");\n\t\t\t\t}\n\t\t\t\tif (Array.IndexOf(statChanges, PBEStat.SpAttack) != -1)\n\t\t\t\t{\n\t\t\t\t\tstatStrs.Add($\"[SA] x{PBEBattle.GetStatChangeModifier(pkmn.SpAttackChange, false):0.00}\");\n\t\t\t\t}\n\t\t\t\tif (Array.IndexOf(statChanges, PBEStat.SpDefense) != -1)\n\t\t\t\t{\n\t\t\t\t\tstatStrs.Add($\"[SD] x{PBEBattle.GetStatChangeModifier(pkmn.SpDefenseChange, false):0.00}\");\n\t\t\t\t}\n\t\t\t\tif (Array.IndexOf(statChanges, PBEStat.Speed) != -1)\n\t\t\t\t{\n\t\t\t\t\tstatStrs.Add($\"[S] x{PBEBattle.GetStatChangeModifier(pkmn.SpeedChange, false):0.00}\");\n\t\t\t\t}\n\t\t\t\tif (Array.IndexOf(statChanges, PBEStat.Accuracy) != -1)\n\t\t\t\t{\n\t\t\t\t\tstatStrs.Add($\"[AC] x{PBEBattle.GetStatChangeModifier(pkmn.AccuracyChange, true):0.00}\");\n\t\t\t\t}\n\t\t\t\tif (Array.IndexOf(statChanges, PBEStat.Evasion) != -1)\n\t\t\t\t{\n\t\t\t\t\tstatStrs.Add($\"[E] x{PBEBattle.GetStatChangeModifier(pkmn.EvasionChange, true):0.00}\");\n\t\t\t\t}\n\t\t\t\tsb.AppendLine($\"Stat changes: {string.Join(\", \", statStrs)}\");\n\t\t\t}\n\t\t}\n\t\tvoid AddStatus1()\n\t\t{\n\t\t\tif (pkmn.Status1 != PBEStatus1.None)\n\t\t\t{\n\t\t\t\tsb.AppendLine($\"Main status: {pkmn.Status1}\");\n\t\t\t\tif (pkmn.Status1 == PBEStatus1.Asleep)\n\t\t\t\t{\n\t\t\t\t\tsb.AppendLine($\"Asleep turns: {pkmn.Status1Counter}\");\n\t\t\t\t}\n\t\t\t\telse if (pkmn.Status1 == PBEStatus1.BadlyPoisoned)\n\t\t\t\t{\n\t\t\t\t\tsb.AppendLine($\"Toxic counter: {pkmn.Status1Counter}\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tvoid AddStatus2(PBEStatus2 status2)\n\t\t{\n\t\t\tstatus2 &= ~PBEStatus2.Flinching; // Don't show flinching\n\t\t\tsb.AppendLine($\"Volatile status: {status2}\");\n\t\t\tif (status2.HasFlag(PBEStatus2.Disguised))\n\t\t\t{\n\t\t\t\tstring formStr = PBEDataUtils.HasForms(pkmn.KnownSpecies, false) ? $\" ({PBEDataProvider.Instance.GetFormName(pkmn.KnownSpecies, pkmn.KnownForm).FromGlobalLanguage()})\" : string.Empty;\n\t\t\t\tsb.AppendLine($\"Disguised as: {pkmn.KnownNickname}/{PBEDataProvider.Instance.GetSpeciesName(pkmn.KnownSpecies).FromGlobalLanguage()}{formStr} {pkmn.KnownGender.ToSymbol()}\");\n\t\t\t}\n\t\t\tif (pkmn.Battle.BattleFormat != PBEBattleFormat.Single)\n\t\t\t{\n\t\t\t\tif (status2.HasFlag(PBEStatus2.Infatuated))\n\t\t\t\t{\n\t\t\t\t\tsb.AppendLine($\"Infatuated with: {GetTeamNickname(pkmn.InfatuatedWithPokemon!)}\");\n\t\t\t\t}\n\t\t\t\tif (status2.HasFlag(PBEStatus2.LeechSeed))\n\t\t\t\t{\n\t\t\t\t\tsb.AppendLine($\"Seeded position: {pkmn.SeededTeam!.CombinedName}'s {pkmn.SeededPosition}\");\n\t\t\t\t}\n\t\t\t\tif (status2.HasFlag(PBEStatus2.LockOn))\n\t\t\t\t{\n\t\t\t\t\tsb.AppendLine($\"Taking aim at: {GetTeamNickname(pkmn.LockOnPokemon!)}\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (useKnownInfo)\n\t\t{\n\t\t\tIPBEPokemonData pData = PBEDataProvider.Instance.GetPokemonData(pkmn.KnownSpecies, pkmn.KnownForm);\n\t\t\tstring formStr = PBEDataUtils.HasForms(pkmn.KnownSpecies, false) ? $\" ({PBEDataProvider.Instance.GetFormName(pkmn.KnownSpecies, pkmn.KnownForm).FromGlobalLanguage()})\" : string.Empty;\n\t\t\tsb.AppendLine($\"{pkmn.KnownNickname}/{PBEDataProvider.Instance.GetSpeciesName(pkmn.KnownSpecies).FromGlobalLanguage()}{formStr} {(pkmn.KnownStatus2.HasFlag(PBEStatus2.Transformed) ? pkmn.Gender.ToSymbol() : pkmn.KnownGender.ToSymbol())} Lv.{pkmn.Level}\");\n\t\t\tsb.AppendLine($\"HP: {pkmn.HPPercentage:P2}\");\n\t\t\tsb.Append($\"Known types: {PBEDataProvider.Instance.GetTypeName(pkmn.KnownType1).FromGlobalLanguage()}\");\n\t\t\tif (pkmn.KnownType2 != PBEType.None)\n\t\t\t{\n\t\t\t\tsb.Append($\"/{PBEDataProvider.Instance.GetTypeName(pkmn.KnownType2).FromGlobalLanguage()}\");\n\t\t\t}\n\t\t\tsb.AppendLine();\n\t\t\tif (pkmn.FieldPosition != PBEFieldPosition.None)\n\t\t\t{\n\t\t\t\tsb.AppendLine($\"Position: {pkmn.Team.CombinedName}'s {pkmn.FieldPosition}\");\n\t\t\t}\n\t\t\tAddStatus1();\n\t\t\tif (pkmn.FieldPosition != PBEFieldPosition.None)\n\t\t\t{\n\t\t\t\tif (pkmn.KnownStatus2 != PBEStatus2.None)\n\t\t\t\t{\n\t\t\t\t\tAddStatus2(pkmn.KnownStatus2);\n\t\t\t\t}\n\t\t\t}\n\t\t\tPBEDataUtils.GetStatRange(pData, PBEStat.HP, pkmn.Level, pkmn.Battle.Settings, out ushort lowHP, out ushort highHP);\n\t\t\tPBEDataUtils.GetStatRange(pData, PBEStat.Attack, pkmn.Level, pkmn.Battle.Settings, out ushort lowAttack, out ushort highAttack);\n\t\t\tPBEDataUtils.GetStatRange(pData, PBEStat.Defense, pkmn.Level, pkmn.Battle.Settings, out ushort lowDefense, out ushort highDefense);\n\t\t\tPBEDataUtils.GetStatRange(pData, PBEStat.SpAttack, pkmn.Level, pkmn.Battle.Settings, out ushort lowSpAttack, out ushort highSpAttack);\n\t\t\tPBEDataUtils.GetStatRange(pData, PBEStat.SpDefense, pkmn.Level, pkmn.Battle.Settings, out ushort lowSpDefense, out ushort highSpDefense);\n\t\t\tPBEDataUtils.GetStatRange(pData, PBEStat.Speed, pkmn.Level, pkmn.Battle.Settings, out ushort lowSpeed, out ushort highSpeed);\n\t\t\tsb.AppendLine($\"Stat range: [HP] {lowHP}-{highHP}, [A] {lowAttack}-{highAttack}, [D] {lowDefense}-{highDefense}, [SA] {lowSpAttack}-{highSpAttack}, [SD] {lowSpDefense}-{highSpDefense}, [S] {lowSpeed}-{highSpeed}, [W] {pkmn.KnownWeight:0.0}\");\n\t\t\tif (pkmn.FieldPosition != PBEFieldPosition.None)\n\t\t\t{\n\t\t\t\tAddStatChanges();\n\t\t\t}\n\t\t\tif (pkmn.KnownAbility == PBEAbility.MAX)\n\t\t\t{\n\t\t\t\tsb.AppendLine($\"Possible abilities: {string.Join(\", \", pData.Abilities.Select(a => PBEDataProvider.Instance.GetAbilityName(a).FromGlobalLanguage()))}\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tsb.AppendLine($\"Known ability: {PBEDataProvider.Instance.GetAbilityName(pkmn.KnownAbility).FromGlobalLanguage()}\");\n\t\t\t}\n\t\t\tsb.AppendLine($\"Known item: {(pkmn.KnownItem == (PBEItem)ushort.MaxValue ? \"???\" : PBEDataProvider.Instance.GetItemName(pkmn.KnownItem).FromGlobalLanguage())}\");\n\t\t\tsb.Append(\"Known moves: \");\n\t\t\tfor (int i = 0; i < pkmn.Battle.Settings.NumMoves; i++)\n\t\t\t{\n\t\t\t\tPBEBattleMoveset.PBEBattleMovesetSlot slot = pkmn.KnownMoves[i];\n\t\t\t\tPBEMove move = slot.Move;\n\t\t\t\tint pp = slot.PP;\n\t\t\t\tint maxPP = slot.MaxPP;\n\t\t\t\tif (i > 0)\n\t\t\t\t{\n\t\t\t\t\tsb.Append(\", \");\n\t\t\t\t}\n\t\t\t\tsb.Append(move == PBEMove.MAX ? \"???\" : PBEDataProvider.Instance.GetMoveName(move).FromGlobalLanguage());\n\t\t\t\tif (move != PBEMove.None && move != PBEMove.MAX)\n\t\t\t\t{\n\t\t\t\t\tsb.Append($\" ({pp}{(maxPP == 0 ? \")\" : $\"/{maxPP})\")}\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstring formStr = PBEDataUtils.HasForms(pkmn.Species, false) ? $\" ({PBEDataProvider.Instance.GetFormName(pkmn).FromGlobalLanguage()})\" : string.Empty;\n\t\t\tsb.AppendLine($\"{pkmn.Nickname}/{PBEDataProvider.Instance.GetSpeciesName(pkmn.Species).FromGlobalLanguage()}{formStr} {pkmn.Gender.ToSymbol()} Lv.{pkmn.Level}\");\n\t\t\tsb.AppendLine($\"HP: {pkmn.HP}/{pkmn.MaxHP} ({pkmn.HPPercentage:P2})\");\n\t\t\tsb.Append($\"Types: {PBEDataProvider.Instance.GetTypeName(pkmn.Type1).FromGlobalLanguage()}\");\n\t\t\tif (pkmn.Type2 != PBEType.None)\n\t\t\t{\n\t\t\t\tsb.Append($\"/{PBEDataProvider.Instance.GetTypeName(pkmn.Type2).FromGlobalLanguage()}\");\n\t\t\t}\n\t\t\tsb.AppendLine();\n\t\t\tif (pkmn.FieldPosition != PBEFieldPosition.None)\n\t\t\t{\n\t\t\t\tsb.AppendLine($\"Position: {pkmn.Team.CombinedName}'s {pkmn.FieldPosition}\");\n\t\t\t}\n\t\t\tAddStatus1();\n\t\t\tif (pkmn.FieldPosition != PBEFieldPosition.None && pkmn.Status2 != PBEStatus2.None)\n\t\t\t{\n\t\t\t\tAddStatus2(pkmn.Status2);\n\t\t\t}\n\t\t\tsb.AppendLine($\"Stats: [A] {pkmn.Attack}, [D] {pkmn.Defense}, [SA] {pkmn.SpAttack}, [SD] {pkmn.SpDefense}, [S] {pkmn.Speed}, [W] {pkmn.Weight:0.0}\");\n\t\t\tif (pkmn.FieldPosition != PBEFieldPosition.None)\n\t\t\t{\n\t\t\t\tAddStatChanges();\n\t\t\t}\n\t\t\tsb.AppendLine($\"Ability: {PBEDataProvider.Instance.GetAbilityName(pkmn.Ability).FromGlobalLanguage()}\");\n\t\t\tsb.AppendLine($\"Item: {PBEDataProvider.Instance.GetItemName(pkmn.Item).FromGlobalLanguage()}\");\n\t\t\tif (pkmn.Moves.Contains(PBEMoveEffect.Frustration) || pkmn.Moves.Contains(PBEMoveEffect.Return))\n\t\t\t{\n\t\t\t\tsb.AppendLine($\"Friendship: {pkmn.Friendship} ({pkmn.Friendship / (float)byte.MaxValue:P2})\");\n\t\t\t}\n\t\t\tif (pkmn.Moves.Contains(PBEMoveEffect.HiddenPower))\n\t\t\t{\n\t\t\t\tPBEReadOnlyStatCollection ivs = pkmn.IndividualValues!;\n\t\t\t\tsb.AppendLine($\"{PBEDataProvider.Instance.GetMoveName(PBEMove.HiddenPower).FromGlobalLanguage()}: {PBEDataProvider.Instance.GetTypeName(ivs.GetHiddenPowerType()).FromGlobalLanguage()}|{ivs.GetHiddenPowerBasePower(pkmn.Battle.Settings)}\");\n\t\t\t}\n\t\t\tsb.Append(\"Moves: \");\n\t\t\tfor (int i = 0; i < pkmn.Battle.Settings.NumMoves; i++)\n\t\t\t{\n\t\t\t\tPBEBattleMoveset.PBEBattleMovesetSlot slot = pkmn.Moves[i];\n\t\t\t\tPBEMove move = slot.Move;\n\t\t\t\tif (i > 0)\n\t\t\t\t{\n\t\t\t\t\tsb.Append(\", \");\n\t\t\t\t}\n\t\t\t\tsb.Append(PBEDataProvider.Instance.GetMoveName(slot.Move).FromGlobalLanguage());\n\t\t\t\tif (move != PBEMove.None)\n\t\t\t\t{\n\t\t\t\t\tsb.Append($\" ({slot.PP}/{slot.MaxPP})\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tsb.AppendLine();\n\t\t\tsb.Append($\"Usable moves: {string.Join(\", \", pkmn.GetUsableMoves().Select(m => PBEDataProvider.Instance.GetMoveName(m).FromGlobalLanguage()))}\");\n\t\t}\n\t\treturn sb.ToString();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineClient/Infrastructure/WriteableBitmapSurface.cs",
    "content": "﻿using Avalonia.Controls.Platform.Surfaces;\nusing Avalonia.Media.Imaging;\nusing Avalonia.Platform;\n\nnamespace Kermalis.PokemonBattleEngineClient.Infrastructure;\n\ninternal sealed class WriteableBitmapSurface : IFramebufferPlatformSurface\n{\n\tprivate readonly WriteableBitmap _bitmap;\n\tpublic WriteableBitmapSurface(WriteableBitmap bmp)\n\t{\n\t\t_bitmap = bmp;\n\t}\n\tpublic ILockedFramebuffer Lock()\n\t{\n\t\treturn _bitmap.Lock();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineClient/MainWindow.xaml",
    "content": "﻿<Window xmlns=\"https://github.com/avaloniaui\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:views=\"clr-namespace:Kermalis.PokemonBattleEngineClient.Views;assembly=PokemonBattleEngineClient\"\n        x:Class=\"Kermalis.PokemonBattleEngineClient.MainWindow\"\n        MinWidth=\"896\" MinHeight=\"618\" Width=\"896\" Height=\"618\" WindowStartupLocation=\"CenterScreen\" Title=\"Pokémon Battle Engine\">\n\t<views:MainView Name=\"Main\" />\n</Window>"
  },
  {
    "path": "PokemonBattleEngineClient/MainWindow.xaml.cs",
    "content": "﻿using Avalonia.Controls;\nusing Avalonia.Markup.Xaml;\nusing Kermalis.PokemonBattleEngineClient.Views;\n\nnamespace Kermalis.PokemonBattleEngineClient;\n\npublic sealed class MainWindow : Window\n{\n\tpublic MainWindow()\n\t{\n\t\t// TODO: iOS does not support dynamically loading assemblies\n\t\t// so we must refer to this resource DLL statically. For\n\t\t// now I am doing that here. But we need a better solution!!\n\t\tvar theme = new Avalonia.Themes.Default.DefaultTheme();\n\t\ttheme.TryGetResource(\"Button\", out _);\n\t\tAvaloniaXamlLoader.Load(this);\n\t}\n\n\tprotected override bool HandleClosing()\n\t{\n\t\tthis.FindControl<MainView>(\"Main\").HandleClosing();\n\t\treturn base.HandleClosing();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineClient/Models/MoveInfo.cs",
    "content": "﻿using Avalonia.Media;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.DefaultData;\nusing ReactiveUI;\nusing System;\nusing System.Collections.Generic;\nusing System.Reactive;\nusing System.Text;\n\nnamespace Kermalis.PokemonBattleEngineClient.Models;\n\npublic sealed class MoveInfo\n{\n\tprivate static Dictionary<PBEType, (SolidColorBrush Brush, SolidColorBrush BorderBrush)> _typeToBrush = null!;\n\tinternal static void CreateBrushes()\n\t{\n\t\t_typeToBrush = new Dictionary<PBEType, (SolidColorBrush Brush, SolidColorBrush BorderBrush)>\n\t\t{\n\t\t\t{ PBEType.None, (new SolidColorBrush(Color.FromRgb(146, 154, 156)), new SolidColorBrush(Color.FromRgb(83, 89, 88))) },\n\t\t\t{ PBEType.Bug, (new SolidColorBrush(Color.FromRgb(162, 212, 56)), new SolidColorBrush(Color.FromRgb(87, 127, 12))) },\n\t\t\t{ PBEType.Dark, (new SolidColorBrush(Color.FromRgb(106, 122, 156)), new SolidColorBrush(Color.FromRgb(64, 79, 109))) },\n\t\t\t{ PBEType.Dragon, (new SolidColorBrush(Color.FromRgb(80, 136, 188)), new SolidColorBrush(Color.FromRgb(6, 83, 137))) },\n\t\t\t{ PBEType.Electric, (new SolidColorBrush(Color.FromRgb(246, 216, 48)), new SolidColorBrush(Color.FromRgb(173, 148, 24))) },\n\t\t\t{ PBEType.Fighting, (new SolidColorBrush(Color.FromRgb(244, 100, 138)), new SolidColorBrush(Color.FromRgb(153, 62, 86))) },\n\t\t\t{ PBEType.Fire, (new SolidColorBrush(Color.FromRgb(255, 152, 56)), new SolidColorBrush(Color.FromRgb(196, 86, 13))) },\n\t\t\t{ PBEType.Flying, (new SolidColorBrush(Color.FromRgb(80, 124, 212)), new SolidColorBrush(Color.FromRgb(36, 75, 153))) },\n\t\t\t{ PBEType.Ghost, (new SolidColorBrush(Color.FromRgb(94, 100, 208)), new SolidColorBrush(Color.FromRgb(49, 56, 137))) },\n\t\t\t{ PBEType.Grass, (new SolidColorBrush(Color.FromRgb(64, 208, 112)), new SolidColorBrush(Color.FromRgb(27, 135, 63))) },\n\t\t\t{ PBEType.Ground, (new SolidColorBrush(Color.FromRgb(232, 130, 68)), new SolidColorBrush(Color.FromRgb(150, 83, 45))) },\n\t\t\t{ PBEType.Ice, (new SolidColorBrush(Color.FromRgb(98, 204, 212)), new SolidColorBrush(Color.FromRgb(52, 128, 145))) },\n\t\t\t{ PBEType.Normal, (new SolidColorBrush(Color.FromRgb(146, 154, 156)), new SolidColorBrush(Color.FromRgb(83, 89, 88))) },\n\t\t\t{ PBEType.Poison, (new SolidColorBrush(Color.FromRgb(188, 82, 232)), new SolidColorBrush(Color.FromRgb(117, 52, 145))) },\n\t\t\t{ PBEType.Psychic, (new SolidColorBrush(Color.FromRgb(255, 136, 130)), new SolidColorBrush(Color.FromRgb(173, 81, 89))) },\n\t\t\t{ PBEType.Rock, (new SolidColorBrush(Color.FromRgb(196, 174, 112)), new SolidColorBrush(Color.FromRgb(114, 101, 66))) },\n\t\t\t{ PBEType.Steel, (new SolidColorBrush(Color.FromRgb(94, 160, 178)), new SolidColorBrush(Color.FromRgb(66, 105, 114))) },\n\t\t\t{ PBEType.Water, (new SolidColorBrush(Color.FromRgb(58, 176, 232)), new SolidColorBrush(Color.FromRgb(34, 106, 137))) }\n\t\t};\n\t}\n\n\tpublic PBEMove Move { get; }\n\tpublic IBrush Brush { get; }\n\tpublic IBrush BorderBrush { get; }\n\tpublic string Description { get; }\n\tpublic ReactiveCommand<Unit, Unit> SelectMoveCommand { get; }\n\n\tinternal MoveInfo(PBEBattlePokemon pkmn, PBEMove move, Action<PBEMove> clickAction)\n\t{\n\t\tMove = move;\n\t\tPBEType moveType = pkmn.GetMoveType(move);\n\t\t(Brush, BorderBrush) = _typeToBrush[moveType];\n\t\tif (move == PBEMove.None)\n\t\t{\n\t\t\tDescription = string.Empty;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tIPBEMoveData mData = PBEDataProvider.Instance.GetMoveData(move);\n\t\t\tstring s = $\"Type: {PBEDataProvider.Instance.GetTypeName(mData.Type).FromGlobalLanguage()}\";\n\t\t\tif (mData.Type != moveType)\n\t\t\t{\n\t\t\t\ts += $\" → {PBEDataProvider.Instance.GetTypeName(moveType).FromGlobalLanguage()}\";\n\t\t\t}\n\t\t\tvar sb = new StringBuilder();\n\t\t\tsb.AppendLine(s);\n\t\t\tsb.AppendLine($\"Category: {mData.Category}\");\n\t\t\tPBEBattleMoveset.PBEBattleMovesetSlot? slot = pkmn.Moves[move];\n\t\t\tif (slot is not null) // TempLocked move you do not own (like Struggle)\n\t\t\t{\n\t\t\t\tsb.AppendLine($\"PP: {slot.PP}/{slot.MaxPP}\");\n\t\t\t}\n\t\t\tsb.AppendLine($\"Priority: {mData.Priority}\");\n\t\t\tsb.AppendLine($\"Power: {(mData.Power == 0 ? \"―\" : mData.Power.ToString())}\");\n\t\t\tsb.AppendLine($\"Accuracy: {(mData.Accuracy == 0 ? \"―\" : mData.Accuracy.ToString())}\");\n\t\t\ts = $\"Targets: {mData.Targets}\";\n\t\t\tPBEMoveTarget moveTargets = pkmn.GetMoveTargets(move);\n\t\t\tif (mData.Targets != moveTargets)\n\t\t\t{\n\t\t\t\ts += $\" → {moveTargets}\";\n\t\t\t}\n\t\t\tsb.AppendLine(s);\n\t\t\tsb.AppendLine($\"Flags: {mData.Flags}\");\n\t\t\tswitch (mData.Effect)\n\t\t\t{\n\t\t\t\tcase PBEMoveEffect.Recoil: sb.AppendLine($\"Recoil: 1/{mData.EffectParam} damage dealt\"); break;\n\t\t\t\tcase PBEMoveEffect.Recoil__10PercentBurn: sb.AppendLine($\"Recoil: 1/{mData.EffectParam} damage dealt\"); break; // TODO: Burn chance\n\t\t\t\tcase PBEMoveEffect.Recoil__10PercentParalyze: sb.AppendLine($\"Recoil: 1/{mData.EffectParam} damage dealt\"); break; // TODO: Paralyze chance\n\t\t\t\tcase PBEMoveEffect.Struggle: sb.AppendLine(\"Recoil: 1/4 user's max HP\"); break;\n\t\t\t}\n\t\t\tsb.AppendLine();\n\t\t\tsb.Append(PBEDefaultDataProvider.Instance.GetMoveDescription(move).FromGlobalLanguage().Replace('\\n', ' '));\n\t\t\tDescription = sb.ToString();\n\t\t}\n\t\tSelectMoveCommand = ReactiveCommand.Create(() => clickAction(move));\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineClient/Models/PokemonInfo.cs",
    "content": "﻿using Avalonia.Media.Imaging;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data.Utils;\nusing Kermalis.PokemonBattleEngineClient.Clients;\nusing Kermalis.PokemonBattleEngineClient.Infrastructure;\n\nnamespace Kermalis.PokemonBattleEngineClient.Models;\n\npublic sealed class PokemonInfo\n{\n\tpublic Bitmap? MiniSprite { get; }\n\tpublic string? Name { get; }\n\n\tinternal PokemonInfo(PBEBattlePokemon pkmn, bool useKnownInfo)\n\t{\n\t\tMiniSprite = (Bitmap)SpeciesToMinispriteConverter.Instance.Convert(pkmn, typeof(Bitmap), useKnownInfo, null)!;\n\t\tName = useKnownInfo ? pkmn.KnownNickname : pkmn.Nickname + (useKnownInfo && !pkmn.KnownStatus2.HasFlag(PBEStatus2.Transformed) ? pkmn.KnownGender : pkmn.Gender).ToSymbol();\n\t}\n\n\tinternal static PokemonInfo? From(BattleClient client, PBETeam team, PBEFieldPosition pos)\n\t{\n\t\tif (!team.TryGetPokemon(pos, out PBEBattlePokemon? pkmn))\n\t\t{\n\t\t\treturn null;\n\t\t}\n\t\treturn new PokemonInfo(pkmn, client.ShouldUseKnownInfo(pkmn.Trainer));\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineClient/Models/SwitchInfo.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngineClient.Infrastructure;\nusing ReactiveUI;\nusing System;\nusing System.Reactive;\n\nnamespace Kermalis.PokemonBattleEngineClient.Models;\n\npublic sealed class SwitchInfo\n{\n\tpublic ReactiveCommand<Unit, Unit> SelectPokemonCommand { get; }\n\tpublic bool Enabled { get; }\n\n\tpublic PokemonInfo Pokemon { get; }\n\tpublic string Description { get; }\n\n\tinternal SwitchInfo(PBEBattlePokemon pkmn, bool locked, Action<PBEBattlePokemon> clickAction)\n\t{\n\t\tPokemon = new PokemonInfo(pkmn, false);\n\t\tDescription = Utils.CustomPokemonToString(pkmn, false);\n\t\tEnabled = !locked && pkmn.FieldPosition == PBEFieldPosition.None && pkmn.CanBattle;\n\t\tSelectPokemonCommand = ReactiveCommand.Create(() => clickAction(pkmn));\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineClient/Models/TargetInfo.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing System.ComponentModel;\n\nnamespace Kermalis.PokemonBattleEngineClient.Models;\n\npublic sealed class TargetInfo : INotifyPropertyChanged\n{\n\tprivate void OnPropertyChanged(string property)\n\t{\n\t\tPropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));\n\t}\n\tpublic event PropertyChangedEventHandler? PropertyChanged;\n\n\tprivate bool _enabled;\n\tpublic bool Enabled\n\t{\n\t\tget => _enabled;\n\t\tinternal set\n\t\t{\n\t\t\tif (_enabled != value)\n\t\t\t{\n\t\t\t\t_enabled = value;\n\t\t\t\tOnPropertyChanged(nameof(Enabled));\n\t\t\t}\n\t\t}\n\t}\n\tprivate PokemonInfo? _pokemon;\n\tpublic PokemonInfo? Pokemon\n\t{\n\t\tget => _pokemon;\n\t\tinternal set\n\t\t{\n\t\t\tif (_pokemon != value)\n\t\t\t{\n\t\t\t\t_pokemon = value;\n\t\t\t\tOnPropertyChanged(nameof(Pokemon));\n\t\t\t}\n\t\t}\n\t}\n\tprivate bool _lineRightVisible;\n\tpublic bool LineRightVisible\n\t{\n\t\tget => _lineRightVisible;\n\t\tinternal set\n\t\t{\n\t\t\tif (_lineRightVisible != value)\n\t\t\t{\n\t\t\t\t_lineRightVisible = value;\n\t\t\t\tOnPropertyChanged(nameof(LineRightVisible));\n\t\t\t}\n\t\t}\n\t}\n\tprivate bool _lineDownVisible;\n\tpublic bool LineDownVisible\n\t{\n\t\tget => _lineDownVisible;\n\t\tinternal set\n\t\t{\n\t\t\tif (_lineDownVisible != value)\n\t\t\t{\n\t\t\t\t_lineDownVisible = value;\n\t\t\t\tOnPropertyChanged(nameof(LineDownVisible));\n\t\t\t}\n\t\t}\n\t}\n\n\tinternal PBETurnTarget Targets { get; set; }\n}\n"
  },
  {
    "path": "PokemonBattleEngineClient/Models/TeamInfo.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data.Legality;\n\nnamespace Kermalis.PokemonBattleEngineClient.Models;\n\npublic sealed class TeamInfo\n{\n\tpublic string Name { get; }\n\tpublic PBELegalPokemonCollection Party { get; }\n\n\tinternal TeamInfo(string name, PBELegalPokemonCollection party)\n\t{\n\t\tName = name;\n\t\tParty = party;\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineClient/PokemonBattleEngineClient.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n\t<PropertyGroup>\n\t\t<TargetFramework>net7.0</TargetFramework>\n\t\t<OutputType>Library</OutputType>\n\t\t<RootNamespace>Kermalis.PokemonBattleEngineClient</RootNamespace>\n\t\t<Authors>Kermalis</Authors>\n\t\t<Copyright>Kermalis</Copyright>\n\t\t<RepositoryUrl>https://github.com/Kermalis/PokemonBattleEngine</RepositoryUrl>\n\t\t<AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n\t\t<NoWarn />\n\t\t<TreatWarningsAsErrors>false</TreatWarningsAsErrors>\n\t\t<WarningsAsErrors>NU1605</WarningsAsErrors>\n\t\t<Nullable>enable</Nullable>\n\t</PropertyGroup>\n\n\t<ItemGroup>\n\t\t<Compile Update=\"**\\*.xaml.cs\">\n\t\t\t<DependentUpon>%(Filename)</DependentUpon>\n\t\t</Compile>\n\t\t<AvaloniaResource Include=\"**\\*.xaml\">\n\t\t\t<SubType>Designer</SubType>\n\t\t</AvaloniaResource>\n\t\t<EmbeddedResource Include=\"..\\Shared Assets\\**\" />\n\t\t<Compile Remove=\"Dependencies\\**\" />\n\t\t<EmbeddedResource Remove=\"Dependencies\\**\" />\n\t\t<None Remove=\"Dependencies\\**\" />\n\t\t<PackageReference Include=\"Avalonia\" Version=\"0.10.5\" /> <!--Had to downgrade because Avalonia can't get their shit together-->\n\t\t<PackageReference Include=\"Avalonia.Desktop\" Version=\"0.10.5\" />\n\t\t<PackageReference Include=\"Avalonia.ReactiveUI\" Version=\"0.10.5\" />\n\t\t<ProjectReference Include=\"..\\PokemonBattleEngine.DefaultData\\PokemonBattleEngine.DefaultData.csproj\" />\n\t\t<Reference Include=\"Dependencies\\AvaloniaGif.dll\" />\n\t</ItemGroup>\n\n</Project>"
  },
  {
    "path": "PokemonBattleEngineClient/Views/ActionsView.xaml",
    "content": "﻿<UserControl xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:infrastructure=\"clr-namespace:Kermalis.PokemonBattleEngineClient.Infrastructure;assembly=PokemonBattleEngineClient\"\n             xmlns:models=\"clr-namespace:Kermalis.PokemonBattleEngineClient.Models;assembly=PokemonBattleEngineClient\"\n             x:Class=\"Kermalis.PokemonBattleEngineClient.Views.ActionsView\"\n             Width=\"512\" Height=\"192\" Background=\"#313131\">\n\t<UserControl.DataTemplates>\n\t\t<DataTemplate DataType=\"models:MoveInfo\">\n\t\t\t<Button Background=\"{Binding Brush}\" BorderBrush=\"{Binding BorderBrush}\" Margin=\"4,*\" Command=\"{Binding SelectMoveCommand}\">\n\t\t\t\t<Image Stretch=\"None\" Source=\"{Binding Move, Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, Mode=OneWay}\"/>\n\t\t\t\t<ToolTip.Tip>\n\t\t\t\t\t<Image Stretch=\"None\" Source=\"{Binding Description, Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuBlack, Mode=OneWay}\"/>\n\t\t\t\t</ToolTip.Tip>\n\t\t\t</Button>\n\t\t</DataTemplate>\n\t\t<DataTemplate DataType=\"models:PokemonInfo\">\n\t\t\t<StackPanel Orientation=\"Horizontal\">\n\t\t\t\t<Image Stretch=\"None\" Source=\"{Binding MiniSprite, Mode=OneWay}\"/>\n\t\t\t\t<Image Stretch=\"None\" Source=\"{Binding Name, Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, Mode=OneWay}\"/>\n\t\t\t</StackPanel>\n\t\t</DataTemplate>\n\t\t<DataTemplate DataType=\"models:SwitchInfo\">\n\t\t\t<Button Background=\"#313131\" BorderBrush=\"#4A4A4A\" Margin=\"4,*\" Command=\"{Binding SelectPokemonCommand}\"\n\t\t\t\t\tIsEnabled=\"{Binding Enabled}\" Content=\"{Binding Pokemon}\">\n\t\t\t\t<ToolTip.Tip>\n\t\t\t\t\t<Image Stretch=\"None\" Source=\"{Binding Description, Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuBlack, Mode=OneWay}\"/>\n\t\t\t\t</ToolTip.Tip>\n\t\t\t</Button>\n\t\t</DataTemplate>\n\t</UserControl.DataTemplates>\n\t<UserControl.Resources>\n\t\t<DataTemplate x:Key=\"PositionButtonTemplate\">\n\t\t\t<Image Stretch=\"None\" Source=\"{Binding Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuBlack, Mode=OneWay}\"/>\n\t\t</DataTemplate>\n\t\t<ItemsPanelTemplate x:Key=\"WrapPanelTemplate\">\n\t\t\t<infrastructure:BetterWrapPanel Orientation=\"Horizontal\" HorizontalContentAlignment=\"Center\"/>\n\t\t</ItemsPanelTemplate>\n\t</UserControl.Resources>\n\t<UserControl.Styles>\n\t\t<Style Selector=\"Button\">\n\t\t\t<Setter Property=\"BorderThickness\" Value=\"4\"/>\n\t\t</Style>\n\t\t<Style Selector=\"Button /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"CornerRadius\" Value=\"4\"/>\n\t\t</Style>\n\t</UserControl.Styles>\n\t<ScrollViewer VerticalScrollBarVisibility=\"Auto\" HorizontalScrollBarVisibility=\"Disabled\">\n\t\t<StackPanel Orientation=\"Vertical\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\">\n\t\t\t<ItemsControl IsVisible=\"{Binding MovesVisible}\" Items=\"{Binding Moves}\" ItemsPanel=\"{StaticResource WrapPanelTemplate}\"/>\n\t\t\t<ItemsControl IsVisible=\"{Binding SwitchesVisible}\" Items=\"{Binding Party}\" ItemsPanel=\"{StaticResource WrapPanelTemplate}\"/>\n\t\t\t<Grid IsVisible=\"{Binding TargetsVisible}\">\n\t\t\t\t<Grid.RowDefinitions>\n\t\t\t\t\t<RowDefinition Height=\"3*\"/>\n\t\t\t\t\t<RowDefinition Height=\"1*\"/>\n\t\t\t\t\t<RowDefinition Height=\"3*\"/>\n\t\t\t\t</Grid.RowDefinitions>\n\t\t\t\t<Grid.ColumnDefinitions>\n\t\t\t\t\t<ColumnDefinition Width=\"3*\"/>\n\t\t\t\t\t<ColumnDefinition Width=\"1*\"/>\n\t\t\t\t\t<ColumnDefinition Width=\"3*\"/>\n\t\t\t\t\t<ColumnDefinition Width=\"1*\"/>\n\t\t\t\t\t<ColumnDefinition Width=\"3*\"/>\n\t\t\t\t</Grid.ColumnDefinitions>\n\t\t\t\t<Button Grid.Column=\"0\" Grid.Row=\"0\" MinWidth=\"100\" MinHeight=\"50\" Background=\"#F75231\" BorderBrush=\"#732108\" Command=\"{Binding SelectTarget}\"\n\t\t\t\t\t\tCommandParameter=\"FoeRight\" IsEnabled=\"{Binding TargetFoeRight.Enabled}\" Content=\"{Binding TargetFoeRight.Pokemon}\"/>\n\t\t\t\t<Button Grid.Column=\"0\" Grid.Row=\"2\" MinWidth=\"100\" MinHeight=\"50\" Background=\"#7B63E7\" BorderBrush=\"#4A3994\" Command=\"{Binding SelectTarget}\"\n\t\t\t\t\t\tCommandParameter=\"AllyLeft\" IsEnabled=\"{Binding TargetAllyLeft.Enabled}\" Content=\"{Binding TargetAllyLeft.Pokemon}\"/>\n\t\t\t\t<Button Grid.Column=\"2\" Grid.Row=\"0\" MinWidth=\"100\" MinHeight=\"50\" Background=\"#F75231\" BorderBrush=\"#732108\" Command=\"{Binding SelectTarget}\"\n\t\t\t\t\t\tCommandParameter=\"FoeCenter\" IsEnabled=\"{Binding TargetFoeCenter.Enabled}\" IsVisible=\"{Binding CenterTargetsVisible}\" Content=\"{Binding TargetFoeCenter.Pokemon}\"/>\n\t\t\t\t<Button Grid.Column=\"2\" Grid.Row=\"2\" MinWidth=\"100\" MinHeight=\"50\" Background=\"#7B63E7\" BorderBrush=\"#4A3994\" Command=\"{Binding SelectTarget}\"\n\t\t\t\t\t\tCommandParameter=\"AllyCenter\" IsEnabled=\"{Binding TargetAllyCenter.Enabled}\" IsVisible=\"{Binding CenterTargetsVisible}\" Content=\"{Binding TargetAllyCenter.Pokemon}\"/>\n\t\t\t\t<Button Grid.Column=\"4\" Grid.Row=\"0\" MinWidth=\"100\" MinHeight=\"50\" Background=\"#F75231\" BorderBrush=\"#732108\" Command=\"{Binding SelectTarget}\"\n\t\t\t\t\t\tCommandParameter=\"FoeLeft\" IsEnabled=\"{Binding TargetFoeLeft.Enabled}\" Content=\"{Binding TargetFoeLeft.Pokemon}\"/>\n\t\t\t\t<Button Grid.Column=\"4\" Grid.Row=\"2\" MinWidth=\"100\" MinHeight=\"50\" Background=\"#7B63E7\" BorderBrush=\"#4A3994\" Command=\"{Binding SelectTarget}\"\n\t\t\t\t\t\tCommandParameter=\"AllyRight\" IsEnabled=\"{Binding TargetAllyRight.Enabled}\" Content=\"{Binding TargetAllyRight.Pokemon}\"/>\n\n\t\t\t\t<Rectangle Grid.Column=\"0\" Grid.Row=\"1\" Stroke=\"#425294\" StrokeThickness=\"2\" Fill=\"#9CADF7\"\n\t\t\t\t\t\t   Width=\"12\" Height=\"24\" Opacity=\"{Binding TargetFoeRight.LineDownVisible}\"/>\n\t\t\t\t<Rectangle Grid.Column=\"1\" Grid.Row=\"0\" Stroke=\"#425294\" StrokeThickness=\"2\" Fill=\"#9CADF7\"\n\t\t\t\t\t\t   Width=\"48\" Height=\"12\" Opacity=\"{Binding TargetFoeRight.LineRightVisible}\"/>\n\t\t\t\t<Rectangle Grid.Column=\"1\" Grid.Row=\"2\" Stroke=\"#425294\" StrokeThickness=\"2\" Fill=\"#9CADF7\"\n\t\t\t\t\t\t   Width=\"48\" Height=\"12\" Opacity=\"{Binding TargetAllyLeft.LineRightVisible}\"/>\n\t\t\t\t<Rectangle Grid.Column=\"2\" Grid.Row=\"1\" Stroke=\"#425294\" StrokeThickness=\"2\" Fill=\"#9CADF7\"\n\t\t\t\t\t\t   Width=\"12\" Height=\"24\" Opacity=\"{Binding TargetFoeCenter.LineDownVisible}\" IsVisible=\"{Binding CenterTargetsVisible}\"/>\n\t\t\t\t<Rectangle Grid.Column=\"3\" Grid.Row=\"0\" Stroke=\"#425294\" StrokeThickness=\"2\" Fill=\"#9CADF7\"\n\t\t\t\t\t\t   Width=\"48\" Height=\"12\" Opacity=\"{Binding TargetFoeCenter.LineRightVisible}\" IsVisible=\"{Binding CenterTargetsVisible}\"/>\n\t\t\t\t<Rectangle Grid.Column=\"3\" Grid.Row=\"2\" Stroke=\"#425294\" StrokeThickness=\"2\" Fill=\"#9CADF7\"\n\t\t\t\t\t\t   Width=\"48\" Height=\"12\" Opacity=\"{Binding TargetAllyCenter.LineRightVisible}\" IsVisible=\"{Binding CenterTargetsVisible}\"/>\n\t\t\t\t<Rectangle Grid.Column=\"4\" Grid.Row=\"1\" Stroke=\"#425294\" StrokeThickness=\"2\" Fill=\"#9CADF7\"\n\t\t\t\t\t\t   Width=\"12\" Height=\"24\" Opacity=\"{Binding TargetFoeLeft.LineDownVisible}\"/>\n\t\t\t</Grid>\n\t\t\t<infrastructure:BetterWrapPanel IsVisible=\"{Binding PositionsVisible}\" Orientation=\"Horizontal\" HorizontalContentAlignment=\"Center\">\n\t\t\t\t<Button Background=\"White\" BorderBrush=\"#A2A2A2\" Margin=\"4,*\" Command=\"{Binding SelectPosition}\" ContentTemplate=\"{StaticResource PositionButtonTemplate}\"\n\t\t\t\t\t\tCommandParameter=\"Left\" IsEnabled=\"{Binding LeftPositionEnabled}\" Content=\"Left\"/>\n\t\t\t\t<Button Background=\"White\" BorderBrush=\"#A2A2A2\" Margin=\"4,*\" Command=\"{Binding SelectPosition}\" ContentTemplate=\"{StaticResource PositionButtonTemplate}\"\n\t\t\t\t\t\tCommandParameter=\"Center\" IsEnabled=\"{Binding CenterPositionEnabled}\" IsVisible=\"{Binding CenterPositionEnabled}\" Content=\"Center\"/>\n\t\t\t\t<Button Background=\"White\" BorderBrush=\"#A2A2A2\" Margin=\"4,*\" Command=\"{Binding SelectPosition}\" ContentTemplate=\"{StaticResource PositionButtonTemplate}\"\n\t\t\t\t\t\tCommandParameter=\"Right\" IsEnabled=\"{Binding RightPositionEnabled}\" Content=\"Right\"/>\n\t\t\t</infrastructure:BetterWrapPanel>\n\t\t\t<Button Background=\"White\" BorderBrush=\"#A2A2A2\" Margin=\"4,*\" HorizontalAlignment=\"Center\" Command=\"{Binding UndoLast}\" ContentTemplate=\"{StaticResource PositionButtonTemplate}\"\n\t\t\t\t\t\tIsVisible=\"{Binding BackVisible}\" Content=\"Back\"/>\n\t\t</StackPanel>\n\t</ScrollViewer>\n</UserControl>"
  },
  {
    "path": "PokemonBattleEngineClient/Views/ActionsView.xaml.cs",
    "content": "﻿using Avalonia.Controls;\nusing Avalonia.Markup.Xaml;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngineClient.Clients;\nusing Kermalis.PokemonBattleEngineClient.Models;\nusing System;\nusing System.ComponentModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngineClient.Views;\n\npublic sealed class ActionsView : UserControl, INotifyPropertyChanged\n{\n\tprivate void OnPropertyChanged(string property)\n\t{\n\t\tPropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));\n\t}\n\tpublic new event PropertyChangedEventHandler? PropertyChanged;\n\n\tpublic TargetInfo TargetAllyLeft { get; } = new();\n\tpublic TargetInfo TargetAllyCenter { get; } = new();\n\tpublic TargetInfo TargetAllyRight { get; } = new();\n\tpublic TargetInfo TargetFoeLeft { get; } = new();\n\tpublic TargetInfo TargetFoeCenter { get; } = new();\n\tpublic TargetInfo TargetFoeRight { get; } = new();\n\n\tprivate bool _centerTargetsVisible;\n\tpublic bool CenterTargetsVisible\n\t{\n\t\tget => _centerTargetsVisible;\n\t\tprivate set\n\t\t{\n\t\t\tif (_centerTargetsVisible != value)\n\t\t\t{\n\t\t\t\t_centerTargetsVisible = value;\n\t\t\t\tOnPropertyChanged(nameof(CenterTargetsVisible));\n\t\t\t}\n\t\t}\n\t}\n\tprivate bool _backVisible;\n\tpublic bool BackVisible\n\t{\n\t\tget => _backVisible;\n\t\tprivate set\n\t\t{\n\t\t\tif (_backVisible != value)\n\t\t\t{\n\t\t\t\t_backVisible = value;\n\t\t\t\tOnPropertyChanged(nameof(BackVisible));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate bool _leftPositionEnabled;\n\tpublic bool LeftPositionEnabled\n\t{\n\t\tget => _leftPositionEnabled;\n\t\tprivate set\n\t\t{\n\t\t\tif (_leftPositionEnabled != value)\n\t\t\t{\n\t\t\t\t_leftPositionEnabled = value;\n\t\t\t\tOnPropertyChanged(nameof(LeftPositionEnabled));\n\t\t\t}\n\t\t}\n\t}\n\tprivate bool _centerPositionEnabled;\n\tpublic bool CenterPositionEnabled\n\t{\n\t\tget => _centerPositionEnabled;\n\t\tprivate set\n\t\t{\n\t\t\tif (_centerPositionEnabled != value)\n\t\t\t{\n\t\t\t\t_centerPositionEnabled = value;\n\t\t\t\tOnPropertyChanged(nameof(CenterPositionEnabled));\n\t\t\t}\n\t\t}\n\t}\n\tprivate bool _rightPositionEnabled;\n\tpublic bool RightPositionEnabled\n\t{\n\t\tget => _rightPositionEnabled;\n\t\tprivate set\n\t\t{\n\t\t\tif (_rightPositionEnabled != value)\n\t\t\t{\n\t\t\t\t_rightPositionEnabled = value;\n\t\t\t\tOnPropertyChanged(nameof(RightPositionEnabled));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate PBEMove _fightMove;\n\n\tprivate MoveInfo[] _moves;\n\tpublic MoveInfo[] Moves\n\t{\n\t\tget => _moves;\n\t\tprivate set\n\t\t{\n\t\t\tif (_moves != value)\n\t\t\t{\n\t\t\t\t_moves = value;\n\t\t\t\tOnPropertyChanged(nameof(Moves));\n\t\t\t}\n\t\t}\n\t}\n\tprivate SwitchInfo[] _party;\n\tpublic SwitchInfo[] Party\n\t{\n\t\tget => _party;\n\t\tprivate set\n\t\t{\n\t\t\tif (_party != value)\n\t\t\t{\n\t\t\t\t_party = value;\n\t\t\t\tOnPropertyChanged(nameof(Party));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate bool _targetsVisible;\n\tpublic bool TargetsVisible\n\t{\n\t\tget => _targetsVisible;\n\t\tprivate set\n\t\t{\n\t\t\tif (_targetsVisible != value)\n\t\t\t{\n\t\t\t\t_targetsVisible = value;\n\t\t\t\tOnPropertyChanged(nameof(TargetsVisible));\n\t\t\t}\n\t\t}\n\t}\n\tprivate bool _movesVisible;\n\tpublic bool MovesVisible\n\t{\n\t\tget => _movesVisible;\n\t\tprivate set\n\t\t{\n\t\t\tif (_movesVisible != value)\n\t\t\t{\n\t\t\t\t_movesVisible = value;\n\t\t\t\tOnPropertyChanged(nameof(MovesVisible));\n\t\t\t}\n\t\t}\n\t}\n\tprivate bool _switchesVisible;\n\tpublic bool SwitchesVisible\n\t{\n\t\tget => _switchesVisible;\n\t\tprivate set\n\t\t{\n\t\t\tif (_switchesVisible != value)\n\t\t\t{\n\t\t\t\t_switchesVisible = value;\n\t\t\t\tOnPropertyChanged(nameof(SwitchesVisible));\n\t\t\t}\n\t\t}\n\t}\n\tprivate bool _positionsVisible;\n\tpublic bool PositionsVisible\n\t{\n\t\tget => _positionsVisible;\n\t\tprivate set\n\t\t{\n\t\t\tif (_positionsVisible != value)\n\t\t\t{\n\t\t\t\t_positionsVisible = value;\n\t\t\t\tOnPropertyChanged(nameof(PositionsVisible));\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic BattleView BattleView { get; internal set; }\n\tinternal ActionsBuilder? ActionsBuilder { get; set; }\n\tinternal SwitchesBuilder? SwitchesBuilder { get; set; }\n\tpublic PBEBattlePokemon Pokemon { get; private set; }\n\tprivate int _pkmnIndex;\n\n\tpublic ActionsView()\n\t{\n\t\t// These are set in the appropriate states\n\t\t_moves = null!;\n\t\t_party = null!;\n\t\tBattleView = null!;\n\t\tPokemon = null!;\n\t\t// Avalonia setup\n\t\tDataContext = this;\n\t\tAvaloniaXamlLoader.Load(this);\n\t}\n\n\tinternal void DisplayActions(int index, PBEBattlePokemon pkmn)\n\t{\n\t\tBackVisible = index != 0;\n\t\t_pkmnIndex = index;\n\t\tPokemon = pkmn;\n\t\tPBEMove[] usableMoves = pkmn.GetUsableMoves();\n\t\tvar mInfo = new MoveInfo[usableMoves.Length];\n\t\tfor (int i = 0; i < mInfo.Length; i++)\n\t\t{\n\t\t\tmInfo[i] = new MoveInfo(pkmn, usableMoves[i], SelectMoveForTurn);\n\t\t}\n\t\tMoves = mInfo;\n\t\tvar pInfo = new SwitchInfo[pkmn.Trainer.Party.Count];\n\t\tfor (int i = 0; i < pInfo.Length; i++)\n\t\t{\n\t\t\tPBEBattlePokemon p = pkmn.Trainer.Party[i];\n\t\t\tpInfo[i] = new SwitchInfo(p, !pkmn.CanSwitchOut() || ActionsBuilder!.IsStandBy(p), SelectPokemonForTurn);\n\t\t}\n\t\tParty = pInfo;\n\t\tMovesVisible = true;\n\t\tSwitchesVisible = true;\n\t}\n\tinternal void DisplaySwitches(int index)\n\t{\n\t\tBackVisible = index != 0;\n\t\t_pkmnIndex = index;\n\t\tPBEList<PBEBattlePokemon> pa = BattleView.Client.Trainer!.Party;\n\t\tvar pInfo = new SwitchInfo[pa.Count];\n\t\tfor (int i = 0; i < pa.Count; i++)\n\t\t{\n\t\t\tPBEBattlePokemon p = pa[i];\n\t\t\tpInfo[i] = new SwitchInfo(p, SwitchesBuilder!.IsStandBy(p), SelectSwitch);\n\t\t}\n\t\tParty = pInfo;\n\t\tSwitchesVisible = true;\n\t}\n\n\tpublic void UndoLast()\n\t{\n\t\t// Actions mode\n\t\tif (ActionsBuilder is not null)\n\t\t{\n\t\t\tif (_movesVisible)\n\t\t\t{\n\t\t\t\tActionsBuilder.Pop(); // If we're selecting a move, go back to the previous pkmn\n\t\t\t}\n\t\t\telse // Go from selecting targets to selecting move\n\t\t\t{\n\t\t\t\tTargetsVisible = false;\n\t\t\t\tMovesVisible = true;\n\t\t\t\tSwitchesVisible = true;\n\t\t\t\tBackVisible = _pkmnIndex != 0;\n\t\t\t}\n\t\t}\n\t\telse // Switches mode\n\t\t{\n\t\t\tSwitchesBuilder!.Pop();\n\t\t}\n\t}\n\n\tprivate void SelectPokemonForTurn(PBEBattlePokemon pkmn)\n\t{\n\t\tMovesVisible = false;\n\t\tSwitchesVisible = false;\n\t\tBackVisible = false;\n\t\tActionsBuilder!.PushSwitch(pkmn);\n\t}\n\tprivate void SelectMoveForTurn(PBEMove move)\n\t{\n\t\tMovesVisible = false;\n\t\tSwitchesVisible = false;\n\t\tDisplayTargets(move);\n\t}\n\tprivate void SelectSwitch(PBEBattlePokemon pkmn)\n\t{\n\t\tPokemon = pkmn;\n\t\tSwitchesVisible = false;\n\t\tDisplayPositions();\n\t}\n\tprivate void DisplayTargets(PBEMove move)\n\t{\n\t\tPBEMoveTarget possibleTargets = Pokemon.GetMoveTargets(move);\n\n\t\tBattleClient client = BattleView.Client;\n\t\tif (client.Battle.BattleFormat == PBEBattleFormat.Single || client.Battle.BattleFormat == PBEBattleFormat.Rotation)\n\t\t{\n\t\t\tPBETurnTarget targets;\n\t\t\tswitch (possibleTargets)\n\t\t\t{\n\t\t\t\tcase PBEMoveTarget.All: targets = PBETurnTarget.AllyCenter | PBETurnTarget.FoeCenter; break;\n\t\t\t\tcase PBEMoveTarget.AllFoes:\n\t\t\t\tcase PBEMoveTarget.AllFoesSurrounding:\n\t\t\t\tcase PBEMoveTarget.AllSurrounding:\n\t\t\t\tcase PBEMoveTarget.RandomFoeSurrounding:\n\t\t\t\tcase PBEMoveTarget.SingleFoeSurrounding:\n\t\t\t\tcase PBEMoveTarget.SingleNotSelf:\n\t\t\t\tcase PBEMoveTarget.SingleSurrounding: targets = PBETurnTarget.FoeCenter; break;\n\t\t\t\tcase PBEMoveTarget.AllTeam:\n\t\t\t\tcase PBEMoveTarget.Self:\n\t\t\t\tcase PBEMoveTarget.SelfOrAllySurrounding:\n\t\t\t\tcase PBEMoveTarget.SingleAllySurrounding: targets = PBETurnTarget.AllyCenter; break;\n\t\t\t\tdefault: throw new InvalidDataException(nameof(possibleTargets));\n\t\t\t}\n\t\t\tBackVisible = false;\n\t\t\tActionsBuilder!.PushMove(move, targets);\n\t\t\treturn;\n\t\t}\n\t\t// Double / Triple\n\t\tBackVisible = true;\n\t\tPBETeam team = client.Trainer!.Team;\n\t\tPBETeam oTeam = team.OpposingTeam;\n\t\t_fightMove = move;\n\t\tTargetAllyLeft.Pokemon = PokemonInfo.From(client, team, PBEFieldPosition.Left);\n\t\tTargetAllyCenter.Pokemon = PokemonInfo.From(client, team, PBEFieldPosition.Center);\n\t\tTargetAllyRight.Pokemon = PokemonInfo.From(client, team, PBEFieldPosition.Right);\n\t\tTargetFoeLeft.Pokemon = PokemonInfo.From(client, oTeam, PBEFieldPosition.Left);\n\t\tTargetFoeCenter.Pokemon = PokemonInfo.From(client, oTeam, PBEFieldPosition.Center);\n\t\tTargetFoeRight.Pokemon = PokemonInfo.From(client, oTeam, PBEFieldPosition.Right);\n\n\t\tif (client.Battle.BattleFormat == PBEBattleFormat.Double)\n\t\t{\n\t\t\tCenterTargetsVisible = false;\n\t\t\tTargetFoeCenter.LineDownVisible = TargetFoeCenter.LineRightVisible = TargetAllyCenter.LineRightVisible = false;\n\t\t\tswitch (possibleTargets)\n\t\t\t{\n\t\t\t\tcase PBEMoveTarget.All:\n\t\t\t\t{\n\t\t\t\t\tTargetAllyLeft.Enabled = TargetAllyRight.Enabled = TargetFoeLeft.Enabled = TargetFoeRight.Enabled = true;\n\t\t\t\t\tTargetAllyLeft.LineRightVisible = TargetFoeRight.LineRightVisible = TargetFoeLeft.LineDownVisible = TargetFoeRight.LineDownVisible = true;\n\t\t\t\t\tTargetAllyLeft.Targets = TargetAllyRight.Targets = TargetFoeLeft.Targets = TargetFoeRight.Targets = PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeRight;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEMoveTarget.AllFoes:\n\t\t\t\tcase PBEMoveTarget.AllFoesSurrounding:\n\t\t\t\t{\n\t\t\t\t\tTargetAllyLeft.Enabled = TargetAllyRight.Enabled = false;\n\t\t\t\t\tTargetFoeLeft.Enabled = TargetFoeRight.Enabled = true;\n\t\t\t\t\tTargetAllyLeft.LineRightVisible = TargetFoeLeft.LineDownVisible = TargetFoeRight.LineDownVisible = false;\n\t\t\t\t\tTargetFoeRight.LineRightVisible = true;\n\t\t\t\t\tTargetFoeLeft.Targets = TargetFoeRight.Targets = PBETurnTarget.FoeLeft | PBETurnTarget.FoeRight;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEMoveTarget.AllSurrounding:\n\t\t\t\t{\n\t\t\t\t\tif (Pokemon.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t{\n\t\t\t\t\t\tTargetAllyLeft.Enabled = false;\n\t\t\t\t\t\tTargetAllyRight.Enabled = true;\n\t\t\t\t\t\tTargetFoeRight.LineDownVisible = false;\n\t\t\t\t\t\tTargetFoeLeft.LineDownVisible = true;\n\t\t\t\t\t\tTargetAllyRight.Targets = TargetFoeLeft.Targets = TargetFoeRight.Targets = PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeRight;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tTargetAllyLeft.Enabled = true;\n\t\t\t\t\t\tTargetAllyRight.Enabled = false;\n\t\t\t\t\t\tTargetFoeRight.LineDownVisible = true;\n\t\t\t\t\t\tTargetFoeLeft.LineDownVisible = false;\n\t\t\t\t\t\tTargetAllyLeft.Targets = TargetFoeLeft.Targets = TargetFoeRight.Targets = PBETurnTarget.AllyLeft | PBETurnTarget.FoeLeft | PBETurnTarget.FoeRight;\n\t\t\t\t\t}\n\t\t\t\t\tTargetFoeLeft.Enabled = TargetFoeRight.Enabled = true;\n\t\t\t\t\tTargetAllyLeft.LineRightVisible = false;\n\t\t\t\t\tTargetFoeRight.LineRightVisible = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEMoveTarget.AllTeam:\n\t\t\t\t{\n\t\t\t\t\tTargetAllyLeft.Enabled = TargetAllyRight.Enabled = true;\n\t\t\t\t\tTargetFoeLeft.Enabled = TargetFoeRight.Enabled = false;\n\t\t\t\t\tTargetFoeRight.LineRightVisible = TargetFoeLeft.LineDownVisible = TargetFoeRight.LineDownVisible = false;\n\t\t\t\t\tTargetAllyLeft.LineRightVisible = true;\n\t\t\t\t\tTargetAllyLeft.Targets = TargetAllyRight.Targets = PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEMoveTarget.RandomFoeSurrounding:\n\t\t\t\tcase PBEMoveTarget.Self:\n\t\t\t\t{\n\t\t\t\t\tif (Pokemon.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t{\n\t\t\t\t\t\tTargetAllyLeft.Enabled = true;\n\t\t\t\t\t\tTargetAllyRight.Enabled = false;\n\t\t\t\t\t\tTargetAllyLeft.Targets = PBETurnTarget.AllyLeft;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tTargetAllyLeft.Enabled = false;\n\t\t\t\t\t\tTargetAllyRight.Enabled = true;\n\t\t\t\t\t\tTargetAllyRight.Targets = PBETurnTarget.AllyRight;\n\t\t\t\t\t}\n\t\t\t\t\tTargetFoeLeft.Enabled = TargetFoeRight.Enabled = false;\n\t\t\t\t\tTargetAllyLeft.LineRightVisible = TargetFoeRight.LineRightVisible = TargetFoeLeft.LineDownVisible = TargetFoeRight.LineDownVisible = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEMoveTarget.SelfOrAllySurrounding:\n\t\t\t\t{\n\t\t\t\t\tTargetAllyLeft.Enabled = TargetAllyRight.Enabled = true;\n\t\t\t\t\tTargetFoeLeft.Enabled = TargetFoeRight.Enabled = false;\n\t\t\t\t\tTargetAllyLeft.LineRightVisible = TargetFoeRight.LineRightVisible = TargetFoeLeft.LineDownVisible = TargetFoeRight.LineDownVisible = false;\n\t\t\t\t\tTargetAllyLeft.Targets = PBETurnTarget.AllyLeft;\n\t\t\t\t\tTargetAllyRight.Targets = PBETurnTarget.AllyRight;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEMoveTarget.SingleAllySurrounding:\n\t\t\t\t{\n\t\t\t\t\tif (Pokemon.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t{\n\t\t\t\t\t\tTargetAllyLeft.Enabled = false;\n\t\t\t\t\t\tTargetAllyRight.Enabled = true;\n\t\t\t\t\t\tTargetAllyRight.Targets = PBETurnTarget.AllyRight;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tTargetAllyLeft.Enabled = true;\n\t\t\t\t\t\tTargetAllyRight.Enabled = false;\n\t\t\t\t\t\tTargetAllyLeft.Targets = PBETurnTarget.AllyLeft;\n\t\t\t\t\t}\n\t\t\t\t\tTargetFoeLeft.Enabled = TargetFoeRight.Enabled = false;\n\t\t\t\t\tTargetAllyLeft.LineRightVisible = TargetFoeRight.LineRightVisible = TargetFoeLeft.LineDownVisible = TargetFoeRight.LineDownVisible = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEMoveTarget.SingleFoeSurrounding:\n\t\t\t\t{\n\t\t\t\t\tTargetAllyLeft.Enabled = TargetAllyRight.Enabled = false;\n\t\t\t\t\tTargetFoeLeft.Enabled = TargetFoeRight.Enabled = true;\n\t\t\t\t\tTargetAllyLeft.LineRightVisible = TargetFoeRight.LineRightVisible = TargetFoeLeft.LineDownVisible = TargetFoeRight.LineDownVisible = false;\n\t\t\t\t\tTargetFoeLeft.Targets = PBETurnTarget.FoeLeft;\n\t\t\t\t\tTargetFoeRight.Targets = PBETurnTarget.FoeRight;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEMoveTarget.SingleNotSelf:\n\t\t\t\tcase PBEMoveTarget.SingleSurrounding:\n\t\t\t\t{\n\t\t\t\t\tif (Pokemon.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t{\n\t\t\t\t\t\tTargetAllyLeft.Enabled = false;\n\t\t\t\t\t\tTargetAllyRight.Enabled = true;\n\t\t\t\t\t\tTargetAllyRight.Targets = PBETurnTarget.AllyRight;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tTargetAllyLeft.Enabled = true;\n\t\t\t\t\t\tTargetAllyRight.Enabled = false;\n\t\t\t\t\t\tTargetAllyLeft.Targets = PBETurnTarget.AllyLeft;\n\t\t\t\t\t}\n\t\t\t\t\tTargetFoeLeft.Enabled = TargetFoeRight.Enabled = true;\n\t\t\t\t\tTargetAllyLeft.LineRightVisible = TargetFoeRight.LineRightVisible = TargetFoeLeft.LineDownVisible = TargetFoeRight.LineDownVisible = false;\n\t\t\t\t\tTargetFoeLeft.Targets = PBETurnTarget.FoeLeft;\n\t\t\t\t\tTargetFoeRight.Targets = PBETurnTarget.FoeRight;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tdefault: throw new InvalidDataException(nameof(possibleTargets));\n\t\t\t}\n\t\t}\n\t\telse // Triple\n\t\t{\n\t\t\tCenterTargetsVisible = true;\n\t\t\tswitch (possibleTargets)\n\t\t\t{\n\t\t\t\tcase PBEMoveTarget.All:\n\t\t\t\t{\n\t\t\t\t\tTargetAllyLeft.Enabled = TargetAllyCenter.Enabled = TargetAllyRight.Enabled = TargetFoeLeft.Enabled = TargetFoeCenter.Enabled = TargetFoeRight.Enabled = true;\n\t\t\t\t\tTargetAllyLeft.LineRightVisible = TargetAllyCenter.LineRightVisible = TargetFoeRight.LineRightVisible = TargetFoeCenter.LineRightVisible = TargetFoeCenter.LineDownVisible = TargetFoeLeft.LineDownVisible = TargetFoeRight.LineDownVisible = true;\n\t\t\t\t\tTargetAllyLeft.Targets = TargetAllyCenter.Targets = TargetAllyRight.Targets = TargetFoeLeft.Targets = TargetFoeCenter.Targets = TargetFoeRight.Targets = PBETurnTarget.AllyLeft | PBETurnTarget.AllyCenter | PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEMoveTarget.AllFoes:\n\t\t\t\t{\n\t\t\t\t\tTargetAllyLeft.Enabled = TargetAllyCenter.Enabled = TargetAllyRight.Enabled = false;\n\t\t\t\t\tTargetFoeLeft.Enabled = TargetFoeCenter.Enabled = TargetFoeRight.Enabled = true;\n\t\t\t\t\tTargetAllyLeft.LineRightVisible = TargetAllyCenter.LineRightVisible = TargetFoeCenter.LineDownVisible = TargetFoeLeft.LineDownVisible = TargetFoeRight.LineDownVisible = false;\n\t\t\t\t\tTargetFoeRight.LineRightVisible = TargetFoeCenter.LineRightVisible = true;\n\t\t\t\t\tTargetFoeLeft.Targets = TargetFoeCenter.Targets = TargetFoeRight.Targets = PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEMoveTarget.AllFoesSurrounding:\n\t\t\t\t{\n\t\t\t\t\tif (Pokemon.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t{\n\t\t\t\t\t\tTargetFoeCenter.Enabled = TargetFoeRight.Enabled = true;\n\t\t\t\t\t\tTargetFoeLeft.Enabled = false;\n\t\t\t\t\t\tTargetFoeRight.LineRightVisible = true;\n\t\t\t\t\t\tTargetFoeCenter.LineRightVisible = false;\n\t\t\t\t\t\tTargetFoeCenter.Targets = TargetFoeRight.Targets = PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight;\n\t\t\t\t\t}\n\t\t\t\t\telse if (Pokemon.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t{\n\t\t\t\t\t\tTargetFoeLeft.Enabled = TargetFoeCenter.Enabled = TargetFoeRight.Enabled = true;\n\t\t\t\t\t\tTargetFoeRight.LineRightVisible = TargetFoeCenter.LineRightVisible = true;\n\t\t\t\t\t\tTargetFoeLeft.Targets = TargetFoeCenter.Targets = TargetFoeRight.Targets = PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tTargetFoeLeft.Enabled = TargetFoeCenter.Enabled = true;\n\t\t\t\t\t\tTargetFoeRight.Enabled = false;\n\t\t\t\t\t\tTargetFoeCenter.LineRightVisible = true;\n\t\t\t\t\t\tTargetFoeRight.LineRightVisible = false;\n\t\t\t\t\t\tTargetFoeLeft.Targets = TargetFoeCenter.Targets = PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter;\n\t\t\t\t\t}\n\t\t\t\t\tTargetAllyLeft.Enabled = TargetAllyCenter.Enabled = TargetAllyRight.Enabled = false;\n\t\t\t\t\tTargetFoeRight.LineDownVisible = TargetAllyLeft.LineRightVisible = TargetAllyCenter.LineRightVisible = TargetFoeCenter.LineDownVisible = TargetFoeLeft.LineDownVisible = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEMoveTarget.AllSurrounding:\n\t\t\t\t{\n\t\t\t\t\tif (Pokemon.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t{\n\t\t\t\t\t\tTargetFoeRight.Enabled = TargetFoeCenter.Enabled = TargetAllyCenter.Enabled = true;\n\t\t\t\t\t\tTargetAllyLeft.Enabled = TargetAllyRight.Enabled = TargetFoeLeft.Enabled = false;\n\t\t\t\t\t\tTargetAllyLeft.LineRightVisible = TargetAllyCenter.LineRightVisible = TargetFoeCenter.LineRightVisible = TargetFoeLeft.LineDownVisible = TargetFoeRight.LineDownVisible = false;\n\t\t\t\t\t\tTargetFoeRight.LineRightVisible = TargetFoeCenter.LineDownVisible = true;\n\t\t\t\t\t\tTargetAllyCenter.Targets = TargetFoeCenter.Targets = TargetFoeRight.Targets = PBETurnTarget.AllyCenter | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight;\n\t\t\t\t\t}\n\t\t\t\t\telse if (Pokemon.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t{\n\t\t\t\t\t\tTargetAllyLeft.Enabled = TargetAllyRight.Enabled = TargetFoeLeft.Enabled = TargetFoeCenter.Enabled = TargetFoeRight.Enabled = true;\n\t\t\t\t\t\tTargetAllyCenter.Enabled = false;\n\t\t\t\t\t\tTargetAllyLeft.LineRightVisible = TargetAllyCenter.LineRightVisible = TargetFoeCenter.LineDownVisible = false;\n\t\t\t\t\t\tTargetFoeRight.LineRightVisible = TargetFoeCenter.LineRightVisible = TargetFoeLeft.LineDownVisible = TargetFoeRight.LineDownVisible = true;\n\t\t\t\t\t\tTargetAllyLeft.Targets = TargetAllyRight.Targets = TargetFoeLeft.Targets = TargetFoeCenter.Targets = TargetFoeRight.Targets = PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tTargetAllyLeft.Enabled = TargetAllyRight.Enabled = TargetFoeRight.Enabled = false;\n\t\t\t\t\t\tTargetAllyCenter.Enabled = TargetFoeLeft.Enabled = TargetFoeCenter.Enabled = true;\n\t\t\t\t\t\tTargetAllyLeft.LineRightVisible = TargetAllyCenter.LineRightVisible = TargetFoeRight.LineRightVisible = TargetFoeLeft.LineDownVisible = TargetFoeRight.LineDownVisible = false;\n\t\t\t\t\t\tTargetFoeCenter.LineRightVisible = TargetFoeCenter.LineDownVisible = true;\n\t\t\t\t\t\tTargetAllyCenter.Targets = TargetFoeLeft.Targets = TargetFoeCenter.Targets = PBETurnTarget.AllyCenter | PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEMoveTarget.AllTeam:\n\t\t\t\t{\n\t\t\t\t\tTargetAllyLeft.Enabled = TargetAllyCenter.Enabled = TargetAllyRight.Enabled = true;\n\t\t\t\t\tTargetFoeLeft.Enabled = TargetFoeCenter.Enabled = TargetFoeRight.Enabled = false;\n\t\t\t\t\tTargetAllyLeft.LineRightVisible = TargetAllyCenter.LineRightVisible = true;\n\t\t\t\t\tTargetFoeRight.LineRightVisible = TargetFoeCenter.LineRightVisible = TargetFoeCenter.LineDownVisible = TargetFoeLeft.LineDownVisible = TargetFoeRight.LineDownVisible = false;\n\t\t\t\t\tTargetAllyLeft.Targets = TargetAllyCenter.Targets = TargetAllyRight.Targets = PBETurnTarget.AllyLeft | PBETurnTarget.AllyCenter | PBETurnTarget.AllyRight;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEMoveTarget.RandomFoeSurrounding:\n\t\t\t\tcase PBEMoveTarget.Self:\n\t\t\t\t{\n\t\t\t\t\tif (Pokemon.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t{\n\t\t\t\t\t\tTargetAllyLeft.Enabled = true;\n\t\t\t\t\t\tTargetAllyCenter.Enabled = TargetAllyRight.Enabled = false;\n\t\t\t\t\t\tTargetAllyLeft.Targets = PBETurnTarget.AllyLeft;\n\t\t\t\t\t}\n\t\t\t\t\telse if (Pokemon.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t{\n\t\t\t\t\t\tTargetAllyCenter.Enabled = true;\n\t\t\t\t\t\tTargetAllyLeft.Enabled = TargetAllyRight.Enabled = false;\n\t\t\t\t\t\tTargetAllyCenter.Targets = PBETurnTarget.AllyCenter;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tTargetAllyRight.Enabled = true;\n\t\t\t\t\t\tTargetAllyLeft.Enabled = TargetAllyCenter.Enabled = false;\n\t\t\t\t\t\tTargetAllyRight.Targets = PBETurnTarget.AllyRight;\n\t\t\t\t\t}\n\t\t\t\t\tTargetFoeLeft.Enabled = TargetFoeCenter.Enabled = TargetFoeRight.Enabled = false;\n\t\t\t\t\tTargetAllyLeft.LineRightVisible = TargetAllyCenter.LineRightVisible = TargetFoeRight.LineRightVisible = TargetFoeCenter.LineRightVisible = TargetFoeCenter.LineDownVisible = TargetFoeLeft.LineDownVisible = TargetFoeRight.LineDownVisible = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEMoveTarget.SelfOrAllySurrounding:\n\t\t\t\t{\n\t\t\t\t\tif (Pokemon.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t{\n\t\t\t\t\t\tTargetAllyLeft.Enabled = TargetAllyCenter.Enabled = true;\n\t\t\t\t\t\tTargetAllyRight.Enabled = false;\n\t\t\t\t\t\tTargetAllyLeft.Targets = PBETurnTarget.AllyLeft;\n\t\t\t\t\t\tTargetAllyCenter.Targets = PBETurnTarget.AllyCenter;\n\t\t\t\t\t}\n\t\t\t\t\telse if (Pokemon.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t{\n\t\t\t\t\t\tTargetAllyCenter.Enabled = TargetAllyLeft.Enabled = TargetAllyRight.Enabled = true;\n\t\t\t\t\t\tTargetAllyLeft.Targets = PBETurnTarget.AllyLeft;\n\t\t\t\t\t\tTargetAllyCenter.Targets = PBETurnTarget.AllyCenter;\n\t\t\t\t\t\tTargetAllyRight.Targets = PBETurnTarget.AllyRight;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tTargetAllyCenter.Enabled = TargetAllyRight.Enabled = true;\n\t\t\t\t\t\tTargetAllyLeft.Enabled = false;\n\t\t\t\t\t\tTargetAllyCenter.Targets = PBETurnTarget.AllyCenter;\n\t\t\t\t\t\tTargetAllyRight.Targets = PBETurnTarget.AllyRight;\n\t\t\t\t\t}\n\t\t\t\t\tTargetFoeLeft.Enabled = TargetFoeCenter.Enabled = TargetFoeRight.Enabled = false;\n\t\t\t\t\tTargetAllyLeft.LineRightVisible = TargetAllyCenter.LineRightVisible = TargetFoeRight.LineRightVisible = TargetFoeCenter.LineRightVisible = TargetFoeCenter.LineDownVisible = TargetFoeLeft.LineDownVisible = TargetFoeRight.LineDownVisible = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEMoveTarget.SingleAllySurrounding:\n\t\t\t\t{\n\t\t\t\t\tif (Pokemon.FieldPosition == PBEFieldPosition.Left || Pokemon.FieldPosition == PBEFieldPosition.Right)\n\t\t\t\t\t{\n\t\t\t\t\t\tTargetAllyCenter.Enabled = true;\n\t\t\t\t\t\tTargetAllyLeft.Enabled = TargetAllyRight.Enabled = false;\n\t\t\t\t\t\tTargetAllyCenter.Targets = PBETurnTarget.AllyCenter;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tTargetAllyCenter.Enabled = false;\n\t\t\t\t\t\tTargetAllyLeft.Enabled = TargetAllyRight.Enabled = true;\n\t\t\t\t\t\tTargetAllyLeft.Targets = PBETurnTarget.AllyLeft;\n\t\t\t\t\t\tTargetAllyRight.Targets = PBETurnTarget.AllyRight;\n\t\t\t\t\t}\n\t\t\t\t\tTargetFoeLeft.Enabled = TargetFoeCenter.Enabled = TargetFoeRight.Enabled = false;\n\t\t\t\t\tTargetAllyLeft.LineRightVisible = TargetAllyCenter.LineRightVisible = TargetFoeRight.LineRightVisible = TargetFoeCenter.LineRightVisible = TargetFoeCenter.LineDownVisible = TargetFoeLeft.LineDownVisible = TargetFoeRight.LineDownVisible = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEMoveTarget.SingleFoeSurrounding:\n\t\t\t\t{\n\t\t\t\t\tif (Pokemon.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t{\n\t\t\t\t\t\tTargetFoeCenter.Enabled = TargetFoeRight.Enabled = true;\n\t\t\t\t\t\tTargetFoeLeft.Enabled = false;\n\t\t\t\t\t\tTargetFoeCenter.Targets = PBETurnTarget.FoeCenter;\n\t\t\t\t\t\tTargetFoeRight.Targets = PBETurnTarget.FoeRight;\n\t\t\t\t\t}\n\t\t\t\t\telse if (Pokemon.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t{\n\t\t\t\t\t\tTargetFoeLeft.Enabled = TargetFoeCenter.Enabled = TargetFoeRight.Enabled = true;\n\t\t\t\t\t\tTargetFoeLeft.Targets = PBETurnTarget.FoeLeft;\n\t\t\t\t\t\tTargetFoeCenter.Targets = PBETurnTarget.FoeCenter;\n\t\t\t\t\t\tTargetFoeRight.Targets = PBETurnTarget.FoeRight;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tTargetFoeLeft.Enabled = TargetFoeCenter.Enabled = true;\n\t\t\t\t\t\tTargetFoeRight.Enabled = false;\n\t\t\t\t\t\tTargetFoeLeft.Targets = PBETurnTarget.FoeLeft;\n\t\t\t\t\t\tTargetFoeCenter.Targets = PBETurnTarget.FoeCenter;\n\t\t\t\t\t}\n\t\t\t\t\tTargetAllyLeft.Enabled = TargetAllyCenter.Enabled = TargetAllyRight.Enabled = false;\n\t\t\t\t\tTargetAllyLeft.LineRightVisible = TargetAllyCenter.LineRightVisible = TargetFoeRight.LineRightVisible = TargetFoeCenter.LineRightVisible = TargetFoeCenter.LineDownVisible = TargetFoeLeft.LineDownVisible = TargetFoeRight.LineDownVisible = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEMoveTarget.SingleNotSelf:\n\t\t\t\t{\n\t\t\t\t\tif (Pokemon.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t{\n\t\t\t\t\t\tTargetAllyLeft.Enabled = false;\n\t\t\t\t\t\tTargetAllyCenter.Enabled = TargetAllyRight.Enabled = true;\n\t\t\t\t\t\tTargetAllyCenter.Targets = PBETurnTarget.AllyCenter;\n\t\t\t\t\t\tTargetAllyRight.Targets = PBETurnTarget.AllyRight;\n\t\t\t\t\t}\n\t\t\t\t\telse if (Pokemon.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t{\n\t\t\t\t\t\tTargetAllyCenter.Enabled = false;\n\t\t\t\t\t\tTargetAllyLeft.Enabled = TargetAllyRight.Enabled = true;\n\t\t\t\t\t\tTargetAllyLeft.Targets = PBETurnTarget.AllyLeft;\n\t\t\t\t\t\tTargetAllyRight.Targets = PBETurnTarget.AllyRight;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tTargetAllyRight.Enabled = false;\n\t\t\t\t\t\tTargetAllyLeft.Enabled = TargetAllyCenter.Enabled = true;\n\t\t\t\t\t\tTargetAllyLeft.Targets = PBETurnTarget.AllyLeft;\n\t\t\t\t\t\tTargetAllyCenter.Targets = PBETurnTarget.AllyCenter;\n\t\t\t\t\t}\n\t\t\t\t\tTargetFoeLeft.Enabled = TargetFoeCenter.Enabled = TargetFoeRight.Enabled = true;\n\t\t\t\t\tTargetAllyLeft.LineRightVisible = TargetAllyCenter.LineRightVisible = TargetFoeRight.LineRightVisible = TargetFoeCenter.LineRightVisible = TargetFoeCenter.LineDownVisible = TargetFoeLeft.LineDownVisible = TargetFoeRight.LineDownVisible = false;\n\t\t\t\t\tTargetFoeLeft.Targets = PBETurnTarget.FoeLeft;\n\t\t\t\t\tTargetFoeCenter.Targets = PBETurnTarget.FoeCenter;\n\t\t\t\t\tTargetFoeRight.Targets = PBETurnTarget.FoeRight;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEMoveTarget.SingleSurrounding:\n\t\t\t\t{\n\t\t\t\t\tif (Pokemon.FieldPosition == PBEFieldPosition.Left)\n\t\t\t\t\t{\n\t\t\t\t\t\tTargetAllyCenter.Enabled = TargetFoeCenter.Enabled = TargetFoeRight.Enabled = true;\n\t\t\t\t\t\tTargetAllyLeft.Enabled = TargetAllyRight.Enabled = TargetFoeLeft.Enabled = false;\n\t\t\t\t\t\tTargetAllyCenter.Targets = PBETurnTarget.AllyCenter;\n\t\t\t\t\t\tTargetFoeCenter.Targets = PBETurnTarget.FoeCenter;\n\t\t\t\t\t\tTargetFoeRight.Targets = PBETurnTarget.FoeRight;\n\t\t\t\t\t}\n\t\t\t\t\telse if (Pokemon.FieldPosition == PBEFieldPosition.Center)\n\t\t\t\t\t{\n\t\t\t\t\t\tTargetAllyLeft.Enabled = TargetAllyRight.Enabled = TargetFoeLeft.Enabled = TargetFoeCenter.Enabled = TargetFoeRight.Enabled = true;\n\t\t\t\t\t\tTargetAllyCenter.Enabled = false;\n\t\t\t\t\t\tTargetAllyLeft.Targets = PBETurnTarget.AllyLeft;\n\t\t\t\t\t\tTargetAllyRight.Targets = PBETurnTarget.AllyRight;\n\t\t\t\t\t\tTargetFoeLeft.Targets = PBETurnTarget.FoeLeft;\n\t\t\t\t\t\tTargetFoeCenter.Targets = PBETurnTarget.FoeCenter;\n\t\t\t\t\t\tTargetFoeRight.Targets = PBETurnTarget.FoeRight;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tTargetAllyCenter.Enabled = TargetFoeLeft.Enabled = TargetFoeCenter.Enabled = true;\n\t\t\t\t\t\tTargetAllyLeft.Enabled = TargetAllyRight.Enabled = TargetFoeRight.Enabled = false;\n\t\t\t\t\t\tTargetAllyCenter.Targets = PBETurnTarget.AllyCenter;\n\t\t\t\t\t\tTargetFoeLeft.Targets = PBETurnTarget.FoeLeft;\n\t\t\t\t\t\tTargetFoeCenter.Targets = PBETurnTarget.FoeCenter;\n\t\t\t\t\t}\n\t\t\t\t\tTargetAllyLeft.LineRightVisible = TargetAllyCenter.LineRightVisible = TargetFoeRight.LineRightVisible = TargetFoeCenter.LineRightVisible = TargetFoeCenter.LineDownVisible = TargetFoeLeft.LineDownVisible = TargetFoeRight.LineDownVisible = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tdefault: throw new InvalidDataException(nameof(possibleTargets));\n\t\t\t}\n\t\t}\n\n\t\t// This would still show the lines if a move had lines\n\t\tif (Pokemon.TempLockedTargets != PBETurnTarget.None)\n\t\t{\n\t\t\tif (!Pokemon.TempLockedTargets.HasFlag(PBETurnTarget.AllyLeft))\n\t\t\t{\n\t\t\t\tTargetAllyLeft.Enabled = false;\n\t\t\t}\n\t\t\tif (!Pokemon.TempLockedTargets.HasFlag(PBETurnTarget.AllyCenter))\n\t\t\t{\n\t\t\t\tTargetAllyCenter.Enabled = false;\n\t\t\t}\n\t\t\tif (!Pokemon.TempLockedTargets.HasFlag(PBETurnTarget.AllyRight))\n\t\t\t{\n\t\t\t\tTargetAllyRight.Enabled = false;\n\t\t\t}\n\t\t\tif (!Pokemon.TempLockedTargets.HasFlag(PBETurnTarget.FoeLeft))\n\t\t\t{\n\t\t\t\tTargetFoeLeft.Enabled = false;\n\t\t\t}\n\t\t\tif (!Pokemon.TempLockedTargets.HasFlag(PBETurnTarget.FoeCenter))\n\t\t\t{\n\t\t\t\tTargetFoeCenter.Enabled = false;\n\t\t\t}\n\t\t\tif (!Pokemon.TempLockedTargets.HasFlag(PBETurnTarget.FoeRight))\n\t\t\t{\n\t\t\t\tTargetFoeRight.Enabled = false;\n\t\t\t}\n\t\t}\n\n\t\tTargetsVisible = true;\n\t}\n\tprivate bool CanUsePosition(PBEFieldPosition pos)\n\t{\n\t\tPBETrainer t = BattleView.Client.Trainer!;\n\t\treturn !SwitchesBuilder!.IsStandBy(pos) && t.OwnsSpot(pos) && !t.Team.IsSpotOccupied(pos);\n\t}\n\tprivate void DisplayPositions()\n\t{\n\t\tLeftPositionEnabled = CenterPositionEnabled = RightPositionEnabled = false;\n\t\tswitch (BattleView.Client.Battle.BattleFormat)\n\t\t{\n\t\t\tcase PBEBattleFormat.Single:\n\t\t\t{\n\t\t\t\tSelectPosition(\"Center\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEBattleFormat.Double:\n\t\t\t{\n\t\t\t\tLeftPositionEnabled = CanUsePosition(PBEFieldPosition.Left);\n\t\t\t\tRightPositionEnabled = CanUsePosition(PBEFieldPosition.Right);\n\t\t\t\tif (_leftPositionEnabled && !_rightPositionEnabled)\n\t\t\t\t{\n\t\t\t\t\tSelectPosition(\"Left\");\n\t\t\t\t}\n\t\t\t\telse if (!_leftPositionEnabled && _rightPositionEnabled)\n\t\t\t\t{\n\t\t\t\t\tSelectPosition(\"Right\");\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tBattleView.AddMessage($\"Send {Pokemon.Nickname} where?\", messageLog: false);\n\t\t\t\t\tPositionsVisible = true;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEBattleFormat.Triple:\n\t\t\tcase PBEBattleFormat.Rotation:\n\t\t\t{\n\t\t\t\tLeftPositionEnabled = CanUsePosition(PBEFieldPosition.Left);\n\t\t\t\tCenterPositionEnabled = CanUsePosition(PBEFieldPosition.Center);\n\t\t\t\tRightPositionEnabled = CanUsePosition(PBEFieldPosition.Right);\n\t\t\t\tif (_leftPositionEnabled && !_centerPositionEnabled && !_rightPositionEnabled)\n\t\t\t\t{\n\t\t\t\t\tSelectPosition(\"Left\");\n\t\t\t\t}\n\t\t\t\telse if (!_leftPositionEnabled && _centerPositionEnabled && !_rightPositionEnabled)\n\t\t\t\t{\n\t\t\t\t\tSelectPosition(\"Center\");\n\t\t\t\t}\n\t\t\t\telse if (!_leftPositionEnabled && !_centerPositionEnabled && _rightPositionEnabled)\n\t\t\t\t{\n\t\t\t\t\tSelectPosition(\"Right\");\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tBattleView.AddMessage($\"Send {Pokemon.Nickname} where?\", messageLog: false);\n\t\t\t\t\tPositionsVisible = true;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: throw new InvalidDataException(nameof(BattleView.Client.Battle.BattleFormat));\n\t\t}\n\t}\n\tpublic void SelectTarget(string arg)\n\t{\n\t\tPBETurnTarget targets;\n\t\tswitch (arg)\n\t\t{\n\t\t\tcase \"AllyLeft\": targets = TargetAllyLeft.Targets; break;\n\t\t\tcase \"AllyCenter\": targets = TargetAllyCenter.Targets; break;\n\t\t\tcase \"AllyRight\": targets = TargetAllyRight.Targets; break;\n\t\t\tcase \"FoeLeft\": targets = TargetFoeLeft.Targets; break;\n\t\t\tcase \"FoeCenter\": targets = TargetFoeCenter.Targets; break;\n\t\t\tcase \"FoeRight\": targets = TargetFoeRight.Targets; break;\n\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(arg));\n\t\t}\n\t\tTargetsVisible = false;\n\t\tBackVisible = false;\n\t\tActionsBuilder!.PushMove(_fightMove, targets);\n\t}\n\tpublic void SelectPosition(string arg)\n\t{\n\t\tPBEFieldPosition pos = Enum.Parse<PBEFieldPosition>(arg);\n\t\tPositionsVisible = false;\n\t\tBackVisible = false;\n\t\tSwitchesBuilder!.Push(Pokemon, pos);\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineClient/Views/BattleView.xaml",
    "content": "﻿<UserControl xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:views=\"clr-namespace:Kermalis.PokemonBattleEngineClient.Views;assembly=PokemonBattleEngineClient\"\n             x:Class=\"Kermalis.PokemonBattleEngineClient.Views.BattleView\"\n             Width=\"896\" Height=\"576\">\n\t<StackPanel Orientation=\"Horizontal\">\n\t\t<StackPanel Orientation=\"Vertical\">\n\t\t\t<views:FieldView Name=\"Field\"/>\n\t\t\t<views:ActionsView Name=\"Actions\"/>\n\t\t</StackPanel>\n\t\t<views:MessageView Name=\"Messages\"/>\n\t</StackPanel>\n</UserControl>"
  },
  {
    "path": "PokemonBattleEngineClient/Views/BattleView.xaml.cs",
    "content": "﻿using Avalonia.Controls;\nusing Avalonia.Markup.Xaml;\nusing Kermalis.PokemonBattleEngineClient.Clients;\n\nnamespace Kermalis.PokemonBattleEngineClient.Views;\n\npublic sealed class BattleView : UserControl\n{\n\tpublic FieldView Field { get; }\n\tpublic ActionsView Actions { get; }\n\tprivate readonly MessageView _messages;\n\n\tinternal BattleClient Client { get; }\n\n\tpublic BattleView()\n\t{\n\t\t// This constructor only exists so xaml compiles\n\t\tField = null!;\n\t\tActions = null!;\n\t\t_messages = null!;\n\t\tClient = null!;\n\t\tAvaloniaXamlLoader.Load(this);\n\t}\n\tinternal BattleView(BattleClient client)\n\t{\n\t\tAvaloniaXamlLoader.Load(this);\n\n\t\tClient = client;\n\t\tField = this.FindControl<FieldView>(\"Field\");\n\t\tField.SetBattleView(this);\n\t\tActions = this.FindControl<ActionsView>(\"Actions\");\n\t\tActions.BattleView = this;\n\t\t_messages = this.FindControl<MessageView>(\"Messages\");\n\t}\n\n\tinternal void AddMessage(string message, bool messageBox = true, bool messageLog = true)\n\t{\n\t\tif (messageBox)\n\t\t{\n\t\t\tField.SetMessage(message);\n\t\t}\n\t\tif (messageLog)\n\t\t{\n\t\t\t_messages.AddMessage(message);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineClient/Views/FieldView.xaml",
    "content": "﻿<UserControl xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:gif=\"clr-namespace:AvaloniaGif;assembly=AvaloniaGif\"\n             xmlns:infrastructure=\"clr-namespace:Kermalis.PokemonBattleEngineClient.Infrastructure;assembly=PokemonBattleEngineClient\"\n             xmlns:views=\"clr-namespace:Kermalis.PokemonBattleEngineClient.Views;assembly=PokemonBattleEngineClient\"\n             x:Class=\"Kermalis.PokemonBattleEngineClient.Views.FieldView\"\n             Background=\"Black\">\n\t<Canvas Width=\"512\" Height=\"384\">\n\t\t<Canvas.Background>\n\t\t\t<ImageBrush Source=\"{Binding BGSource}\"/>\n\t\t</Canvas.Background>\n\n\t\t<views:PokemonView Name=\"Battler1_Right\" Canvas.Left=\"{Binding Location.X}\" Canvas.Top=\"{Binding Location.Y}\"/>\n\t\t<views:PokemonView Name=\"Battler1_Left\" Canvas.Left=\"{Binding Location.X}\" Canvas.Top=\"{Binding Location.Y}\"/>\n\t\t<views:PokemonView Name=\"Battler1_Center\" Canvas.Left=\"{Binding Location.X}\" Canvas.Top=\"{Binding Location.Y}\"/>\n\t\t<views:PokemonView Name=\"Battler0_Center\" Canvas.Left=\"{Binding Location.X}\" Canvas.Top=\"{Binding Location.Y}\">\n\t\t\t<views:PokemonView.RenderTransform>\n\t\t\t\t<ScaleTransform ScaleX=\"2\" ScaleY=\"2\"/>\n\t\t\t</views:PokemonView.RenderTransform>\n\t\t</views:PokemonView>\n\t\t<views:PokemonView Name=\"Battler0_Left\" Canvas.Left=\"{Binding Location.X}\" Canvas.Top=\"{Binding Location.Y}\">\n\t\t\t<views:PokemonView.RenderTransform>\n\t\t\t\t<ScaleTransform ScaleX=\"2\" ScaleY=\"2\"/>\n\t\t\t</views:PokemonView.RenderTransform>\n\t\t</views:PokemonView>\n\t\t<views:PokemonView Name=\"Battler0_Right\" Canvas.Left=\"{Binding Location.X}\" Canvas.Top=\"{Binding Location.Y}\">\n\t\t\t<views:PokemonView.RenderTransform>\n\t\t\t\t<ScaleTransform ScaleX=\"2\" ScaleY=\"2\"/>\n\t\t\t</views:PokemonView.RenderTransform>\n\t\t</views:PokemonView>\n\n\t\t<gif:GifImage Name=\"WeatherGif\" IsHitTestVisible=\"false\"/>\n\t\t<Rectangle Name=\"WeatherDim\" Width=\"512\" Height=\"384\" IsHitTestVisible=\"false\"/>\n\n\t\t<views:HPBarView Name=\"Bar0_Left\" Canvas.Left=\"{Binding Location.X}\" Canvas.Top=\"{Binding Location.Y}\"/>\n\t\t<views:HPBarView Name=\"Bar0_Center\" Canvas.Left=\"{Binding Location.X}\" Canvas.Top=\"{Binding Location.Y}\"/>\n\t\t<views:HPBarView Name=\"Bar0_Right\" Canvas.Left=\"{Binding Location.X}\" Canvas.Top=\"{Binding Location.Y}\"/>\n\t\t<views:HPBarView Name=\"Bar1_Left\" Canvas.Left=\"{Binding Location.X}\" Canvas.Top=\"{Binding Location.Y}\"/>\n\t\t<views:HPBarView Name=\"Bar1_Center\" Canvas.Left=\"{Binding Location.X}\" Canvas.Top=\"{Binding Location.Y}\"/>\n\t\t<views:HPBarView Name=\"Bar1_Right\" Canvas.Left=\"{Binding Location.X}\" Canvas.Top=\"{Binding Location.Y}\"/>\n\n\t\t<Rectangle Width=\"512\" Height=\"1\" Canvas.Top=\"336\" Fill=\"#90000000\" IsVisible=\"{Binding MessageBoxVisible}\" IsHitTestVisible=\"false\"/>\n\t\t<Rectangle Width=\"512\" Height=\"1\" Canvas.Top=\"337\" Fill=\"#5A000000\" IsVisible=\"{Binding MessageBoxVisible}\" IsHitTestVisible=\"false\"/>\n\t\t<Rectangle Width=\"512\" Height=\"1\" Canvas.Top=\"338\" Fill=\"#76000000\" IsVisible=\"{Binding MessageBoxVisible}\" IsHitTestVisible=\"false\"/>\n\t\t<Rectangle Width=\"512\" Height=\"36\" Canvas.Top=\"339\" Fill=\"#90000000\" IsVisible=\"{Binding MessageBoxVisible}\" IsHitTestVisible=\"false\"/>\n\t\t<Rectangle Width=\"512\" Height=\"1\" Canvas.Top=\"375\" Fill=\"#76000000\" IsVisible=\"{Binding MessageBoxVisible}\" IsHitTestVisible=\"false\"/>\n\t\t<Rectangle Width=\"512\" Height=\"1\" Canvas.Top=\"376\" Fill=\"#5A000000\" IsVisible=\"{Binding MessageBoxVisible}\" IsHitTestVisible=\"false\"/>\n\t\t<Rectangle Width=\"512\" Height=\"1\" Canvas.Top=\"377\" Fill=\"#90000000\" IsVisible=\"{Binding MessageBoxVisible}\" IsHitTestVisible=\"false\"/>\n\t\t<Image Source=\"{Binding Message, Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=BattleWhite, Mode=OneWay}\" Canvas.Left=\"16\" Canvas.Top=\"341\" IsVisible=\"{Binding MessageBoxVisible}\" IsHitTestVisible=\"false\"/>\n\t</Canvas>\n</UserControl>"
  },
  {
    "path": "PokemonBattleEngineClient/Views/FieldView.xaml.cs",
    "content": "﻿using Avalonia;\nusing Avalonia.Controls;\nusing Avalonia.Controls.Shapes;\nusing Avalonia.Markup.Xaml;\nusing Avalonia.Media;\nusing Avalonia.Media.Imaging;\nusing Avalonia.Threading;\nusing AvaloniaGif;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngineClient.Infrastructure;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngineClient.Views;\n\npublic sealed class FieldView : UserControl, INotifyPropertyChanged\n{\n\tprivate void OnPropertyChanged(string property)\n\t{\n\t\tPropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));\n\t}\n\tpublic new event PropertyChangedEventHandler? PropertyChanged;\n\n\tpublic IBitmap BGSource { get; private set; }\n\tprivate string _message;\n\tpublic string Message\n\t{\n\t\tget => _message;\n\t\tprivate set\n\t\t{\n\t\t\tif (_message != value)\n\t\t\t{\n\t\t\t\t_message = value;\n\t\t\t\tOnPropertyChanged(nameof(Message));\n\t\t\t}\n\t\t}\n\t}\n\tprivate bool _messageBoxVisible;\n\tpublic bool MessageBoxVisible\n\t{\n\t\tget => _messageBoxVisible;\n\t\tprivate set\n\t\t{\n\t\t\tif (_messageBoxVisible != value)\n\t\t\t{\n\t\t\t\t_messageBoxVisible = value;\n\t\t\t\tOnPropertyChanged(nameof(MessageBoxVisible));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate BattleView _battleView;\n\tprivate readonly Rectangle _dim;\n\tprivate readonly GifImage _gif;\n\t// Resources\n\tprivate static IBrush _hailstormDim = null!,\n\t\t\t_harshSunlightDim = null!,\n\t\t\t_rainDim = null!,\n\t\t\t_sandstormDim = null!;\n\tprivate static Dictionary<PBEWeather, Stream> _weathers = null!;\n\n\tinternal static void CreateResources()\n\t{\n\t\t_hailstormDim = new SolidColorBrush(Color.FromUInt32(0x20D0FFFF));\n\t\t_harshSunlightDim = new LinearGradientBrush()\n\t\t{\n\t\t\tStartPoint = new RelativePoint(0.0, 1.0, RelativeUnit.Relative),\n\t\t\tEndPoint = new RelativePoint(1.0, 0.0, RelativeUnit.Relative),\n\t\t\tGradientStops =\n\t\t\t\t{\n\t\t\t\t\tnew GradientStop { Color = Color.FromUInt32(0x60FFF0A0), Offset = 0.0 },\n\t\t\t\t\tnew GradientStop { Color = Color.FromUInt32(0x20FFD080), Offset = 1.0 }\n\t\t\t\t}\n\t\t};\n\t\t_rainDim = new SolidColorBrush(Color.FromUInt32(0x40000000));\n\t\t_sandstormDim = new LinearGradientBrush()\n\t\t{\n\t\t\tStartPoint = new RelativePoint(0.0, 0.0, RelativeUnit.Relative),\n\t\t\tEndPoint = new RelativePoint(0.0, 1.0, RelativeUnit.Relative),\n\t\t\tGradientStops =\n\t\t\t\t{\n\t\t\t\t\tnew GradientStop { Color = Color.FromUInt32(0x40FF7F00), Offset = 0.0 },\n\t\t\t\t\tnew GradientStop { Color = Color.FromUInt32(0x60FFF0A0), Offset = 0.3 },\n\t\t\t\t\tnew GradientStop { Color = Color.FromUInt32(0x30FFC000), Offset = 1.0 }\n\t\t\t\t}\n\t\t};\n\n\t\t_weathers = new Dictionary<PBEWeather, Stream>\n\t\t\t{\n\t\t\t\t{ PBEWeather.Hailstorm, Utils.GetResourceStream(\"MISC.WEATHER_Hailstorm.gif\") },\n\t\t\t\t{ PBEWeather.Rain, Utils.GetResourceStream(\"MISC.WEATHER_Rain.gif\") }\n\t\t\t};\n\t}\n\n\tpublic FieldView()\n\t{\n\t\tDataContext = this;\n\t\tAvaloniaXamlLoader.Load(this);\n\n\t\t_dim = this.FindControl<Rectangle>(\"WeatherDim\");\n\t\t_gif = this.FindControl<GifImage>(\"WeatherGif\");\n\t\t// These are set in the appropriate states\n\t\tBGSource = null!;\n\t\t_message = null!;\n\t\t_battleView = null!;\n\t}\n\tinternal void SetBattleView(BattleView battleView)\n\t{\n\t\t_battleView = battleView;\n\t\tPBEBattle b = _battleView.Client.Battle;\n\t\tswitch (b.BattleFormat)\n\t\t{\n\t\t\tcase PBEBattleFormat.Single:\n\t\t\t{\n\t\t\t\tthis.FindControl<HPBarView>(\"Bar0_Center\").Location = new Point(204, 35);\n\n\t\t\t\tthis.FindControl<HPBarView>(\"Bar1_Center\").Location = new Point(204, 6);\n\n\t\t\t\tthis.FindControl<PokemonView>(\"Battler0_Center\").Location = new Point(75, 53);\n\n\t\t\t\tthis.FindControl<PokemonView>(\"Battler1_Center\").Location = new Point(284, 8);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEBattleFormat.Double:\n\t\t\t{\n\t\t\t\tthis.FindControl<HPBarView>(\"Bar0_Left\").Location = new Point(101, 35);\n\t\t\t\tthis.FindControl<HPBarView>(\"Bar0_Right\").Location = new Point(307, 35);\n\n\t\t\t\tthis.FindControl<HPBarView>(\"Bar1_Right\").Location = new Point(101, 6);\n\t\t\t\tthis.FindControl<HPBarView>(\"Bar1_Left\").Location = new Point(307, 6);\n\n\t\t\t\tthis.FindControl<PokemonView>(\"Battler0_Left\").Location = new Point(-37, 43);\n\t\t\t\tthis.FindControl<PokemonView>(\"Battler0_Right\").Location = new Point(168, 54);\n\n\t\t\t\tthis.FindControl<PokemonView>(\"Battler1_Right\").Location = new Point(242, 9);\n\t\t\t\tthis.FindControl<PokemonView>(\"Battler1_Left\").Location = new Point(332, 15);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEBattleFormat.Triple:\n\t\t\t{\n\t\t\t\tthis.FindControl<HPBarView>(\"Bar0_Left\").Location = new Point(50, 35);\n\t\t\t\tthis.FindControl<HPBarView>(\"Bar0_Center\").Location = new Point(204, 35);\n\t\t\t\tthis.FindControl<HPBarView>(\"Bar0_Right\").Location = new Point(358, 35);\n\n\t\t\t\tthis.FindControl<HPBarView>(\"Bar1_Right\").Location = new Point(50, 6);\n\t\t\t\tthis.FindControl<HPBarView>(\"Bar1_Center\").Location = new Point(204, 6);\n\t\t\t\tthis.FindControl<HPBarView>(\"Bar1_Left\").Location = new Point(358, 6);\n\n\t\t\t\tthis.FindControl<PokemonView>(\"Battler0_Left\").Location = new Point(-53, 51);\n\t\t\t\tthis.FindControl<PokemonView>(\"Battler0_Center\").Location = new Point(92, 31);\n\t\t\t\tthis.FindControl<PokemonView>(\"Battler0_Right\").Location = new Point(221, 76);\n\n\t\t\t\tthis.FindControl<PokemonView>(\"Battler1_Right\").Location = new Point(209, -1);\n\t\t\t\tthis.FindControl<PokemonView>(\"Battler1_Center\").Location = new Point(282, 16);\n\t\t\t\tthis.FindControl<PokemonView>(\"Battler1_Left\").Location = new Point(362, 8);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEBattleFormat.Rotation:\n\t\t\t{\n\t\t\t\tthis.FindControl<HPBarView>(\"Bar0_Left\").Location = new Point(50, 35);\n\t\t\t\tthis.FindControl<HPBarView>(\"Bar0_Center\").Location = new Point(204, 35);\n\t\t\t\tthis.FindControl<HPBarView>(\"Bar0_Right\").Location = new Point(358, 35);\n\n\t\t\t\tthis.FindControl<HPBarView>(\"Bar1_Right\").Location = new Point(50, 6);\n\t\t\t\tthis.FindControl<HPBarView>(\"Bar1_Center\").Location = new Point(204, 6);\n\t\t\t\tthis.FindControl<HPBarView>(\"Bar1_Left\").Location = new Point(358, 6);\n\n\t\t\t\tthis.FindControl<PokemonView>(\"Battler0_Left\").Location = new Point(-46, 384); // Hidden\n\t\t\t\tthis.FindControl<PokemonView>(\"Battler0_Center\").Location = new Point(52, 72);\n\t\t\t\tthis.FindControl<PokemonView>(\"Battler0_Right\").Location = new Point(228, 384); // Hidden\n\n\t\t\t\tthis.FindControl<PokemonView>(\"Battler1_Right\").Location = new Point(211, -34);\n\t\t\t\tthis.FindControl<PokemonView>(\"Battler1_Center\").Location = new Point(282, 16);\n\t\t\t\tthis.FindControl<PokemonView>(\"Battler1_Left\").Location = new Point(421, -24);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: throw new InvalidDataException(nameof(_battleView.Client.Battle.BattleFormat));\n\t\t}\n\t\tBGSource = new Bitmap(Utils.GetResourceStream($\"BG.BG_{b.BattleTerrain}_{b.BattleFormat}.png\"));\n\t\tOnPropertyChanged(nameof(BGSource));\n\t}\n\n\tinternal void SetMessage(string message)\n\t{\n\t\tMessage = message;\n\t\tMessageBoxVisible = !string.IsNullOrWhiteSpace(message); // Currently always true\n\t}\n\n\tinternal void UpdateWeather()\n\t{\n\t\tDispatcher.UIThread.InvokeAsync(() =>\n\t\t{\n\t\t\tswitch (_battleView.Client.Battle.Weather)\n\t\t\t{\n\t\t\t\tcase PBEWeather.Hailstorm:\n\t\t\t\t{\n\t\t\t\t\t_dim.Fill = _hailstormDim;\n\t\t\t\t\t_dim.IsVisible = true;\n\t\t\t\t\t_gif.SourceStream = _weathers[PBEWeather.Hailstorm];\n\t\t\t\t\t_gif.IsVisible = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEWeather.HarshSunlight:\n\t\t\t\t{\n\t\t\t\t\t_dim.Fill = _harshSunlightDim;\n\t\t\t\t\t_dim.IsVisible = true;\n\t\t\t\t\t_gif.IsVisible = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEWeather.Rain:\n\t\t\t\t{\n\t\t\t\t\t_dim.Fill = _rainDim;\n\t\t\t\t\t_dim.IsVisible = true;\n\t\t\t\t\t_gif.SourceStream = _weathers[PBEWeather.Rain];\n\t\t\t\t\t_gif.IsVisible = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEWeather.Sandstorm:\n\t\t\t\t{\n\t\t\t\t\t_dim.Fill = _sandstormDim;\n\t\t\t\t\t_dim.IsVisible = true;\n\t\t\t\t\t_gif.IsVisible = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t{\n\t\t\t\t\t_dim.IsVisible = false;\n\t\t\t\t\t_gif.IsVisible = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\tprivate void GetPokemonViewStuff(PBEBattlePokemon pkmn, PBEFieldPosition position, out bool backSprite, out HPBarView hpView, out PokemonView pkmnView)\n\t{\n\t\tbyte? owner = _battleView.Client.Trainer?.Team.Id;\n\t\tbackSprite = pkmn.Team.Id == 0 ? owner != 1 : owner == 1; // Spectators and replays view from team 0's perspective\n\t\thpView = this.FindControl<HPBarView>($\"Bar{(backSprite ? 0 : 1)}_{position}\");\n\t\tpkmnView = this.FindControl<PokemonView>($\"Battler{(backSprite ? 0 : 1)}_{position}\");\n\t}\n\tprivate void UpdatePokemon(PBEBattlePokemon pkmn, bool backSprite, HPBarView hpView, PokemonView pkmnView, bool hpBar, bool sprite)\n\t{\n\t\tbool useKnownInfo = _battleView.Client.ShouldUseKnownInfo(pkmn.Trainer);\n\t\tif (hpBar)\n\t\t{\n\t\t\thpView.Update(pkmn, useKnownInfo);\n\t\t}\n\t\tif (sprite)\n\t\t{\n\t\t\tpkmnView.Update(pkmn, backSprite, useKnownInfo);\n\t\t}\n\t}\n\t// pkmn.FieldPosition must be updated before calling these\n\tinternal void ShowPokemon(PBEBattlePokemon pkmn)\n\t{\n\t\tDispatcher.UIThread.InvokeAsync(() =>\n\t\t{\n\t\t\tGetPokemonViewStuff(pkmn, pkmn.FieldPosition, out bool backSprite, out HPBarView hpView, out PokemonView pkmnView);\n\t\t\tUpdatePokemon(pkmn, backSprite, hpView, pkmnView, true, true);\n\t\t\thpView.IsVisible = true;\n\t\t\tpkmnView.IsVisible = true;\n\t\t});\n\t}\n\tinternal void HidePokemon(PBEBattlePokemon pkmn, PBEFieldPosition oldPosition)\n\t{\n\t\tDispatcher.UIThread.InvokeAsync(() =>\n\t\t{\n\t\t\tGetPokemonViewStuff(pkmn, oldPosition, out _, out HPBarView hpView, out PokemonView pkmnView);\n\t\t\thpView.IsVisible = false;\n\t\t\tpkmnView.IsVisible = false;\n\t\t});\n\t}\n\tinternal void UpdatePokemon(PBEBattlePokemon pkmn, bool hpBar, bool sprite)\n\t{\n\t\tDispatcher.UIThread.InvokeAsync(() =>\n\t\t{\n\t\t\tGetPokemonViewStuff(pkmn, pkmn.FieldPosition, out bool backSprite, out HPBarView hpView, out PokemonView pkmnView);\n\t\t\tUpdatePokemon(pkmn, backSprite, hpView, pkmnView, hpBar, sprite);\n\t\t});\n\t}\n\tinternal void MovePokemon(PBEBattlePokemon pkmn, PBEFieldPosition oldPosition)\n\t{\n\t\tDispatcher.UIThread.InvokeAsync(() =>\n\t\t{\n\t\t\tGetPokemonViewStuff(pkmn, oldPosition, out _, out HPBarView hpView, out PokemonView pkmnView);\n\t\t\thpView.IsVisible = false;\n\t\t\tpkmnView.IsVisible = false;\n\t\t\tGetPokemonViewStuff(pkmn, pkmn.FieldPosition, out bool backSprite, out hpView, out pkmnView);\n\t\t\tUpdatePokemon(pkmn, backSprite, hpView, pkmnView, true, true);\n\t\t\thpView.IsVisible = true;\n\t\t\tpkmnView.IsVisible = true;\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineClient/Views/HPBarView.xaml",
    "content": "﻿<UserControl xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:infrastructure=\"clr-namespace:Kermalis.PokemonBattleEngineClient.Infrastructure;assembly=PokemonBattleEngineClient\"\n             x:Class=\"Kermalis.PokemonBattleEngineClient.Views.HPBarView\"\n             IsVisible=\"False\">\n\t<Image Name=\"Drawn\">\n\t\t<ToolTip.Tip>\n\t\t\t<Image Source=\"{Binding Description, Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuBlack, Mode=OneWay}\"/>\n\t\t</ToolTip.Tip>\n\t</Image>\n</UserControl>"
  },
  {
    "path": "PokemonBattleEngineClient/Views/HPBarView.xaml.cs",
    "content": "﻿using Avalonia;\nusing Avalonia.Controls;\nusing Avalonia.Markup.Xaml;\nusing Avalonia.Media;\nusing Avalonia.Media.Imaging;\nusing Avalonia.Platform;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngineClient.Infrastructure;\nusing System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\n\nnamespace Kermalis.PokemonBattleEngineClient.Views;\n\npublic sealed class HPBarView : UserControl, INotifyPropertyChanged\n{\n\tprivate void OnPropertyChanged(string property)\n\t{\n\t\tPropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));\n\n\t}\n\tpublic new event PropertyChangedEventHandler? PropertyChanged;\n\n\tprivate PBEBattlePokemon _pokemon;\n\tprivate Point _location;\n\tpublic Point Location\n\t{\n\t\tget => _location;\n\t\tinternal set\n\t\t{\n\t\t\tif (!_location.Equals(value))\n\t\t\t{\n\t\t\t\t_location = value;\n\t\t\t\tOnPropertyChanged(nameof(Location));\n\t\t\t}\n\t\t}\n\t}\n\tprivate bool _useKnownInfo;\n\tpublic string Description => Utils.CustomPokemonToString(_pokemon, _useKnownInfo);\n\n\tprivate readonly Image _drawn;\n\n\tprivate static SolidColorBrush _greenSides = null!,\n\t\t\t_greenMid = null!,\n\t\t\t_yellowSides = null!,\n\t\t\t_yellowMid = null!,\n\t\t\t_redSides = null!,\n\t\t\t_redMid = null!;\n\tprivate static Bitmap[] _hpBars = null!;\n\tprivate static Dictionary<PBEStatus1, Bitmap> _status1s = null!;\n\n\tinternal static void CreateResources()\n\t{\n\t\t_greenSides = new SolidColorBrush(0xFF008C29);\n\t\t_greenMid = new SolidColorBrush(0xFF00FF4A);\n\t\t_yellowSides = new SolidColorBrush(0xFF9C6310);\n\t\t_yellowMid = new SolidColorBrush(0xFFF7B500);\n\t\t_redSides = new SolidColorBrush(0xFF942131);\n\t\t_redMid = new SolidColorBrush(0xFFFF3142);\n\n\t\t_hpBars = new Bitmap[2] { new Bitmap(Utils.GetResourceStream(\"MISC.HPBAR_Ally.png\")), new Bitmap(Utils.GetResourceStream(\"MISC.HPBAR_Foe.png\")) };\n\t\t_status1s = new Dictionary<PBEStatus1, Bitmap>();\n\t\tfor (PBEStatus1 s = PBEStatus1.None + 1; s < PBEStatus1.MAX; s++)\n\t\t{\n\t\t\t_status1s.Add(s, new Bitmap(Utils.GetResourceStream(\"MISC.STATUS1_\" + s + \".png\")));\n\t\t}\n\t}\n\n\tpublic HPBarView()\n\t{\n\t\tDataContext = this;\n\t\tAvaloniaXamlLoader.Load(this);\n\n\t\t_drawn = this.FindControl<Image>(\"Drawn\");\n\t\t_pokemon = null!;\n\t}\n\n\tinternal void Update(PBEBattlePokemon pkmn, bool useKnownInfo)\n\t{\n\t\t_useKnownInfo = useKnownInfo;\n\t\t_pokemon = pkmn;\n\n\t\tvar wb = new WriteableBitmap(new PixelSize(104, 27), new Vector(96, 96), PixelFormat.Bgra8888, AlphaFormat.Premul);\n\t\tusing (IRenderTarget rtb = Utils.RenderInterface.CreateRenderTarget(new[] { new WriteableBitmapSurface(wb) }))\n\t\tusing (IDrawingContextImpl ctx = rtb.CreateDrawingContext(null))\n\t\t{\n\t\t\tint barResource;\n\t\t\tbyte yOffset;\n\t\t\tif (useKnownInfo)\n\t\t\t{\n\t\t\t\tbarResource = 1;\n\t\t\t\tyOffset = 2;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tbarResource = 0;\n\t\t\t\tyOffset = 0;\n\t\t\t}\n\t\t\tBitmap hpBar = _hpBars[barResource];\n\t\t\tctx.DrawBitmap(hpBar.PlatformImpl, 1.0, new Rect(0, 0, hpBar.PixelSize.Width, hpBar.PixelSize.Height), new Rect(0, 11 + yOffset, hpBar.PixelSize.Width, hpBar.PixelSize.Height));\n\n\t\t\tBitmap nickname = StringRenderer.Render(pkmn.KnownNickname, \"BattleName\");\n\t\t\tctx.DrawBitmap(nickname.PlatformImpl, 1.0, new Rect(0, 0, nickname.PixelSize.Width, nickname.PixelSize.Height), new Rect(72 - Math.Max(54, nickname.PixelSize.Width), yOffset, nickname.PixelSize.Width, nickname.PixelSize.Height));\n\n\t\t\tPBEGender gender = useKnownInfo && !pkmn.KnownStatus2.HasFlag(PBEStatus2.Transformed) ? pkmn.KnownGender : pkmn.Gender;\n\t\t\tBitmap level = StringRenderer.Render($\"{(gender == PBEGender.Female ? \"♀\" : gender == PBEGender.Male ? \"♂\" : \" \")}[LV]{pkmn.Level}\", \"BattleLevel\");\n\t\t\tctx.DrawBitmap(level.PlatformImpl, 1.0, new Rect(0, 0, level.PixelSize.Width, level.PixelSize.Height), new Rect(70, 1 + yOffset, level.PixelSize.Width, level.PixelSize.Height));\n\n\t\t\tif (pkmn.Status1 != PBEStatus1.None)\n\t\t\t{\n\t\t\t\tBitmap status = _status1s[pkmn.Status1];\n\t\t\t\tctx.DrawBitmap(status.PlatformImpl, 1.0, new Rect(0, 0, status.PixelSize.Width, status.PixelSize.Height), new Rect(1, 11 + yOffset, status.PixelSize.Width, status.PixelSize.Height));\n\t\t\t}\n\n\t\t\tIBrush hpSides, hpMid;\n\t\t\tif (pkmn.HPPercentage <= 0.20)\n\t\t\t{\n\t\t\t\thpSides = _redSides;\n\t\t\t\thpMid = _redMid;\n\t\t\t}\n\t\t\telse if (pkmn.HPPercentage <= 0.50)\n\t\t\t{\n\t\t\t\thpSides = _yellowSides;\n\t\t\t\thpMid = _yellowMid;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\thpSides = _greenSides;\n\t\t\t\thpMid = _greenMid;\n\t\t\t}\n\t\t\tconst byte lineW = 48;\n\t\t\tint theW = (int)(lineW * pkmn.HPPercentage);\n\t\t\tif (theW == 0 && pkmn.HPPercentage > 0)\n\t\t\t{\n\t\t\t\ttheW = 1;\n\t\t\t}\n\t\t\tctx.DrawRectangle(hpSides, null, new Rect(38, 13 + yOffset, theW, 1));\n\t\t\tctx.DrawRectangle(hpMid, null, new Rect(38, 13 + yOffset + 1, theW, 1));\n\t\t\tctx.DrawRectangle(hpSides, null, new Rect(38, 13 + yOffset + 2, theW, 1));\n\n\t\t\tif (!useKnownInfo)\n\t\t\t{\n\t\t\t\tBitmap hp = StringRenderer.Render(pkmn.HP.ToString(), \"BattleHP\");\n\t\t\t\tctx.DrawBitmap(hp.PlatformImpl, 1.0, new Rect(0, 0, hp.PixelSize.Width, hp.PixelSize.Height), new Rect(62 - hp.PixelSize.Width, 16 + yOffset, hp.PixelSize.Width, hp.PixelSize.Height));\n\t\t\t\tBitmap maxHP = StringRenderer.Render(pkmn.MaxHP.ToString(), \"BattleHP\");\n\t\t\t\tctx.DrawBitmap(maxHP.PlatformImpl, 1.0, new Rect(0, 0, maxHP.PixelSize.Width, maxHP.PixelSize.Height), new Rect(70, 16 + yOffset, maxHP.PixelSize.Width, maxHP.PixelSize.Height));\n\t\t\t}\n\t\t}\n\t\t_drawn.Source = wb;\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineClient/Views/MainView.xaml",
    "content": "﻿<UserControl xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:infrastructure=\"clr-namespace:Kermalis.PokemonBattleEngineClient.Infrastructure;assembly=PokemonBattleEngineClient\"\n             xmlns:views=\"clr-namespace:Kermalis.PokemonBattleEngineClient.Views;assembly=PokemonBattleEngineClient\"\n             x:Class=\"Kermalis.PokemonBattleEngineClient.Views.MainView\"\n             Background=\"#313131\">\n\t<UserControl.Styles>\n\t\t<Style Selector=\"StackPanel.nice > StackPanel > Layoutable\">\n\t\t\t<Setter Property=\"VerticalAlignment\" Value=\"Center\"/>\n\t\t</Style>\n\t\t<Style Selector=\"StackPanel.nice > StackPanel\">\n\t\t\t<Setter Property=\"Orientation\" Value=\"Horizontal\" />\n\t\t\t<Setter Property=\"Spacing\" Value=\"5\" />\n\t\t\t<Setter Property=\"HorizontalAlignment\" Value=\"Center\" />\n\t\t</Style>\n\t\t<Style Selector=\"StackPanel.nice\">\n\t\t\t<Setter Property=\"Orientation\" Value=\"Vertical\"/>\n\t\t</Style>\n\t</UserControl.Styles>\n\t<Viewbox Stretch=\"Uniform\" VerticalAlignment=\"Top\">\n\t\t<TabControl Name=\"Tabs\">\n\t\t\t<TabItem Header=\"Home\">\n\t\t\t\t<StackPanel Classes=\"nice\">\n\t\t\t\t\t<views:TeamBuilderView Name=\"TeamBuilder\"/>\n\t\t\t\t\t<StackPanel>\n\t\t\t\t\t\t<Image Stretch=\"None\" Source=\"{Binding Source='Online Multiplayer:', Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuWhite, Mode=OneWay}\"/>\n\t\t\t\t\t\t<TextBox Name=\"IP\" Width=\"100\" Text=\"127.0.0.1\" Watermark=\"IP Address\"/>\n\t\t\t\t\t\t<NumericUpDown Name=\"Port\" Width=\"100\" Value=\"8888\" Minimum=\"0\" Maximum=\"65535\" ClipValueToMinMax=\"True\"/>\n\t\t\t\t\t\t<Button Width=\"150\" Height=\"30\" Background=\"White\" BorderBrush=\"#A2A2A2\" BorderThickness=\"4\" Name=\"Connect\" Command=\"{Binding Connect}\">\n\t\t\t\t\t\t\t<Image Stretch=\"None\" Source=\"{Binding ConnectText, Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuBlack, Mode=OneWay}\"/>\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</StackPanel>\n\t\t\t\t\t<StackPanel>\n\t\t\t\t\t\t<Image Stretch=\"None\" Source=\"{Binding Source='Singleplayer:', Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuWhite, Mode=OneWay}\"/>\n\t\t\t\t\t\t<TextBox Name=\"Name\" Width=\"100\" Text=\"Dawn\" Watermark=\"Player Name\"/>\n\t\t\t\t\t\t<Button Width=\"100\" Height=\"30\" Background=\"White\" BorderBrush=\"#A2A2A2\" BorderThickness=\"4\" Command=\"{Binding SinglePlayer}\" CommandParameter=\"S\"\n\t\t\t\t\t\t\t\tIsEnabled=\"{Binding !#Multi.IsChecked}\">\n\t\t\t\t\t\t\t<Image Stretch=\"None\" Source=\"{Binding Source='Single Battle', Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuBlack, Mode=OneWay}\"/>\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t<Button Width=\"100\" Height=\"30\" Background=\"White\" BorderBrush=\"#A2A2A2\" BorderThickness=\"4\" Command=\"{Binding SinglePlayer}\" CommandParameter=\"D\">\n\t\t\t\t\t\t\t<Image Stretch=\"None\" Source=\"{Binding Source='Double Battle', Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuBlack, Mode=OneWay}\"/>\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t<Button Width=\"100\" Height=\"30\" Background=\"White\" BorderBrush=\"#A2A2A2\" BorderThickness=\"4\" Command=\"{Binding SinglePlayer}\" CommandParameter=\"T\">\n\t\t\t\t\t\t\t<Image Stretch=\"None\" Source=\"{Binding Source='Triple Battle', Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuBlack, Mode=OneWay}\"/>\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t<CheckBox Name=\"Multi\">\n\t\t\t\t\t\t\t<Image Stretch=\"None\" Source=\"{Binding Source='Multi-Battle', Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuWhite, Mode=OneWay}\"/>\n\t\t\t\t\t\t</CheckBox>\n\t\t\t\t\t</StackPanel>\n\t\t\t\t\t<StackPanel>\n\t\t\t\t\t\t<Image Stretch=\"None\" Source=\"{Binding Source='Replays:', Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuWhite, Mode=OneWay}\"/>\n\t\t\t\t\t\t<Button Width=\"100\" Height=\"30\" Background=\"White\" BorderBrush=\"#A2A2A2\" BorderThickness=\"4\" Command=\"{Binding WatchReplay}\">\n\t\t\t\t\t\t\t<Image Stretch=\"None\" Source=\"{Binding Source='Watch Replay', Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuBlack, Mode=OneWay}\"/>\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</StackPanel>\n\t\t\t\t</StackPanel>\n\t\t\t</TabItem>\n\t\t</TabControl>\n\t</Viewbox>\n</UserControl>"
  },
  {
    "path": "PokemonBattleEngineClient/Views/MainView.xaml.cs",
    "content": "﻿using Avalonia.Controls;\nusing Avalonia.Markup.Xaml;\nusing Avalonia.Threading;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Data.Legality;\nusing Kermalis.PokemonBattleEngine.DefaultData;\nusing Kermalis.PokemonBattleEngine.Network;\nusing Kermalis.PokemonBattleEngine.Packets;\nusing Kermalis.PokemonBattleEngineClient.Clients;\nusing System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Linq;\nusing System.Threading;\n\nnamespace Kermalis.PokemonBattleEngineClient.Views;\n\npublic sealed class MainView : UserControl, INotifyPropertyChanged\n{\n\tprivate void OnPropertyChanged(string property)\n\t{\n\t\tPropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));\n\t}\n\tpublic new event PropertyChangedEventHandler? PropertyChanged;\n\n\tprivate string _connectText;\n\tpublic string ConnectText\n\t{\n\t\tget => _connectText;\n\t\tprivate set\n\t\t{\n\t\t\tif (_connectText != value)\n\t\t\t{\n\t\t\t\t_connectText = value;\n\t\t\t\tOnPropertyChanged(nameof(ConnectText));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate readonly List<BattleClient> _battles = new();\n\n\tprivate readonly TabControl _tabs;\n\tprivate readonly TeamBuilderView _teamBuilder;\n\tprivate readonly TextBox _ip;\n\tprivate readonly NumericUpDown _port;\n\tprivate readonly Button _connect;\n\tprivate readonly TextBox _name;\n\tprivate readonly CheckBox _multi;\n\n#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.\n\tpublic MainView()\n#pragma warning restore CS8618 // _connectText is set in ResetConnectButton()\n\t{\n\t\tDataContext = this;\n\t\tAvaloniaXamlLoader.Load(this);\n\n\t\t_tabs = this.FindControl<TabControl>(\"Tabs\");\n\t\t_teamBuilder = this.FindControl<TeamBuilderView>(\"TeamBuilder\");\n\t\t_ip = this.FindControl<TextBox>(\"IP\");\n\t\t_port = this.FindControl<NumericUpDown>(\"Port\");\n\t\t_connect = this.FindControl<Button>(\"Connect\");\n\t\t_name = this.FindControl<TextBox>(\"Name\");\n\t\t_multi = this.FindControl<CheckBox>(\"Multi\");\n\t\tResetConnectButton();\n\t}\n\tprivate void ResetConnectButton()\n\t{\n\t\tConnectText = \"Connect\";\n\t\t_connect.IsEnabled = true;\n\t}\n\n\tpublic void Connect()\n\t{\n\t\t_connect.IsEnabled = false;\n\t\tConnectText = \"Connecting...\";\n\t\tstring host = _ip.Text;\n\t\tushort port = (ushort)_port.Value;\n\t\tnew Thread(() =>\n\t\t{\n\t\t\tNetworkClientConnection? con = null;\n\t\t\tvoid ConnectHandler(object? arg)\n\t\t\t{\n\t\t\t\tDispatcher.UIThread.InvokeAsync(() =>\n\t\t\t\t{\n\t\t\t\t\tif (arg is string str)\n\t\t\t\t\t{\n\t\t\t\t\t\tConnectText = str;\n\t\t\t\t\t}\n\t\t\t\t\telse if (arg is null)\n\t\t\t\t\t{\n\t\t\t\t\t\tResetConnectButton();\n\t\t\t\t\t\tcon?.Dispose();\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t// No need to dispose con because NetworkClient.Dispose disposes the same thing\n\t\t\t\t\t\tvar tup = (Tuple<PBEClient, PBEBattlePacket, byte>)arg;\n\t\t\t\t\t\tAdd(new NetworkClient(tup.Item1, tup.Item2, tup.Item3, $\"MP {_battles.Count + 1}\"));\n\t\t\t\t\t\tResetConnectButton();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t\ttry\n\t\t\t{\n\t\t\t\tcon = new NetworkClientConnection(host, port, _teamBuilder.Team.Party, ConnectHandler);\n\t\t\t}\n\t\t\tcatch\n\t\t\t{\n\t\t\t\tcon = null;\n\t\t\t}\n\t\t})\n\t\t{\n\t\t\tName = \"Connect Thread\"\n\t\t}.Start();\n\t}\n\tpublic void WatchReplay()\n\t{\n\t\tconst string path = \"SinglePlayer Battle.pbereplay\";\n\t\t//const string path = @\"C:\\Users\\Kermalis\\Documents\\Development\\GitHub\\PokemonBattleEngine\\PokemonBattleEngineExtras\\bin\\Debug\\netcoreapp3.1\\AI Demo.pbereplay\";\n\t\t//const string path = @\"C:\\Users\\Kermalis\\Documents\\Development\\GitHub\\PokeI\\bin\\Release\\netcoreapp3.1\\AI Final Replay.pbereplay\";\n\t\tAdd(new ReplayClient(path, $\"Replay {_battles.Count + 1}\"));\n\t}\n\tpublic void SinglePlayer(string battleType)\n\t{\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\t\tPBEBattleFormat battleFormat;\n\t\tIReadOnlyList<PBETrainerInfo> t0, t1;\n\t\tvoid Create1v1()\n\t\t{\n\t\t\tPBELegalPokemonCollection p0, p1;\n\t\t\tint numPerTrainer = settings.MaxPartySize;\n\t\t\tp0 = PBEDDRandomTeamGenerator.CreateRandomTeam(numPerTrainer);\n\t\t\tp1 = PBEDDRandomTeamGenerator.CreateRandomTeam(numPerTrainer);\n\t\t\tt0 = new[] { new PBETrainerInfo(p0, GetName(), true) };\n\t\t\tt1 = new[] { new PBETrainerInfo(p1, \"Champion Cynthia\", false) };\n\t\t}\n\t\tstring GetName()\n\t\t{\n\t\t\treturn _name.Text;\n\t\t}\n\t\tswitch (battleType)\n\t\t{\n\t\t\tcase \"S\":\n\t\t\t{\n\t\t\t\tbattleFormat = PBEBattleFormat.Single;\n\t\t\t\tCreate1v1();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"D\":\n\t\t\t{\n\t\t\t\tbattleFormat = PBEBattleFormat.Double;\n\t\t\t\tif (_multi.IsChecked == true)\n\t\t\t\t{\n\t\t\t\t\tPBELegalPokemonCollection p0, p1, p2, p3;\n\t\t\t\t\tint numPerTrainer = settings.MaxPartySize / 2;\n\t\t\t\t\tp0 = PBEDDRandomTeamGenerator.CreateRandomTeam(numPerTrainer);\n\t\t\t\t\tp1 = PBEDDRandomTeamGenerator.CreateRandomTeam(numPerTrainer);\n\t\t\t\t\tp2 = PBEDDRandomTeamGenerator.CreateRandomTeam(numPerTrainer);\n\t\t\t\t\tp3 = PBEDDRandomTeamGenerator.CreateRandomTeam(numPerTrainer);\n\t\t\t\t\tt0 = new[] { new PBETrainerInfo(p0, GetName(), true), new PBETrainerInfo(p1, \"Barry\", false) };\n\t\t\t\t\tt1 = new[] { new PBETrainerInfo(p2, \"Leader Volkner\", false), new PBETrainerInfo(p3, \"Elite Four Flint\", false) };\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tCreate1v1();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"T\":\n\t\t\t{\n\t\t\t\tbattleFormat = PBEBattleFormat.Triple;\n\t\t\t\tif (_multi.IsChecked == true)\n\t\t\t\t{\n\t\t\t\t\tPBELegalPokemonCollection p0, p1, p2, p3, p4, p5;\n\t\t\t\t\tint numPerTrainer = settings.MaxPartySize / 3;\n\t\t\t\t\tp0 = PBEDDRandomTeamGenerator.CreateRandomTeam(numPerTrainer);\n\t\t\t\t\tp1 = PBEDDRandomTeamGenerator.CreateRandomTeam(numPerTrainer);\n\t\t\t\t\tp2 = PBEDDRandomTeamGenerator.CreateRandomTeam(numPerTrainer);\n\t\t\t\t\tp3 = PBEDDRandomTeamGenerator.CreateRandomTeam(numPerTrainer);\n\t\t\t\t\tp4 = PBEDDRandomTeamGenerator.CreateRandomTeam(numPerTrainer);\n\t\t\t\t\tp5 = PBEDDRandomTeamGenerator.CreateRandomTeam(numPerTrainer);\n\t\t\t\t\tt0 = new[] { new PBETrainerInfo(p0, GetName(), true), new PBETrainerInfo(p1, \"Barry\", false), new PBETrainerInfo(p2, \"Lucas\", false) };\n\t\t\t\t\tt1 = new[] { new PBETrainerInfo(p3, \"Champion Cynthia\", false), new PBETrainerInfo(p4, \"Leader Volkner\", false), new PBETrainerInfo(p5, \"Elite Four Flint\", false) };\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tCreate1v1();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(battleType));\n\t\t}\n\t\tvar b = PBEBattle.CreateTrainerBattle(battleFormat, settings, t0, t1,\n\t\t\t\t\tbattleTerrain: PBEDataProvider.GlobalRandom.RandomBattleTerrain());\n\t\tAdd(new SinglePlayerClient(b, $\"SP {_battles.Count + 1}\"));\n\t}\n\n\t// TODO: Removing battles (with disposing)\n\tprivate void Add(BattleClient client)\n\t{\n\t\t_battles.Add(client);\n\t\tvar pages = _tabs.Items.Cast<object>().ToList();\n\t\tvar tab = new TabItem\n\t\t{\n\t\t\tHeader = client.Name,\n\t\t\tContent = client.BattleView\n\t\t};\n\t\tpages.Add(tab);\n\t\t_tabs.Items = pages;\n\t\t_tabs.SelectedItem = tab;\n\t}\n\n\tinternal void HandleClosing()\n\t{\n\t\tforeach (BattleClient bc in _battles)\n\t\t{\n\t\t\tbc.Dispose();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineClient/Views/MessageView.xaml",
    "content": "﻿<UserControl xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             x:Class=\"Kermalis.PokemonBattleEngineClient.Views.MessageView\"\n             Width=\"384\" Height=\"576\">\n\t<ListBox Name=\"List\" Items=\"{Binding Messages}\" Background=\"White\" BorderThickness=\"0\">\n\t\t<ListBox.ItemTemplate>\n\t\t\t<DataTemplate>\n\t\t\t\t<Image Stretch=\"None\" Source=\"{Binding Bitmap}\" HorizontalAlignment=\"{Binding Alignment}\" RenderOptions.BitmapInterpolationMode=\"Default\" Height=\"{Binding Height}\">\n\t\t\t\t\t<Image.RenderTransform>\n\t\t\t\t\t\t<ScaleTransform ScaleX=\"{Binding Scale}\" ScaleY=\"{Binding Scale}\"/>\n\t\t\t\t\t</Image.RenderTransform>\n\t\t\t\t</Image>\n\t\t\t</DataTemplate>\n\t\t</ListBox.ItemTemplate>\n\t</ListBox>\n</UserControl>"
  },
  {
    "path": "PokemonBattleEngineClient/Views/MessageView.xaml.cs",
    "content": "﻿using Avalonia.Controls;\nusing Avalonia.Layout;\nusing Avalonia.Markup.Xaml;\nusing Avalonia.Media.Imaging;\nusing Avalonia.Threading;\nusing Kermalis.PokemonBattleEngineClient.Infrastructure;\nusing System.Collections.ObjectModel;\nusing System.Text.RegularExpressions;\n\nnamespace Kermalis.PokemonBattleEngineClient.Views;\n\npublic sealed class MessageView : UserControl\n{\n\tpublic sealed class Message\n\t{\n\t\tpublic Bitmap Bitmap { get; }\n\t\tpublic HorizontalAlignment Alignment { get; }\n\t\tpublic double Scale { get; }\n\t\tpublic double Height { get; }\n\n\t\tinternal Message(string message)\n\t\t{\n\t\t\tBitmap = StringRenderer.Render(message, \"MenuBlack\");\n\t\t\tMatch m = Regex.Match(message, @\"^Turn (\\d+)$\");\n\t\t\tif (m.Success)\n\t\t\t{\n\t\t\t\tAlignment = HorizontalAlignment.Center;\n\t\t\t\tScale = 2;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tAlignment = HorizontalAlignment.Left;\n\t\t\t\tScale = 1;\n\t\t\t}\n\t\t\tHeight = Bitmap.PixelSize.Height * Scale;\n\t\t}\n\t}\n\tpublic ObservableCollection<Message> Messages { get; } = new();\n\tprivate readonly ListBox _listBox;\n\n\tpublic MessageView()\n\t{\n\t\tDataContext = this;\n\t\tAvaloniaXamlLoader.Load(this);\n\n\t\t_listBox = this.FindControl<ListBox>(\"List\");\n\t}\n\n\tpublic void AddMessage(string message)\n\t{\n\t\tDispatcher.UIThread.InvokeAsync(() =>\n\t\t{\n\t\t\tvar m = new Message(message);\n\t\t\tMessages.Add(m);\n\t\t\t_listBox.ScrollIntoView(m);\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineClient/Views/PokemonView.xaml",
    "content": "﻿<UserControl xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:gif=\"clr-namespace:AvaloniaGif;assembly=AvaloniaGif\"\n             xmlns:infrastructure=\"clr-namespace:Kermalis.PokemonBattleEngineClient.Infrastructure;assembly=PokemonBattleEngineClient\"\n             x:Class=\"Kermalis.PokemonBattleEngineClient.Views.PokemonView\"\n             Width=\"192\" Height=\"192\" RenderOptions.BitmapInterpolationMode=\"Default\" IsVisible=\"False\">\n\t<gif:GifImage Name=\"Sprite\" Stretch=\"None\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Bottom\">\n\t\t<ToolTip.Tip>\n\t\t\t<Image Source=\"{Binding Description, Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuBlack, Mode=OneWay}\"/>\n\t\t</ToolTip.Tip>\n\t</gif:GifImage>\n</UserControl>"
  },
  {
    "path": "PokemonBattleEngineClient/Views/PokemonView.xaml.cs",
    "content": "﻿using Avalonia;\nusing Avalonia.Controls;\nusing Avalonia.Markup.Xaml;\nusing AvaloniaGif;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngineClient.Infrastructure;\nusing System.ComponentModel;\n\nnamespace Kermalis.PokemonBattleEngineClient.Views;\n\npublic sealed class PokemonView : UserControl, INotifyPropertyChanged\n{\n\tprivate void OnPropertyChanged(string property)\n\t{\n\t\tPropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));\n\t}\n\tpublic new event PropertyChangedEventHandler? PropertyChanged;\n\n\tprivate PBEBattlePokemon _pokemon;\n\tprivate Point _location;\n\tpublic Point Location\n\t{\n\t\tget => _location;\n\t\tinternal set\n\t\t{\n\t\t\tif (!_location.Equals(value))\n\t\t\t{\n\t\t\t\t_location = value;\n\t\t\t\tOnPropertyChanged(nameof(Location));\n\t\t\t}\n\t\t}\n\t}\n\tprivate bool _useKnownInfo;\n\tpublic string Description => Utils.CustomPokemonToString(_pokemon, _useKnownInfo);\n\n\tprivate readonly GifImage _sprite;\n\n\tpublic PokemonView()\n\t{\n\t\tDataContext = this;\n\t\tAvaloniaXamlLoader.Load(this);\n\n\t\t_sprite = this.FindControl<GifImage>(\"Sprite\");\n\t\t_pokemon = null!;\n\t}\n\n\tpublic void Update(PBEBattlePokemon pkmn, bool backSprite, bool useKnownInfo)\n\t{\n\t\t_useKnownInfo = useKnownInfo;\n\t\t_pokemon = pkmn;\n\n\t\tPBEStatus2 status2 = useKnownInfo ? _pokemon.KnownStatus2 : _pokemon.Status2;\n\t\tdouble opacity = 1;\n\t\tif (!status2.HasFlag(PBEStatus2.Substitute))\n\t\t{\n\t\t\tif (status2.HasFlag(PBEStatus2.Disguised))\n\t\t\t{\n\t\t\t\topacity *= 0.7;\n\t\t\t}\n\t\t\tif (status2.HasFlag(PBEStatus2.Airborne) || status2.HasFlag(PBEStatus2.ShadowForce) || status2.HasFlag(PBEStatus2.Underground) || status2.HasFlag(PBEStatus2.Underwater))\n\t\t\t{\n\t\t\t\topacity *= 0.4;\n\t\t\t}\n\t\t}\n\t\t_sprite.Opacity = opacity;\n\t\t_sprite.SourceUri = Utils.GetPokemonSpriteUri(_pokemon, backSprite);\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineClient/Views/TeamBuilderView.xaml",
    "content": "﻿<UserControl xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:gif=\"clr-namespace:AvaloniaGif;assembly=AvaloniaGif\"\n             xmlns:infrastructure=\"clr-namespace:Kermalis.PokemonBattleEngineClient.Infrastructure;assembly=PokemonBattleEngineClient\"\n             xmlns:pbedata=\"clr-namespace:Kermalis.PokemonBattleEngine.Data.Utils;assembly=PokemonBattleEngine\"\n             xmlns:views=\"clr-namespace:Kermalis.PokemonBattleEngineClient.Views;assembly=PokemonBattleEngineClient\"\n             x:Class=\"Kermalis.PokemonBattleEngineClient.Views.TeamBuilderView\"\n             Background=\"#313131\">\n\t<UserControl.Styles>\n\t\t<Style Selector=\"Button\">\n\t\t\t<Setter Property=\"Background\" Value=\"White\"/>\n\t\t\t<Setter Property=\"BorderBrush\" Value=\"#A2A2A2\"/>\n\t\t\t<Setter Property=\"BorderThickness\" Value=\"4\"/>\n\t\t</Style>\n\t\t<Style Selector=\"ComboBox\">\n\t\t\t<Setter Property=\"Background\" Value=\"White\"/>\n\t\t</Style>\n\t</UserControl.Styles>\n\t<UserControl.Resources>\n\t\t<DataTemplate x:Key=\"MenuBlack\">\n\t\t\t<Image Stretch=\"None\" Source=\"{Binding Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuBlack, Mode=OneWay}\"/>\n\t\t</DataTemplate>\n\t\t<DataTemplate x:Key=\"FormMenuBlack\">\n\t\t\t<Image Stretch=\"None\">\n\t\t\t\t<Image.Source>\n\t\t\t\t\t<MultiBinding Converter=\"{x:Static infrastructure:FormToTextBitmapConverter.Instance}\" ConverterParameter=\"MenuBlack\" Mode=\"OneWay\">\n\t\t\t\t\t\t<Binding Path=\"SelectedItem\" ElementName=\"Species\"/>\n\t\t\t\t\t\t<!--<Binding Path=\"/\"/> Crashes, read https://github.com/AvaloniaUI/Avalonia/issues/4067 -->\n\t\t\t\t\t</MultiBinding>\n\t\t\t\t</Image.Source>\n\t\t\t</Image>\n\t\t</DataTemplate>\n\t</UserControl.Resources>\n\t<StackPanel Orientation=\"Horizontal\" HorizontalAlignment=\"Center\" Spacing=\"5\">\n\t\t<StackPanel Orientation=\"Vertical\">\n\t\t\t<StackPanel Orientation=\"Horizontal\" Spacing=\"5\">\n\t\t\t\t<Grid>\n\t\t\t\t\t<Grid.RowDefinitions>\n\t\t\t\t\t\t<RowDefinition Height=\"Auto\"/>\n\t\t\t\t\t\t<RowDefinition Height=\"Auto\"/>\n\t\t\t\t\t\t<RowDefinition Height=\"Auto\"/>\n\t\t\t\t\t\t<RowDefinition Height=\"Auto\"/>\n\t\t\t\t\t\t<RowDefinition Height=\"Auto\"/>\n\t\t\t\t\t\t<RowDefinition Height=\"Auto\"/>\n\t\t\t\t\t\t<RowDefinition Height=\"Auto\"/>\n\t\t\t\t\t\t<RowDefinition Height=\"Auto\"/>\n\t\t\t\t\t\t<RowDefinition Height=\"Auto\"/>\n\t\t\t\t\t\t<RowDefinition Height=\"Auto\"/>\n\t\t\t\t\t\t<RowDefinition Height=\"Auto\"/>\n\t\t\t\t\t\t<RowDefinition Height=\"Auto\"/>\n\t\t\t\t\t</Grid.RowDefinitions>\n\t\t\t\t\t<Grid.ColumnDefinitions>\n\t\t\t\t\t\t<ColumnDefinition Width=\"Auto\"/>\n\t\t\t\t\t\t<ColumnDefinition Width=\"Auto\"/>\n\t\t\t\t\t</Grid.ColumnDefinitions>\n\t\t\t\t\t<CheckBox Grid.Row=\"0\" Grid.Column=\"0\" HorizontalAlignment=\"Left\" Command=\"{Binding UpdateSprites}\" IsChecked=\"{Binding Pkmn.Shiny}\" IsThreeState=\"False\">\n\t\t\t\t\t\t<Image Stretch=\"None\" Source=\"{Binding Source='Shiny', Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuWhite, Mode=OneWay}\"/>\n\t\t\t\t\t</CheckBox>\n\t\t\t\t\t<CheckBox Grid.Row=\"0\" Grid.Column=\"1\" HorizontalAlignment=\"Left\" IsChecked=\"{Binding Pkmn.Pokerus}\" IsThreeState=\"False\">\n\t\t\t\t\t\t<Image Stretch=\"None\" Source=\"{Binding Source='Pokerus', Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuWhite, Mode=OneWay}\"/>\n\t\t\t\t\t</CheckBox>\n\t\t\t\t\t<Image Grid.Row=\"1\" Grid.Column=\"0\" Stretch=\"None\" HorizontalAlignment=\"Left\" Source=\"{Binding Source='Species:', Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuWhite, Mode=OneWay}\"/>\n\t\t\t\t\t<ComboBox Grid.Row=\"1\" Grid.Column=\"1\" Name=\"Species\" Items=\"{x:Static pbedata:PBEDataUtils.AllSpecies}\">\n\t\t\t\t\t\t<ComboBox.ItemTemplate>\n\t\t\t\t\t\t\t<DataTemplate>\n\t\t\t\t\t\t\t\t<StackPanel Spacing=\"4\" Orientation=\"Horizontal\">\n\t\t\t\t\t\t\t\t\t<Image Stretch=\"None\" Source=\"{Binding Converter={x:Static infrastructure:SpeciesToMinispriteConverter.Instance}, Mode=OneWay}\"/>\n\t\t\t\t\t\t\t\t\t<Image Stretch=\"None\" Source=\"{Binding Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuBlack, Mode=OneWay}\"/>\n\t\t\t\t\t\t\t\t</StackPanel>\n\t\t\t\t\t\t\t</DataTemplate>\n\t\t\t\t\t\t</ComboBox.ItemTemplate>\n\t\t\t\t\t</ComboBox>\n\t\t\t\t\t<Image Grid.Row=\"2\" Grid.Column=\"0\" Stretch=\"None\" HorizontalAlignment=\"Left\" Source=\"{Binding Source='Form:', Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuWhite, Mode=OneWay}\" IsVisible=\"{Binding !!Shell.SelectableForms.Count}\"/>\n\t\t\t\t\t<ComboBox Grid.Row=\"2\" Grid.Column=\"1\" Name=\"Form\" Items=\"{Binding Pkmn.SelectableForms, Mode=OneWay}\" ItemTemplate=\"{StaticResource FormMenuBlack}\" IsVisible=\"{Binding !!Pkmn.SelectableForms.Count}\"/>\n\t\t\t\t\t<Image Grid.Row=\"3\" Grid.Column=\"0\" Stretch=\"None\" HorizontalAlignment=\"Left\" Source=\"{Binding Source='Nickname:', Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuWhite, Mode=OneWay}\"/>\n\t\t\t\t\t<TextBox Grid.Row=\"3\" Grid.Column=\"1\" Text=\"{Binding Pkmn.Nickname}\"/>\n\t\t\t\t\t<Image Grid.Row=\"4\" Grid.Column=\"0\" Stretch=\"None\" HorizontalAlignment=\"Left\" Source=\"{Binding Source='Level:', Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuWhite, Mode=OneWay}\"/>\n\t\t\t\t\t<NumericUpDown Grid.Row=\"4\" Grid.Column=\"1\" Minimum=\"{Binding Pkmn.Settings.MinLevel, Mode=OneWay}\" Maximum=\"{Binding Pkmn.Settings.MaxLevel, Mode=OneWay}\" ClipValueToMinMax=\"true\" Value=\"{Binding Pkmn.Level}\"/>\n\t\t\t\t\t<Image Grid.Row=\"5\" Grid.Column=\"0\" Stretch=\"None\" HorizontalAlignment=\"Left\" Source=\"{Binding Source='EXP:', Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuWhite, Mode=OneWay}\"/>\n\t\t\t\t\t<NumericUpDown Grid.Row=\"5\" Grid.Column=\"1\" Minimum=\"{Binding MinEXP, Mode=OneWay}\" Maximum=\"{Binding MaxEXP, Mode=OneWay}\" ClipValueToMinMax=\"true\" Value=\"{Binding Pkmn.EXP}\"/>\n\t\t\t\t\t<Image Grid.Row=\"6\" Grid.Column=\"0\" Stretch=\"None\" HorizontalAlignment=\"Left\" Source=\"{Binding Source='Friendship:', Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuWhite, Mode=OneWay}\"/>\n\t\t\t\t\t<NumericUpDown Grid.Row=\"6\" Grid.Column=\"1\" Minimum=\"0\" Maximum=\"255\" ClipValueToMinMax=\"true\" Value=\"{Binding Pkmn.Friendship}\"/>\n\t\t\t\t\t<Image Grid.Row=\"7\" Grid.Column=\"0\" Stretch=\"None\" HorizontalAlignment=\"Left\" Source=\"{Binding Source='Ability:', Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuWhite, Mode=OneWay}\"/>\n\t\t\t\t\t<ComboBox Grid.Row=\"7\" Grid.Column=\"1\" Name=\"Ability\" Items=\"{Binding Pkmn.SelectableAbilities, Mode=OneWay}\" ItemTemplate=\"{StaticResource MenuBlack}\"/>\n\t\t\t\t\t<Image Grid.Row=\"8\" Grid.Column=\"0\" Stretch=\"None\" HorizontalAlignment=\"Left\" Source=\"{Binding Source='Nature:', Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuWhite, Mode=OneWay}\"/>\n\t\t\t\t\t<ComboBox Grid.Row=\"8\" Grid.Column=\"1\" Items=\"{x:Static pbedata:PBEDataUtils.AllNatures}\" ItemTemplate=\"{StaticResource MenuBlack}\" SelectedItem=\"{Binding Pkmn.Nature}\"/>\n\t\t\t\t\t<Image Grid.Row=\"9\" Grid.Column=\"0\" Stretch=\"None\" HorizontalAlignment=\"Left\" Source=\"{Binding Source='Gender:', Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuWhite, Mode=OneWay}\"/>\n\t\t\t\t\t<ComboBox Grid.Row=\"9\" Grid.Column=\"1\" Name=\"Gender\" Items=\"{Binding Pkmn.SelectableGenders, Mode=OneWay}\" ItemTemplate=\"{StaticResource MenuBlack}\"/>\n\t\t\t\t\t<Image Grid.Row=\"10\" Grid.Column=\"0\" Stretch=\"None\" HorizontalAlignment=\"Left\" Source=\"{Binding Source='Item:', Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuWhite, Mode=OneWay}\"/>\n\t\t\t\t\t<ComboBox Grid.Row=\"10\" Grid.Column=\"1\" Name=\"Item\" Items=\"{Binding Pkmn.SelectableItems, Mode=OneWay}\" ItemTemplate=\"{StaticResource MenuBlack}\"/>\n\t\t\t\t\t<Image Grid.Row=\"11\" Grid.Column=\"0\" Stretch=\"None\" HorizontalAlignment=\"Left\" Source=\"{Binding Source='Ball:', Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuWhite, Mode=OneWay}\"/>\n\t\t\t\t\t<ComboBox Grid.Row=\"11\" Grid.Column=\"1\" Items=\"{x:Static pbedata:PBEDataUtils.AllBalls}\" ItemTemplate=\"{StaticResource MenuBlack}\" SelectedItem=\"{Binding Pkmn.CaughtBall}\"/>\n\t\t\t\t</Grid>\n\t\t\t\t<Panel Width=\"192\" Height=\"192\">\n\t\t\t\t\t<gif:GifImage Stretch=\"None\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\" RenderOptions.BitmapInterpolationMode=\"Default\" SourceUri=\"{Binding SpriteUri}\">\n\t\t\t\t\t\t<gif:GifImage.RenderTransform>\n\t\t\t\t\t\t\t<ScaleTransform ScaleX=\"2\" ScaleY=\"2\"/>\n\t\t\t\t\t\t</gif:GifImage.RenderTransform>\n\t\t\t\t\t</gif:GifImage>\n\t\t\t\t</Panel>\n\t\t\t</StackPanel>\n\t\t\t<Grid>\n\t\t\t\t<Grid.RowDefinitions>\n\t\t\t\t\t<RowDefinition Height=\"Auto\"/>\n\t\t\t\t\t<RowDefinition Height=\"Auto\"/>\n\t\t\t\t\t<RowDefinition Height=\"Auto\"/>\n\t\t\t\t</Grid.RowDefinitions>\n\t\t\t\t<Grid.ColumnDefinitions>\n\t\t\t\t\t<ColumnDefinition Width=\"Auto\"/>\n\t\t\t\t\t<ColumnDefinition Width=\"Auto\"/>\n\t\t\t\t\t<ColumnDefinition Width=\"Auto\"/>\n\t\t\t\t</Grid.ColumnDefinitions>\n\t\t\t\t<Image Grid.Row=\"0\" Grid.Column=\"0\" Stretch=\"None\" HorizontalAlignment=\"Left\" Source=\"{Binding Source='EVs:', Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuWhite, Mode=OneWay}\"/>\n\t\t\t\t<ItemsControl Grid.Row=\"0\" Grid.Column=\"1\" Items=\"{Binding Pkmn.EffortValues, Mode=OneWay}\">\n\t\t\t\t\t<ItemsControl.ItemsPanel>\n\t\t\t\t\t\t<ItemsPanelTemplate>\n\t\t\t\t\t\t\t<WrapPanel />\n\t\t\t\t\t\t</ItemsPanelTemplate>\n\t\t\t\t\t</ItemsControl.ItemsPanel>\n\t\t\t\t\t<ItemsControl.ItemTemplate>\n\t\t\t\t\t\t<DataTemplate>\n\t\t\t\t\t\t\t<StackPanel Orientation=\"Vertical\">\n\t\t\t\t\t\t\t\t<Image Stretch=\"None\" Source=\"{Binding Stat, Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuWhite, Mode=OneWay}\"/>\n\t\t\t\t\t\t\t\t<NumericUpDown Minimum=\"0\" Maximum=\"255\" ClipValueToMinMax=\"True\" Value=\"{Binding Value}\"/>\n\t\t\t\t\t\t\t</StackPanel>\n\t\t\t\t\t\t</DataTemplate>\n\t\t\t\t\t</ItemsControl.ItemTemplate>\n\t\t\t\t</ItemsControl>\n\t\t\t\t<StackPanel Grid.Row=\"0\" Grid.Column=\"2\" Orientation=\"Horizontal\">\n\t\t\t\t\t<Image Stretch=\"None\" HorizontalAlignment=\"Left\" Source=\"{Binding Pkmn.EffortValues.StatTotal, Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuWhite, Mode=OneWay}\"/>\n\t\t\t\t\t<Image Stretch=\"None\" HorizontalAlignment=\"Left\" Source=\"{Binding Source='/', Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuWhite, Mode=OneWay}\"/>\n\t\t\t\t\t<Image Stretch=\"None\" HorizontalAlignment=\"Left\" Source=\"{Binding Pkmn.Settings.MaxTotalEVs, Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuWhite, Mode=OneWay}\"/>\n\t\t\t\t</StackPanel>\n\t\t\t\t<Image Grid.Row=\"1\" Grid.Column=\"0\" Stretch=\"None\" HorizontalAlignment=\"Left\" Source=\"{Binding Source='IVs:', Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuWhite, Mode=OneWay}\"/>\n\t\t\t\t<ItemsControl Grid.Row=\"1\" Grid.Column=\"1\" Items=\"{Binding Pkmn.IndividualValues, Mode=OneWay}\">\n\t\t\t\t\t<ItemsControl.ItemsPanel>\n\t\t\t\t\t\t<ItemsPanelTemplate>\n\t\t\t\t\t\t\t<WrapPanel />\n\t\t\t\t\t\t</ItemsPanelTemplate>\n\t\t\t\t\t</ItemsControl.ItemsPanel>\n\t\t\t\t\t<ItemsControl.ItemTemplate>\n\t\t\t\t\t\t<DataTemplate>\n\t\t\t\t\t\t\t<NumericUpDown Minimum=\"0\" Maximum=\"{Binding RelativeSource={RelativeSource AncestorType=views:TeamBuilderView}, Path=Pkmn.Settings.MaxIVs, Mode=OneWay}\" ClipValueToMinMax=\"True\" Value=\"{Binding Value}\"/>\n\t\t\t\t\t\t</DataTemplate>\n\t\t\t\t\t</ItemsControl.ItemTemplate>\n\t\t\t\t</ItemsControl>\n\t\t\t\t<StackPanel Grid.Row=\"1\" Grid.Column=\"2\" Orientation=\"Horizontal\">\n\t\t\t\t\t<Image Stretch=\"None\" HorizontalAlignment=\"Left\" Source=\"{Binding Pkmn.IndividualValues.HiddenPowerType, Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuWhite, Mode=OneWay}\"/>\n\t\t\t\t\t<Image Stretch=\"None\" HorizontalAlignment=\"Left\" Source=\"{Binding Source='|', Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuWhite, Mode=OneWay}\"/>\n\t\t\t\t\t<Image Stretch=\"None\" HorizontalAlignment=\"Left\" Source=\"{Binding Pkmn.IndividualValues.HiddenPowerBasePower, Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuWhite, Mode=OneWay}\"/>\n\t\t\t\t</StackPanel>\n\t\t\t\t<Image Grid.Row=\"2\" Grid.Column=\"0\" Stretch=\"None\" HorizontalAlignment=\"Left\" Source=\"{Binding Source='Moves:&#x0a;&#x0a;PP-Ups:', Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuWhite, Mode=OneWay}\"/>\n\t\t\t\t<ItemsControl Grid.Row=\"2\" Grid.Column=\"1\" Items=\"{Binding Pkmn.Moveset, Mode=OneWay}\">\n\t\t\t\t\t<ItemsControl.ItemsPanel>\n\t\t\t\t\t\t<ItemsPanelTemplate>\n\t\t\t\t\t\t\t<WrapPanel />\n\t\t\t\t\t\t</ItemsPanelTemplate>\n\t\t\t\t\t</ItemsControl.ItemsPanel>\n\t\t\t\t\t<ItemsControl.ItemTemplate>\n\t\t\t\t\t\t<DataTemplate>\n\t\t\t\t\t\t\t<StackPanel Orientation=\"Vertical\">\n\t\t\t\t\t\t\t\t<ComboBox Items=\"{Binding Allowed, Mode=OneWay}\" ItemTemplate=\"{StaticResource MenuBlack}\" SelectedItem=\"{Binding Move}\" IsEnabled=\"{Binding IsMoveEditable, Mode=OneWay}\"/>\n\t\t\t\t\t\t\t\t<NumericUpDown Minimum=\"0\" Maximum=\"{Binding RelativeSource={RelativeSource AncestorType=views:TeamBuilderView}, Path=Pkmn.Settings.MaxPPUps, Mode=OneWay}\" ClipValueToMinMax=\"True\" IsEnabled=\"{Binding IsPPUpsEditable, Mode=OneWay}\" Value=\"{Binding PPUps}\"/>\n\t\t\t\t\t\t\t</StackPanel>\n\t\t\t\t\t\t</DataTemplate>\n\t\t\t\t\t</ItemsControl.ItemTemplate>\n\t\t\t\t</ItemsControl>\n\t\t\t</Grid>\n\t\t</StackPanel>\n\t\t<StackPanel Orientation=\"Vertical\">\n\t\t\t<StackPanel Orientation=\"Horizontal\">\n\t\t\t\t<Button Name=\"AddParty\" Command=\"{Binding AddPartyMember}\">\n\t\t\t\t\t<Image Stretch=\"None\" Source=\"{Binding Source='Add', Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuBlack, Mode=OneWay}\"/>\n\t\t\t\t</Button>\n\t\t\t\t<Button Name=\"RemoveParty\" Command=\"{Binding RemovePartyMember}\">\n\t\t\t\t\t<Image Stretch=\"None\" Source=\"{Binding Source='Remove', Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuBlack, Mode=OneWay}\"/>\n\t\t\t\t</Button>\n\t\t\t</StackPanel>\n\t\t\t<ListBox Name=\"Party\" SelectionMode=\"AlwaysSelected\">\n\t\t\t\t<ListBox.ItemTemplate>\n\t\t\t\t\t<DataTemplate>\n\t\t\t\t\t\t<StackPanel Spacing=\"4\" Orientation=\"Horizontal\">\n\t\t\t\t\t\t\t<Image Stretch=\"None\" Source=\"{Binding Converter={x:Static infrastructure:SpeciesToMinispriteConverter.Instance}, Mode=OneWay}\"/>\n\t\t\t\t\t\t\t<Image Stretch=\"None\" Source=\"{Binding Nickname, Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuBlack, Mode=OneWay}\"/>\n\t\t\t\t\t\t</StackPanel>\n\t\t\t\t\t</DataTemplate>\n\t\t\t\t</ListBox.ItemTemplate>\n\t\t\t</ListBox>\n\t\t</StackPanel>\n\t\t<StackPanel Orientation=\"Vertical\">\n\t\t\t<StackPanel Orientation=\"Horizontal\">\n\t\t\t\t<Button Command=\"{Binding AddTeam}\">\n\t\t\t\t\t<Image Stretch=\"None\" Source=\"{Binding Source='Add', Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuBlack, Mode=OneWay}\"/>\n\t\t\t\t</Button>\n\t\t\t\t<Button Command=\"{Binding RemoveTeam}\">\n\t\t\t\t\t<Image Stretch=\"None\" Source=\"{Binding Source='Remove', Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuBlack, Mode=OneWay}\"/>\n\t\t\t\t</Button>\n\t\t\t</StackPanel>\n\t\t\t<Button Command=\"{Binding SaveTeam}\">\n\t\t\t\t<Image Stretch=\"None\" Source=\"{Binding Source='Save', Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuBlack, Mode=OneWay}\"/>\n\t\t\t</Button>\n\t\t\t<ListBox Name=\"SavedTeams\" Items=\"{Binding Teams, Mode=OneWay}\" SelectionMode=\"AlwaysSelected\">\n\t\t\t\t<ListBox.ItemTemplate>\n\t\t\t\t\t<DataTemplate>\n\t\t\t\t\t\t<Image Stretch=\"None\" Source=\"{Binding Name, Converter={x:Static infrastructure:ObjectToTextBitmapConverter.Instance}, ConverterParameter=MenuBlack, Mode=OneWay}\"/>\n\t\t\t\t\t</DataTemplate>\n\t\t\t\t</ListBox.ItemTemplate>\n\t\t\t</ListBox>\n\t\t</StackPanel>\n\t</StackPanel>\n</UserControl>"
  },
  {
    "path": "PokemonBattleEngineClient/Views/TeamBuilderView.xaml.cs",
    "content": "﻿using Avalonia.Controls;\nusing Avalonia.Markup.Xaml;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Data.Legality;\nusing Kermalis.PokemonBattleEngineClient.Infrastructure;\nusing Kermalis.PokemonBattleEngineClient.Models;\nusing System;\nusing System.Collections.ObjectModel;\nusing System.Collections.Specialized;\nusing System.ComponentModel;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngineClient.Views;\n\npublic sealed class TeamBuilderView : UserControl, INotifyPropertyChanged\n{\n\tprivate void OnPropertyChanged(string property)\n\t{\n\t\tPropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));\n\t}\n\tpublic new event PropertyChangedEventHandler? PropertyChanged;\n\n\tprivate Uri _spriteUri;\n\tpublic Uri SpriteUri\n\t{\n\t\tget => _spriteUri;\n\t\tprivate set\n\t\t{\n\t\t\tif (_spriteUri != value)\n\t\t\t{\n\t\t\t\t_spriteUri = value;\n\t\t\t\tOnPropertyChanged(nameof(SpriteUri));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate PBELegalPokemon _pkmn;\n\tpublic PBELegalPokemon Pkmn\n\t{\n\t\tget => _pkmn;\n\t\tset\n\t\t{\n\t\t\tif (_pkmn != value)\n\t\t\t{\n\t\t\t\tPBELegalPokemon old = _pkmn;\n\t\t\t\tif (old is not null)\n\t\t\t\t{\n\t\t\t\t\told.PropertyChanged -= OnPkmnPropertyChanged;\n\t\t\t\t}\n\t\t\t\t_pkmn = value;\n\t\t\t\tvalue.PropertyChanged += OnPkmnPropertyChanged;\n\t\t\t\t_ignoreComboBoxChanges = true;\n\t\t\t\tOnPropertyChanged(nameof(Pkmn));\n\t\t\t\t_partyListBox.SelectedItem = value;\n\t\t\t\tUpdateEXPRequirements();\n\t\t\t\tUpdateComboBoxes(null);\n\t\t\t\t_ignoreComboBoxChanges = false;\n\t\t\t}\n\t\t}\n\t}\n\tprivate uint _minEXP;\n\tpublic uint MinEXP\n\t{\n\t\tget => _minEXP;\n\t\tset\n\t\t{\n\t\t\tif (_minEXP != value)\n\t\t\t{\n\t\t\t\t_minEXP = value;\n\t\t\t\tOnPropertyChanged(nameof(MinEXP));\n\t\t\t}\n\t\t}\n\t}\n\tprivate uint _maxEXP;\n\tpublic uint MaxEXP\n\t{\n\t\tget => _maxEXP;\n\t\tset\n\t\t{\n\t\t\tif (_maxEXP != value)\n\t\t\t{\n\t\t\t\t_maxEXP = value;\n\t\t\t\tOnPropertyChanged(nameof(MaxEXP));\n\t\t\t}\n\t\t}\n\t}\n\tprivate TeamInfo _team;\n\tpublic TeamInfo Team\n\t{\n\t\tget => _team;\n\t\tset\n\t\t{\n\t\t\tif (_team != value)\n\t\t\t{\n\t\t\t\t_teamListBox.SelectedItem = value;\n\t\t\t\t_team = value;\n\t\t\t\tOnPropertyChanged(nameof(Team));\n\t\t\t}\n\t\t}\n\t}\n\tpublic ObservableCollection<TeamInfo> Teams { get; } = new();\n\n\tprivate readonly string _teamPath;\n\tprivate readonly Button _addPartyButton;\n\tprivate readonly Button _removePartyButton;\n\t// Avalonia selection is broken (as always) so I need to manually get the SelectedItem instead of using the bindings :))))))))))))))\n\tprivate readonly ListBox _partyListBox;\n\tprivate readonly ListBox _teamListBox;\n\tprivate bool _ignoreComboBoxChanges = false;\n\tprivate readonly ComboBox _abilityComboBox;\n\tprivate readonly ComboBox _formComboBox;\n\tprivate readonly ComboBox _genderComboBox;\n\tprivate readonly ComboBox _itemComboBox;\n\tprivate readonly ComboBox _speciesComboBox;\n\n\tprivate void UpdateEXPRequirements()\n\t{\n\t\tPBEGrowthRate type = PBEDataProvider.Instance.GetPokemonData(_pkmn).GrowthRate;\n\t\tMinEXP = PBEDataProvider.Instance.GetEXPRequired(type, _pkmn.Settings.MinLevel);\n\t\tMaxEXP = PBEDataProvider.Instance.GetEXPRequired(type, _pkmn.Settings.MaxLevel);\n\t}\n\tprivate void UpdateComboBoxes(string? property)\n\t{\n\t\tbool all = property is null;\n\t\tbool ability = all;\n\t\tbool form = all;\n\t\tbool gender = all;\n\t\tbool item = all;\n\t\tbool species = all;\n\t\tif (!all)\n\t\t{\n\t\t\tswitch (property)\n\t\t\t{\n\t\t\t\tcase nameof(PBELegalPokemon.Ability): ability = true; break;\n\t\t\t\tcase nameof(PBELegalPokemon.Form): form = true; break;\n\t\t\t\tcase nameof(PBELegalPokemon.Gender): gender = true; break;\n\t\t\t\tcase nameof(PBELegalPokemon.Item): item = true; break;\n\t\t\t\tcase nameof(PBELegalPokemon.Species): species = true; break;\n\t\t\t}\n\t\t}\n\t\tif (ability)\n\t\t{\n\t\t\t_abilityComboBox.SelectedItem = _pkmn.Ability;\n\t\t}\n\t\tif (form)\n\t\t{\n\t\t\t_formComboBox.SelectedItem = _pkmn.Form;\n\t\t}\n\t\tif (gender)\n\t\t{\n\t\t\t_genderComboBox.SelectedItem = _pkmn.Gender;\n\t\t}\n\t\tif (item)\n\t\t{\n\t\t\t_itemComboBox.SelectedItem = _pkmn.Item;\n\t\t}\n\t\tif (species)\n\t\t{\n\t\t\t_speciesComboBox.SelectedItem = _pkmn.Species;\n\t\t}\n\t}\n\tprivate void OnPkmnPropertyChanged(object? sender, PropertyChangedEventArgs e)\n\t{\n\t\tUpdateComboBoxes(e.PropertyName);\n\t\tif (e.PropertyName == nameof(PBELegalPokemon.Species) || e.PropertyName == nameof(PBELegalPokemon.Form))\n\t\t{\n\t\t\tUpdateEXPRequirements();\n\t\t}\n\t}\n\tprivate void OnComboBoxSelectionChanged(object? sender, SelectionChangedEventArgs thing)\n\t{\n\t\tif (_ignoreComboBoxChanges)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\t_ignoreComboBoxChanges = true;\n\t\tvar c = (ComboBox)sender!;\n\t\tif (c == _abilityComboBox)\n\t\t{\n\t\t\t_pkmn.Ability = (PBEAbility)c.SelectedItem!;\n\t\t}\n\t\telse if (c == _formComboBox)\n\t\t{\n\t\t\t_pkmn.Form = (PBEForm)c.SelectedItem!;\n\t\t}\n\t\telse if (c == _genderComboBox)\n\t\t{\n\t\t\t_pkmn.Gender = (PBEGender)c.SelectedItem!;\n\t\t}\n\t\telse if (c == _itemComboBox)\n\t\t{\n\t\t\t_pkmn.Item = (PBEItem)c.SelectedItem!;\n\t\t}\n\t\telse if (c == _speciesComboBox)\n\t\t{\n\t\t\t_pkmn.Species = (PBESpecies)c.SelectedItem!;\n\t\t}\n\t\t_ignoreComboBoxChanges = false;\n\t}\n\n#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.\n\tpublic TeamBuilderView()\n#pragma warning restore CS8618 // _team, _pkmn, _spriteUri\n\t{\n\t\tDataContext = this;\n\t\tAvaloniaXamlLoader.Load(this);\n\n\t\t_abilityComboBox = this.FindControl<ComboBox>(\"Ability\");\n\t\t_abilityComboBox.SelectionChanged += OnComboBoxSelectionChanged;\n\t\t_formComboBox = this.FindControl<ComboBox>(\"Form\");\n\t\t_formComboBox.SelectionChanged += OnComboBoxSelectionChanged;\n\t\t_formComboBox.SelectionChanged += OnVisualChanged;\n\t\t_genderComboBox = this.FindControl<ComboBox>(\"Gender\");\n\t\t_genderComboBox.SelectionChanged += OnComboBoxSelectionChanged;\n\t\t_genderComboBox.SelectionChanged += OnVisualChanged;\n\t\t_itemComboBox = this.FindControl<ComboBox>(\"Item\");\n\t\t_itemComboBox.SelectionChanged += OnComboBoxSelectionChanged;\n\t\t_speciesComboBox = this.FindControl<ComboBox>(\"Species\");\n\t\t_speciesComboBox.SelectionChanged += OnComboBoxSelectionChanged;\n\t\t_speciesComboBox.SelectionChanged += OnVisualChanged;\n\t\t_teamListBox = this.FindControl<ListBox>(\"SavedTeams\");\n\t\t_teamListBox.SelectionChanged += OnSelectedTeamChanged;\n\t\t_addPartyButton = this.FindControl<Button>(\"AddParty\");\n\t\t_removePartyButton = this.FindControl<Button>(\"RemoveParty\");\n\t\t_partyListBox = this.FindControl<ListBox>(\"Party\");\n\t\t_partyListBox.SelectionChanged += OnSelectedMonChanged;\n\n\t\t_teamPath = Path.Combine(Utils.WorkingDirectory, \"Teams\");\n\t\tif (Directory.Exists(_teamPath))\n\t\t{\n\t\t\tstring[] files = Directory.GetFiles(_teamPath);\n\t\t\tif (files.Length > 0)\n\t\t\t{\n\t\t\t\tfor (int i = 0; i < files.Length; i++)\n\t\t\t\t{\n\t\t\t\t\tstring file = files[i];\n\t\t\t\t\tvar t = new TeamInfo(Path.GetFileNameWithoutExtension(file), new PBELegalPokemonCollection(file));\n\t\t\t\t\tTeams.Add(t);\n\t\t\t\t\tif (i == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tTeam = t;\n\t\t\t\t\t\tPkmn = t.Party[0];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tDirectory.CreateDirectory(_teamPath);\n\t\t}\n\t\tAddTeam();\n\t}\n\n\tpublic void AddTeam()\n\t{\n\t\tvar t = new TeamInfo($\"Team {DateTime.Now.Ticks}\", new PBELegalPokemonCollection(PBESettings.DefaultSettings, 1, true));\n\t\tTeams.Add(t);\n\t\tTeam = t;\n\t\tPkmn = t.Party[0];\n\t}\n\tpublic void RemoveTeam()\n\t{\n\t\tFile.Delete(Path.Combine(_teamPath, $\"{_team.Name}.json\"));\n\t\tTeamInfo old = _team;\n\t\tif (Teams.Count == 1)\n\t\t{\n\t\t\tAddTeam();\n\t\t}\n\t\tTeams.Remove(old);\n\t}\n\tpublic void SaveTeam()\n\t{\n\t\t_team.Party.ToJsonFile(Path.Combine(_teamPath, $\"{_team.Name}.json\"));\n\t}\n\t// I love Avalonia :))))))))))\n\t// Using it for years and still has the same problems\n\tpublic void AddPartyMember()\n\t{\n\t\tint index = _team.Party.Count;\n\t\t_team.Party.AddRandom(true);\n\t\t_partyListBox.Items = null; // How is it so broken still\n\t\t_partyListBox.Items = _team.Party;\n\t\tPkmn = _team.Party[index];\n\t}\n\tpublic void RemovePartyMember()\n\t{\n\t\t_team.Party.Remove(_pkmn);\n\t\t_partyListBox.Items = null;\n\t\t_partyListBox.Items = _team.Party;\n\t\tPkmn = _team.Party[_team.Party.Count - 1];\n\t}\n\tprivate void OnSelectedMonChanged(object? sender, SelectionChangedEventArgs e)\n\t{\n\t\tvar s = (PBELegalPokemon?)_partyListBox.SelectedItem;\n\t\tif (s is not null)\n\t\t{\n\t\t\tPkmn = s;\n\t\t}\n\t}\n\tprivate void OnSelectedTeamSizeChanged(object? sender, NotifyCollectionChangedEventArgs? e)\n\t{\n\t\t_addPartyButton.IsEnabled = _team.Party.Count < _team.Party.Settings.MaxPartySize;\n\t\t_removePartyButton.IsEnabled = _team.Party.Count > 1;\n\t}\n\tprivate void OnSelectedTeamChanged(object? sender, SelectionChangedEventArgs e)\n\t{\n\t\tif (_team is not null)\n\t\t{\n\t\t\t_team.Party.CollectionChanged -= OnSelectedTeamSizeChanged;\n\t\t}\n\t\t_team = (TeamInfo)_teamListBox.SelectedItem!;\n\t\t_team.Party.CollectionChanged += OnSelectedTeamSizeChanged;\n\t\t_partyListBox.Items = _team.Party;\n\t\tOnSelectedTeamSizeChanged(null, null);\n\t\tPkmn = _team.Party[0];\n\t}\n\tprivate void OnVisualChanged(object? sender, SelectionChangedEventArgs e)\n\t{\n\t\tUpdateSprites();\n\t}\n\tpublic void UpdateSprites()\n\t{\n\t\tSpriteUri = Utils.GetPokemonSpriteUri(_pkmn);\n\t\t// Force redraw of minisprite\n\t\tIControl c = _partyListBox.ItemContainerGenerator.ContainerFromIndex(_partyListBox.SelectedIndex);\n\t\tif (c is ListBoxItem item)\n\t\t{\n\t\t\tobject old = item.Content;\n\t\t\titem.Content = null;\n\t\t\titem.Content = old;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineClient.Android/Assets/AboutAssets.txt",
    "content": "Any raw assets you want to be deployed with your application can be placed in\nthis directory (and child directories) and given a Build Action of \"AndroidAsset\".\n\nThese files will be deployed with you package and will be accessible using Android's\nAssetManager, like this:\n\npublic class ReadAsset : Activity\n{\n\tprotected override void OnCreate (Bundle bundle)\n\t{\n\t\tbase.OnCreate (bundle);\n\n\t\tInputStream input = Assets.Open (\"my_asset.txt\");\n\t}\n}\n\nAdditionally, some Android functions will automatically load asset files:\n\nTypeface tf = Typeface.CreateFromAsset (Context.Assets, \"fonts/samplefont.ttf\");"
  },
  {
    "path": "PokemonBattleEngineClient.Android/MainActivity.cs",
    "content": "﻿using Android.App;\nusing Android.Content.PM;\nusing Android.OS;\nusing Avalonia;\nusing Avalonia.Android;\nusing Kermalis.PokemonBattleEngineClient.Infrastructure;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngineClient.Android\n{\n    [Activity(Label = \"Pokémon Battle Engine\", Icon = \"@drawable/icon\", MainLauncher = true, LaunchMode = LaunchMode.SingleInstance)]\n    public sealed class MainActivity : AvaloniaActivity\n    {\n        protected override void OnCreate(Bundle savedInstanceState)\n        {\n            if (Avalonia.Application.Current == null)\n            {\n                CopyDatabase();\n                AppBuilder.Configure(new App())\n                    .UseAndroid()\n                    .SetupWithoutStarting();\n                Content = new MainView();\n            }\n            base.OnCreate(savedInstanceState);\n        }\n\n        private void CopyDatabase()\n        {\n            string path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);\n            if (!Directory.Exists(path))\n            {\n                Directory.CreateDirectory(path);\n            }\n            const string dbName = \"PokemonBattleEngine.db\";\n            using (var br = new BinaryReader(Utils.GetResourceStream(dbName)))\n            {\n                using (var bw = new BinaryWriter(new FileStream(Path.Combine(path, dbName), FileMode.Create)))\n                {\n                    byte[] buffer = new byte[0x800];\n                    int length = 0;\n                    while ((length = br.Read(buffer, 0, buffer.Length)) > 0)\n                    {\n                        bw.Write(buffer, 0, length);\n                    }\n                }\n            }\n            Utils.SetWorkingDirectory(path);\n        }\n    }\n}\n\n"
  },
  {
    "path": "PokemonBattleEngineClient.Android/PokemonBattleEngineClient.Android.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProductVersion>8.0.30703</ProductVersion>\n    <SchemaVersion>2.0</SchemaVersion>\n    <ProjectGuid>{29132311-1848-4FD6-AE0C-4FF841151BD3}</ProjectGuid>\n    <ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>\n    <OutputType>Library</OutputType>\n    <AppDesignerFolder>Properties</AppDesignerFolder>\n    <RootNamespace>PokemonBattleEngineClient.Android</RootNamespace>\n    <AssemblyName>PokemonBattleEngineClient.Android</AssemblyName>\n    <FileAlignment>512</FileAlignment>\n    <AndroidApplication>true</AndroidApplication>\n    <AndroidResgenFile>Resources\\Resource.Designer.cs</AndroidResgenFile>\n    <GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>\n    <AndroidUseLatestPlatformSdk>False</AndroidUseLatestPlatformSdk>\n    <TargetFrameworkVersion>v9.0</TargetFrameworkVersion>\n    <AndroidManifest>Properties\\AndroidManifest.xml</AndroidManifest>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DebugSymbols>True</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>bin\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <AndroidUseSharedRuntime>True</AndroidUseSharedRuntime>\n    <AndroidLinkMode>None</AndroidLinkMode>\n    <EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>\n    <BundleAssemblies>false</BundleAssemblies>\n    <AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi>\n    <AndroidSupportedAbis>armeabi-v7a;x86</AndroidSupportedAbis>\n    <Debugger>Xamarin</Debugger>\n    <AndroidEnableMultiDex>False</AndroidEnableMultiDex>\n    <AotAssemblies>false</AotAssemblies>\n    <EnableLLVM>false</EnableLLVM>\n    <EnableProguard>False</EnableProguard>\n    <WarningsAsErrors>NU1605</WarningsAsErrors>\n    <AndroidEnableProfiledAot>false</AndroidEnableProfiledAot>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>bin\\Release\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>\n    <AndroidLinkMode>Full</AndroidLinkMode>\n    <EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>\n    <BundleAssemblies>False</BundleAssemblies>\n    <AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi>\n    <AndroidSupportedAbis>armeabi-v7a,x86</AndroidSupportedAbis>\n    <Debugger>Xamarin</Debugger>\n    <AotAssemblies>False</AotAssemblies>\n    <EnableLLVM>False</EnableLLVM>\n    <AndroidEnableMultiDex>False</AndroidEnableMultiDex>\n    <EnableProguard>False</EnableProguard>\n    <DebugSymbols>False</DebugSymbols>\n    <WarningsAsErrors>NU1605</WarningsAsErrors>\n  </PropertyGroup>\n  <ItemGroup>\n    <Compile Include=\"MainActivity.cs\" />\n    <Compile Include=\"Resources\\Resource.Designer.cs\" />\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"Resources\\AboutResources.txt\" />\n    <None Include=\"Assets\\AboutAssets.txt\" />\n  </ItemGroup>\n  <ItemGroup>\n    <AndroidResource Include=\"Resources\\layout\\Main.axml\">\n      <SubType>Designer</SubType>\n    </AndroidResource>\n  </ItemGroup>\n  <ItemGroup>\n    <AndroidResource Include=\"Resources\\values\\Strings.xml\" />\n  </ItemGroup>\n  <ItemGroup>\n    <AndroidResource Include=\"Resources\\drawable\\Icon.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"Properties\\AndroidManifest.xml\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\PokemonBattleEngineClient\\PokemonBattleEngineClient.csproj\">\n      <Project>{d0a739b9-3c68-4ba6-a328-41606954b6bd}</Project>\n      <Name>PokemonBattleEngineClient</Name>\n    </ProjectReference>\n  </ItemGroup>\n  <ItemGroup>\n    <Reference Include=\"Mono.Android\" />\n    <Reference Include=\"mscorlib\" />\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Xml.Linq\" />\n    <Reference Include=\"System.Xml\" />\n    <PackageReference Include=\"Avalonia.Android\">\n      <Version>0.9.1</Version>\n    </PackageReference>\n    <PackageReference Include=\"System.Reactive\" Version=\"4.1.6\" />\n    <PackageReference Include=\"System.Runtime.CompilerServices.Unsafe\" Version=\"4.5.2\" />\n    <PackageReference Include=\"System.Buffers\" Version=\"4.5.0\" />\n  </ItemGroup>\n  <Import Project=\"$(MSBuildExtensionsPath)\\Xamarin\\Android\\Xamarin.Android.CSharp.targets\" />\n</Project>"
  },
  {
    "path": "PokemonBattleEngineClient.Android/Properties/AndroidManifest.xml",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" package=\"com.Kermalis.PokemonBattleEngine\" android:versionCode=\"1\" android:versionName=\"1.0\" android:installLocation=\"auto\">\n\t<uses-sdk android:minSdkVersion=\"21\" android:targetSdkVersion=\"28\" />\n\t<application android:label=\"Pokémon Battle Engine\" android:icon=\"@drawable/Icon\"></application>\n</manifest>"
  },
  {
    "path": "PokemonBattleEngineClient.Android/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\nusing Android.App;\n\n// General Information about an assembly is controlled through the following \n// set of attributes. Change these attribute values to modify the information\n// associated with an assembly.\n[assembly: AssemblyTitle(\"PokemonBattleEngineClient.Android\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyCompany(\"Kermalis\")]\n[assembly: AssemblyProduct(\"PokemonBattleEngineClient.Android\")]\n[assembly: AssemblyCopyright(\"Copyright ©  2019\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n[assembly: ComVisible(false)]\n\n// Version information for an assembly consists of the following four values:\n//\n//      Major Version\n//      Minor Version \n//      Build Number\n//      Revision\n//\n// You can specify all the values or you can default the Build and Revision Numbers \n// by using the '*' as shown below:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n[assembly: AssemblyVersion(\"1.0.0.0\")]\n[assembly: AssemblyFileVersion(\"1.0.0.0\")]\n"
  },
  {
    "path": "PokemonBattleEngineClient.Android/Resources/AboutResources.txt",
    "content": "Images, layout descriptions, binary blobs and string dictionaries can be included \nin your application as resource files.  Various Android APIs are designed to \noperate on the resource IDs instead of dealing with images, strings or binary blobs \ndirectly.\n\nFor example, a sample Android app that contains a user interface layout (main.axml),\nan internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) \nwould keep its resources in the \"Resources\" directory of the application:\n\nResources/\n    drawable/\n        icon.png\n\n    layout/\n        main.axml\n\n    values/\n        strings.xml\n\nIn order to get the build system to recognize Android resources, set the build action to\n\"AndroidResource\".  The native Android APIs do not operate directly with filenames, but \ninstead operate on resource IDs.  When you compile an Android application that uses resources, \nthe build system will package the resources for distribution and generate a class called \"R\" \n(this is an Android convention) that contains the tokens for each one of the resources \nincluded. For example, for the above Resources layout, this is what the R class would expose:\n\npublic class R {\n    public class drawable {\n        public const int icon = 0x123;\n    }\n\n    public class layout {\n        public const int main = 0x456;\n    }\n\n    public class strings {\n        public const int first_string = 0xabc;\n        public const int second_string = 0xbcd;\n    }\n}\n\nYou would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main \nto reference the layout/main.axml file, or R.strings.first_string to reference the first \nstring in the dictionary file values/strings.xml."
  },
  {
    "path": "PokemonBattleEngineClient.Android/Resources/Resource.Designer.cs",
    "content": "#pragma warning disable 1591\n//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\n[assembly: global::Android.Runtime.ResourceDesignerAttribute(\"PokemonBattleEngineClient.Android.Resource\", IsApplication=true)]\n\nnamespace PokemonBattleEngineClient.Android\n{\n\t\n\t\n\t[System.CodeDom.Compiler.GeneratedCodeAttribute(\"Xamarin.Android.Build.Tasks\", \"1.0.0.0\")]\n\tpublic partial class Resource\n\t{\n\t\t\n\t\tstatic Resource()\n\t\t{\n\t\t\tglobal::Android.Runtime.ResourceIdManager.UpdateIdValues();\n\t\t}\n\t\t\n\t\tpublic static void UpdateIdValues()\n\t\t{\n\t\t}\n\t\t\n\t\tpublic partial class Attribute\n\t\t{\n\t\t\t\n\t\t\tstatic Attribute()\n\t\t\t{\n\t\t\t\tglobal::Android.Runtime.ResourceIdManager.UpdateIdValues();\n\t\t\t}\n\t\t\t\n\t\t\tprivate Attribute()\n\t\t\t{\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic partial class Drawable\n\t\t{\n\t\t\t\n\t\t\t// aapt resource value: 0x7F010000\n\t\t\tpublic const int Icon = 2130771968;\n\t\t\t\n\t\t\tstatic Drawable()\n\t\t\t{\n\t\t\t\tglobal::Android.Runtime.ResourceIdManager.UpdateIdValues();\n\t\t\t}\n\t\t\t\n\t\t\tprivate Drawable()\n\t\t\t{\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic partial class Id\n\t\t{\n\t\t\t\n\t\t\t// aapt resource value: 0x7F020000\n\t\t\tpublic const int MyButton = 2130837504;\n\t\t\t\n\t\t\tstatic Id()\n\t\t\t{\n\t\t\t\tglobal::Android.Runtime.ResourceIdManager.UpdateIdValues();\n\t\t\t}\n\t\t\t\n\t\t\tprivate Id()\n\t\t\t{\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic partial class Layout\n\t\t{\n\t\t\t\n\t\t\t// aapt resource value: 0x7F030000\n\t\t\tpublic const int Main = 2130903040;\n\t\t\t\n\t\t\tstatic Layout()\n\t\t\t{\n\t\t\t\tglobal::Android.Runtime.ResourceIdManager.UpdateIdValues();\n\t\t\t}\n\t\t\t\n\t\t\tprivate Layout()\n\t\t\t{\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic partial class String\n\t\t{\n\t\t\t\n\t\t\t// aapt resource value: 0x7F040000\n\t\t\tpublic const int ApplicationName = 2130968576;\n\t\t\t\n\t\t\t// aapt resource value: 0x7F040001\n\t\t\tpublic const int Hello = 2130968577;\n\t\t\t\n\t\t\tstatic String()\n\t\t\t{\n\t\t\t\tglobal::Android.Runtime.ResourceIdManager.UpdateIdValues();\n\t\t\t}\n\t\t\t\n\t\t\tprivate String()\n\t\t\t{\n\t\t\t}\n\t\t}\n\t}\n}\n#pragma warning restore 1591\n"
  },
  {
    "path": "PokemonBattleEngineClient.Android/Resources/layout/Main.axml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:orientation=\"vertical\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    >\n<Button  \n    android:id=\"@+id/MyButton\"\n    android:layout_width=\"match_parent\" \n    android:layout_height=\"wrap_content\" \n    android:text=\"@string/Hello\"\n    />\n</LinearLayout>\n"
  },
  {
    "path": "PokemonBattleEngineClient.Android/Resources/values/Strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"Hello\">Hello World, Click Me!</string>\n    <string name=\"ApplicationName\">ControlCatalog.Android</string>\n</resources>\n"
  },
  {
    "path": "PokemonBattleEngineClient.Desktop/PokemonBattleEngineClient.Desktop.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n\t<PropertyGroup>\n\t\t<TargetFrameworks>net7.0</TargetFrameworks>\n\t\t<OutputType>Exe</OutputType>\n\t\t<RootNamespace>Kermalis.PokemonBattleEngineClient.Desktop</RootNamespace>\n\t\t<Authors>Kermalis</Authors>\n\t\t<Copyright>Kermalis</Copyright>\n\t\t<RepositoryUrl>https://github.com/Kermalis/PokemonBattleEngine</RepositoryUrl>\n\t\t<NoWarn />\n\t\t<StartupObject>Kermalis.PokemonBattleEngineClient.Desktop.Program</StartupObject>\n\t\t<Nullable>enable</Nullable>\n\t</PropertyGroup>\n\n\t<ItemGroup>\n\t\t<ProjectReference Include=\"..\\PokemonBattleEngineClient\\PokemonBattleEngineClient.csproj\" />\n\t</ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "PokemonBattleEngineClient.Desktop/Program.cs",
    "content": "using Avalonia;\nusing Avalonia.ReactiveUI;\nusing Kermalis.PokemonBattleEngineClient.Infrastructure;\nusing System;\n\nnamespace Kermalis.PokemonBattleEngineClient.Desktop;\n\ninternal static class Program\n{\n\t[STAThread]\n\tprivate static void Main()\n\t{\n\t\tUtils.SetWorkingDirectory(string.Empty);\n\t\tBuildAvaloniaApp().StartWithClassicDesktopLifetime(null);\n\t}\n\t/// <summary>This method is needed for IDE previewer infrastructure.</summary>\n\tpublic static AppBuilder BuildAvaloniaApp()\n\t{\n\t\treturn AppBuilder.Configure<App>()\n\t\t\t.UsePlatformDetect()\n\t\t\t.UseReactiveUI()\n\t\t\t.LogToTrace();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineClient.iOS/AppDelegate.cs",
    "content": "using Avalonia;\nusing Avalonia.iOS;\nusing Avalonia.Media;\nusing Foundation;\nusing Kermalis.PokemonBattleEngineClient.Infrastructure;\nusing System;\nusing System.IO;\nusing UIKit;\n\nnamespace Kermalis.PokemonBattleEngineClient.iOS\n{\n    // The UIApplicationDelegate for the application. This class is responsible for launching the \n    // User Interface of the application, as well as listening (and optionally responding) to \n    // application events from iOS.\n    [Register(\"AppDelegate\")]\n    public sealed partial class AppDelegate : UIApplicationDelegate\n    {\n        public override UIWindow Window { get; set; }\n\n        //\n        // This method is invoked when the application has loaded and is ready to run. In this \n        // method you should instantiate the window, load the UI into it and then make the window\n        // visible.\n        //\n        // You have 17 seconds to return from this method, or iOS will terminate your application.\n        //\n        public override bool FinishedLaunching(UIApplication uiapp, NSDictionary options)\n        {\n            CopyDatabase();\n            AppBuilder.Configure<App>()\n                .UseiOS()\n                //.UseSkia()\n                .SetupWithoutStarting();\n            Window = new AvaloniaWindow() { Content = new MainView(), StatusBarColor = Colors.LightSteelBlue };\n            Window.MakeKeyAndVisible();\n            return true;\n        }\n\n        private void CopyDatabase()\n        {\n            string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), \"..\", \"Library\");\n            if (!Directory.Exists(path))\n            {\n                Directory.CreateDirectory(path);\n            }\n            const string dbName = \"PokemonBattleEngine.db\";\n            using (var br = new BinaryReader(Utils.GetResourceStream(dbName)))\n            {\n                using (var bw = new BinaryWriter(new FileStream(Path.Combine(path, dbName), FileMode.Create)))\n                {\n                    byte[] buffer = new byte[0x800];\n                    int length = 0;\n                    while ((length = br.Read(buffer, 0, buffer.Length)) > 0)\n                    {\n                        bw.Write(buffer, 0, length);\n                    }\n                }\n            }\n            Utils.SetWorkingDirectory(path);\n        }\n    }\n}"
  },
  {
    "path": "PokemonBattleEngineClient.iOS/Entitlements.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict/>\n</plist>\n"
  },
  {
    "path": "PokemonBattleEngineClient.iOS/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDisplayName</key>\n\t<string>Pokémon Battle Engine</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>com.Kermalis.PokemonBattleEngine</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>1.0</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>MinimumOSVersion</key>\n\t<string>8.0</string>\n\t<key>UIDeviceFamily</key>\n\t<array>\n\t\t<integer>1</integer>\n\t\t<integer>2</integer>\n\t</array>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>armv7</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "PokemonBattleEngineClient.iOS/Main.cs",
    "content": "using UIKit;\n\nnamespace Kermalis.PokemonBattleEngineClient.iOS\n{\n    public sealed class Application\n    {\n        private static void Main(string[] args)\n        {\n            UIApplication.Main(args, null, \"AppDelegate\");\n        }\n    }\n}"
  },
  {
    "path": "PokemonBattleEngineClient.iOS/PokemonBattleEngineClient.iOS.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"..\\packages\\Avalonia.0.8.0\\build\\Avalonia.props\" Condition=\"Exists('..\\packages\\Avalonia.0.8.0\\build\\Avalonia.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">iPhoneSimulator</Platform>\n    <ProjectGuid>{57E0455D-D565-44BB-B069-EE1AA20F8337}</ProjectGuid>\n    <ProjectTypeGuids>{FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>\n    <OutputType>Exe</OutputType>\n    <RootNamespace>PokemonBattleEngineClient.iOS</RootNamespace>\n    <IPhoneResourcePrefix>Resources</IPhoneResourcePrefix>\n    <AssemblyName>PokemonBattleEngineClient.iOS</AssemblyName>\n    <NuGetPackageImportStamp>\n    </NuGetPackageImportStamp>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|iPhoneSimulator' \">\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>bin\\iPhoneSimulator\\Debug</OutputPath>\n    <DefineConstants>DEBUG</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <ConsolePause>false</ConsolePause>\n    <MtouchArch>i386</MtouchArch>\n    <MtouchLink>SdkOnly</MtouchLink>\n    <MtouchDebug>True</MtouchDebug>\n    <MtouchSdkVersion>9.1</MtouchSdkVersion>\n    <MtouchProfiling>False</MtouchProfiling>\n    <MtouchFastDev>False</MtouchFastDev>\n    <MtouchNoSymbolStrip>False</MtouchNoSymbolStrip>\n    <MtouchUseLlvm>False</MtouchUseLlvm>\n    <MtouchUseThumb>False</MtouchUseThumb>\n    <MtouchEnableBitcode>False</MtouchEnableBitcode>\n    <MtouchUseSGen>False</MtouchUseSGen>\n    <MtouchUseRefCounting>False</MtouchUseRefCounting>\n    <OptimizePNGs>True</OptimizePNGs>\n    <MtouchTlsProvider>Default</MtouchTlsProvider>\n    <MtouchHttpClientHandler>HttpClientHandler</MtouchHttpClientHandler>\n    <MtouchFloat32>False</MtouchFloat32>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|iPhoneSimulator' \">\n    <DebugType>none</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>bin\\iPhoneSimulator\\Release</OutputPath>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <MtouchLink>None</MtouchLink>\n    <MtouchArch>i386</MtouchArch>\n    <ConsolePause>false</ConsolePause>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|iPhone' \">\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>bin\\iPhone\\Debug</OutputPath>\n    <DefineConstants>DEBUG</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <ConsolePause>false</ConsolePause>\n    <MtouchArch>ARMv7, ARM64</MtouchArch>\n    <CodesignEntitlements>Entitlements.plist</CodesignEntitlements>\n    <CodesignKey>iPhone Developer</CodesignKey>\n    <MtouchDebug>true</MtouchDebug>\n    <WarningsAsErrors>NU1605</WarningsAsErrors>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|iPhone' \">\n    <DebugType>none</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>bin\\iPhone\\Release</OutputPath>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <CodesignEntitlements>Entitlements.plist</CodesignEntitlements>\n    <MtouchArch>ARMv7, ARM64</MtouchArch>\n    <ConsolePause>false</ConsolePause>\n    <CodesignKey>iPhone Developer</CodesignKey>\n    <WarningsAsErrors>NU1605</WarningsAsErrors>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Ad-Hoc|iPhone' \">\n    <DebugType>none</DebugType>\n    <Optimize>True</Optimize>\n    <OutputPath>bin\\iPhone\\Ad-Hoc</OutputPath>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <ConsolePause>False</ConsolePause>\n    <MtouchArch>ARMv7, ARM64</MtouchArch>\n    <CodesignEntitlements>Entitlements.plist</CodesignEntitlements>\n    <BuildIpa>True</BuildIpa>\n    <CodesignProvision>Automatic:AdHoc</CodesignProvision>\n    <CodesignKey>iPhone Distribution</CodesignKey>\n    <WarningsAsErrors>NU1605</WarningsAsErrors>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'AppStore|iPhone' \">\n    <DebugType>none</DebugType>\n    <Optimize>True</Optimize>\n    <OutputPath>bin\\iPhone\\AppStore</OutputPath>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <ConsolePause>False</ConsolePause>\n    <MtouchArch>ARMv7, ARM64</MtouchArch>\n    <CodesignEntitlements>Entitlements.plist</CodesignEntitlements>\n    <CodesignProvision>Automatic:AppStore</CodesignProvision>\n    <CodesignKey>iPhone Distribution</CodesignKey>\n    <WarningsAsErrors>NU1605</WarningsAsErrors>\n  </PropertyGroup>\n  <ItemGroup>\n    <Compile Include=\"Main.cs\" />\n    <Compile Include=\"AppDelegate.cs\" />\n    <None Include=\"Info.plist\" />\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\n    <InterfaceDefinition Include=\"Resources\\LaunchScreen.xib\" />\n    <Content Include=\"Entitlements.plist\" />\n  </ItemGroup>\n  <ItemGroup>\n    <PackageReference Include=\"Avalonia.iOS\">\n      <Version>0.9.1</Version>\n    </PackageReference>\n    <Reference Include=\"System.Xml\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"Xamarin.iOS\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\PokemonBattleEngineClient\\PokemonBattleEngineClient.csproj\">\n      <Project>{d0a739b9-3c68-4ba6-a328-41606954b6bd}</Project>\n      <Name>PokemonBattleEngineClient</Name>\n    </ProjectReference>\n  </ItemGroup>\n  <Import Project=\"$(MSBuildExtensionsPath)\\Xamarin\\iOS\\Xamarin.iOS.CSharp.targets\" />\n</Project>"
  },
  {
    "path": "PokemonBattleEngineClient.iOS/Properties/AssemblyInfo.cs",
    "content": "using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Information about an assembly is controlled through the following \n// set of attributes. Change these attribute values to modify the information\n// associated with an assembly.\n[assembly: AssemblyTitle(\"PokemonBattleEngineClient.iOS\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyCompany(\"Kermalis\")]\n[assembly: AssemblyProduct(\"PokemonBattleEngineClient.iOS\")]\n[assembly: AssemblyCopyright(\"Copyright ©  2019\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n\n// Setting ComVisible to false makes the types in this assembly not visible \n// to COM components.  If you need to access a type in this assembly from \n// COM, set the ComVisible attribute to true on that type.\n[assembly: ComVisible(false)]\n\n// The following GUID is for the ID of the typelib if this project is exposed to COM\n[assembly: Guid(\"57e0455d-d565-44bb-b069-ee1aa20f8337\")]\n\n// Version information for an assembly consists of the following four values:\n//\n//      Major Version\n//      Minor Version \n//      Build Number\n//      Revision\n//\n// You can specify all the values or you can default the Build and Revision Numbers \n// by using the '*' as shown below:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n[assembly: AssemblyVersion(\"1.0.0.0\")]\n[assembly: AssemblyFileVersion(\"1.0.0.0\")]\n"
  },
  {
    "path": "PokemonBattleEngineClient.iOS/Resources/LaunchScreen.xib",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" version=\"3.0\" toolsVersion=\"6214\" systemVersion=\"14A314h\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" useTraitCollections=\"YES\">\n\t<dependencies>\n\t\t<plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"6207\" />\n\t\t<capability name=\"Constraints with non-1.0 multipliers\" minToolsVersion=\"5.1\" />\n\t</dependencies>\n\t<objects>\n\t\t<placeholder placeholderIdentifier=\"IBFilesOwner\" id=\"-1\" userLabel=\"File's Owner\" />\n\t\t<placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"-2\" customClass=\"UIResponder\" />\n\t\t<view contentMode=\"scaleToFill\" id=\"iN0-l3-epB\">\n\t\t\t<rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"480\" height=\"480\" />\n\t\t\t<autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\" />\n\t\t\t<subviews>\n\t\t\t\t<label opaque=\"NO\" clipsSubviews=\"YES\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"  Copyright (c) 2016 \" textAlignment=\"center\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\"\n\t\t\t\t\tminimumFontSize=\"9\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"8ie-xW-0ye\">\n\t\t\t\t\t<rect key=\"frame\" x=\"20\" y=\"439\" width=\"441\" height=\"21\" />\n\t\t\t\t\t<fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\" />\n\t\t\t\t\t<color key=\"textColor\" cocoaTouchSystemColor=\"darkTextColor\" />\n\t\t\t\t\t<nil key=\"highlightedColor\" />\n\t\t\t\t</label>\n\t\t\t\t<label opaque=\"NO\" clipsSubviews=\"YES\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"ControlCatalog.iOS\" textAlignment=\"center\" lineBreakMode=\"middleTruncation\" baselineAdjustment=\"alignBaselines\"\n\t\t\t\t\tminimumFontSize=\"18\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"kId-c2-rCX\">\n\t\t\t\t\t<rect key=\"frame\" x=\"20\" y=\"140\" width=\"441\" height=\"43\" />\n\t\t\t\t\t<fontDescription key=\"fontDescription\" type=\"boldSystem\" pointSize=\"36\" />\n\t\t\t\t\t<color key=\"textColor\" cocoaTouchSystemColor=\"darkTextColor\" />\n\t\t\t\t\t<nil key=\"highlightedColor\" />\n\t\t\t\t</label>\n\t\t\t</subviews>\n\t\t\t<color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"calibratedWhite\" />\n\t\t\t<constraints>\n\t\t\t\t<constraint firstItem=\"kId-c2-rCX\" firstAttribute=\"centerY\" secondItem=\"iN0-l3-epB\" secondAttribute=\"bottom\" multiplier=\"1/3\" constant=\"1\" id=\"5cJ-9S-tgC\" />\n\t\t\t\t<constraint firstAttribute=\"centerX\" secondItem=\"kId-c2-rCX\" secondAttribute=\"centerX\" id=\"Koa-jz-hwk\" />\n\t\t\t\t<constraint firstAttribute=\"bottom\" secondItem=\"8ie-xW-0ye\" secondAttribute=\"bottom\" constant=\"20\" id=\"Kzo-t9-V3l\" />\n\t\t\t\t<constraint firstItem=\"8ie-xW-0ye\" firstAttribute=\"leading\" secondItem=\"iN0-l3-epB\" secondAttribute=\"leading\" constant=\"20\" symbolic=\"YES\" id=\"MfP-vx-nX0\" />\n\t\t\t\t<constraint firstAttribute=\"centerX\" secondItem=\"8ie-xW-0ye\" secondAttribute=\"centerX\" id=\"ZEH-qu-HZ9\" />\n\t\t\t\t<constraint firstItem=\"kId-c2-rCX\" firstAttribute=\"leading\" secondItem=\"iN0-l3-epB\" secondAttribute=\"leading\" constant=\"20\" symbolic=\"YES\" id=\"fvb-Df-36g\" />\n\t\t\t</constraints>\n\t\t\t<nil key=\"simulatedStatusBarMetrics\" />\n\t\t\t<freeformSimulatedSizeMetrics key=\"simulatedDestinationMetrics\" />\n\t\t\t<point key=\"canvasLocation\" x=\"548\" y=\"455\" />\n\t\t</view>\n\t</objects>\n</document>\n"
  },
  {
    "path": "PokemonBattleEngineDiscord/BattleContext.cs",
    "content": "﻿using Discord;\nusing Discord.WebSocket;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Data.Utils;\nusing Kermalis.PokemonBattleEngine.DefaultData.AI;\nusing Kermalis.PokemonBattleEngine.Packets;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace Kermalis.PokemonBattleEngineDiscord;\n\ninternal sealed partial class BattleContext\n{\n\tprivate sealed class Battler\n\t{\n\t\tpublic readonly PBETeam Team;\n\t\tpublic readonly PBETrainer Trainer;\n\t\tpublic readonly SocketUser? User; // Null means PBEAI is battling\n\t\tpublic readonly PBEDDAI? AI;\n\n\t\tpublic Battler(PBETeam t, SocketUser? u)\n\t\t{\n\t\t\tTeam = t;\n\t\t\tTrainer = t.Trainers[0];\n\t\t\tUser = u;\n\t\t\tif (u is null)\n\t\t\t{\n\t\t\t\tAI = new PBEDDAI(Trainer);\n\t\t\t}\n\t\t}\n\n\t\tpublic string GetName()\n\t\t{\n\t\t\treturn User is null ? Trainer.Name : User.Username;\n\t\t}\n\t\tpublic bool Is(SocketUser u)\n\t\t{\n\t\t\treturn User is not null && User.Id == u.Id;\n\t\t}\n\t}\n\n\tprivate static readonly object _activeBattlesLockObj = new();\n\tprivate static readonly List<BattleContext> _activeBattles = new();\n\tprivate static readonly Dictionary<SocketUser, BattleContext> _activeBattlers = new(DiscordComparers.UserComparer);\n\tprivate static readonly Dictionary<ITextChannel, BattleContext> _activeChannels = new(DiscordComparers.ChannelComparer);\n\tprivate static readonly Dictionary<IGuild, List<BattleContext>> _activeGuilds = new(DiscordComparers.GuildComparer);\n\tprivate static ulong _battleCounter = 1;\n\n\tpublic readonly ulong BattleId;\n\tprivate readonly PBEBattle _battle;\n\tprivate readonly Battler _battler0;\n\tprivate readonly Battler _battler1;\n\tprivate ITextChannel _channel;\n\tprivate readonly StringBuilder _queuedMessages;\n\n\tpublic BattleContext(PBEBattle battle, SocketUser? battler0, SocketUser? battler1)\n\t{\n\t\tlock (_activeBattlesLockObj)\n\t\t{\n\t\t\t_activeBattles.Add(this);\n\t\t\tif (battler0 is not null)\n\t\t\t{\n\t\t\t\t_activeBattlers.Add(battler0, this);\n\t\t\t}\n\t\t\tif (battler1 is not null)\n\t\t\t{\n\t\t\t\t_activeBattlers.Add(battler1, this);\n\t\t\t}\n\n\t\t\t_queuedMessages = new StringBuilder();\n\t\t\tBattleId = _battleCounter++;\n\t\t\t_battle = battle;\n\t\t\t_battler0 = new Battler(battle.Teams[0], battler0);\n\t\t\t_battler1 = new Battler(battle.Teams[1], battler1);\n\t\t\tSetEmbedTitle();\n\n\t\t\tbattle.OnNewEvent += Battle_OnNewEvent;\n\t\t\tbattle.OnStateChanged += Battle_OnStateChanged;\n\t\t}\n\t\t_channel = null!; // _channel will be set in Begin()\n\t}\n\tpublic async Task Begin(ITextChannel channel)\n\t{\n\t\tlock (_activeBattlesLockObj)\n\t\t{\n\t\t\t_activeChannels.Add(channel, this);\n\t\t\t_channel = channel;\n\t\t\tIGuild guild = channel.Guild;\n\t\t\tif (!_activeGuilds.TryGetValue(guild, out List<BattleContext>? list))\n\t\t\t{\n\t\t\t\tlist = new List<BattleContext>();\n\t\t\t\t_activeGuilds.Add(guild, list);\n\t\t\t}\n\t\t\tlist.Add(this);\n\t\t}\n\t\ttry\n\t\t{\n\t\t\tnew Thread(() => _battle.Begin()).Start();\n\t\t}\n\t\tcatch (Exception ex)\n\t\t{\n\t\t\tawait CloseNormalWithException(ex);\n\t\t}\n\t}\n\n\tprivate void Battle_OnStateChanged(PBEBattle battle)\n\t{\n\t\tBattle_OnStateChanged().GetAwaiter().GetResult();\n\t}\n\tprivate void Battle_OnNewEvent(PBEBattle battle, IPBEPacket packet)\n\t{\n\t\tBattle_OnNewEvent(packet).GetAwaiter().GetResult();\n\t}\n\n\tpublic static void OnChannelDeleted(SocketChannel channel)\n\t{\n\t\tif (channel is ITextChannel c)\n\t\t{\n\t\t\tlock (_activeBattlesLockObj)\n\t\t\t{\n\t\t\t\tif (_activeChannels.TryGetValue(c, out BattleContext? bc))\n\t\t\t\t{\n\t\t\t\t\tbc.CloseSilent(false);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tpublic static void OnLeftGuild(SocketGuild guild)\n\t{\n\t\tlock (_activeBattlesLockObj)\n\t\t{\n\t\t\tif (_activeGuilds.TryGetValue(guild, out List<BattleContext>? list))\n\t\t\t{\n\t\t\t\tforeach (BattleContext bc in list.ToArray()) // Prevent collection being modified in loop\n\t\t\t\t{\n\t\t\t\t\tbc.CloseSilent(false);\n\t\t\t\t}\n\t\t\t\t_activeGuilds.Remove(guild);\n\t\t\t}\n\t\t}\n\t}\n\tpublic static Task OnUserLeft(SocketGuildUser user)\n\t{\n\t\tlock (_activeBattlesLockObj)\n\t\t{\n\t\t\tif (_activeBattlers.TryGetValue(user, out BattleContext? bc))\n\t\t\t{\n\t\t\t\treturn bc.Forfeit(user);\n\t\t\t}\n\t\t}\n\t\treturn Task.CompletedTask;\n\t}\n\tpublic static void OnGuildMemberUpdated(SocketGuildUser user)\n\t{\n\t\tlock (_activeBattlesLockObj)\n\t\t{\n\t\t\tif (_activeBattlers.TryGetValue(user, out BattleContext? bc))\n\t\t\t{\n\t\t\t\tbc.SetEmbedTitle();\n\t\t\t}\n\t\t}\n\t}\n\tpublic static bool GetBattleContext(SocketUser user, [NotNullWhen(true)] out BattleContext? bc)\n\t{\n\t\tlock (_activeBattlesLockObj)\n\t\t{\n\t\t\treturn _activeBattlers.TryGetValue(user, out bc);\n\t\t}\n\t}\n\n\tpublic async Task Forfeit(SocketUser user)\n\t{\n\t\t// Assumes \"user\" is not null (AI)\n\t\tawait Forfeit(GetBattler(user));\n\t}\n\tprivate async Task Forfeit(Battler battler)\n\t{\n\t\tawait CloseNormalWithMessage(string.Format(\"{0} has forfeited the match.\", battler.GetName()), ReplaySaver.ShouldSaveForfeits, true);\n\t}\n\tprivate async Task CloseNormalWithException(Exception ex)\n\t{\n\t\tConsole.WriteLine(\"Battle #{0} exception:{1}{2}\", BattleId, Environment.NewLine, ex);\n\t\tawait CloseNormalWithMessage(string.Format(\"Encountered an error, battle resulted in a draw. Error:\\n{0}\", ex.Message), false, false);\n\t}\n\tprivate async Task CloseNormalWithMessage(string message, bool saveReplay, bool deleteChannel)\n\t{\n\t\tawait CreateAndSendEmbedAsync(message);\n\t\tif (deleteChannel)\n\t\t{\n\t\t\tawait CloseNormalAndDelete(saveReplay);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tawait CloseNormal(saveReplay);\n\t\t}\n\t}\n\tprivate async Task CloseNormalAndDelete(bool saveReplay)\n\t{\n\t\tawait CloseNormal(saveReplay);\n\t\tawait ChannelHandler.MarkChannelForDeletion(_channel);\n\t}\n\tprivate async Task CloseNormal(bool saveReplay)\n\t{\n\t\tCloseSilent(saveReplay);\n\t\tawait ChannelHandler.ChangeCategory(_channel);\n\t}\n\tprivate void CloseSilent(bool saveReplay)\n\t{\n\t\tSocketUser? u0 = _battler0.User;\n\t\tSocketUser? u1 = _battler1.User;\n\t\tlock (_activeBattlesLockObj)\n\t\t{\n\t\t\t_activeBattles.Remove(this);\n\t\t\tif (u0 is not null)\n\t\t\t{\n\t\t\t\t_activeBattlers.Remove(u0);\n\t\t\t}\n\t\t\tif (u1 is not null)\n\t\t\t{\n\t\t\t\t_activeBattlers.Remove(u1);\n\t\t\t}\n\t\t\t_activeChannels.Remove(_channel);\n\t\t\t_activeGuilds[_channel.Guild].Remove(this);\n\t\t\t// Only save replay if ((saveReplay is true) and (not battling an AI or (battling an AI and should save AI replays)))\n\t\t\tif (saveReplay && ((u0 is not null && u1 is not null) || ReplaySaver.ShouldSaveAIBattles))\n\t\t\t{\n\t\t\t\tReplaySaver.SaveReplay(_battle, BattleId); // Save battle in the lock so they don't conflict while directory checking\n\t\t\t}\n\t\t}\n\t\t_battle.OnNewEvent -= Battle_OnNewEvent;\n\t\t_battle.OnStateChanged -= Battle_OnStateChanged;\n\t\tReactionHandler.RemoveListeners(u0, u1);\n\t}\n\n\tprivate string? _embedTitle; // Mini performance saver\n\tprivate void SetEmbedTitle()\n\t{\n\t\tstring s = $\"**[#{BattleId}] ― {_battler0.GetName()} vs {_battler1.GetName()}\";\n\t\tif (_battle.TurnNumber > 0)\n\t\t{\n\t\t\ts += $\" (Turn {_battle.TurnNumber})\";\n\t\t}\n\t\tif (_battle.Weather != PBEWeather.None)\n\t\t{\n\t\t\ts += $\" {Utils.WeatherEmotes[_battle.Weather]}\";\n\t\t}\n\t\t_embedTitle = s + \"**\";\n\t}\n\tprivate ushort _lastSwitchinsTurn;\n\tprivate async Task SendQueuedMessages()\n\t{\n\t\tstring str = _queuedMessages.ToString();\n\t\t_queuedMessages.Clear();\n\t\tint i = 0;\n\t\tdo\n\t\t{\n\t\t\tint count = Math.Min(str.Length - i, EmbedBuilder.MaxDescriptionLength);\n\t\t\tstring sub = str.Substring(i, count);\n\t\t\tawait CreateAndSendEmbedAsync(sub);\n\t\t\ti += count;\n\t\t} while (i < str.Length);\n\t}\n\tprivate async Task SendActiveBattlerEmbeds()\n\t{\n\t\tbool b = true;\n\t\tforeach (PBEBattlePokemon pkmn in _battle.ActiveBattlers)\n\t\t{\n\t\t\tawait CreateAndSendEmbedAsync(CreateKnownPokemonEmbed(pkmn), messageText: b ? Separator : string.Empty, pkmn: pkmn);\n\t\t}\n\t}\n\tprivate async Task<IUserMessage> CreateAndSendEmbedAsync(string embedDescription, string messageText = \"\",\n\t\tPBEBattlePokemon? pkmn = null, bool useUpperImage = false, EmbedFieldBuilder[]? fields = null, SocketUser? userToSendTo = null)\n\t{\n\t\tEmbedBuilder embed = new EmbedBuilder()\n\t\t\t\t.WithUrl(Utils.URL)\n\t\t\t\t.WithTitle(_embedTitle)\n\t\t\t\t.WithDescription(embedDescription);\n\t\tif (pkmn is null)\n\t\t{\n\t\t\tembed.WithColor(Utils.RandomColor());\n\t\t}\n\t\telse\n\t\t{\n\t\t\tembed.WithColor(Utils.GetColor(pkmn));\n\t\t\tstring sprite = Utils.GetPokemonSprite(pkmn);\n\t\t\tif (useUpperImage)\n\t\t\t{\n\t\t\t\tembed.WithThumbnailUrl(sprite);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tembed.WithImageUrl(sprite);\n\t\t\t}\n\t\t}\n\t\tif (fields is not null)\n\t\t{\n\t\t\tforeach (EmbedFieldBuilder f in fields)\n\t\t\t{\n\t\t\t\tembed.AddField(f);\n\t\t\t}\n\t\t}\n\t\tif (userToSendTo is not null)\n\t\t{\n\t\t\treturn await userToSendTo.SendMessageAsync(messageText, embed: embed.Build());\n\t\t}\n\t\treturn await _channel.SendMessageAsync(messageText, embed: embed.Build());\n\t}\n\tprivate static void AddStatChanges(PBEBattlePokemon pkmn, StringBuilder sb)\n\t{\n\t\tPBEStat[] statChanges = pkmn.GetChangedStats();\n\t\tif (statChanges.Length > 0)\n\t\t{\n\t\t\tvar statStrs = new List<string>(7);\n\t\t\tif (Array.IndexOf(statChanges, PBEStat.Attack) != -1)\n\t\t\t{\n\t\t\t\tstatStrs.Add($\"[A] x{PBEBattle.GetStatChangeModifier(pkmn.AttackChange, false):0.00}\");\n\t\t\t}\n\t\t\tif (Array.IndexOf(statChanges, PBEStat.Defense) != -1)\n\t\t\t{\n\t\t\t\tstatStrs.Add($\"[D] x{PBEBattle.GetStatChangeModifier(pkmn.DefenseChange, false):0.00}\");\n\t\t\t}\n\t\t\tif (Array.IndexOf(statChanges, PBEStat.SpAttack) != -1)\n\t\t\t{\n\t\t\t\tstatStrs.Add($\"[SA] x{PBEBattle.GetStatChangeModifier(pkmn.SpAttackChange, false):0.00}\");\n\t\t\t}\n\t\t\tif (Array.IndexOf(statChanges, PBEStat.SpDefense) != -1)\n\t\t\t{\n\t\t\t\tstatStrs.Add($\"[SD] x{PBEBattle.GetStatChangeModifier(pkmn.SpDefenseChange, false):0.00}\");\n\t\t\t}\n\t\t\tif (Array.IndexOf(statChanges, PBEStat.Speed) != -1)\n\t\t\t{\n\t\t\t\tstatStrs.Add($\"[S] x{PBEBattle.GetStatChangeModifier(pkmn.SpeedChange, false):0.00}\");\n\t\t\t}\n\t\t\tif (Array.IndexOf(statChanges, PBEStat.Accuracy) != -1)\n\t\t\t{\n\t\t\t\tstatStrs.Add($\"[AC] x{PBEBattle.GetStatChangeModifier(pkmn.AccuracyChange, true):0.00}\");\n\t\t\t}\n\t\t\tif (Array.IndexOf(statChanges, PBEStat.Evasion) != -1)\n\t\t\t{\n\t\t\t\tstatStrs.Add($\"[E] x{PBEBattle.GetStatChangeModifier(pkmn.EvasionChange, true):0.00}\");\n\t\t\t}\n\t\t\tsb.AppendLine($\"**Stat changes:** {string.Join(\", \", statStrs)}\");\n\t\t}\n\t}\n\tprivate static void CreatePokemonEmbed(PBEBattlePokemon pkmn, bool addReactionChars, out string outStr, out EmbedFieldBuilder[] outFields)\n\t{\n\t\tvar sb = new StringBuilder();\n\t\tstring formStr = PBEDataUtils.HasForms(pkmn.Species, false) ? $\" ({PBEDataProvider.Instance.GetFormName(pkmn).English})\" : string.Empty;\n\t\tsb.AppendLine($\"{pkmn.Nickname}/{PBEDataProvider.Instance.GetSpeciesName(pkmn.Species).English}{formStr} {pkmn.Gender.ToSymbol()} Lv.{pkmn.Level}{(pkmn.Shiny ? $\" {_shinyEmoji}\" : string.Empty)}\");\n\t\tsb.AppendLine($\"**HP:** {pkmn.HP}/{pkmn.MaxHP} ({pkmn.HPPercentage:P2})\");\n\t\tsb.Append($\"**Types:** {Utils.TypeEmotes[pkmn.Type1]}\");\n\t\tif (pkmn.Type2 != PBEType.None)\n\t\t{\n\t\t\tsb.Append($\" {Utils.TypeEmotes[pkmn.Type2]}\");\n\t\t}\n\t\tsb.AppendLine();\n\t\tif (pkmn.Status1 != PBEStatus1.None)\n\t\t{\n\t\t\tsb.AppendLine($\"**Main status:** {Utils.Status1Emotes[pkmn.Status1]}\");\n\t\t}\n\t\tif (pkmn.FieldPosition != PBEFieldPosition.None)\n\t\t{\n\t\t\tif (pkmn.Status1 == PBEStatus1.Asleep)\n\t\t\t{\n\t\t\t\tsb.AppendLine($\"**{Utils.Status1Emotes[PBEStatus1.Asleep]} turns:** {pkmn.Status1Counter}\");\n\t\t\t}\n\t\t\telse if (pkmn.Status1 == PBEStatus1.BadlyPoisoned)\n\t\t\t{\n\t\t\t\tsb.AppendLine($\"**{Utils.Status1Emotes[PBEStatus1.BadlyPoisoned]} counter:** {pkmn.Status1Counter}\");\n\t\t\t}\n\t\t\tif (pkmn.Status2 != PBEStatus2.None)\n\t\t\t{\n\t\t\t\tsb.AppendLine($\"**Volatile status:** {pkmn.Status2}\");\n\t\t\t\tif (pkmn.Status2.HasFlag(PBEStatus2.Confused))\n\t\t\t\t{\n\t\t\t\t\tsb.AppendLine($\"**Confusion turns:** {pkmn.ConfusionCounter}\");\n\t\t\t\t}\n\t\t\t\tif (pkmn.Status2.HasFlag(PBEStatus2.Disguised))\n\t\t\t\t{\n\t\t\t\t\tformStr = PBEDataUtils.HasForms(pkmn.KnownSpecies, false) ? $\" ({PBEDataProvider.Instance.GetFormName(pkmn.KnownSpecies, pkmn.KnownForm).English})\" : string.Empty;\n\t\t\t\t\tsb.AppendLine($\"**Disguised as:** {pkmn.KnownNickname}/{PBEDataProvider.Instance.GetSpeciesName(pkmn.KnownSpecies).English}{formStr} {pkmn.KnownGender.ToSymbol()}\");\n\t\t\t\t}\n\t\t\t\tif (pkmn.Status2.HasFlag(PBEStatus2.Substitute))\n\t\t\t\t{\n\t\t\t\t\tsb.AppendLine($\"**Substitute HP:** {pkmn.SubstituteHP}\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tsb.AppendLine($\"**Stats:** [A] {pkmn.Attack}, [D] {pkmn.Defense}, [SA] {pkmn.SpAttack}, [SD] {pkmn.SpDefense}, [S] {pkmn.Speed}, [W] {pkmn.Weight:0.0}\");\n\t\tif (pkmn.FieldPosition != PBEFieldPosition.None)\n\t\t{\n\t\t\tAddStatChanges(pkmn, sb);\n\t\t}\n\t\tsb.AppendLine($\"**Ability:** {PBEDataProvider.Instance.GetAbilityName(pkmn.Ability).English}\");\n\t\tsb.AppendLine($\"**Item:** {PBEDataProvider.Instance.GetItemName(pkmn.Item).English}\");\n\t\tif (pkmn.Moves.Contains(PBEMoveEffect.Frustration) || pkmn.Moves.Contains(PBEMoveEffect.Return))\n\t\t{\n\t\t\tsb.AppendLine($\"**Friendship:** {pkmn.Friendship} ({pkmn.Friendship / (float)byte.MaxValue:P2})\");\n\t\t}\n\t\toutStr = sb.ToString();\n\t\tsb.Clear();\n\t\toutFields = new EmbedFieldBuilder[2];\n\t\tEmbedFieldBuilder field = new EmbedFieldBuilder()\n\t\t\t\t.WithName(\"**Moves:**\")\n\t\t\t\t.WithIsInline(true);\n\t\toutFields[0] = field;\n\t\tfor (int i = 0; i < PBESettings.DefaultNumMoves; i++)\n\t\t{\n\t\t\tPBEBattleMoveset.PBEBattleMovesetSlot slot = pkmn.Moves[i];\n\t\t\tPBEMove move = slot.Move;\n\t\t\tif (move != PBEMove.None)\n\t\t\t{\n\t\t\t\tPBEType moveType = pkmn.GetMoveType(move);\n\t\t\t\tsb.Append($\"{Utils.TypeEmotes[moveType]} {PBEDataProvider.Instance.GetMoveName(move).English} (**{slot.PP}**/**{slot.MaxPP}** **PP**)\");\n\t\t\t\tif (i < PBESettings.DefaultNumMoves - 1)\n\t\t\t\t{\n\t\t\t\t\tsb.AppendLine();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfield.WithValue(sb.ToString());\n\t\tsb.Clear();\n\t\tfield = new EmbedFieldBuilder()\n\t\t\t.WithName(\"**Usable moves:**\")\n\t\t\t.WithIsInline(true);\n\t\toutFields[1] = field;\n\t\tPBEMove[] usableMoves = pkmn.GetUsableMoves();\n\t\tfor (int i = 0; i < usableMoves.Length; i++)\n\t\t{\n\t\t\tPBEMove move = usableMoves[i];\n\t\t\tPBEType moveType = pkmn.GetMoveType(move);\n\t\t\tif (addReactionChars)\n\t\t\t{\n\t\t\t\tsb.Append($\"{_moveEmotes[i][moveType]} \");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tsb.Append($\"{Utils.TypeEmotes[moveType]} \");\n\t\t\t}\n\t\t\tsb.Append(PBEDataProvider.Instance.GetMoveName(move).English);\n\t\t\tIPBEMoveData mData = PBEDataProvider.Instance.GetMoveData(move);\n\t\t\tstring powerStr;\n\t\t\tif (mData.Effect == PBEMoveEffect.HiddenPower)\n\t\t\t{\n\t\t\t\tpowerStr = pkmn.IndividualValues!.GetHiddenPowerBasePower(PBESettings.DefaultSettings).ToString();\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tpowerStr = mData.Power == 0 ? \"―\" : mData.Power.ToString();\n\t\t\t}\n\t\t\tsb.Append($\" (**{powerStr}** | **{(mData.Accuracy == 0 ? \"―\" : mData.Accuracy.ToString() + '%')}** | **{mData.Category}**)\");\n\t\t\tif (i < usableMoves.Length - 1)\n\t\t\t{\n\t\t\t\tsb.AppendLine();\n\t\t\t}\n\t\t}\n\t\tfield.WithValue(sb.ToString());\n\t}\n\tprivate string CreateKnownPokemonEmbed(PBEBattlePokemon pkmn)\n\t{\n\t\tIPBEPokemonData pData = PBEDataProvider.Instance.GetPokemonData(pkmn.KnownSpecies, pkmn.KnownForm);\n\t\tvar sb = new StringBuilder();\n\t\tstring formStr = PBEDataUtils.HasForms(pkmn.KnownSpecies, false) ? $\" ({PBEDataProvider.Instance.GetFormName(pkmn.KnownSpecies, pkmn.KnownForm).English})\" : string.Empty;\n\t\tsb.AppendLine($\"{GetTrainerName(pkmn.Trainer)}'s {pkmn.KnownNickname}/{pkmn.KnownSpecies}{formStr} {(pkmn.KnownStatus2.HasFlag(PBEStatus2.Transformed) ? pkmn.Gender.ToSymbol() : pkmn.KnownGender.ToSymbol())} Lv.{pkmn.Level}{(pkmn.KnownShiny ? $\" {_shinyEmoji}\" : string.Empty)}\");\n\t\tsb.AppendLine($\"**HP:** {pkmn.HPPercentage:P2}\");\n\t\tsb.Append($\"**Known types:** {Utils.TypeEmotes[pkmn.KnownType1]}\");\n\t\tif (pkmn.KnownType2 != PBEType.None)\n\t\t{\n\t\t\tsb.Append($\" {Utils.TypeEmotes[pkmn.KnownType2]}\");\n\t\t}\n\t\tsb.AppendLine();\n\t\tif (pkmn.Status1 != PBEStatus1.None)\n\t\t{\n\t\t\tsb.AppendLine($\"**Main status:** {Utils.Status1Emotes[pkmn.Status1]}\");\n\t\t\tif (pkmn.Status1 == PBEStatus1.Asleep)\n\t\t\t{\n\t\t\t\tsb.AppendLine($\"**{Utils.Status1Emotes[PBEStatus1.Asleep]} turns:** {pkmn.Status1Counter}\");\n\t\t\t}\n\t\t\telse if (pkmn.Status1 == PBEStatus1.BadlyPoisoned)\n\t\t\t{\n\t\t\t\tsb.AppendLine($\"**{Utils.Status1Emotes[PBEStatus1.BadlyPoisoned]} counter:** {pkmn.Status1Counter}\");\n\t\t\t}\n\t\t}\n\t\tif (pkmn.KnownStatus2 != PBEStatus2.None)\n\t\t{\n\t\t\tsb.AppendLine($\"**Volatile status:** {pkmn.KnownStatus2}\");\n\t\t\tif (pkmn.KnownStatus2.HasFlag(PBEStatus2.Confused))\n\t\t\t{\n\t\t\t\tsb.AppendLine($\"**Confusion turns:** {pkmn.ConfusionCounter}\");\n\t\t\t}\n\t\t}\n\t\tPBEDataUtils.GetStatRange(pData, PBEStat.HP, pkmn.Level, PBESettings.DefaultSettings, out ushort lowHP, out ushort highHP);\n\t\tPBEDataUtils.GetStatRange(pData, PBEStat.Attack, pkmn.Level, PBESettings.DefaultSettings, out ushort lowAttack, out ushort highAttack);\n\t\tPBEDataUtils.GetStatRange(pData, PBEStat.Defense, pkmn.Level, PBESettings.DefaultSettings, out ushort lowDefense, out ushort highDefense);\n\t\tPBEDataUtils.GetStatRange(pData, PBEStat.SpAttack, pkmn.Level, PBESettings.DefaultSettings, out ushort lowSpAttack, out ushort highSpAttack);\n\t\tPBEDataUtils.GetStatRange(pData, PBEStat.SpDefense, pkmn.Level, PBESettings.DefaultSettings, out ushort lowSpDefense, out ushort highSpDefense);\n\t\tPBEDataUtils.GetStatRange(pData, PBEStat.Speed, pkmn.Level, PBESettings.DefaultSettings, out ushort lowSpeed, out ushort highSpeed);\n\t\tsb.AppendLine($\"**Stat range:** [HP] {lowHP}-{highHP}, [A] {lowAttack}-{highAttack}, [D] {lowDefense}-{highDefense}, [SA] {lowSpAttack}-{highSpAttack}, [SD] {lowSpDefense}-{highSpDefense}, [S] {lowSpeed}-{highSpeed}, [W] {pkmn.KnownWeight:0.0}\");\n\t\tAddStatChanges(pkmn, sb);\n\t\tif (pkmn.KnownAbility == PBEAbility.MAX)\n\t\t{\n\t\t\tsb.AppendLine($\"**Possible abilities:** {string.Join(\", \", pData.Abilities.Select(a => PBEDataProvider.Instance.GetAbilityName(a).English))}\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tsb.AppendLine($\"**Known ability:** {PBEDataProvider.Instance.GetAbilityName(pkmn.KnownAbility).English}\");\n\t\t}\n\t\tsb.AppendLine($\"**Known item:** {(pkmn.KnownItem == (PBEItem)ushort.MaxValue ? \"???\" : PBEDataProvider.Instance.GetItemName(pkmn.KnownItem).English)}\");\n\t\tsb.Append(\"**Known moves:** \");\n\t\tfor (int i = 0; i < PBESettings.DefaultNumMoves; i++)\n\t\t{\n\t\t\tPBEBattleMoveset.PBEBattleMovesetSlot slot = pkmn.KnownMoves[i];\n\t\t\tPBEMove move = slot.Move;\n\t\t\tif (move != PBEMove.None)\n\t\t\t{\n\t\t\t\tint pp = slot.PP;\n\t\t\t\tint maxPP = slot.MaxPP;\n\t\t\t\tif (i > 0)\n\t\t\t\t{\n\t\t\t\t\tsb.Append(\", \");\n\t\t\t\t}\n\t\t\t\tif (move == PBEMove.MAX)\n\t\t\t\t{\n\t\t\t\t\tsb.Append(\"???\");\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tsb.Append($\"{Utils.TypeEmotes[pkmn.GetMoveType(move, useKnownInfo: true)]} {PBEDataProvider.Instance.GetMoveName(move).English} ({pp}{(maxPP == 0 ? \")\" : $\"/{maxPP})\")}\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn sb.ToString();\n\t}\n\tprivate Battler GetBattler(SocketUser battler)\n\t{\n\t\tif (_battler0.Is(battler))\n\t\t{\n\t\t\treturn _battler0;\n\t\t}\n\t\tif (_battler1.Is(battler))\n\t\t{\n\t\t\treturn _battler1;\n\t\t}\n\t\tthrow new ArgumentOutOfRangeException(nameof(battler));\n\t}\n\tprivate Battler GetBattler(PBETrainer trainer)\n\t{\n\t\tif (trainer.Id == 0)\n\t\t{\n\t\t\treturn _battler0;\n\t\t}\n\t\tif (trainer.Id == 1)\n\t\t{\n\t\t\treturn _battler1;\n\t\t}\n\t\tthrow new ArgumentOutOfRangeException(nameof(trainer));\n\t}\n\t// These two are passed to the message handler\n\tprivate string GetTrainerName(PBETrainer trainer)\n\t{\n\t\treturn GetBattler(trainer).GetName();\n\t}\n\tprivate string GetTeamName(PBETeam team, bool _)\n\t{\n\t\treturn GetTrainerName(team.Trainers[0]);\n\t}\n\n\tprivate async Task Battle_OnStateChanged()\n\t{\n\t\tswitch (_battle.BattleState)\n\t\t{\n\t\t\tcase PBEBattleState.Ended:\n\t\t\t{\n\t\t\t\tawait CloseNormalAndDelete(true);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEBattleState.ReadyToRunSwitches:\n\t\t\t{\n\t\t\t\ttry\n\t\t\t\t{\n\t\t\t\t\tnew Thread(() => _battle.RunSwitches()).Start();\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex)\n\t\t\t\t{\n\t\t\t\t\tawait CloseNormalWithException(ex);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEBattleState.ReadyToRunTurn:\n\t\t\t{\n\t\t\t\ttry\n\t\t\t\t{\n\t\t\t\t\tnew Thread(() => _battle.RunTurn()).Start();\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex)\n\t\t\t\t{\n\t\t\t\t\tawait CloseNormalWithException(ex);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tprivate async Task Battle_OnNewEvent(IPBEPacket packet)\n\t{\n\t\t// Packets that need extra logic and/or should not have a default message\n\t\tswitch (packet)\n\t\t{\n\t\t\tcase PBEMovePPChangedPacket _: return;\n\t\t\tcase PBEActionsRequestPacket arp:\n\t\t\t{\n\t\t\t\tPBETrainer trainer = arp.Trainer;\n\t\t\t\tBattler battler = GetBattler(trainer);\n\t\t\t\tSocketUser? user = battler.User;\n\t\t\t\tif (user is null) // PBEAI\n\t\t\t\t{\n\t\t\t\t\tbattler.AI!.CreateActions();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tPBEBattlePokemon mainPkmn = trainer.ActionsRequired[0];\n\t\t\t\tvar reactionsToAdd = new List<(IUserMessage Message, IEmote Reaction)>(PBESettings.DefaultMaxPartySize - 1 + PBESettings.DefaultNumMoves); // 5 switch reactions, 4 move reactions\n\t\t\t\tstring description;\n\t\t\t\tEmbedFieldBuilder[] fields;\n\n\t\t\t\tif (mainPkmn.CanSwitchOut())\n\t\t\t\t{\n\t\t\t\t\tasync Task SwitchReactionClicked(IUserMessage switchMsg, PBEBattlePokemon switchPkmn)\n\t\t\t\t\t{\n\t\t\t\t\t\tawait switchMsg.AddReactionAsync(_confirmationEmoji); // Put this here so it happens before RunTurn() takes its time\n\t\t\t\t\t\ttrainer.SelectActionsIfValid(out _, new PBETurnAction(mainPkmn, switchPkmn));\n\t\t\t\t\t}\n\n\t\t\t\t\tList<PBEBattlePokemon> switches = trainer.Party.FindAll(p => p != mainPkmn && p.CanBattle);\n\t\t\t\t\tfor (int i = 0; i < switches.Count; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tPBEBattlePokemon switchPkmn = switches[i];\n\t\t\t\t\t\tCreatePokemonEmbed(switchPkmn, false, out description, out fields);\n\t\t\t\t\t\tIUserMessage switchMsg = await CreateAndSendEmbedAsync(description, messageText: i == 0 ? Separator : string.Empty, pkmn: switchPkmn, useUpperImage: true, fields: fields, userToSendTo: user);\n\t\t\t\t\t\treactionsToAdd.Add((switchMsg, _switchEmoji));\n\t\t\t\t\t\tReactionHandler.AddListener(user, switchMsg, _switchEmoji, () => SwitchReactionClicked(switchMsg, switchPkmn));\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tCreatePokemonEmbed(mainPkmn, true, out description, out fields);\n\t\t\t\tIUserMessage mainMsg = await CreateAndSendEmbedAsync($\"{description}\\nTo check a move: `!move info {PBEDataProvider.Instance.GetMoveName(Utils.RandomElement(PBEDataUtils.AllMoves)).English}`\", pkmn: mainPkmn, useUpperImage: false, fields: fields, userToSendTo: user);\n\n\t\t\t\tasync Task MoveReactionClicked(PBEMove move)\n\t\t\t\t{\n\t\t\t\t\tawait mainMsg.AddReactionAsync(_confirmationEmoji); // Put this here so it happens before RunTurn() takes its time\n\t\t\t\t\tPBEMoveTarget possibleTargets = mainPkmn.GetMoveTargets(move);\n\t\t\t\t\tPBETurnTarget targets;\n\t\t\t\t\tswitch (possibleTargets)\n\t\t\t\t\t{\n\t\t\t\t\t\tcase PBEMoveTarget.All:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttargets = PBETurnTarget.AllyCenter | PBETurnTarget.FoeCenter;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcase PBEMoveTarget.AllFoes:\n\t\t\t\t\t\tcase PBEMoveTarget.AllFoesSurrounding:\n\t\t\t\t\t\tcase PBEMoveTarget.AllSurrounding:\n\t\t\t\t\t\tcase PBEMoveTarget.RandomFoeSurrounding:\n\t\t\t\t\t\tcase PBEMoveTarget.SingleFoeSurrounding:\n\t\t\t\t\t\tcase PBEMoveTarget.SingleNotSelf:\n\t\t\t\t\t\tcase PBEMoveTarget.SingleSurrounding:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttargets = PBETurnTarget.FoeCenter;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcase PBEMoveTarget.AllTeam:\n\t\t\t\t\t\tcase PBEMoveTarget.Self:\n\t\t\t\t\t\tcase PBEMoveTarget.SelfOrAllySurrounding:\n\t\t\t\t\t\tcase PBEMoveTarget.SingleAllySurrounding:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttargets = PBETurnTarget.AllyCenter;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tdefault: throw new InvalidDataException(nameof(possibleTargets));\n\t\t\t\t\t}\n\n\t\t\t\t\ttrainer.SelectActionsIfValid(out _, new PBETurnAction(mainPkmn, move, targets));\n\t\t\t\t}\n\n\t\t\t\tPBEMove[] usableMoves = mainPkmn.GetUsableMoves();\n\t\t\t\tfor (int i = 0; i < usableMoves.Length; i++)\n\t\t\t\t{\n\t\t\t\t\tPBEMove move = usableMoves[i]; // move must be evaluated before it reaches the lambda\n\t\t\t\t\tEmote emoji = _moveEmotes[i][mainPkmn.GetMoveType(move)];\n\t\t\t\t\treactionsToAdd.Add((mainMsg, emoji));\n\t\t\t\t\tReactionHandler.AddListener(user, mainMsg, emoji, () => MoveReactionClicked(move));\n\t\t\t\t}\n\n\t\t\t\t// All listeners are added, so now we can send the reactions\n\t\t\t\tforeach ((IUserMessage Message, IEmote Reaction) in reactionsToAdd)\n\t\t\t\t{\n\t\t\t\t\tawait Message.AddReactionAsync(Reaction);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcase PBESwitchInRequestPacket sirp:\n\t\t\t{\n\t\t\t\tPBETrainer trainer = sirp.Trainer;\n\t\t\t\tList<PBEBattlePokemon> switches = trainer.Party.FindAll(p => p.CanBattle);\n\t\t\t\tif (switches.Count == 1)\n\t\t\t\t{\n\t\t\t\t\ttrainer.SelectSwitchesIfValid(out _, new PBESwitchIn(switches[0], PBEFieldPosition.Center));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tBattler battler = GetBattler(trainer);\n\t\t\t\tSocketUser? user = battler.User;\n\t\t\t\tif (user is null) // PBEAI\n\t\t\t\t{\n\t\t\t\t\tbattler.AI!.CreateSwitches();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tushort curTurn = _battle.TurnNumber;\n\t\t\t\tif (_lastSwitchinsTurn != curTurn)\n\t\t\t\t{\n\t\t\t\t\tawait SendQueuedMessages();\n\t\t\t\t\tawait SendActiveBattlerEmbeds();\n\t\t\t\t\t_lastSwitchinsTurn = curTurn;\n\t\t\t\t}\n\n\t\t\t\tasync Task SwitchReactionClicked(IUserMessage switchMsg, PBEBattlePokemon switchPkmn)\n\t\t\t\t{\n\t\t\t\t\tawait switchMsg.AddReactionAsync(_confirmationEmoji); // Put this here so it happens before RunTurn() takes its time\n\t\t\t\t\ttrainer.SelectSwitchesIfValid(out _, new PBESwitchIn(switchPkmn, PBEFieldPosition.Center));\n\t\t\t\t}\n\n\t\t\t\tvar reactionsToAdd = new (IUserMessage Message, IEmote Reaction)[switches.Count];\n\t\t\t\tfor (int i = 0; i < switches.Count; i++)\n\t\t\t\t{\n\t\t\t\t\tPBEBattlePokemon switchPkmn = switches[i];\n\t\t\t\t\tCreatePokemonEmbed(switchPkmn, false, out string description, out EmbedFieldBuilder[] fields);\n\t\t\t\t\tIUserMessage switchMsg = await CreateAndSendEmbedAsync(description, messageText: i == 0 ? Separator : string.Empty, pkmn: switchPkmn, useUpperImage: true, fields: fields, userToSendTo: user);\n\t\t\t\t\treactionsToAdd[i] = (switchMsg, _switchEmoji);\n\t\t\t\t\tReactionHandler.AddListener(user, switchMsg, _switchEmoji, () => SwitchReactionClicked(switchMsg, switchPkmn));\n\t\t\t\t}\n\n\t\t\t\t// All listeners are added, so now we can send the reactions\n\t\t\t\tfor (int i = 0; i < reactionsToAdd.Length; i++)\n\t\t\t\t{\n\t\t\t\t\t(IUserMessage Message, IEmote Reaction) = reactionsToAdd[i];\n\t\t\t\t\tawait Message.AddReactionAsync(Reaction);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcase PBEWeatherPacket wp:\n\t\t\t{\n\t\t\t\tif (wp.WeatherAction == PBEWeatherAction.Added || wp.WeatherAction == PBEWeatherAction.Ended)\n\t\t\t\t{\n\t\t\t\t\tSetEmbedTitle();\n\t\t\t\t}\n\t\t\t\tbreak; // Continue to the default message\n\t\t\t}\n\t\t\tcase PBETurnBeganPacket tbp:\n\t\t\t{\n\t\t\t\tawait SendQueuedMessages();\n\t\t\t\tSetEmbedTitle();\n\t\t\t\tawait SendActiveBattlerEmbeds();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcase PBEBattleResultPacket brp: // We do not want the default message since it uses the combined name\n\t\t\t{\n\t\t\t\tstring m;\n\t\t\t\tswitch (brp.BattleResult)\n\t\t\t\t{\n\t\t\t\t\tcase PBEBattleResult.Team0Win: m = \"{0} defeated {1}!\"; break;\n\t\t\t\t\tcase PBEBattleResult.Team1Win: m = \"{1} defeated {0}!\"; break;\n\t\t\t\t\tdefault: throw new InvalidDataException(nameof(brp.BattleResult));\n\t\t\t\t}\n\t\t\t\t_queuedMessages.AppendLine(string.Format(m, _battler0.GetName(), _battler1.GetName()));\n\t\t\t\tawait SendQueuedMessages();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t// Get default message\n\t\tstring? message = PBEBattle.GetDefaultMessage(_battle, packet, trainerNameFunc: GetTrainerName, teamNameFunc: GetTeamName);\n\t\tif (string.IsNullOrEmpty(message))\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\t_queuedMessages.AppendLine(message);\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineDiscord/BattleContext_Constants.cs",
    "content": "﻿using Discord;\nusing Kermalis.PokemonBattleEngine.Data;\nusing System.Collections.Generic;\n\nnamespace Kermalis.PokemonBattleEngineDiscord;\n\ninternal sealed partial class BattleContext\n{\n\tprivate const string Separator = \"**--------------------**\";\n\tprivate static readonly Emoji _shinyEmoji = new(\"✨\");\n\tprivate static readonly Emoji _switchEmoji = new(\"😼\");\n\tprivate static readonly Emoji _confirmationEmoji = new(\"👍\");\n\tprivate static readonly Dictionary<PBEType, Emote>[] _moveEmotes = new Dictionary<PBEType, Emote>[PBESettings.DefaultNumMoves]\n\t{\n\t\tnew Dictionary<PBEType, Emote>\n\t\t{\n\t\t\t{ PBEType.None, Emote.Parse(\"<:Normal1:708768399538520095>\") },\n\t\t\t{ PBEType.Bug, Emote.Parse(\"<:Bug1:708768297889300503>\") },\n\t\t\t{ PBEType.Dark, Emote.Parse(\"<:Dark1:708768298732355667>\") },\n\t\t\t{ PBEType.Dragon, Emote.Parse(\"<:Dragon1:708768299235672084>\") },\n\t\t\t{ PBEType.Electric, Emote.Parse(\"<:Electric1:708768298103341073>\") },\n\t\t\t{ PBEType.Fighting, Emote.Parse(\"<:Fighting1:708768297457549472>\") },\n\t\t\t{ PBEType.Fire, Emote.Parse(\"<:Fire1:708768298782818394>\") },\n\t\t\t{ PBEType.Flying, Emote.Parse(\"<:Flying1:708768298841669713>\") },\n\t\t\t{ PBEType.Ghost, Emote.Parse(\"<:Ghost1:708768298598400081>\") },\n\t\t\t{ PBEType.Grass, Emote.Parse(\"<:Grass1:708768299219025950>\") },\n\t\t\t{ PBEType.Ground, Emote.Parse(\"<:Ground1:708768298317119548>\") },\n\t\t\t{ PBEType.Ice, Emote.Parse(\"<:Ice1:708768397214744627>\") },\n\t\t\t{ PBEType.Normal, Emote.Parse(\"<:Normal1:708768399538520095>\") },\n\t\t\t{ PBEType.Poison, Emote.Parse(\"<:Poison1:708768399450308680>\") },\n\t\t\t{ PBEType.Psychic, Emote.Parse(\"<:Psychic1:708768399915876462>\") },\n\t\t\t{ PBEType.Rock, Emote.Parse(\"<:Rock1:708768399836315670>\") },\n\t\t\t{ PBEType.Steel, Emote.Parse(\"<:Steel1:708768399400108083>\") },\n\t\t\t{ PBEType.Water, Emote.Parse(\"<:Water1:708768400389963877>\") }\n\t\t},\n\t\tnew Dictionary<PBEType, Emote>\n\t\t{\n\t\t\t{ PBEType.None, Emote.Parse(\"<:Normal2:708768399496314880>\") },\n\t\t\t{ PBEType.Bug, Emote.Parse(\"<:Bug2:708768298665246791>\") },\n\t\t\t{ PBEType.Dark, Emote.Parse(\"<:Dark2:708768298992533586>\") },\n\t\t\t{ PBEType.Dragon, Emote.Parse(\"<:Dragon2:708768298883350570>\") },\n\t\t\t{ PBEType.Electric, Emote.Parse(\"<:Electric2:708768297960603708>\") },\n\t\t\t{ PBEType.Fighting, Emote.Parse(\"<:Fighting2:708768297654681620>\") },\n\t\t\t{ PBEType.Fire, Emote.Parse(\"<:Fire2:708768298870767616>\") },\n\t\t\t{ PBEType.Flying, Emote.Parse(\"<:Flying2:708768298921099325>\") },\n\t\t\t{ PBEType.Ghost, Emote.Parse(\"<:Ghost2:708768299072356402>\") },\n\t\t\t{ PBEType.Grass, Emote.Parse(\"<:Grass2:708768298900127845>\") },\n\t\t\t{ PBEType.Ground, Emote.Parse(\"<:Ground2:708768298338353232>\") },\n\t\t\t{ PBEType.Ice, Emote.Parse(\"<:Ice2:708768397336510534>\") },\n\t\t\t{ PBEType.Normal, Emote.Parse(\"<:Normal2:708768399496314880>\") },\n\t\t\t{ PBEType.Poison, Emote.Parse(\"<:Poison2:708768399647440907>\") },\n\t\t\t{ PBEType.Psychic, Emote.Parse(\"<:Psychic2:708768399441788938>\") },\n\t\t\t{ PBEType.Rock, Emote.Parse(\"<:Rock2:708768399442051114>\") },\n\t\t\t{ PBEType.Steel, Emote.Parse(\"<:Steel2:708768399873933325>\") },\n\t\t\t{ PBEType.Water, Emote.Parse(\"<:Water2:708768398829682759>\") }\n\t\t},\n\t\tnew Dictionary<PBEType, Emote>\n\t\t{\n\t\t\t{ PBEType.None, Emote.Parse(\"<:Normal3:708768399404302426>\") },\n\t\t\t{ PBEType.Bug, Emote.Parse(\"<:Bug3:708768298958979233>\") },\n\t\t\t{ PBEType.Dark, Emote.Parse(\"<:Dark3:708768298690674739>\") },\n\t\t\t{ PBEType.Dragon, Emote.Parse(\"<:Dragon3:708768300045434911>\") },\n\t\t\t{ PBEType.Electric, Emote.Parse(\"<:Electric3:708768298057203823>\") },\n\t\t\t{ PBEType.Fighting, Emote.Parse(\"<:Fighting3:708768297679847474>\") },\n\t\t\t{ PBEType.Fire, Emote.Parse(\"<:Fire3:708768298744938546>\") },\n\t\t\t{ PBEType.Flying, Emote.Parse(\"<:Flying3:708768298480828557>\") },\n\t\t\t{ PBEType.Ghost, Emote.Parse(\"<:Ghost3:708768298845601822>\") },\n\t\t\t{ PBEType.Grass, Emote.Parse(\"<:Grass3:708768298858184785>\") },\n\t\t\t{ PBEType.Ground, Emote.Parse(\"<:Ground3:708768298312925244>\") },\n\t\t\t{ PBEType.Ice, Emote.Parse(\"<:Ice3:708768396644450355>\") },\n\t\t\t{ PBEType.Normal, Emote.Parse(\"<:Normal3:708768399404302426>\") },\n\t\t\t{ PBEType.Poison, Emote.Parse(\"<:Poison3:708768399681126420>\") },\n\t\t\t{ PBEType.Psychic, Emote.Parse(\"<:Psychic3:708768401123836015>\") },\n\t\t\t{ PBEType.Rock, Emote.Parse(\"<:Rock3:708768399274016838>\") },\n\t\t\t{ PBEType.Steel, Emote.Parse(\"<:Steel3:708768399383330836>\") },\n\t\t\t{ PBEType.Water, Emote.Parse(\"<:Water3:708768399936978977>\") }\n\t\t},\n\t\tnew Dictionary<PBEType, Emote>\n\t\t{\n\t\t\t{ PBEType.None, Emote.Parse(\"<:Normal4:708768399332999240>\") },\n\t\t\t{ PBEType.Bug, Emote.Parse(\"<:Bug4:708768298883612792>\") },\n\t\t\t{ PBEType.Dark, Emote.Parse(\"<:Dark4:708768298665508906>\") },\n\t\t\t{ PBEType.Dragon, Emote.Parse(\"<:Dragon4:708768298627498066>\") },\n\t\t\t{ PBEType.Electric, Emote.Parse(\"<:Electric4:708768297918660698>\") },\n\t\t\t{ PBEType.Fighting, Emote.Parse(\"<:Fighting4:708768297532915785>\") },\n\t\t\t{ PBEType.Fire, Emote.Parse(\"<:Fire4:708768298380034152>\") },\n\t\t\t{ PBEType.Flying, Emote.Parse(\"<:Flying4:708768298795270245>\") },\n\t\t\t{ PBEType.Ghost, Emote.Parse(\"<:Ghost4:708768298841669672>\") },\n\t\t\t{ PBEType.Grass, Emote.Parse(\"<:Grass4:708768298875093084>\") },\n\t\t\t{ PBEType.Ground, Emote.Parse(\"<:Ground4:708768298468114512>\") },\n\t\t\t{ PBEType.Ice, Emote.Parse(\"<:Ice4:708768398821163009>\") },\n\t\t\t{ PBEType.Normal, Emote.Parse(\"<:Normal4:708768399332999240>\") },\n\t\t\t{ PBEType.Poison, Emote.Parse(\"<:Poison4:708768400020602910>\") },\n\t\t\t{ PBEType.Psychic, Emote.Parse(\"<:Psychic4:708768399328673803>\") },\n\t\t\t{ PBEType.Rock, Emote.Parse(\"<:Rock4:708768399345451009>\") },\n\t\t\t{ PBEType.Steel, Emote.Parse(\"<:Steel4:708768399161032735>\") },\n\t\t\t{ PBEType.Water, Emote.Parse(\"<:Water4:708768398691139656>\") }\n\t\t}\n\t};\n}\n"
  },
  {
    "path": "PokemonBattleEngineDiscord/BotCommands.cs",
    "content": "﻿using Discord;\nusing Discord.Commands;\nusing Discord.WebSocket;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Data.Utils;\nusing Kermalis.PokemonBattleEngine.DefaultData;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text.RegularExpressions;\nusing System.Threading.Tasks;\n\nnamespace Kermalis.PokemonBattleEngineDiscord;\n\npublic sealed class BotCommands : ModuleBase<SocketCommandContext>\n{\n\t[Group(\"ability\")]\n\tpublic sealed class AbilityCommands : ModuleBase<SocketCommandContext>\n\t{\n\t\t[Command(\"info\")]\n\t\t[Alias(\"data\")]\n\t\tpublic async Task Info([Remainder] string abilityName)\n\t\t{\n\t\t\tif (!PBEDataProvider.Instance.GetAbilityByName(abilityName, out PBEAbility? nAbility) || nAbility.Value == PBEAbility.None)\n\t\t\t{\n\t\t\t\tawait Context.Channel.SendMessageAsync($\"{Context.User.Mention} ― Invalid ability!\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tPBEAbility ability = nAbility.Value;\n\t\t\t\tEmbedBuilder embed = new EmbedBuilder()\n\t\t\t\t\t\t.WithAuthor(Context.User)\n\t\t\t\t\t\t.WithColor(Utils.RandomColor())\n\t\t\t\t\t\t.WithTitle(PBEDataProvider.Instance.GetAbilityName(ability).English)\n\t\t\t\t\t\t.WithUrl(Utils.URL)\n\t\t\t\t\t\t.WithDescription(PBEDefaultDataProvider.Instance.GetAbilityDescription(ability).English.Replace('\\n', ' '));\n\t\t\t\tawait Context.Channel.SendMessageAsync(string.Empty, embed: embed.Build());\n\t\t\t}\n\t\t}\n\t}\n\n\t[Command(\"accept\", true)]\n\tpublic async Task AcceptChallenge()\n\t{\n\t\tawait Matchmaking.AcceptChallenge(Context);\n\t}\n\n\t[Command(\"challenge\")]\n\t[Alias(\"battle\", \"duel\", \"fight\")]\n\tpublic async Task ChallengeUser(SocketUser battler2)\n\t{\n\t\tawait Matchmaking.ChallengeUser(Context, battler2);\n\t}\n\n\t[Command(\"challengeai\", true)]\n\t[Alias(\"battleai\", \"duelai\", \"fightai\")]\n\tpublic async Task ChallengeAI()\n\t{\n\t\tawait Matchmaking.ChallengeAI(Context);\n\t}\n\n\t[Command(\"dontdelete\", true)]\n\t[RequireUserPermission(GuildPermission.ManageChannels)]\n\tpublic async Task DontDelete()\n\t{\n\t\tawait ChannelHandler.DontDelete(Context);\n\t}\n\n\t[Command(\"forfeit\", true)]\n\t[Alias(\"ff\", \"giveup\", \"surrender\", \"throwinthetowel\")]\n\tpublic async Task Forfeit()\n\t{\n\t\tawait Matchmaking.Forfeit(Context);\n\t}\n\n\t[Command(\"help\", true)]\n\t[Alias(\"helpme\", \"pbehelp\", \"tf\", \"wtf\")]\n\tpublic async Task Help()\n\t{\n\t\tEmbedBuilder embed = new EmbedBuilder()\n\t\t\t\t\t\t.WithAuthor(Context.User)\n\t\t\t\t\t\t.WithColor(Utils.RandomColor())\n\t\t\t\t\t\t.WithTitle(\"PokémonBattleEngine ― List of Commands\")\n\t\t\t\t\t\t.WithUrl(Utils.URL)\n\n\t\t\t.AddField(\"***Battle Commands:***\", \"*These commands control battles in **PokémonBattleEngine**.*\")\n\t\t\t.AddField(\"`!accept`\", \"Accepts a pending challenge.\")\n\t\t\t.AddField(\"`!challengeAI`\", \"Challenges the default AI. It's pretty bad, but it can wreck you.\")\n\t\t\t.AddField(\"`!challenge [user]`\", \"Challenges a specific user. User must have DMs open. *Example:* `!challenge @Kermalis`\")\n\t\t\t.AddField(\"`!forfeit`\", \"Forfeits your current battle.\")\n\n\t\t\t.AddField(\"***Info Commands:***\", \"*These commands give information about specific attributes of Pokémon. Names must be in English, French, German, Italian, Japanese, Korean, or Spanish.*\")\n\t\t\t.AddField(\"`!ability info [ability]`\", \"Gives details about an ability. *Example:* `!ability info Mold Breaker`\")\n\t\t\t.AddField(\"`!item info [item]`\", \"Gives details about an item or berry. *Example:* `!item info Nanab Berry`\")\n\t\t\t.AddField(\"`!move info [move]`\", \"Gives details about a move. *Example:* `!move info Sing`\")\n\t\t\t.AddField(\"`!species info [species]`\", \"Gives details about a species (base form). *Example:* `!species info Piplup`\")\n\t\t\t.AddField(\"`!species info [species] [(form)]`\", \"Gives details about a species. *Example:* `!species info Shaymin (Sky Forme)`\")\n\t\t\t.AddField(\"`!type info [type]`\", \"Gives details about a type. *Example:* `!type info Dragon`\");\n\n\t\tawait Context.Channel.SendMessageAsync(string.Empty, embed: embed.Build());\n\t}\n\n\t[Group(\"item\")]\n\t[Alias(\"helditem\")]\n\tpublic sealed class ItemCommands : ModuleBase<SocketCommandContext>\n\t{\n\t\t[Command(\"info\")]\n\t\t[Alias(\"data\")]\n\t\tpublic async Task Info([Remainder] string itemName)\n\t\t{\n\t\t\tif (!PBEDataProvider.Instance.GetItemByName(itemName, out PBEItem? nItem) || nItem.Value == PBEItem.None)\n\t\t\t{\n\t\t\t\tawait Context.Channel.SendMessageAsync($\"{Context.User.Mention} ― Invalid item!\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tPBEItem item = nItem.Value;\n\t\t\t\tIPBEItemData iData = PBEDataProvider.Instance.GetItemData(item);\n\t\t\t\tColor color;\n\t\t\t\tif (PBEDataProvider.Instance.TryGetBerryData(item, out IPBEBerryData? bData))\n\t\t\t\t{\n\t\t\t\t\tcolor = Utils.TypeColors[bData.NaturalGiftType];\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tcolor = Utils.RandomColor();\n\t\t\t\t}\n\t\t\t\tEmbedBuilder embed = new EmbedBuilder()\n\t\t\t\t\t\t.WithAuthor(Context.User)\n\t\t\t\t\t\t.WithColor(color)\n\t\t\t\t\t\t.WithTitle(PBEDataProvider.Instance.GetItemName(item).English)\n\t\t\t\t\t\t.WithUrl(Utils.URL)\n\t\t\t\t\t\t.WithDescription(PBEDefaultDataProvider.Instance.GetItemDescription(item).English.Replace('\\n', ' '));\n\t\t\t\tif (iData.FlingPower > 0)\n\t\t\t\t{\n\t\t\t\t\tembed.AddField(\"Fling Power\", iData.FlingPower, true);\n\t\t\t\t}\n\t\t\t\tif (bData is not null)\n\t\t\t\t{\n\t\t\t\t\tembed.AddField(\"Natural Gift Power\", bData.NaturalGiftPower, true);\n\t\t\t\t\tembed.AddField(\"Natural Gift Type\", Utils.TypeEmotes[bData.NaturalGiftType], true);\n\t\t\t\t\tif (bData.Bitterness > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tembed.AddField(\"Bitterness\", bData.Bitterness, true);\n\t\t\t\t\t}\n\t\t\t\t\tif (bData.Dryness > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tembed.AddField(\"Dryness\", bData.Dryness, true);\n\t\t\t\t\t}\n\t\t\t\t\tif (bData.Sourness > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tembed.AddField(\"Sourness\", bData.Sourness, true);\n\t\t\t\t\t}\n\t\t\t\t\tif (bData.Spicyness > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tembed.AddField(\"Spicyness\", bData.Spicyness, true);\n\t\t\t\t\t}\n\t\t\t\t\tif (bData.Sweetness > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tembed.AddField(\"Sweetness\", bData.Sweetness, true);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tawait Context.Channel.SendMessageAsync(string.Empty, embed: embed.Build());\n\t\t\t}\n\t\t}\n\t}\n\n\t[Group(\"move\")]\n\t[Alias(\"attack\")]\n\tpublic sealed class MoveCommands : ModuleBase<SocketCommandContext>\n\t{\n\t\t[Command(\"info\")]\n\t\t[Alias(\"data\")]\n\t\tpublic async Task Info([Remainder] string moveName)\n\t\t{\n\t\t\tif (!PBEDataProvider.Instance.GetMoveByName(moveName, out PBEMove? nMove) || nMove.Value == PBEMove.None)\n\t\t\t{\n\t\t\t\tawait Context.Channel.SendMessageAsync($\"{Context.User.Mention} ― Invalid move!\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tPBEMove move = nMove.Value;\n\t\t\t\tmoveName = PBEDataProvider.Instance.GetMoveName(move).English;\n\t\t\t\tIPBEMoveData mData = PBEDataProvider.Instance.GetMoveData(move);\n\t\t\t\tEmbedBuilder embed = new EmbedBuilder()\n\t\t\t\t\t\t.WithAuthor(Context.User)\n\t\t\t\t\t\t.WithColor(Utils.TypeColors[mData.Type])\n\t\t\t\t\t\t.WithTitle(moveName)\n\t\t\t\t\t\t.WithUrl(Utils.URL)\n\t\t\t\t\t\t.WithDescription(PBEDefaultDataProvider.Instance.GetMoveDescription(move).English.Replace('\\n', ' '))\n\t\t\t\t\t\t.AddField(\"Type\", Utils.TypeEmotes[mData.Type], true)\n\t\t\t\t\t\t.AddField(\"Category\", mData.Category, true)\n\t\t\t\t\t\t.AddField(\"Priority\", mData.Priority, true)\n\t\t\t\t\t\t.AddField(\"PP\", Math.Max(1, mData.PPTier * PBESettings.DefaultPPMultiplier), true)\n\t\t\t\t\t\t.AddField(\"Power\", mData.Power == 0 ? \"―\" : mData.Power.ToString(), true)\n\t\t\t\t\t\t.AddField(\"Accuracy\", mData.Accuracy == 0 ? \"―\" : mData.Accuracy.ToString(), true)\n\t\t\t\t\t\t.AddField(\"Targets\", mData.Targets, true)\n\t\t\t\t\t\t.AddField(\"Flags\", mData.Flags, true);\n\t\t\t\tswitch (mData.Effect)\n\t\t\t\t{\n\t\t\t\t\tcase PBEMoveEffect.Recoil: embed.AddField(\"Recoil\", $\"1/{mData.EffectParam} damage dealt\"); break;\n\t\t\t\t\tcase PBEMoveEffect.Recoil__10PercentBurn: embed.AddField(\"Recoil\", $\"1/{mData.EffectParam} damage dealt\"); break; // TODO: Burn chance\n\t\t\t\t\tcase PBEMoveEffect.Recoil__10PercentParalyze: embed.AddField(\"Recoil\", $\"1/{mData.EffectParam} damage dealt\"); break; // TODO: Paralyze chance\n\t\t\t\t\tcase PBEMoveEffect.Struggle: embed.AddField(\"Recoil\", \"1/4 user's max HP\"); break;\n\t\t\t\t\tcase PBEMoveEffect.TODOMOVE: embed.AddField(\"**ATTENTION**\", $\"{moveName} is not yet implemented in Pokémon Battle Engine\"); break;\n\t\t\t\t}\n\t\t\t\tawait Context.Channel.SendMessageAsync(string.Empty, embed: embed.Build());\n\t\t\t}\n\t\t}\n\t}\n\n\t[Group(\"pokemon\")]\n\t[Alias(\"pokémon\", \"species\", \"pkmn\", \"poke\", \"poké\")]\n\tpublic sealed class SpeciesCommands : ModuleBase<SocketCommandContext>\n\t{\n\t\t[Command(\"info\")]\n\t\t[Alias(\"data\")]\n\t\tpublic async Task Info([Remainder] string input)\n\t\t{\n\t\t\t// Inputs for forms should be like \"Giratina (Origin Forme)\"\n\t\t\tMatch m = Regex.Match(input, @\"^(\\S+) \\((.+)\\)$\");\n\t\t\tstring speciesName;\n\t\t\tstring? formName;\n\t\t\tif (m.Success)\n\t\t\t{\n\t\t\t\tspeciesName = m.Groups[1].Value;\n\t\t\t\tformName = m.Groups[2].Value;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tspeciesName = input;\n\t\t\t\tformName = null;\n\t\t\t}\n\t\t\tif (!PBEDataProvider.Instance.GetSpeciesByName(speciesName, out PBESpecies? nSpecies))\n\t\t\t{\n\t\t\t\tawait Context.Channel.SendMessageAsync($\"{Context.User.Mention} ― Invalid species!\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tPBESpecies species = nSpecies.Value;\n\t\t\tspeciesName = PBEDataProvider.Instance.GetSpeciesName(species).English;\n\t\t\tPBEForm form;\n\t\t\tif (formName is null)\n\t\t\t{\n\t\t\t\tform = 0;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (!PBEDataProvider.Instance.GetFormByName(species, formName, out PBEForm? nForm))\n\t\t\t\t{\n\t\t\t\t\tIReadOnlyList<PBEForm> forms = PBEDataUtils.GetForms(species, false);\n\t\t\t\t\tstring str = $\"{Context.User.Mention} ― Invalid form for {speciesName}\";\n\t\t\t\t\tif (forms.Count > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tstr += \", valid forms are:\\n**\" + string.Join(\"\\n\", forms.Select(f => PBEDataProvider.Instance.GetFormName(species, f).English)) + \"**\";\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tstr += \"! It has no forms!\";\n\t\t\t\t\t}\n\t\t\t\t\tawait Context.Channel.SendMessageAsync(str);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tform = nForm.Value;\n\t\t\t}\n\t\t\tformName = PBEDataUtils.HasForms(species, false) ? $\" ({PBEDataProvider.Instance.GetFormName(species, form).English})\" : string.Empty;\n\t\t\tIPBEPokemonData pData = PBEDataProvider.Instance.GetPokemonData(species, form);\n\t\t\tstring types = $\"{Utils.TypeEmotes[pData.Type1]}\";\n\t\t\tif (pData.Type2 != PBEType.None)\n\t\t\t{\n\t\t\t\ttypes += $\" {Utils.TypeEmotes[pData.Type2]}\";\n\t\t\t}\n\t\t\tstring ratio;\n\t\t\tswitch (pData.GenderRatio)\n\t\t\t{\n\t\t\t\tcase PBEGenderRatio.M7_F1: ratio = \"87.5% Male, 12.5% Female\"; break;\n\t\t\t\tcase PBEGenderRatio.M3_F1: ratio = \"75% Male, 25% Female\"; break;\n\t\t\t\tcase PBEGenderRatio.M1_F1: ratio = \"50% Male, 50% Female\"; break;\n\t\t\t\tcase PBEGenderRatio.M1_F3: ratio = \"25% Male, 75% Female\"; break;\n\t\t\t\tcase PBEGenderRatio.M0_F1: ratio = \"100% Female\"; break;\n\t\t\t\tcase PBEGenderRatio.M1_F0: ratio = \"100% Male\"; break;\n\t\t\t\tcase PBEGenderRatio.M0_F0: ratio = \"Genderless Species\"; break;\n\t\t\t\tdefault: throw new InvalidDataException(nameof(pData.GenderRatio));\n\t\t\t}\n\t\t\tstring weaknesses = string.Empty,\n\t\t\t\t\t\tresistances = string.Empty,\n\t\t\t\t\t\timmunities = string.Empty;\n\t\t\tfor (PBEType atk = PBEType.None + 1; atk < PBEType.MAX; atk++)\n\t\t\t{\n\t\t\t\tfloat d = PBETypeEffectiveness.GetEffectiveness(atk, pData);\n\t\t\t\tif (d <= 0)\n\t\t\t\t{\n\t\t\t\t\tif (immunities != string.Empty)\n\t\t\t\t\t{\n\t\t\t\t\t\timmunities += ' ';\n\t\t\t\t\t}\n\t\t\t\t\timmunities += Utils.TypeEmotes[atk];\n\t\t\t\t}\n\t\t\t\telse if (d < 1)\n\t\t\t\t{\n\t\t\t\t\tif (resistances != string.Empty)\n\t\t\t\t\t{\n\t\t\t\t\t\tresistances += ' ';\n\t\t\t\t\t}\n\t\t\t\t\tresistances += Utils.TypeEmotes[atk];\n\t\t\t\t}\n\t\t\t\tif (d > 1)\n\t\t\t\t{\n\t\t\t\t\tif (weaknesses != string.Empty)\n\t\t\t\t\t{\n\t\t\t\t\t\tweaknesses += ' ';\n\t\t\t\t\t}\n\t\t\t\t\tweaknesses += Utils.TypeEmotes[atk];\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (weaknesses == string.Empty)\n\t\t\t{\n\t\t\t\tweaknesses = \"No Weaknesses\";\n\t\t\t}\n\t\t\tif (resistances == string.Empty)\n\t\t\t{\n\t\t\t\tresistances = \"No Resistances\";\n\t\t\t}\n\t\t\tif (immunities == string.Empty)\n\t\t\t{\n\t\t\t\timmunities = \"No Immunities\";\n\t\t\t}\n\n\t\t\tEmbedBuilder embed = new EmbedBuilder()\n\t\t\t\t\t\t.WithAuthor(Context.User)\n\t\t\t\t\t\t.WithColor(Utils.GetColor(pData.Type1, pData.Type2))\n\t\t\t\t\t\t.WithTitle($\"{speciesName}{formName} ― {PBEDefaultDataProvider.Instance.GetSpeciesCategory(species).English}\")\n\t\t\t\t\t\t.WithUrl(Utils.URL)\n\t\t\t\t\t\t.WithDescription(PBEDefaultDataProvider.Instance.GetSpeciesEntry(species).English.Replace('\\n', ' '))\n\t\t\t\t\t\t.AddField(\"Types\", types, true)\n\t\t\t\t\t\t.AddField(\"Gender Ratio\", ratio, true)\n\t\t\t\t\t\t.AddField(\"Weight\", $\"{pData.Weight:N1} kg\", true)\n\t\t\t\t\t\t.AddField(\"Abilities\", string.Join(\", \", pData.Abilities.Select(a => PBEDataProvider.Instance.GetAbilityName(a).English)), false)\n\t\t\t\t\t\t.AddField(\"HP\", pData.BaseStats.HP, true)\n\t\t\t\t\t\t.AddField(\"Attack\", pData.BaseStats.Attack, true)\n\t\t\t\t\t\t.AddField(\"Defense\", pData.BaseStats.Defense, true)\n\t\t\t\t\t\t.AddField(\"Special Attack\", pData.BaseStats.SpAttack, true)\n\t\t\t\t\t\t.AddField(\"Special Defense\", pData.BaseStats.SpDefense, true)\n\t\t\t\t\t\t.AddField(\"Speed\", pData.BaseStats.Speed, true)\n\t\t\t\t\t\t.AddField(\"Type Weaknesses\", weaknesses, true)\n\t\t\t\t\t\t.AddField(\"Type Resistances\", resistances, true)\n\t\t\t\t\t\t.AddField(\"Type Immunities\", immunities, true)\n\t\t\t\t\t\t.WithImageUrl(Utils.GetPokemonSprite(species, form, PBEDataProvider.GlobalRandom.RandomShiny(), PBEDataProvider.GlobalRandom.RandomGender(pData.GenderRatio), false, false));\n\t\t\tawait Context.Channel.SendMessageAsync(string.Empty, embed: embed.Build());\n\t\t}\n\t}\n\n\t[Group(\"type\")]\n\tpublic sealed class TypeCommands : ModuleBase<SocketCommandContext>\n\t{\n\t\tprivate const string _tableStart = \"⬛\";\n\t\tprivate const string _offense = \"🗡️\";\n\t\tprivate const string _defense = \"🛡️\";\n\n\t\tprivate const string _ineffective = \"❌\";\n\t\tprivate const string _notVeryEffective = \"♿\";\n\t\tprivate const string _effective = \"✅\";\n\t\tprivate const string _superEffective = \"💥\";\n\n\t\t[Command(\"info\")]\n\t\t[Alias(\"data, effectiveness, weaknesses\")]\n\t\tpublic async Task Info([Remainder] string typeName)\n\t\t{\n\t\t\tif (!PBEDataProvider.Instance.GetTypeByName(typeName, out PBEType? nType) || nType.Value == PBEType.None)\n\t\t\t{\n\t\t\t\tawait Context.Channel.SendMessageAsync($\"{Context.User.Mention} ― Invalid type!\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tPBEType type = nType.Value;\n\t\t\t\tstring description = $\"{_tableStart}{_tableStart}\";\n\t\t\t\t// Build columns\n\t\t\t\tfor (PBEType other = PBEType.None + 1; other < PBEType.MAX; other++)\n\t\t\t\t{\n\t\t\t\t\tdescription += $\"|{Utils.TypeEmotes[other]}\";\n\t\t\t\t}\n\t\t\t\t// Build rows\n\t\t\t\tfor (int i = 0; i < 2; i++)\n\t\t\t\t{\n\t\t\t\t\tbool doOffense = i == 0;\n\t\t\t\t\tdescription += $\"\\n{(doOffense ? _offense : _defense)}{Utils.TypeEmotes[type]}\";\n\t\t\t\t\tfor (PBEType other = PBEType.None + 1; other < PBEType.MAX; other++)\n\t\t\t\t\t{\n\t\t\t\t\t\tfloat d = PBETypeEffectiveness.GetEffectiveness(doOffense ? type : other, doOffense ? other : type);\n\t\t\t\t\t\tstring s;\n\t\t\t\t\t\tif (d <= 0) // (-infinity, 0]\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ts = _ineffective;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (d < 1) // (0, 1)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ts = _notVeryEffective;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (d == 1) // [1, 1]\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ts = _effective;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse // (1, infinity)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ts = _superEffective;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tdescription += $\"|{s}\";\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tEmbedBuilder embed = new EmbedBuilder()\n\t\t\t\t\t.WithAuthor(Context.User)\n\t\t\t\t\t.WithColor(Utils.TypeColors[type])\n\t\t\t\t\t.WithTitle(PBEDataProvider.Instance.GetTypeName(type).English)\n\t\t\t\t\t.WithUrl(Utils.URL)\n\t\t\t\t\t.WithDescription(description);\n\t\t\t\tawait Context.Channel.SendMessageAsync(string.Empty, embed: embed.Build());\n\t\t\t}\n\t\t}\n\n\t\t// BROKEN CUZ DISCORD CUTS OFF THE TABLE EVEN THOUGH YOU CAN SEND THE ENTIRE MESSAGE IN A CODE BLOCK\n\t\t/*[Command(\"chart\")]\n\t\t[Alias(\"table\")]\n\t\tpublic async Task Chart()\n\t\t{\n\t\t\tstring description = _tableStart;\n\t\t\t// Build columns\n\t\t\tfor (PBEType def = PBEType.None + 1; def < PBEType.MAX; def++)\n\t\t\t{\n\t\t\t\tdescription += $\"|{Utils.TypeEmotes[def]}\";\n\t\t\t}\n\t\t\t// Build rows\n\t\t\tfor (PBEType atk = PBEType.None + 1; atk < PBEType.MAX; atk++)\n\t\t\t{\n\t\t\t\tdescription += $\"\\n{Utils.TypeEmotes[atk]}\";\n\t\t\t\tfor (PBEType def = PBEType.None + 1; def < PBEType.MAX; def++)\n\t\t\t\t{\n\t\t\t\t\tfloat d = PBETypeEffectiveness.GetEffectiveness(atk, def);\n\t\t\t\t\tstring s;\n\t\t\t\t\tif (d <= 0) // (-infinity, 0]\n\t\t\t\t\t{\n\t\t\t\t\t\ts = _ineffective;\n\t\t\t\t\t}\n\t\t\t\t\telse if (d < 1) // (0, 1)\n\t\t\t\t\t{\n\t\t\t\t\t\ts = _notVeryEffective;\n\t\t\t\t\t}\n\t\t\t\t\telse if (d == 1) // [1, 1]\n\t\t\t\t\t{\n\t\t\t\t\t\ts = _effective;\n\t\t\t\t\t}\n\t\t\t\t\telse // (1, infinity)\n\t\t\t\t\t{\n\t\t\t\t\t\ts = _superEffective;\n\t\t\t\t\t}\n\t\t\t\t\tdescription += $\"|{s}\";\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tEmbedBuilder embed = new EmbedBuilder()\n\t\t\t\t.WithAuthor(Context.User)\n\t\t\t\t.WithColor(Utils.RandomColor())\n\t\t\t\t.WithTitle(\"Type Chart\")\n\t\t\t\t.WithUrl(Utils.URL)\n\t\t\t\t.WithDescription(description);\n\t\t\tawait Context.Channel.SendMessageAsync(string.Empty, embed: embed.Build());\n\t\t}*/\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineDiscord/ChannelHandler.cs",
    "content": "﻿using Discord;\nusing Discord.Commands;\nusing Discord.WebSocket;\nusing System;\nusing System.Collections.Generic;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace Kermalis.PokemonBattleEngineDiscord;\n\ninternal sealed class ChannelHandler\n{\n\tprivate const int NumMinutesUntilChannelDeleted = 60; // 60 is fine for the small userbase now, 15 is probably the target later (add customization)\n\tprivate const string CategoryName1 = \"Ongoing Pokémon Battles\";\n\tprivate const string CategoryName2 = \"Ended Pokémon Battles\";\n\tprivate static readonly object _channelHandlerLockObj = new();\n\tprivate static readonly Dictionary<IGuild, (ICategoryChannel, ICategoryChannel)> _categoryLookup = new(DiscordComparers.GuildComparer);\n\tprivate static readonly Dictionary<ITextChannel, (DateTime, IUserMessage)> _channelDeletion = new(DiscordComparers.ChannelComparer);\n\tprivate static Timer? _channelDeletionTimer;\n\n\tpublic static Task OnChannelDeleted(SocketChannel channel)\n\t{\n\t\tasync Task Do()\n\t\t{\n\t\t\tif (channel is ITextChannel tc)\n\t\t\t{\n\t\t\t\t_channelDeletion.Remove(tc);\n\t\t\t}\n\t\t\telse if (channel is ICategoryChannel gc)\n\t\t\t{\n\t\t\t\tIGuild guild = gc.Guild;\n\t\t\t\tif (_categoryLookup.TryGetValue(guild, out (ICategoryChannel, ICategoryChannel) tup))\n\t\t\t\t{\n\t\t\t\t\tif (tup.Item1.Id == gc.Id)\n\t\t\t\t\t{\n\t\t\t\t\t\ttup.Item1 = await CreateCategory(guild, CategoryName1);\n\t\t\t\t\t}\n\t\t\t\t\telse if (tup.Item2.Id == gc.Id)\n\t\t\t\t\t{\n\t\t\t\t\t\ttup.Item2 = await CreateCategory(guild, CategoryName2);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tlock (_channelHandlerLockObj)\n\t\t{\n\t\t\treturn Do();\n\t\t}\n\t}\n\tpublic static void OnLeftGuild(SocketGuild guild)\n\t{\n\t\tlock (_channelHandlerLockObj)\n\t\t{\n\t\t\t_categoryLookup.Remove(guild);\n\t\t}\n\t}\n\tpublic static void OnConnected()\n\t{\n\t\t_channelDeletionTimer?.Dispose();\n\t\t_channelDeletionTimer = new Timer(CheckChannelsForDeletion, null, 0, 1_000 * 60);\n\t}\n\tpublic static void OnDisconnected()\n\t{\n\t\t_channelDeletionTimer?.Dispose();\n\t\t_channelDeletionTimer = null;\n\t}\n\n\tprivate static async Task CreateCategories(IGuild guild)\n\t{\n\t\tif (!_categoryLookup.ContainsKey(guild))\n\t\t{\n\t\t\tIReadOnlyCollection<ICategoryChannel> all = await guild.GetCategoriesAsync();\n\t\t\tICategoryChannel? c1 = null, c2 = null;\n\t\t\tforeach (ICategoryChannel c in all)\n\t\t\t{\n\t\t\t\tif (c.Name == CategoryName1)\n\t\t\t\t{\n\t\t\t\t\tc1 = c;\n\t\t\t\t\tif (c2 is not null)\n\t\t\t\t\t{\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (c.Name == CategoryName2)\n\t\t\t\t{\n\t\t\t\t\tc2 = c;\n\t\t\t\t\tif (c1 is not null)\n\t\t\t\t\t{\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (c1 is null)\n\t\t\t{\n\t\t\t\tc1 = await CreateCategory(guild, CategoryName1);\n\t\t\t}\n\t\t\tif (c2 is null)\n\t\t\t{\n\t\t\t\tc2 = await CreateCategory(guild, CategoryName2);\n\t\t\t}\n\t\t\t_categoryLookup.Add(guild, (c1, c2));\n\t\t}\n\t}\n\tprivate static Task<ICategoryChannel> CreateCategory(IGuild guild, string name)\n\t{\n\t\treturn guild.CreateCategoryAsync(name);\n\t}\n\tpublic static async Task<ITextChannel> CreateChannel(IGuild guild, string name)\n\t{\n\t\tawait CreateCategories(guild);\n\t\treturn await guild.CreateTextChannelAsync(name, func: p => p.CategoryId = _categoryLookup[guild].Item1.Id);\n\t}\n\n\tprivate static Embed GetChannelDeletionEmbed(int num)\n\t{\n\t\tstring str = string.Format(\"This channel will be deleted in {0} minute{1}.\\nTo prevent this from happening, have a moderator use `!dontdelete`.\", num, num == 1 ? string.Empty : \"s\");\n\t\treturn new EmbedBuilder()\n\t\t\t.WithUrl(Utils.URL)\n\t\t\t.WithDescription(str)\n\t\t\t.WithColor(Utils.RandomColor())\n\t\t\t.Build();\n\t}\n\tprivate static void CheckChannelsForDeletion(object? state)\n\t{\n\t\tlock (_channelHandlerLockObj)\n\t\t{\n\t\t\tCheckChannelsForDeletion().GetAwaiter().GetResult();\n\t\t}\n\t}\n\tprivate static async Task CheckChannelsForDeletion()\n\t{\n\t\tDateTime dt = DateTime.Now;\n\t\tvar toRemove = new List<ITextChannel>();\n\t\tforeach (KeyValuePair<ITextChannel, (DateTime, IUserMessage)> kvp in _channelDeletion)\n\t\t{\n\t\t\tITextChannel chan = kvp.Key;\n\t\t\t(DateTime endTime, IUserMessage msg) = kvp.Value;\n\t\t\tint mins = (endTime - dt).Minutes;\n\t\t\tif (mins <= 0)\n\t\t\t{\n\t\t\t\ttoRemove.Add(chan);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tawait msg.ModifyAsync(p => p.Embed = GetChannelDeletionEmbed(mins));\n\t\t\t}\n\t\t}\n\t\tforeach (ITextChannel chan in toRemove)\n\t\t{\n\t\t\t_channelDeletion.Remove(chan);\n\t\t\tawait chan.DeleteAsync();\n\t\t}\n\t}\n\tpublic static Task DontDelete(SocketCommandContext context)\n\t{\n\t\tif (context.Channel is ITextChannel c)\n\t\t{\n\t\t\tasync Task Do()\n\t\t\t{\n\t\t\t\tif (_channelDeletion.TryGetValue(c, out (DateTime, IUserMessage) tup))\n\t\t\t\t{\n\t\t\t\t\t_channelDeletion.Remove(c);\n\t\t\t\t\tawait tup.Item2.DeleteAsync();\n\t\t\t\t\tawait c.SendMessageAsync($\"{context.User.Mention} ― This channel will no longer be deleted.\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tlock (_channelHandlerLockObj)\n\t\t\t{\n\t\t\t\treturn Do();\n\t\t\t}\n\t\t}\n\t\treturn Task.CompletedTask;\n\t}\n\tpublic static Task MarkChannelForDeletion(ITextChannel channel)\n\t{\n\t\tasync Task Do()\n\t\t{\n\t\t\tIUserMessage msg = await channel.SendMessageAsync(embed: GetChannelDeletionEmbed(NumMinutesUntilChannelDeleted));\n\t\t\t_channelDeletion.Add(channel, (DateTime.Now.AddMinutes(NumMinutesUntilChannelDeleted), msg));\n\t\t}\n\t\tlock (_channelHandlerLockObj)\n\t\t{\n\t\t\treturn Do();\n\t\t}\n\t}\n\tpublic static Task ChangeCategory(ITextChannel channel)\n\t{\n\t\tlock (_channelHandlerLockObj)\n\t\t{\n\t\t\treturn channel.ModifyAsync(p => p.CategoryId = _categoryLookup[channel.Guild].Item2.Id);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineDiscord/Matchmaking.cs",
    "content": "﻿using Discord;\nusing Discord.Commands;\nusing Discord.WebSocket;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Data.Legality;\nusing Kermalis.PokemonBattleEngine.DefaultData;\nusing System;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\n\nnamespace Kermalis.PokemonBattleEngineDiscord;\n\ninternal static class Matchmaking\n{\n\tpublic sealed class Challenge\n\t{\n\t\tpublic SocketUser Challenger { get; }\n\t\tpublic SocketUser Challengee { get; }\n\t\tpublic IGuild Guild { get; }\n\t\tpublic DateTime ChallengeTime { get; }\n\n\t\tpublic Challenge(SocketUser challenger, SocketUser challengee, IGuild guild)\n\t\t{\n\t\t\tChallenger = challenger;\n\t\t\tChallengee = challengee;\n\t\t\tGuild = guild;\n\t\t\tChallengeTime = DateTime.Now;\n\t\t}\n\t}\n\n\tprivate const int ChallengeMinuteExpiration = 5;\n\tprivate static readonly object _matchmakingLockObj = new();\n\tprivate static readonly List<Challenge> _challenges = new();\n\n\tpublic static void OnLeftGuild(SocketGuild guild)\n\t{\n\t\tlock (_matchmakingLockObj)\n\t\t{\n\t\t\t_challenges.RemoveAll(c => c.Guild.Id == guild.Id);\n\t\t}\n\t}\n\tpublic static void OnUserLeft(SocketGuildUser user)\n\t{\n\t\tlock (_matchmakingLockObj)\n\t\t{\n\t\t\t_challenges.RemoveAll(c => (user.Id == c.Challenger.Id || user.Id == c.Challengee.Id) && user.Guild.Id == c.Guild.Id);\n\t\t}\n\t}\n\n\tprivate static async Task PrintParticipating(SocketUser tag, SocketUser participant, ISocketMessageChannel channel)\n\t{\n\t\tawait channel.SendMessageAsync($\"{tag.Mention} ― {participant.Username} is already participating in a battle.\");\n\t}\n\tprivate static void RemoveOldChallenges()\n\t{\n\t\tDateTime dt = DateTime.Now;\n\t\t_challenges.RemoveAll(c => (dt - c.ChallengeTime).Minutes >= ChallengeMinuteExpiration);\n\t}\n\tprivate static Challenge? GetChallenge(SocketUser challengee)\n\t{\n\t\tforeach (Challenge c in _challenges)\n\t\t{\n\t\t\tif (c.Challengee.Id == challengee.Id)\n\t\t\t{\n\t\t\t\treturn c;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\tprivate static async Task StartBattle(Challenge c)\n\t{\n\t\tSocketUser a = c.Challenger;\n\t\tSocketUser b = c.Challengee;\n\t\tIGuild guild = c.Guild;\n\t\t_challenges.Remove(c);\n\n\t\tawait StartBattle(guild, a, b, a.Username, b.Username, a.Mention, b.Mention);\n\t}\n\tprivate static async Task StartBattle(IGuild guild, SocketUser? battler0, SocketUser? battler1, string team0Name, string team1Name, string team0Mention, string team1Mention)\n\t{\n\t\tPBELegalPokemonCollection p0, p1;\n\t\t// Competitively Randomized Pokémon\n\t\tp0 = PBEDDRandomTeamGenerator.CreateRandomTeam(3);\n\t\tp1 = PBEDDRandomTeamGenerator.CreateRandomTeam(3);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, PBESettings.DefaultSettings, new PBETrainerInfo(p0, team0Name, false), new PBETrainerInfo(p1, team1Name, false));\n\n\t\tvar bc = new BattleContext(battle, battler0, battler1);\n\t\tITextChannel channel = await ChannelHandler.CreateChannel(guild, $\"battle-{bc.BattleId}\");\n\t\tawait channel.SendMessageAsync($\"**Battle #{bc.BattleId} ― {team0Mention} vs {team1Mention}**\");\n\t\tawait bc.Begin(channel);\n\t}\n\n\tpublic static Task AcceptChallenge(SocketCommandContext ctx)\n\t{\n\t\tasync Task Do()\n\t\t{\n\t\t\tRemoveOldChallenges();\n\n\t\t\tSocketUser challengee = ctx.User;\n\t\t\tif (BattleContext.GetBattleContext(challengee, out _))\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tChallenge? c = GetChallenge(challengee);\n\t\t\tif (c is null)\n\t\t\t{\n\t\t\t\tawait ctx.Channel.SendMessageAsync($\"{challengee.Mention} ― You have no pending challenges.\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tSocketUser challenger = c.Challenger;\n\t\t\tif (BattleContext.GetBattleContext(challenger, out _))\n\t\t\t{\n\t\t\t\tawait PrintParticipating(challengee, challenger, ctx.Channel);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tawait StartBattle(c);\n\t\t\t}\n\t\t}\n\t\tlock (_matchmakingLockObj)\n\t\t{\n\t\t\treturn Do();\n\t\t}\n\t}\n\tpublic static Task ChallengeAI(SocketCommandContext ctx)\n\t{\n\t\tlock (_matchmakingLockObj)\n\t\t{\n\t\t\tconst string AIName = \"*PBEAI*\";\n\t\t\tSocketUser a = ctx.User;\n\t\t\treturn StartBattle(ctx.Guild, a, null, a.Username, AIName, a.Mention, AIName);\n\t\t}\n\t}\n\tpublic static Task ChallengeUser(SocketCommandContext ctx, SocketUser challengee)\n\t{\n\t\tasync Task Do()\n\t\t{\n\t\t\tRemoveOldChallenges();\n\n\t\t\tSocketUser challenger = ctx.User;\n\t\t\tif (challenger.Id == challengee.Id || BattleContext.GetBattleContext(challenger, out _))\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (BattleContext.GetBattleContext(challengee, out _))\n\t\t\t{\n\t\t\t\tawait PrintParticipating(challenger, challengee, ctx.Channel);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tChallenge? c = GetChallenge(challenger);\n\t\t\tif (c is not null && c.Challenger.Id == challengee.Id)\n\t\t\t{\n\t\t\t\tawait StartBattle(c);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tc = GetChallenge(challengee);\n\t\t\tif (c is not null)\n\t\t\t{\n\t\t\t\tawait ctx.Channel.SendMessageAsync($\"{challenger.Mention} ― {challengee.Username} already has a pending challenge.\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tstring msg = $\"You were challenged to a Pokémon battle by {challenger.Mention}!\\nThe challenge expires in {ChallengeMinuteExpiration} minutes.\\nType `!accept` to accept the challenge.\";\n\t\t\ttry\n\t\t\t{\n\t\t\t\tawait challengee.SendMessageAsync(msg);\n\t\t\t}\n\t\t\tcatch (Discord.Net.HttpException ex)\n\t\t\t{\n\t\t\t\tif (ex.DiscordCode == 50007)\n\t\t\t\t{\n\t\t\t\t\tawait ctx.Channel.SendMessageAsync($\"{challenger.Mention} ― Cannot challenge {challengee.Mention} because their DMs are closed.\");\n\t\t\t\t}\n\t\t\t\tConsole.WriteLine(\"Challenge exception:{0}{1}\", Environment.NewLine, ex);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t_challenges.Add(new Challenge(challenger, challengee, ctx.Guild));\n\t\t\tawait ctx.Channel.SendMessageAsync($\"{challenger.Mention} ― Your challenge has been sent to {challengee.Username}.\");\n\t\t}\n\t\tlock (_matchmakingLockObj)\n\t\t{\n\t\t\treturn Do();\n\t\t}\n\t}\n\tpublic static async Task Forfeit(SocketCommandContext ctx)\n\t{\n\t\tSocketUser sucker = ctx.User;\n\t\tif (BattleContext.GetBattleContext(sucker, out BattleContext? bc))\n\t\t{\n\t\t\tawait bc.Forfeit(sucker);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineDiscord/PokemonBattleEngineDiscord.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n\t<PropertyGroup>\n\t\t<TargetFramework>net7.0</TargetFramework>\n\t\t<OutputType>Exe</OutputType>\n\t\t<RootNamespace>Kermalis.PokemonBattleEngineDiscord</RootNamespace>\n\t\t<Authors>Kermalis</Authors>\n\t\t<Copyright>Kermalis</Copyright>\n\t\t<RepositoryUrl>https://github.com/Kermalis/PokemonBattleEngine</RepositoryUrl>\n\t\t<NoWarn />\n\t\t<StartupObject>Kermalis.PokemonBattleEngineDiscord.Program</StartupObject>\n\t\t<Nullable>enable</Nullable>\n\t</PropertyGroup>\n\n\t<ItemGroup>\n\t\t<PackageReference Include=\"Discord.Net\" Version=\"2.4.0\" />\n\t\t<ProjectReference Include=\"..\\PokemonBattleEngine.DefaultData\\PokemonBattleEngine.DefaultData.csproj\" />\n\t</ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "PokemonBattleEngineDiscord/Program.cs",
    "content": "﻿using Discord;\nusing Discord.Commands;\nusing Discord.WebSocket;\nusing Kermalis.PokemonBattleEngine.DefaultData;\nusing System;\nusing System.Reflection;\nusing System.Threading.Tasks;\n\nnamespace Kermalis.PokemonBattleEngineDiscord;\n\ninternal sealed class Program\n{\n\tprivate const char CommandPrefix = '!';\n\tprivate DiscordSocketClient _client = null!;\n\tprivate CommandService _commands = null!;\n\n\tpublic static void Main(string[] args)\n\t{\n\t\tnew Program().MainAsync(args).GetAwaiter().GetResult();\n\t}\n\tprivate async Task MainAsync(string[] args)\n\t{\n\t\tawait Utils.InitFemaleSpriteLookup();\n\t\tReplaySaver.RemoveOldReplays();\n\t\tPBEDefaultDataProvider.InitEngine(string.Empty);\n\n\t\t_client = new DiscordSocketClient();\n\n\t\t_commands = new CommandService(new CommandServiceConfig { DefaultRunMode = RunMode.Async });\n\t\tawait _commands.AddModulesAsync(Assembly.GetEntryAssembly(), null);\n\n\t\t_client.Log += LogMessage;\n\t\t_client.MessageReceived += CommandMessageReceived;\n\t\t_client.ReactionAdded += OnReactionAdded;\n\t\t_client.ChannelDestroyed += OnChannelDeleted;\n\t\t_client.LeftGuild += OnLeftGuild;\n\t\t_client.UserLeft += OnUserLeft;\n\t\t_client.Connected += OnConnected;\n\t\t_client.Disconnected += OnDisconnected;\n\t\t_client.GuildMemberUpdated += OnGuildMemberUpdated;\n\n\t\tawait _client.LoginAsync(TokenType.Bot, args[0]); // Token is passed in as args[0]\n\t\tawait _client.StartAsync();\n\n\t\tawait Task.Delay(-1);\n\t}\n\n\tprivate Task OnReactionAdded(Cacheable<IUserMessage, ulong> message, ISocketMessageChannel channel, SocketReaction reaction)\n\t{\n\t\tReactionHandler.OnReactionAdded(reaction);\n\t\treturn Task.CompletedTask;\n\t}\n\tprivate async Task OnChannelDeleted(SocketChannel arg)\n\t{\n\t\t// TODO: Prevent abuse of constant deletions of our stuff\n\t\tawait ChannelHandler.OnChannelDeleted(arg);\n\t\tBattleContext.OnChannelDeleted(arg);\n\t}\n\tprivate Task OnLeftGuild(SocketGuild arg)\n\t{\n\t\tMatchmaking.OnLeftGuild(arg);\n\t\tChannelHandler.OnLeftGuild(arg);\n\t\tBattleContext.OnLeftGuild(arg);\n\t\treturn Task.CompletedTask;\n\t}\n\tprivate async Task OnUserLeft(SocketGuildUser arg)\n\t{\n\t\tMatchmaking.OnUserLeft(arg);\n\t\tawait BattleContext.OnUserLeft(arg);\n\t}\n\tprivate Task OnConnected()\n\t{\n\t\tChannelHandler.OnConnected();\n\t\treturn Task.CompletedTask;\n\t}\n\tprivate Task OnDisconnected(Exception arg)\n\t{\n\t\tConsole.WriteLine(arg);\n\t\tChannelHandler.OnDisconnected();\n\t\treturn Task.CompletedTask;\n\t}\n\tprivate Task OnGuildMemberUpdated(SocketGuildUser arg1, SocketGuildUser arg2)\n\t{\n\t\tBattleContext.OnGuildMemberUpdated(arg2);\n\t\treturn Task.CompletedTask;\n\t}\n\n\tprivate async Task CommandMessageReceived(SocketMessage arg)\n\t{\n\t\tint argPos = 0;\n\t\tif (arg is not SocketUserMessage message\n\t\t\t|| message.Author.Id == _client.CurrentUser.Id\n\t\t\t|| !message.HasCharPrefix(CommandPrefix, ref argPos))\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\tvar context = new SocketCommandContext(_client, message);\n\t\tIResult result = await _commands.ExecuteAsync(context, argPos, null);\n\t\tif (!result.IsSuccess)\n\t\t{\n\t\t\tConsole.WriteLine(result.ErrorReason);\n\t\t}\n\t}\n\tprivate Task LogMessage(LogMessage arg)\n\t{\n\t\tConsole.WriteLine(arg.Message);\n\t\treturn Task.CompletedTask;\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineDiscord/ReactionHandler.cs",
    "content": "﻿using Discord;\nusing Discord.WebSocket;\nusing System;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\n\nnamespace Kermalis.PokemonBattleEngineDiscord;\n\ninternal static class ReactionHandler\n{\n\tprivate class Reaction\n\t{\n\t\tpublic IUserMessage Message { get; }\n\t\tpublic IEmote Emote { get; }\n\t\tpublic Func<Task> ClickFunc { get; }\n\n\t\tpublic Reaction(IUserMessage msg, IEmote emote, Func<Task> clickFunc)\n\t\t{\n\t\t\tMessage = msg;\n\t\t\tEmote = emote;\n\t\t\tClickFunc = clickFunc;\n\t\t}\n\t}\n\n\tprivate static readonly object _reactionListenersLockObj = new();\n\tprivate static readonly Dictionary<IUser, List<Reaction>> _reactionListeners = new(DiscordComparers.UserComparer);\n\n\tpublic static void OnReactionAdded(SocketReaction inEvent)\n\t{\n\t\tlock (_reactionListenersLockObj)\n\t\t{\n\t\t\tIUser user = inEvent.User.Value;\n\t\t\tif (!_reactionListeners.TryGetValue(user, out List<Reaction>? list))\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tulong msg = inEvent.MessageId;\n\t\t\tIEmote emote = inEvent.Emote;\n\t\t\tforeach (Reaction r in list)\n\t\t\t{\n\t\t\t\tif (r.Message.Id == msg && emote.Equals(r.Emote))\n\t\t\t\t{\n\t\t\t\t\t_reactionListeners.Remove(user);\n\t\t\t\t\tr.ClickFunc.Invoke(); // Do not return Task because we do not want to block the main thread\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static void AddListener(IUser user, IUserMessage msg, IEmote emote, Func<Task> clickFunc)\n\t{\n\t\tlock (_reactionListenersLockObj)\n\t\t{\n\t\t\tif (!_reactionListeners.TryGetValue(user, out List<Reaction>? list))\n\t\t\t{\n\t\t\t\tlist = new List<Reaction>();\n\t\t\t\t_reactionListeners.Add(user, list);\n\t\t\t}\n\t\t\tlist.Add(new Reaction(msg, emote, clickFunc));\n\t\t}\n\t}\n\tpublic static void RemoveListeners(IUser? a, IUser? b)\n\t{\n\t\tlock (_reactionListenersLockObj)\n\t\t{\n\t\t\tif (a is not null)\n\t\t\t{\n\t\t\t\t_reactionListeners.Remove(a);\n\t\t\t}\n\t\t\tif (b is not null)\n\t\t\t{\n\t\t\t\t_reactionListeners.Remove(b);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineDiscord/ReplaySaver.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing System;\nusing System.IO;\nusing System.Text.RegularExpressions;\n\nnamespace Kermalis.PokemonBattleEngineDiscord;\n\ninternal static class ReplaySaver\n{\n\tpublic const bool ShouldSaveForfeits = false; // false for now because 1: not useful 2: exception occurs if saving replay of unended battle\n\tpublic const bool ShouldSaveAIBattles = false;\n\tprivate const int NumDaysTillRemoval = 30;\n\tprivate const string ReplayDirectory = \"Replays\";\n\tprivate const string DateRegexPattern = @\"^([0-9]+)\\-([0-9]+)\\-([0-9]+)$\";\n\n\tprivate static DateTime _lastDateTime = DateTime.Today;\n\n\tprivate static string GetTodayFolderPath()\n\t{\n\t\tDateTime today = DateTime.Today;\n\t\tif (today != _lastDateTime)\n\t\t{\n\t\t\t_lastDateTime = today;\n\t\t\tConsole.WriteLine(\"Date changed; checking old replay directories...\");\n\t\t\tRemoveOldReplays();\n\t\t}\n\t\treturn string.Format(\"{0}-{1}-{2}\", today.Year, today.Month, today.Day);\n\t}\n\n\tpublic static void SaveReplay(PBEBattle battle, ulong battleId)\n\t{\n\t\tstring dir = Path.Combine(ReplayDirectory, GetTodayFolderPath());\n\t\tif (!Directory.Exists(dir))\n\t\t{\n\t\t\tDirectory.CreateDirectory(dir);\n\t\t}\n\t\tbattle.SaveReplay(Path.Combine(dir, $\"#{battleId} {battle.GetDefaultReplayFileName()}\"));\n\t}\n\n\tpublic static void RemoveOldReplays()\n\t{\n\t\tif (!Directory.Exists(ReplayDirectory))\n\t\t{\n\t\t\tDirectory.CreateDirectory(ReplayDirectory);\n\t\t\treturn;\n\t\t}\n\t\tDateTime today = DateTime.Today;\n\t\tforeach (string dir in Directory.EnumerateDirectories(ReplayDirectory))\n\t\t{\n\t\t\tstring dirName = new DirectoryInfo(dir).Name;\n\t\t\tMatch m = Regex.Match(dirName, DateRegexPattern);\n\t\t\tif (!m.Success)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tTimeSpan timePassed = today - new DateTime(int.Parse(m.Groups[1].Value), int.Parse(m.Groups[2].Value), int.Parse(m.Groups[3].Value));\n\t\t\tif (timePassed.Days >= NumDaysTillRemoval)\n\t\t\t{\n\t\t\t\tConsole.WriteLine(\"Deleting old replay directory: {0}\", dirName);\n\t\t\t\tDirectory.Delete(dir, true);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineDiscord/Utils.cs",
    "content": "﻿using Discord;\nusing Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Data.Utils;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Net.Http;\nusing System.Threading.Tasks;\n\nnamespace Kermalis.PokemonBattleEngineDiscord;\n\ninternal static class Utils\n{\n\tpublic const string URL = \"https://github.com/Kermalis/PokemonBattleEngine\";\n\tprivate const string ImageURL = \"https://raw.githubusercontent.com/Kermalis/PokemonBattleEngine/master/Shared%20Assets/PKMN/\";\n\n\t#region Static Collections\n\tpublic static readonly Dictionary<PBEType, Color> TypeColors = new()\n\t{\n\t\t{ PBEType.None, new Color(146, 154, 156) },\n\t\t{ PBEType.Bug, new Color(162, 212, 56) },\n\t\t{ PBEType.Dark, new Color(106, 122, 156) },\n\t\t{ PBEType.Dragon, new Color(80, 136, 188) },\n\t\t{ PBEType.Electric, new Color(246, 216, 48) },\n\t\t{ PBEType.Fighting, new Color(244, 100, 138) },\n\t\t{ PBEType.Fire, new Color(255, 152, 56) },\n\t\t{ PBEType.Flying, new Color(80, 124, 212) },\n\t\t{ PBEType.Ghost, new Color(94, 100, 208) },\n\t\t{ PBEType.Grass, new Color(64, 208, 112) },\n\t\t{ PBEType.Ground, new Color(232, 130, 68) },\n\t\t{ PBEType.Ice, new Color(98, 204, 212) },\n\t\t{ PBEType.Normal, new Color(146, 154, 156) },\n\t\t{ PBEType.Poison, new Color(188, 82, 232) },\n\t\t{ PBEType.Psychic, new Color(255, 136, 130) },\n\t\t{ PBEType.Rock, new Color(196, 174, 112) },\n\t\t{ PBEType.Steel, new Color(94, 160, 178) },\n\t\t{ PBEType.Water, new Color(58, 176, 232) }\n\t};\n\tpublic static readonly Dictionary<PBEType, Emote> TypeEmotes = new()\n\t{\n\t\t{ PBEType.None, Emote.Parse(\"<:Normal:708768400167665755>\") },\n\t\t{ PBEType.Bug, Emote.Parse(\"<:Bug:708768296731934751>\") },\n\t\t{ PBEType.Dark, Emote.Parse(\"<:Dark:708768299248386109>\") },\n\t\t{ PBEType.Dragon, Emote.Parse(\"<:Dragon:708768299420483675>\") },\n\t\t{ PBEType.Electric, Emote.Parse(\"<:Electric:708768297792831549>\") },\n\t\t{ PBEType.Fighting, Emote.Parse(\"<:Fighting:708768297386246154>\") },\n\t\t{ PBEType.Fire, Emote.Parse(\"<:Fire:708768299319820369>\") },\n\t\t{ PBEType.Flying, Emote.Parse(\"<:Flying:708768299252711535>\") },\n\t\t{ PBEType.Ghost, Emote.Parse(\"<:Ghost:708768299231739964>\") },\n\t\t{ PBEType.Grass, Emote.Parse(\"<:Grass:708768299319558164>\") },\n\t\t{ PBEType.Ground, Emote.Parse(\"<:Ground:708768298829086822>\") },\n\t\t{ PBEType.Ice, Emote.Parse(\"<:Ice:708768398104068158>\") },\n\t\t{ PBEType.Normal, Emote.Parse(\"<:Normal:708768400167665755>\") },\n\t\t{ PBEType.Poison, Emote.Parse(\"<:Poison:708768399928590337>\") },\n\t\t{ PBEType.Psychic, Emote.Parse(\"<:Psychic:708768399161032725>\") },\n\t\t{ PBEType.Rock, Emote.Parse(\"<:Rock:708768399311765577>\") },\n\t\t{ PBEType.Steel, Emote.Parse(\"<:Steel:708768399383330867>\") },\n\t\t{ PBEType.Water, Emote.Parse(\"<:Water:708768402356830268>\") }\n\t};\n\tpublic static readonly Dictionary<PBEStatus1, Emote> Status1Emotes = new()\n\t{\n\t\t{ PBEStatus1.Asleep, Emote.Parse(\"<:Asleep:708841651690537030>\") },\n\t\t{ PBEStatus1.BadlyPoisoned, Emote.Parse(\"<:BadlyPoisoned:708841652151910400>\") },\n\t\t{ PBEStatus1.Burned, Emote.Parse(\"<:Burned:708841651711246442>\") },\n\t\t{ PBEStatus1.Frozen, Emote.Parse(\"<:Frozen:708841651359186995>\") },\n\t\t{ PBEStatus1.Paralyzed, Emote.Parse(\"<:Paralyzed:708841651568771093>\") },\n\t\t{ PBEStatus1.Poisoned, Emote.Parse(\"<:Poisoned:708841651787005962>\") }\n\t};\n\tpublic static readonly Dictionary<PBEWeather, Emote> WeatherEmotes = new()\n\t{\n\t\t{ PBEWeather.Hailstorm, Emote.Parse(\"<a:Hailstorm:709213589612920834>\") },\n\t\t{ PBEWeather.HarshSunlight, Emote.Parse(\"<a:HarshSunlight:709213589587755121>\") },\n\t\t{ PBEWeather.Rain, Emote.Parse(\"<a:Rain:709213589977694239>\") },\n\t\t{ PBEWeather.Sandstorm, Emote.Parse(\"<a:Sandstorm:709213589826830357>\") }\n\t};\n\t#endregion\n\n\t// https://stackoverflow.com/a/3722337\n\tpublic static Color Blend(this Color color, Color backColor, float depth = 0.5f)\n\t{\n\t\tbyte r = (byte)((color.R * depth) + (backColor.R * (1 - depth)));\n\t\tbyte g = (byte)((color.G * depth) + (backColor.G * (1 - depth)));\n\t\tbyte b = (byte)((color.B * depth) + (backColor.B * (1 - depth)));\n\t\treturn new Color(r, g, b);\n\t}\n\tpublic static Color GetColor(PBEType type1, PBEType type2)\n\t{\n\t\tColor color = TypeColors[type1];\n\t\tif (type2 != PBEType.None)\n\t\t{\n\t\t\tcolor = color.Blend(TypeColors[type2]);\n\t\t}\n\t\treturn color;\n\t}\n\tpublic static Color GetColor(PBEBattlePokemon pkmn)\n\t{\n\t\treturn GetColor(pkmn.KnownType1, pkmn.KnownType2);\n\t}\n\n\tprivate static readonly Random _rand = new();\n\tpublic static Color RandomColor()\n\t{\n\t\tbyte[] bytes = new byte[3];\n\t\t_rand.NextBytes(bytes);\n\t\treturn new Color(bytes[0], bytes[1], bytes[2]);\n\t}\n\tpublic static T RandomElement<T>(this IReadOnlyList<T> source)\n\t{\n\t\tint count = source.Count;\n\t\tif (count == 1)\n\t\t{\n\t\t\treturn source[0];\n\t\t}\n\t\tif (count < 1)\n\t\t{\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(source), $\"\\\"{nameof(source)}\\\" must have at least one element.\");\n\t\t}\n\t\treturn source[_rand.Next(count)];\n\t}\n\n\tprivate static readonly List<PBESpecies> _femaleSpriteLookup = new();\n\tpublic static async Task InitFemaleSpriteLookup()\n\t{\n\t\tconst string address = ImageURL + \"FemaleSpriteLookup.txt\";\n\t\tusing (var w = new HttpClient())\n\t\tusing (var reader = new StringReader(await w.GetStringAsync(address)))\n\t\t{\n\t\t\tstring? line;\n\t\t\twhile ((line = reader.ReadLine()) is not null)\n\t\t\t{\n\t\t\t\tif (!Enum.TryParse(line, out PBESpecies species))\n\t\t\t\t{\n\t\t\t\t\tthrow new InvalidDataException($\"Failed to parse \\\"{address}\\\"\");\n\t\t\t\t}\n\t\t\t\t_femaleSpriteLookup.Add(species);\n\t\t\t}\n\t\t}\n\t}\n\tprivate static bool HasFemaleSprite(PBESpecies species)\n\t{\n\t\treturn _femaleSpriteLookup.Contains(species);\n\t}\n\tpublic static string GetPokemonSprite(PBEBattlePokemon pokemon)\n\t{\n\t\treturn GetPokemonSprite(pokemon.KnownSpecies, pokemon.KnownForm, pokemon.KnownShiny, pokemon.KnownGender, pokemon.KnownStatus2.HasFlag(PBEStatus2.Substitute), false);\n\t}\n\tpublic static string GetPokemonSprite(PBESpecies species, PBEForm form, bool shiny, PBEGender gender, bool behindSubstitute, bool backSprite)\n\t{\n\t\tstring orientation = backSprite ? \"_B\" : \"_F\";\n\t\tif (behindSubstitute)\n\t\t{\n\t\t\treturn ImageURL + \"STATUS2_Substitute\" + orientation + \".gif\";\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstring speciesStr = PBEDataUtils.GetNameOfForm(species, form) ?? species.ToString();\n\t\t\tstring genderStr = gender == PBEGender.Female && HasFemaleSprite(species) ? \"_F\" : string.Empty;\n\t\t\treturn ImageURL + \"PKMN_\" + speciesStr + orientation + (shiny ? \"_S\" : string.Empty) + genderStr + \".gif\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineExtras/AIBattleDemo.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Data.Legality;\nusing Kermalis.PokemonBattleEngine.DefaultData;\nusing Kermalis.PokemonBattleEngine.DefaultData.AI;\nusing Kermalis.PokemonBattleEngine.Packets;\nusing System;\nusing System.IO;\nusing System.Threading;\n\nnamespace Kermalis.PokemonBattleEngineExtras;\n\ninternal sealed class AIBattleDemo\n{\n\tprivate const string LogFile = \"AI Demo Log.txt\";\n\tprivate const string ReplayFile = \"AI Demo.pbereplay\";\n\tprivate readonly PBEBattle _battle;\n\tprivate readonly PBEDDAI _ai0, _ai1;\n\tprivate readonly StreamWriter _writer;\n\tprivate readonly TextWriter _oldWriter;\n\n\tpublic AIBattleDemo()\n\t{\n\t\tConsole.WriteLine(\"----- Pokémon Battle Engine - AI Battle Demo -----\");\n\t\ttry\n\t\t{\n\t\t\t_writer = new StreamWriter(new FileStream(LogFile, FileMode.Create, FileAccess.Write));\n\t\t}\n\t\tcatch (Exception e)\n\t\t{\n\t\t\tConsole.WriteLine($\"Cannot open \\\"{LogFile}\\\" for writing.\");\n\t\t\tConsole.WriteLine(e.Message);\n\t\t\t_battle = null!;\n\t\t\t_ai0 = null!;\n\t\t\t_ai1 = null!;\n\t\t\t_writer = null!;\n\t\t\t_oldWriter = null!;\n\t\t\treturn;\n\t\t}\n\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\t\t//var settings = new PBESettings { NumMoves = 8, MaxPartySize = 10 };\n\t\t//settings.MakeReadOnly();\n\t\tPBELegalPokemonCollection p0, p1;\n\n\t\t// Competitively Randomized Pokémon\n\t\tp0 = PBEDDRandomTeamGenerator.CreateRandomTeam(settings.MaxPartySize);\n\t\tp1 = PBEDDRandomTeamGenerator.CreateRandomTeam(settings.MaxPartySize);\n\n\t\t_battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Double, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false),\n\t\t\tbattleTerrain: PBEDataProvider.GlobalRandom.RandomBattleTerrain());\n\t\t_battle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\t\t_battle.OnNewEvent += Battle_OnNewEvent;\n\t\t_battle.OnStateChanged += Battle_OnStateChanged;\n\t\t_ai0 = new PBEDDAI(_battle.Trainers[0]);\n\t\t_ai1 = new PBEDDAI(_battle.Trainers[1]);\n\t\t_oldWriter = Console.Out;\n\t\tConsole.SetOut(_writer);\n\t\tnew Thread(() =>\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\t_battle.Begin();\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tCatchException(e);\n\t\t\t}\n\t\t})\n\t\t{ Name = \"Battle Thread\" }.Start();\n\t}\n\n\tprivate void CatchException(Exception e)\n\t{\n\t\tConsole.WriteLine(e.Message);\n\t\tConsole.WriteLine(e.StackTrace);\n\t\tConsole.SetOut(_oldWriter);\n\t\t_writer.Dispose();\n\t\t_battle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t_battle.OnNewEvent -= Battle_OnNewEvent;\n\t\t_battle.OnStateChanged -= Battle_OnStateChanged;\n\t\tConsole.WriteLine(\"Demo battle threw an exception; check \\\"{0}\\\" for details.\", LogFile);\n\t\tConsole.ReadKey();\n\t}\n\n\tprivate PBEDDAI GetAI(PBETrainer t)\n\t{\n\t\treturn t.Id == 0 ? _ai0 : _ai1;\n\t}\n\n\tprivate void Battle_OnNewEvent(PBEBattle battle, IPBEPacket packet)\n\t{\n\t\ttry\n\t\t{\n\t\t\tswitch (packet)\n\t\t\t{\n\t\t\t\tcase PBEActionsRequestPacket arp:\n\t\t\t\t{\n\t\t\t\t\tGetAI(arp.Trainer).CreateActions();\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBESwitchInRequestPacket sirp:\n\t\t\t\t{\n\t\t\t\t\tGetAI(sirp.Trainer).CreateSwitches();\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBETurnBeganPacket tbp:\n\t\t\t\t{\n\t\t\t\t\tConsole.SetOut(_oldWriter);\n\t\t\t\t\tDateTime time = DateTime.Now;\n\t\t\t\t\tConsole.WriteLine($\"Emulating turn {tbp.TurnNumber}... ({time.Hour:D2}:{time.Minute:D2}:{time.Second:D2}:{time.Millisecond:D3})\");\n\t\t\t\t\tConsole.SetOut(_writer);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e)\n\t\t{\n\t\t\tCatchException(e);\n\t\t}\n\t}\n\tprivate void Battle_OnStateChanged(PBEBattle battle)\n\t{\n\t\ttry\n\t\t{\n\t\t\tswitch (_battle.BattleState)\n\t\t\t{\n\t\t\t\tcase PBEBattleState.Ended:\n\t\t\t\t{\n\t\t\t\t\tConsole.SetOut(_oldWriter);\n\t\t\t\t\t_writer.Dispose();\n\t\t\t\t\ttry\n\t\t\t\t\t{\n\t\t\t\t\t\t_battle.SaveReplay(ReplayFile);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Exception e)\n\t\t\t\t\t{\n\t\t\t\t\t\tConsole.WriteLine(\"Error saving replay:\");\n\t\t\t\t\t\tConsole.WriteLine(e.Message);\n\t\t\t\t\t\tConsole.WriteLine(e.StackTrace);\n\t\t\t\t\t}\n\t\t\t\t\tConsole.WriteLine(\"Test battle ended. The battle was saved to \\\"{0}\\\" and \\\"{1}\\\".\", LogFile, ReplayFile);\n\t\t\t\t\tConsole.ReadKey();\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEBattleState.ReadyToRunSwitches:\n\t\t\t\t{\n\t\t\t\t\tnew Thread(() =>\n\t\t\t\t\t{\n\t\t\t\t\t\ttry\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t_battle.RunSwitches();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (Exception e)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tCatchException(e);\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t{ Name = \"Battle Thread\" }.Start();\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEBattleState.ReadyToRunTurn:\n\t\t\t\t{\n\t\t\t\t\tforeach (PBETrainer t in _battle.Trainers)\n\t\t\t\t\t{\n\t\t\t\t\t\tConsole.WriteLine();\n\t\t\t\t\t\tConsole.WriteLine(\"{0}'s team:\", t.Name);\n\t\t\t\t\t\tforeach (PBEBattlePokemon p in t.ActiveBattlersOrdered)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tConsole.WriteLine(p);\n\t\t\t\t\t\t\tConsole.WriteLine();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tnew Thread(() =>\n\t\t\t\t\t{\n\t\t\t\t\t\ttry\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t_battle.RunTurn();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (Exception e)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tCatchException(e);\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t{ Name = \"Battle Thread\" }.Start();\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e)\n\t\t{\n\t\t\tCatchException(e);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineExtras/LocalizationDumper.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Data.Utils;\nusing Kermalis.SimpleNARC;\nusing Microsoft.Data.Sqlite;\nusing System;\n\nnamespace Kermalis.PokemonBattleEngineExtras;\n\ninternal static class LocalizationDumper\n{\n\t// You must dump the NARC files yourself (/a/0/0/2 in each language)\n\tpublic static void Run(SqliteConnection con)\n\t{\n\t\tvar english = new NARC(@\"../../../\\DumpedData\\W2EnglishTexts.narc\");\n\t\tvar french = new NARC(@\"../../../\\DumpedData\\W2FrenchTexts.narc\");\n\t\tvar german = new NARC(@\"../../../\\DumpedData\\W2GermanTexts.narc\");\n\t\tvar italian = new NARC(@\"../../../\\DumpedData\\W2ItalianTexts.narc\");\n\t\tvar japanese = new NARC(@\"../../../\\DumpedData\\W2JapaneseTexts.narc\");\n\t\tvar korean = new NARC(@\"../../../\\DumpedData\\W2KoreanTexts.narc\");\n\t\tvar spanish = new NARC(@\"../../../\\DumpedData\\W2SpanishTexts.narc\");\n\t\tusing (SqliteTransaction transaction = con.BeginTransaction())\n\t\tusing (SqliteCommand cmd = con.CreateCommand())\n\t\t{\n\t\t\tcmd.Transaction = transaction;\n\n\t\t\tvoid CreateTable(string tableName)\n\t\t\t{\n\t\t\t\tcmd.CommandText = $\"DROP TABLE IF EXISTS {tableName}\";\n\t\t\t\tcmd.ExecuteNonQuery();\n\t\t\t\tcmd.CommandText = $\"CREATE TABLE {tableName}(Id TEXT PRIMARY KEY, English TEXT, French TEXT, German TEXT, Italian TEXT, Japanese_Kana TEXT, Japanese_Kanji TEXT, Korean TEXT, Spanish TEXT)\";\n\t\t\t\tcmd.ExecuteNonQuery();\n\t\t\t}\n\t\t\tvoid Insert(string tableName, string id, string e, string f, string g, string i, string jkana, string jkanji, string k, string s)\n\t\t\t{\n\t\t\t\tcmd.CommandText = $\"INSERT INTO {tableName} VALUES(@0, @1, @2, @3, @4, @5, @6, @7, @8)\";\n\t\t\t\tcmd.Parameters.AddWithValue(\"@0\", id);\n\t\t\t\tcmd.Parameters.AddWithValue(\"@1\", e);\n\t\t\t\tcmd.Parameters.AddWithValue(\"@2\", f);\n\t\t\t\tcmd.Parameters.AddWithValue(\"@3\", g);\n\t\t\t\tcmd.Parameters.AddWithValue(\"@4\", i);\n\t\t\t\tcmd.Parameters.AddWithValue(\"@5\", jkana);\n\t\t\t\tcmd.Parameters.AddWithValue(\"@6\", jkanji);\n\t\t\t\tcmd.Parameters.AddWithValue(\"@7\", k);\n\t\t\t\tcmd.Parameters.AddWithValue(\"@8\", s);\n\t\t\t\tcmd.ExecuteNonQuery();\n\t\t\t\tcmd.Parameters.Clear();\n\t\t\t}\n\n\t\t\tstring[][] eng, fre, ger, ita, jap, kor, spa;\n\t\t\tvoid LoadTexts(int fileNum)\n\t\t\t{\n\t\t\t\teng = Utils.ReadTextFile(english, fileNum);\n\t\t\t\tfre = Utils.ReadTextFile(french, fileNum);\n\t\t\t\tger = Utils.ReadTextFile(german, fileNum);\n\t\t\t\tita = Utils.ReadTextFile(italian, fileNum);\n\t\t\t\tjap = Utils.ReadTextFile(japanese, fileNum);\n\t\t\t\tkor = Utils.ReadTextFile(korean, fileNum);\n\t\t\t\tspa = Utils.ReadTextFile(spanish, fileNum);\n\t\t\t}\n\t\t\tvoid WriteTexts(string tableName, string id, int text)\n\t\t\t{\n\t\t\t\tInsert(tableName, id, eng[0][text], fre[0][text], ger[0][text], ita[0][text], jap[0][text], jap[1][text], kor[0][text], spa[0][text]);\n\t\t\t}\n\n\t\t\t// Abilities\n\t\t\t{\n\t\t\t\tvoid WriteAll(string tableName)\n\t\t\t\t{\n\t\t\t\t\tCreateTable(tableName);\n\t\t\t\t\tfor (PBEAbility i = 0; i < PBEAbility.MAX; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tWriteTexts(tableName, i.ToString(), (int)i);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tLoadTexts(374);\n\t\t\t\tWriteAll(\"AbilityNames\");\n\t\t\t\tLoadTexts(375);\n\t\t\t\tWriteAll(\"AbilityDescriptions\");\n\t\t\t}\n\t\t\t// Forms\n\t\t\t{\n\t\t\t\tvoid InsertForm(string tableName, PBESpecies species, PBEForm form, string e, string f, string g, string i, string jkana, string jkanji, string k, string s)\n\t\t\t\t{\n\t\t\t\t\tcmd.CommandText = $\"INSERT INTO {tableName} VALUES(@0, @1, @2, @3, @4, @5, @6, @7, @8, @9)\";\n\t\t\t\t\tcmd.Parameters.AddWithValue(\"@0\", species.ToString());\n\t\t\t\t\tcmd.Parameters.AddWithValue(\"@1\", PBEDataUtils.GetNameOfForm(species, form) ?? \"0\");\n\t\t\t\t\tcmd.Parameters.AddWithValue(\"@2\", e);\n\t\t\t\t\tcmd.Parameters.AddWithValue(\"@3\", f);\n\t\t\t\t\tcmd.Parameters.AddWithValue(\"@4\", g);\n\t\t\t\t\tcmd.Parameters.AddWithValue(\"@5\", i);\n\t\t\t\t\tcmd.Parameters.AddWithValue(\"@6\", jkana);\n\t\t\t\t\tcmd.Parameters.AddWithValue(\"@7\", jkanji);\n\t\t\t\t\tcmd.Parameters.AddWithValue(\"@8\", k);\n\t\t\t\t\tcmd.Parameters.AddWithValue(\"@9\", s);\n\t\t\t\t\tcmd.ExecuteNonQuery();\n\t\t\t\t\tcmd.Parameters.Clear();\n\t\t\t\t}\n\t\t\t\tvoid WriteFormTexts(string tableName, PBESpecies species, PBEForm form, int text)\n\t\t\t\t{\n\t\t\t\t\tInsertForm(tableName, species, form, eng[0][text], fre[0][text], ger[0][text], ita[0][text], jap[0][text], jap[1][text], kor[0][text], spa[0][text]);\n\t\t\t\t}\n\t\t\t\tvoid WriteUnown(string tableName, PBEForm form, string letter)\n\t\t\t\t{\n\t\t\t\t\tInsertForm(tableName, PBESpecies.Unown, form, letter, letter, letter, letter, letter, letter, letter, letter);\n\t\t\t\t}\n\t\t\t\tvoid WriteArceusGenesect(string tableName, PBESpecies species, PBEForm form, PBEType type)\n\t\t\t\t{\n\t\t\t\t\tint text = -1;\n\t\t\t\t\tfor (int i = 0; i < Utils.Gen5Types.Length; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (Utils.Gen5Types[i] == type)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttext = i;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tWriteFormTexts(tableName, species, form, text);\n\t\t\t\t}\n\t\t\t\tLoadTexts(450);\n\t\t\t\tconst string tableName = \"FormNames\";\n\t\t\t\tcmd.CommandText = $\"DROP TABLE IF EXISTS {tableName}\";\n\t\t\t\tcmd.ExecuteNonQuery();\n\t\t\t\tcmd.CommandText = $\"CREATE TABLE {tableName}(Species TEXT, Form TEXT, English TEXT, French TEXT, German TEXT, Italian TEXT, Japanese_Kana TEXT, Japanese_Kanji TEXT, Korean TEXT, Spanish TEXT)\";\n\t\t\t\tcmd.ExecuteNonQuery();\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Castform, PBEForm.Castform, 351);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Deoxys, PBEForm.Deoxys, 386);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Burmy, PBEForm.Burmy_Plant, 412);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Wormadam, PBEForm.Wormadam_Plant, 413);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Cherrim, PBEForm.Cherrim, 421);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Shellos, PBEForm.Shellos_West, 422);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Gastrodon, PBEForm.Gastrodon_West, 423);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Rotom, PBEForm.Rotom, 479);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Giratina, PBEForm.Giratina, 487);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Shaymin, PBEForm.Shaymin, 492);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Basculin, PBEForm.Basculin_Red, 550);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Darmanitan, PBEForm.Darmanitan, 555);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Deerling, PBEForm.Deerling_Spring, 585);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Sawsbuck, PBEForm.Sawsbuck_Spring, 586);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Tornadus, PBEForm.Tornadus, 641);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Thundurus, PBEForm.Thundurus, 642);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Landorus, PBEForm.Landorus, 645);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Kyurem, PBEForm.Kyurem, 646);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Keldeo, PBEForm.Keldeo, 647);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Meloetta, PBEForm.Meloetta, 648);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Castform, PBEForm.Castform_Sunny, 678);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Castform, PBEForm.Castform_Rainy, 679);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Castform, PBEForm.Castform_Snowy, 680);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Deoxys, PBEForm.Deoxys_Attack, 681);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Deoxys, PBEForm.Deoxys_Defense, 682);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Deoxys, PBEForm.Deoxys_Speed, 683);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Burmy, PBEForm.Burmy_Sandy, 684);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Burmy, PBEForm.Burmy_Trash, 685);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Wormadam, PBEForm.Wormadam_Sandy, 686);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Wormadam, PBEForm.Wormadam_Trash, 687);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Cherrim, PBEForm.Cherrim_Sunshine, 688);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Shellos, PBEForm.Shellos_East, 689);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Gastrodon, PBEForm.Gastrodon_East, 690);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Rotom, PBEForm.Rotom_Heat, 691);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Rotom, PBEForm.Rotom_Wash, 692);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Rotom, PBEForm.Rotom_Frost, 693);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Rotom, PBEForm.Rotom_Fan, 694);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Rotom, PBEForm.Rotom_Mow, 695);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Giratina, PBEForm.Giratina_Origin, 696);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Shaymin, PBEForm.Shaymin_Sky, 697);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Basculin, PBEForm.Basculin_Blue, 714);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Darmanitan, PBEForm.Darmanitan_Zen, 715);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Deerling, PBEForm.Deerling_Summer, 716);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Deerling, PBEForm.Deerling_Autumn, 717);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Deerling, PBEForm.Deerling_Winter, 718);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Sawsbuck, PBEForm.Sawsbuck_Summer, 719);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Sawsbuck, PBEForm.Sawsbuck_Autumn, 720);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Sawsbuck, PBEForm.Sawsbuck_Winter, 721);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Tornadus, PBEForm.Tornadus_Therian, 722);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Thundurus, PBEForm.Thundurus_Therian, 723);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Landorus, PBEForm.Landorus_Therian, 724);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Kyurem, PBEForm.Kyurem_White, 725);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Kyurem, PBEForm.Kyurem_Black, 726);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Keldeo, PBEForm.Keldeo_Resolute, 727);\n\t\t\t\tWriteFormTexts(tableName, PBESpecies.Meloetta, PBEForm.Meloetta_Pirouette, 728);\n\t\t\t\t// All Unown forms are called \"One Form\", all Arceus forms are called \"Arceus\", and all Genesect forms are called \"Genesect\", so I'm changing them here\n\t\t\t\tWriteUnown(tableName, PBEForm.Unown_A, \"A\");\n\t\t\t\tWriteUnown(tableName, PBEForm.Unown_B, \"B\");\n\t\t\t\tWriteUnown(tableName, PBEForm.Unown_C, \"C\");\n\t\t\t\tWriteUnown(tableName, PBEForm.Unown_D, \"D\");\n\t\t\t\tWriteUnown(tableName, PBEForm.Unown_E, \"E\");\n\t\t\t\tWriteUnown(tableName, PBEForm.Unown_Exclamation, \"!\");\n\t\t\t\tWriteUnown(tableName, PBEForm.Unown_F, \"F\");\n\t\t\t\tWriteUnown(tableName, PBEForm.Unown_G, \"G\");\n\t\t\t\tWriteUnown(tableName, PBEForm.Unown_H, \"H\");\n\t\t\t\tWriteUnown(tableName, PBEForm.Unown_I, \"I\");\n\t\t\t\tWriteUnown(tableName, PBEForm.Unown_J, \"J\");\n\t\t\t\tWriteUnown(tableName, PBEForm.Unown_K, \"K\");\n\t\t\t\tWriteUnown(tableName, PBEForm.Unown_L, \"L\");\n\t\t\t\tWriteUnown(tableName, PBEForm.Unown_M, \"M\");\n\t\t\t\tWriteUnown(tableName, PBEForm.Unown_N, \"N\");\n\t\t\t\tWriteUnown(tableName, PBEForm.Unown_O, \"O\");\n\t\t\t\tWriteUnown(tableName, PBEForm.Unown_P, \"P\");\n\t\t\t\tWriteUnown(tableName, PBEForm.Unown_Q, \"Q\");\n\t\t\t\tWriteUnown(tableName, PBEForm.Unown_Question, \"?\");\n\t\t\t\tWriteUnown(tableName, PBEForm.Unown_R, \"R\");\n\t\t\t\tWriteUnown(tableName, PBEForm.Unown_S, \"S\");\n\t\t\t\tWriteUnown(tableName, PBEForm.Unown_T, \"T\");\n\t\t\t\tWriteUnown(tableName, PBEForm.Unown_U, \"U\");\n\t\t\t\tWriteUnown(tableName, PBEForm.Unown_V, \"V\");\n\t\t\t\tWriteUnown(tableName, PBEForm.Unown_W, \"W\");\n\t\t\t\tWriteUnown(tableName, PBEForm.Unown_X, \"X\");\n\t\t\t\tWriteUnown(tableName, PBEForm.Unown_Y, \"Y\");\n\t\t\t\tWriteUnown(tableName, PBEForm.Unown_Z, \"Z\");\n\t\t\t\tLoadTexts(398); // Load types texts\n\t\t\t\tWriteArceusGenesect(tableName, PBESpecies.Arceus, PBEForm.Arceus, PBEType.Normal);\n\t\t\t\tWriteArceusGenesect(tableName, PBESpecies.Arceus, PBEForm.Arceus_Bug, PBEType.Bug);\n\t\t\t\tWriteArceusGenesect(tableName, PBESpecies.Arceus, PBEForm.Arceus_Dark, PBEType.Dark);\n\t\t\t\tWriteArceusGenesect(tableName, PBESpecies.Arceus, PBEForm.Arceus_Dragon, PBEType.Dragon);\n\t\t\t\tWriteArceusGenesect(tableName, PBESpecies.Arceus, PBEForm.Arceus_Electric, PBEType.Electric);\n\t\t\t\tWriteArceusGenesect(tableName, PBESpecies.Arceus, PBEForm.Arceus_Fighting, PBEType.Fighting);\n\t\t\t\tWriteArceusGenesect(tableName, PBESpecies.Arceus, PBEForm.Arceus_Fire, PBEType.Fire);\n\t\t\t\tWriteArceusGenesect(tableName, PBESpecies.Arceus, PBEForm.Arceus_Flying, PBEType.Flying);\n\t\t\t\tWriteArceusGenesect(tableName, PBESpecies.Arceus, PBEForm.Arceus_Ghost, PBEType.Ghost);\n\t\t\t\tWriteArceusGenesect(tableName, PBESpecies.Arceus, PBEForm.Arceus_Grass, PBEType.Grass);\n\t\t\t\tWriteArceusGenesect(tableName, PBESpecies.Arceus, PBEForm.Arceus_Ground, PBEType.Ground);\n\t\t\t\tWriteArceusGenesect(tableName, PBESpecies.Arceus, PBEForm.Arceus_Ice, PBEType.Ice);\n\t\t\t\tWriteArceusGenesect(tableName, PBESpecies.Arceus, PBEForm.Arceus_Poison, PBEType.Poison);\n\t\t\t\tWriteArceusGenesect(tableName, PBESpecies.Arceus, PBEForm.Arceus_Psychic, PBEType.Psychic);\n\t\t\t\tWriteArceusGenesect(tableName, PBESpecies.Arceus, PBEForm.Arceus_Rock, PBEType.Rock);\n\t\t\t\tWriteArceusGenesect(tableName, PBESpecies.Arceus, PBEForm.Arceus_Steel, PBEType.Steel);\n\t\t\t\tWriteArceusGenesect(tableName, PBESpecies.Arceus, PBEForm.Arceus_Water, PBEType.Water);\n\t\t\t\tWriteArceusGenesect(tableName, PBESpecies.Genesect, PBEForm.Genesect, PBEType.Normal);\n\t\t\t\tWriteArceusGenesect(tableName, PBESpecies.Genesect, PBEForm.Genesect_Burn, PBEType.Fire);\n\t\t\t\tWriteArceusGenesect(tableName, PBESpecies.Genesect, PBEForm.Genesect_Chill, PBEType.Ice);\n\t\t\t\tWriteArceusGenesect(tableName, PBESpecies.Genesect, PBEForm.Genesect_Douse, PBEType.Water);\n\t\t\t\tWriteArceusGenesect(tableName, PBESpecies.Genesect, PBEForm.Genesect_Shock, PBEType.Electric);\n\t\t\t}\n\t\t\t// Genders (Does not have PBEGender.Genderless)\n\t\t\t{\n\t\t\t\tLoadTexts(441);\n\t\t\t\tconst string tableName = \"GenderNames\";\n\t\t\t\tCreateTable(tableName);\n\t\t\t\tWriteTexts(tableName, PBEGender.Female.ToString(), 115);\n\t\t\t\tInsert(tableName, PBEGender.Genderless.ToString(), \"Unknown\", \"Inconnu\", \"Unbekannt\", \"Sconosciuto\", \"不明のすがた\", \"不明のすがた\", \"불명의 모습\", \"Desconocido\");\n\t\t\t\tWriteTexts(tableName, PBEGender.Male.ToString(), 114);\n\t\t\t}\n\t\t\t// Items\n\t\t\t{\n\t\t\t\tPBEItem[] allItems = Enum.GetValues<PBEItem>();\n\t\t\t\tvoid WriteAll(string tableName)\n\t\t\t\t{\n\t\t\t\t\tCreateTable(tableName);\n\t\t\t\t\tforeach (PBEItem item in allItems)\n\t\t\t\t\t{\n\t\t\t\t\t\tWriteTexts(tableName, item.ToString(), (int)item);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tLoadTexts(63);\n\t\t\t\tWriteAll(\"ItemDescriptions\");\n\t\t\t\tLoadTexts(64);\n\t\t\t\tWriteAll(\"ItemNames\");\n\t\t\t}\n\t\t\t// Moves\n\t\t\t{\n\t\t\t\tvoid WriteAll(string tableName)\n\t\t\t\t{\n\t\t\t\t\tCreateTable(tableName);\n\t\t\t\t\tfor (PBEMove i = 0; i < PBEMove.MAX; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tWriteTexts(tableName, i.ToString(), (int)i);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tLoadTexts(402);\n\t\t\t\tWriteAll(\"MoveDescriptions\");\n\t\t\t\tLoadTexts(403);\n\t\t\t\tWriteAll(\"MoveNames\");\n\t\t\t}\n\t\t\t// Natures\n\t\t\t{\n\t\t\t\tLoadTexts(379);\n\t\t\t\tconst string tableName = \"NatureNames\";\n\t\t\t\tCreateTable(tableName);\n\t\t\t\tfor (PBENature i = 0; i < PBENature.MAX; i++)\n\t\t\t\t{\n\t\t\t\t\tWriteTexts(tableName, i.ToString(), (int)i + 35); // Nature 0 is at entry 35 in this file\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Species\n\t\t\t{\n\t\t\t\tvoid WriteAll(string tableName)\n\t\t\t\t{\n\t\t\t\t\tCreateTable(tableName);\n\t\t\t\t\tfor (var i = (PBESpecies)1; i < PBESpecies.MAX; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tWriteTexts(tableName, i.ToString(), (int)i);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tLoadTexts(90);\n\t\t\t\tWriteAll(\"SpeciesNames\");\n\t\t\t\tLoadTexts(442);\n\t\t\t\tWriteAll(\"SpeciesEntries\");\n\t\t\t\tLoadTexts(464);\n\t\t\t\tWriteAll(\"SpeciesCategories\");\n\t\t\t}\n\t\t\t// Stats (Non-Japanese languages do not have PBEStat.Accuracy or PBEStat.Evasion)\n\t\t\t{\n\t\t\t\tLoadTexts(372);\n\t\t\t\tconst string tableName = \"StatNames\";\n\t\t\t\tCreateTable(tableName);\n\t\t\t\tWriteTexts(tableName, PBEStat.HP.ToString(), 0);\n\t\t\t\tWriteTexts(tableName, PBEStat.Attack.ToString(), 1);\n\t\t\t\tWriteTexts(tableName, PBEStat.Defense.ToString(), 2);\n\t\t\t\tWriteTexts(tableName, PBEStat.SpAttack.ToString(), 4);\n\t\t\t\tWriteTexts(tableName, PBEStat.SpDefense.ToString(), 5);\n\t\t\t\tWriteTexts(tableName, PBEStat.Speed.ToString(), 3);\n\t\t\t\tInsert(tableName, PBEStat.Accuracy.ToString(), \"Accuracy\", \"Précision\", \"Genauigkeit\", \"Precisione\", jap[0][6], jap[1][6], \"명중률\", \"Precisión\");\n\t\t\t\tInsert(tableName, PBEStat.Evasion.ToString(), \"Evasiveness\", \"Esquive\", \"Fluchtwert\", \"Elusione\", jap[0][7], jap[1][7], \"회피율\", \"Evasión\");\n\t\t\t}\n\t\t\t// Types (Does not have PBEType.None)\n\t\t\t{\n\t\t\t\tLoadTexts(398);\n\t\t\t\tconst string tableName = \"TypeNames\";\n\t\t\t\tCreateTable(tableName);\n\t\t\t\tconst string none =  \"-----\";\n\t\t\t\tInsert(tableName, PBEType.None.ToString(), none, none, none, none, none, none, none, none);\n\t\t\t\tfor (byte i = 0; i < Utils.Gen5Types.Length; i++)\n\t\t\t\t{\n\t\t\t\t\tWriteTexts(tableName, Utils.Gen5Types[i].ToString(), i);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttransaction.Commit();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineExtras/NARCTextDumper.cs",
    "content": "﻿using Kermalis.SimpleNARC;\nusing System;\nusing System.IO;\nusing System.Text;\n\nnamespace Kermalis.PokemonBattleEngineExtras;\n\ninternal static class NARCTextDumper\n{\n\tprivate static readonly string[] _langs = new string[] { \"English\", \"French\", \"German\", \"Italian\", \"Japanese\", \"Korean\", \"Spanish\" };\n\n\t// You must dump the NARC files yourself (/a/0/0/2 in each language)\n\tpublic static void Dump()\n\t{\n\t\tfor (int lang = 0; lang < _langs.Length; lang++)\n\t\t{\n\t\t\tstring l = _langs[lang];\n\t\t\tstring dir = Path.Combine(@\"../../../\\DumpedData\\Dumped\\Texts\", l);\n\t\t\tDirectory.CreateDirectory(dir);\n\t\t\tvar narc = new NARC($@\"../../../\\DumpedData\\W2{l}Texts.narc\");\n\t\t\tfor (int file = 0; file < narc.Count; file++)\n\t\t\t{\n\t\t\t\tstring[][] fileTexts = Utils.ReadTextFile(narc, file);\n\t\t\t\tfor (int block = 0; block < fileTexts.Length; block++)\n\t\t\t\t{\n\t\t\t\t\tstring[] b = fileTexts[block];\n\t\t\t\t\tstring s = string.Empty;\n\t\t\t\t\tfor (int entry = 0; entry < b.Length; entry++)\n\t\t\t\t\t{\n\t\t\t\t\t\ts += \"Entry \" + entry + ':' + Environment.NewLine;\n\t\t\t\t\t\ts += b[entry] + Environment.NewLine + Environment.NewLine;\n\t\t\t\t\t}\n\t\t\t\t\tFile.WriteAllText(Path.Combine(dir, $\"{file}_{block}.txt\"), s, Encoding.Unicode);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineExtras/PokemonBattleEngineExtras.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n\t<PropertyGroup>\n\t\t<TargetFramework>net7.0</TargetFramework>\n\t\t<OutputType>Exe</OutputType>\n\t\t<RootNamespace>Kermalis.PokemonBattleEngineExtras</RootNamespace>\n\t\t<Authors>Kermalis</Authors>\n\t\t<Copyright>Kermalis</Copyright>\n\t\t<RepositoryUrl>https://github.com/Kermalis/PokemonBattleEngine</RepositoryUrl>\n\t\t<NoWarn />\n\t\t<StartupObject>Kermalis.PokemonBattleEngineExtras.Program</StartupObject>\n\t\t<Nullable>enable</Nullable>\n\t</PropertyGroup>\n\n\t<ItemGroup>\n\t\t<None Remove=\"Dependencies\\**\" />\n\t\t<None Remove=\"DumpedData\\**\" />\n\t\t<PackageReference Include=\"System.Drawing.Common\" Version=\"7.0.0\" />\n\t\t<ProjectReference Include=\"..\\PokemonBattleEngine.DefaultData\\PokemonBattleEngine.DefaultData.csproj\" />\n\t\t<Reference Include=\"Dependencies\\SimpleNARC.dll\" />\n\t</ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "PokemonBattleEngineExtras/PokemonDataDumper.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Data.Utils;\nusing Kermalis.PokemonBattleEngine.DefaultData;\nusing Kermalis.SimpleNARC;\nusing Microsoft.Data.Sqlite;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\n\nnamespace Kermalis.PokemonBattleEngineExtras;\n\ninternal static partial class PokemonDataDumper\n{\n\t// You must dump everything yourself\n\t// The GBA ROMs must all be USA v1.0\n\t// Colo and XD must be USA\n\t// DPPt dumps use overlay files which may or may not have different offsets depending on the region, so just keep in mind I have USA versions of each game\n\t// HGSS/Gen5 dumps should work across all regions\n\t//\n\t// Colo and XD level-up moves are in common.fsys/common_rel.fdat\n\t//\n\t// D, P, and Pt level-up move NARC is /poketool/personal/wotbl.narc (D and P have identical level-up move NARCs)\n\t// D, P, and Pt egg moves are in overlay/overlay_0005.bin\n\t// Pt TMHM moves are in the Pokémon data NARC which is /poketool/personal/pl_personal.narc (Pt changed no TMHM compatibility from DP so I use it alone)\n\t// Pt tutor compatibility is in overlay/overlay_0005.bin\n\t// HG and SS level-up move NARC is /a/0/3/3 (HG and SS have identical level-up move NARCs)\n\t// HG and SS TMHM moves are in the Pokémon data NARC which is /a/0/0/2 (HG and SS have identical Pokémon data NARCs)\n\t// HG and SS tutor compatibility is in /fielddata/wazaoshie/waza_oshie.bin (HG and SS have identical tutor compatibility)\n\t// HG and SS egg move NARC is /a/2/2/9 (HG and SS have identical egg move NARCs)\n\t//\n\t// B2 and W2 evolution NARC is /a/0/1/9 (B2 and W2 have identical evolution NARCs)\n\t// B, W, B2, and W2 level-up move NARC is /a/0/1/8 (B and W have identical level-up move NARCs) (B2 and W2 have identical level-up move NARCs)\n\t// B, W, B2, and W2 TMHM moves are in the Pokémon data NARC which is /a/0/1/6 (B and W have identical Pokémon data NARCs) (B2 and W2 have identical Pokémon data NARCs)\n\t// B2 and W2 tutor compatibility is in the Pokémon data NARC which is /a/0/1/6 (B2 and W2 have identical Pokémon data NARCs)\n\t// B and W egg move NARC is /a/1/2/3, B2 and W2 egg move NARC is /a/1/2/4 (B, W, B2, and W2 have identical egg move NARCs)\n\n\tprivate static Dictionary<(PBESpecies, PBEForm), Pokemon> _dict = null!;\n\tprivate static EndianBinaryReader _r = null!;\n\tprivate static EndianBinaryReader _s = null!;\n\tprivate static EndianBinaryReader _fr = null!;\n\tprivate static EndianBinaryReader _lg = null!;\n\tprivate static EndianBinaryReader _e = null!;\n\tprivate static EndianBinaryReader _coloCommonRel = null!;\n\tprivate static EndianBinaryReader _xdCommonRel = null!;\n\n\tpublic static void Run(SqliteConnection con)\n\t{\n\t\tusing (FileStream s_r = File.OpenRead(@\"../../../\\DumpedData\\R.gba\"))\n\t\tusing (FileStream s_s = File.OpenRead(@\"../../../\\DumpedData\\S.gba\"))\n\t\tusing (FileStream s_fr = File.OpenRead(@\"../../../\\DumpedData\\FR.gba\"))\n\t\tusing (FileStream s_lg = File.OpenRead(@\"../../../\\DumpedData\\LG.gba\"))\n\t\tusing (FileStream s_e = File.OpenRead(@\"../../../\\DumpedData\\E.gba\"))\n\t\tusing (FileStream s_colo = File.OpenRead(@\"../../../\\DumpedData\\Colocommon_rel.fdat\"))\n\t\tusing (FileStream s_xd = File.OpenRead(@\"../../../\\DumpedData\\XDcommon_rel.fdat\"))\n\t\tusing (SqliteTransaction transaction = con.BeginTransaction())\n\t\tusing (SqliteCommand cmd = con.CreateCommand())\n\t\t{\n\t\t\tcmd.Transaction = transaction;\n\t\t\t_dict = new();\n\n\t\t\t_r = new EndianBinaryReader(s_r, endianness: Endianness.LittleEndian);\n\t\t\t_s = new EndianBinaryReader(s_s, endianness: Endianness.LittleEndian);\n\t\t\t_fr = new EndianBinaryReader(s_fr, endianness: Endianness.LittleEndian);\n\t\t\t_lg = new EndianBinaryReader(s_lg, endianness: Endianness.LittleEndian);\n\t\t\t_e = new EndianBinaryReader(s_e, endianness: Endianness.LittleEndian);\n\t\t\t_coloCommonRel = new EndianBinaryReader(s_colo, endianness: Endianness.BigEndian);\n\t\t\t_xdCommonRel = new EndianBinaryReader(s_xd, endianness: Endianness.BigEndian);\n\n\t\t\tB2W2_PokeData();\n\n\t\t\tGen3_LevelUp();\n\t\t\tGen4_LevelUp();\n\t\t\tGen5_LevelUp();\n\n\t\t\tGen3_TMHM();\n\t\t\tGen4_TMHM();\n\t\t\tGen5_TMHM();\n\n\t\t\tFRLGE_Tutor();\n\t\t\tXD_Tutor();\n\t\t\tPt_Tutor();\n\t\t\tHGSS_Tutor();\n\t\t\tGen5_Tutor();\n\n\t\t\tGen34_Egg();\n\t\t\tGen5_Egg();\n\n\t\t\tFix_FormPokedata();\n\t\t\tFix_BasculinBlueAbility();\n\t\t\tFix_ArceusTypes();\n\t\t\tFix_FormEggMoves();\n\t\t\tFix_ShedinjaEvoMoves();\n\t\t\tFix_FRLGStarterMoves();\n\t\t\tFix_XDMew();\n\t\t\tFix_VoltTackle();\n\t\t\tFix_Gen4FreeMoves();\n\t\t\tFix_RotomFormMoves();\n\t\t\tFix_RelicSong_SecretSword();\n\n\t\t\tDreamWorld();\n\n\t\t\tWriteToDatabase(cmd);\n\n\t\t\ttransaction.Commit();\n\t\t}\n\t}\n\n\tprivate static Pokemon AddSpecies((PBESpecies, PBEForm) key)\n\t{\n\t\tif (!_dict.TryGetValue(key, out Pokemon? pkmn))\n\t\t{\n\t\t\tpkmn = new Pokemon();\n\t\t\t_dict.Add(key, pkmn);\n\t\t}\n\t\treturn pkmn;\n\t}\n\tprivate static void AddLevelUpMove((PBESpecies, PBEForm) key, PBEMove move, byte level, PBEDDMoveObtainMethod flag)\n\t{\n\t\tPokemon pkmn = AddSpecies(key);\n\t\tDictionary<(PBEMove, byte), PBEDDMoveObtainMethod> levelUp = pkmn.LevelUpMoves;\n\t\t(PBEMove, byte) lKey = (move, level);\n\t\tif (levelUp.ContainsKey(lKey))\n\t\t{\n\t\t\tlevelUp[lKey] |= flag;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlevelUp.Add(lKey, flag);\n\t\t}\n\t}\n\tprivate static void AddOtherMove((PBESpecies, PBEForm) key, PBEMove move, PBEDDMoveObtainMethod flag)\n\t{\n\t\tPokemon pkmn = AddSpecies(key);\n\t\tDictionary<PBEMove, PBEDDMoveObtainMethod> other = pkmn.OtherMoves;\n\t\tif (other.ContainsKey(move))\n\t\t{\n\t\t\tother[move] |= flag;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tother.Add(move, flag);\n\t\t}\n\t}\n\tprivate static void AddEvolution((PBESpecies, PBEForm) baybee, (PBESpecies, PBEForm) dadee)\n\t{\n\t\tAddSpecies(baybee).Evolutions.Add(dadee);\n\t\tAddSpecies(dadee).PreEvolutions.Add(baybee);\n\t}\n\tprivate static (PBESpecies, PBEForm) GetGen3Key(EndianBinaryReader reader, int sp)\n\t{\n\t\tPBESpecies species = _gen3SpeciesIndexToPBESpecies[sp];\n\t\tPBEForm form = 0;\n\t\tif (species == PBESpecies.Deoxys)\n\t\t{\n\t\t\tif (reader == _e)\n\t\t\t{\n\t\t\t\tform = PBEForm.Deoxys_Speed;\n\t\t\t}\n\t\t\telse if (reader == _lg)\n\t\t\t{\n\t\t\t\tform = PBEForm.Deoxys_Defense;\n\t\t\t}\n\t\t\telse if (reader == _fr)\n\t\t\t{\n\t\t\t\tform = PBEForm.Deoxys_Attack;\n\t\t\t}\n\t\t}\n\t\treturn (species, form);\n\t}\n\n\tprivate static void B2W2_PokeData()\n\t{\n\t\tvar b2w2Pokedata = new NARC(@\"../../../\\DumpedData\\B2W2Pokedata.narc\");\n\t\tvar b2w2Evolution = new NARC(@\"../../../\\DumpedData\\B2W2Evolution.narc\");\n\n\t\tfor (int sp = 1; sp <= 708; sp++)\n\t\t{\n\t\t\t// Skip Egg, Bad Egg, and Pokéstar Studios Pokémon\n\t\t\tif (sp > 649 && sp < 685)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!_b2w2SpeciesIndexToPBESpecies.TryGetValue(sp, out (PBESpecies, PBEForm) key))\n\t\t\t{\n\t\t\t\tkey = ((PBESpecies)sp, 0);\n\t\t\t}\n\t\t\tPokemon pkmn = AddSpecies(key);\n\n\t\t\tusing (var s_p = new MemoryStream(b2w2Pokedata[sp]))\n\t\t\tusing (var s_e = new MemoryStream(b2w2Evolution[sp]))\n\t\t\t{\n\t\t\t\tvar pokedata = new EndianBinaryReader(s_p, endianness: Endianness.LittleEndian);\n\t\t\t\tvar evolution = new EndianBinaryReader(s_e, endianness: Endianness.LittleEndian);\n\n\t\t\t\t// Pokedata\n\t\t\t\tpkmn.HP = pokedata.ReadByte(0x0);\n\t\t\t\tpkmn.Attack = pokedata.ReadByte(0x1);\n\t\t\t\tpkmn.Defense = pokedata.ReadByte(0x2);\n\t\t\t\tpkmn.SpAttack = pokedata.ReadByte(0x4);\n\t\t\t\tpkmn.SpDefense = pokedata.ReadByte(0x5);\n\t\t\t\tpkmn.Speed = pokedata.ReadByte(0x3);\n\t\t\t\tpkmn.Type1 = Utils.Gen5Types[pokedata.ReadByte(0x6)];\n\t\t\t\tpkmn.Type2 = Utils.Gen5Types[pokedata.ReadByte(0x7)];\n\t\t\t\tif (pkmn.Type1 == pkmn.Type2)\n\t\t\t\t{\n\t\t\t\t\tpkmn.Type2 = PBEType.None;\n\t\t\t\t}\n\t\t\t\tpkmn.CatchRate = pokedata.ReadByte(0x8);\n\t\t\t\tpkmn.GenderRatio = (PBEGenderRatio)pokedata.ReadByte(0x12);\n\t\t\t\tpkmn.GrowthRate = (PBEGrowthRate)pokedata.ReadByte(0x15);\n\t\t\t\tfor (int i = 0; i < 3; i++)\n\t\t\t\t{\n\t\t\t\t\tvar ability = (PBEAbility)pokedata.ReadByte(0x18 + i);\n\t\t\t\t\tif (ability != PBEAbility.None && !pkmn.Abilities.Contains(ability))\n\t\t\t\t\t{\n\t\t\t\t\t\tpkmn.Abilities.Add(ability);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tpkmn.FleeRate = pokedata.ReadByte(0x1B);\n\t\t\t\tpkmn.BaseEXPYield = pokedata.ReadUInt16(0x22);\n\t\t\t\tpkmn.Weight = MathF.Round(pokedata.ReadUInt16(0x26) * 0.1f, 1);\n\t\t\t\t// Evolution\n\t\t\t\tfor (int i = 0; i < 7; i++)\n\t\t\t\t{\n\t\t\t\t\tushort method = evolution.ReadUInt16();\n\t\t\t\t\tevolution.ReadUInt16(); // Param\n\t\t\t\t\tvar evo = (PBESpecies)evolution.ReadUInt16();\n\t\t\t\t\tif (method != 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tAddEvolution(key, (evo, 0));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void Gen3_LevelUp()\n\t{\n#pragma warning disable CS8321 // Local function is declared but never used\n\t\tfor (int sp = 1; sp <= 411; sp++)\n\t\t{\n\t\t\t// Gen 2 Unown slots are ignored in gen 3\n\t\t\tif (sp > 251 && sp < 277)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// It is the same in Ruby, Sapphire, Colo, and XD; the others have some differences\n\t\t\t_r.Stream.Position = 0x207BC8 + (sizeof(uint) * sp);\n\t\t\t_s.Stream.Position = 0x207B58 + (sizeof(uint) * sp);\n\t\t\t_fr.Stream.Position = 0x25D7B4 + (sizeof(uint) * sp);\n\t\t\t_lg.Stream.Position = 0x25D794 + (sizeof(uint) * sp);\n\t\t\t_e.Stream.Position = 0x32937C + (sizeof(uint) * sp);\n\t\t\t_coloCommonRel.Stream.Position = 0x123250 + (0x11C * sp) + 0xBA;\n\t\t\t_xdCommonRel.Stream.Position = 0x29DA8 + (0x124 * sp) + 0xC4;\n\n\t\t\tvoid ReadGBALevelUpMoves(EndianBinaryReader reader, PBEDDMoveObtainMethod flag)\n\t\t\t{\n\t\t\t\t(PBESpecies, PBEForm) key = GetGen3Key(reader, sp);\n\t\t\t\treader.Stream.Position = reader.ReadUInt32() - 0x8000000;\n\t\t\t\twhile (true)\n\t\t\t\t{\n\t\t\t\t\tushort val = reader.ReadUInt16();\n\t\t\t\t\tif (val == 0xFFFF)\n\t\t\t\t\t{\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tAddLevelUpMove(key, (PBEMove)(val & 0x1FF), (byte)(val >> 9), flag);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tReadGBALevelUpMoves(_r, PBEDDMoveObtainMethod.LevelUp_RSColoXD);\n\t\t\t//ReadGBALevelUpMoves(_s, PBEDDMoveObtainMethod.LevelUp_RSColoXD);\n\t\t\tReadGBALevelUpMoves(_fr, PBEDDMoveObtainMethod.LevelUp_FR);\n\t\t\tReadGBALevelUpMoves(_lg, PBEDDMoveObtainMethod.LevelUp_LG);\n\t\t\tReadGBALevelUpMoves(_e, PBEDDMoveObtainMethod.LevelUp_E);\n\t\t\tvoid ReadGCLevelUpMoves(EndianBinaryReader reader, PBEDDMoveObtainMethod flag)\n\t\t\t{\n\t\t\t\t(PBESpecies, PBEForm) key = GetGen3Key(reader, sp);\n\t\t\t\tfor (int i = 0; i < 17; i++)\n\t\t\t\t{\n\t\t\t\t\tbyte level = reader.ReadByte();\n\t\t\t\t\treader.ReadByte(); // Padding\n\t\t\t\t\tvar move = (PBEMove)reader.ReadUInt16();\n\t\t\t\t\tif (move == PBEMove.None)\n\t\t\t\t\t{\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tAddLevelUpMove(key, move, level, flag);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t//ReadGCLevelUpMoves(_coloCommonRel, PBEDDMoveObtainMethod.LevelUp_RSColoXD);\n\t\t\t//ReadGCLevelUpMoves(_xdCommonRel, PBEDDMoveObtainMethod.LevelUp_RSColoXD);\n\t\t}\n#pragma warning restore CS8321 // Local function is declared but never used\n\t}\n\tprivate static void Gen4_LevelUp()\n\t{\n\t\tvar dp = new NARC(@\"../../../\\DumpedData\\DPLevelUp.narc\");\n\t\tvar pt = new NARC(@\"../../../\\DumpedData\\PtLevelUp.narc\");\n\t\tvar hgss = new NARC(@\"../../../\\DumpedData\\HGSSLevelUp.narc\");\n\n\t\tfor (int sp = 1; sp <= 507; sp++)\n\t\t{\n\t\t\t// 494 is Egg, 495 is Bad Egg\n\t\t\tif (sp == 494 || sp == 495)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!_gen4SpeciesIndexToPBESpecies.TryGetValue(sp, out (PBESpecies, PBEForm) key))\n\t\t\t{\n\t\t\t\tkey = ((PBESpecies)sp, 0);\n\t\t\t}\n\n\t\t\tvoid ReadLevelUpMoves(byte[] file, PBEDDMoveObtainMethod flag)\n\t\t\t{\n\t\t\t\tusing (var ms = new MemoryStream(file))\n\t\t\t\t{\n\t\t\t\t\tvar reader = new EndianBinaryReader(ms, endianness: Endianness.LittleEndian);\n\t\t\t\t\twhile (true)\n\t\t\t\t\t{\n\t\t\t\t\t\tushort val = reader.ReadUInt16();\n\t\t\t\t\t\tif (val == 0xFFFF)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tAddLevelUpMove(key, (PBEMove)(val & 0x1FF), (byte)(val >> 9), flag);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// DP only has 0-500\n\t\t\tif (sp <= 500)\n\t\t\t{\n\t\t\t\tReadLevelUpMoves(dp[sp], PBEDDMoveObtainMethod.LevelUp_DP);\n\t\t\t}\n\t\t\tReadLevelUpMoves(pt[sp], PBEDDMoveObtainMethod.LevelUp_Pt);\n\t\t\tReadLevelUpMoves(hgss[sp], PBEDDMoveObtainMethod.LevelUp_HGSS);\n\t\t}\n\t}\n\tprivate static void Gen5_LevelUp()\n\t{\n\t\tvar bw = new NARC(@\"../../../\\DumpedData\\BWLevelUp.narc\");\n\t\tvar b2w2 = new NARC(@\"../../../\\DumpedData\\B2W2LevelUp.narc\");\n\t\tfor (int sp = 1; sp <= 708; sp++)\n\t\t{\n\t\t\tvoid ReadLevelUpMoves(byte[] file, bool isBW)\n\t\t\t{\n\t\t\t\tif (!(isBW ? _bwSpeciesIndexToPBESpecies : _b2w2SpeciesIndexToPBESpecies).TryGetValue(sp, out (PBESpecies, PBEForm) key))\n\t\t\t\t{\n\t\t\t\t\tkey = ((PBESpecies)sp, 0);\n\t\t\t\t}\n\n\t\t\t\tusing (var ms = new MemoryStream(file))\n\t\t\t\t{\n\t\t\t\t\tvar reader = new EndianBinaryReader(ms, endianness: Endianness.LittleEndian);\n\t\t\t\t\twhile (true)\n\t\t\t\t\t{\n\t\t\t\t\t\tuint val = reader.ReadUInt32();\n\t\t\t\t\t\tif (val == 0xFFFFFFFF)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tAddLevelUpMove(key, (PBEMove)val, (byte)(val >> 0x10), isBW ? PBEDDMoveObtainMethod.LevelUp_BW : PBEDDMoveObtainMethod.LevelUp_B2W2);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// BW only has 0-667 (no Egg or Bad Egg)\n\t\t\tif (sp <= 667)\n\t\t\t{\n\t\t\t\tReadLevelUpMoves(bw[sp], true);\n\t\t\t}\n\t\t\t// Skip Egg, Bad Egg, and Pokéstar Studios Pokémon in B2W2\n\t\t\tif (sp <= 649 || sp >= 685)\n\t\t\t{\n\t\t\t\tReadLevelUpMoves(b2w2[sp], false);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void Gen3_TMHM()\n\t{\n#pragma warning disable CS8321 // Local function is declared but never used\n\t\tfor (int sp = 1; sp <= 411; sp++)\n\t\t{\n\t\t\t// Gen 2 Unown slots are ignored in gen 3\n\t\t\tif (sp > 251 && sp < 277)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// It is the same across all of gen 3, so I will only read one\n\t\t\t_r.Stream.Position = 0x1FD0F0 + (8 * sp);\n\t\t\t_s.Stream.Position = 0x1FD080 + (8 * sp);\n\t\t\t_fr.Stream.Position = 0x252BC8 + (8 * sp);\n\t\t\t_lg.Stream.Position = 0x252BA4 + (8 * sp);\n\t\t\t_e.Stream.Position = 0x31E898 + (8 * sp);\n\t\t\t_coloCommonRel.Stream.Position = 0x123250 + (0x11C * sp) + 0x34;\n\t\t\t_xdCommonRel.Stream.Position = 0x29DA8 + (0x124 * sp) + 0x34;\n\n\t\t\tPBEDDMoveObtainMethod GetFlag(int i)\n\t\t\t{\n\t\t\t\treturn i < 50 ? PBEDDMoveObtainMethod.TM_RSFRLGEColoXD : PBEDDMoveObtainMethod.HM_RSFRLGEColoXD;\n\t\t\t}\n\t\t\tvoid ReadGBATMHM(EndianBinaryReader reader)\n\t\t\t{\n\t\t\t\t(PBESpecies, PBEForm) key = GetGen3Key(reader, sp);\n\t\t\t\tSpan<byte> bytes = stackalloc byte[8];\n\t\t\t\treader.ReadBytes(bytes);\n\t\t\t\tfor (int i = 0; i < _gen3TMHMs.Length; i++)\n\t\t\t\t{\n\t\t\t\t\tif ((bytes[i / 8] & (1 << (i % 8))) != 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tAddOtherMove(key, _gen3TMHMs[i], GetFlag(i));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tReadGBATMHM(_r);\n\t\t\t//ReadGBATMHM(_s);\n\t\t\t//ReadGBATMHM(_fr);\n\t\t\t//ReadGBATMHM(_lg);\n\t\t\t//ReadGBATMHM(_e);\n\t\t\tvoid ReadGCTMHM(EndianBinaryReader reader)\n\t\t\t{\n\t\t\t\t(PBESpecies, PBEForm) key = GetGen3Key(reader, sp);\n\t\t\t\tfor (int i = 0; i < _gen3TMHMs.Length; i++)\n\t\t\t\t{\n\t\t\t\t\tif (reader.ReadBoolean())\n\t\t\t\t\t{\n\t\t\t\t\t\tAddOtherMove(key, _gen3TMHMs[i], GetFlag(i));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t//ReadGCTMHM(_coloCommonRel);\n\t\t\t//ReadGCTMHM(_xdCommonRel);\n\t\t}\n#pragma warning restore CS8321 // Local function is declared but never used\n\t}\n\tprivate static void Gen4_TMHM()\n\t{\n\t\tvar dppt = new NARC(@\"../../../\\DumpedData\\PtPokedata.narc\");\n\t\tvar hgss = new NARC(@\"../../../\\DumpedData\\HGSSPokedata.narc\");\n\n\t\tfor (int sp = 1; sp <= 507; sp++)\n\t\t{\n\t\t\t// 494 is Egg, 495 is Bad Egg\n\t\t\tif (sp == 494 || sp == 495)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!_gen4SpeciesIndexToPBESpecies.TryGetValue(sp, out (PBESpecies, PBEForm) key))\n\t\t\t{\n\t\t\t\tkey = ((PBESpecies)sp, 0);\n\t\t\t}\n\t\t\tvoid ReadTMHMMoves(byte[] file, bool isDPPt)\n\t\t\t{\n\t\t\t\tSpan<byte> bytes = stackalloc byte[13];\n\t\t\t\tusing (var ms = new MemoryStream(file))\n\t\t\t\t{\n\t\t\t\t\tvar reader = new EndianBinaryReader(ms, endianness: Endianness.LittleEndian);\n\t\t\t\t\tms.Position = 0x1C;\n\t\t\t\t\treader.ReadBytes(bytes);\n\t\t\t\t\tfor (int i = 0; i < _gen4TMHMs.Length; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif ((bytes[i / 8] & (1 << (i % 8))) != 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tPBEMove move = _gen4TMHMs[i];\n\t\t\t\t\t\t\tif (move == PBEMove.None)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tmove = isDPPt ? PBEMove.Defog : PBEMove.Whirlpool;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tAddOtherMove(key, move, i < 92 ? (isDPPt ? PBEDDMoveObtainMethod.TM_DPPt : PBEDDMoveObtainMethod.TM_HGSS) : (isDPPt ? PBEDDMoveObtainMethod.HM_DPPt : PBEDDMoveObtainMethod.HM_HGSS));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tReadTMHMMoves(dppt[sp], true);\n\t\t\tReadTMHMMoves(hgss[sp], false);\n\t\t}\n\t}\n\tprivate static void Gen5_TMHM()\n\t{\n\t\tvar bw = new NARC(@\"../../../\\DumpedData\\BWPokedata.narc\");\n\t\tvar b2w2 = new NARC(@\"../../../\\DumpedData\\B2W2Pokedata.narc\");\n\n\t\tfor (int sp = 1; sp <= 708; sp++)\n\t\t{\n\t\t\tvoid ReadTMHMMoves(byte[] file, bool isBW)\n\t\t\t{\n\t\t\t\tif (!(isBW ? _bwSpeciesIndexToPBESpecies : _b2w2SpeciesIndexToPBESpecies).TryGetValue(sp, out (PBESpecies, PBEForm) key))\n\t\t\t\t{\n\t\t\t\t\tkey = ((PBESpecies)sp, 0);\n\t\t\t\t}\n\n\t\t\t\tSpan<byte> bytes = stackalloc byte[13];\n\t\t\t\tusing (var ms = new MemoryStream(file))\n\t\t\t\t{\n\t\t\t\t\tvar reader = new EndianBinaryReader(ms, endianness: Endianness.LittleEndian);\n\t\t\t\t\tms.Position = 0x28;\n\t\t\t\t\treader.ReadBytes(bytes);\n\t\t\t\t\tfor (int i = 0; i < _gen5TMHMs.Length; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif ((bytes[i / 8] & (1 << (i % 8))) == 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tPBEDDMoveObtainMethod flag;\n\t\t\t\t\t\tif (i < 95)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tflag = isBW ? PBEDDMoveObtainMethod.TM_BW : PBEDDMoveObtainMethod.TM_B2W2;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tflag = PBEDDMoveObtainMethod.HM_BWB2W2;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tAddOtherMove(key, _gen5TMHMs[i], flag);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// BW only has 0-667 (no Egg or Bad Egg)\n\t\t\tif (sp <= 667)\n\t\t\t{\n\t\t\t\tReadTMHMMoves(bw[sp], true);\n\t\t\t}\n\t\t\t// Skip Egg, Bad Egg, and Pokéstar Studios Pokémon in B2W2\n\t\t\tif (sp <= 649 || sp >= 685)\n\t\t\t{\n\t\t\t\tReadTMHMMoves(b2w2[sp], false);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void FRLGE_Tutor()\n\t{\n\t\tfor (int sp = 1; sp <= 411; sp++)\n\t\t{\n\t\t\t// Gen 2 Unown slots are ignored in gen 3\n\t\t\tif (sp > 251 && sp < 277)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// It is the same in FR and LG, so I will only read one\n\t\t\t_fr.Stream.Position = 0x459B7E + (sizeof(ushort) * sp);\n\t\t\t_lg.Stream.Position = 0x45959E + (sizeof(ushort) * sp);\n\t\t\t_e.Stream.Position = 0x615048 + (sizeof(uint) * sp);\n\n\t\t\tvoid ReadTutorMoves(EndianBinaryReader reader, PBEMove[] tutorMoves, PBEDDMoveObtainMethod flag)\n\t\t\t{\n\t\t\t\t(PBESpecies, PBEForm) key = GetGen3Key(reader, sp);\n\t\t\t\tuint val = reader == _e ? reader.ReadUInt32() : reader.ReadUInt16();\n\t\t\t\tfor (int i = 0; i < tutorMoves.Length; i++)\n\t\t\t\t{\n\t\t\t\t\tif ((val & (1u << i)) != 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tAddOtherMove(key, tutorMoves[i], flag);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tReadTutorMoves(_fr, _frlgTutorMoves, PBEDDMoveObtainMethod.MoveTutor_FRLG);\n\t\t\t//ReadTutorMoves(_lg, frlgTutorMoves, PBEDDMoveObtainMethod.MoveTutor_FRLG);\n\t\t\tReadTutorMoves(_e, _emeraldTutorMoves, PBEDDMoveObtainMethod.MoveTutor_E);\n\t\t}\n\t}\n\tprivate static void XD_Tutor()\n\t{\n\t\tfor (int sp = 1; sp <= 411; sp++)\n\t\t{\n\t\t\t// Gen 2 Unown slots are ignored in gen 3\n\t\t\tif (sp > 251 && sp < 277)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t(PBESpecies, PBEForm) key = GetGen3Key(_xdCommonRel, sp);\n\t\t\t_xdCommonRel.Stream.Position = 0x29DA8 + (0x124 * sp) + 0x6E;\n\t\t\tfor (int i = 0; i < _xdTutorMoves.Length; i++)\n\t\t\t{\n\t\t\t\tif (_xdCommonRel.ReadBoolean())\n\t\t\t\t{\n\t\t\t\t\tAddOtherMove(key, _xdTutorMoves[i], PBEDDMoveObtainMethod.MoveTutor_XD);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tprivate static void Pt_Tutor()\n\t{\n\t\tusing (FileStream fs = File.OpenRead(@\"../../../\\DumpedData\\Ptoverlay_0005.bin\"))\n\t\t{\n\t\t\tvar pt = new EndianBinaryReader(fs, endianness: Endianness.LittleEndian);\n\t\t\tSpan<byte> bytes = stackalloc byte[5];\n\n\t\t\tfs.Position = 0x3012C;\n\t\t\tfor (int sp = 1; sp <= 493; sp++)\n\t\t\t{\n\t\t\t\t(PBESpecies, PBEForm) key = ((PBESpecies)sp, 0);\n\t\t\t\tpt.ReadBytes(bytes);\n\n\t\t\t\tfor (int i = 0; i < _ptTutorMoves.Length; i++)\n\t\t\t\t{\n\t\t\t\t\tif ((bytes[i / 8] & (1 << (i % 8))) != 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tAddOtherMove(key, _ptTutorMoves[i], PBEDDMoveObtainMethod.MoveTutor_Pt);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tprivate static void HGSS_Tutor()\n\t{\n\t\tusing (FileStream fs = File.OpenRead(@\"../../../\\DumpedData\\HGSSTutor.bin\"))\n\t\t{\n\t\t\tvar r = new EndianBinaryReader(fs, endianness: Endianness.LittleEndian);\n\t\t\tSpan<byte> bytes = stackalloc byte[8];\n\n\t\t\tfor (int sp = 1; sp <= 505; sp++) // Includes forms but not eggs\n\t\t\t{\n\t\t\t\t(PBESpecies, PBEForm) key = sp > 493 ? _gen4SpeciesIndexToPBESpecies[sp + 2] : ((PBESpecies)sp, 0);\n\t\t\t\tr.ReadBytes(bytes);\n\n\t\t\t\tfor (int i = 0; i < _hgssTutorMoves.Length; i++)\n\t\t\t\t{\n\t\t\t\t\tif ((bytes[i / 8] & (1 << (i % 8))) != 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tAddOtherMove(key, _hgssTutorMoves[i], PBEDDMoveObtainMethod.MoveTutor_HGSS);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tprivate static void Gen5_Tutor()\n\t{\n\t\tvar bw = new NARC(@\"../../../\\DumpedData\\BWPokedata.narc\");\n\t\tvar b2w2 = new NARC(@\"../../../\\DumpedData\\B2W2Pokedata.narc\");\n\n\t\tfor (int sp = 1; sp <= 708; sp++)\n\t\t{\n\t\t\tvoid ReadFreeTutorMoves(EndianBinaryReader reader, bool isBW)\n\t\t\t{\n\t\t\t\tif (!(isBW ? _bwSpeciesIndexToPBESpecies : _b2w2SpeciesIndexToPBESpecies).TryGetValue(sp, out (PBESpecies, PBEForm) key))\n\t\t\t\t{\n\t\t\t\t\tkey = ((PBESpecies)sp, 0);\n\t\t\t\t}\n\t\t\t\tbyte val = reader.ReadByte(0x38);\n\t\t\t\tfor (int i = 0; i < _gen5FreeTutorMoves.Length; i++)\n\t\t\t\t{\n\t\t\t\t\tif ((val & (1 << i)) != 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tAddOtherMove(key, _gen5FreeTutorMoves[i], isBW ? PBEDDMoveObtainMethod.MoveTutor_BW : PBEDDMoveObtainMethod.MoveTutor_B2W2);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tvoid ReadB2W2TutorMoves(EndianBinaryReader reader)\n\t\t\t{\n\t\t\t\tif (!_b2w2SpeciesIndexToPBESpecies.TryGetValue(sp, out (PBESpecies, PBEForm) key))\n\t\t\t\t{\n\t\t\t\t\tkey = ((PBESpecies)sp, 0);\n\t\t\t\t}\n\t\t\t\treader.Stream.Position = 0x3C;\n\t\t\t\tfor (int i = 0; i < _b2w2TutorMoves.Length; i++)\n\t\t\t\t{\n\t\t\t\t\tuint val = reader.ReadUInt32();\n\t\t\t\t\tfor (int j = 0; j < _b2w2TutorMoves[i].Length; j++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif ((val & (1u << j)) != 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tAddOtherMove(key, _b2w2TutorMoves[i][j], PBEDDMoveObtainMethod.MoveTutor_B2W2);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// BW only has 0-667 (no Egg or Bad Egg)\n\t\t\tif (sp <= 667)\n\t\t\t{\n\t\t\t\tusing (var ms = new MemoryStream(bw[sp]))\n\t\t\t\t{\n\t\t\t\t\tvar reader = new EndianBinaryReader(ms, endianness: Endianness.LittleEndian);\n\t\t\t\t\tReadFreeTutorMoves(reader, true);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Skip Egg, Bad Egg, and Pokéstar Studios Pokémon\n\t\t\tif (sp <= 649 || sp >= 685)\n\t\t\t{\n\t\t\t\tusing (var ms = new MemoryStream(b2w2[sp]))\n\t\t\t\t{\n\t\t\t\t\tvar reader = new EndianBinaryReader(ms, endianness: Endianness.LittleEndian);\n\t\t\t\t\tReadFreeTutorMoves(reader, false);\n\t\t\t\t\tReadB2W2TutorMoves(reader);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void Gen34_Egg()\n\t{\n\t\tusing (FileStream s_d = File.OpenRead(@\"../../../\\DumpedData\\Doverlay_0005.bin\"))\n\t\tusing (FileStream s_p = File.OpenRead(@\"../../../\\DumpedData\\Poverlay_0005.bin\"))\n\t\tusing (FileStream s_pt = File.OpenRead(@\"../../../\\DumpedData\\Ptoverlay_0005.bin\"))\n\t\tusing (var s_hgss = new MemoryStream(new NARC(@\"../../../\\DumpedData\\HGSSEgg.narc\")[0]))\n\t\t{\n\t\t\tvar d = new EndianBinaryReader(s_d, endianness: Endianness.LittleEndian);\n\t\t\tvar p = new EndianBinaryReader(s_p, endianness: Endianness.LittleEndian);\n\t\t\tvar pt = new EndianBinaryReader(s_pt, endianness: Endianness.LittleEndian);\n\t\t\tvar hgss = new EndianBinaryReader(s_hgss, endianness: Endianness.LittleEndian);\n\n\t\t\t// The table is the same in all five GBA games, so I will only read one\n\t\t\t_r.Stream.Position = 0x2091DC;\n\t\t\t_s.Stream.Position = 0x20916C;\n\t\t\t_fr.Stream.Position = 0x25EF0C;\n\t\t\t_lg.Stream.Position = 0x25EEEC;\n\t\t\t_e.Stream.Position = 0x32ADD8;\n\t\t\t// The table is the same across DPPt, so I will only read one\n\t\t\td.Stream.Position = 0x20668;\n\t\t\tp.Stream.Position = 0x20668;\n\t\t\tpt.Stream.Position = 0x29222;\n\n\t\t\tstatic void ReadEggMoves(EndianBinaryReader reader, bool isGen3, PBEDDMoveObtainMethod flag)\n\t\t\t{\n\t\t\t\tPBESpecies species = 0;\n\t\t\t\twhile (true)\n\t\t\t\t{\n\t\t\t\t\tushort val = reader.ReadUInt16();\n\t\t\t\t\tif (val == 0xFFFF)\n\t\t\t\t\t{\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\telse if (val > 20000)\n\t\t\t\t\t{\n\t\t\t\t\t\tint speciesIndex = val - 20000;\n\t\t\t\t\t\tspecies = isGen3 ? _gen3SpeciesIndexToPBESpecies[speciesIndex] : (PBESpecies)speciesIndex;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tAddOtherMove((species, 0), (PBEMove)val, flag);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tReadEggMoves(_r, true, PBEDDMoveObtainMethod.EggMove_RSFRLGE);\n\t\t\t//ReadEggMoves(_s, true, PBEDDMoveObtainMethod.EggMove_RSFRLGE);\n\t\t\t//ReadEggMoves(_fr, true, PBEDDMoveObtainMethod.EggMove_RSFRLGE);\n\t\t\t//ReadEggMoves(_lg, true, PBEDDMoveObtainMethod.EggMove_RSFRLGE);\n\t\t\t//ReadEggMoves(_e, true, PBEDDMoveObtainMethod.EggMove_RSFRLGE);\n\t\t\tReadEggMoves(d, false, PBEDDMoveObtainMethod.EggMove_DPPt);\n\t\t\t//ReadEggMoves(p, false, PBEDDMoveObtainMethod.EggMove_DPPt);\n\t\t\t//ReadEggMoves(pt, false, PBEDDMoveObtainMethod.EggMove_DPPt);\n\t\t\tReadEggMoves(hgss, false, PBEDDMoveObtainMethod.EggMove_HGSS);\n\t\t}\n\t}\n\tprivate static void Gen5_Egg()\n\t{\n\t\tvar bwb2w2 = new NARC(@\"../../../\\DumpedData\\BWB2W2Egg.narc\");\n\t\tfor (int sp = 1; sp <= 649; sp++)\n\t\t{\n\t\t\tusing (var ms = new MemoryStream(bwb2w2[sp]))\n\t\t\t{\n\t\t\t\tvar reader = new EndianBinaryReader(ms, endianness: Endianness.LittleEndian);\n\t\t\t\tushort numEggMoves = reader.ReadUInt16();\n\t\t\t\tif (numEggMoves > 0)\n\t\t\t\t{\n\t\t\t\t\t(PBESpecies, PBEForm) key = ((PBESpecies)sp, 0);\n\t\t\t\t\tfor (int i = 0; i < numEggMoves; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tAddOtherMove(key, (PBEMove)reader.ReadUInt16(), PBEDDMoveObtainMethod.EggMove_BWB2W2);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void Fix_FormPokedata()\n\t{\n\t\tstatic void CopySpecies((PBESpecies, PBEForm) baseKey, (PBESpecies, PBEForm) newKey)\n\t\t{\n\t\t\tPokemon basePkmn = _dict[baseKey];\n\t\t\tPokemon pkmn = AddSpecies(newKey);\n\t\t\tpkmn.HP = basePkmn.HP;\n\t\t\tpkmn.Attack = basePkmn.Attack;\n\t\t\tpkmn.Defense = basePkmn.Defense;\n\t\t\tpkmn.SpAttack = basePkmn.SpAttack;\n\t\t\tpkmn.SpDefense = basePkmn.SpDefense;\n\t\t\tpkmn.Speed = basePkmn.Speed;\n\t\t\tpkmn.Type1 = basePkmn.Type1;\n\t\t\tpkmn.Type2 = basePkmn.Type2;\n\t\t\tpkmn.GenderRatio = basePkmn.GenderRatio;\n\t\t\tpkmn.GrowthRate = basePkmn.GrowthRate;\n\t\t\tpkmn.BaseEXPYield = basePkmn.BaseEXPYield;\n\t\t\tpkmn.Abilities = basePkmn.Abilities;\n\t\t\tpkmn.CatchRate = basePkmn.CatchRate;\n\t\t\tpkmn.FleeRate = basePkmn.FleeRate;\n\t\t\tpkmn.Weight = basePkmn.Weight;\n\t\t\tpkmn.LevelUpMoves = basePkmn.LevelUpMoves;\n\t\t\tpkmn.OtherMoves = basePkmn.OtherMoves;\n\t\t}\n\t\tCopySpecies((PBESpecies.Burmy, PBEForm.Burmy_Plant), (PBESpecies.Burmy, PBEForm.Burmy_Sandy));\n\t\tCopySpecies((PBESpecies.Burmy, PBEForm.Burmy_Plant), (PBESpecies.Burmy, PBEForm.Burmy_Trash));\n\t\tCopySpecies((PBESpecies.Cherrim, PBEForm.Cherrim), (PBESpecies.Cherrim, PBEForm.Cherrim_Sunshine));\n\t\tCopySpecies((PBESpecies.Deerling, PBEForm.Deerling_Spring), (PBESpecies.Deerling, PBEForm.Deerling_Summer));\n\t\tCopySpecies((PBESpecies.Deerling, PBEForm.Deerling_Spring), (PBESpecies.Deerling, PBEForm.Deerling_Autumn));\n\t\tCopySpecies((PBESpecies.Deerling, PBEForm.Deerling_Spring), (PBESpecies.Deerling, PBEForm.Deerling_Winter));\n\t\tCopySpecies((PBESpecies.Gastrodon, PBEForm.Gastrodon_West), (PBESpecies.Gastrodon, PBEForm.Gastrodon_East));\n\t\tCopySpecies((PBESpecies.Genesect, PBEForm.Genesect), (PBESpecies.Genesect, PBEForm.Genesect_Douse));\n\t\tCopySpecies((PBESpecies.Genesect, PBEForm.Genesect), (PBESpecies.Genesect, PBEForm.Genesect_Shock));\n\t\tCopySpecies((PBESpecies.Genesect, PBEForm.Genesect), (PBESpecies.Genesect, PBEForm.Genesect_Burn));\n\t\tCopySpecies((PBESpecies.Genesect, PBEForm.Genesect), (PBESpecies.Genesect, PBEForm.Genesect_Chill));\n\t\tCopySpecies((PBESpecies.Sawsbuck, PBEForm.Sawsbuck_Spring), (PBESpecies.Sawsbuck, PBEForm.Sawsbuck_Summer));\n\t\tCopySpecies((PBESpecies.Sawsbuck, PBEForm.Sawsbuck_Spring), (PBESpecies.Sawsbuck, PBEForm.Sawsbuck_Autumn));\n\t\tCopySpecies((PBESpecies.Sawsbuck, PBEForm.Sawsbuck_Spring), (PBESpecies.Sawsbuck, PBEForm.Sawsbuck_Winter));\n\t\tCopySpecies((PBESpecies.Shellos, PBEForm.Shellos_West), (PBESpecies.Shellos, PBEForm.Shellos_East));\n\t\tCopySpecies((PBESpecies.Unown, PBEForm.Unown_A), (PBESpecies.Unown, PBEForm.Unown_B));\n\t\tCopySpecies((PBESpecies.Unown, PBEForm.Unown_A), (PBESpecies.Unown, PBEForm.Unown_C));\n\t\tCopySpecies((PBESpecies.Unown, PBEForm.Unown_A), (PBESpecies.Unown, PBEForm.Unown_D));\n\t\tCopySpecies((PBESpecies.Unown, PBEForm.Unown_A), (PBESpecies.Unown, PBEForm.Unown_E));\n\t\tCopySpecies((PBESpecies.Unown, PBEForm.Unown_A), (PBESpecies.Unown, PBEForm.Unown_F));\n\t\tCopySpecies((PBESpecies.Unown, PBEForm.Unown_A), (PBESpecies.Unown, PBEForm.Unown_G));\n\t\tCopySpecies((PBESpecies.Unown, PBEForm.Unown_A), (PBESpecies.Unown, PBEForm.Unown_H));\n\t\tCopySpecies((PBESpecies.Unown, PBEForm.Unown_A), (PBESpecies.Unown, PBEForm.Unown_I));\n\t\tCopySpecies((PBESpecies.Unown, PBEForm.Unown_A), (PBESpecies.Unown, PBEForm.Unown_J));\n\t\tCopySpecies((PBESpecies.Unown, PBEForm.Unown_A), (PBESpecies.Unown, PBEForm.Unown_K));\n\t\tCopySpecies((PBESpecies.Unown, PBEForm.Unown_A), (PBESpecies.Unown, PBEForm.Unown_L));\n\t\tCopySpecies((PBESpecies.Unown, PBEForm.Unown_A), (PBESpecies.Unown, PBEForm.Unown_M));\n\t\tCopySpecies((PBESpecies.Unown, PBEForm.Unown_A), (PBESpecies.Unown, PBEForm.Unown_N));\n\t\tCopySpecies((PBESpecies.Unown, PBEForm.Unown_A), (PBESpecies.Unown, PBEForm.Unown_O));\n\t\tCopySpecies((PBESpecies.Unown, PBEForm.Unown_A), (PBESpecies.Unown, PBEForm.Unown_P));\n\t\tCopySpecies((PBESpecies.Unown, PBEForm.Unown_A), (PBESpecies.Unown, PBEForm.Unown_Q));\n\t\tCopySpecies((PBESpecies.Unown, PBEForm.Unown_A), (PBESpecies.Unown, PBEForm.Unown_R));\n\t\tCopySpecies((PBESpecies.Unown, PBEForm.Unown_A), (PBESpecies.Unown, PBEForm.Unown_S));\n\t\tCopySpecies((PBESpecies.Unown, PBEForm.Unown_A), (PBESpecies.Unown, PBEForm.Unown_T));\n\t\tCopySpecies((PBESpecies.Unown, PBEForm.Unown_A), (PBESpecies.Unown, PBEForm.Unown_U));\n\t\tCopySpecies((PBESpecies.Unown, PBEForm.Unown_A), (PBESpecies.Unown, PBEForm.Unown_V));\n\t\tCopySpecies((PBESpecies.Unown, PBEForm.Unown_A), (PBESpecies.Unown, PBEForm.Unown_W));\n\t\tCopySpecies((PBESpecies.Unown, PBEForm.Unown_A), (PBESpecies.Unown, PBEForm.Unown_X));\n\t\tCopySpecies((PBESpecies.Unown, PBEForm.Unown_A), (PBESpecies.Unown, PBEForm.Unown_Y));\n\t\tCopySpecies((PBESpecies.Unown, PBEForm.Unown_A), (PBESpecies.Unown, PBEForm.Unown_Z));\n\t\tCopySpecies((PBESpecies.Unown, PBEForm.Unown_A), (PBESpecies.Unown, PBEForm.Unown_Exclamation));\n\t\tCopySpecies((PBESpecies.Unown, PBEForm.Unown_A), (PBESpecies.Unown, PBEForm.Unown_Question));\n\t\tAddEvolution((PBESpecies.Burmy, PBEForm.Burmy_Sandy), (PBESpecies.Wormadam, PBEForm.Wormadam_Sandy));\n\t\tAddEvolution((PBESpecies.Burmy, PBEForm.Burmy_Sandy), (PBESpecies.Mothim, 0));\n\t\tAddEvolution((PBESpecies.Burmy, PBEForm.Burmy_Trash), (PBESpecies.Wormadam, PBEForm.Wormadam_Trash));\n\t\tAddEvolution((PBESpecies.Burmy, PBEForm.Burmy_Trash), (PBESpecies.Mothim, 0));\n\t\tAddEvolution((PBESpecies.Deerling, PBEForm.Deerling_Summer), (PBESpecies.Sawsbuck, PBEForm.Sawsbuck_Summer));\n\t\tAddEvolution((PBESpecies.Deerling, PBEForm.Deerling_Autumn), (PBESpecies.Sawsbuck, PBEForm.Sawsbuck_Autumn));\n\t\tAddEvolution((PBESpecies.Deerling, PBEForm.Deerling_Winter), (PBESpecies.Sawsbuck, PBEForm.Sawsbuck_Winter));\n\t\tAddEvolution((PBESpecies.Shellos, PBEForm.Shellos_East), (PBESpecies.Gastrodon, PBEForm.Gastrodon_East));\n\t}\n\tprivate static void Fix_BasculinBlueAbility()\n\t{\n\t\t_dict[(PBESpecies.Basculin, PBEForm.Basculin_Blue)].Abilities.Add(PBEAbility.Reckless);\n\t}\n\tprivate static void Fix_ArceusTypes()\n\t{\n\t\tPokemon basePkmn = _dict[(PBESpecies.Arceus, PBEForm.Arceus)];\n\n\t\tvoid FixArceus(PBEForm form, PBEType type)\n\t\t{\n\t\t\tPokemon pkmn = AddSpecies((PBESpecies.Arceus, form));\n\t\t\tpkmn.HP = basePkmn.HP;\n\t\t\tpkmn.Attack = basePkmn.Attack;\n\t\t\tpkmn.Defense = basePkmn.Defense;\n\t\t\tpkmn.SpAttack = basePkmn.SpAttack;\n\t\t\tpkmn.SpDefense = basePkmn.SpDefense;\n\t\t\tpkmn.Speed = basePkmn.Speed;\n\t\t\tpkmn.Type1 = type;\n\t\t\tpkmn.Type2 = basePkmn.Type2;\n\t\t\tpkmn.GenderRatio = basePkmn.GenderRatio;\n\t\t\tpkmn.GrowthRate = basePkmn.GrowthRate;\n\t\t\tpkmn.BaseEXPYield = basePkmn.BaseEXPYield;\n\t\t\tpkmn.Abilities = basePkmn.Abilities;\n\t\t\tpkmn.CatchRate = basePkmn.CatchRate;\n\t\t\tpkmn.FleeRate = basePkmn.FleeRate;\n\t\t\tpkmn.Weight = basePkmn.Weight;\n\t\t\tpkmn.Evolutions = basePkmn.Evolutions;\n\t\t\tpkmn.PreEvolutions = basePkmn.PreEvolutions;\n\t\t\tpkmn.LevelUpMoves = basePkmn.LevelUpMoves;\n\t\t\tpkmn.OtherMoves = basePkmn.OtherMoves;\n\t\t}\n\n\t\tFixArceus(PBEForm.Arceus_Fighting, PBEType.Fighting);\n\t\tFixArceus(PBEForm.Arceus_Flying, PBEType.Flying);\n\t\tFixArceus(PBEForm.Arceus_Poison, PBEType.Poison);\n\t\tFixArceus(PBEForm.Arceus_Ground, PBEType.Ground);\n\t\tFixArceus(PBEForm.Arceus_Rock, PBEType.Rock);\n\t\tFixArceus(PBEForm.Arceus_Bug, PBEType.Bug);\n\t\tFixArceus(PBEForm.Arceus_Ghost, PBEType.Ghost);\n\t\tFixArceus(PBEForm.Arceus_Steel, PBEType.Steel);\n\t\tFixArceus(PBEForm.Arceus_Fire, PBEType.Fire);\n\t\tFixArceus(PBEForm.Arceus_Water, PBEType.Water);\n\t\tFixArceus(PBEForm.Arceus_Grass, PBEType.Grass);\n\t\tFixArceus(PBEForm.Arceus_Electric, PBEType.Electric);\n\t\tFixArceus(PBEForm.Arceus_Psychic, PBEType.Psychic);\n\t\tFixArceus(PBEForm.Arceus_Ice, PBEType.Ice);\n\t\tFixArceus(PBEForm.Arceus_Dragon, PBEType.Dragon);\n\t\tFixArceus(PBEForm.Arceus_Dark, PBEType.Dark);\n\t}\n\tprivate static void Fix_FormEggMoves()\n\t{\n\t\tSpan<PBEDDMoveObtainMethod> flags = stackalloc PBEDDMoveObtainMethod[]\n\t\t{\n\t\t\tPBEDDMoveObtainMethod.EggMove_RSFRLGE,\n\t\t\tPBEDDMoveObtainMethod.EggMove_DPPt,\n\t\t\tPBEDDMoveObtainMethod.EggMove_HGSS,\n\t\t\tPBEDDMoveObtainMethod.EggMove_BWB2W2,\n\t\t\tPBEDDMoveObtainMethod.EggMove_Special,\n\t\t};\n\n\t\tforeach ((PBESpecies, PBEForm) key in _b2w2SpeciesIndexToPBESpecies.Values)\n\t\t{\n\t\t\tforeach (KeyValuePair<PBEMove, PBEDDMoveObtainMethod> kvp in _dict[(key.Item1, 0)].OtherMoves)\n\t\t\t{\n\t\t\t\tPBEMove move = kvp.Key;\n\t\t\t\tPBEDDMoveObtainMethod o = kvp.Value;\n\t\t\t\tforeach (PBEDDMoveObtainMethod flag in flags)\n\t\t\t\t{\n\t\t\t\t\tif (o.HasFlag(flag))\n\t\t\t\t\t{\n\t\t\t\t\t\tAddOtherMove(key, move, flag);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tprivate static void Fix_ShedinjaEvoMoves()\n\t{\n\t\t(PBESpecies, PBEForm) key = (PBESpecies.Shedinja, 0);\n\t\tSpan<PBEDDMoveObtainMethod> flags = stackalloc PBEDDMoveObtainMethod[]\n\t\t{\n\t\t\tPBEDDMoveObtainMethod.LevelUp_RSColoXD,\n\t\t\tPBEDDMoveObtainMethod.LevelUp_FR,\n\t\t\tPBEDDMoveObtainMethod.LevelUp_E,\n\t\t\tPBEDDMoveObtainMethod.LevelUp_DP,\n\t\t\tPBEDDMoveObtainMethod.LevelUp_Pt,\n\t\t\tPBEDDMoveObtainMethod.LevelUp_HGSS,\n\t\t};\n\n\t\t// Nincada evolves starting at level 20\n\t\tforeach (KeyValuePair<(PBEMove, byte), PBEDDMoveObtainMethod> kvp in _dict[(PBESpecies.Ninjask, 0)].LevelUpMoves)\n\t\t{\n\t\t\t(PBEMove move, byte level) = kvp.Key;\n\t\t\tif (level >= 20)\n\t\t\t{\n\t\t\t\tPBEDDMoveObtainMethod o = kvp.Value;\n\t\t\t\tforeach (PBEDDMoveObtainMethod flag in flags)\n\t\t\t\t{\n\t\t\t\t\tif (o.HasFlag(flag))\n\t\t\t\t\t{\n\t\t\t\t\t\tAddLevelUpMove(key, move, level, flag);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tprivate static void Fix_FRLGStarterMoves()\n\t{\n\t\tPBEDDMoveObtainMethod flag = PBEDDMoveObtainMethod.MoveTutor_FRLG;\n\t\tAddOtherMove((PBESpecies.Venusaur, 0), PBEMove.FrenzyPlant, flag);\n\t\tAddOtherMove((PBESpecies.Charizard, 0), PBEMove.BlastBurn, flag);\n\t\tAddOtherMove((PBESpecies.Blastoise, 0), PBEMove.HydroCannon, flag);\n\t}\n\tprivate static void Fix_XDMew()\n\t{\n\t\t(PBESpecies, PBEForm) key = (PBESpecies.Mew, 0);\n\t\tPokemon pkmn = _dict[key];\n\t\tvar list = new List<PBEMove>\n\t\t{\n\t\t\tPBEMove.FaintAttack,\n\t\t\tPBEMove.FakeOut,\n\t\t\tPBEMove.Hypnosis,\n\t\t\tPBEMove.NightShade,\n\t\t\tPBEMove.RolePlay,\n\t\t\tPBEMove.ZapCannon,\n\t\t};\n\t\tforeach (KeyValuePair<PBEMove, PBEDDMoveObtainMethod> kvp in pkmn.OtherMoves)\n\t\t{\n\t\t\tPBEDDMoveObtainMethod o = kvp.Value;\n\t\t\tif (o.HasFlag(PBEDDMoveObtainMethod.TM_RSFRLGEColoXD)\n\t\t\t\t|| o.HasFlag(PBEDDMoveObtainMethod.HM_RSFRLGEColoXD)\n\t\t\t\t|| o.HasFlag(PBEDDMoveObtainMethod.MoveTutor_FRLG)\n\t\t\t\t|| o.HasFlag(PBEDDMoveObtainMethod.MoveTutor_E))\n\t\t\t{\n\t\t\t\tlist.Add(kvp.Key);\n\t\t\t}\n\t\t}\n\t\tforeach (PBEMove move in list)\n\t\t{\n\t\t\tAddOtherMove(key, move, PBEDDMoveObtainMethod.MoveTutor_XD);\n\t\t}\n\t}\n\tprivate static void Fix_VoltTackle()\n\t{\n\t\tAddOtherMove((PBESpecies.Pichu, 0), PBEMove.VoltTackle, PBEDDMoveObtainMethod.EggMove_Special);\n\t}\n\tprivate static void Fix_Gen4FreeMoves()\n\t{\n\t\tPBEDDMoveObtainMethod flag = PBEDDMoveObtainMethod.MoveTutor_DP | PBEDDMoveObtainMethod.MoveTutor_Pt | PBEDDMoveObtainMethod.MoveTutor_HGSS;\n\t\tAddOtherMove((PBESpecies.Venusaur, 0), PBEMove.FrenzyPlant, flag);\n\t\tAddOtherMove((PBESpecies.Charizard, 0), PBEMove.BlastBurn, flag);\n\t\tAddOtherMove((PBESpecies.Blastoise, 0), PBEMove.HydroCannon, flag);\n\t\tAddOtherMove((PBESpecies.Meganium, 0), PBEMove.FrenzyPlant, flag);\n\t\tAddOtherMove((PBESpecies.Typhlosion, 0), PBEMove.BlastBurn, flag);\n\t\tAddOtherMove((PBESpecies.Feraligatr, 0), PBEMove.HydroCannon, flag);\n\t\tAddOtherMove((PBESpecies.Sceptile, 0), PBEMove.FrenzyPlant, flag);\n\t\tAddOtherMove((PBESpecies.Blaziken, 0), PBEMove.BlastBurn, flag);\n\t\tAddOtherMove((PBESpecies.Swampert, 0), PBEMove.HydroCannon, flag);\n\t\tAddOtherMove((PBESpecies.Torterra, 0), PBEMove.FrenzyPlant, flag);\n\t\tAddOtherMove((PBESpecies.Infernape, 0), PBEMove.BlastBurn, flag);\n\t\tAddOtherMove((PBESpecies.Empoleon, 0), PBEMove.HydroCannon, flag);\n\t\t// Draco Meteor is taught to any Dragon type, including Arceus_Dragon\n\t\tforeach (KeyValuePair<(PBESpecies, PBEForm), Pokemon> pkmn in _dict.Where(kvp => kvp.Key.Item1 <= PBESpecies.Arceus && kvp.Value.HasType(PBEType.Dragon)))\n\t\t{\n\t\t\tAddOtherMove(pkmn.Key, PBEMove.DracoMeteor, flag);\n\t\t}\n\t}\n\tprivate static void Fix_RotomFormMoves()\n\t{\n\t\tAddOtherMove((PBESpecies.Rotom, PBEForm.Rotom), PBEMove.ThunderShock, PBEDDMoveObtainMethod.Form);\n\t\tAddOtherMove((PBESpecies.Rotom, PBEForm.Rotom_Fan), PBEMove.AirSlash, PBEDDMoveObtainMethod.Form);\n\t\tAddOtherMove((PBESpecies.Rotom, PBEForm.Rotom_Frost), PBEMove.Blizzard, PBEDDMoveObtainMethod.Form);\n\t\tAddOtherMove((PBESpecies.Rotom, PBEForm.Rotom_Heat), PBEMove.Overheat, PBEDDMoveObtainMethod.Form);\n\t\tAddOtherMove((PBESpecies.Rotom, PBEForm.Rotom_Mow), PBEMove.LeafStorm, PBEDDMoveObtainMethod.Form);\n\t\tAddOtherMove((PBESpecies.Rotom, PBEForm.Rotom_Wash), PBEMove.HydroPump, PBEDDMoveObtainMethod.Form);\n\t}\n\tprivate static void Fix_RelicSong_SecretSword()\n\t{\n\t\tPBEDDMoveObtainMethod flag = PBEDDMoveObtainMethod.MoveTutor_BW | PBEDDMoveObtainMethod.MoveTutor_B2W2;\n\t\tAddOtherMove((PBESpecies.Meloetta, 0), PBEMove.RelicSong, flag);\n\t\tAddOtherMove((PBESpecies.Keldeo, 0), PBEMove.SecretSword, flag);\n\t}\n\n\tprivate static void DreamWorld()\n\t{\n\t\tforeach ((PBESpecies species, PBEForm form, PBEMove moveA, PBEMove moveB, PBEMove moveC, bool bw, bool b2w2) in _dreamWorld)\n\t\t{\n\t\t\tPBEDDMoveObtainMethod o = PBEDDMoveObtainMethod.None;\n\t\t\tif (bw)\n\t\t\t{\n\t\t\t\to |= PBEDDMoveObtainMethod.DreamWorld_BW;\n\t\t\t}\n\t\t\tif (b2w2)\n\t\t\t{\n\t\t\t\to |= PBEDDMoveObtainMethod.DreamWorld_B2W2;\n\t\t\t}\n\t\t\tif (o == PBEDDMoveObtainMethod.None)\n\t\t\t{\n\t\t\t\tthrow new Exception($\"Problem with Dream World - {species}\");\n\t\t\t}\n\t\t\t(PBESpecies, PBEForm) key = (species, form);\n\t\t\tAddOtherMove(key, moveA, o);\n\t\t\tAddOtherMove(key, moveB, o);\n\t\t\tAddOtherMove(key, moveC, o);\n\t\t}\n\t}\n\n\tprivate static void WriteToDatabase(SqliteCommand cmd)\n\t{\n\t\tconst char Split1Char = '+';\n\t\tconst char Split2Char = '|'; // Don't use commas since flags enums do\n\n\t\tcmd.CommandText = \"DROP TABLE IF EXISTS PokemonData\";\n\t\tcmd.ExecuteNonQuery();\n\t\tcmd.CommandText = \"CREATE TABLE PokemonData(Species TEXT, Form TEXT\"\n\t\t\t+ \", HP INTEGER, Attack INTEGER, Defense INTEGER, SpAttack INTEGER, SpDefense INTEGER, Speed INTEGER\"\n\t\t\t+ \", Type1 INTEGER, Type2 INTEGER, GenderRatio INTEGER, GrowthRate INTEGER, BaseEXPYield INTEGER, CatchRate INTEGER, FleeRate INTEGER, Weight FLOAT\"\n\t\t\t+ \", PreEvolutions TEXT, Evolutions TEXT, Abilities TEXT, LevelUpMoves TEXT, OtherMoves TEXT\"\n\t\t\t+ \")\";\n\t\tcmd.ExecuteNonQuery();\n\t\tcmd.CommandText = \"INSERT INTO PokemonData VALUES(@0, @1, @2, @3, @4, @5, @6, @7, @8, @9, @10, @11, @12, @13, @14, @15, @16, @17, @18, @19, @20)\";\n\t\tvar list = new List<string>();\n\t\tforeach (KeyValuePair<(PBESpecies, PBEForm), Pokemon> tup in _dict)\n\t\t{\n\t\t\t(PBESpecies species, PBEForm form) = tup.Key;\n\t\t\tcmd.Parameters.AddWithValue(\"@0\", species.ToString());\n\t\t\tcmd.Parameters.AddWithValue(\"@1\", PBEDataUtils.GetNameOfForm(species, form) ?? \"0\");\n\t\t\tPokemon pkmn = tup.Value;\n\t\t\tcmd.Parameters.AddWithValue(\"@2\", pkmn.HP);\n\t\t\tcmd.Parameters.AddWithValue(\"@3\", pkmn.Attack);\n\t\t\tcmd.Parameters.AddWithValue(\"@4\", pkmn.Defense);\n\t\t\tcmd.Parameters.AddWithValue(\"@5\", pkmn.SpAttack);\n\t\t\tcmd.Parameters.AddWithValue(\"@6\", pkmn.SpDefense);\n\t\t\tcmd.Parameters.AddWithValue(\"@7\", pkmn.Speed);\n\t\t\tcmd.Parameters.AddWithValue(\"@8\", pkmn.Type1);\n\t\t\tcmd.Parameters.AddWithValue(\"@9\", pkmn.Type2);\n\t\t\tcmd.Parameters.AddWithValue(\"@10\", pkmn.GenderRatio);\n\t\t\tcmd.Parameters.AddWithValue(\"@11\", pkmn.GrowthRate);\n\t\t\tcmd.Parameters.AddWithValue(\"@12\", pkmn.BaseEXPYield);\n\t\t\tcmd.Parameters.AddWithValue(\"@13\", pkmn.CatchRate);\n\t\t\tcmd.Parameters.AddWithValue(\"@14\", pkmn.FleeRate);\n\t\t\tcmd.Parameters.AddWithValue(\"@15\", pkmn.Weight.ToString(\"0.0\")); // Gets weird if you don't truncate (like 9.9 becomes 9.89999999999999)\n\t\t\tlist.Clear();\n\t\t\tforeach ((PBESpecies, PBEForm) key in pkmn.PreEvolutions)\n\t\t\t{\n\t\t\t\tPBESpecies spe = key.Item1;\n\t\t\t\tlist.Add(spe.ToString() + Split2Char + (PBEDataUtils.GetNameOfForm(spe, key.Item2) ?? \"0\"));\n\t\t\t}\n\t\t\tcmd.Parameters.AddWithValue(\"@16\", string.Join(Split1Char, list));\n\t\t\tlist.Clear();\n\t\t\tforeach ((PBESpecies, PBEForm) key in pkmn.Evolutions)\n\t\t\t{\n\t\t\t\tPBESpecies spe = key.Item1;\n\t\t\t\tlist.Add(spe.ToString() + Split2Char + (PBEDataUtils.GetNameOfForm(spe, key.Item2) ?? \"0\"));\n\t\t\t}\n\t\t\tcmd.Parameters.AddWithValue(\"@17\", string.Join(Split1Char, list));\n\t\t\tlist.Clear();\n\t\t\tforeach (PBEAbility ab in pkmn.Abilities)\n\t\t\t{\n\t\t\t\tlist.Add(ab.ToString());\n\t\t\t}\n\t\t\tcmd.Parameters.AddWithValue(\"@18\", string.Join(Split1Char, list));\n\t\t\tlist.Clear();\n\t\t\tforeach (KeyValuePair<(PBEMove Move, byte Level), PBEDDMoveObtainMethod> levelUpMove in pkmn.LevelUpMoves)\n\t\t\t{\n\t\t\t\t(PBEMove move, byte level) = levelUpMove.Key;\n\t\t\t\tlist.Add(move.ToString() + Split2Char + level.ToString() + Split2Char + levelUpMove.Value.ToString());\n\t\t\t}\n\t\t\tcmd.Parameters.AddWithValue(\"@19\", string.Join(Split1Char, list));\n\t\t\tlist.Clear();\n\t\t\tforeach (KeyValuePair<PBEMove, PBEDDMoveObtainMethod> otherMove in pkmn.OtherMoves)\n\t\t\t{\n\t\t\t\tlist.Add(otherMove.Key.ToString() + Split2Char + otherMove.Value.ToString());\n\t\t\t}\n\t\t\tcmd.Parameters.AddWithValue(\"@20\", string.Join(Split1Char, list));\n\t\t\tcmd.ExecuteNonQuery();\n\t\t\tcmd.Parameters.Clear();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineExtras/PokemonDataDumper_Data.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.DefaultData;\nusing System.Collections.Generic;\n\nnamespace Kermalis.PokemonBattleEngineExtras;\n\ninternal static partial class PokemonDataDumper\n{\n\tprivate sealed class Pokemon : IPBEPokemonTypes\n\t{\n\t\tpublic byte HP;\n\t\tpublic byte Attack;\n\t\tpublic byte Defense;\n\t\tpublic byte SpAttack;\n\t\tpublic byte SpDefense;\n\t\tpublic byte Speed;\n\t\tpublic PBEType Type1 { get; set; }\n\t\tpublic PBEType Type2 { get; set; }\n\t\tpublic PBEGenderRatio GenderRatio;\n\t\tpublic PBEGrowthRate GrowthRate;\n\t\tpublic ushort BaseEXPYield;\n\t\tpublic byte CatchRate;\n\t\tpublic byte FleeRate;\n\t\tpublic float Weight;\n\t\tpublic List<(PBESpecies, PBEForm)> PreEvolutions = new();\n\t\tpublic List<(PBESpecies, PBEForm)> Evolutions = new();\n\t\tpublic List<PBEAbility> Abilities = new();\n\t\tpublic Dictionary<(PBEMove Move, byte Level), PBEDDMoveObtainMethod> LevelUpMoves = new();\n\t\tpublic Dictionary<PBEMove, PBEDDMoveObtainMethod> OtherMoves = new();\n\t}\n\n\t#region Static Collections\n\t#region Gen 3\n\tprivate static readonly Dictionary<int, PBESpecies> _gen3SpeciesIndexToPBESpecies = new()\n\t\t{\n\t\t\t{ 1, PBESpecies.Bulbasaur },\n\t\t\t{ 2, PBESpecies.Ivysaur },\n\t\t\t{ 3, PBESpecies.Venusaur },\n\t\t\t{ 4, PBESpecies.Charmander },\n\t\t\t{ 5, PBESpecies.Charmeleon },\n\t\t\t{ 6, PBESpecies.Charizard },\n\t\t\t{ 7, PBESpecies.Squirtle },\n\t\t\t{ 8, PBESpecies.Wartortle },\n\t\t\t{ 9, PBESpecies.Blastoise },\n\t\t\t{ 10, PBESpecies.Caterpie },\n\t\t\t{ 11, PBESpecies.Metapod },\n\t\t\t{ 12, PBESpecies.Butterfree },\n\t\t\t{ 13, PBESpecies.Weedle },\n\t\t\t{ 14, PBESpecies.Kakuna },\n\t\t\t{ 15, PBESpecies.Beedrill },\n\t\t\t{ 16, PBESpecies.Pidgey },\n\t\t\t{ 17, PBESpecies.Pidgeotto },\n\t\t\t{ 18, PBESpecies.Pidgeot },\n\t\t\t{ 19, PBESpecies.Rattata },\n\t\t\t{ 20, PBESpecies.Raticate },\n\t\t\t{ 21, PBESpecies.Spearow },\n\t\t\t{ 22, PBESpecies.Fearow },\n\t\t\t{ 23, PBESpecies.Ekans },\n\t\t\t{ 24, PBESpecies.Arbok },\n\t\t\t{ 25, PBESpecies.Pikachu },\n\t\t\t{ 26, PBESpecies.Raichu },\n\t\t\t{ 27, PBESpecies.Sandshrew },\n\t\t\t{ 28, PBESpecies.Sandslash },\n\t\t\t{ 29, PBESpecies.Nidoran_F },\n\t\t\t{ 30, PBESpecies.Nidorina },\n\t\t\t{ 31, PBESpecies.Nidoqueen },\n\t\t\t{ 32, PBESpecies.Nidoran_M },\n\t\t\t{ 33, PBESpecies.Nidorino },\n\t\t\t{ 34, PBESpecies.Nidoking },\n\t\t\t{ 35, PBESpecies.Clefairy },\n\t\t\t{ 36, PBESpecies.Clefable },\n\t\t\t{ 37, PBESpecies.Vulpix },\n\t\t\t{ 38, PBESpecies.Ninetales },\n\t\t\t{ 39, PBESpecies.Jigglypuff },\n\t\t\t{ 40, PBESpecies.Wigglytuff },\n\t\t\t{ 41, PBESpecies.Zubat },\n\t\t\t{ 42, PBESpecies.Golbat },\n\t\t\t{ 43, PBESpecies.Oddish },\n\t\t\t{ 44, PBESpecies.Gloom },\n\t\t\t{ 45, PBESpecies.Vileplume },\n\t\t\t{ 46, PBESpecies.Paras },\n\t\t\t{ 47, PBESpecies.Parasect },\n\t\t\t{ 48, PBESpecies.Venonat },\n\t\t\t{ 49, PBESpecies.Venomoth },\n\t\t\t{ 50, PBESpecies.Diglett },\n\t\t\t{ 51, PBESpecies.Dugtrio },\n\t\t\t{ 52, PBESpecies.Meowth },\n\t\t\t{ 53, PBESpecies.Persian },\n\t\t\t{ 54, PBESpecies.Psyduck },\n\t\t\t{ 55, PBESpecies.Golduck },\n\t\t\t{ 56, PBESpecies.Mankey },\n\t\t\t{ 57, PBESpecies.Primeape },\n\t\t\t{ 58, PBESpecies.Growlithe },\n\t\t\t{ 59, PBESpecies.Arcanine },\n\t\t\t{ 60, PBESpecies.Poliwag },\n\t\t\t{ 61, PBESpecies.Poliwhirl },\n\t\t\t{ 62, PBESpecies.Poliwrath },\n\t\t\t{ 63, PBESpecies.Abra },\n\t\t\t{ 64, PBESpecies.Kadabra },\n\t\t\t{ 65, PBESpecies.Alakazam },\n\t\t\t{ 66, PBESpecies.Machop },\n\t\t\t{ 67, PBESpecies.Machoke },\n\t\t\t{ 68, PBESpecies.Machamp },\n\t\t\t{ 69, PBESpecies.Bellsprout },\n\t\t\t{ 70, PBESpecies.Weepinbell },\n\t\t\t{ 71, PBESpecies.Victreebel },\n\t\t\t{ 72, PBESpecies.Tentacool },\n\t\t\t{ 73, PBESpecies.Tentacruel },\n\t\t\t{ 74, PBESpecies.Geodude },\n\t\t\t{ 75, PBESpecies.Graveler },\n\t\t\t{ 76, PBESpecies.Golem },\n\t\t\t{ 77, PBESpecies.Ponyta },\n\t\t\t{ 78, PBESpecies.Rapidash },\n\t\t\t{ 79, PBESpecies.Slowpoke },\n\t\t\t{ 80, PBESpecies.Slowbro },\n\t\t\t{ 81, PBESpecies.Magnemite },\n\t\t\t{ 82, PBESpecies.Magneton },\n\t\t\t{ 83, PBESpecies.Farfetchd },\n\t\t\t{ 84, PBESpecies.Doduo },\n\t\t\t{ 85, PBESpecies.Dodrio },\n\t\t\t{ 86, PBESpecies.Seel },\n\t\t\t{ 87, PBESpecies.Dewgong },\n\t\t\t{ 88, PBESpecies.Grimer },\n\t\t\t{ 89, PBESpecies.Muk },\n\t\t\t{ 90, PBESpecies.Shellder },\n\t\t\t{ 91, PBESpecies.Cloyster },\n\t\t\t{ 92, PBESpecies.Gastly },\n\t\t\t{ 93, PBESpecies.Haunter },\n\t\t\t{ 94, PBESpecies.Gengar },\n\t\t\t{ 95, PBESpecies.Onix },\n\t\t\t{ 96, PBESpecies.Drowzee },\n\t\t\t{ 97, PBESpecies.Hypno },\n\t\t\t{ 98, PBESpecies.Krabby },\n\t\t\t{ 99, PBESpecies.Kingler },\n\t\t\t{ 100, PBESpecies.Voltorb },\n\t\t\t{ 101, PBESpecies.Electrode },\n\t\t\t{ 102, PBESpecies.Exeggcute },\n\t\t\t{ 103, PBESpecies.Exeggutor },\n\t\t\t{ 104, PBESpecies.Cubone },\n\t\t\t{ 105, PBESpecies.Marowak },\n\t\t\t{ 106, PBESpecies.Hitmonlee },\n\t\t\t{ 107, PBESpecies.Hitmonchan },\n\t\t\t{ 108, PBESpecies.Lickitung },\n\t\t\t{ 109, PBESpecies.Koffing },\n\t\t\t{ 110, PBESpecies.Weezing },\n\t\t\t{ 111, PBESpecies.Rhyhorn },\n\t\t\t{ 112, PBESpecies.Rhydon },\n\t\t\t{ 113, PBESpecies.Chansey },\n\t\t\t{ 114, PBESpecies.Tangela },\n\t\t\t{ 115, PBESpecies.Kangaskhan },\n\t\t\t{ 116, PBESpecies.Horsea },\n\t\t\t{ 117, PBESpecies.Seadra },\n\t\t\t{ 118, PBESpecies.Goldeen },\n\t\t\t{ 119, PBESpecies.Seaking },\n\t\t\t{ 120, PBESpecies.Staryu },\n\t\t\t{ 121, PBESpecies.Starmie },\n\t\t\t{ 122, PBESpecies.MrMime },\n\t\t\t{ 123, PBESpecies.Scyther },\n\t\t\t{ 124, PBESpecies.Jynx },\n\t\t\t{ 125, PBESpecies.Electabuzz },\n\t\t\t{ 126, PBESpecies.Magmar },\n\t\t\t{ 127, PBESpecies.Pinsir },\n\t\t\t{ 128, PBESpecies.Tauros },\n\t\t\t{ 129, PBESpecies.Magikarp },\n\t\t\t{ 130, PBESpecies.Gyarados },\n\t\t\t{ 131, PBESpecies.Lapras },\n\t\t\t{ 132, PBESpecies.Ditto },\n\t\t\t{ 133, PBESpecies.Eevee },\n\t\t\t{ 134, PBESpecies.Vaporeon },\n\t\t\t{ 135, PBESpecies.Jolteon },\n\t\t\t{ 136, PBESpecies.Flareon },\n\t\t\t{ 137, PBESpecies.Porygon },\n\t\t\t{ 138, PBESpecies.Omanyte },\n\t\t\t{ 139, PBESpecies.Omastar },\n\t\t\t{ 140, PBESpecies.Kabuto },\n\t\t\t{ 141, PBESpecies.Kabutops },\n\t\t\t{ 142, PBESpecies.Aerodactyl },\n\t\t\t{ 143, PBESpecies.Snorlax },\n\t\t\t{ 144, PBESpecies.Articuno },\n\t\t\t{ 145, PBESpecies.Zapdos },\n\t\t\t{ 146, PBESpecies.Moltres },\n\t\t\t{ 147, PBESpecies.Dratini },\n\t\t\t{ 148, PBESpecies.Dragonair },\n\t\t\t{ 149, PBESpecies.Dragonite },\n\t\t\t{ 150, PBESpecies.Mewtwo },\n\t\t\t{ 151, PBESpecies.Mew },\n\t\t\t{ 152, PBESpecies.Chikorita },\n\t\t\t{ 153, PBESpecies.Bayleef },\n\t\t\t{ 154, PBESpecies.Meganium },\n\t\t\t{ 155, PBESpecies.Cyndaquil },\n\t\t\t{ 156, PBESpecies.Quilava },\n\t\t\t{ 157, PBESpecies.Typhlosion },\n\t\t\t{ 158, PBESpecies.Totodile },\n\t\t\t{ 159, PBESpecies.Croconaw },\n\t\t\t{ 160, PBESpecies.Feraligatr },\n\t\t\t{ 161, PBESpecies.Sentret },\n\t\t\t{ 162, PBESpecies.Furret },\n\t\t\t{ 163, PBESpecies.Hoothoot },\n\t\t\t{ 164, PBESpecies.Noctowl },\n\t\t\t{ 165, PBESpecies.Ledyba },\n\t\t\t{ 166, PBESpecies.Ledian },\n\t\t\t{ 167, PBESpecies.Spinarak },\n\t\t\t{ 168, PBESpecies.Ariados },\n\t\t\t{ 169, PBESpecies.Crobat },\n\t\t\t{ 170, PBESpecies.Chinchou },\n\t\t\t{ 171, PBESpecies.Lanturn },\n\t\t\t{ 172, PBESpecies.Pichu },\n\t\t\t{ 173, PBESpecies.Cleffa },\n\t\t\t{ 174, PBESpecies.Igglybuff },\n\t\t\t{ 175, PBESpecies.Togepi },\n\t\t\t{ 176, PBESpecies.Togetic },\n\t\t\t{ 177, PBESpecies.Natu },\n\t\t\t{ 178, PBESpecies.Xatu },\n\t\t\t{ 179, PBESpecies.Mareep },\n\t\t\t{ 180, PBESpecies.Flaaffy },\n\t\t\t{ 181, PBESpecies.Ampharos },\n\t\t\t{ 182, PBESpecies.Bellossom },\n\t\t\t{ 183, PBESpecies.Marill },\n\t\t\t{ 184, PBESpecies.Azumarill },\n\t\t\t{ 185, PBESpecies.Sudowoodo },\n\t\t\t{ 186, PBESpecies.Politoed },\n\t\t\t{ 187, PBESpecies.Hoppip },\n\t\t\t{ 188, PBESpecies.Skiploom },\n\t\t\t{ 189, PBESpecies.Jumpluff },\n\t\t\t{ 190, PBESpecies.Aipom },\n\t\t\t{ 191, PBESpecies.Sunkern },\n\t\t\t{ 192, PBESpecies.Sunflora },\n\t\t\t{ 193, PBESpecies.Yanma },\n\t\t\t{ 194, PBESpecies.Wooper },\n\t\t\t{ 195, PBESpecies.Quagsire },\n\t\t\t{ 196, PBESpecies.Espeon },\n\t\t\t{ 197, PBESpecies.Umbreon },\n\t\t\t{ 198, PBESpecies.Murkrow },\n\t\t\t{ 199, PBESpecies.Slowking },\n\t\t\t{ 200, PBESpecies.Misdreavus },\n\t\t\t{ 201, PBESpecies.Unown },\n\t\t\t{ 202, PBESpecies.Wobbuffet },\n\t\t\t{ 203, PBESpecies.Girafarig },\n\t\t\t{ 204, PBESpecies.Pineco },\n\t\t\t{ 205, PBESpecies.Forretress },\n\t\t\t{ 206, PBESpecies.Dunsparce },\n\t\t\t{ 207, PBESpecies.Gligar },\n\t\t\t{ 208, PBESpecies.Steelix },\n\t\t\t{ 209, PBESpecies.Snubbull },\n\t\t\t{ 210, PBESpecies.Granbull },\n\t\t\t{ 211, PBESpecies.Qwilfish },\n\t\t\t{ 212, PBESpecies.Scizor },\n\t\t\t{ 213, PBESpecies.Shuckle },\n\t\t\t{ 214, PBESpecies.Heracross },\n\t\t\t{ 215, PBESpecies.Sneasel },\n\t\t\t{ 216, PBESpecies.Teddiursa },\n\t\t\t{ 217, PBESpecies.Ursaring },\n\t\t\t{ 218, PBESpecies.Slugma },\n\t\t\t{ 219, PBESpecies.Magcargo },\n\t\t\t{ 220, PBESpecies.Swinub },\n\t\t\t{ 221, PBESpecies.Piloswine },\n\t\t\t{ 222, PBESpecies.Corsola },\n\t\t\t{ 223, PBESpecies.Remoraid },\n\t\t\t{ 224, PBESpecies.Octillery },\n\t\t\t{ 225, PBESpecies.Delibird },\n\t\t\t{ 226, PBESpecies.Mantine },\n\t\t\t{ 227, PBESpecies.Skarmory },\n\t\t\t{ 228, PBESpecies.Houndour },\n\t\t\t{ 229, PBESpecies.Houndoom },\n\t\t\t{ 230, PBESpecies.Kingdra },\n\t\t\t{ 231, PBESpecies.Phanpy },\n\t\t\t{ 232, PBESpecies.Donphan },\n\t\t\t{ 233, PBESpecies.Porygon2 },\n\t\t\t{ 234, PBESpecies.Stantler },\n\t\t\t{ 235, PBESpecies.Smeargle },\n\t\t\t{ 236, PBESpecies.Tyrogue },\n\t\t\t{ 237, PBESpecies.Hitmontop },\n\t\t\t{ 238, PBESpecies.Smoochum },\n\t\t\t{ 239, PBESpecies.Elekid },\n\t\t\t{ 240, PBESpecies.Magby },\n\t\t\t{ 241, PBESpecies.Miltank },\n\t\t\t{ 242, PBESpecies.Blissey },\n\t\t\t{ 243, PBESpecies.Raikou },\n\t\t\t{ 244, PBESpecies.Entei },\n\t\t\t{ 245, PBESpecies.Suicune },\n\t\t\t{ 246, PBESpecies.Larvitar },\n\t\t\t{ 247, PBESpecies.Pupitar },\n\t\t\t{ 248, PBESpecies.Tyranitar },\n\t\t\t{ 249, PBESpecies.Lugia },\n\t\t\t{ 250, PBESpecies.HoOh },\n\t\t\t{ 251, PBESpecies.Celebi },\n\t\t\t{ 277, PBESpecies.Treecko },\n\t\t\t{ 278, PBESpecies.Grovyle },\n\t\t\t{ 279, PBESpecies.Sceptile },\n\t\t\t{ 280, PBESpecies.Torchic },\n\t\t\t{ 281, PBESpecies.Combusken },\n\t\t\t{ 282, PBESpecies.Blaziken },\n\t\t\t{ 283, PBESpecies.Mudkip },\n\t\t\t{ 284, PBESpecies.Marshtomp },\n\t\t\t{ 285, PBESpecies.Swampert },\n\t\t\t{ 286, PBESpecies.Poochyena },\n\t\t\t{ 287, PBESpecies.Mightyena },\n\t\t\t{ 288, PBESpecies.Zigzagoon },\n\t\t\t{ 289, PBESpecies.Linoone },\n\t\t\t{ 290, PBESpecies.Wurmple },\n\t\t\t{ 291, PBESpecies.Silcoon },\n\t\t\t{ 292, PBESpecies.Beautifly },\n\t\t\t{ 293, PBESpecies.Cascoon },\n\t\t\t{ 294, PBESpecies.Dustox },\n\t\t\t{ 295, PBESpecies.Lotad },\n\t\t\t{ 296, PBESpecies.Lombre },\n\t\t\t{ 297, PBESpecies.Ludicolo },\n\t\t\t{ 298, PBESpecies.Seedot },\n\t\t\t{ 299, PBESpecies.Nuzleaf },\n\t\t\t{ 300, PBESpecies.Shiftry },\n\t\t\t{ 301, PBESpecies.Nincada },\n\t\t\t{ 302, PBESpecies.Ninjask },\n\t\t\t{ 303, PBESpecies.Shedinja },\n\t\t\t{ 304, PBESpecies.Taillow },\n\t\t\t{ 305, PBESpecies.Swellow },\n\t\t\t{ 306, PBESpecies.Shroomish },\n\t\t\t{ 307, PBESpecies.Breloom },\n\t\t\t{ 308, PBESpecies.Spinda },\n\t\t\t{ 309, PBESpecies.Wingull },\n\t\t\t{ 310, PBESpecies.Pelipper },\n\t\t\t{ 311, PBESpecies.Surskit },\n\t\t\t{ 312, PBESpecies.Masquerain },\n\t\t\t{ 313, PBESpecies.Wailmer },\n\t\t\t{ 314, PBESpecies.Wailord },\n\t\t\t{ 315, PBESpecies.Skitty },\n\t\t\t{ 316, PBESpecies.Delcatty },\n\t\t\t{ 317, PBESpecies.Kecleon },\n\t\t\t{ 318, PBESpecies.Baltoy },\n\t\t\t{ 319, PBESpecies.Claydol },\n\t\t\t{ 320, PBESpecies.Nosepass },\n\t\t\t{ 321, PBESpecies.Torkoal },\n\t\t\t{ 322, PBESpecies.Sableye },\n\t\t\t{ 323, PBESpecies.Barboach },\n\t\t\t{ 324, PBESpecies.Whiscash },\n\t\t\t{ 325, PBESpecies.Luvdisc },\n\t\t\t{ 326, PBESpecies.Corphish },\n\t\t\t{ 327, PBESpecies.Crawdaunt },\n\t\t\t{ 328, PBESpecies.Feebas },\n\t\t\t{ 329, PBESpecies.Milotic },\n\t\t\t{ 330, PBESpecies.Carvanha },\n\t\t\t{ 331, PBESpecies.Sharpedo },\n\t\t\t{ 332, PBESpecies.Trapinch },\n\t\t\t{ 333, PBESpecies.Vibrava },\n\t\t\t{ 334, PBESpecies.Flygon },\n\t\t\t{ 335, PBESpecies.Makuhita },\n\t\t\t{ 336, PBESpecies.Hariyama },\n\t\t\t{ 337, PBESpecies.Electrike },\n\t\t\t{ 338, PBESpecies.Manectric },\n\t\t\t{ 339, PBESpecies.Numel },\n\t\t\t{ 340, PBESpecies.Camerupt },\n\t\t\t{ 341, PBESpecies.Spheal },\n\t\t\t{ 342, PBESpecies.Sealeo },\n\t\t\t{ 343, PBESpecies.Walrein },\n\t\t\t{ 344, PBESpecies.Cacnea },\n\t\t\t{ 345, PBESpecies.Cacturne },\n\t\t\t{ 346, PBESpecies.Snorunt },\n\t\t\t{ 347, PBESpecies.Glalie },\n\t\t\t{ 348, PBESpecies.Lunatone },\n\t\t\t{ 349, PBESpecies.Solrock },\n\t\t\t{ 350, PBESpecies.Azurill },\n\t\t\t{ 351, PBESpecies.Spoink },\n\t\t\t{ 352, PBESpecies.Grumpig },\n\t\t\t{ 353, PBESpecies.Plusle },\n\t\t\t{ 354, PBESpecies.Minun },\n\t\t\t{ 355, PBESpecies.Mawile },\n\t\t\t{ 356, PBESpecies.Meditite },\n\t\t\t{ 357, PBESpecies.Medicham },\n\t\t\t{ 358, PBESpecies.Swablu },\n\t\t\t{ 359, PBESpecies.Altaria },\n\t\t\t{ 360, PBESpecies.Wynaut },\n\t\t\t{ 361, PBESpecies.Duskull },\n\t\t\t{ 362, PBESpecies.Dusclops },\n\t\t\t{ 363, PBESpecies.Roselia },\n\t\t\t{ 364, PBESpecies.Slakoth },\n\t\t\t{ 365, PBESpecies.Vigoroth },\n\t\t\t{ 366, PBESpecies.Slaking },\n\t\t\t{ 367, PBESpecies.Gulpin },\n\t\t\t{ 368, PBESpecies.Swalot },\n\t\t\t{ 369, PBESpecies.Tropius },\n\t\t\t{ 370, PBESpecies.Whismur },\n\t\t\t{ 371, PBESpecies.Loudred },\n\t\t\t{ 372, PBESpecies.Exploud },\n\t\t\t{ 373, PBESpecies.Clamperl },\n\t\t\t{ 374, PBESpecies.Huntail },\n\t\t\t{ 375, PBESpecies.Gorebyss },\n\t\t\t{ 376, PBESpecies.Absol },\n\t\t\t{ 377, PBESpecies.Shuppet },\n\t\t\t{ 378, PBESpecies.Banette },\n\t\t\t{ 379, PBESpecies.Seviper },\n\t\t\t{ 380, PBESpecies.Zangoose },\n\t\t\t{ 381, PBESpecies.Relicanth },\n\t\t\t{ 382, PBESpecies.Aron },\n\t\t\t{ 383, PBESpecies.Lairon },\n\t\t\t{ 384, PBESpecies.Aggron },\n\t\t\t{ 385, PBESpecies.Castform },\n\t\t\t{ 386, PBESpecies.Volbeat },\n\t\t\t{ 387, PBESpecies.Illumise },\n\t\t\t{ 388, PBESpecies.Lileep },\n\t\t\t{ 389, PBESpecies.Cradily },\n\t\t\t{ 390, PBESpecies.Anorith },\n\t\t\t{ 391, PBESpecies.Armaldo },\n\t\t\t{ 392, PBESpecies.Ralts },\n\t\t\t{ 393, PBESpecies.Kirlia },\n\t\t\t{ 394, PBESpecies.Gardevoir },\n\t\t\t{ 395, PBESpecies.Bagon },\n\t\t\t{ 396, PBESpecies.Shelgon },\n\t\t\t{ 397, PBESpecies.Salamence },\n\t\t\t{ 398, PBESpecies.Beldum },\n\t\t\t{ 399, PBESpecies.Metang },\n\t\t\t{ 400, PBESpecies.Metagross },\n\t\t\t{ 401, PBESpecies.Regirock },\n\t\t\t{ 402, PBESpecies.Regice },\n\t\t\t{ 403, PBESpecies.Registeel },\n\t\t\t{ 404, PBESpecies.Kyogre },\n\t\t\t{ 405, PBESpecies.Groudon },\n\t\t\t{ 406, PBESpecies.Rayquaza },\n\t\t\t{ 407, PBESpecies.Latias },\n\t\t\t{ 408, PBESpecies.Latios },\n\t\t\t{ 409, PBESpecies.Jirachi },\n\t\t\t{ 410, PBESpecies.Deoxys },\n\t\t\t{ 411, PBESpecies.Chimecho },\n\t\t};\n\tprivate static readonly PBEMove[] _gen3TMHMs = new PBEMove[58]\n\t\t{\n\t\t\tPBEMove.FocusPunch,\n\t\t\tPBEMove.DragonClaw,\n\t\t\tPBEMove.WaterPulse,\n\t\t\tPBEMove.CalmMind,\n\t\t\tPBEMove.Roar,\n\t\t\tPBEMove.Toxic,\n\t\t\tPBEMove.Hail,\n\t\t\tPBEMove.BulkUp,\n\t\t\tPBEMove.BulletSeed,\n\t\t\tPBEMove.HiddenPower,\n\t\t\tPBEMove.SunnyDay,\n\t\t\tPBEMove.Taunt,\n\t\t\tPBEMove.IceBeam,\n\t\t\tPBEMove.Blizzard,\n\t\t\tPBEMove.HyperBeam,\n\t\t\tPBEMove.LightScreen,\n\t\t\tPBEMove.Protect,\n\t\t\tPBEMove.RainDance,\n\t\t\tPBEMove.GigaDrain,\n\t\t\tPBEMove.Safeguard,\n\t\t\tPBEMove.Frustration,\n\t\t\tPBEMove.SolarBeam,\n\t\t\tPBEMove.IronTail,\n\t\t\tPBEMove.Thunderbolt,\n\t\t\tPBEMove.Thunder,\n\t\t\tPBEMove.Earthquake,\n\t\t\tPBEMove.Return,\n\t\t\tPBEMove.Dig,\n\t\t\tPBEMove.Psychic,\n\t\t\tPBEMove.ShadowBall,\n\t\t\tPBEMove.BrickBreak,\n\t\t\tPBEMove.DoubleTeam,\n\t\t\tPBEMove.Reflect,\n\t\t\tPBEMove.ShockWave,\n\t\t\tPBEMove.Flamethrower,\n\t\t\tPBEMove.SludgeBomb,\n\t\t\tPBEMove.Sandstorm,\n\t\t\tPBEMove.FireBlast,\n\t\t\tPBEMove.RockTomb,\n\t\t\tPBEMove.AerialAce,\n\t\t\tPBEMove.Torment,\n\t\t\tPBEMove.Facade,\n\t\t\tPBEMove.SecretPower,\n\t\t\tPBEMove.Rest,\n\t\t\tPBEMove.Attract,\n\t\t\tPBEMove.Thief,\n\t\t\tPBEMove.SteelWing,\n\t\t\tPBEMove.SkillSwap,\n\t\t\tPBEMove.Snatch,\n\t\t\tPBEMove.Overheat,\n\t\t\tPBEMove.Cut,\n\t\t\tPBEMove.Fly,\n\t\t\tPBEMove.Surf,\n\t\t\tPBEMove.Strength,\n\t\t\tPBEMove.Flash,\n\t\t\tPBEMove.RockSmash,\n\t\t\tPBEMove.Waterfall,\n\t\t\tPBEMove.Dive\n\t\t};\n\tprivate static readonly PBEMove[] _frlgTutorMoves = new PBEMove[15]\n\t\t{\n\t\t\tPBEMove.MegaPunch,\n\t\t\tPBEMove.SwordsDance,\n\t\t\tPBEMove.MegaKick,\n\t\t\tPBEMove.BodySlam,\n\t\t\tPBEMove.DoubleEdge,\n\t\t\tPBEMove.Counter,\n\t\t\tPBEMove.SeismicToss,\n\t\t\tPBEMove.Mimic,\n\t\t\tPBEMove.Metronome,\n\t\t\tPBEMove.Softboiled,\n\t\t\tPBEMove.DreamEater,\n\t\t\tPBEMove.ThunderWave,\n\t\t\tPBEMove.Explosion,\n\t\t\tPBEMove.RockSlide,\n\t\t\tPBEMove.Substitute\n\t\t};\n\tprivate static readonly PBEMove[] _emeraldTutorMoves = new PBEMove[30]\n\t\t{\n\t\t\tPBEMove.MegaPunch,\n\t\t\tPBEMove.SwordsDance,\n\t\t\tPBEMove.MegaKick,\n\t\t\tPBEMove.BodySlam,\n\t\t\tPBEMove.DoubleEdge,\n\t\t\tPBEMove.Counter,\n\t\t\tPBEMove.SeismicToss,\n\t\t\tPBEMove.Mimic,\n\t\t\tPBEMove.Metronome,\n\t\t\tPBEMove.Softboiled,\n\t\t\tPBEMove.DreamEater,\n\t\t\tPBEMove.ThunderWave,\n\t\t\tPBEMove.Explosion,\n\t\t\tPBEMove.RockSlide,\n\t\t\tPBEMove.Substitute,\n\t\t\tPBEMove.DynamicPunch,\n\t\t\tPBEMove.Rollout,\n\t\t\tPBEMove.PsychUp,\n\t\t\tPBEMove.Snore,\n\t\t\tPBEMove.IcyWind,\n\t\t\tPBEMove.Endure,\n\t\t\tPBEMove.MudSlap,\n\t\t\tPBEMove.IcePunch,\n\t\t\tPBEMove.Swagger,\n\t\t\tPBEMove.SleepTalk,\n\t\t\tPBEMove.Swift,\n\t\t\tPBEMove.DefenseCurl,\n\t\t\tPBEMove.ThunderPunch,\n\t\t\tPBEMove.FirePunch,\n\t\t\tPBEMove.FuryCutter\n\t\t};\n\tprivate static readonly PBEMove[] _xdTutorMoves = new PBEMove[12]\n\t\t{\n\t\t\tPBEMove.BodySlam,\n\t\t\tPBEMove.DoubleEdge,\n\t\t\tPBEMove.SeismicToss,\n\t\t\tPBEMove.Mimic,\n\t\t\tPBEMove.DreamEater,\n\t\t\tPBEMove.ThunderWave,\n\t\t\tPBEMove.Substitute,\n\t\t\tPBEMove.IcyWind,\n\t\t\tPBEMove.Swagger,\n\t\t\tPBEMove.SkyAttack,\n\t\t\tPBEMove.Selfdestruct,\n\t\t\tPBEMove.Nightmare\n\t\t};\n\t#endregion\n\t#region Gen 4\n\tprivate static readonly Dictionary<int, (PBESpecies, PBEForm)> _gen4SpeciesIndexToPBESpecies = new()\n\t\t{\n\t\t\t{ 496, (PBESpecies.Deoxys, PBEForm.Deoxys_Attack) },\n\t\t\t{ 497, (PBESpecies.Deoxys, PBEForm.Deoxys_Defense) },\n\t\t\t{ 498, (PBESpecies.Deoxys, PBEForm.Deoxys_Speed) },\n\t\t\t{ 499, (PBESpecies.Wormadam, PBEForm.Wormadam_Sandy) },\n\t\t\t{ 500, (PBESpecies.Wormadam, PBEForm.Wormadam_Trash) },\n\t\t\t{ 501, (PBESpecies.Giratina, PBEForm.Giratina_Origin) },\n\t\t\t{ 502, (PBESpecies.Shaymin, PBEForm.Shaymin_Sky) },\n\t\t\t{ 503, (PBESpecies.Rotom, PBEForm.Rotom_Heat) },\n\t\t\t{ 504, (PBESpecies.Rotom, PBEForm.Rotom_Wash) },\n\t\t\t{ 505, (PBESpecies.Rotom, PBEForm.Rotom_Frost) },\n\t\t\t{ 506, (PBESpecies.Rotom, PBEForm.Rotom_Fan) },\n\t\t\t{ 507, (PBESpecies.Rotom, PBEForm.Rotom_Mow) }\n\t\t};\n\tprivate static readonly PBEMove[] _gen4TMHMs = new PBEMove[100]\n\t\t{\n\t\t\tPBEMove.FocusPunch,\n\t\t\tPBEMove.DragonClaw,\n\t\t\tPBEMove.WaterPulse,\n\t\t\tPBEMove.CalmMind,\n\t\t\tPBEMove.Roar,\n\t\t\tPBEMove.Toxic,\n\t\t\tPBEMove.Hail,\n\t\t\tPBEMove.BulkUp,\n\t\t\tPBEMove.BulletSeed,\n\t\t\tPBEMove.HiddenPower,\n\t\t\tPBEMove.SunnyDay,\n\t\t\tPBEMove.Taunt,\n\t\t\tPBEMove.IceBeam,\n\t\t\tPBEMove.Blizzard,\n\t\t\tPBEMove.HyperBeam,\n\t\t\tPBEMove.LightScreen,\n\t\t\tPBEMove.Protect,\n\t\t\tPBEMove.RainDance,\n\t\t\tPBEMove.GigaDrain,\n\t\t\tPBEMove.Safeguard,\n\t\t\tPBEMove.Frustration,\n\t\t\tPBEMove.SolarBeam,\n\t\t\tPBEMove.IronTail,\n\t\t\tPBEMove.Thunderbolt,\n\t\t\tPBEMove.Thunder,\n\t\t\tPBEMove.Earthquake,\n\t\t\tPBEMove.Return,\n\t\t\tPBEMove.Dig,\n\t\t\tPBEMove.Psychic,\n\t\t\tPBEMove.ShadowBall,\n\t\t\tPBEMove.BrickBreak,\n\t\t\tPBEMove.DoubleTeam,\n\t\t\tPBEMove.Reflect,\n\t\t\tPBEMove.ShockWave,\n\t\t\tPBEMove.Flamethrower,\n\t\t\tPBEMove.SludgeBomb,\n\t\t\tPBEMove.Sandstorm,\n\t\t\tPBEMove.FireBlast,\n\t\t\tPBEMove.RockTomb,\n\t\t\tPBEMove.AerialAce,\n\t\t\tPBEMove.Torment,\n\t\t\tPBEMove.Facade,\n\t\t\tPBEMove.SecretPower,\n\t\t\tPBEMove.Rest,\n\t\t\tPBEMove.Attract,\n\t\t\tPBEMove.Thief,\n\t\t\tPBEMove.SteelWing,\n\t\t\tPBEMove.SkillSwap,\n\t\t\tPBEMove.Snatch,\n\t\t\tPBEMove.Overheat,\n\t\t\tPBEMove.Roost,\n\t\t\tPBEMove.FocusBlast,\n\t\t\tPBEMove.EnergyBall,\n\t\t\tPBEMove.FalseSwipe,\n\t\t\tPBEMove.Brine,\n\t\t\tPBEMove.Fling,\n\t\t\tPBEMove.ChargeBeam,\n\t\t\tPBEMove.Endure,\n\t\t\tPBEMove.DragonPulse,\n\t\t\tPBEMove.DrainPunch,\n\t\t\tPBEMove.WillOWisp,\n\t\t\tPBEMove.SilverWind,\n\t\t\tPBEMove.Embargo,\n\t\t\tPBEMove.Explosion,\n\t\t\tPBEMove.ShadowClaw,\n\t\t\tPBEMove.Payback,\n\t\t\tPBEMove.Recycle,\n\t\t\tPBEMove.GigaImpact,\n\t\t\tPBEMove.RockPolish,\n\t\t\tPBEMove.Flash,\n\t\t\tPBEMove.StoneEdge,\n\t\t\tPBEMove.Avalanche,\n\t\t\tPBEMove.ThunderWave,\n\t\t\tPBEMove.GyroBall,\n\t\t\tPBEMove.SwordsDance,\n\t\t\tPBEMove.StealthRock,\n\t\t\tPBEMove.PsychUp,\n\t\t\tPBEMove.Captivate,\n\t\t\tPBEMove.DarkPulse,\n\t\t\tPBEMove.RockSlide,\n\t\t\tPBEMove.XScissor,\n\t\t\tPBEMove.SleepTalk,\n\t\t\tPBEMove.NaturalGift,\n\t\t\tPBEMove.PoisonJab,\n\t\t\tPBEMove.DreamEater,\n\t\t\tPBEMove.GrassKnot,\n\t\t\tPBEMove.Swagger,\n\t\t\tPBEMove.Pluck,\n\t\t\tPBEMove.Uturn,\n\t\t\tPBEMove.Substitute,\n\t\t\tPBEMove.FlashCannon,\n\t\t\tPBEMove.TrickRoom,\n\t\t\tPBEMove.Cut,\n\t\t\tPBEMove.Fly,\n\t\t\tPBEMove.Surf,\n\t\t\tPBEMove.Strength,\n\t\t\tPBEMove.None, // Defog/Whirlpool - code will apply the right one\n            PBEMove.RockSmash,\n\t\t\tPBEMove.Waterfall,\n\t\t\tPBEMove.RockClimb\n\t\t};\n\t// These tutor moves are copied from overlay_0005.bin address 0x2FF64 to ram address 0x02200CE4 on each map load (USA offsets)\n\t// The tutor compatibility is at the end of the table (0x3012C and 0x02200EAC [USA offsets]), starting with Bulbasaur and ending with Arceus (no form entries), and each compatibility is a bitfield of 5 bytes\n\t// Each tutor move entry is 0xC bytes:\n\t// u16 moveId\n\t// u8 redShard\n\t// u8 blueShard\n\t// u8 yellowShard\n\t// u8 greenShard\n\t// u16 unk1\n\t// u32 areaId (0 = Route 212, 1 = Survival Area, 2 = Snowpoint City)\n\tprivate static readonly PBEMove[] _ptTutorMoves = new PBEMove[38]\n\t\t{\n\t\t\tPBEMove.Dive,\n\t\t\tPBEMove.MudSlap,\n\t\t\tPBEMove.FuryCutter,\n\t\t\tPBEMove.IcyWind,\n\t\t\tPBEMove.Rollout,\n\t\t\tPBEMove.ThunderPunch,\n\t\t\tPBEMove.FirePunch,\n\t\t\tPBEMove.Superpower,\n\t\t\tPBEMove.IcePunch,\n\t\t\tPBEMove.IronHead,\n\t\t\tPBEMove.AquaTail,\n\t\t\tPBEMove.OminousWind,\n\t\t\tPBEMove.GastroAcid,\n\t\t\tPBEMove.Snore,\n\t\t\tPBEMove.Spite,\n\t\t\tPBEMove.AirCutter,\n\t\t\tPBEMove.HelpingHand,\n\t\t\tPBEMove.Endeavor,\n\t\t\tPBEMove.Outrage,\n\t\t\tPBEMove.AncientPower,\n\t\t\tPBEMove.Synthesis,\n\t\t\tPBEMove.SignalBeam,\n\t\t\tPBEMove.ZenHeadbutt,\n\t\t\tPBEMove.VacuumWave,\n\t\t\tPBEMove.EarthPower,\n\t\t\tPBEMove.GunkShot,\n\t\t\tPBEMove.Twister,\n\t\t\tPBEMove.SeedBomb,\n\t\t\tPBEMove.IronDefense,\n\t\t\tPBEMove.MagnetRise,\n\t\t\tPBEMove.LastResort,\n\t\t\tPBEMove.Bounce,\n\t\t\tPBEMove.Trick,\n\t\t\tPBEMove.HeatWave,\n\t\t\tPBEMove.KnockOff,\n\t\t\tPBEMove.SuckerPunch,\n\t\t\tPBEMove.Swift,\n\t\t\tPBEMove.Uproar\n\t\t};\n\t// These tutor moves are decompressed to memory (ram address 0x022093E0 in HG, 0x022093F0 in SS) on each map load (USA offsets)\n\t// Each tutor move entry is 0x4 bytes:\n\t// u16 moveId\n\t// u8 bpCost\n\t// u8 areaId (0 = Frontier Access [top left tutor], 1 = Frontier Access [top right tutor], 2 = Frontier Access [bottom right tutor], 3 = Ilex Forest [Headbutt tutor])\n\tprivate static readonly PBEMove[] _hgssTutorMoves = new PBEMove[52]\n\t\t{\n\t\t\tPBEMove.Dive,\n\t\t\tPBEMove.MudSlap,\n\t\t\tPBEMove.FuryCutter,\n\t\t\tPBEMove.IcyWind,\n\t\t\tPBEMove.Rollout,\n\t\t\tPBEMove.ThunderPunch,\n\t\t\tPBEMove.FirePunch,\n\t\t\tPBEMove.Superpower,\n\t\t\tPBEMove.IcePunch,\n\t\t\tPBEMove.IronHead,\n\t\t\tPBEMove.AquaTail,\n\t\t\tPBEMove.OminousWind,\n\t\t\tPBEMove.GastroAcid,\n\t\t\tPBEMove.Snore,\n\t\t\tPBEMove.Spite,\n\t\t\tPBEMove.AirCutter,\n\t\t\tPBEMove.HelpingHand,\n\t\t\tPBEMove.Endeavor,\n\t\t\tPBEMove.Outrage,\n\t\t\tPBEMove.AncientPower,\n\t\t\tPBEMove.Synthesis,\n\t\t\tPBEMove.SignalBeam,\n\t\t\tPBEMove.ZenHeadbutt,\n\t\t\tPBEMove.VacuumWave,\n\t\t\tPBEMove.EarthPower,\n\t\t\tPBEMove.GunkShot,\n\t\t\tPBEMove.Twister,\n\t\t\tPBEMove.SeedBomb,\n\t\t\tPBEMove.IronDefense,\n\t\t\tPBEMove.MagnetRise,\n\t\t\tPBEMove.LastResort,\n\t\t\tPBEMove.Bounce,\n\t\t\tPBEMove.Trick,\n\t\t\tPBEMove.HeatWave,\n\t\t\tPBEMove.KnockOff,\n\t\t\tPBEMove.SuckerPunch,\n\t\t\tPBEMove.Swift,\n\t\t\tPBEMove.Uproar,\n\t\t\tPBEMove.SuperFang,\n\t\t\tPBEMove.PainSplit,\n\t\t\tPBEMove.StringShot,\n\t\t\tPBEMove.Tailwind,\n\t\t\tPBEMove.Gravity,\n\t\t\tPBEMove.WorrySeed,\n\t\t\tPBEMove.MagicCoat,\n\t\t\tPBEMove.RolePlay,\n\t\t\tPBEMove.HealBell,\n\t\t\tPBEMove.LowKick,\n\t\t\tPBEMove.SkyAttack,\n\t\t\tPBEMove.Block,\n\t\t\tPBEMove.BugBite,\n\t\t\tPBEMove.Headbutt\n\t\t};\n\t#endregion\n\t#region Gen 5\n\tprivate static readonly Dictionary<int, (PBESpecies, PBEForm)> _bwSpeciesIndexToPBESpecies = new()\n\t\t{\n\t\t\t{ 650, (PBESpecies.Deoxys, PBEForm.Deoxys_Attack) },\n\t\t\t{ 651, (PBESpecies.Deoxys, PBEForm.Deoxys_Defense) },\n\t\t\t{ 652, (PBESpecies.Deoxys, PBEForm.Deoxys_Speed) },\n\t\t\t{ 653, (PBESpecies.Wormadam, PBEForm.Wormadam_Sandy) },\n\t\t\t{ 654, (PBESpecies.Wormadam, PBEForm.Wormadam_Trash) },\n\t\t\t{ 655, (PBESpecies.Shaymin, PBEForm.Shaymin_Sky) },\n\t\t\t{ 656, (PBESpecies.Giratina, PBEForm.Giratina_Origin) },\n\t\t\t{ 657, (PBESpecies.Rotom, PBEForm.Rotom_Heat) },\n\t\t\t{ 658, (PBESpecies.Rotom, PBEForm.Rotom_Wash) },\n\t\t\t{ 659, (PBESpecies.Rotom, PBEForm.Rotom_Frost) },\n\t\t\t{ 660, (PBESpecies.Rotom, PBEForm.Rotom_Fan) },\n\t\t\t{ 661, (PBESpecies.Rotom, PBEForm.Rotom_Mow) },\n\t\t\t{ 662, (PBESpecies.Castform, PBEForm.Castform_Sunny) },\n\t\t\t{ 663, (PBESpecies.Castform, PBEForm.Castform_Rainy) },\n\t\t\t{ 664, (PBESpecies.Castform, PBEForm.Castform_Snowy) },\n\t\t\t{ 665, (PBESpecies.Basculin, PBEForm.Basculin_Blue) },\n\t\t\t{ 666, (PBESpecies.Darmanitan, PBEForm.Darmanitan_Zen) },\n\t\t\t{ 667, (PBESpecies.Meloetta, PBEForm.Meloetta_Pirouette) }\n\t\t};\n\tprivate static readonly Dictionary<int, (PBESpecies, PBEForm)> _b2w2SpeciesIndexToPBESpecies = new()\n\t\t{\n\t\t\t{ 685, (PBESpecies.Deoxys, PBEForm.Deoxys_Attack) },\n\t\t\t{ 686, (PBESpecies.Deoxys, PBEForm.Deoxys_Defense) },\n\t\t\t{ 687, (PBESpecies.Deoxys, PBEForm.Deoxys_Speed) },\n\t\t\t{ 688, (PBESpecies.Wormadam, PBEForm.Wormadam_Sandy) },\n\t\t\t{ 689, (PBESpecies.Wormadam, PBEForm.Wormadam_Trash) },\n\t\t\t{ 690, (PBESpecies.Shaymin, PBEForm.Shaymin_Sky) },\n\t\t\t{ 691, (PBESpecies.Giratina, PBEForm.Giratina_Origin) },\n\t\t\t{ 692, (PBESpecies.Rotom, PBEForm.Rotom_Heat) },\n\t\t\t{ 693, (PBESpecies.Rotom, PBEForm.Rotom_Wash) },\n\t\t\t{ 694, (PBESpecies.Rotom, PBEForm.Rotom_Frost) },\n\t\t\t{ 695, (PBESpecies.Rotom, PBEForm.Rotom_Fan) },\n\t\t\t{ 696, (PBESpecies.Rotom, PBEForm.Rotom_Mow) },\n\t\t\t{ 697, (PBESpecies.Castform, PBEForm.Castform_Sunny) },\n\t\t\t{ 698, (PBESpecies.Castform, PBEForm.Castform_Rainy) },\n\t\t\t{ 699, (PBESpecies.Castform, PBEForm.Castform_Snowy) },\n\t\t\t{ 700, (PBESpecies.Basculin, PBEForm.Basculin_Blue) },\n\t\t\t{ 701, (PBESpecies.Darmanitan, PBEForm.Darmanitan_Zen) },\n\t\t\t{ 702, (PBESpecies.Meloetta, PBEForm.Meloetta_Pirouette) },\n\t\t\t{ 703, (PBESpecies.Kyurem, PBEForm.Kyurem_White) },\n\t\t\t{ 704, (PBESpecies.Kyurem, PBEForm.Kyurem_Black) },\n\t\t\t{ 705, (PBESpecies.Keldeo, PBEForm.Keldeo_Resolute) },\n\t\t\t{ 706, (PBESpecies.Tornadus, PBEForm.Tornadus_Therian) },\n\t\t\t{ 707, (PBESpecies.Thundurus, PBEForm.Thundurus_Therian) },\n\t\t\t{ 708, (PBESpecies.Landorus, PBEForm.Landorus_Therian) }\n\t\t};\n\tprivate static readonly PBEMove[] _gen5TMHMs = new PBEMove[101]\n\t\t{\n\t\t\tPBEMove.HoneClaws,\n\t\t\tPBEMove.DragonClaw,\n\t\t\tPBEMove.Psyshock,\n\t\t\tPBEMove.CalmMind,\n\t\t\tPBEMove.Roar,\n\t\t\tPBEMove.Toxic,\n\t\t\tPBEMove.Hail,\n\t\t\tPBEMove.BulkUp,\n\t\t\tPBEMove.Venoshock,\n\t\t\tPBEMove.HiddenPower,\n\t\t\tPBEMove.SunnyDay,\n\t\t\tPBEMove.Taunt,\n\t\t\tPBEMove.IceBeam,\n\t\t\tPBEMove.Blizzard,\n\t\t\tPBEMove.HyperBeam,\n\t\t\tPBEMove.LightScreen,\n\t\t\tPBEMove.Protect,\n\t\t\tPBEMove.RainDance,\n\t\t\tPBEMove.Telekinesis,\n\t\t\tPBEMove.Safeguard,\n\t\t\tPBEMove.Frustration,\n\t\t\tPBEMove.SolarBeam,\n\t\t\tPBEMove.SmackDown,\n\t\t\tPBEMove.Thunderbolt,\n\t\t\tPBEMove.Thunder,\n\t\t\tPBEMove.Earthquake,\n\t\t\tPBEMove.Return,\n\t\t\tPBEMove.Dig,\n\t\t\tPBEMove.Psychic,\n\t\t\tPBEMove.ShadowBall,\n\t\t\tPBEMove.BrickBreak,\n\t\t\tPBEMove.DoubleTeam,\n\t\t\tPBEMove.Reflect,\n\t\t\tPBEMove.SludgeWave,\n\t\t\tPBEMove.Flamethrower,\n\t\t\tPBEMove.SludgeBomb,\n\t\t\tPBEMove.Sandstorm,\n\t\t\tPBEMove.FireBlast,\n\t\t\tPBEMove.RockTomb,\n\t\t\tPBEMove.AerialAce,\n\t\t\tPBEMove.Torment,\n\t\t\tPBEMove.Facade,\n\t\t\tPBEMove.FlameCharge,\n\t\t\tPBEMove.Rest,\n\t\t\tPBEMove.Attract,\n\t\t\tPBEMove.Thief,\n\t\t\tPBEMove.LowSweep,\n\t\t\tPBEMove.Round,\n\t\t\tPBEMove.EchoedVoice,\n\t\t\tPBEMove.Overheat,\n\t\t\tPBEMove.AllySwitch,\n\t\t\tPBEMove.FocusBlast,\n\t\t\tPBEMove.EnergyBall,\n\t\t\tPBEMove.FalseSwipe,\n\t\t\tPBEMove.Scald,\n\t\t\tPBEMove.Fling,\n\t\t\tPBEMove.ChargeBeam,\n\t\t\tPBEMove.SkyDrop,\n\t\t\tPBEMove.Incinerate,\n\t\t\tPBEMove.Quash,\n\t\t\tPBEMove.WillOWisp,\n\t\t\tPBEMove.Acrobatics,\n\t\t\tPBEMove.Embargo,\n\t\t\tPBEMove.Explosion,\n\t\t\tPBEMove.ShadowClaw,\n\t\t\tPBEMove.Payback,\n\t\t\tPBEMove.Retaliate,\n\t\t\tPBEMove.GigaImpact,\n\t\t\tPBEMove.RockPolish,\n\t\t\tPBEMove.Flash,\n\t\t\tPBEMove.StoneEdge,\n\t\t\tPBEMove.VoltSwitch,\n\t\t\tPBEMove.ThunderWave,\n\t\t\tPBEMove.GyroBall,\n\t\t\tPBEMove.SwordsDance,\n\t\t\tPBEMove.StruggleBug,\n\t\t\tPBEMove.PsychUp,\n\t\t\tPBEMove.Bulldoze,\n\t\t\tPBEMove.FrostBreath,\n\t\t\tPBEMove.RockSlide,\n\t\t\tPBEMove.XScissor,\n\t\t\tPBEMove.DragonTail,\n\t\t\tPBEMove.WorkUp,\n\t\t\tPBEMove.PoisonJab,\n\t\t\tPBEMove.DreamEater,\n\t\t\tPBEMove.GrassKnot,\n\t\t\tPBEMove.Swagger,\n\t\t\tPBEMove.Pluck,\n\t\t\tPBEMove.Uturn,\n\t\t\tPBEMove.Substitute,\n\t\t\tPBEMove.FlashCannon,\n\t\t\tPBEMove.TrickRoom,\n\t\t\tPBEMove.WildCharge,\n\t\t\tPBEMove.RockSmash,\n\t\t\tPBEMove.Snarl,\n\t\t\tPBEMove.Cut,\n\t\t\tPBEMove.Fly,\n\t\t\tPBEMove.Surf,\n\t\t\tPBEMove.Strength,\n\t\t\tPBEMove.Waterfall,\n\t\t\tPBEMove.Dive\n\t\t};\n\tprivate static readonly PBEMove[] _gen5FreeTutorMoves = new PBEMove[7]\n\t\t{\n\t\t\tPBEMove.GrassPledge,\n\t\t\tPBEMove.FirePledge,\n\t\t\tPBEMove.WaterPledge,\n\t\t\tPBEMove.FrenzyPlant,\n\t\t\tPBEMove.BlastBurn,\n\t\t\tPBEMove.HydroCannon,\n\t\t\tPBEMove.DracoMeteor\n\t\t};\n\t// These tutor moves are decompressed to memory (ram address 0x021D0B38 in B2, 0x021D0B6C in W2) on each map load (USA offsets)\n\t// For some reason, the location order in this table is different from the Pokémon's compatibility (this table is [Humilau,Driftveil,Nacrene,Lentimas] but in Pokémon data it is [Driftveil,Lentimas,Humilau,Nacrene])\n\t// Each tutor move entry is 0xC bytes:\n\t// u32 moveId\n\t// u32 shardCost\n\t// u32 indexInList\n\tprivate static readonly PBEMove[][] _b2w2TutorMoves = new PBEMove[4][]\n\t\t{\n\t\t\tnew PBEMove[15] // Driftveil City\n            {\n\t\t\t\tPBEMove.BugBite,\n\t\t\t\tPBEMove.Covet,\n\t\t\t\tPBEMove.SuperFang,\n\t\t\t\tPBEMove.DualChop,\n\t\t\t\tPBEMove.SignalBeam,\n\t\t\t\tPBEMove.IronHead,\n\t\t\t\tPBEMove.SeedBomb,\n\t\t\t\tPBEMove.DrillRun,\n\t\t\t\tPBEMove.Bounce,\n\t\t\t\tPBEMove.LowKick,\n\t\t\t\tPBEMove.GunkShot,\n\t\t\t\tPBEMove.Uproar,\n\t\t\t\tPBEMove.ThunderPunch,\n\t\t\t\tPBEMove.FirePunch,\n\t\t\t\tPBEMove.IcePunch\n\t\t\t},\n\t\t\tnew PBEMove[17] // Lentimas Town\n            {\n\t\t\t\tPBEMove.MagicCoat,\n\t\t\t\tPBEMove.Block,\n\t\t\t\tPBEMove.EarthPower,\n\t\t\t\tPBEMove.FoulPlay,\n\t\t\t\tPBEMove.Gravity,\n\t\t\t\tPBEMove.MagnetRise,\n\t\t\t\tPBEMove.IronDefense,\n\t\t\t\tPBEMove.LastResort,\n\t\t\t\tPBEMove.Superpower,\n\t\t\t\tPBEMove.Electroweb,\n\t\t\t\tPBEMove.IcyWind,\n\t\t\t\tPBEMove.AquaTail,\n\t\t\t\tPBEMove.DarkPulse,\n\t\t\t\tPBEMove.ZenHeadbutt,\n\t\t\t\tPBEMove.DragonPulse,\n\t\t\t\tPBEMove.HyperVoice,\n\t\t\t\tPBEMove.IronTail\n\t\t\t},\n\t\t\tnew PBEMove[13] // Humilau City\n            {\n\t\t\t\tPBEMove.Bind,\n\t\t\t\tPBEMove.Snore,\n\t\t\t\tPBEMove.KnockOff,\n\t\t\t\tPBEMove.Synthesis,\n\t\t\t\tPBEMove.HeatWave,\n\t\t\t\tPBEMove.RolePlay,\n\t\t\t\tPBEMove.HealBell,\n\t\t\t\tPBEMove.Tailwind,\n\t\t\t\tPBEMove.SkyAttack,\n\t\t\t\tPBEMove.PainSplit,\n\t\t\t\tPBEMove.GigaDrain,\n\t\t\t\tPBEMove.DrainPunch,\n\t\t\t\tPBEMove.Roost\n\t\t\t},\n\t\t\tnew PBEMove[15] // Nacrene City\n            {\n\t\t\t\tPBEMove.GastroAcid,\n\t\t\t\tPBEMove.WorrySeed,\n\t\t\t\tPBEMove.Spite,\n\t\t\t\tPBEMove.AfterYou,\n\t\t\t\tPBEMove.HelpingHand,\n\t\t\t\tPBEMove.Trick,\n\t\t\t\tPBEMove.MagicRoom,\n\t\t\t\tPBEMove.WonderRoom,\n\t\t\t\tPBEMove.Endeavor,\n\t\t\t\tPBEMove.Outrage,\n\t\t\t\tPBEMove.Recycle,\n\t\t\t\tPBEMove.Snatch,\n\t\t\t\tPBEMove.StealthRock,\n\t\t\t\tPBEMove.SleepTalk,\n\t\t\t\tPBEMove.SkillSwap\n\t\t\t}\n\t\t};\n\t#endregion\n\t#endregion\n}\n"
  },
  {
    "path": "PokemonBattleEngineExtras/PokemonDataDumper_DreamWorld.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data;\n\nnamespace Kermalis.PokemonBattleEngineExtras;\n\ninternal static partial class PokemonDataDumper\n{\n\tprivate static readonly (PBESpecies Species, PBEForm Form, PBEMove MoveA, PBEMove MoveB, PBEMove MoveC, bool BW, bool B2W2)[] _dreamWorld = new (PBESpecies, PBEForm, PBEMove, PBEMove, PBEMove, bool, bool)[]\n\t{\n\t\t#region Pleasant Forest\n\t\t(PBESpecies.Rattata, 0, PBEMove.QuickAttack, PBEMove.MeFirst, PBEMove.IronTail, true, true),\n\t\t(PBESpecies.Nidoran_F, 0, PBEMove.Scratch, PBEMove.SuckerPunch, PBEMove.SuperFang, true, false),\n\t\t(PBESpecies.Nidoran_M, 0, PBEMove.Peck, PBEMove.Counter, PBEMove.SuperFang, true, false),\n\t\t(PBESpecies.Oddish, 0, PBEMove.SweetScent, PBEMove.TeeterDance, PBEMove.GigaDrain, true, true),\n\t\t(PBESpecies.Bellsprout, 0, PBEMove.VineWhip, PBEMove.Synthesis, PBEMove.SeedBomb, true, true),\n\t\t(PBESpecies.Ponyta, 0, PBEMove.Tackle, PBEMove.Thrash, PBEMove.HeatWave, true, true),\n\t\t(PBESpecies.Farfetchd, 0, PBEMove.FuryCutter, PBEMove.Roost, PBEMove.LeafBlade, true, true),\n\t\t(PBESpecies.Doduo, 0, PBEMove.Growl, PBEMove.Flail, PBEMove.Roost, true, true),\n\t\t(PBESpecies.Exeggcute, 0, PBEMove.Barrage, PBEMove.Synthesis, PBEMove.GigaDrain, true, true),\n\t\t(PBESpecies.Lickitung, 0, PBEMove.Lick, PBEMove.SleepTalk, PBEMove.RockClimb, true, true),\n\t\t(PBESpecies.Tangela, 0, PBEMove.SleepPowder, PBEMove.LeechSeed, PBEMove.SeedBomb, true, true),\n\t\t(PBESpecies.Kangaskhan, 0, PBEMove.FakeOut, PBEMove.Counter, PBEMove.DrainPunch, true, true),\n\t\t(PBESpecies.Sentret, 0, PBEMove.Scratch, PBEMove.Endure, PBEMove.Covet, true, true),\n\t\t(PBESpecies.Igglybuff, 0, PBEMove.Sing, PBEMove.FakeTears, PBEMove.HelpingHand, true, false),\n\t\t(PBESpecies.Mareep, 0, PBEMove.ThunderShock, PBEMove.Reflect, PBEMove.ShockWave, true, true),\n\t\t(PBESpecies.Hoppip, 0, PBEMove.Synthesis, PBEMove.HelpingHand, PBEMove.BulletSeed, true, false),\n\t\t(PBESpecies.Sunkern, 0, PBEMove.MegaDrain, PBEMove.SweetScent, PBEMove.EarthPower, true, true),\n\t\t(PBESpecies.Stantler, 0, PBEMove.Tackle, PBEMove.Disable, PBEMove.SkillSwap, true, true),\n\t\t(PBESpecies.Poochyena, 0, PBEMove.Howl, PBEMove.PoisonFang, PBEMove.DarkPulse, true, true),\n\t\t(PBESpecies.Lotad, 0, PBEMove.Absorb, PBEMove.LeechSeed, PBEMove.WaterPulse, true, false),\n\t\t(PBESpecies.Taillow, 0, PBEMove.Peck, PBEMove.MirrorMove, PBEMove.Tailwind, true, false),\n\t\t(PBESpecies.Surskit, 0, PBEMove.Bubble, PBEMove.HydroPump, PBEMove.GigaDrain, true, true),\n\t\t(PBESpecies.Bidoof, 0, PBEMove.Tackle, PBEMove.AquaTail, PBEMove.SecretPower, true, true),\n\t\t(PBESpecies.Shinx, 0, PBEMove.Charge, PBEMove.MagnetRise, PBEMove.NightSlash, true, true),\n\t\t(PBESpecies.Tympole, 0, PBEMove.Round, PBEMove.EarthPower, PBEMove.WaterPulse, false, true),\n\t\t(PBESpecies.Cottonee, 0, PBEMove.LeechSeed, PBEMove.Encore, PBEMove.WorrySeed, false, true),\n\t\t(PBESpecies.Petilil, 0, PBEMove.SleepPowder, PBEMove.Charm, PBEMove.SweetScent, false, true),\n\t\t(PBESpecies.Karrablast, 0, PBEMove.Endure, PBEMove.Megahorn, PBEMove.BugBite, false, true),\n\t\t(PBESpecies.Shelmet, 0, PBEMove.Acid, PBEMove.BatonPass, PBEMove.Encore, false, true),\n\t\t(PBESpecies.Glameow, 0, PBEMove.FakeOut, PBEMove.Assurance, PBEMove.SecretPower, true, true),\n\t\t(PBESpecies.Scolipede, 0, PBEMove.PoisonTail, PBEMove.ToxicSpikes, PBEMove.Superpower, false, true),\n\t\t(PBESpecies.Psyduck, 0, PBEMove.WaterSport, PBEMove.Encore, PBEMove.Brine, true, true),\n\t\t(PBESpecies.Growlithe, 0, PBEMove.Bite, PBEMove.BodySlam, PBEMove.Endure, true, true),\n\t\t(PBESpecies.Scyther, 0, PBEMove.QuickAttack, PBEMove.BatonPass, PBEMove.Tailwind, true, true),\n\t\t(PBESpecies.Tauros, 0, PBEMove.Rage, PBEMove.IronTail, PBEMove.RockClimb, true, true),\n\t\t(PBESpecies.Marill, 0, PBEMove.DefenseCurl, PBEMove.AquaJet, PBEMove.IcePunch, true, true),\n\t\t(PBESpecies.Sudowoodo, 0, PBEMove.Flail, PBEMove.Rollout, PBEMove.RolePlay, true, true),\n\t\t(PBESpecies.Girafarig, 0, PBEMove.Confusion, PBEMove.MirrorCoat, PBEMove.SkillSwap, true, true),\n\t\t(PBESpecies.Miltank, 0, PBEMove.DefenseCurl, PBEMove.Curse, PBEMove.IronTail, true, true),\n\t\t(PBESpecies.Zigzagoon, 0, PBEMove.Tackle, PBEMove.Trick, PBEMove.LastResort, true, true),\n\t\t(PBESpecies.Electrike, 0, PBEMove.ThunderWave, PBEMove.IceFang, PBEMove.SignalBeam, true, false),\n\t\t(PBESpecies.Castform, 0, PBEMove.Ember, PBEMove.OminousWind, PBEMove.WaterPulse, true, false),\n\t\t(PBESpecies.Pachirisu, 0, PBEMove.QuickAttack, PBEMove.Covet, PBEMove.ShockWave, true, false),\n\t\t(PBESpecies.Buneary, 0, PBEMove.Foresight, PBEMove.FakeOut, PBEMove.DrainPunch, true, true),\n\t\t(PBESpecies.Vulpix, 0, PBEMove.Roar, PBEMove.HeatWave, PBEMove.DarkPulse, true, true),\n\t\t(PBESpecies.Poliwag, 0, PBEMove.Hypnosis, PBEMove.Mist, PBEMove.SleepTalk, true, true),\n\t\t(PBESpecies.Natu, 0, PBEMove.NightShade, PBEMove.FeatherDance, PBEMove.GigaDrain, true, true),\n\t\t(PBESpecies.Elekid, 0, PBEMove.ThunderShock, PBEMove.CrossChop, PBEMove.MagnetRise, true, true),\n\t\t(PBESpecies.Skitty, 0, PBEMove.Foresight, PBEMove.Tickle, PBEMove.Captivate, true, true),\n\t\t#endregion\n\t\t#region Windswept Sky\n\t\t(PBESpecies.Butterfree, 0, PBEMove.Confusion, PBEMove.Roost, PBEMove.AirCutter, true, false),\n\t\t(PBESpecies.Pidgey, 0, PBEMove.Gust, PBEMove.SteelWing, PBEMove.SecretPower, true, true),\n\t\t(PBESpecies.Spearow, 0, PBEMove.Peck, PBEMove.FaintAttack, PBEMove.SteelWing, true, true),\n\t\t(PBESpecies.Zubat, 0, PBEMove.Supersonic, PBEMove.Hypnosis, PBEMove.SuperFang, true, true),\n\t\t(PBESpecies.Aerodactyl, 0, PBEMove.Bite, PBEMove.Assurance, PBEMove.StealthRock, true, true),\n\t\t(PBESpecies.Hoothoot, 0, PBEMove.Foresight, PBEMove.NightShade, PBEMove.Recycle, true, false),\n\t\t(PBESpecies.Ledyba, 0, PBEMove.CometPunch, PBEMove.BugBite, PBEMove.ThunderPunch, true, true),\n\t\t(PBESpecies.Hoppip, 0, PBEMove.Synthesis, PBEMove.Encore, PBEMove.Bounce, true, true),\n\t\t(PBESpecies.Yanma, 0, PBEMove.QuickAttack, PBEMove.Feint, PBEMove.GigaDrain, true, true),\n\t\t(PBESpecies.Murkrow, 0, PBEMove.Peck, PBEMove.ConfuseRay, PBEMove.Roost, true, true),\n\t\t(PBESpecies.Gligar, 0, PBEMove.SandAttack, PBEMove.Feint, PBEMove.Tailwind, true, true),\n\t\t(PBESpecies.Delibird, 0, PBEMove.Present, PBEMove.IceShard, PBEMove.FocusPunch, true, true),\n\t\t(PBESpecies.Taillow, 0, PBEMove.Peck, PBEMove.Endure, PBEMove.BraveBird, true, true),\n\t\t(PBESpecies.Wingull, 0, PBEMove.WaterGun, PBEMove.Twister, PBEMove.ShockWave, true, false),\n\t\t(PBESpecies.Swablu, 0, PBEMove.Peck, PBEMove.FeatherDance, PBEMove.Roost, true, false),\n\t\t(PBESpecies.Staravia, 0, PBEMove.WingAttack, PBEMove.FeatherDance, PBEMove.Tailwind, true, true),\n\t\t(PBESpecies.Pidove, 0, PBEMove.Gust, PBEMove.Hypnosis, PBEMove.MorningSun, false, true),\n\t\t(PBESpecies.Sigilyph, 0, PBEMove.Hypnosis, PBEMove.StoredPower, PBEMove.HeatWave, false, true),\n\t\t(PBESpecies.Ducklett, 0, PBEMove.Defog, PBEMove.Brine, PBEMove.MeFirst, false, true),\n\t\t(PBESpecies.Emolga, 0, PBEMove.QuickAttack, PBEMove.AirSlash, PBEMove.Charm, false, true),\n\t\t(PBESpecies.Skarmory, 0, PBEMove.Peck, PBEMove.DrillPeck, PBEMove.Roost, true, true),\n\t\t(PBESpecies.Tropius, 0, PBEMove.Gust, PBEMove.LeechSeed, PBEMove.SilverWind, true, true),\n\t\t(PBESpecies.Drifloon, 0, PBEMove.Minimize, PBEMove.Hypnosis, PBEMove.SkillSwap, true, false),\n\t\t(PBESpecies.Chatot, 0, PBEMove.MirrorMove, PBEMove.NastyPlot, PBEMove.RolePlay, true, false),\n\t\t#endregion\n\t\t#region Sparkling Sea\n\t\t(PBESpecies.Slowpoke, 0, PBEMove.Yawn, PBEMove.Block, PBEMove.Brine, true, false),\n\t\t(PBESpecies.Seel, 0, PBEMove.Headbutt, PBEMove.IcicleSpear, PBEMove.SleepTalk, true, true),\n\t\t(PBESpecies.Shellder, 0, PBEMove.Withdraw, PBEMove.Barrier, PBEMove.IcyWind, true, true),\n\t\t(PBESpecies.Krabby, 0, PBEMove.ViceGrip, PBEMove.Amnesia, PBEMove.SecretPower, true, false),\n\t\t(PBESpecies.Horsea, 0, PBEMove.Bubble, PBEMove.Octazooka, PBEMove.Brine, true, true),\n\t\t(PBESpecies.Goldeen, 0, PBEMove.Peck, PBEMove.Psybeam, PBEMove.WaterPulse, true, true),\n\t\t(PBESpecies.Seaking, 0, PBEMove.WaterPulse, PBEMove.SleepTalk, PBEMove.Endure, true, false),\n\t\t(PBESpecies.Staryu, 0, PBEMove.WaterGun, PBEMove.Recycle, PBEMove.IcyWind, true, false),\n\t\t(PBESpecies.Magikarp, 0, PBEMove.Splash, PBEMove.Flail, PBEMove.Bounce, true, true),\n\t\t(PBESpecies.Omanyte, 0, PBEMove.Bite, PBEMove.MuddyWater, PBEMove.IcyWind, true, true),\n\t\t(PBESpecies.Kabuto, 0, PBEMove.Absorb, PBEMove.Flail, PBEMove.StealthRock, true, true),\n\t\t(PBESpecies.Chinchou, 0, PBEMove.ThunderWave, PBEMove.Amnesia, PBEMove.ShockWave, true, true),\n\t\t(PBESpecies.Wooper, 0, PBEMove.WaterGun, PBEMove.BodySlam, PBEMove.AquaTail, true, true),\n\t\t(PBESpecies.Qwilfish, 0, PBEMove.PoisonSting, PBEMove.AquaJet, PBEMove.SecretPower, true, true),\n\t\t(PBESpecies.Corsola, 0, PBEMove.Bubble, PBEMove.ConfuseRay, PBEMove.StealthRock, true, false),\n\t\t(PBESpecies.Remoraid, 0, PBEMove.LockOn, PBEMove.RockBlast, PBEMove.Brine, true, true),\n\t\t(PBESpecies.Mantine, 0, PBEMove.Supersonic, PBEMove.MirrorCoat, PBEMove.AirCutter, true, true),\n\t\t(PBESpecies.Wailmer, 0, PBEMove.WaterGun, PBEMove.SleepTalk, PBEMove.Bounce, true, true),\n\t\t(PBESpecies.Barboach, 0, PBEMove.MudSlap, PBEMove.SleepTalk, PBEMove.Spark, true, true),\n\t\t(PBESpecies.Clamperl, 0, PBEMove.Whirlpool, PBEMove.Captivate, PBEMove.AquaRing, true, true),\n\t\t(PBESpecies.Relicanth, 0, PBEMove.WaterGun, PBEMove.SleepTalk, PBEMove.EarthPower, true, true),\n\t\t(PBESpecies.Luvdisc, 0, PBEMove.Charm, PBEMove.MudSport, PBEMove.IcyWind, true, true),\n\t\t(PBESpecies.Buizel, 0, PBEMove.WaterSport, PBEMove.Slash, PBEMove.WaterPulse, true, true),\n\t\t(PBESpecies.Finneon, 0, PBEMove.Attract, PBEMove.SweetKiss, PBEMove.WaterPulse, true, true),\n\t\t(PBESpecies.Basculin, PBEForm.Basculin_Red, PBEMove.Headbutt, PBEMove.Agility, PBEMove.ZenHeadbutt, false, true),\n\t\t(PBESpecies.Basculin, PBEForm.Basculin_Blue, PBEMove.Headbutt, PBEMove.Agility, PBEMove.ZenHeadbutt, false, true),\n\t\t(PBESpecies.Alomomola, 0, PBEMove.AquaRing, PBEMove.MirrorCoat, PBEMove.PainSplit, false, true),\n\t\t(PBESpecies.Stunfisk, 0, PBEMove.MudSlap, PBEMove.Curse, PBEMove.Yawn, false, true),\n\t\t(PBESpecies.Tirtouga, 0, PBEMove.Rollout, PBEMove.Flail, PBEMove.IronDefense, false, true),\n\t\t(PBESpecies.Tentacool, 0, PBEMove.Supersonic, PBEMove.Acupressure, PBEMove.GigaDrain, true, true),\n\t\t(PBESpecies.Carvanha, 0, PBEMove.Bite, PBEMove.Thrash, PBEMove.DarkPulse, true, true),\n\t\t(PBESpecies.Corphish, 0, PBEMove.Harden, PBEMove.MetalClaw, PBEMove.Endeavor, true, true),\n\t\t(PBESpecies.Lileep, 0, PBEMove.Acid, PBEMove.MirrorCoat, PBEMove.GigaDrain, true, true),\n\t\t(PBESpecies.Anorith, 0, PBEMove.Scratch, PBEMove.StealthRock, PBEMove.CrossPoison, true, true),\n\t\t(PBESpecies.Feebas, 0, PBEMove.Splash, PBEMove.Captivate, PBEMove.MirrorCoat, true, true),\n\t\t(PBESpecies.Shellos, PBEForm.Shellos_West, PBEMove.MudSlap, PBEMove.Yawn, PBEMove.SecretPower, true, false),\n\t\t(PBESpecies.Shellos, PBEForm.Shellos_East, PBEMove.MudSlap, PBEMove.Yawn, PBEMove.SecretPower, true, false),\n\t\t(PBESpecies.Lapras, 0, PBEMove.ConfuseRay, PBEMove.HornDrill, PBEMove.IcyWind, true, true),\n\t\t(PBESpecies.Dratini, 0, PBEMove.ThunderWave, PBEMove.WaterPulse, PBEMove.DragonBreath, true, true),\n\t\t#endregion\n\t\t#region Spooky Manor\n\t\t(PBESpecies.Gastly, 0, PBEMove.Hypnosis, PBEMove.Disable, PBEMove.SludgeWave, true, true),\n\t\t(PBESpecies.Drowzee, 0, PBEMove.Hypnosis, PBEMove.PsychoCut, PBEMove.DrainPunch, true, true),\n\t\t(PBESpecies.MrMime, 0, PBEMove.Barrier, PBEMove.TeeterDance, PBEMove.SkillSwap, true, true),\n\t\t(PBESpecies.Spinarak, 0, PBEMove.PoisonSting, PBEMove.Electroweb, PBEMove.BugBite, true, true),\n\t\t(PBESpecies.Misdreavus, 0, PBEMove.Psywave, PBEMove.DestinyBond, PBEMove.Inferno, true, true),\n\t\t(PBESpecies.Wobbuffet, 0, PBEMove.MirrorCoat, PBEMove.Charm, PBEMove.Encore, true, false),\n\t\t(PBESpecies.Houndour, 0, PBEMove.Howl, PBEMove.Feint, PBEMove.DarkPulse, true, true),\n\t\t(PBESpecies.Smoochum, 0, PBEMove.SweetKiss, PBEMove.Captivate, PBEMove.SkillSwap, true, false),\n\t\t(PBESpecies.Mawile, 0, PBEMove.FakeTears, PBEMove.FireFang, PBEMove.IcePunch, true, false),\n\t\t(PBESpecies.Meditite, 0, PBEMove.Meditate, PBEMove.DrainPunch, PBEMove.Endure, true, false),\n\t\t(PBESpecies.Spoink, 0, PBEMove.Psywave, PBEMove.SkillSwap, PBEMove.Recycle, true, true),\n\t\t(PBESpecies.Shuppet, 0, PBEMove.NightShade, PBEMove.DestinyBond, PBEMove.PainSplit, true, true),\n\t\t(PBESpecies.Duskull, 0, PBEMove.Disable, PBEMove.PainSplit, PBEMove.Trick, true, true),\n\t\t(PBESpecies.Chimecho, 0, PBEMove.Wrap, PBEMove.Hypnosis, PBEMove.HyperVoice, true, true),\n\t\t(PBESpecies.Stunky, 0, PBEMove.Screech, PBEMove.FoulPlay, PBEMove.SuckerPunch, true, true),\n\t\t(PBESpecies.Bronzor, 0, PBEMove.Hypnosis, PBEMove.SkillSwap, PBEMove.Gravity, true, false),\n\t\t(PBESpecies.Elgyem, 0, PBEMove.HealBlock, PBEMove.Barrier, PBEMove.NastyPlot, false, true),\n\t\t(PBESpecies.Pawniard, 0, PBEMove.FuryCutter, PBEMove.PsychoCut, PBEMove.SuckerPunch, false, true),\n\t\t(PBESpecies.Galvantula, 0, PBEMove.ElectroBall, PBEMove.Disable, PBEMove.Pursuit, false, true),\n\t\t(PBESpecies.Meowth, 0, PBEMove.Scratch, PBEMove.Hypnosis, PBEMove.SecretPower, true, false),\n\t\t(PBESpecies.Snubbull, 0, PBEMove.Charm, PBEMove.CloseCombat, PBEMove.DoubleEdge, true, true),\n\t\t(PBESpecies.Smeargle, 0, PBEMove.Sketch, PBEMove.Captivate, PBEMove.SleepTalk, true, true),\n\t\t(PBESpecies.Volbeat, 0, PBEMove.Flash, PBEMove.Trick, PBEMove.Tailwind, true, true),\n\t\t(PBESpecies.Illumise, 0, PBEMove.Charm, PBEMove.FakeTears, PBEMove.Tailwind, true, true),\n\t\t(PBESpecies.Rotom, PBEForm.Rotom, PBEMove.ThunderWave, PBEMove.ShockWave, PBEMove.SignalBeam, true, false),\n\t\t(PBESpecies.Abra, 0, PBEMove.Teleport, PBEMove.SkillSwap, PBEMove.Gravity, true, true),\n\t\t(PBESpecies.Ralts, 0, PBEMove.Confusion, PBEMove.DestinyBond, PBEMove.HelpingHand, true, false),\n\t\t(PBESpecies.Sableye, 0, PBEMove.Foresight, PBEMove.SuckerPunch, PBEMove.Spite, true, false),\n\t\t(PBESpecies.Spiritomb, 0, PBEMove.Spite, PBEMove.PainSplit, PBEMove.IcyWind, true, false),\n\t\t(PBESpecies.Duosion, 0, PBEMove.Recover, PBEMove.Imprison, PBEMove.Trick, false, true),\n\t\t(PBESpecies.Golett, 0, PBEMove.Rollout, PBEMove.FirePunch, PBEMove.ThunderPunch, false, true),\n\t\t#endregion\n\t\t#region Rugged Mountain\n\t\t(PBESpecies.Mankey, 0, PBEMove.LowKick, PBEMove.Reversal, PBEMove.ThunderPunch, true, false),\n\t\t(PBESpecies.Machop, 0, PBEMove.LowKick, PBEMove.BulletPunch, PBEMove.HelpingHand, true, true),\n\t\t(PBESpecies.Magnemite, 0, PBEMove.MetalSound, PBEMove.Recycle, PBEMove.Gravity, true, true),\n\t\t(PBESpecies.Koffing, 0, PBEMove.Smog, PBEMove.DarkPulse, PBEMove.SludgeWave, true, true),\n\t\t(PBESpecies.Rhyhorn, 0, PBEMove.HornAttack, PBEMove.Counter, PBEMove.DoubleEdge, true, false),\n\t\t(PBESpecies.Slugma, 0, PBEMove.Ember, PBEMove.Inferno, PBEMove.HeatWave, true, true),\n\t\t(PBESpecies.Phanpy, 0, PBEMove.Flail, PBEMove.HeavySlam, PBEMove.SeedBomb, true, false),\n\t\t(PBESpecies.Larvitar, 0, PBEMove.Bite, PBEMove.DarkPulse, PBEMove.StealthRock, true, true),\n\t\t(PBESpecies.Torkoal, 0, PBEMove.Ember, PBEMove.Fissure, PBEMove.StealthRock, true, true),\n\t\t(PBESpecies.Trapinch, 0, PBEMove.Bite, PBEMove.SignalBeam, PBEMove.GigaDrain, true, true),\n\t\t(PBESpecies.Cacnea, 0, PBEMove.Absorb, PBEMove.TeeterDance, PBEMove.ThunderPunch, true, true),\n\t\t(PBESpecies.Burmy, PBEForm.Burmy_Sandy, PBEMove.Protect, PBEMove.BugBite, PBEMove.Snore, true, true),\n\t\t(PBESpecies.Hippopotas, 0, PBEMove.Bite, PBEMove.Stockpile, PBEMove.Superpower, true, true),\n\t\t(PBESpecies.Skorupi, 0, PBEMove.Bite, PBEMove.Agility, PBEMove.AquaTail, true, false),\n\t\t(PBESpecies.Heatmor, 0, PBEMove.Incinerate, PBEMove.HeatWave, PBEMove.GigaDrain, false, true),\n\t\t(PBESpecies.Durant, 0, PBEMove.FuryCutter, PBEMove.Endure, PBEMove.ThunderFang, false, true),\n\t\t(PBESpecies.Maractus, 0, PBEMove.PinMissile, PBEMove.LeechSeed, PBEMove.Spikes, false, true),\n\t\t(PBESpecies.Crustle, 0, PBEMove.RockSlide, PBEMove.Counter, PBEMove.NightSlash, false, true),\n\t\t(PBESpecies.Magby, 0, PBEMove.Ember, PBEMove.ThunderPunch, PBEMove.HeatWave, true, true),\n\t\t(PBESpecies.Teddiursa, 0, PBEMove.FakeTears, PBEMove.Crunch, PBEMove.FocusPunch, true, false),\n\t\t(PBESpecies.Makuhita, 0, PBEMove.ArmThrust, PBEMove.HelpingHand, PBEMove.IcePunch, true, false),\n\t\t(PBESpecies.Numel, 0, PBEMove.Ember, PBEMove.BodySlam, PBEMove.HeatWave, true, true),\n\t\t(PBESpecies.Spinda, 0, PBEMove.Copycat, PBEMove.FakeOut, PBEMove.Superpower, true, false),\n\t\t(PBESpecies.Absol, 0, PBEMove.Feint, PBEMove.Megahorn, PBEMove.Superpower, true, true),\n\t\t(PBESpecies.Beldum, 0, PBEMove.TakeDown, PBEMove.ZenHeadbutt, PBEMove.IronHead, true, false),\n\t\t(PBESpecies.Croagunk, 0, PBEMove.PoisonSting, PBEMove.DrainPunch, PBEMove.GunkShot, true, true),\n\t\t(PBESpecies.Tyrogue, 0, PBEMove.FakeOut, PBEMove.Feint, PBEMove.MachPunch, true, true),\n\t\t(PBESpecies.Bagon, 0, PBEMove.Bite, PBEMove.DragonDance, PBEMove.Outrage, true, true),\n\t\t(PBESpecies.Krookodile, 0, PBEMove.Crunch, PBEMove.Counter, PBEMove.MeanLook, false, true),\n\t\t(PBESpecies.Riolu, 0, PBEMove.Endure, PBEMove.BulletPunch, PBEMove.FocusPunch, true, false),\n\t\t#endregion\n\t\t#region Icy Cave\n\t\t(PBESpecies.Sandshrew, 0, PBEMove.SandAttack, PBEMove.Counter, PBEMove.SuperFang, true, true),\n\t\t(PBESpecies.Geodude, 0, PBEMove.DefenseCurl, PBEMove.StealthRock, PBEMove.RockClimb, true, true),\n\t\t(PBESpecies.Onix, 0, PBEMove.Bind, PBEMove.StealthRock, PBEMove.RockClimb, true, true),\n\t\t(PBESpecies.Voltorb, 0, PBEMove.Charge, PBEMove.SignalBeam, PBEMove.NaturalGift, true, true),\n\t\t(PBESpecies.Cubone, 0, PBEMove.BoneClub, PBEMove.PerishSong, PBEMove.LowKick, true, true),\n\t\t(PBESpecies.Cleffa, 0, PBEMove.Encore, PBEMove.Aromatherapy, PBEMove.SleepTalk, true, false),\n\t\t(PBESpecies.Shuckle, 0, PBEMove.Encore, PBEMove.HelpingHand, PBEMove.ShellSmash, true, false),\n\t\t(PBESpecies.Whismur, 0, PBEMove.Uproar, PBEMove.Endeavor, PBEMove.ZenHeadbutt, true, true),\n\t\t(PBESpecies.Nosepass, 0, PBEMove.Tackle, PBEMove.StealthRock, PBEMove.AncientPower, true, false),\n\t\t(PBESpecies.Aron, 0, PBEMove.Harden, PBEMove.Endeavor, PBEMove.HeadSmash, true, true),\n\t\t(PBESpecies.Lunatone, 0, PBEMove.Confusion, PBEMove.EarthPower, PBEMove.Moonlight, true, true),\n\t\t(PBESpecies.Solrock, 0, PBEMove.Confusion, PBEMove.ZenHeadbutt, PBEMove.MorningSun, true, true),\n\t\t(PBESpecies.Baltoy, 0, PBEMove.RapidSpin, PBEMove.Gravity, PBEMove.ZenHeadbutt, true, true),\n\t\t(PBESpecies.Spheal, 0, PBEMove.PowderSnow, PBEMove.Fissure, PBEMove.AquaTail, true, false),\n\t\t(PBESpecies.Cranidos, 0, PBEMove.Headbutt, PBEMove.IronHead, PBEMove.FirePunch, true, false),\n\t\t(PBESpecies.Snover, 0, PBEMove.RazorLeaf, PBEMove.Avalanche, PBEMove.GigaDrain, true, true),\n\t\t(PBESpecies.Drilbur, 0, PBEMove.RapidSpin, PBEMove.MetalSound, PBEMove.RockClimb, false, true),\n\t\t(PBESpecies.Druddigon, 0, PBEMove.Bite, PBEMove.FireFang, PBEMove.SuckerPunch, false, true),\n\t\t(PBESpecies.Diglett, 0, PBEMove.SandAttack, PBEMove.BeatUp, PBEMove.StealthRock, true, true),\n\t\t(PBESpecies.Dunsparce, 0, PBEMove.DefenseCurl, PBEMove.MagicCoat, PBEMove.StealthRock, true, false),\n\t\t(PBESpecies.Boldore, 0, PBEMove.SmackDown, PBEMove.Curse, PBEMove.HeavySlam, false, true),\n\t\t(PBESpecies.Vanillish, 0, PBEMove.MirrorShot, PBEMove.IceShard, PBEMove.Imprison, false, true),\n\t\t(PBESpecies.Klang, 0, PBEMove.ChargeBeam, PBEMove.Gravity, PBEMove.MagnetRise, false, true),\n\t\t(PBESpecies.Sneasel, 0, PBEMove.Taunt, PBEMove.IcePunch, PBEMove.LowKick, true, true),\n\t\t(PBESpecies.Snorunt, 0, PBEMove.PowderSnow, PBEMove.WeatherBall, PBEMove.WaterPulse, true, true),\n\t\t(PBESpecies.Shieldon, 0, PBEMove.Protect, PBEMove.Counter, PBEMove.Fissure, true, false),\n\t\t(PBESpecies.Swinub, 0, PBEMove.OdorSleuth, PBEMove.AncientPower, PBEMove.IcicleSpear, true, true),\n\t\t(PBESpecies.Gible, 0, PBEMove.DragonRage, PBEMove.Outrage, PBEMove.Endure, true, true),\n\t\t(PBESpecies.Axew, 0, PBEMove.DragonRage, PBEMove.Counter, PBEMove.NightSlash, false, true),\n\t\t#endregion\n\t\t#region Dream Park\n\t\t(PBESpecies.Paras, 0, PBEMove.StunSpore, PBEMove.CrossPoison, PBEMove.Synthesis, true, true),\n\t\t(PBESpecies.Pineco, 0, PBEMove.Selfdestruct, PBEMove.ToxicSpikes, PBEMove.Gravity, true, true),\n\t\t(PBESpecies.Wurmple, 0, PBEMove.PoisonSting, PBEMove.BugBite, PBEMove.Snore, true, true),\n\t\t(PBESpecies.Seedot, 0, PBEMove.Growth, PBEMove.BulletSeed, PBEMove.FoulPlay, true, true),\n\t\t(PBESpecies.Slakoth, 0, PBEMove.Yawn, PBEMove.NightSlash, PBEMove.SuckerPunch, true, true),\n\t\t(PBESpecies.Nincada, 0, PBEMove.LeechLife, PBEMove.Endure, PBEMove.NightSlash, true, true),\n\t\t(PBESpecies.Plusle, 0, PBEMove.ThunderWave, PBEMove.Discharge, PBEMove.SignalBeam, true, true),\n\t\t(PBESpecies.Minun, 0, PBEMove.ThunderWave, PBEMove.Discharge, PBEMove.SignalBeam, true, true),\n\t\t(PBESpecies.Gulpin, 0, PBEMove.PoisonGas, PBEMove.AcidArmor, PBEMove.GigaDrain, true, true),\n\t\t(PBESpecies.Kecleon, 0, PBEMove.FaintAttack, PBEMove.SkillSwap, PBEMove.ReflectType, true, true),\n\t\t(PBESpecies.Kricketot, 0, PBEMove.StruggleBug, PBEMove.Endeavor, PBEMove.Uproar, true, true),\n\t\t(PBESpecies.Cherubi, 0, PBEMove.LeechSeed, PBEMove.HealPulse, PBEMove.BulletSeed, true, true),\n\t\t(PBESpecies.Carnivine, 0, PBEMove.Bite, PBEMove.RagePowder, PBEMove.GastroAcid, true, true),\n\t\t(PBESpecies.Audino, 0, PBEMove.HelpingHand, PBEMove.Encore, PBEMove.Yawn, false, true),\n\t\t(PBESpecies.Throh, 0, PBEMove.Bind, PBEMove.IcePunch, PBEMove.Superpower, false, true),\n\t\t(PBESpecies.Sawk, 0, PBEMove.RockSmash, PBEMove.ThunderPunch, PBEMove.DualChop, false, true),\n\t\t(PBESpecies.Scraggy, 0, PBEMove.LowKick, PBEMove.FakeOut, PBEMove.DrainPunch, false, true),\n\t\t(PBESpecies.Venonat, 0, PBEMove.Disable, PBEMove.BatonPass, PBEMove.SkillSwap, true, false),\n\t\t(PBESpecies.Grimer, 0, PBEMove.PoisonGas, PBEMove.Haze, PBEMove.ShadowSneak, true, false),\n\t\t(PBESpecies.Combee, 0, PBEMove.Gust, PBEMove.Tailwind, PBEMove.AirCutter, true, false),\n\t\t(PBESpecies.Beedrill, 0, PBEMove.FuryAttack, PBEMove.AirCutter, PBEMove.FuryCutter, true, false),\n\t\t(PBESpecies.Ekans, 0, PBEMove.PoisonSting, PBEMove.BeatUp, PBEMove.DarkPulse, true, true),\n\t\t(PBESpecies.Togepi, 0, PBEMove.Metronome, PBEMove.LuckyChant, PBEMove.Uproar, true, true),\n\t\t(PBESpecies.Aipom, 0, PBEMove.Scratch, PBEMove.FakeOut, PBEMove.FirePunch, true, true),\n\t\t(PBESpecies.Shroomish, 0, PBEMove.StunSpore, PBEMove.BulletSeed, PBEMove.FocusPunch, true, true),\n\t\t(PBESpecies.Gurdurr, 0, PBEMove.LowKick, PBEMove.MachPunch, PBEMove.DrainPunch, false, true),\n\t\t(PBESpecies.Roselia, 0, PBEMove.Growth, PBEMove.SleepPowder, PBEMove.Swift, true, true),\n\t\t(PBESpecies.Zangoose, 0, PBEMove.QuickAttack, PBEMove.DoubleHit, PBEMove.LowKick, true, false),\n\t\t(PBESpecies.Seviper, 0, PBEMove.Bite, PBEMove.BodySlam, PBEMove.AquaTail, true, false),\n\t\t(PBESpecies.Chansey, 0, PBEMove.Growl, PBEMove.Counter, PBEMove.HelpingHand, true, true),\n\t\t(PBESpecies.Pinsir, 0, PBEMove.ViceGrip, PBEMove.CloseCombat, PBEMove.MeFirst, true, true),\n\t\t(PBESpecies.Eevee, 0, PBEMove.SandAttack, PBEMove.Charm, PBEMove.Swift, true, true),\n\t\t(PBESpecies.Snorlax, 0, PBEMove.Amnesia, PBEMove.FirePunch, PBEMove.Recycle, true, true),\n\t\t(PBESpecies.Heracross, 0, PBEMove.HornAttack, PBEMove.Flail, PBEMove.FocusPunch, true, true),\n\t\t#endregion\n\t\t#region Pokémon Café Forest\n\t\t(PBESpecies.Poliwhirl, 0, PBEMove.RainDance, PBEMove.Haze, PBEMove.WaterPulse, true, true),\n\t\t(PBESpecies.Eevee, 0, PBEMove.HelpingHand, PBEMove.Charm, PBEMove.Swift, true, true),\n\t\t(PBESpecies.Smeargle, 0, PBEMove.Sketch, PBEMove.Captivate, PBEMove.SleepTalk, true, true),\n\t\t(PBESpecies.Burmy, PBEForm.Burmy_Plant, PBEMove.Protect, PBEMove.BugBite, PBEMove.Snore, true, true)\n\t\t#endregion\n\t};\n}\n"
  },
  {
    "path": "PokemonBattleEngineExtras/Program.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.DefaultData;\nusing Microsoft.Data.Sqlite;\nusing System;\n\nnamespace Kermalis.PokemonBattleEngineExtras;\n\ninternal sealed class Program\n{\n\tprivate enum Extra\n\t{\n\t\tAIBattle,\n\t\tLocalizationDumper,\n\t\tNARCTextDumper,\n\t\tPokemonDataDumper\n\t}\n\n\tpublic static void Main()\n\t{\n\t\tstatic SqliteConnection GetConnection()\n\t\t{\n\t\t\tSQLitePCL.Batteries_V2.Init();\n\t\t\tconst string databasePath = @\"PokemonBattleEngine.db\";\n\t\t\tvar con = new SqliteConnection($\"Filename={databasePath};\");\n\t\t\tcon.Open();\n\t\t\treturn con;\n\t\t}\n\n\t\tExtra e = Extra.AIBattle;\n\t\tswitch (e)\n\t\t{\n\t\t\tcase Extra.AIBattle:\n\t\t\t{\n\t\t\t\tPBEDefaultDataProvider.InitEngine(string.Empty);\n\t\t\t\t_ = new AIBattleDemo();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase Extra.LocalizationDumper:\n\t\t\t{\n\t\t\t\tusing (SqliteConnection con = GetConnection())\n\t\t\t\t{\n\t\t\t\t\tLocalizationDumper.Run(con);\n\t\t\t\t\tcon.Close();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase Extra.NARCTextDumper: NARCTextDumper.Dump(); break;\n\t\t\tcase Extra.PokemonDataDumper:\n\t\t\t{\n\t\t\t\tusing (SqliteConnection con = GetConnection())\n\t\t\t\t{\n\t\t\t\t\tPokemonDataDumper.Run(con);\n\t\t\t\t\tcon.Close();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: throw new Exception(nameof(e));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineExtras/Utils.cs",
    "content": "﻿using Kermalis.EndianBinaryIO;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.SimpleNARC;\nusing System.IO;\n\nnamespace Kermalis.PokemonBattleEngineExtras;\n\ninternal static class Utils\n{\n\tpublic static PBEType[] Gen5Types { get; } = new PBEType[17]\n\t{\n\t\tPBEType.Normal,\n\t\tPBEType.Fighting,\n\t\tPBEType.Flying,\n\t\tPBEType.Poison,\n\t\tPBEType.Ground,\n\t\tPBEType.Rock,\n\t\tPBEType.Bug,\n\t\tPBEType.Ghost,\n\t\tPBEType.Steel,\n\t\tPBEType.Fire,\n\t\tPBEType.Water,\n\t\tPBEType.Grass,\n\t\tPBEType.Electric,\n\t\tPBEType.Psychic,\n\t\tPBEType.Ice,\n\t\tPBEType.Dragon,\n\t\tPBEType.Dark\n\t};\n\n\tpublic static string[][] ReadTextFile(NARC narc, int fileNum)\n\t{\n\t\tusing (var ms = new MemoryStream(narc[fileNum]))\n\t\t{\n\t\t\tvar r = new EndianBinaryReader(ms);\n\t\t\tushort numBlocks = r.ReadUInt16();\n\t\t\tushort numEntries = r.ReadUInt16();\n\t\t\tr.ReadUInt32(); // fileSize\n\t\t\tr.ReadUInt32(); // padding\n\t\t\tstring[][] texts = new string[numBlocks][];\n\t\t\tuint[] blockOffsets = new uint[numBlocks];\n\t\t\tfor (int i = 0; i < numBlocks; i++)\n\t\t\t{\n\t\t\t\ttexts[i] = new string[numEntries];\n\t\t\t\tblockOffsets[i] = r.ReadUInt32();\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < numBlocks; i++)\n\t\t\t{\n\t\t\t\tms.Position = blockOffsets[i];\n\t\t\t\tr.ReadUInt32(); // blockSize\n\t\t\t\tuint[] stringOffsets = new uint[numEntries];\n\t\t\t\tushort[] stringLengths = new ushort[numEntries];\n\t\t\t\tfor (int j = 0; j < numEntries; j++)\n\t\t\t\t{\n\t\t\t\t\tstringOffsets[j] = r.ReadUInt32();\n\t\t\t\t\tstringLengths[j] = r.ReadUInt16();\n\t\t\t\t\tr.ReadUInt16(); // textFlags[j]\n\t\t\t\t}\n\t\t\t\tfor (int j = 0; j < numEntries; j++)\n\t\t\t\t{\n\t\t\t\t\tms.Position = blockOffsets[i] + stringOffsets[j];\n\t\t\t\t\tushort len = stringLengths[j];\n\t\t\t\t\tushort[] encoded = new ushort[len];\n\t\t\t\t\tfor (int k = 0; k < len; k++)\n\t\t\t\t\t{\n\t\t\t\t\t\tencoded[k] = r.ReadUInt16();\n\t\t\t\t\t}\n\t\t\t\t\tint key = encoded[len - 1] ^ 0xFFFF;\n\t\t\t\t\tint[] decoded = new int[len];\n\t\t\t\t\tfor (int k = len - 1; k >= 0; k--)\n\t\t\t\t\t{\n\t\t\t\t\t\tdecoded[k] = encoded[k] ^ key;\n\t\t\t\t\t\tkey = ((key >> 3) | (key << 13)) & 0xFFFF;\n\t\t\t\t\t}\n\t\t\t\t\tstring text = string.Empty; // Prevent null entries\n\t\t\t\t\tfor (int k = 0; k < len; k++)\n\t\t\t\t\t{\n\t\t\t\t\t\tint c = decoded[k];\n\t\t\t\t\t\tif (c == 0xFFFF)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tstring car;\n\t\t\t\t\t\t\tswitch (c)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tcase '\"': car = \"”\"; break;\n\t\t\t\t\t\t\t\tcase 0x246D: car = \"♂\"; break;\n\t\t\t\t\t\t\t\tcase 0x246E: car = \"♀\"; break;\n\t\t\t\t\t\t\t\tcase 0x2486: car = \"[PK]\"; break;\n\t\t\t\t\t\t\t\tcase 0x2487: car = \"[MN]\"; break;\n\t\t\t\t\t\t\t\tcase 0xFFFE: car = \"\\n\"; break;\n\t\t\t\t\t\t\t\tdefault: car = ((char)c).ToString(); break;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\ttext += car;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\ttexts[i][j] = text;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn texts;\n\t\t}\n\t}\n\n\tpublic static byte ReadByte(this EndianBinaryReader r, int ofs)\n\t{\n\t\tr.Stream.Position = ofs;\n\t\treturn r.ReadByte();\n\t}\n\tpublic static ushort ReadUInt16(this EndianBinaryReader r, int ofs)\n\t{\n\t\tr.Stream.Position = ofs;\n\t\treturn r.ReadUInt16();\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineServer/BattleServer.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.DefaultData;\nusing Kermalis.PokemonBattleEngine.Network;\nusing Kermalis.PokemonBattleEngine.Packets;\nusing Kermalis.PokemonBattleEngine.Utils;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Net;\nusing System.Threading;\n\nnamespace Kermalis.PokemonBattleEngineServer;\n\ninternal sealed class BattleServer\n{\n\t// TODO: Events still sent after someone disconnects during a turn, need to return out of event subscription\n\t// Server does not support wild battles\n\t// Server does not support gaining EXP or levels\n\tprivate enum ServerState\n\t{\n\t\tResetting,           // Server is currently resetting the game\n\t\tWaitingForPlayers,   // Server is waiting for players to connect\n\t\tWaitingForActions,   // Server is waiting for players to select actions\n\t\tWaitingForSwitchIns, // Server is waiting for players to switch in new Pokémon\n\t\tBattleProcessing,    // Battle is running and sending events\n\t\tBattleEnded          // Battle ended\n\t}\n\tpublic readonly bool RequireLegalParties;\n\tprivate readonly PBEServer _server;\n\tprivate ServerState _state = ServerState.Resetting;\n\tprivate readonly ManualResetEvent _resetEvent = new(true);\n\tprivate PBEBattle _battle;\n\tpublic readonly PBESettings Settings = PBESettings.DefaultSettings;\n\tprivate const PBEBattleFormat BattleFormat = PBEBattleFormat.Double;\n\tprivate const int NumTrainersPerTeam = 1; // Must be changed if BattleFormat is changed\n\tprivate const int NumBattlers = 2 * NumTrainersPerTeam;\n\tprivate Player[] _battlers;\n\tprivate readonly List<IPBEPacket> _spectatorPackets = new();\n\tprivate readonly Dictionary<PBEServerClient, Player> _readyPlayers = new();\n\n\tprivate byte _battlerCounter = 0;\n\tprivate PBETrainerInfo[][] _incomingTrainers;\n\n\tpublic static void Main(string[] args)\n\t{\n\t\tif (args.Length != 3\n\t\t\t|| !IPAddress.TryParse(args[0], out IPAddress? ip)\n\t\t\t|| !ushort.TryParse(args[1], out ushort port)\n\t\t\t|| !bool.TryParse(args[2], out bool requireLegalParties))\n\t\t{\n\t\t\tConsole.WriteLine(\"Usage:\\tPokemonBattleEngineServer {ip} {port} {requireLegalParties}\");\n\t\t\tConsole.WriteLine(\"Example:\\tPokemonBattleEngineServer 127.0.0.1 8888 true\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\t_ = new BattleServer(ip, port, requireLegalParties);\n\t\t}\n\t}\n\tprivate BattleServer(IPAddress ip, ushort port, bool requireLegalParties)\n\t{\n\t\t// These are set in their proper server states\n\t\t_battle = null!;\n\t\t_battlers = null!;\n\t\t_incomingTrainers = null!; // in Reset()\n\t\tusing (_server = new PBEServer())\n\t\t{\n\t\t\t_server.ClientConnected += OnClientConnected;\n\t\t\t_server.ClientDisconnected += OnClientDisconnected;\n\t\t\t_server.ClientRefused += OnClientRefused;\n\t\t\t_server.Error += OnError; // Events unsubscribe in _server.Dispose()\n\t\t\t_server.Start(new IPEndPoint(ip, port), 100, new PBEPacketProcessor());\n\t\t\tRequireLegalParties = requireLegalParties;\n\t\t\tPBEDefaultDataProvider.InitEngine(string.Empty);\n\t\t\tConsole.WriteLine(\"Server online.\");\n\t\t\tReset();\n\t\t\tThread.Sleep(-1);\n\t\t}\n\t}\n\tprivate static readonly string[] _tempNames = new string[] { \"Sasha\", \"Nikki\", \"Lara\", \"Violet\", \"Naomi\", \"Rose\", \"Sabrina\", \"Nicole\" };\n\tprivate void OnClientConnected(object? sender, PBEServerClient client)\n\t{\n\t\t// Need to spawn a new thread so \"WaitOne()\" doesn't block the thread that receives client packets\n\t\tnew Thread(() =>\n\t\t{\n\t\t\tlock (this)\n\t\t\t{\n\t\t\t\t// Wait for the server to be in a state where no events will be sent\n\t\t\t\t_resetEvent.WaitOne();\n\n\t\t\t\tstring name = PBEDataProvider.GlobalRandom.RandomElement(_tempNames);\n\t\t\t\tif (_battlerCounter < NumBattlers)\n\t\t\t\t{\n\t\t\t\t\tConnectBattler(client, name);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tConnectSpectator(client, name);\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t\t{\n\t\t\tName = \"Client Connected Thread\"\n\t\t}.Start();\n\t}\n\tprivate void OnClientDisconnected(object? sender, PBEServerClient client)\n\t{\n\t\t// Need to spawn a new thread so \"WaitOne()\" doesn't block the thread that receives client packets\n\t\tnew Thread(() =>\n\t\t{\n\t\t\tlock (this)\n\t\t\t{\n\t\t\t\t// Wait for the server to be in a state where no events will be sent\n\t\t\t\t_resetEvent.WaitOne();\n\n\t\t\t\tif (_readyPlayers.TryGetValue(client, out Player? player))\n\t\t\t\t{\n\t\t\t\t\t_readyPlayers.Remove(client);\n\t\t\t\t\tplayer.Dispose();\n\n\t\t\t\t\tConsole.WriteLine($\"Client disconnected ({player.BattleId} {player.TrainerName})\");\n\t\t\t\t\tif (player.BattleId != byte.MaxValue)\n\t\t\t\t\t{\n\t\t\t\t\t\tCancelMatch();\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t// Temporarily ignore spectators\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t\t{\n\t\t\tName = \"Client Disconnected Thread\"\n\t\t}.Start();\n\t}\n\tprivate void OnClientRefused(object? sender, IPEndPoint clientIP, bool refusedForBan)\n\t{\n\t\tConsole.WriteLine($\"Client refused ({clientIP} {(refusedForBan ? \"banned\" : \"no more room\")})\");\n\t}\n\tprivate void OnError(object? sender, Exception ex)\n\t{\n\t\tConsole.WriteLine($\"Server error:{Environment.NewLine}{ex}\");\n\t}\n\n\tprivate void ConnectBattler(PBEServerClient client, string name)\n\t{\n\t\tbyte id = _battlerCounter;\n\t\tConsole.WriteLine($\"Client connected ({client.IP} {id} {name})\");\n\t\tvar newPlayer = new Player(this, client, id, name);\n\t\tIPBEPokemonCollection? party = newPlayer.AskForParty(RequireLegalParties);\n\t\tif (party is null)\n\t\t{\n\t\t\tnewPlayer.Dispose(); // No response, so disconnect them\n\t\t\treturn;\n\t\t}\n\t\t_incomingTrainers[id / NumTrainersPerTeam][id % NumTrainersPerTeam] = new PBETrainerInfo(party, name, false);\n\t\t_battlers[id] = newPlayer;\n\t\t_readyPlayers.Add(client, newPlayer);\n\n\t\t// Start battle\n\t\tif (++_battlerCounter == NumBattlers)\n\t\t{\n\t\t\tConsole.WriteLine(\"All players connected!\");\n\t\t\t_battle = PBEBattle.CreateTrainerBattle(BattleFormat, Settings, _incomingTrainers[0], _incomingTrainers[1]);\n\t\t\t_incomingTrainers = null!; // _incomingTrainers is created again in Reset()\n\t\t\t_battle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\t\t\t_battle.OnNewEvent += BattleEventHandler;\n\t\t\t_battle.OnStateChanged += BattleStateHandler;\n\t\t\t_server.Battle = _battle;\n\t\t\tBattleStateHandler(_battle); // Call RunTurn, which sends battle packet\n\t\t}\n\t}\n\tprivate void ConnectSpectator(PBEServerClient client, string name)\n\t{\n\t\tconst byte spectatorId = byte.MaxValue;\n\t\tConsole.WriteLine($\"Client connected ({client.IP} {spectatorId} {name})\");\n\t\tvar newPlayer = new Player(this, client, spectatorId, name);\n\t\tvar chakoPackay = new PBEPlayerJoinedPacket(name);\n\t\tforeach (Player p in _readyPlayers.Values.ToArray())\n\t\t{\n\t\t\tp.Send(chakoPackay);\n\t\t\tif (!p.WaitForResponse(typeof(PBEResponsePacket)) && p.BattleId != spectatorId)\n\t\t\t{\n\t\t\t\tnewPlayer.Dispose();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t_spectatorPackets.Add(chakoPackay);\n\t\tforeach (IPBEPacket packet in _spectatorPackets)\n\t\t{\n\t\t\tnewPlayer.Send(packet);\n\t\t\tif (!newPlayer.WaitForResponse(typeof(PBEResponsePacket)))\n\t\t\t{\n\t\t\t\tnewPlayer.Dispose();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t_readyPlayers.Add(client, newPlayer);\n\t}\n\n\tprivate void CancelMatch()\n\t{\n\t\tif (_state == ServerState.Resetting)\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\tlock (this)\n\t\t{\n\t\t\tif (_state == ServerState.Resetting)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t_state = ServerState.Resetting;\n\t\t\tConsole.WriteLine(\"Cancelling match...\");\n\t\t\tSendToAll(new PBEMatchCancelledPacket());\n\t\t\tReset();\n\t\t}\n\t}\n\tprivate void Reset()\n\t{\n\t\tConsole.WriteLine(\"Resetting...\");\n\t\t_resetEvent.Reset();\n\t\t_state = ServerState.Resetting;\n\t\tforeach (Player c in _readyPlayers.Values.ToArray())\n\t\t{\n\t\t\tDisconnectClient(c);\n\t\t}\n\t\t_battlerCounter = 0;\n\t\tif (_battle is not null)\n\t\t{\n\t\t\t_battle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t\t_battle.OnNewEvent -= BattleEventHandler;\n\t\t\t_battle.OnStateChanged -= BattleStateHandler;\n\t\t}\n\t\t_battlers = new Player[NumBattlers];\n\t\t_incomingTrainers = new PBETrainerInfo[2][];\n\t\tfor (int i = 0; i < 2; i++)\n\t\t{\n\t\t\t_incomingTrainers[i] = new PBETrainerInfo[NumTrainersPerTeam];\n\t\t}\n\t\t_spectatorPackets.Clear();\n\t\t_state = ServerState.WaitingForPlayers;\n\t\t_resetEvent.Set();\n\t\tConsole.WriteLine(\"Reset complete.\");\n\t}\n\tpublic void ActionsSubmitted(Player player, IReadOnlyList<PBETurnAction> actions)\n\t{\n\t\tif (_state != ServerState.WaitingForActions)\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\tlock (this)\n\t\t{\n\t\t\tif (_state != ServerState.WaitingForActions)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tConsole.WriteLine($\"Received actions ({player.BattleId} {player.TrainerName})\");\n\t\t\tif (!_battle.Trainers[player.BattleId].SelectActionsIfValid(actions, out string? valid))\n\t\t\t{\n\t\t\t\tConsole.WriteLine(\"Actions are invalid! - {0}\", valid);\n\t\t\t\tCancelMatch();\n\t\t\t}\n\t\t}\n\t}\n\tpublic void FleeSubmitted(Player player)\n\t{\n\t\tif (_state != ServerState.WaitingForActions && _state != ServerState.WaitingForSwitchIns)\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\tlock (this)\n\t\t{\n\t\t\tif (_state != ServerState.WaitingForActions && _state != ServerState.WaitingForSwitchIns)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tConsole.WriteLine($\"Received flee request ({player.BattleId} {player.TrainerName})\");\n\t\t\tif (!_battle.Trainers[player.BattleId].SelectFleeIfValid(out string? valid))\n\t\t\t{\n\t\t\t\tConsole.WriteLine(\"Flee is invalid! - {0}\", valid);\n\t\t\t\tCancelMatch();\n\t\t\t}\n\t\t}\n\t}\n\tpublic void SwitchesSubmitted(Player player, IReadOnlyList<PBESwitchIn> switches)\n\t{\n\t\tif (_state != ServerState.WaitingForSwitchIns)\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\tlock (this)\n\t\t{\n\t\t\tif (_state != ServerState.WaitingForSwitchIns)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tConsole.WriteLine($\"Received switches ({player.BattleId} {player.TrainerName})\");\n\t\t\tif (!_battle.Trainers[player.BattleId].SelectSwitchesIfValid(switches, out string? valid))\n\t\t\t{\n\t\t\t\tConsole.WriteLine(\"Switches are invalid! - {0}\", valid);\n\t\t\t\tCancelMatch();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void BattleStateHandler(PBEBattle battle)\n\t{\n\t\tConsole.WriteLine(\"Battle state changed: {0}\", battle.BattleState);\n\t\tswitch (battle.BattleState)\n\t\t{\n\t\t\tcase PBEBattleState.Ended:\n\t\t\t{\n\t\t\t\t_battle.SaveReplay(\"Server Battle.pbereplay\");\n\t\t\t\t_resetEvent.Set();\n\t\t\t\t_state = ServerState.BattleEnded;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEBattleState.Processing:\n\t\t\t{\n\t\t\t\t_resetEvent.Reset();\n\t\t\t\t_state = ServerState.BattleProcessing;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEBattleState.ReadyToBegin:\n\t\t\t{\n\t\t\t\t_resetEvent.Reset();\n\t\t\t\tConsole.WriteLine(\"Battle starting!\");\n\t\t\t\tnew Thread(battle.Begin) { Name = \"Battle Thread\" }.Start();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEBattleState.ReadyToRunSwitches:\n\t\t\t{\n\t\t\t\tnew Thread(battle.RunSwitches) { Name = \"Battle Thread\" }.Start();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEBattleState.ReadyToRunTurn:\n\t\t\t{\n\t\t\t\tnew Thread(battle.RunTurn) { Name = \"Battle Thread\" }.Start();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tprivate void BattleEventHandler(PBEBattle battle, IPBEPacket packet)\n\t{\n\t\tvoid SendOriginalPacketToTeamOwnerAndEveryoneElseGetsAPacketWithHiddenInfo(IPBEPacket realPacket, IPBEPacket hiddenInfo, byte trainerId)\n\t\t{\n\t\t\tPlayer trainer = _battlers[trainerId];\n\t\t\ttrainer.Send(realPacket);\n\t\t\tif (!trainer.WaitForResponse(typeof(PBEResponsePacket)))\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tforeach (Player p in _readyPlayers.Values.ExceptOne(trainer))\n\t\t\t{\n\t\t\t\tp.Send(hiddenInfo);\n\t\t\t\tif (!p.WaitForResponse(typeof(PBEResponsePacket)) && p.BattleId != byte.MaxValue)\n\t\t\t\t{\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\t_spectatorPackets.Add(hiddenInfo);\n\t\t}\n\n\t\tswitch (packet)\n\t\t{\n\t\t\tcase PBEMoveLockPacket mlp:\n\t\t\t{\n\t\t\t\tPlayer p = _battlers[mlp.MoveUserTrainer.Id];\n\t\t\t\tp.Send(mlp);\n\t\t\t\tif (!p.WaitForResponse(typeof(PBEResponsePacket)))\n\t\t\t\t{\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEPkmnFaintedPacket pfp:\n\t\t\t{\n\t\t\t\tSendOriginalPacketToTeamOwnerAndEveryoneElseGetsAPacketWithHiddenInfo(pfp, new PBEPkmnFaintedPacket_Hidden(pfp), pfp.PokemonTrainer.Id);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEPkmnFormChangedPacket pfcp:\n\t\t\t{\n\t\t\t\tSendOriginalPacketToTeamOwnerAndEveryoneElseGetsAPacketWithHiddenInfo(pfcp, new PBEPkmnFormChangedPacket_Hidden(pfcp), pfcp.PokemonTrainer.Id);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEPkmnHPChangedPacket phcp:\n\t\t\t{\n\t\t\t\tSendOriginalPacketToTeamOwnerAndEveryoneElseGetsAPacketWithHiddenInfo(phcp, new PBEPkmnHPChangedPacket_Hidden(phcp), phcp.PokemonTrainer.Id);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEPkmnSwitchInPacket psip:\n\t\t\t{\n\t\t\t\tSendOriginalPacketToTeamOwnerAndEveryoneElseGetsAPacketWithHiddenInfo(psip, new PBEPkmnSwitchInPacket_Hidden(psip), psip.Trainer.Id);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEPkmnSwitchOutPacket psop:\n\t\t\t{\n\t\t\t\tSendOriginalPacketToTeamOwnerAndEveryoneElseGetsAPacketWithHiddenInfo(psop, new PBEPkmnSwitchOutPacket_Hidden(psop), psop.PokemonTrainer.Id);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t/*case PBEWildPkmnAppearedPacket wpap:\n\t\t\t{\n\t\t\t\tSendOriginalPacketToTeamOwnerAndEveryoneElseGetsAPacketWithHiddenInfo(wpap, new PBEWildPkmnAppearedPacket_Hidden(wpap), wpap.Trainer.Id);\n\t\t\t\tbreak;\n\t\t\t}*/\n\t\t\tcase PBEReflectTypePacket rtp:\n\t\t\t{\n\t\t\t\tvar hidden = new PBEReflectTypePacket_Hidden(rtp);\n\t\t\t\tforeach (Player p in _battlers)\n\t\t\t\t{\n\t\t\t\t\tp.Send(rtp.UserTrainer.Id == p.BattleId || rtp.TargetTrainer.Id == p.BattleId ? rtp : hidden);\n\t\t\t\t\tif (!p.WaitForResponse(typeof(PBEResponsePacket)))\n\t\t\t\t\t{\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tforeach (Player player in _readyPlayers.Values.Except(_battlers))\n\t\t\t\t{\n\t\t\t\t\tplayer.Send(hidden);\n\t\t\t\t\tplayer.WaitForResponse(typeof(PBEResponsePacket));\n\t\t\t\t}\n\t\t\t\t_spectatorPackets.Add(hidden);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBETransformPacket tp:\n\t\t\t{\n\t\t\t\tforeach (Player p in _battlers)\n\t\t\t\t{\n\t\t\t\t\tif (tp.UserTrainer.Id == p.BattleId || tp.TargetTrainer.Id == p.BattleId)\n\t\t\t\t\t{\n\t\t\t\t\t\tp.Send(tp);\n\t\t\t\t\t\tif (!p.WaitForResponse(typeof(PBEResponsePacket)))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEActionsRequestPacket arp:\n\t\t\t{\n\t\t\t\t_state = ServerState.WaitingForActions;\n\t\t\t\tforeach (Player player in _readyPlayers.Values.ToArray())\n\t\t\t\t{\n\t\t\t\t\tplayer.Send(packet);\n\t\t\t\t\tif (!player.WaitForResponse(typeof(PBEResponsePacket)) && player.BattleId != byte.MaxValue)\n\t\t\t\t\t{\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t_battlers[arp.Trainer.Id].SetWaitingForActions(typeof(PBEActionsResponsePacket));\n\t\t\t\t_spectatorPackets.Add(packet);\n\t\t\t\t_resetEvent.Set();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEAutoCenterPacket acp:\n\t\t\t{\n\t\t\t\tvar team0 = new PBEAutoCenterPacket_Hidden0(acp);\n\t\t\t\tvar team1 = new PBEAutoCenterPacket_Hidden1(acp);\n\t\t\t\tvar spectators = new PBEAutoCenterPacket_Hidden01(acp);\n\t\t\t\tforeach (Player p in _readyPlayers.Values.ToArray())\n\t\t\t\t{\n\t\t\t\t\tIPBEAutoCenterPacket chakoPackay = p.BattleId == byte.MaxValue\n\t\t\t\t\t\t\t? spectators\n\t\t\t\t\t\t\t: acp.Pokemon0Trainer.Id == p.BattleId ? team0 : acp.Pokemon1Trainer.Id == p.BattleId ? team1: spectators;\n\t\t\t\t\tp.Send(chakoPackay);\n\t\t\t\t\tif (!p.WaitForResponse(typeof(PBEResponsePacket)) && p.BattleId != byte.MaxValue)\n\t\t\t\t\t{\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t_spectatorPackets.Add(spectators);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBEBattlePacket bp:\n\t\t\t{\n\t\t\t\tforeach (Player p in _battlers)\n\t\t\t\t{\n\t\t\t\t\tp.Send(new PBEBattlePacket(bp, p.BattleId));\n\t\t\t\t\tif (!p.WaitForResponse(typeof(PBEResponsePacket)))\n\t\t\t\t\t{\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tvar spectators = new PBEBattlePacket(bp, null);\n\t\t\t\tforeach (Player player in _readyPlayers.Values.Except(_battlers))\n\t\t\t\t{\n\t\t\t\t\tplayer.Send(spectators);\n\t\t\t\t\tplayer.WaitForResponse(typeof(PBEResponsePacket));\n\t\t\t\t}\n\t\t\t\t_spectatorPackets.Add(spectators);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PBESwitchInRequestPacket sirp:\n\t\t\t{\n\t\t\t\t_state = ServerState.WaitingForSwitchIns;\n\t\t\t\tforeach (Player player in _readyPlayers.Values.ToArray())\n\t\t\t\t{\n\t\t\t\t\tplayer.Send(packet);\n\t\t\t\t\tif (!player.WaitForResponse(typeof(PBEResponsePacket)) && player.BattleId != byte.MaxValue)\n\t\t\t\t\t{\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t_battlers[sirp.Trainer.Id].SetWaitingForActions(typeof(PBESwitchInResponsePacket));\n\t\t\t\t_spectatorPackets.Add(packet);\n\t\t\t\t_resetEvent.Set();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\tforeach (Player player in _readyPlayers.Values.ToArray())\n\t\t\t\t{\n\t\t\t\t\tplayer.Send(packet);\n\t\t\t\t\tif (!player.WaitForResponse(typeof(PBEResponsePacket)) && player.BattleId != byte.MaxValue)\n\t\t\t\t\t{\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t_spectatorPackets.Add(packet);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void DisconnectClient(Player player)\n\t{\n\t\tConsole.WriteLine($\"Disconnecting client ({player.BattleId} {player.TrainerName})\");\n\t\t_server.DisconnectClient(player.Client);\n\t}\n\tprivate void SendToAll(IPBEPacket packet)\n\t{\n\t\tforeach (Player p in _readyPlayers.Values.ToArray())\n\t\t{\n\t\t\tp.Send(packet);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineServer/Player.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Network;\nusing Kermalis.PokemonBattleEngine.Packets;\nusing System;\nusing System.Diagnostics;\nusing System.Threading;\n\nnamespace Kermalis.PokemonBattleEngineServer;\n\ninternal sealed class Player : IDisposable\n{\n\tpublic byte BattleId { get; }\n\tpublic string TrainerName { get; }\n\tpublic BattleServer Server { get; }\n\tpublic PBEServerClient Client { get; }\n\n\tprivate Type? _packetType;\n\tprivate Type? _actionType;\n\tprivate readonly ManualResetEvent _resetEvent = new(true);\n\n\tpublic Player(BattleServer server, PBEServerClient client, byte battleId, string name)\n\t{\n\t\tBattleId = battleId;\n\t\tTrainerName = name;\n\t\tServer = server;\n\t\tClient = client;\n\t\tClient.PacketReceived += OnPacketReceived;\n\t}\n\n\tpublic bool WaitForResponse(Type packetType)\n\t{\n\t\t_packetType = packetType;\n\t\tbool receivedResponseInTime = _resetEvent.WaitOne(1000 * 5);\n\t\tif (!receivedResponseInTime)\n\t\t{\n\t\t\tServer.DisconnectClient(this);\n\t\t}\n\t\treturn receivedResponseInTime;\n\t}\n\tpublic void SetWaitingForActions(Type packetType)\n\t{\n\t\t_actionType = packetType;\n\t}\n\tpublic void Send(IPBEPacket packet)\n\t{\n\t\tif (Client.IsConnected)\n\t\t{\n\t\t\tDebug.WriteLine($\"Packet sent ({BattleId} {TrainerName} \\\"{packet.GetType().Name}\\\")\");\n\t\t\t_resetEvent.Reset();\n\t\t\tClient.Send(packet);\n\t\t}\n\t}\n\tprivate IPBEPokemonCollection? _party;\n\tpublic IPBEPokemonCollection? AskForParty(bool requireLegal)\n\t{\n\t\tSend(new PBEPartyRequestPacket(BattleId, requireLegal));\n\t\tWaitForResponse(requireLegal ? typeof(PBELegalPartyResponsePacket) : typeof(PBEPartyResponsePacket));\n\t\tIPBEPokemonCollection? ret = _party;\n\t\t_party = null;\n\t\treturn ret;\n\t}\n\n\tprivate void OnPacketReceived(object? sender, IPBEPacket packet)\n\t{\n\t\t// TODO: Kick players who are sending broken packets or sending too many\n\t\tType type = packet.GetType();\n\t\tDebug.WriteLine($\"Packet received ({BattleId} {TrainerName} \\\"{type.Name}\\\")\");\n\t\tif (_packetType is not null && type.Equals(_packetType))\n\t\t{\n\t\t\t_packetType = null;\n\t\t\tswitch (packet)\n\t\t\t{\n\t\t\t\tcase PBELegalPartyResponsePacket lprp:\n\t\t\t\t{\n\t\t\t\t\tConsole.WriteLine($\"Received party ({BattleId} {TrainerName})\");\n\t\t\t\t\tif (!Server.Settings.Equals(lprp.Party.Settings))\n\t\t\t\t\t{\n\t\t\t\t\t\tConsole.WriteLine(\"Party does not have matching settings!\");\n\t\t\t\t\t\tConsole.WriteLine(\"\\tServer: \\\"{0}\\\"\", Server.Settings);\n\t\t\t\t\t\tConsole.WriteLine(\"\\tParty: \\\"{0}\\\"\", lprp.Party.Settings);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t_party = lprp.Party;\n\t\t\t\t\t\t_resetEvent.Set();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase PBEPartyResponsePacket prp:\n\t\t\t\t{\n\t\t\t\t\tConsole.WriteLine($\"Received party ({BattleId} {TrainerName})\");\n\t\t\t\t\t_party = prp.Party;\n\t\t\t\t\t_resetEvent.Set();\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tdefault: _resetEvent.Set(); break;\n\t\t\t}\n\t\t}\n\t\telse if (_actionType is not null && (type.Equals(_actionType) || type.Equals(typeof(PBEFleeResponsePacket))))\n\t\t{\n\t\t\t_actionType = null;\n\t\t\tswitch (packet)\n\t\t\t{\n\t\t\t\tcase PBEActionsResponsePacket arp: Server.ActionsSubmitted(this, arp.Actions); break;\n\t\t\t\tcase PBEFleeResponsePacket _: Server.FleeSubmitted(this); break;\n\t\t\t\tcase PBESwitchInResponsePacket sirp: Server.SwitchesSubmitted(this, sirp.Switches); break;\n\t\t\t\tdefault: throw new ArgumentOutOfRangeException(nameof(packet));\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void Dispose()\n\t{\n\t\t_resetEvent.Dispose();\n\t\tClient.PacketReceived -= OnPacketReceived;\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineServer/PokemonBattleEngineServer.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n\t<PropertyGroup>\n\t\t<TargetFramework>net7.0</TargetFramework>\n\t\t<OutputType>Exe</OutputType>\n\t\t<RootNamespace>Kermalis.PokemonBattleEngineServer</RootNamespace>\n\t\t<Authors>Kermalis</Authors>\n\t\t<Copyright>Kermalis</Copyright>\n\t\t<RepositoryUrl>https://github.com/Kermalis/PokemonBattleEngine</RepositoryUrl>\n\t\t<NoWarn />\n\t\t<StartupObject>Kermalis.PokemonBattleEngineServer.BattleServer</StartupObject>\n\t\t<Nullable>enable</Nullable>\n\t</PropertyGroup>\n\n\t<ItemGroup>\n\t\t<ProjectReference Include=\"..\\PokemonBattleEngine.DefaultData\\PokemonBattleEngine.DefaultData.csproj\" />\n\t</ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "PokemonBattleEngineServer/Properties/launchSettings.json",
    "content": "{\n  \"profiles\": {\n    \"PokemonBattleEngineServer\": {\n      \"commandName\": \"Project\",\n      \"commandLineArgs\": \"127.0.0.1 8888\"\n    }\n  }\n}"
  },
  {
    "path": "PokemonBattleEngineTests/Abilities/AntiStatusAbilityTests.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace Kermalis.PokemonBattleEngineTests.Abilities;\n\n[Collection(\"Utils\")]\npublic class AntiStatusAbilityTests\n{\n\tpublic AntiStatusAbilityTests(TestUtils _, ITestOutputHelper output)\n\t{\n\t\tTestUtils.SetOutputHelper(output);\n\t}\n\n\t[Fact]\n\tpublic void Immunity_Works()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0; // Seed prevents Toxic from missing\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Seviper, 0, 100, PBEMove.Toxic);\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Zangoose, 0, 100, PBEMove.Splash)\n\t\t{\n\t\t\tAbility = PBEAbility.Immunity\n\t\t};\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon seviper = t0.Party[0];\n\t\tPBEBattlePokemon zangoose = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Badly Poison Zangoose and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(seviper, PBEMove.Toxic, PBETurnTarget.FoeCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(zangoose, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(battle.VerifyMoveResultHappened(seviper, zangoose, PBEResult.Ineffective_Ability));\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineTests/Abilities/IllusionTests.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Packets;\nusing System.Linq;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace Kermalis.PokemonBattleEngineTests.Abilities;\n\n[Collection(\"Utils\")]\npublic class IllusionTests\n{\n\tpublic IllusionTests(TestUtils _, ITestOutputHelper output)\n\t{\n\t\tTestUtils.SetOutputHelper(output);\n\t}\n\n\t[Fact]\n\tpublic void Illusion_Does_Not_Copy_Same_Species()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 100, PBEMove.Splash);\n\n\t\tvar p1 = new TestPokemonCollection(2);\n\t\tp1[0] = p1[1] = new TestPokemon(settings, PBESpecies.Zoroark, 0, 100, PBEMove.Splash)\n\t\t{\n\t\t\tAbility = PBEAbility.Illusion\n\t\t};\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon zoroark1 = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Check\n\t\tAssert.False(zoroark1.Status2.HasFlag(PBEStatus2.Disguised));\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t[Fact]\n\tpublic void Illusion_Does_Not_Copy_Active_Wild_Teammate()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 100, PBEMove.Splash);\n\n\t\tvar p1 = new TestPokemonCollection(2);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Zoroark, 0, 100, PBEMove.Splash)\n\t\t{\n\t\t\tAbility = PBEAbility.Illusion,\n\t\t\tCaughtBall = PBEItem.None\n\t\t};\n\t\tp1[1] = p0[0];\n\n\t\tvar battle = PBEBattle.CreateWildBattle(PBEBattleFormat.Double, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBEWildInfo(p1));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon zoroark = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Check\n\t\tAssert.False(zoroark.Status2.HasFlag(PBEStatus2.Disguised));\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t[Fact]\n\tpublic void Illusion_Does_Copy_Active_Trainer_Teammate()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 100, PBEMove.Splash);\n\n\t\tvar p1 = new TestPokemonCollection(2);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Zoroark, 0, 100, PBEMove.Splash)\n\t\t{\n\t\t\tAbility = PBEAbility.Illusion\n\t\t};\n\t\tp1[1] = p0[0];\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Double, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon zoroark = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Check\n\t\tAssert.True(zoroark.Status2.HasFlag(PBEStatus2.Disguised));\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t[Fact]\n\tpublic void Illusion_Does_Not_Copy_Just_Swapped_Mon()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Happiny, 0, 1, PBEMove.Splash);\n\n\t\tvar p1 = new TestPokemonCollection(3);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Feebas, 0, 1, PBEMove.Splash);\n\t\tp1[1] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 1, PBEMove.Splash);\n\t\tp1[2] = new TestPokemon(settings, PBESpecies.Zoroark, 0, 100, PBEMove.Splash)\n\t\t{\n\t\t\tAbility = PBEAbility.Illusion\n\t\t};\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false),\n\t\t\t\tbattleTerrain: PBEBattleTerrain.Snow);\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon happiny = t0.Party[0];\n\t\tPBEBattlePokemon feebas = t1.Party[0];\n\t\tPBEBattlePokemon zoroark = t1.Party[2];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Swap Feebas for Zoroark and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(happiny, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(feebas, zoroark)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(zoroark.KnownSpecies == PBESpecies.Zoroark && zoroark.KnownForm == 0);\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t[Fact]\n\tpublic void Illusion_Copies_Just_Swapped_Mon()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Happiny, 0, 1, PBEMove.Splash);\n\n\t\tvar p1 = new TestPokemonCollection(4);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Trubbish, 0, 10, PBEMove.Splash); // Trubbish needs more speed to swap first\n\t\tp1[1] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 1, PBEMove.Splash);\n\t\tp1[2] = new TestPokemon(settings, PBESpecies.Zoroark, 0, 100, PBEMove.Splash)\n\t\t{\n\t\t\tAbility = PBEAbility.Illusion\n\t\t};\n\t\tp1[3] = new TestPokemon(settings, PBESpecies.Feebas, 0, 1, PBEMove.Splash);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Double, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false),\n\t\t\t\tbattleTerrain: PBEBattleTerrain.Snow);\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon happiny = t0.Party[0];\n\t\tPBEBattlePokemon trubbish = t1.Party[0];\n\t\tPBEBattlePokemon magikarp = t1.Party[1];\n\t\tPBEBattlePokemon zoroark = t1.Party[2];\n\t\tPBEBattlePokemon feebas = t1.Party[3];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Swap Trubbish and Magikarp for Feebas and Zoroark then check\n\t\tAssert.True(t0.SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(happiny, PBEMove.Splash, PBETurnTarget.AllyLeft)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(trubbish, feebas),\n\t\t\tnew PBETurnAction(magikarp, zoroark)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(zoroark.KnownSpecies == trubbish.Species && zoroark.KnownForm == trubbish.Form);\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t[Fact]\n\tpublic void Illusion_Copies_Shaymin_Reversion()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 1; // Seed ensures SecretPower freezes\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Happiny, 0, 100, PBEMove.SecretPower, PBEMove.Splash)\n\t\t{\n\t\t\tAbility = PBEAbility.SereneGrace\n\t\t};\n\n\t\tvar p1 = new TestPokemonCollection(3);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Shaymin, PBEForm.Shaymin_Sky, 100, PBEMove.Splash);\n\t\tp1[1] = new TestPokemon(settings, PBESpecies.Zoroark, 0, 100, PBEMove.Splash)\n\t\t{\n\t\t\tAbility = PBEAbility.Illusion\n\t\t};\n\t\tp1[2] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 100, PBEMove.Splash);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false),\n\t\t\t\tbattleTerrain: PBEBattleTerrain.Snow);\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon happiny = t0.Party[0];\n\t\tPBEBattlePokemon shaymin = t1.Party[0];\n\t\tPBEBattlePokemon zoroark = t1.Party[1];\n\t\tPBEBattlePokemon magikarp = t1.Party[2];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Freeze Shaymin\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(happiny, PBEMove.SecretPower, PBETurnTarget.FoeCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(shaymin, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(shaymin.Status1 == PBEStatus1.Frozen && shaymin.Form == PBEForm.Shaymin);\n\t\t#endregion\n\n\t\t#region Swap Shaymin for Magikarp\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(happiny, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(shaymin, magikarp)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(t1.Party[2] == shaymin);\n\t\t#endregion\n\n\t\t#region Swap Magikarp for Zoroark and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(happiny, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(magikarp, zoroark)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(zoroark.KnownSpecies == PBESpecies.Shaymin && zoroark.KnownForm == PBEForm.Shaymin);\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t[Fact]\n\tpublic void Illusion_Works_Wild()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 1, PBEMove.Tackle);\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Zoroark, 0, 100, PBEMove.Splash)\n\t\t{\n\t\t\tAbility = PBEAbility.Illusion,\n\t\t\tCaughtBall = PBEItem.None\n\t\t};\n\n\t\tvar battle = PBEBattle.CreateWildBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBEWildInfo(p1));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon magikarp = t0.Party[0];\n\t\tPBEBattlePokemon zoroark = t1.Party[0];\n\n\t\tzoroark.Status2 |= PBEStatus2.Disguised;\n\t\tzoroark.KnownGender = PBEGender.Genderless;\n\t\tzoroark.KnownCaughtBall = PBEItem.None;\n\t\tzoroark.KnownShiny = false;\n\t\tzoroark.KnownSpecies = PBESpecies.Entei;\n\t\tzoroark.KnownForm = 0;\n\t\tzoroark.KnownNickname = zoroark.KnownSpecies.ToString();\n\t\tIPBEPokemonData pData = PBEDataProvider.Instance.GetPokemonData(zoroark.KnownSpecies, zoroark.KnownForm);\n\t\tzoroark.KnownType1 = pData.Type1;\n\t\tzoroark.KnownType2 = pData.Type2;\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Check that the disguise works\n\t\tAssert.True(zoroark.Status2.HasFlag(PBEStatus2.Disguised)\n\t\t\t&& ((PBEWildPkmnAppearedPacket)battle.Events.Single(p => p is PBEWildPkmnAppearedPacket)).Pokemon[0].IsDisguised);\n\t\t#endregion\n\n\t\t#region Break the disguise and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(magikarp, PBEMove.Tackle, PBETurnTarget.FoeCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(zoroark, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(!zoroark.Status2.HasFlag(PBEStatus2.Disguised)\n\t\t\t&& zoroark.KnownSpecies == PBESpecies.Zoroark);\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineTests/Abilities/IntimidateTests.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace Kermalis.PokemonBattleEngineTests.Abilities;\n\n[Collection(\"Utils\")]\npublic class IntimidateTests\n{\n\tpublic IntimidateTests(TestUtils _, ITestOutputHelper output)\n\t{\n\t\tTestUtils.SetOutputHelper(output);\n\t}\n\n\t[Fact]\n\tpublic void Intimidate_Works()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(3);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Shuckle, 0, 100, PBEMove.Splash);\n\t\tp0[1] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 100, PBEMove.Splash);\n\t\tp0[2] = new TestPokemon(settings, PBESpecies.Happiny, 0, 100, PBEMove.Splash);\n\n\t\tvar p1 = new TestPokemonCollection(2);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Luxray, 0, 100, PBEMove.Splash)\n\t\t{\n\t\t\tAbility = PBEAbility.Intimidate\n\t\t};\n\t\tp1[1] = new TestPokemon(settings, PBESpecies.Skitty, 0, 100, PBEMove.Splash);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Triple, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon shuckle = t0.Party[0];\n\t\tPBEBattlePokemon magikarp = t0.Party[1];\n\t\tPBEBattlePokemon happiny = t0.Party[2];\n\t\tPBEBattlePokemon luxray = t1.Party[0];\n\t\tPBEBattlePokemon skitty = t1.Party[1];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Check\n\t\tAssert.True(battle.VerifyAbilityHappened(luxray, luxray, PBEAbility.Intimidate, PBEAbilityAction.Stats) // Activated\n\t\t\t&& happiny.AttackChange < 0 && magikarp.AttackChange < 0 && shuckle.AttackChange == 0 && skitty.AttackChange == 0); // Hit only surrounding foes\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t[Fact]\n\tpublic void Intimidate_Does_Not_Announce_If_No_Foes()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Shuckle, 0, 100, PBEMove.Splash);\n\n\t\tvar p1 = new TestPokemonCollection(2);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Luxray, 0, 100, PBEMove.Splash)\n\t\t{\n\t\t\tAbility = PBEAbility.Intimidate\n\t\t};\n\t\tp1[1] = new TestPokemon(settings, PBESpecies.Skitty, 0, 100, PBEMove.Splash);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Triple, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon shuckle = t0.Party[0];\n\t\tPBEBattlePokemon luxray = t1.Party[0];\n\t\tPBEBattlePokemon skitty = t1.Party[1];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Check\n\t\tAssert.False(battle.VerifyAbilityHappened(luxray, luxray, PBEAbility.Intimidate, PBEAbilityAction.Stats)); // Did not activate\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t[Fact]\n\tpublic void Intimidate_Does_Not_Hit_Through_Substitute()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Shuckle, 0, 100, PBEMove.Substitute, PBEMove.Splash);\n\n\t\tvar p1 = new TestPokemonCollection(2);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Skitty, 0, 100, PBEMove.Splash);\n\t\tp1[1] = new TestPokemon(settings, PBESpecies.Luxray, 0, 100, PBEMove.Splash)\n\t\t{\n\t\t\tAbility = PBEAbility.Intimidate\n\t\t};\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon shuckle = t0.Party[0];\n\t\tPBEBattlePokemon skitty = t1.Party[0];\n\t\tPBEBattlePokemon luxray = t1.Party[1];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use Substitute\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(shuckle, PBEMove.Substitute, PBETurnTarget.AllyCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(skitty, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(shuckle.Status2.HasFlag(PBEStatus2.Substitute));\n\t\t#endregion\n\n\t\t#region Switch in Luxray and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(shuckle, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(skitty, luxray)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(battle.VerifyAbilityHappened(luxray, luxray, PBEAbility.Intimidate, PBEAbilityAction.Stats) // Activated\n\t\t\t&& battle.VerifyMoveResultHappened(luxray, shuckle, PBEResult.Ineffective_Substitute) && shuckle.AttackChange == 0); // Did not affect\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineTests/Abilities/NaturalCureTests.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace Kermalis.PokemonBattleEngineTests.Abilities;\n\n[Collection(\"Utils\")]\npublic class NaturalCureTests\n{\n\tpublic NaturalCureTests(TestUtils _, ITestOutputHelper output)\n\t{\n\t\tTestUtils.SetOutputHelper(output);\n\t}\n\n\t[Fact]\n\tpublic void NaturalCure_Works_On_Battle_Ending()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Happiny, 0, 1, PBEMove.Splash);\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Shaymin, PBEForm.Shaymin, 100, PBEMove.Splash, PBEMove.QuickAttack)\n\t\t{\n\t\t\tAbility = PBEAbility.NaturalCure,\n\t\t\tItem = PBEItem.FlameOrb\n\t\t};\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon happiny = t0.Party[0];\n\t\tPBEBattlePokemon shaymin = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Burn Shaymin\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(happiny, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(shaymin, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(shaymin.Status1 == PBEStatus1.Burned);\n\t\t#endregion\n\n\t\t#region End battle and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(happiny, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(shaymin, PBEMove.QuickAttack, PBETurnTarget.FoeCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(battle.BattleResult == PBEBattleResult.Team1Win\n\t\t\t&& battle.BattleState == PBEBattleState.Ended\n\t\t\t&& shaymin.Status1 == PBEStatus1.None);\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineTests/Abilities/PoisonHealTests.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace Kermalis.PokemonBattleEngineTests.Abilities;\n\n[Collection(\"Utils\")]\npublic class PoisonHealTests\n{\n\tpublic PoisonHealTests(TestUtils _, ITestOutputHelper output)\n\t{\n\t\tTestUtils.SetOutputHelper(output);\n\t}\n\n\t[Fact]\n\tpublic void PoisonHeal_BadlyPoisoned_Counter_Works()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0; // Seed prevents Toxic from missing\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Seviper, 0, 100, PBEMove.Toxic);\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Gliscor, 0, 100, PBEMove.Splash)\n\t\t{\n\t\t\tAbility = PBEAbility.PoisonHeal\n\t\t};\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon seviper = t0.Party[0];\n\t\tPBEBattlePokemon gliscor = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Badly Poison Gliscor and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(seviper, PBEMove.Toxic, PBETurnTarget.FoeCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(gliscor, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(gliscor.Status1 == PBEStatus1.BadlyPoisoned // Was afflicted\n\t\t\t&& !battle.VerifyAbilityHappened(gliscor, gliscor, PBEAbility.PoisonHeal, PBEAbilityAction.RestoredHP) // Did not activate\n\t\t\t&& !battle.VerifyStatus1Happened(gliscor, gliscor, PBEStatus1.BadlyPoisoned, PBEStatusAction.Damage) // Did not activate\n\t\t\t&& gliscor.Status1Counter == 2 // Counter still increments\n\t\t\t&& gliscor.HPPercentage == 1); // Did not take damage\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineTests/ActionsTests.cs",
    "content": "using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing System;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace Kermalis.PokemonBattleEngineTests;\n\n[Collection(\"Utils\")]\npublic class ActionsTests\n{\n\tpublic ActionsTests(TestUtils _, ITestOutputHelper output)\n\t{\n\t\tTestUtils.SetOutputHelper(output);\n\t}\n\n\t// TODO: bad field position to switch into, bad move, bad targets, bad targets with templockedmove, battle status, bad pkmn id,\n\t// TODO: wrong team pkmn id, duplicate pkmn id, can't switch out but tried, invalid switch mon (null hp pos), duplicate switch mon\n\t// TODO: Too many items, items we do not have, items when in templockedmove\n\t// TODO: Flee\n\t[Fact]\n\tpublic void Basic_Actions()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(2);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Koffing, 0, 100, PBEMove.Selfdestruct);\n\t\tp0[1] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 100, PBEMove.Splash);\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Darkrai, 0, 100, PBEMove.Protect);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon koffing = t0.Party[0];\n\t\tPBEBattlePokemon magikarp = t0.Party[1];\n\t\tPBEBattlePokemon darkrai = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Darkrai uses Protect, Koffing uses Selfdestruct and faints\n\t\tvar a = new PBETurnAction(koffing, PBEMove.Selfdestruct, PBETurnTarget.FoeCenter);\n\t\tAssert.False(t0.SelectActionsIfValid(out _, a, a)); // Too many actions\n\t\tAssert.True(t0.SelectActionsIfValid(out _, a)); // Good actions\n\t\tAssert.False(t0.SelectActionsIfValid(out _, a)); // Actions were already submitted\n\t\tAssert.False(t0.SelectActionsIfValid(out _, Array.Empty<PBETurnAction>())); // 0 despite us now needing 0 additional actions\n\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(darkrai, PBEMove.Protect, PBETurnTarget.AllyCenter))); // True for good actions\n\n\t\tbattle.RunTurn();\n\t\t#endregion\n\n\t\t#region More checks\n\t\tvar s = new PBESwitchIn(magikarp, PBEFieldPosition.Center);\n\t\tAssert.False(t0.SelectSwitchesIfValid(out _, s, s)); // Too many\n\t\tAssert.True(t0.SelectSwitchesIfValid(out _, s)); // Good switches\n\n\t\t// Below two wouldn't work because of battle status lol\n\t\t//Assert.False(t0.SelectSwitchesIfValid(out _, s)); // Switches were already submitted\n\t\t//Assert.False(t0.SelectSwitchesIfValid(out _, Array.Empty<PBESwitchIn>())); // 0 despite us now needing 0 additional switches\n\n\t\t//battle.RunSwitches();\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t[Fact]\n\tpublic void Cannot_Send_Egg()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(3);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Koffing, 0, 100, PBEMove.Selfdestruct);\n\t\tp0[1] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 1, PBEMove.Splash)\n\t\t{\n\t\t\tPBEIgnore = true\n\t\t};\n\t\tp0[2] = new TestPokemon(settings, PBESpecies.Happiny, 0, 100, PBEMove.Splash);\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Darkrai, 0, 100, PBEMove.Protect);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon koffing = t0.Party[0];\n\t\tPBEBattlePokemon magikarp = t0.Party[1];\n\t\tPBEBattlePokemon darkrai = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Darkrai uses Protect, Koffing uses Selfdestruct and faints\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(koffing, PBEMove.Selfdestruct, PBETurnTarget.FoeCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(darkrai, PBEMove.Protect, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\t\t#endregion\n\n\t\t#region Check\n\t\tAssert.False(t0.SelectSwitchesIfValid(out _, new PBESwitchIn(magikarp, PBEFieldPosition.Center)));\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t[Fact]\n\tpublic void Cannot_Switch_In_Egg()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(2);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Koffing, 0, 100, PBEMove.Selfdestruct);\n\t\tp0[1] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 1, PBEMove.Splash)\n\t\t{\n\t\t\tPBEIgnore = true\n\t\t};\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Darkrai, 0, 100, PBEMove.Protect);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon koffing = t0.Party[0];\n\t\tPBEBattlePokemon magikarp = t0.Party[1];\n\t\tPBEBattlePokemon darkrai = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Check\n\t\tAssert.False(t0.SelectActionsIfValid(out _, new PBETurnAction(koffing, magikarp)));\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineTests/AutoCenterTests.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace Kermalis.PokemonBattleEngineTests;\n\n[Collection(\"Utils\")]\npublic class AutoCenterTests\n{\n\tpublic AutoCenterTests(TestUtils _, ITestOutputHelper output)\n\t{\n\t\tTestUtils.SetOutputHelper(output);\n\t}\n\n\t[Theory]\n\t[InlineData(true)]\n\t[InlineData(false)]\n\tpublic void AutoCenter_Works(bool faintLeft)\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p = new TestPokemonCollection(3);\n\t\tp[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 1, PBEMove.Protect, PBEMove.Splash);\n\t\tp[1] = new TestPokemon(settings, PBESpecies.Golem, 0, 100, PBEMove.Explosion);\n\t\tp[2] = new TestPokemon(settings, PBESpecies.Happiny, 0, 1, PBEMove.Protect, PBEMove.Splash);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Triple, settings, new PBETrainerInfo(p, \"Trainer 0\", false), new PBETrainerInfo(p, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon magikarp0 = t0.Party[0];\n\t\tPBEBattlePokemon golem0 = t0.Party[1];\n\t\tPBEBattlePokemon happiny0 = t0.Party[2];\n\t\tPBEBattlePokemon magikarp1 = t1.Party[0];\n\t\tPBEBattlePokemon golem1 = t1.Party[1];\n\t\tPBEBattlePokemon happiny1 = t1.Party[2];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Force auto-center and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(magikarp0, faintLeft ? PBEMove.Splash : PBEMove.Protect, PBETurnTarget.AllyLeft),\n\t\t\tnew PBETurnAction(golem0, PBEMove.Explosion, PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight),\n\t\t\tnew PBETurnAction(happiny0, faintLeft ? PBEMove.Protect : PBEMove.Splash, PBETurnTarget.AllyRight)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(magikarp1, faintLeft ? PBEMove.Splash : PBEMove.Protect, PBETurnTarget.AllyLeft),\n\t\t\tnew PBETurnAction(golem1, PBEMove.Explosion, PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight),\n\t\t\tnew PBETurnAction(happiny1, faintLeft ? PBEMove.Protect : PBEMove.Splash, PBETurnTarget.AllyRight)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True((faintLeft ? happiny0 : magikarp0).FieldPosition == PBEFieldPosition.Center && (faintLeft ? happiny1 : magikarp1).FieldPosition == PBEFieldPosition.Center);\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t// https://github.com/Kermalis/PokemonBattleEngine/issues/318\n\t[Theory]\n\t[InlineData(true)]\n\t[InlineData(false)]\n\tpublic void AutoCenter_Works_Despite_Available(bool faintLeft)\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0; // Seed ensures protect doesn't fail\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0L = new TestPokemonCollection(faintLeft ? 1 : 2);\n\t\tvar p0C = new TestPokemonCollection(1);\n\t\tvar p0R = new TestPokemonCollection(faintLeft ? 2 : 1);\n\t\tvar p1 = new TestPokemonCollection(4);\n\t\tp0L[0] = p1[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 1, PBEMove.Protect, PBEMove.Splash);\n\t\tp0C[0] = p1[1] = new TestPokemon(settings, PBESpecies.Golem, 0, 100, PBEMove.Explosion);\n\t\tp0R[0] = p1[2] = new TestPokemon(settings, PBESpecies.Happiny, 0, 1, PBEMove.Protect, PBEMove.Splash);\n\t\t(faintLeft ? p0R : p0L)[1] = p1[3] = new TestPokemon(settings, PBESpecies.Weezing, 0, 100, PBEMove.Explosion);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Triple, settings,\n\t\t\t\tnew[] { new PBETrainerInfo(p0L, \"Trainer 0\", false), new PBETrainerInfo(p0C, \"Trainer 1\", false), new PBETrainerInfo(p0R, \"Trainer 2\", false) },\n\t\t\t\tnew[] { new PBETrainerInfo(p1, \"Trainer 3\", false) });\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0L = battle.Trainers[0];\n\t\tPBETrainer t0C = battle.Trainers[1];\n\t\tPBETrainer t0R = battle.Trainers[2];\n\t\tPBETrainer t1 = battle.Trainers[3];\n\t\tPBEBattlePokemon magikarp0 = t0L.Party[0];\n\t\tPBEBattlePokemon golem0 = t0C.Party[0];\n\t\tPBEBattlePokemon happiny0 = t0R.Party[0];\n\t\tPBEBattlePokemon weezing0 = (faintLeft ? t0R : t0L).Party[1];\n\t\tPBEBattlePokemon magikarp1 = t1.Party[0];\n\t\tPBEBattlePokemon golem1 = t1.Party[1];\n\t\tPBEBattlePokemon happiny1 = t1.Party[2];\n\t\tPBEBattlePokemon weezing1 = t1.Party[3];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Force switch-in from trainer 3\n\t\tAssert.True(t0L.SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(magikarp0, faintLeft ? PBEMove.Splash : PBEMove.Protect, PBETurnTarget.AllyLeft)));\n\t\tAssert.True(t0C.SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(golem0, PBEMove.Explosion, PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight)));\n\t\tAssert.True(t0R.SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(happiny0, faintLeft ? PBEMove.Protect : PBEMove.Splash, PBETurnTarget.AllyRight)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(magikarp1, faintLeft ? PBEMove.Splash : PBEMove.Protect, PBETurnTarget.AllyLeft),\n\t\t\tnew PBETurnAction(golem1, PBEMove.Explosion, PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight),\n\t\t\tnew PBETurnAction(happiny1, faintLeft ? PBEMove.Protect : PBEMove.Splash, PBETurnTarget.AllyRight)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.False((faintLeft ? happiny0 : magikarp0).FieldPosition == PBEFieldPosition.Center && (faintLeft ? happiny1 : magikarp1).FieldPosition == PBEFieldPosition.Center);\n\t\tAssert.True(t0L.SwitchInsRequired == 0 && t0C.SwitchInsRequired == 0 && t0R.SwitchInsRequired == 0 && t1.SwitchInsRequired == 1);\n\t\t#endregion\n\n\t\t#region Switch-in\n\t\tAssert.True(t1.SelectSwitchesIfValid(out _,\n\t\t\tnew PBESwitchIn(weezing1, PBEFieldPosition.Center)));\n\n\t\tbattle.RunSwitches();\n\t\t#endregion\n\n\t\t#region Force auto-center and check\n\t\tAssert.True((faintLeft ? t0R : t0L).SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(faintLeft ? happiny0 : magikarp0, PBEMove.Protect, faintLeft ? PBETurnTarget.AllyRight : PBETurnTarget.AllyLeft)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(weezing1, PBEMove.Explosion, PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight),\n\t\t\tnew PBETurnAction(faintLeft ? happiny1 : magikarp1, PBEMove.Protect, faintLeft ? PBETurnTarget.AllyRight : PBETurnTarget.AllyLeft)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True((faintLeft ? happiny0 : magikarp0).FieldPosition == PBEFieldPosition.Center && (faintLeft ? happiny1 : magikarp1).FieldPosition == PBEFieldPosition.Center);\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t[Fact]\n\tpublic void AutoCenter_ActivatesFromHazard()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 2; // Seed ensures Regigigas doesn't flinch and Rock Slide hits\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(5);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 1, PBEMove.Splash)\n\t\t{\n\t\t\tItem = PBEItem.FocusSash\n\t\t};\n\t\tp0[1] = new TestPokemon(settings, PBESpecies.Munchlax, 0, 1, PBEMove.Splash)\n\t\t{\n\t\t\tItem = PBEItem.FocusSash\n\t\t};\n\t\tp0[2] = new TestPokemon(settings, PBESpecies.Happiny, 0, 1, PBEMove.Splash);\n\t\tp0[3] = new TestPokemon(settings, PBESpecies.Regigigas, 0, 10, PBEMove.Explosion)\n\t\t{\n\t\t\tAbility = PBEAbility.SlowStart\n\t\t};\n\t\tp0[4] = new TestPokemon(settings, PBESpecies.Mudkip, 0, 1, PBEMove.Splash);\n\n\t\tvar p1 = new TestPokemonCollection(3);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Budew, 0, 1, PBEMove.Splash);\n\t\tp1[1] = new TestPokemon(settings, PBESpecies.Butterfree, 0, 10, PBEMove.RockSlide, PBEMove.Splash)\n\t\t{\n\t\t\tAbility = PBEAbility.Compoundeyes\n\t\t};\n\t\tp1[2] = new TestPokemon(settings, PBESpecies.Sunkern, 0, 1, PBEMove.Splash, PBEMove.StealthRock);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Triple, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon magikarp = t0.Party[0];\n\t\tPBEBattlePokemon munchlax = t0.Party[1];\n\t\tPBEBattlePokemon happiny = t0.Party[2];\n\t\tPBEBattlePokemon regigigas = t0.Party[3];\n\t\tPBEBattlePokemon mudkip = t0.Party[4];\n\t\tPBEBattlePokemon budew = t1.Party[0];\n\t\tPBEBattlePokemon butterfree = t1.Party[1];\n\t\tPBEBattlePokemon sunkern = t1.Party[2];\n\t\thappiny.HP = 1; // Set Happiny HP to 1 so it faints on switch\n\t\thappiny.UpdateHPPercentage();\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Set up Stealth Rock while lowering HP, swap Happiny for Regigigas\n\t\tAssert.True(t0.SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(magikarp, PBEMove.Splash, PBETurnTarget.AllyLeft),\n\t\t\tnew PBETurnAction(munchlax, PBEMove.Splash, PBETurnTarget.AllyCenter),\n\t\t\tnew PBETurnAction(happiny, regigigas)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(budew, PBEMove.Splash, PBETurnTarget.AllyLeft),\n\t\t\tnew PBETurnAction(butterfree, PBEMove.RockSlide, PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight),\n\t\t\tnew PBETurnAction(sunkern, PBEMove.StealthRock, PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(t0.Team.TeamStatus.HasFlag(PBETeamStatus.StealthRock));\n\t\t#endregion\n\n\t\t#region Yeet everyone\n\t\tAssert.True(t0.SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(magikarp, PBEMove.Splash, PBETurnTarget.AllyLeft),\n\t\t\tnew PBETurnAction(munchlax, PBEMove.Splash, PBETurnTarget.AllyCenter),\n\t\t\tnew PBETurnAction(regigigas, PBEMove.Explosion, PBETurnTarget.AllyCenter | PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(budew, PBEMove.Splash, PBETurnTarget.AllyLeft),\n\t\t\tnew PBETurnAction(butterfree, PBEMove.RockSlide, PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight),\n\t\t\tnew PBETurnAction(sunkern, PBEMove.Splash, PBETurnTarget.AllyRight)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(magikarp.HP == 0 && munchlax.HP == 0 && regigigas.HP == 0 && budew.HP == 0 && butterfree.HP == 0\n\t\t\t&& happiny.HP != 0 && mudkip.HP != 0 && sunkern.HP != 0);\n\t\t#endregion\n\n\t\t#region Switch in and check\n\t\tAssert.True(t0.SelectSwitchesIfValid(out _,\n\t\t\tnew PBESwitchIn(happiny, PBEFieldPosition.Center),\n\t\t\tnew PBESwitchIn(mudkip, PBEFieldPosition.Right)));\n\n\t\tbattle.RunSwitches();\n\n\t\tAssert.True(happiny.HP == 0 && mudkip.FieldPosition == PBEFieldPosition.Center && sunkern.FieldPosition == PBEFieldPosition.Center);\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t[Theory]\n\t[InlineData(true)]\n\t[InlineData(false)]\n\tpublic void AutoCenter_Works_MultiBattle(bool faintLeft)\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar pL = new TestPokemonCollection(1);\n\t\tpL[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 1, PBEMove.Protect, PBEMove.Splash);\n\t\tvar pC = new TestPokemonCollection(1);\n\t\tpC[0] = new TestPokemon(settings, PBESpecies.Golem, 0, 100, PBEMove.Explosion);\n\t\tvar pR = new TestPokemonCollection(1);\n\t\tpR[0] = new TestPokemon(settings, PBESpecies.Happiny, 0, 1, PBEMove.Protect, PBEMove.Splash);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Triple, settings,\n\t\t\t\tnew[] { new PBETrainerInfo(pL, \"Trainer 0\", false), new PBETrainerInfo(pC, \"Trainer 1\", false), new PBETrainerInfo(pR, \"Trainer 2\", false) },\n\t\t\t\tnew[] { new PBETrainerInfo(pL, \"Trainer 3\", false), new PBETrainerInfo(pC, \"Trainer 4\", false), new PBETrainerInfo(pR, \"Trainer 5\", false) });\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0L = battle.Trainers[0];\n\t\tPBETrainer t0C = battle.Trainers[1];\n\t\tPBETrainer t0R = battle.Trainers[2];\n\t\tPBETrainer t1L = battle.Trainers[3];\n\t\tPBETrainer t1C = battle.Trainers[4];\n\t\tPBETrainer t1R = battle.Trainers[5];\n\t\tPBEBattlePokemon magikarp0 = t0L.Party[0];\n\t\tPBEBattlePokemon golem0 = t0C.Party[0];\n\t\tPBEBattlePokemon happiny0 = t0R.Party[0];\n\t\tPBEBattlePokemon magikarp1 = t1L.Party[0];\n\t\tPBEBattlePokemon golem1 = t1C.Party[0];\n\t\tPBEBattlePokemon happiny1 = t1R.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Force auto-center and check\n\t\tAssert.True(t0L.SelectActionsIfValid(out _, new PBETurnAction(magikarp0, faintLeft ? PBEMove.Splash : PBEMove.Protect, PBETurnTarget.AllyLeft)));\n\t\tAssert.True(t0C.SelectActionsIfValid(out _, new PBETurnAction(golem0, PBEMove.Explosion, PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight)));\n\t\tAssert.True(t0R.SelectActionsIfValid(out _, new PBETurnAction(happiny0, faintLeft ? PBEMove.Protect : PBEMove.Splash, PBETurnTarget.AllyRight)));\n\t\tAssert.True(t1L.SelectActionsIfValid(out _, new PBETurnAction(magikarp1, faintLeft ? PBEMove.Splash : PBEMove.Protect, PBETurnTarget.AllyLeft)));\n\t\tAssert.True(t1C.SelectActionsIfValid(out _, new PBETurnAction(golem1, PBEMove.Explosion, PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight)));\n\t\tAssert.True(t1R.SelectActionsIfValid(out _, new PBETurnAction(happiny1, faintLeft ? PBEMove.Protect : PBEMove.Splash, PBETurnTarget.AllyRight)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True((faintLeft ? happiny0 : magikarp0).FieldPosition == PBEFieldPosition.Center && (faintLeft ? happiny1 : magikarp1).FieldPosition == PBEFieldPosition.Center);\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineTests/BattleResultTests.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace Kermalis.PokemonBattleEngineTests;\n\n[Collection(\"Utils\")]\npublic class BattleResultTests\n{\n\tpublic BattleResultTests(TestUtils _, ITestOutputHelper output)\n\t{\n\t\tTestUtils.SetOutputHelper(output);\n\t}\n\n\t// TODO: Who wins if you use PerishSong and everyone faints at the same time? Is it based on who's slowest?\n\n\t[Fact]\n\tpublic void Explosion_User_Loses_Single()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Golem, 0, 100, PBEMove.Explosion);\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 1, PBEMove.Splash);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon golem = t0.Party[0];\n\t\tPBEBattlePokemon magikarp = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use move and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(golem, PBEMove.Explosion, PBETurnTarget.FoeCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(magikarp, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(golem.HP == 0 && magikarp.HP == 0 // All faint\n\t\t\t&& battle.BattleResult == PBEBattleResult.Team1Win); // Golem's team loses\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t[Fact]\n\tpublic void Explosion_User_Loses_Multiple()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(3);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Qwilfish, 0, 1, PBEMove.Splash);\n\t\tp0[1] = new TestPokemon(settings, PBESpecies.Golem, 0, 100, PBEMove.Explosion);\n\t\tp0[2] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 1, PBEMove.Splash);\n\n\t\tvar p1 = new TestPokemonCollection(3);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Patrat, 0, 1, PBEMove.Splash);\n\t\tp1[1] = new TestPokemon(settings, PBESpecies.Lickilicky, 0, 1, PBEMove.Splash);\n\t\tp1[2] = new TestPokemon(settings, PBESpecies.Happiny, 0, 1, PBEMove.Splash);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Triple, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon qwilfish = t0.Party[0];\n\t\tPBEBattlePokemon golem = t0.Party[1];\n\t\tPBEBattlePokemon magikarp = t0.Party[2];\n\t\tPBEBattlePokemon patrat = t1.Party[0];\n\t\tPBEBattlePokemon lickilicky = t1.Party[1];\n\t\tPBEBattlePokemon happiny = t1.Party[2];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use move and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(qwilfish, PBEMove.Splash, PBETurnTarget.AllyLeft),\n\t\t\tnew PBETurnAction(golem, PBEMove.Explosion, PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight),\n\t\t\tnew PBETurnAction(magikarp, PBEMove.Splash, PBETurnTarget.AllyRight)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(patrat, PBEMove.Splash, PBETurnTarget.AllyLeft),\n\t\t\tnew PBETurnAction(lickilicky, PBEMove.Splash, PBETurnTarget.AllyCenter),\n\t\t\tnew PBETurnAction(happiny, PBEMove.Splash, PBETurnTarget.AllyRight)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(qwilfish.HP == 0 && golem.HP == 0 && magikarp.HP == 0 && patrat.HP == 0 && lickilicky.HP == 0 && happiny.HP == 0 // All faint\n\t\t\t&& battle.BattleResult == PBEBattleResult.Team1Win); // Golem's team loses\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t[Fact]\n\tpublic void FinalGambit_User_Loses()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Staraptor, 0, 100, PBEMove.FinalGambit);\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 1, PBEMove.Splash);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon staraptor = t0.Party[0];\n\t\tPBEBattlePokemon magikarp = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use FinalGambit and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(staraptor, PBEMove.FinalGambit, PBETurnTarget.FoeCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(magikarp, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(staraptor.HP == 0 && magikarp.HP == 0 // Both fainted\n\t\t\t&& battle.BattleResult == PBEBattleResult.Team1Win); // Magikarp's team wins\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t[Fact]\n\tpublic void HPDrain_And_LiquidOoze()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Deoxys, PBEForm.Deoxys_Attack, 100, PBEMove.DrainPunch);\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Blissey, 0, 100, PBEMove.Splash)\n\t\t{\n\t\t\tAbility = PBEAbility.LiquidOoze\n\t\t};\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon deoxys = t0.Party[0];\n\t\tPBEBattlePokemon blissey = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use DrainPunch and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(deoxys, PBEMove.DrainPunch, PBETurnTarget.FoeCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(blissey, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(deoxys.HP == 0 && blissey.HP == 0 // Both fainted\n\t\t\t&& battle.BattleResult == PBEBattleResult.Team1Win); // Blissey's team wins\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t// IronBarbs/RockyHelmet/RoughSkin\n\t[Fact]\n\tpublic void IronBarbs_User_Loses()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Lucario, 0, 100, PBEMove.Pound);\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Ferroseed, 0, 1, PBEMove.Splash)\n\t\t{\n\t\t\tAbility = PBEAbility.IronBarbs\n\t\t};\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon lucario = t0.Party[0];\n\t\tPBEBattlePokemon ferroseed = t1.Party[0];\n\t\tlucario.HP = 1;\n\t\tlucario.UpdateHPPercentage();\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use Pound and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(lucario, PBEMove.Pound, PBETurnTarget.FoeCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(ferroseed, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(lucario.HP == 0 && ferroseed.HP == 0 // Both fainted\n\t\t\t&& battle.BattleResult == PBEBattleResult.Team1Win); // Ferroseed's team wins\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t[Fact]\n\tpublic void LeechSeed_And_LiquidOoze()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0; // Seed ensures LeechSeed doesn't miss\n\t\tvar settings = new PBESettings { LeechSeedDenominator = 1 };\n\t\tsettings.MakeReadOnly();\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Shroomish, 0, 1, PBEMove.LeechSeed);\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Tentacruel, 0, 100, PBEMove.Splash)\n\t\t{\n\t\t\tAbility = PBEAbility.LiquidOoze\n\t\t};\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon shroomish = t0.Party[0];\n\t\tPBEBattlePokemon tentacruel = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use LeechSeed and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(shroomish, PBEMove.LeechSeed, PBETurnTarget.FoeCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(tentacruel, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(shroomish.HP == 0 && tentacruel.HP == 0 // Both fainted\n\t\t\t&& battle.BattleResult == PBEBattleResult.Team0Win); // Shroomish's team wins\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t[Fact]\n\tpublic void LifeOrb_User_Wins()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Riolu, 0, 100, PBEMove.VacuumWave)\n\t\t{\n\t\t\tItem = PBEItem.LifeOrb\n\t\t};\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 1, PBEMove.Splash);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon riolu = t0.Party[0];\n\t\tPBEBattlePokemon magikarp = t1.Party[0];\n\t\triolu.HP = 1;\n\t\triolu.UpdateHPPercentage();\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use HeadCharge and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(riolu, PBEMove.VacuumWave, PBETurnTarget.FoeCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(magikarp, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(riolu.HP == 0 && magikarp.HP == 0 // Both fainted\n\t\t\t&& battle.BattleResult == PBEBattleResult.Team0Win); // Bouffalant's team wins\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t[Fact]\n\tpublic void Recoil_User_Wins()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Bouffalant, 0, 100, PBEMove.HeadCharge);\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 1, PBEMove.Splash);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon bouffalant = t0.Party[0];\n\t\tPBEBattlePokemon magikarp = t1.Party[0];\n\t\tbouffalant.HP = 1;\n\t\tbouffalant.UpdateHPPercentage();\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use HeadCharge and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(bouffalant, PBEMove.HeadCharge, PBETurnTarget.FoeCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(magikarp, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(bouffalant.HP == 0 && magikarp.HP == 0 // Both fainted\n\t\t\t&& battle.BattleResult == PBEBattleResult.Team0Win); // Bouffalant's team wins\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineTests/BehaviorTests.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing System.Linq;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace Kermalis.PokemonBattleEngineTests;\n\n[Collection(\"Utils\")]\npublic class BehaviorTests\n{\n\tpublic BehaviorTests(TestUtils _, ITestOutputHelper output)\n\t{\n\t\tTestUtils.SetOutputHelper(output);\n\t}\n\n\t[Fact]\n\tpublic void Wild_Pkmn_Positions_Set_Before_Begin()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 100, PBEMove.Splash);\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Darkrai, 0, 100, PBEMove.Splash)\n\t\t{\n\t\t\tCaughtBall = PBEItem.None\n\t\t};\n\n\t\tvar battle = PBEBattle.CreateWildBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBEWildInfo(p1));\n\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon darkrai = t1.Party[0];\n\t\t#endregion\n\n\t\t#region Check\n\t\tAssert.True(darkrai.FieldPosition == PBEFieldPosition.Center\n\t\t\t&& battle.ActiveBattlers.Single() == darkrai);\n\t\t#endregion\n\t}\n\n\t[Fact]\n\tpublic void Fainted_Pkmn_Not_Sent_Out_Single()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPartyPokemonCollection(2);\n\t\tp0[0] = new TestPartyPokemon(settings, PBESpecies.Magikarp, 0, 100, PBEMove.Splash)\n\t\t{\n\t\t\tHP = 0\n\t\t};\n\t\tp0[1] = new TestPartyPokemon(settings, PBESpecies.Absol, 0, 100, PBEMove.Splash);\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Darkrai, 0, 100, PBEMove.Splash);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBEBattlePokemon magikarp = t0.Party[0];\n\t\tPBEBattlePokemon absol = t0.Party[1];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Check\n\t\tAssert.True(magikarp.FieldPosition == PBEFieldPosition.None\n\t\t\t&& absol.FieldPosition == PBEFieldPosition.Center);\n\t\t#endregion\n\t}\n\n\t[Fact]\n\tpublic void Fainted_Pkmn_Not_Sent_Out_Double()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPartyPokemonCollection(3);\n\t\tp0[0] = new TestPartyPokemon(settings, PBESpecies.Magikarp, 0, 100, PBEMove.Splash);\n\t\tp0[1] = new TestPartyPokemon(settings, PBESpecies.Absol, 0, 100, PBEMove.Splash)\n\t\t{\n\t\t\tHP = 0\n\t\t};\n\t\tp0[2] = new TestPartyPokemon(settings, PBESpecies.Feebas, 0, 100, PBEMove.Splash);\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Darkrai, 0, 100, PBEMove.Splash);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Double, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBEBattlePokemon magikarp = t0.Party[0];\n\t\tPBEBattlePokemon absol = t0.Party[1];\n\t\tPBEBattlePokemon feebas = t0.Party[2];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Check\n\t\tAssert.True(magikarp.FieldPosition == PBEFieldPosition.Left\n\t\t\t&& absol.FieldPosition == PBEFieldPosition.None\n\t\t\t&& feebas.FieldPosition == PBEFieldPosition.Right);\n\t\t#endregion\n\t}\n\n\t[Fact]\n\tpublic void Fainted_Pkmn_Not_Sent_Out_Triple()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPartyPokemonCollection(5);\n\t\tp0[0] = new TestPartyPokemon(settings, PBESpecies.Magikarp, 0, 100, PBEMove.Splash)\n\t\t{\n\t\t\tHP = 0\n\t\t};\n\t\tp0[1] = new TestPartyPokemon(settings, PBESpecies.Absol, 0, 100, PBEMove.Splash)\n\t\t{\n\t\t\tHP = 0\n\t\t};\n\t\tp0[2] = new TestPartyPokemon(settings, PBESpecies.Feebas, 0, 100, PBEMove.Splash)\n\t\t{\n\t\t\tHP = 0\n\t\t};\n\t\tp0[3] = new TestPartyPokemon(settings, PBESpecies.Happiny, 0, 100, PBEMove.Splash);\n\t\tp0[4] = new TestPartyPokemon(settings, PBESpecies.Gastly, 0, 100, PBEMove.Splash);\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Darkrai, 0, 100, PBEMove.Splash);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Triple, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBEBattlePokemon magikarp = t0.Party[0];\n\t\tPBEBattlePokemon absol = t0.Party[1];\n\t\tPBEBattlePokemon feebas = t0.Party[2];\n\t\tPBEBattlePokemon happiny = t0.Party[3];\n\t\tPBEBattlePokemon gastly = t0.Party[4];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Check\n\t\tAssert.True(magikarp.FieldPosition == PBEFieldPosition.None\n\t\t\t&& absol.FieldPosition == PBEFieldPosition.None\n\t\t\t&& feebas.FieldPosition == PBEFieldPosition.None\n\t\t\t&& happiny.FieldPosition == PBEFieldPosition.Left\n\t\t\t&& gastly.FieldPosition == PBEFieldPosition.Center);\n\t\t#endregion\n\t}\n\n\t[Fact]\n\tpublic void Lose_If_Remaining_Ignored()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(2);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Koffing, 0, 100, PBEMove.Selfdestruct);\n\t\tp0[1] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 1, PBEMove.Splash)\n\t\t{\n\t\t\tPBEIgnore = true\n\t\t};\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Darkrai, 0, 100, PBEMove.Protect);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon koffing = t0.Party[0];\n\t\tPBEBattlePokemon magikarp = t0.Party[1];\n\t\tPBEBattlePokemon darkrai = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Darkrai uses Protect, Koffing uses Selfdestruct and faints\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(koffing, PBEMove.Selfdestruct, PBETurnTarget.FoeCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(darkrai, PBEMove.Protect, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\t\t#endregion\n\n\t\t#region Check\n\t\tAssert.True(koffing.HP == 0 && magikarp.HP > 0\n\t\t\t&& battle.BattleResult == PBEBattleResult.Team1Win); // Koffing's team loses\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineTests/Forms/CastformCherrimTests.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace Kermalis.PokemonBattleEngineTests.Forms;\n\n[Collection(\"Utils\")]\npublic class CastformCherrimTests\n{\n\tpublic CastformCherrimTests(TestUtils _, ITestOutputHelper output)\n\t{\n\t\tTestUtils.SetOutputHelper(output);\n\t}\n\n\t[Theory]\n\t[InlineData(PBESpecies.Castform, PBEAbility.Forecast, PBEForm.Castform_Sunny)]\n\t[InlineData(PBESpecies.Cherrim, PBEAbility.FlowerGift, PBEForm.Cherrim_Sunshine)]\n\tpublic void CastformCherrim_Interacts_With_AirLock(PBESpecies species, PBEAbility ability, PBEForm form)\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(2);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 100, PBEMove.Splash);\n\t\tp0[1] = new TestPokemon(settings, PBESpecies.Rayquaza, 0, 100, PBEMove.Splash)\n\t\t{\n\t\t\tAbility = PBEAbility.AirLock\n\t\t};\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, species, 0, 100, PBEMove.Splash)\n\t\t{\n\t\t\tAbility = ability\n\t\t};\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false),\n\t\t\t\tweather: PBEWeather.HarshSunlight);\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon magikarp = t0.Party[0];\n\t\tPBEBattlePokemon rayquaza = t0.Party[1];\n\t\tPBEBattlePokemon castformCherrim = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Check Castform/Cherrim for correct form\n\t\tAssert.True(battle.Weather == PBEWeather.HarshSunlight && castformCherrim.Form == form);\n\t\t#endregion\n\n\t\t#region Swap Magikarp for Rayquaza and check for no form\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(magikarp, rayquaza)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(castformCherrim, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(battle.Weather == PBEWeather.HarshSunlight && castformCherrim.Form == 0);\n\t\t#endregion\n\n\t\t#region Swap Rayquaza for Magikarp and check for correct form\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(rayquaza, magikarp)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(castformCherrim, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(battle.Weather == PBEWeather.HarshSunlight && castformCherrim.Form == form);\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t[Theory]\n\t[InlineData(PBESpecies.Castform, PBEAbility.Forecast, PBEForm.Castform_Sunny)]\n\t[InlineData(PBESpecies.Cherrim, PBEAbility.FlowerGift, PBEForm.Cherrim_Sunshine)]\n\tpublic void CastformCherrim_Loses_Form(PBESpecies species, PBEAbility ability, PBEForm form)\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Shuckle, 0, 100, PBEMove.GastroAcid, PBEMove.Splash);\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, species, 0, 100, PBEMove.Splash, PBEMove.SunnyDay)\n\t\t{\n\t\t\tAbility = ability\n\t\t};\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon shuckle = t0.Party[0];\n\t\tPBEBattlePokemon castformCherrim = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use Sunny Day and check for correct form\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(shuckle, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(castformCherrim, PBEMove.SunnyDay, PBETurnTarget.AllyCenter | PBETurnTarget.FoeCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(battle.Weather == PBEWeather.HarshSunlight && castformCherrim.Form == form);\n\t\t#endregion\n\n\t\t#region Use Gastro Acid and check for no form\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(shuckle, PBEMove.GastroAcid, PBETurnTarget.FoeCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(castformCherrim, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(battle.Weather == PBEWeather.HarshSunlight && castformCherrim.Form == 0);\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineTests/Forms/ShayminTests.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace Kermalis.PokemonBattleEngineTests.Forms;\n\n[Collection(\"Utils\")]\npublic class ShayminTests\n{\n\tpublic ShayminTests(TestUtils _, ITestOutputHelper output)\n\t{\n\t\tTestUtils.SetOutputHelper(output);\n\t}\n\n\t[Fact]\n\tpublic void Shaymin_Reverts_To_Normal_Form_Forever()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0; // Seed ensures SecretPower freezes\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Happiny, 0, 100, PBEMove.SecretPower, PBEMove.Splash)\n\t\t{\n\t\t\tAbility = PBEAbility.SereneGrace\n\t\t};\n\n\t\tvar p1 = new TestPokemonCollection(2);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Shaymin, PBEForm.Shaymin_Sky, 100, PBEMove.Splash);\n\t\tp1[1] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 100, PBEMove.Splash);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false),\n\t\t\t\tbattleTerrain: PBEBattleTerrain.Snow);\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon happiny = t0.Party[0];\n\t\tPBEBattlePokemon shaymin = t1.Party[0];\n\t\tPBEBattlePokemon magikarp = t1.Party[1];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Freeze Shaymin\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(happiny, PBEMove.SecretPower, PBETurnTarget.FoeCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(shaymin, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(shaymin.Status1 == PBEStatus1.Frozen && shaymin.Form == PBEForm.Shaymin);\n\t\t#endregion\n\n\t\t#region Swap Shaymin for Magikarp and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(happiny, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(shaymin, magikarp)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(shaymin.Form == PBEForm.Shaymin);\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineTests/Items/GemTests.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace Kermalis.PokemonBattleEngineTests.Items;\n\n[Collection(\"Utils\")]\npublic class GemTests\n{\n\tpublic GemTests(TestUtils _, ITestOutputHelper output)\n\t{\n\t\tTestUtils.SetOutputHelper(output);\n\t}\n\n\t// TODO: Do gems activate even if Explosion hits nobody?\n\t// TODO: Do gems activate for WaterAbsorb, LightningRod, etc?\n\n\t[Theory]\n\t[InlineData(PBEMove.Megahorn, PBEItem.BugGem)]\n\t[InlineData(PBEMove.FoulPlay, PBEItem.DarkGem)]\n\t[InlineData(PBEMove.DracoMeteor, PBEItem.DragonGem)]\n\t[InlineData(PBEMove.BoltStrike, PBEItem.ElectricGem)]\n\t[InlineData(PBEMove.Superpower, PBEItem.FightingGem)]\n\t[InlineData(PBEMove.Eruption, PBEItem.FireGem)]\n\t[InlineData(PBEMove.Hurricane, PBEItem.FlyingGem)]\n\t[InlineData(PBEMove.ShadowBall, PBEItem.GhostGem)]\n\t[InlineData(PBEMove.LeafStorm, PBEItem.GrassGem)]\n\t[InlineData(PBEMove.Earthquake, PBEItem.GroundGem)]\n\t[InlineData(PBEMove.Blizzard, PBEItem.IceGem)]\n\t[InlineData(PBEMove.MegaKick, PBEItem.NormalGem)]\n\t[InlineData(PBEMove.GunkShot, PBEItem.PoisonGem)]\n\t[InlineData(PBEMove.PsychoBoost, PBEItem.PsychicGem)]\n\t[InlineData(PBEMove.StoneEdge, PBEItem.RockGem)]\n\t[InlineData(PBEMove.IronTail, PBEItem.SteelGem)]\n\t[InlineData(PBEMove.WaterSpout, PBEItem.WaterGem)]\n\tpublic void Gem_Works(PBEMove move, PBEItem item)\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 4; // Seed ensures all moves do not miss\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Mew, 0, 1, move)\n\t\t{\n\t\t\tItem = item\n\t\t};\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 100, PBEMove.Splash);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon mew = t0.Party[0];\n\t\tPBEBattlePokemon magikarp = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(mew, move, PBETurnTarget.FoeCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(magikarp, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(!battle.VerifyMoveResultHappened(mew, magikarp, PBEResult.Missed) // Did not miss\n\t\t\t&& battle.VerifyItemHappened(mew, mew, item, PBEItemAction.Consumed) // Gem consumed\n\t\t\t&& mew.Item == PBEItem.None); // Properly removed\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t[Theory]\n\t[InlineData(true)]\n\t[InlineData(false)]\n\tpublic void Gem_Activates__FixedDamage__Bug(bool bugFix)\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tvar settings = new PBESettings { BugFix = bugFix };\n\t\tsettings.MakeReadOnly();\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Swellow, 0, 1, PBEMove.Endeavor)\n\t\t{\n\t\t\tItem = PBEItem.NormalGem\n\t\t};\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 100, PBEMove.Splash);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon swellow = t0.Party[0];\n\t\tPBEBattlePokemon magikarp = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(swellow, PBEMove.Endeavor, PBETurnTarget.FoeCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(magikarp, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tif (settings.BugFix)\n\t\t{\n\t\t\tAssert.True(!battle.VerifyItemHappened(swellow, swellow, PBEItem.NormalGem, PBEItemAction.Consumed)\n\t\t\t\t&& swellow.Item == PBEItem.NormalGem); // Not consumed\n\t\t}\n\t\telse\n\t\t{\n\t\t\tAssert.True(battle.VerifyItemHappened(swellow, swellow, PBEItem.NormalGem, PBEItemAction.Consumed)\n\t\t\t\t&& swellow.Item == PBEItem.None); // Buggy\n\t\t}\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t[Fact]\n\tpublic void Gem_Does_Not_Activate__Effectiveness()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(2);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Excadrill, 0, 100, PBEMove.Earthquake)\n\t\t{\n\t\t\tItem = PBEItem.GroundGem\n\t\t};\n\t\tp0[1] = new TestPokemon(settings, PBESpecies.Starly, 0, 1, PBEMove.Splash);\n\n\t\tvar p1 = new TestPokemonCollection(2);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Rotom, 0, 1, PBEMove.Splash)\n\t\t{\n\t\t\tAbility = PBEAbility.Levitate\n\t\t};\n\t\tp1[1] = new TestPokemon(settings, PBESpecies.Shedinja, 0, 1, PBEMove.Splash)\n\t\t{\n\t\t\tAbility = PBEAbility.WonderGuard\n\t\t};\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Double, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon excadrill = t0.Party[0];\n\t\tPBEBattlePokemon starly = t0.Party[1];\n\t\tPBEBattlePokemon rotom = t1.Party[0];\n\t\tPBEBattlePokemon shedinja = t1.Party[1];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(excadrill, PBEMove.Earthquake, PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeRight),\n\t\t\tnew PBETurnAction(starly, PBEMove.Splash, PBETurnTarget.AllyRight)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(rotom, PBEMove.Splash, PBETurnTarget.AllyLeft),\n\t\t\tnew PBETurnAction(shedinja, PBEMove.Splash, PBETurnTarget.AllyRight)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(battle.VerifyMoveResultHappened(excadrill, starly, PBEResult.Ineffective_Type) && starly.HPPercentage == 1 // Doesn't affect Flying\n\t\t\t&& battle.VerifyMoveResultHappened(excadrill, rotom, PBEResult.Ineffective_Ability) && rotom.HPPercentage == 1 // Doesn't affect Levitate\n\t\t\t&& battle.VerifyMoveResultHappened(excadrill, shedinja, PBEResult.Ineffective_Ability) && shedinja.HPPercentage == 1 // Doesn't affect WonderGuard\n\t\t\t&& excadrill.Item == PBEItem.GroundGem); // Gem not consumed\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t// Failing is technically \"effectiveness\", but here's another test just because I can\n\t[Fact]\n\tpublic void Gem_Does_Not_Activate__Fail()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Spiritomb, 0, 100, PBEMove.SuckerPunch)\n\t\t{\n\t\t\tItem = PBEItem.DarkGem\n\t\t};\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 1, PBEMove.Splash);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon spiritomb = t0.Party[0];\n\t\tPBEBattlePokemon magikarp = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(spiritomb, PBEMove.SuckerPunch, PBETurnTarget.FoeCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(magikarp, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(battle.VerifyMoveResultHappened(spiritomb, magikarp, PBEResult.InvalidConditions) && magikarp.HPPercentage == 1 // Fail\n\t\t\t&& spiritomb.Item == PBEItem.DarkGem); // Gem not consumed\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t[Fact]\n\tpublic void Gem_Does_Not_Activate__Miss()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 2; // Seed ensures all miss\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(2);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Corsola, 0, 100, PBEMove.Earthquake)\n\t\t{\n\t\t\tAbility = PBEAbility.Hustle,\n\t\t\tItem = PBEItem.GroundGem\n\t\t};\n\t\tp0[1] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 1, PBEMove.Splash);\n\n\t\tvar p1 = new TestPokemonCollection(2);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Happiny, 0, 1, PBEMove.Splash);\n\t\tp1[1] = new TestPokemon(settings, PBESpecies.Qwilfish, 0, 1, PBEMove.Splash);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Double, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon corsola = t0.Party[0];\n\t\tPBEBattlePokemon magikarp = t0.Party[1];\n\t\tPBEBattlePokemon happiny = t1.Party[0];\n\t\tPBEBattlePokemon qwilfish = t1.Party[1];\n\t\tcorsola.AccuracyChange = (sbyte)-settings.MaxStatChange;\n\t\tmagikarp.EvasionChange = settings.MaxStatChange;\n\t\thappiny.EvasionChange = settings.MaxStatChange;\n\t\tqwilfish.EvasionChange = settings.MaxStatChange;\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(corsola, PBEMove.Earthquake, PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeRight),\n\t\t\tnew PBETurnAction(magikarp, PBEMove.Splash, PBETurnTarget.AllyRight)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(happiny, PBEMove.Splash, PBETurnTarget.AllyLeft),\n\t\t\tnew PBETurnAction(qwilfish, PBEMove.Splash, PBETurnTarget.AllyRight)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(battle.VerifyMoveResultHappened(corsola, magikarp, PBEResult.Missed) && magikarp.HPPercentage == 1 // Miss everyone\n\t\t\t&& battle.VerifyMoveResultHappened(corsola, happiny, PBEResult.Missed) && happiny.HPPercentage == 1\n\t\t\t&& battle.VerifyMoveResultHappened(corsola, qwilfish, PBEResult.Missed) && qwilfish.HPPercentage == 1\n\t\t\t&& corsola.Item == PBEItem.GroundGem); // Gem not consumed\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t// Protection is technically \"missing\", but here's another test just because I can\n\t[Fact]\n\tpublic void Gem_Does_Not_Activate__Protection()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Excadrill, 0, 100, PBEMove.Earthquake)\n\t\t{\n\t\t\tItem = PBEItem.GroundGem\n\t\t};\n\n\t\tvar p1 = new TestPokemonCollection(2);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Happiny, 0, 1, PBEMove.WideGuard);\n\t\tp1[1] = new TestPokemon(settings, PBESpecies.Qwilfish, 0, 1, PBEMove.Splash);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Double, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon excadrill = t0.Party[0];\n\t\tPBEBattlePokemon happiny = t1.Party[0];\n\t\tPBEBattlePokemon qwilfish = t1.Party[1];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(excadrill, PBEMove.Earthquake, PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeRight)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(happiny, PBEMove.WideGuard, PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight),\n\t\t\tnew PBETurnAction(qwilfish, PBEMove.Splash, PBETurnTarget.AllyRight)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(battle.VerifyTeamStatusDamageHappened(t1.Team, PBETeamStatus.WideGuard, happiny) && happiny.HPPercentage == 1\n\t\t\t&& battle.VerifyTeamStatusDamageHappened(t1.Team, PBETeamStatus.WideGuard, qwilfish) && qwilfish.HPPercentage == 1\n\t\t\t&& excadrill.Item == PBEItem.GroundGem); // Gem not consumed\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineTests/Moves/BellyDrumTests.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace Kermalis.PokemonBattleEngineTests.Moves;\n\n[Collection(\"Utils\")]\npublic class BellyDrumTests\n{\n\tpublic BellyDrumTests(TestUtils _, ITestOutputHelper output)\n\t{\n\t\tTestUtils.SetOutputHelper(output);\n\t}\n\n\t[Theory]\n\t[InlineData(true)]\n\t[InlineData(false)]\n\tpublic void BellyDrum_Contrary__Bug(bool bugFix)\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tvar settings = new PBESettings { BugFix = bugFix };\n\t\tsettings.MakeReadOnly();\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Hariyama, 0, 100, PBEMove.BellyDrum)\n\t\t{\n\t\t\tAbility = PBEAbility.Contrary\n\t\t};\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 100, PBEMove.Splash);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon hariyama = t0.Party[0];\n\t\tPBEBattlePokemon magikarp = t1.Party[0];\n\t\thariyama.AttackChange = settings.MaxStatChange;\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(hariyama, PBEMove.BellyDrum, PBETurnTarget.AllyCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(magikarp, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tif (settings.BugFix)\n\t\t{\n\t\t\tAssert.True(!battle.VerifyMoveResultHappened(hariyama, hariyama, PBEResult.InvalidConditions)\n\t\t\t\t&& hariyama.AttackChange == -settings.MaxStatChange); // Stat minimized because of Contrary\n\t\t}\n\t\telse\n\t\t{\n\t\t\tAssert.True(battle.VerifyMoveResultHappened(hariyama, hariyama, PBEResult.InvalidConditions)\n\t\t\t\t&& hariyama.AttackChange == settings.MaxStatChange); // Buggy\n\t\t}\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineTests/Moves/CamouflageTests.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace Kermalis.PokemonBattleEngineTests.Moves;\n\n[Collection(\"Utils\")]\npublic class CamouflageTests\n{\n\tpublic CamouflageTests(TestUtils _, ITestOutputHelper output)\n\t{\n\t\tTestUtils.SetOutputHelper(output);\n\t}\n\n\t[Theory]\n\t[InlineData(PBEBattleTerrain.Cave, PBEType.Rock, PBESpecies.Kecleon)]\n\t[InlineData(PBEBattleTerrain.Cave, PBEType.Rock, PBESpecies.Starmie)]\n\t[InlineData(PBEBattleTerrain.Grass, PBEType.Grass, PBESpecies.Kecleon)]\n\t[InlineData(PBEBattleTerrain.Grass, PBEType.Grass, PBESpecies.Starmie)]\n\t[InlineData(PBEBattleTerrain.Plain, PBEType.Normal, PBESpecies.Kecleon)]\n\t[InlineData(PBEBattleTerrain.Plain, PBEType.Normal, PBESpecies.Starmie)]\n\t[InlineData(PBEBattleTerrain.Puddle, PBEType.Ground, PBESpecies.Kecleon)]\n\t[InlineData(PBEBattleTerrain.Puddle, PBEType.Ground, PBESpecies.Starmie)]\n\t[InlineData(PBEBattleTerrain.Sand, PBEType.Ground, PBESpecies.Kecleon)]\n\t[InlineData(PBEBattleTerrain.Sand, PBEType.Ground, PBESpecies.Starmie)]\n\t[InlineData(PBEBattleTerrain.Snow, PBEType.Ice, PBESpecies.Kecleon)]\n\t[InlineData(PBEBattleTerrain.Snow, PBEType.Ice, PBESpecies.Starmie)]\n\t[InlineData(PBEBattleTerrain.Water, PBEType.Water, PBESpecies.Kecleon)]\n\t[InlineData(PBEBattleTerrain.Water, PBEType.Water, PBESpecies.Starmie)]\n\tpublic void Camouflage_Works(PBEBattleTerrain battleTerrain, PBEType expectedType, PBESpecies species)\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, species, 0, 100, PBEMove.Camouflage);\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 100, PBEMove.Splash);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false),\n\t\t\t\tbattleTerrain: battleTerrain);\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon camouflager = t0.Party[0];\n\t\tPBEBattlePokemon magikarp = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use Camouflage and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(camouflager, PBEMove.Camouflage, PBETurnTarget.AllyCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(magikarp, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(camouflager.Type1 == expectedType && camouflager.Type2 == PBEType.None);\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t[Fact]\n\tpublic void Camouflage_Fails()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Staryu, 0, 100, PBEMove.Camouflage);\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 100, PBEMove.Splash);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false),\n\t\t\t\tbattleTerrain: PBEBattleTerrain.Water);\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon staryu = t0.Party[0];\n\t\tPBEBattlePokemon magikarp = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use Camouflage and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(staryu, PBEMove.Camouflage, PBETurnTarget.AllyCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(magikarp, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(battle.VerifyMoveResultHappened(staryu, staryu, PBEResult.InvalidConditions));\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineTests/Moves/HelpingHandTests.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace Kermalis.PokemonBattleEngineTests.Moves;\n\n[Collection(\"Utils\")]\npublic class HelpingHandTests\n{\n\tpublic HelpingHandTests(TestUtils _, ITestOutputHelper output)\n\t{\n\t\tTestUtils.SetOutputHelper(output);\n\t}\n\n\t// https://github.com/Kermalis/PokemonBattleEngine/issues/308\n\t[Theory]\n\t[InlineData(PBEMove.Bounce, PBEStatus2.Airborne)]\n\t[InlineData(PBEMove.Dig, PBEStatus2.Underground)]\n\t[InlineData(PBEMove.Dive, PBEStatus2.Underwater)]\n\t[InlineData(PBEMove.Fly, PBEStatus2.Airborne)]\n\t[InlineData(PBEMove.ShadowForce, PBEStatus2.ShadowForce)]\n\t//[InlineData(PBEMove.SkyDrop, PBEStatus2.Airborne)]\n\tpublic void HelpingHand_HitsSemiInvulnerable(PBEMove move, PBEStatus2 status2)\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(2);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Minun, 0, 100, PBEMove.HelpingHand, PBEMove.Splash);\n\t\tp0[1] = new TestPokemon(settings, PBESpecies.Giratina, 0, 1, move);\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 100, PBEMove.Splash);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Double, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon minun = t0.Party[0];\n\t\tPBEBattlePokemon giratina = t0.Party[1];\n\t\tPBEBattlePokemon magikarp = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use Shadow Force\n\t\tAssert.True(t0.SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(minun, PBEMove.Splash, PBETurnTarget.AllyLeft),\n\t\t\tnew PBETurnAction(giratina, move, PBETurnTarget.FoeLeft)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(magikarp, PBEMove.Splash, PBETurnTarget.AllyLeft)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(giratina.Status2.HasFlag(status2));\n\t\t#endregion\n\n\t\t#region Use Helping Hand and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(minun, PBEMove.HelpingHand, PBETurnTarget.AllyRight),\n\t\t\tnew PBETurnAction(giratina, move, PBETurnTarget.FoeLeft)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(magikarp, PBEMove.Splash, PBETurnTarget.AllyLeft)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(battle.VerifyStatus2Happened(giratina, minun, PBEStatus2.HelpingHand, PBEStatusAction.Added));\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t[Fact]\n\tpublic void HelpingHand_Fails_If_Self()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Minun, 0, 100, PBEMove.HelpingHand);\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 100, PBEMove.Splash);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon minun = t0.Party[0];\n\t\tPBEBattlePokemon magikarp = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use Helping Hand and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(minun, PBEMove.HelpingHand, PBETurnTarget.AllyCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(magikarp, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(battle.VerifyMoveResultHappened(minun, minun, PBEResult.NoTarget) // Fail\n\t\t\t&& !minun.Status2.HasFlag(PBEStatus2.HelpingHand)); // No status\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineTests/Moves/MultiStrikeTests.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace Kermalis.PokemonBattleEngineTests.Moves;\n\n[Collection(\"Utils\")]\npublic class MultiStrikeTests\n{\n\tpublic MultiStrikeTests(TestUtils _, ITestOutputHelper output)\n\t{\n\t\tTestUtils.SetOutputHelper(output);\n\t}\n\n\t[Theory]\n\t[InlineData(PBEAbility.Technician, 2)]\n\t[InlineData(PBEAbility.SkillLink, 5)]\n\tpublic void SkillLink_Works__2To5(PBEAbility ability, byte numHits)\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 1230; // Seed ensures hits would normally not be 5\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Cinccino, 0, 1, PBEMove.TailSlap)\n\t\t{\n\t\t\tAbility = ability\n\t\t};\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 100, PBEMove.Splash);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon cinccino = t0.Party[0];\n\t\tPBEBattlePokemon magikarp = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(cinccino, PBEMove.TailSlap, PBETurnTarget.FoeCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(magikarp, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(battle.VerifySpecialMessageHappened(PBESpecialMessage.MultiHit, numHits)); // Correct number of hits\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineTests/Moves/ProtectionTests.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace Kermalis.PokemonBattleEngineTests.Moves;\n\n[Collection(\"Utils\")]\npublic class ProtectionTests\n{\n\tpublic ProtectionTests(TestUtils _, ITestOutputHelper output)\n\t{\n\t\tTestUtils.SetOutputHelper(output);\n\t}\n\n\t// https://github.com/Kermalis/PokemonBattleEngine/issues/261\n\t[Theory]\n\t[InlineData(PBEMove.Detect)]\n\t[InlineData(PBEMove.Protect)]\n\t[InlineData(PBEMove.QuickGuard)]\n\t[InlineData(PBEMove.WideGuard)]\n\tpublic void Protection_Counter_Resets(PBEMove move)\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Mienshao, 0, 100, move, PBEMove.CalmMind);\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 100, PBEMove.Splash);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon mienshao = t0.Party[0];\n\t\tPBEBattlePokemon magikarp = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use move\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(mienshao, move, PBETurnTarget.AllyCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(magikarp, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(mienshao.Protection_Counter == 1);\n\t\t#endregion\n\n\t\t#region Use Calm Mind and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(mienshao, PBEMove.CalmMind, PBETurnTarget.AllyCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(magikarp, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(mienshao.Protection_Counter == 0);\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t[Theory]\n\t[InlineData(true)]\n\t[InlineData(false)]\n\tpublic void Feint_And_QuickGuard(bool ally)\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(2);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Lucario, 0, 100, PBEMove.Feint);\n\t\tp0[1] = new TestPokemon(settings, PBESpecies.Mienshao, 0, 100, PBEMove.QuickGuard);\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.MrMime, 0, 100, PBEMove.QuickGuard);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Double, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon lucario = t0.Party[0];\n\t\tPBEBattlePokemon mienshao = t0.Party[1];\n\t\tPBEBattlePokemon mrmime = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use move and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(lucario, PBEMove.Feint, ally ? PBETurnTarget.AllyRight : PBETurnTarget.FoeLeft),\n\t\t\tnew PBETurnAction(mienshao, PBEMove.QuickGuard, PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(mrmime, PBEMove.QuickGuard, PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(battle.VerifyTeamStatusDamageHappened((ally ? t0 : t1).Team, PBETeamStatus.QuickGuard, ally ? mienshao : mrmime) == ally);\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t[Fact]\n\tpublic void UserProtection_Works()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Lucario, 0, 100, PBEMove.Tackle);\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Mienshao, 0, 100, PBEMove.Protect);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon lucario = t0.Party[0];\n\t\tPBEBattlePokemon mienshao = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use move and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(lucario, PBEMove.Tackle, PBETurnTarget.FoeCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(mienshao, PBEMove.Protect, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(battle.VerifyStatus2Happened(mienshao, lucario, PBEStatus2.Protected, PBEStatusAction.Damage) && !mienshao.Status2.HasFlag(PBEStatus2.Protected));\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t[Theory]\n\t[InlineData(PBEMove.QuickGuard, PBEMove.QuickAttack, PBETeamStatus.QuickGuard)]\n\t[InlineData(PBEMove.WideGuard, PBEMove.Earthquake, PBETeamStatus.WideGuard)]\n\tpublic void TeamProtection_Works(PBEMove move, PBEMove move2, PBETeamStatus teamStatus)\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Mienshao, 0, 100, move);\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Lucario, 0, 100, move2);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon mienshao = t0.Party[0];\n\t\tPBEBattlePokemon lucario = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use move and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(mienshao, move, PBETurnTarget.AllyCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(lucario, move2, PBETurnTarget.FoeCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(battle.VerifyTeamStatusDamageHappened(t0.Team, teamStatus, mienshao)\n\t\t\t&& !t0.Team.TeamStatus.HasFlag(teamStatus));\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineTests/Moves/RoostTests.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace Kermalis.PokemonBattleEngineTests.Moves;\n\n[Collection(\"Utils\")]\npublic class RoostTests\n{\n\tpublic RoostTests(TestUtils _, ITestOutputHelper output)\n\t{\n\t\tTestUtils.SetOutputHelper(output);\n\t}\n\n\t[Theory]\n\t[InlineData(PBESpecies.Mew, false)] // Non-Flying single-type\n\t[InlineData(PBESpecies.Tornadus, false)] // Flying single-type\n\t[InlineData(PBESpecies.Volcarona, false)] // Non-Flying dual-type\n\t[InlineData(PBESpecies.Gyarados, true)] // Flying dual-type-primary\n\t[InlineData(PBESpecies.Gyarados, false)] // Flying dual-type-secondary\n\tpublic void Roost_Works(PBESpecies species, bool swapTypes) // Swap types around since there's no primary flying-type\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Lucario, 0, 50, PBEMove.Earthquake, PBEMove.VacuumWave);\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, species, 0, 100, PBEMove.Roost, PBEMove.Splash);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon lucario = t0.Party[0];\n\t\tPBEBattlePokemon rooster = t1.Party[0];\n\t\tPBEType type1 = rooster.Type1;\n\t\tPBEType type2 = rooster.Type2;\n\t\tif (swapTypes)\n\t\t{\n\t\t\trooster.Type1 = type2;\n\t\t\trooster.Type2 = type1;\n\t\t\trooster.KnownType1 = type2;\n\t\t\trooster.KnownType2 = type1;\n\t\t}\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use VacuumWave to lower HP\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(lucario, PBEMove.VacuumWave, PBETurnTarget.FoeCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(rooster, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\t\t#endregion\n\n\t\t#region Use Roost and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(lucario, PBEMove.Earthquake, PBETurnTarget.FoeCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(rooster, PBEMove.Roost, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(!battle.VerifyMoveResultHappened(lucario, rooster, PBEResult.Ineffective_Type) // Earthquake hit\n\t\t\t&& !rooster.Status2.HasFlag(PBEStatus2.Roost) // Roost ended properly\n\t\t\t&& rooster.Type1 == type1 && rooster.Type2 == type2 // Types restored properly\n\t\t\t&& rooster.KnownType1 == type1 && rooster.KnownType2 == type2);\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineTests/Moves/SecretPowerTests.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace Kermalis.PokemonBattleEngineTests.Moves;\n\n[Collection(\"Utils\")]\npublic class SecretPowerTests\n{\n\tpublic SecretPowerTests(TestUtils _, ITestOutputHelper output)\n\t{\n\t\tTestUtils.SetOutputHelper(output);\n\t}\n\n\t[Theory]\n\t[InlineData(true)]\n\t[InlineData(false)]\n\tpublic void SecretPower_SereneGrace__Bug(bool bugFix)\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 2; // Seed ensures SecretPower does not freeze without the bugfix\n\t\tvar settings = new PBESettings { BugFix = bugFix };\n\t\tsettings.MakeReadOnly();\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Makuhita, 0, 1, PBEMove.SecretPower)\n\t\t{\n\t\t\tAbility = PBEAbility.SereneGrace\n\t\t};\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 100, PBEMove.Splash);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false),\n\t\t\t\tbattleTerrain: PBEBattleTerrain.Snow);\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon makuhita = t0.Party[0];\n\t\tPBEBattlePokemon magikarp = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(makuhita, PBEMove.SecretPower, PBETurnTarget.FoeCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(magikarp, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tif (settings.BugFix)\n\t\t{\n\t\t\tAssert.True(battle.VerifyStatus1Happened(magikarp, makuhita, PBEStatus1.Frozen, PBEStatusAction.Added)\n\t\t\t\t&& magikarp.Status1 == PBEStatus1.Frozen); // Frozen because of Serene Grace\n\t\t}\n\t\telse\n\t\t{\n\t\t\tAssert.True(!battle.VerifyStatus1Happened(magikarp, makuhita, PBEStatus1.Frozen, PBEStatusAction.Added)\n\t\t\t\t&& magikarp.Status1 == PBEStatus1.None); // Buggy\n\t\t}\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineTests/Moves/TeleportTests.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace Kermalis.PokemonBattleEngineTests.Moves;\n\n[Collection(\"Utils\")]\npublic class TeleportTests\n{\n\tpublic TeleportTests(TestUtils _, ITestOutputHelper output)\n\t{\n\t\tTestUtils.SetOutputHelper(output);\n\t}\n\n\t[Fact]\n\tpublic void Teleport_Works()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Abra, 0, 100, PBEMove.Teleport);\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 1, PBEMove.Splash)\n\t\t{\n\t\t\tCaughtBall = PBEItem.None\n\t\t};\n\n\t\tvar battle = PBEBattle.CreateWildBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBEWildInfo(p1));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon abra = t0.Party[0];\n\t\tPBEBattlePokemon magikarp = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use Teleport and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(abra, PBEMove.Teleport, PBETurnTarget.AllyCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(magikarp, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(!battle.VerifyMoveResultHappened(abra, abra, PBEResult.InvalidConditions) // No fail\n\t\t\t&& battle.BattleResult == PBEBattleResult.WildEscape); // Escaped\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineTests/Moves/WhirlwindTests.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace Kermalis.PokemonBattleEngineTests.Moves;\n\n[Collection(\"Utils\")]\npublic class WhirlwindTests\n{\n\tpublic WhirlwindTests(TestUtils _, ITestOutputHelper output)\n\t{\n\t\tTestUtils.SetOutputHelper(output);\n\t}\n\n\t[Fact]\n\tpublic void Whirlwind_FailsLevel_WildSingleBattle()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Tropius, 0, 1, PBEMove.Whirlwind);\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 100, PBEMove.Splash)\n\t\t{\n\t\t\tCaughtBall = PBEItem.None\n\t\t};\n\n\t\tvar battle = PBEBattle.CreateWildBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBEWildInfo(p1));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon tropius = t0.Party[0];\n\t\tPBEBattlePokemon magikarp = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use Whirlwind and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(tropius, PBEMove.Whirlwind, PBETurnTarget.FoeCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(magikarp, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(battle.VerifyMoveResultHappened(tropius, magikarp, PBEResult.Ineffective_Level) // Fail\n\t\t\t&& battle.BattleResult is null); // Did not flee\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t[Fact]\n\tpublic void Whirlwind_Fails_WildDoubleBattle()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Tropius, 0, 100, PBEMove.Whirlwind);\n\n\t\tvar p1 = new TestPokemonCollection(2);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 1, PBEMove.Splash)\n\t\t{\n\t\t\tCaughtBall = PBEItem.None\n\t\t};\n\t\tp1[1] = new TestPokemon(settings, PBESpecies.Happiny, 0, 1, PBEMove.Splash)\n\t\t{\n\t\t\tCaughtBall = PBEItem.None\n\t\t};\n\n\t\tvar battle = PBEBattle.CreateWildBattle(PBEBattleFormat.Double, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBEWildInfo(p1));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon tropius = t0.Party[0];\n\t\tPBEBattlePokemon magikarp = t1.Party[0];\n\t\tPBEBattlePokemon happiny = t1.Party[1];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use Whirlwind and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(tropius, PBEMove.Whirlwind, PBETurnTarget.FoeLeft)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(magikarp, PBEMove.Splash, PBETurnTarget.AllyLeft),\n\t\t\tnew PBETurnAction(happiny, PBEMove.Splash, PBETurnTarget.AllyRight)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(battle.VerifyMoveResultHappened(tropius, magikarp, PBEResult.InvalidConditions) // Fail\n\t\t\t&& battle.BattleResult is null); // Did not flee\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t[Fact]\n\tpublic void Whirlwind_Succeeds_WildSingleBattle()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Tropius, 0, 100, PBEMove.Whirlwind);\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 1, PBEMove.Splash)\n\t\t{\n\t\t\tCaughtBall = PBEItem.None\n\t\t};\n\n\t\tvar battle = PBEBattle.CreateWildBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBEWildInfo(p1));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon tropius = t0.Party[0];\n\t\tPBEBattlePokemon magikarp = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use Whirlwind and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(tropius, PBEMove.Whirlwind, PBETurnTarget.FoeCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(magikarp, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(!battle.VerifyMoveResultHappened(tropius, magikarp, PBEResult.InvalidConditions) // No fail\n\t\t\t&& battle.BattleResult == PBEBattleResult.WildFlee); // Fled\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t[Fact]\n\tpublic void Whirlwind_Succeeds_WildDoubleBattle()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(3);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Diglett, 0, 1, PBEMove.Splash);\n\t\tp0[1] = new TestPokemon(settings, PBESpecies.Geodude, 0, 1, PBEMove.Splash);\n\t\tp0[2] = new TestPokemon(settings, PBESpecies.Trubbish, 0, 1, PBEMove.Splash);\n\n\t\tvar p1 = new TestPokemonCollection(2);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Starly, 0, 100, PBEMove.Whirlwind)\n\t\t{\n\t\t\tCaughtBall = PBEItem.None\n\t\t};\n\t\tp1[1] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 100, PBEMove.Splash)\n\t\t{\n\t\t\tCaughtBall = PBEItem.None\n\t\t};\n\n\t\tvar battle = PBEBattle.CreateWildBattle(PBEBattleFormat.Double, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBEWildInfo(p1));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon diglett = t0.Party[0];\n\t\tPBEBattlePokemon geodude = t0.Party[1];\n\t\tPBEBattlePokemon trubbish = t0.Party[2];\n\t\tPBEBattlePokemon starly = t1.Party[0];\n\t\tPBEBattlePokemon magikarp = t1.Party[1];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use Whirlwind and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(diglett, PBEMove.Splash, PBETurnTarget.AllyLeft),\n\t\t\tnew PBETurnAction(geodude, PBEMove.Splash, PBETurnTarget.AllyRight)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _,\n\t\t\tnew PBETurnAction(starly, PBEMove.Whirlwind, PBETurnTarget.FoeLeft),\n\t\t\tnew PBETurnAction(magikarp, PBEMove.Splash, PBETurnTarget.AllyRight)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(!battle.VerifyMoveResultHappened(starly, diglett, PBEResult.InvalidConditions) // No fail\n\t\t\t&& diglett.FieldPosition == PBEFieldPosition.None && trubbish.FieldPosition == PBEFieldPosition.Left // Properly swapped\n\t\t\t&& battle.BattleResult is null); // Did not flee\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineTests/PokemonBattleEngineTests.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n\t<PropertyGroup>\n\t\t<TargetFramework>net7.0</TargetFramework>\n\t\t<RootNamespace>Kermalis.PokemonBattleEngineTests</RootNamespace>\n\t\t<Authors>Kermalis</Authors>\n\t\t<Copyright>Kermalis</Copyright>\n\t\t<RepositoryUrl>https://github.com/Kermalis/PokemonBattleEngine</RepositoryUrl>\n\t\t<NoWarn />\n\t\t<IsPackable>false</IsPackable>\n\t\t<IsTestProject>true</IsTestProject>\n\t\t<Nullable>enable</Nullable>\n\t</PropertyGroup>\n\n\t<ItemGroup>\n\t\t<PackageReference Include=\"Microsoft.NET.Test.Sdk\" Version=\"17.4.1\" />\n\t\t<PackageReference Include=\"xunit\" Version=\"2.4.2\" />\n\t\t<PackageReference Include=\"xunit.runner.visualstudio\" Version=\"2.4.5\">\n\t\t\t<PrivateAssets>all</PrivateAssets>\n\t\t\t<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n\t\t</PackageReference>\n\t\t<ProjectReference Include=\"..\\PokemonBattleEngine.DefaultData\\PokemonBattleEngine.DefaultData.csproj\" />\n\t</ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "PokemonBattleEngineTests/Statuses/ConfusionTests.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace Kermalis.PokemonBattleEngineTests.Statuses;\n\n[Collection(\"Utils\")]\npublic class ConfusionTests\n{\n\tpublic ConfusionTests(TestUtils _, ITestOutputHelper output)\n\t{\n\t\tTestUtils.SetOutputHelper(output);\n\t}\n\n\t[Theory]\n\t[InlineData(true)]\n\t[InlineData(false)]\n\tpublic void Confusion_Heal__Bug(bool bugFix)\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0; // Seed ensures Swagger does not miss and Deoxys hurts itself\n\t\tvar settings = new PBESettings { BugFix = bugFix };\n\t\tsettings.MakeReadOnly();\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Deoxys, PBEForm.Deoxys, 50, PBEMove.Splash)\n\t\t{\n\t\t\tItem = PBEItem.SitrusBerry\n\t\t};\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Accelgor, 0, 100, PBEMove.Swagger);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon deoxys = t0.Party[0];\n\t\tPBEBattlePokemon accelgor = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(deoxys, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(accelgor, PBEMove.Swagger, PBETurnTarget.FoeCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(battle.VerifyStatus2Happened(deoxys, accelgor, PBEStatus2.Confused, PBEStatusAction.Added)\n\t\t\t&& battle.VerifyStatus2Happened(deoxys, deoxys, PBEStatus2.Confused, PBEStatusAction.Damage));\n\t\tif (settings.BugFix)\n\t\t{\n\t\t\tAssert.True(battle.VerifyItemHappened(deoxys, deoxys, PBEItem.SitrusBerry, PBEItemAction.Consumed)\n\t\t\t\t&& deoxys.Item == PBEItem.None); // Healed\n\t\t}\n\t\telse\n\t\t{\n\t\t\tAssert.True(!battle.VerifyItemHappened(deoxys, deoxys, PBEItem.SitrusBerry, PBEItemAction.Consumed)\n\t\t\t\t&& deoxys.Item == PBEItem.SitrusBerry); // Buggy\n\t\t}\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n\n\t[Fact]\n\tpublic void Confusion_Does_Not_Ignore_Sturdy()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0; // Seed ensures Swagger does not miss and Deoxys hurts itself\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Deoxys, PBEForm.Deoxys_Attack, 50, PBEMove.Splash)\n\t\t{\n\t\t\tAbility = PBEAbility.Sturdy\n\t\t};\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Accelgor, 0, 100, PBEMove.Swagger);\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon deoxys = t0.Party[0];\n\t\tPBEBattlePokemon accelgor = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(deoxys, PBEMove.Splash, PBETurnTarget.AllyCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(accelgor, PBEMove.Swagger, PBETurnTarget.FoeCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(battle.VerifyStatus2Happened(deoxys, accelgor, PBEStatus2.Confused, PBEStatusAction.Added)\n\t\t\t&& battle.VerifyStatus2Happened(deoxys, deoxys, PBEStatus2.Confused, PBEStatusAction.Damage)\n\t\t\t&& battle.VerifyAbilityHappened(deoxys, deoxys, PBEAbility.Sturdy, PBEAbilityAction.Damage)\n\t\t\t&& deoxys.HP == 1);\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineTests/Statuses/PowerTrickTests.cs",
    "content": "﻿using Xunit;\nusing Xunit.Abstractions;\n\nnamespace Kermalis.PokemonBattleEngineTests.Statuses;\n\n[Collection(\"Utils\")]\npublic class PowerTrickTests\n{\n\tpublic PowerTrickTests(TestUtils _, ITestOutputHelper output)\n\t{\n\t\tTestUtils.SetOutputHelper(output);\n\t}\n\n\t// TODO: BugFix tests\n}\n"
  },
  {
    "path": "PokemonBattleEngineTests/Statuses/SubstituteTests.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace Kermalis.PokemonBattleEngineTests.Statuses;\n\n[Collection(\"Utils\")]\npublic class SubstituteTests\n{\n\tpublic SubstituteTests(TestUtils _, ITestOutputHelper output)\n\t{\n\t\tTestUtils.SetOutputHelper(output);\n\t}\n\n\t[Fact]\n\tpublic void ColorChange_Does_Not_Activate()\n\t{\n\t\t#region Setup\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Conkeldurr, 0, 50, PBEMove.CloseCombat);\n\n\t\tvar p1 = new TestPokemonCollection(1);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Kecleon, 0, 100, PBEMove.Substitute)\n\t\t{\n\t\t\tAbility = PBEAbility.ColorChange\n\t\t};\n\n\t\tvar battle = PBEBattle.CreateTrainerBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBETrainerInfo(p1, \"Trainer 1\", false));\n\t\tbattle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;\n\n\t\tPBETrainer t0 = battle.Trainers[0];\n\t\tPBETrainer t1 = battle.Trainers[1];\n\t\tPBEBattlePokemon conkeldurr = t0.Party[0];\n\t\tPBEBattlePokemon kecleon = t1.Party[0];\n\n\t\tbattle.Begin();\n\t\t#endregion\n\n\t\t#region Use and check\n\t\tAssert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(conkeldurr, PBEMove.CloseCombat, PBETurnTarget.FoeCenter)));\n\t\tAssert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(kecleon, PBEMove.Substitute, PBETurnTarget.AllyCenter)));\n\n\t\tbattle.RunTurn();\n\n\t\tAssert.True(battle.VerifyStatus2Happened(kecleon, kecleon, PBEStatus2.Substitute, PBEStatusAction.Added) // Substitute added\n\t\t\t&& !kecleon.Status2.HasFlag(PBEStatus2.Substitute) // Substitute broke\n\t\t\t&& kecleon.Type1 == PBEType.Normal && kecleon.Type2 == PBEType.None); // ColorChange not activated\n\t\t#endregion\n\n\t\t#region Cleanup\n\t\tbattle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineTests/TestUtils.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing Kermalis.PokemonBattleEngine.Data.Utils;\nusing Kermalis.PokemonBattleEngine.DefaultData;\nusing Kermalis.PokemonBattleEngine.Packets;\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Text;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace Kermalis.PokemonBattleEngineTests;\n\n[CollectionDefinition(\"Utils\")]\npublic class TestUtilsCollection : ICollectionFixture<TestUtils>\n{\n\t//\n}\n\npublic class TestUtils\n{\n\tpublic TestUtils()\n\t{\n\t\tPBEDefaultDataProvider.InitEngine(string.Empty);\n\t}\n\n\t#region Output\n\tpublic static void SetOutputHelper(ITestOutputHelper output)\n\t{\n\t\tConsole.SetOut(new TestOutputConverter(output));\n\t}\n\n\tprivate class TestOutputConverter : TextWriter\n\t{\n\t\tprivate readonly ITestOutputHelper _output;\n\t\tpublic TestOutputConverter(ITestOutputHelper output)\n\t\t{\n\t\t\t_output = output;\n\t\t}\n\t\tpublic override Encoding Encoding => Encoding.Unicode;\n\t\tpublic override void WriteLine(string? message)\n\t\t{\n\t\t\t_output.WriteLine(message);\n\t\t}\n\t\tpublic override void WriteLine(string format, params object?[] args)\n\t\t{\n\t\t\t_output.WriteLine(format, args);\n\t\t}\n\t}\n\t#endregion\n}\n\ninternal sealed class TestMoveset : IPBEMoveset, IPBEMoveset<TestMoveset.TestMovesetSlot>\n{\n\tpublic sealed class TestMovesetSlot : IPBEMovesetSlot\n\t{\n\t\tpublic PBEMove Move { get; }\n\t\tpublic byte PPUps { get; }\n\n\t\tpublic TestMovesetSlot(PBEMove move, byte ppUps)\n\t\t{\n\t\t\tMove = move;\n\t\t\tPPUps = ppUps;\n\t\t}\n\t}\n\n\tprivate readonly TestMovesetSlot[] _list;\n\tpublic int Count => _list.Length;\n\tpublic TestMovesetSlot this[int index]\n\t{\n\t\tget\n\t\t{\n\t\t\tif (index >= _list.Length)\n\t\t\t{\n\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(index));\n\t\t\t}\n\t\t\treturn _list[index];\n\t\t}\n\t}\n\tIPBEMovesetSlot IReadOnlyList<IPBEMovesetSlot>.this[int index] => this[index];\n\n\tpublic TestMoveset(PBESettings settings, params PBEMove[] moves)\n\t{\n\t\tint numMoves = settings.NumMoves;\n\t\t_list = new TestMovesetSlot[numMoves];\n\t\tint count = moves.Length;\n\t\tint i = 0;\n\t\tfor (; i < count; i++)\n\t\t{\n\t\t\t_list[i] = new TestMovesetSlot(moves[i], 0);\n\t\t}\n\t\tfor (; i < numMoves; i++)\n\t\t{\n\t\t\t_list[i] = new TestMovesetSlot(PBEMove.None, 0);\n\t\t}\n\t}\n\n\tpublic IEnumerator<TestMovesetSlot> GetEnumerator()\n\t{\n\t\treturn ((IEnumerable<TestMovesetSlot>)_list).GetEnumerator();\n\t}\n\tIEnumerator<IPBEMovesetSlot> IEnumerable<IPBEMovesetSlot>.GetEnumerator()\n\t{\n\t\treturn ((IEnumerable<TestMovesetSlot>)_list).GetEnumerator();\n\t}\n\tIEnumerator IEnumerable.GetEnumerator()\n\t{\n\t\treturn _list.GetEnumerator();\n\t}\n}\ninternal sealed class TestPartyMoveset : IPBEMoveset, IPBEPartyMoveset, IPBEMoveset<TestPartyMoveset.TestPartyMovesetSlot>, IPBEPartyMoveset<TestPartyMoveset.TestPartyMovesetSlot>\n{\n\tpublic sealed class TestPartyMovesetSlot : IPBEPartyMovesetSlot\n\t{\n\t\tpublic PBEMove Move { get; }\n\t\tpublic byte PPUps { get; }\n\t\tpublic int PP { get; set; }\n\n\t\tpublic TestPartyMovesetSlot(PBEMove move, byte ppUps, PBESettings settings)\n\t\t{\n\t\t\tMove = move;\n\t\t\tPPUps = ppUps;\n\t\t\tPP = PBEDataUtils.CalcMaxPP(move, ppUps, settings);\n\t\t}\n\t}\n\n\tprivate readonly TestPartyMovesetSlot[] _list;\n\tpublic int Count => _list.Length;\n\n\tpublic TestPartyMovesetSlot this[int index]\n\t{\n\t\tget\n\t\t{\n\t\t\tif (index >= _list.Length)\n\t\t\t{\n\t\t\t\tthrow new ArgumentOutOfRangeException(nameof(index));\n\t\t\t}\n\t\t\treturn _list[index];\n\t\t}\n\t}\n\tIPBEPartyMovesetSlot IReadOnlyList<IPBEPartyMovesetSlot>.this[int index] => this[index];\n\tIPBEMovesetSlot IReadOnlyList<IPBEMovesetSlot>.this[int index] => this[index];\n\n\tpublic TestPartyMoveset(PBESettings settings, params PBEMove[] moves)\n\t{\n\t\tint numMoves = settings.NumMoves;\n\t\t_list = new TestPartyMovesetSlot[numMoves];\n\t\tint count = moves.Length;\n\t\tint i = 0;\n\t\tfor (; i < count; i++)\n\t\t{\n\t\t\t_list[i] = new TestPartyMovesetSlot(moves[i], 0, settings);\n\t\t}\n\t\tfor (; i < numMoves; i++)\n\t\t{\n\t\t\t_list[i] = new TestPartyMovesetSlot(PBEMove.None, 0, settings);\n\t\t}\n\t}\n\n\tpublic IEnumerator<TestPartyMovesetSlot> GetEnumerator()\n\t{\n\t\treturn ((IEnumerable<TestPartyMovesetSlot>)_list).GetEnumerator();\n\t}\n\tIEnumerator<IPBEMovesetSlot> IEnumerable<IPBEMovesetSlot>.GetEnumerator()\n\t{\n\t\treturn ((IEnumerable<TestPartyMovesetSlot>)_list).GetEnumerator();\n\t}\n\tIEnumerator<IPBEPartyMovesetSlot> IEnumerable<IPBEPartyMovesetSlot>.GetEnumerator()\n\t{\n\t\treturn ((IEnumerable<TestPartyMovesetSlot>)_list).GetEnumerator();\n\t}\n\tIEnumerator IEnumerable.GetEnumerator()\n\t{\n\t\treturn _list.GetEnumerator();\n\t}\n}\ninternal sealed class TestPokemon : IPBEPokemon\n{\n\tpublic bool PBEIgnore { get; set; }\n\tpublic PBESpecies Species { get; set; }\n\tpublic PBEForm Form { get; set; }\n\tpublic PBEGender Gender { get; set; }\n\tpublic string Nickname { get; set; }\n\tpublic bool Shiny { get; set; }\n\tpublic byte Level { get; set; }\n\tpublic uint EXP { get; set; }\n\tpublic bool Pokerus { get; set; }\n\tpublic PBEItem Item { get; set; }\n\tpublic byte Friendship { get; set; }\n\tpublic PBEAbility Ability { get; set; }\n\tpublic PBENature Nature { get; set; }\n\tpublic PBEItem CaughtBall { get; set; }\n\tpublic IPBEStatCollection EffortValues { get; set; }\n\tpublic IPBEReadOnlyStatCollection IndividualValues { get; set; }\n\tpublic TestMoveset Moveset { get; set; }\n\tIPBEMoveset IPBEPokemon.Moveset => Moveset;\n\n\tpublic TestPokemon(PBESettings settings, PBESpecies species, PBEForm form, byte level, params PBEMove[] moves)\n\t{\n\t\tSpecies = species;\n\t\tForm = form;\n\t\tLevel = level;\n\t\tEXP = PBEDataProvider.Instance.GetEXPRequired(PBEDataProvider.Instance.GetPokemonData(species, form).GrowthRate, level);\n\t\tNickname = species.ToString();\n\t\tGender = PBEDataProvider.GlobalRandom.RandomGender(PBEDataProvider.Instance.GetPokemonData(species, form).GenderRatio);\n\t\tCaughtBall = PBEItem.PokeBall;\n\t\tEffortValues = new PBEStatCollection(0, 0, 0, 0, 0, 0);\n\t\tIndividualValues = new PBEStatCollection(0, 0, 0, 0, 0, 0);\n\t\tMoveset = new TestMoveset(settings, moves);\n\t}\n}\ninternal sealed class TestPartyPokemon : IPBEPartyPokemon\n{\n\tpublic bool PBEIgnore { get; set; }\n\tpublic ushort HP { get; set; }\n\tpublic PBEStatus1 Status1 { get; set; }\n\tpublic byte SleepTurns { get; set; }\n\tpublic PBESpecies Species { get; set; }\n\tpublic PBEForm Form { get; set; }\n\tpublic PBEGender Gender { get; set; }\n\tpublic string Nickname { get; set; }\n\tpublic bool Shiny { get; set; }\n\tpublic byte Level { get; set; }\n\tpublic uint EXP { get; set; }\n\tpublic bool Pokerus { get; set; }\n\tpublic PBEItem Item { get; set; }\n\tpublic byte Friendship { get; set; }\n\tpublic PBEAbility Ability { get; set; }\n\tpublic PBENature Nature { get; set; }\n\tpublic PBEItem CaughtBall { get; set; }\n\tpublic IPBEStatCollection EffortValues { get; set; }\n\tpublic IPBEReadOnlyStatCollection IndividualValues { get; set; }\n\tpublic TestPartyMoveset Moveset { get; set; }\n\tIPBEMoveset IPBEPokemon.Moveset => Moveset;\n\tIPBEPartyMoveset IPBEPartyPokemon.Moveset => Moveset;\n\n\tpublic TestPartyPokemon(PBESettings settings, PBESpecies species, PBEForm form, byte level, params PBEMove[] moves)\n\t{\n\t\tSpecies = species;\n\t\tForm = form;\n\t\tLevel = level;\n\t\tEXP = PBEDataProvider.Instance.GetEXPRequired(PBEDataProvider.Instance.GetPokemonData(species, form).GrowthRate, level);\n\t\tNickname = species.ToString();\n\t\tGender = PBEDataProvider.GlobalRandom.RandomGender(PBEDataProvider.Instance.GetPokemonData(species, form).GenderRatio);\n\t\tCaughtBall = PBEItem.PokeBall;\n\t\tEffortValues = new PBEStatCollection(0, 0, 0, 0, 0, 0);\n\t\tIndividualValues = new PBEStatCollection(0, 0, 0, 0, 0, 0);\n\t\tMoveset = new TestPartyMoveset(settings, moves);\n\t\tHP = PBEDataUtils.CalculateStat(species, form, PBEStat.HP, Nature, EffortValues.GetStat(PBEStat.HP), IndividualValues.GetStat(PBEStat.HP), level, settings);\n\t}\n}\ninternal sealed class TestPokemonCollection : IPBEPokemonCollection, IPBEPokemonCollection<TestPokemon>\n{\n\tprivate readonly TestPokemon[] _list;\n\tpublic int Count => _list.Length;\n\tpublic TestPokemon this[int index]\n\t{\n\t\tget => _list[index];\n\t\tset => _list[index] = value;\n\t}\n\tIPBEPokemon IReadOnlyList<IPBEPokemon>.this[int index] => this[index];\n\n\tpublic TestPokemonCollection(int count)\n\t{\n\t\t_list = new TestPokemon[count];\n\t}\n\n\tpublic IEnumerator<TestPokemon> GetEnumerator()\n\t{\n\t\treturn ((IEnumerable<TestPokemon>)_list).GetEnumerator();\n\t}\n\tIEnumerator<IPBEPokemon> IEnumerable<IPBEPokemon>.GetEnumerator()\n\t{\n\t\treturn ((IEnumerable<TestPokemon>)_list).GetEnumerator();\n\t}\n\tIEnumerator IEnumerable.GetEnumerator()\n\t{\n\t\treturn _list.GetEnumerator();\n\t}\n}\ninternal sealed class TestPartyPokemonCollection : IPBEPokemonCollection, IPBEPartyPokemonCollection, IPBEPokemonCollection<TestPartyPokemon>, IPBEPartyPokemonCollection<TestPartyPokemon>\n{\n\tprivate readonly TestPartyPokemon[] _list;\n\tpublic int Count => _list.Length;\n\n\tpublic TestPartyPokemon this[int index]\n\t{\n\t\tget => _list[index];\n\t\tset => _list[index] = value;\n\t}\n\tIPBEPartyPokemon IReadOnlyList<IPBEPartyPokemon>.this[int index] => this[index];\n\tIPBEPokemon IReadOnlyList<IPBEPokemon>.this[int index] => this[index];\n\n\tpublic TestPartyPokemonCollection(int count)\n\t{\n\t\t_list = new TestPartyPokemon[count];\n\t}\n\n\tpublic IEnumerator<TestPartyPokemon> GetEnumerator()\n\t{\n\t\treturn ((IEnumerable<TestPartyPokemon>)_list).GetEnumerator();\n\t}\n\tIEnumerator<IPBEPartyPokemon> IEnumerable<IPBEPartyPokemon>.GetEnumerator()\n\t{\n\t\treturn ((IEnumerable<TestPartyPokemon>)_list).GetEnumerator();\n\t}\n\tIEnumerator<IPBEPokemon> IEnumerable<IPBEPokemon>.GetEnumerator()\n\t{\n\t\treturn ((IEnumerable<TestPartyPokemon>)_list).GetEnumerator();\n\t}\n\tIEnumerator IEnumerable.GetEnumerator()\n\t{\n\t\treturn _list.GetEnumerator();\n\t}\n}\n\ninternal static class TestExtensions\n{\n\tpublic static bool VerifyAbilityHappened(this PBEBattle battle, PBEBattlePokemon abilityOwner, PBEBattlePokemon pokemon2, PBEAbility ability, PBEAbilityAction abilityAction)\n\t{\n\t\tforeach (IPBEPacket packet in battle.Events)\n\t\t{\n\t\t\tif (packet is PBEAbilityPacket ap\n\t\t\t\t&& ap.Ability == ability\n\t\t\t\t&& ap.AbilityAction == abilityAction\n\t\t\t\t&& ap.AbilityOwnerTrainer.GetPokemon(ap.AbilityOwner) == abilityOwner\n\t\t\t\t&& ap.Pokemon2Trainer.GetPokemon(ap.Pokemon2) == pokemon2)\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\tpublic static bool VerifyItemHappened(this PBEBattle battle, PBEBattlePokemon itemHolder, PBEBattlePokemon pokemon2, PBEItem item, PBEItemAction itemAction)\n\t{\n\t\tforeach (IPBEPacket packet in battle.Events)\n\t\t{\n\t\t\tif (packet is PBEItemPacket ip\n\t\t\t\t&& ip.Item == item\n\t\t\t\t&& ip.ItemAction == itemAction\n\t\t\t\t&& ip.ItemHolderTrainer.GetPokemon(ip.ItemHolder) == itemHolder\n\t\t\t\t&& ip.Pokemon2Trainer.GetPokemon(ip.Pokemon2) == pokemon2)\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\tpublic static bool VerifyMoveResultHappened(this PBEBattle battle, PBEBattlePokemon moveUser, PBEBattlePokemon pokemon2, PBEResult result)\n\t{\n\t\tforeach (IPBEPacket packet in battle.Events)\n\t\t{\n\t\t\tif (packet is PBEMoveResultPacket mrp\n\t\t\t\t&& mrp.Result == result\n\t\t\t\t&& mrp.MoveUserTrainer.GetPokemon(mrp.MoveUser) == moveUser\n\t\t\t\t&& mrp.Pokemon2Trainer.GetPokemon(mrp.Pokemon2) == pokemon2)\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\tpublic static bool VerifySpecialMessageHappened(this PBEBattle battle, PBESpecialMessage message, params object[] p)\n\t{\n\t\tforeach (IPBEPacket packet in battle.Events)\n\t\t{\n\t\t\tif (packet is PBESpecialMessagePacket smp\n\t\t\t\t&& smp.Message == message\n\t\t\t\t&& p.Length == smp.Params.Count)\n\t\t\t{\n\t\t\t\tfor (int i = 0; i < p.Length; i++)\n\t\t\t\t{\n\t\t\t\t\tif (!p[i].Equals(smp.Params[i]))\n\t\t\t\t\t{\n\t\t\t\t\t\tgoto nope;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\tnope:\n\t\t\t;\n\t\t}\n\t\treturn false;\n\t}\n\tpublic static bool VerifyStatus1Happened(this PBEBattle battle, PBEBattlePokemon status1Receiver, PBEBattlePokemon pokemon2, PBEStatus1 status1, PBEStatusAction statusAction)\n\t{\n\t\tforeach (IPBEPacket packet in battle.Events)\n\t\t{\n\t\t\tif (packet is PBEStatus1Packet s1p\n\t\t\t\t&& s1p.Status1 == status1\n\t\t\t\t&& s1p.StatusAction == statusAction\n\t\t\t\t&& s1p.Status1ReceiverTrainer.GetPokemon(s1p.Status1Receiver) == status1Receiver\n\t\t\t\t&& s1p.Pokemon2Trainer.GetPokemon(s1p.Pokemon2) == pokemon2)\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\tpublic static bool VerifyStatus2Happened(this PBEBattle battle, PBEBattlePokemon status2Receiver, PBEBattlePokemon pokemon2, PBEStatus2 status2, PBEStatusAction statusAction)\n\t{\n\t\tforeach (IPBEPacket packet in battle.Events)\n\t\t{\n\t\t\tif (packet is PBEStatus2Packet s2p\n\t\t\t\t&& s2p.Status2 == status2\n\t\t\t\t&& s2p.StatusAction == statusAction\n\t\t\t\t&& s2p.Status2ReceiverTrainer.GetPokemon(s2p.Status2Receiver) == status2Receiver\n\t\t\t\t&& s2p.Pokemon2Trainer.GetPokemon(s2p.Pokemon2) == pokemon2)\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\tpublic static bool VerifyTeamStatusDamageHappened(this PBEBattle battle, PBETeam team, PBETeamStatus teamStatus, PBEBattlePokemon damageVictim)\n\t{\n\t\tforeach (IPBEPacket packet in battle.Events)\n\t\t{\n\t\t\tif (packet is PBETeamStatusDamagePacket tsp\n\t\t\t\t&& tsp.Team == team\n\t\t\t\t&& tsp.TeamStatus == teamStatus\n\t\t\t\t&& tsp.DamageVictimTrainer.GetPokemon(tsp.DamageVictim) == damageVictim)\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "PokemonBattleEngineTests/ThrowTests.cs",
    "content": "﻿using Kermalis.PokemonBattleEngine.Battle;\nusing Kermalis.PokemonBattleEngine.Data;\nusing System;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace Kermalis.PokemonBattleEngineTests;\n\n[Collection(\"Utils\")]\npublic class ThrowTests\n{\n\tpublic ThrowTests(TestUtils _, ITestOutputHelper output)\n\t{\n\t\tTestUtils.SetOutputHelper(output);\n\t}\n\n\t[Theory]\n\t[InlineData(PBEBattleFormat.Single, 1, false)]\n\t[InlineData(PBEBattleFormat.Single, 2, true)]\n\t[InlineData(PBEBattleFormat.Single, 3, true)]\n\t[InlineData(PBEBattleFormat.Single, 4, true)]\n\t[InlineData(PBEBattleFormat.Double, 1, false)]\n\t[InlineData(PBEBattleFormat.Double, 2, false)]\n\t[InlineData(PBEBattleFormat.Double, 3, true)]\n\t[InlineData(PBEBattleFormat.Double, 4, true)]\n\t[InlineData(PBEBattleFormat.Triple, 3, false)]\n\t[InlineData(PBEBattleFormat.Triple, 4, true)]\n\tpublic void Wild_Battle_Throws_For_Illegal_Party_Size(PBEBattleFormat format, int count, bool expectException)\n\t{\n\t\t#region Setup and check\n\t\tPBEDataProvider.GlobalRandom.Seed = 0;\n\t\tPBESettings settings = PBESettings.DefaultSettings;\n\n\t\tvar p0 = new TestPokemonCollection(1);\n\t\tp0[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 100, PBEMove.Splash);\n\n\t\tvar p1 = new TestPokemonCollection(count);\n\t\tp1[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 100, PBEMove.Splash)\n\t\t{\n\t\t\tCaughtBall = PBEItem.None\n\t\t};\n\t\tfor (int i = 1; i < count; i++)\n\t\t{\n\t\t\tp1[i] = p1[0];\n\t\t}\n\n\t\tif (expectException)\n\t\t{\n\t\t\tAssert.Throws<ArgumentException>(() => PBEBattle.CreateWildBattle(format, settings, new PBETrainerInfo(p0, \"Trainer 0\", false), new PBEWildInfo(p1)));\n\t\t}\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "README.md",
    "content": "# PokemonBattleEngine\n\n[![Join on Discord](https://discordapp.com/api/guilds/717575608393138187/widget.png?style=shield)][Discord]\n\nPokémon Battle Engine is a C# library that can emulate Pokémon battles.\nThis repository includes the engine library, a Windows/Linux/MacOS/Android/iOS client, a server, a Discord bot, and some extra code to rip data from the official games.\nThe engine only emulates Pokémon B2W2 versions, so nothing introduced after generation 5 is included and neither are generation 1/2 exclusive items, event Pokémon, and move compatibility.\n\n![ClientPreview](Client%20Preview.gif)\n\nThe engine does not send information a player should not know; information only gets sent to each player/spectator when it is revealed.\nFor example, a client has no way of knowing if the opponent has sent out a Pokémon with Illusion, the opponent's nature, stats, Hidden Power damage/type, unused item/moves, etc.\nTherefore, a custom/modified client cannot do anything more than an ordinary player unless it is hosting the battle. A lot of work has been done to prevent any cheating.\n\nJoin our [Discord server][Discord] to talk or try out the battle bot!\n\n![DiscordPreview](Discord%20Preview.png)\n\n## Important Note for Discord:\nIf you are going to self-host the bot after downloading the code, you will need to have your bot join these two servers for the emotes:\n* https://discord.gg/bjrk2NRW8n\n* https://discord.gg/5gKGKWUg9u\n\n## Other Features:\n* There are [settings](PokemonBattleEngine/Data/Settings.cs) that you can change, such as having more moves, a higher maximum level, or weaker poison.\n* There are helper classes to build legal Pokémon, as well as require legality for a battle.\n* Alternatively, you can represent the Pokémon in any way you wish, as long as you have the basic info to start the battle. This works well with custom games like [PokemonGameEngine][PokemonGameEngine].\n* You can save battle replays to watch them back in the client or to train a neural network.\n* There is a work-in-progress random team generator inspired by [Pokémon Showdown](https://github.com/smogon/pokemon-showdown)'s which aims to work well with custom settings and moves.\n* The library has classes which automatically use the correct language from the games, meaning you can see the Pokémon's names, items, etc in your language, as long as your language was one supported by Pokémon B2W2.\n* Multi-Battles exist, and they can be customized to work in 2v1, 2v2, 3v1, or 3v3 battles.\n* Wild-Battles exist, and a wild Pokémon can appear disguised from the start, and have statuses etc.\n* EXP & Level up work, but not currently with remote battles. It can be added though.\n\n----\n## To Do:\n* Triple-battle shifting, Rotation-battles\n* Add the remaining vanilla [abilities](To%20Do%20Abilities.txt), [items](To%20Do%20Items.txt), and [moves](To%20Do%20Moves.txt)\n* Finish adding all event Pokémon\n* Add previews of the Android/iOS apps\n\n----\n## PokemonBattleEngine Is Used By:\n* [PokemonGameEngine][PokemonGameEngine]\n\n----\n## PokemonBattleEngine Uses:\n* [EndianBinaryIO](https://github.com/Kermalis/EndianBinaryIO)\n* [Microsoft.Data.Sqlite](https://docs.microsoft.com/en-us/ef/core)\n* [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json)\n* [SQLitePCLRaw](https://github.com/ericsink/SQLitePCL.raw)\n* [TCP networking insprired by EasyTcp](https://github.com/Job79/EasyTcp)\n\n## PokemonBattleEngineClient Uses:\n* [Avalonia](https://github.com/AvaloniaUI/Avalonia)\n* [AvaloniaGif](https://github.com/jmacato/AvaloniaGif)\n\n## PokemonBattleEngineDiscord Uses:\n* [Discord.Net](https://github.com/RogueException/Discord.Net)\n\n## PokemonBattleEngineExtras Uses:\n* [SimpleNARC](https://github.com/Kermalis/SimpleNARC)\n\n## PokemonBattleEngineTests Uses:\n* [xUnit.net](https://github.com/xunit/xunit)\n\n[Discord]: https://discord.gg/Z4Mn9qX\n[PokemonGameEngine]: https://github.com/Kermalis/PokemonGameEngine"
  },
  {
    "path": "Shared Assets/PKMN/FemaleMinispriteLookup.txt",
    "content": "Frillish\nJellicent"
  },
  {
    "path": "Shared Assets/PKMN/FemaleSpriteLookup.txt",
    "content": "Abomasnow\nAipom\nAlakazam\nAmbipom\nBeautifly\nBibarel\nBidoof\nBlaziken\nBuizel\nButterfree\nCacturne\nCamerupt\nCombee\nCombusken\nCroagunk\nDodrio\nDoduo\nDonphan\nDustox\nFinneon\nFloatzel\nFrillish\nGabite\nGarchomp\nGible\nGirafarig\nGligar\nGloom\nGolbat\nGoldeen\nGulpin\nGyarados\nHeracross\nHippopotas\nHippowdon\nHoundoom\nHypno\nJellicent\nKadabra\nKricketot\nKricketune\nLedian\nLedyba\nLudicolo\nLumineon\nLuxio\nLuxray\nMagikarp\nMamoswine\nMedicham\nMeditite\nMeganium\nMilotic\nMurkrow\nNumel\nNuzleaf\nOctillery\nPachirisu\nPikachu\nPiloswine\nPolitoed\nQuagsire\nRaichu\nRaticate\nRattata\nRelicanth\nRhydon\nRhyhorn\nRhyperior\nRoselia\nRoserade\nScizor\nScyther\nSeaking\nShiftry\nShinx\nSneasel\nSnover\nStaraptor\nStaravia\nStarly\nSteelix\nSudowoodo\nSwalot\nTangrowth\nTorchic\nToxicroak\nUnfezant\nUrsaring\nVenusaur\nVileplume\nWeavile\nWobbuffet\nWooper\nXatu\nZubat"
  },
  {
    "path": "To Do Abilities.txt",
    "content": "Aftermath\nAnalytic\nAngerPoint\nArenaTrap\nCursedBody\nDamp\nDefiant\nDrySkin\nFlashFire\nForewarn\nFriendGuard\nFrisk\nGluttony\nHarvest\nHeavyMetal\nHydration\nKlutz\nLightMetal\nLightningrod\nMagicBounce\nMagicGuard\nMagnetPull\nMotorDrive\nMoxie\nMultiscale\nPickpocket\nPickup\nPoisonTouch\nPressure\nRivalry\nSapSipper\nShadowTag\nSheerForce\nShieldDust\nSoundproof\nStall\nStench\nStickyHold\nStormDrain\nSuctionCups\nSynchronize\nTelepathy\nTrace\nTruant\nUnburden\nUnnerve\nVoltAbsorb\nWaterAbsorb\nZenMode"
  },
  {
    "path": "To Do Items.txt",
    "content": "AbsorbBulb\nAirBalloon\nAntidote\nAspearBerry\nAwakening\nBabiriBerry\nBelueBerry\nBindingBand\nBlukBerry\nBurnHeal\nCasteliacone\nCellBattery\nChartiBerry\nCheriBerry\nChestoBerry\nChilanBerry\nChopleBerry\nCobaBerry\nColburBerry\nCornnBerry\nCustapBerry\nDireHit\nDurinBerry\nEjectButton\nElixir\nEnergyPowder\nEnergyRoot\nEnigmaBerry\nEther\nFloatStone\nFreshWater\nFullHeal\nFullIncense\nFullRestore\nGrepaBerry\nGripClaw\nGuardSpec\nHabanBerry\nHealPowder\nHondewBerry\nHyperPotion\nIceHeal\nIronBall\nJabocaBerry\nKasibBerry\nKebiaBerry\nKelpsyBerry\nKingsRock\nLaggingTail\nLansatBerry\nLavaCookie\nLemonade\nLeppaBerry\nLumBerry\nMagostBerry\nMaxElixir\nMaxEther\nMaxPotion\nMaxRevive\nMentalHerb\nMetronome\nMicleBerry\nMoomooMilk\nNanabBerry\nNomelBerry\nOccaBerry\nOldGateau\nPamtreBerry\nParalyzHeal\nPasshoBerry\nPayapaBerry\nPechaBerry\nPersimBerry\nPinapBerry\nPomegBerry\nPotion\nQualotBerry\nQuickClaw\nRabutaBerry\nRageCandyBar\nRawstBerry\nRazorFang\nRazzBerry\nRedCard\nRevivalHerb\nRevive\nRindoBerry\nRingTarget\nRowapBerry\nSacredAsh\nShedShell\nShellBell\nShucaBerry\nSodaPop\nSpelonBerry\nStickyBarb\nSuperPotion\nSweetHeart\nTamatoBerry\nTangaBerry\nWacanBerry\nWatmelBerry\nWepearBerry\nWhiteHerb\nXAccuracy\nXAttack\nXDefend\nXSpecial\nXSpDef\nXSpeed\nYacheBerry\nZoomLens"
  },
  {
    "path": "To Do Moves.txt",
    "content": "Acupressure\nAfterYou\nAllySwitch\nAquaRing\nAromatherapy\nAssist\nAssurance\nAutotomize\nAvalanche\nBatonPass\nBeatUp\nBestow\nBide\nBind\nBlastBurn\nBlock\nBugBite\nCharge\nCircleThrow\nClamp\nClearSmog\nConversion2\nCopycat\nCounter\nCovet\nDefog\nDestinyBond\nDisable\nDoomDesire\nDragonTail\nEchoedVoice\nElectroBall\nEmbargo\nEncore\nEndure\nFakeOut\nFalseSwipe\nFirePledge\nFireSpin\nFlameBurst\nFling\nFocusPunch\nFollowMe\nFreezeShock\nFrenzyPlant\nFuryCutter\nFusionBolt\nFusionFlare\nFutureSight\nGigaImpact\nGrassPledge\nGravity\nGrudge\nGuardSplit\nGuardSwap\nGyroBall\nHealBell\nHealBlock\nHealingWish\nHeartSwap\nHiJumpKick\nHydroCannon\nHyperBeam\nIceBall\nIceBurn\nImprison\nIncinerate\nIngrain\nJumpKick\nKnockOff\nLastResort\nLunarDance\nMagicCoat\nMagicRoom\nMagmaStorm\nMeanLook\nMeFirst\nMemento\nMetalBurst\nMimic\nMirrorCoat\nMirrorMove\nMist\nMudSport\nNaturalGift\nNaturePower\nOutrage\nPerishSong\nPetalDance\nPluck\nPowerSplit\nPowerSwap\nPresent\nPsychoShift\nPursuit\nQuash\nRage\nRagePowder\nRapidSpin\nRazorWind\nRecycle\nRelicSong\nRevenge\nRoarOfTime\nRockWrecker\nRollout\nRound\nSandTomb\nSketch\nSkillSwap\nSkullBash\nSkyAttack\nSkyDrop\nSleepTalk\nSmackDown\nSnatch\nSolarBeam\nSpiderWeb\nSpite\nSpitUp\nStockpile\nSwallow\nSwitcheroo\nSynchronoise\nTaunt\nTelekinesis\nThief\nThrash\nTorment\nTrick\nTripleKick\nTrumpCard\nUproar\nUturn\nVoltSwitch\nWaterPledge\nWaterSport\nWhirlpool\nWish\nWonderRoom\nWrap\nYawn"
  },
  {
    "path": "nuget.config",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n  <packageSources>\n    <add key=\"AvaloniaCI\" value=\"https://www.myget.org/F/avalonia-ci/api/v2\" />\n    <add key=\"AvaloniaPR\" value=\"https://www.myget.org/F/avalonia-prs/api/v3/index.json\" />\n  </packageSources>\n</configuration>"
  }
]