├── BulkImporter
├── BulkEggGenerator.csproj
├── BulkImporter.sln
└── BulkEggGenerator.cs
├── BulkEggGenerator.sln
├── .gitattributes
├── README.md
└── .gitignore
/BulkImporter/BulkEggGenerator.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net90-windows
5 |
6 | Library
7 | 8
8 | enable
9 | 2.0.0
10 | true
11 | true
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/BulkImporter/BulkImporter.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.31129.286
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BulkImporter", "BulkImporter.csproj", "{EAE93960-86AE-480D-8578-EAFC2CE10A05}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {EAE93960-86AE-480D-8578-EAFC2CE10A05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {EAE93960-86AE-480D-8578-EAFC2CE10A05}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {EAE93960-86AE-480D-8578-EAFC2CE10A05}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {EAE93960-86AE-480D-8578-EAFC2CE10A05}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {29E2C562-F23D-4420-9BFE-CCA6FDB15FEB}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/BulkEggGenerator.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.14.36109.1 d17.14
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BulkEggGenerator", "BulkImporter\BulkEggGenerator.csproj", "{A2DB1F31-4192-4965-B720-D36BF522DCA3}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {A2DB1F31-4192-4965-B720-D36BF522DCA3}.Debug|Any CPU.ActiveCfg = Release|Any CPU
15 | {A2DB1F31-4192-4965-B720-D36BF522DCA3}.Debug|Any CPU.Build.0 = Release|Any CPU
16 | {A2DB1F31-4192-4965-B720-D36BF522DCA3}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {A2DB1F31-4192-4965-B720-D36BF522DCA3}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {CBADA77B-A43F-4D2C-A8DE-14DB59E67D8F}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PKHeX Bulk Egg Generator
2 | ## About
3 | Previously called the Bulk Pokemon Importer. The Bulk Egg Generator is a PKHeX plugin that allows you to generate eggs in your Pokemon save files. There are additional options that allow the user to control certain aspects of the generated eggs, such as their typing, IVs, nature, and so on.
4 |
5 | ## Current Features
6 | * Generate one egg per Pokemon species
7 | * Restrict the generated eggs by one or more types, and opt to consider the eggs' future evolutions
8 | * Set the minimum and maxiumum IV values you want the generated eggs to have
9 | * Set the chance of a Pokemon being shiny
10 | * Set the chance of a Pokemon having its Hidden Ability (gen 5+)
11 | * Set the chance of a Pokemon having egg moves
12 |
13 | ## Installation
14 | - If it does not already exist, create a `plugins` folder in the same directory as `PKHeX.exe`.
15 | - Download the latest version of BulkEggGenerator from the releases page.
16 | - Place `BulkEggGenerator.dll` to the `plugins` folder.
17 |
18 | Example file structure:
19 | ```
20 | PKHeX/
21 | |- PKHeX.exe
22 | |- plugins/
23 | |-- BulkEggGenerator.dll
24 | ```
25 |
26 |
27 | ## Usage
28 | Once the plugin has been installed, you will see an option for "Egg Generator" appear under the "Tools" menu in PKHeX. Clicking on it will open the egg generation menu, where you can set the percentage for shiny chance, hidden ability chance, and so on. When you are satisfied, click "Generate Eggs" to populate your boxes with Pokemon eggs.
29 |
30 | ## Known Issues & FAQ
31 | ### I can't see the Egg Generator in the menu
32 | PKHeX sometimes fails to load plugins. You can keep closing and re-opening it to try (which is admittedly annoying), or change its `PluginLoadMethod`. You can do this by going to `Options > Settings > Startup` and changing `PluginLoadMethod` to `LoadFromFile`. Restart PKHeX after making the changes. You may still have to close and reopen PKHeX once or twice, but not nearly as many times as before.
33 |
34 | ### Shiny Pokemon In Gold/Silver/Crystal ignore the min/max IV settings!
35 | This is expected behaviour, and it's because of the way shinies work in Gen 2. From Gen 3 onward, shininess is determined by three things: the Pokemon's original trainer's ID and secret ID, and the Pokemon's personality value. In Gen 2, however, shininess is determined by the Pokemon's IVs. For a Pokemon to be shiny in Gen 2, it must have a value of 10 for its Speed, Defense, and Special IVs, and a value of 2, 3, 6, 7, 10, 11, 14 or 15 for its Attack IV.
36 |
37 | You can read more about the mechanic here.
38 |
39 | ### PKHeX is flagging all Scarlet & Violet Pokemon as illegal
40 | The Pokemon being generated *are* legal; PKHeX just has issues recognizing them as such. You can verify this by selecting any Pokemon, and unchecking and re-checking the `Is Egg` option in the `Main` tab. If you then click the error icon, it will change to the valid icon.
41 |
42 | This is a known issue, and is being looked into.
43 |
44 | ### PKHeX is flagging the generated Pokemon as illegal, but when I check each Pokemon individually it says they're fine
45 | This is a known issue, and a solution is being looked into. It affects the following games:
46 | * X and Y
47 | * Omega Ruby and Alpha Sapphire
48 | * Ruby, Sapphire, and Emerald
49 | * FireRed and LeafGreen
50 |
51 | ### One or more of the generated Pokemon is illegal
52 | Please log an issue in the issues tab, and include the problems PKHeX has with the Pokemon.
53 |
54 |
55 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | project.fragment.lock.json
46 | artifacts/
47 |
48 | *_i.c
49 | *_p.c
50 | *_i.h
51 | *.ilk
52 | *.meta
53 | *.obj
54 | *.pch
55 | *.pdb
56 | *.pgc
57 | *.pgd
58 | *.rsp
59 | *.sbr
60 | *.tlb
61 | *.tli
62 | *.tlh
63 | *.tmp
64 | *.tmp_proj
65 | *.log
66 | *.vspscc
67 | *.vssscc
68 | .builds
69 | *.pidb
70 | *.svclog
71 | *.scc
72 |
73 | # Chutzpah Test files
74 | _Chutzpah*
75 |
76 | # Visual C++ cache files
77 | ipch/
78 | *.aps
79 | *.ncb
80 | *.opendb
81 | *.opensdf
82 | *.sdf
83 | *.cachefile
84 | *.VC.db
85 | *.VC.VC.opendb
86 |
87 | # Visual Studio profiler
88 | *.psess
89 | *.vsp
90 | *.vspx
91 | *.sap
92 |
93 | # TFS 2012 Local Workspace
94 | $tf/
95 |
96 | # Guidance Automation Toolkit
97 | *.gpState
98 |
99 | # ReSharper is a .NET coding add-in
100 | _ReSharper*/
101 | *.[Rr]e[Ss]harper
102 | *.DotSettings.user
103 |
104 | # JustCode is a .NET coding add-in
105 | .JustCode
106 |
107 | # TeamCity is a build add-in
108 | _TeamCity*
109 |
110 | # DotCover is a Code Coverage Tool
111 | *.dotCover
112 |
113 | # NCrunch
114 | _NCrunch_*
115 | .*crunch*.local.xml
116 | nCrunchTemp_*
117 |
118 | # MightyMoose
119 | *.mm.*
120 | AutoTest.Net/
121 |
122 | # Web workbench (sass)
123 | .sass-cache/
124 |
125 | # Installshield output folder
126 | [Ee]xpress/
127 |
128 | # DocProject is a documentation generator add-in
129 | DocProject/buildhelp/
130 | DocProject/Help/*.HxT
131 | DocProject/Help/*.HxC
132 | DocProject/Help/*.hhc
133 | DocProject/Help/*.hhk
134 | DocProject/Help/*.hhp
135 | DocProject/Help/Html2
136 | DocProject/Help/html
137 |
138 | # Click-Once directory
139 | publish/
140 |
141 | # Publish Web Output
142 | *.[Pp]ublish.xml
143 | *.azurePubxml
144 | # TODO: Comment the next line if you want to checkin your web deploy settings
145 | # but database connection strings (with potential passwords) will be unencrypted
146 | #*.pubxml
147 | *.publishproj
148 |
149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
150 | # checkin your Azure Web App publish settings, but sensitive information contained
151 | # in these scripts will be unencrypted
152 | PublishScripts/
153 |
154 | # NuGet Packages
155 | *.nupkg
156 | # The packages folder can be ignored because of Package Restore
157 | **/packages/*
158 | # except build/, which is used as an MSBuild target.
159 | !**/packages/build/
160 | # Uncomment if necessary however generally it will be regenerated when needed
161 | #!**/packages/repositories.config
162 | # NuGet v3's project.json files produces more ignoreable files
163 | *.nuget.props
164 | *.nuget.targets
165 |
166 | # Microsoft Azure Build Output
167 | csx/
168 | *.build.csdef
169 |
170 | # Microsoft Azure Emulator
171 | ecf/
172 | rcf/
173 |
174 | # Windows Store app package directories and files
175 | AppPackages/
176 | BundleArtifacts/
177 | Package.StoreAssociation.xml
178 | _pkginfo.txt
179 |
180 | # Visual Studio cache files
181 | # files ending in .cache can be ignored
182 | *.[Cc]ache
183 | # but keep track of directories ending in .cache
184 | !*.[Cc]ache/
185 |
186 | # Others
187 | ClientBin/
188 | ~$*
189 | *~
190 | *.dbmdl
191 | *.dbproj.schemaview
192 | *.jfm
193 | *.pfx
194 | *.publishsettings
195 | node_modules/
196 | orleans.codegen.cs
197 |
198 | # Since there are multiple workflows, uncomment next line to ignore bower_components
199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
200 | #bower_components/
201 |
202 | # RIA/Silverlight projects
203 | Generated_Code/
204 |
205 | # Backup & report files from converting an old project file
206 | # to a newer Visual Studio version. Backup files are not needed,
207 | # because we have git ;-)
208 | _UpgradeReport_Files/
209 | Backup*/
210 | UpgradeLog*.XML
211 | UpgradeLog*.htm
212 |
213 | # SQL Server files
214 | *.mdf
215 | *.ldf
216 |
217 | # Business Intelligence projects
218 | *.rdl.data
219 | *.bim.layout
220 | *.bim_*.settings
221 |
222 | # Microsoft Fakes
223 | FakesAssemblies/
224 |
225 | # GhostDoc plugin setting file
226 | *.GhostDoc.xml
227 |
228 | # Node.js Tools for Visual Studio
229 | .ntvs_analysis.dat
230 |
231 | # Visual Studio 6 build log
232 | *.plg
233 |
234 | # Visual Studio 6 workspace options file
235 | *.opt
236 |
237 | # Visual Studio LightSwitch build output
238 | **/*.HTMLClient/GeneratedArtifacts
239 | **/*.DesktopClient/GeneratedArtifacts
240 | **/*.DesktopClient/ModelManifest.xml
241 | **/*.Server/GeneratedArtifacts
242 | **/*.Server/ModelManifest.xml
243 | _Pvt_Extensions
244 |
245 | # Paket dependency manager
246 | .paket/paket.exe
247 | paket-files/
248 |
249 | # FAKE - F# Make
250 | .fake/
251 |
252 | # JetBrains Rider
253 | .idea/
254 | *.sln.iml
255 |
256 | # CodeRush
257 | .cr/
258 |
259 | # Python Tools for Visual Studio (PTVS)
260 | __pycache__/
261 | *.pyc
--------------------------------------------------------------------------------
/BulkImporter/BulkEggGenerator.cs:
--------------------------------------------------------------------------------
1 | using PKHeX.Core;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Windows.Forms;
6 |
7 | namespace BulkEggGenerator
8 | {
9 | public class BulkEggGenerator : IPlugin
10 | {
11 | public string Name => nameof(BulkEggGenerator);
12 | public int Priority => 1; // Loading order, lowest is first.
13 |
14 | // Initialized on plugin load
15 | // Initialized on plugin load
16 | public ISaveFileProvider SaveFileEditor { get; private set; } = null!;
17 | public IPKMView PKMEditor { get; private set; } = null!;
18 |
19 | // Random number generator for form generation
20 | Random random = new Random();
21 |
22 | // UI Forms
23 | private Form form = new Form();
24 | private TextBox input = new TextBox();
25 |
26 | private NumericUpDown numberToGenerate = new NumericUpDown();
27 | private NumericUpDown shinyChance = new NumericUpDown();
28 | private NumericUpDown eggMoveChance = new NumericUpDown();
29 | private NumericUpDown hiddenAbilityChance = new NumericUpDown();
30 | private NumericUpDown maxIVValue = new NumericUpDown();
31 | private NumericUpDown minIVValue = new NumericUpDown();
32 |
33 | private CheckedListBox typeSelection = new CheckedListBox();
34 | private CheckBox considerFutureTypesYes = new CheckBox();
35 | private CheckBox allowDuplicates = new CheckBox();
36 |
37 | // Plugin initialization
38 | public void Initialize(params object[] args)
39 | {
40 | Console.WriteLine($"Loading {Name}...");
41 |
42 | SaveFileEditor = (ISaveFileProvider)Array.Find(args, z => z is ISaveFileProvider);
43 | PKMEditor = (IPKMView)Array.Find(args, z => z is IPKMView);
44 |
45 | var menu = (ToolStrip)Array.Find(args, z => z is ToolStrip);
46 | var items = menu.Items;
47 | if (!(items.Find("Menu_Tools", false)[0] is ToolStripDropDownItem tools))
48 | return;
49 |
50 | foreach (ToolStripItem item in tools.DropDownItems)
51 | {
52 | if (item.Name == "Egg Generator")
53 | {
54 | return;
55 | }
56 | }
57 |
58 | var menuItem = new ToolStripMenuItem("Egg Generator");
59 | menuItem.Click += (sender, eventArgs) => generateForm();
60 | tools.DropDownItems.Add(menuItem);
61 |
62 | NotifySaveLoaded();
63 | }
64 |
65 | // Create the UI for the importer
66 | public void generateForm()
67 | {
68 | SaveFile sav = SaveFileEditor.SAV; // current savefile
69 |
70 | List formControls = new List();
71 | Button createButton = new Button();
72 |
73 | string[] pokemonTypes = { "Select All", "Normal", "Fire", "Fighting", "Water", "Flying", "Grass", "Poison", "Electric", "Ground", "Psychic", "Rock", "Ice", "Bug", "Dragon", "Ghost", "Dark", "Steel", "Fairy" };
74 | typeSelection.Items.AddRange(pokemonTypes);
75 |
76 | // Set up form
77 | form.Size = new System.Drawing.Size(390, 500);
78 | form.Name = "Egg Generator";
79 |
80 | // Set up Type Selection
81 | formControls.Add(new Label
82 | {
83 | Location = new System.Drawing.Point(8, 10),
84 | AutoSize = true,
85 | Text = "Types",
86 | Font = new System.Drawing.Font(Control.DefaultFont, System.Drawing.FontStyle.Bold)
87 | });
88 |
89 | typeSelection.Location = new System.Drawing.Point(10, 30);
90 | typeSelection.Size = new System.Drawing.Size(150, 350);
91 | typeSelection.SelectedValueChanged += new EventHandler(SelectOrDeselectAllTypes);
92 |
93 | formControls.Add(new Label
94 | {
95 | Location = new System.Drawing.Point(170, 270),
96 | AutoSize = true,
97 | Text = "Consider typing of future\nevolutions?",
98 | Font = new System.Drawing.Font(Control.DefaultFont, System.Drawing.FontStyle.Bold)
99 | });
100 |
101 | considerFutureTypesYes.Text = "Yes";
102 | considerFutureTypesYes.Location = new System.Drawing.Point(175, 300);
103 | considerFutureTypesYes.TextAlign = System.Drawing.ContentAlignment.TopLeft;
104 | considerFutureTypesYes.AutoSize = true;
105 | considerFutureTypesYes.Checked = false;
106 | considerFutureTypesYes.Name = "considerFutureTypes";
107 |
108 | ToolTip futureTypesTooltip = new ToolTip();
109 | futureTypesTooltip.SetToolTip(considerFutureTypesYes, "If checked, the tool will include Pokemon that\ndo not have one of the specified types,\nbut have an evolution that does.");
110 |
111 | formControls.Add(typeSelection);
112 | formControls.Add(considerFutureTypesYes);
113 |
114 | // Set up the input for the min IV value
115 | formControls.Add(new Label
116 | {
117 | Location = new System.Drawing.Point(170, 20),
118 | AutoSize = true,
119 | Text = "Min IV Value",
120 | Font = new System.Drawing.Font(Control.DefaultFont, System.Drawing.FontStyle.Bold)
121 | });
122 |
123 | minIVValue.Value = 0;
124 | minIVValue.Maximum = 31;
125 | minIVValue.Minimum = 0;
126 | minIVValue.Location = new System.Drawing.Point(170, 35);
127 |
128 | formControls.Add(minIVValue);
129 |
130 | //Set up the input for the max IV value
131 | formControls.Add(new Label
132 | {
133 | Location = new System.Drawing.Point(170, 70),
134 | AutoSize = true,
135 | Text = "Max IV Value",
136 | Font = new System.Drawing.Font(Control.DefaultFont, System.Drawing.FontStyle.Bold)
137 | });
138 |
139 | maxIVValue.Value = 31;
140 | maxIVValue.Maximum = 31;
141 | maxIVValue.Minimum = 0;
142 | maxIVValue.Location = new System.Drawing.Point(170, 85);
143 |
144 | formControls.Add(maxIVValue);
145 |
146 | // Set up the input for shiny chance
147 | formControls.Add(new Label
148 | {
149 | Location = new System.Drawing.Point(170, 120),
150 | AutoSize = true,
151 | Text = "Shiny Chance (%)",
152 | Font = new System.Drawing.Font(Control.DefaultFont, System.Drawing.FontStyle.Bold)
153 | });
154 |
155 | ToolTip shinyChanceToolTip = new ToolTip();
156 | shinyChanceToolTip.SetToolTip(shinyChance, "In Gen 2, this will overwrite IV settings.");
157 |
158 | shinyChance.Value = 1;
159 | shinyChance.Maximum = 100;
160 | shinyChance.Minimum = 0;
161 | shinyChance.Location = new System.Drawing.Point(170, 135);
162 |
163 | formControls.Add(shinyChance);
164 |
165 | // Set up the input for Egg Move Chance
166 | formControls.Add(new Label
167 | {
168 | Location = new System.Drawing.Point(170, 170),
169 | AutoSize = true,
170 | Text = "Egg Move Chance (%)",
171 | Font = new System.Drawing.Font(Control.DefaultFont, System.Drawing.FontStyle.Bold)
172 | });
173 |
174 | eggMoveChance.Value = 25;
175 | eggMoveChance.Maximum = 100;
176 | eggMoveChance.Minimum = 0;
177 | eggMoveChance.Location = new System.Drawing.Point(170, 185);
178 |
179 | formControls.Add(eggMoveChance);
180 |
181 | // Set up the input for Hidden Ability Chance
182 | formControls.Add(new Label
183 | {
184 | Location = new System.Drawing.Point(170, 220),
185 | AutoSize = true,
186 | Text = "Hidden Ability Chance (%)",
187 | Font = new System.Drawing.Font(Control.DefaultFont, System.Drawing.FontStyle.Bold)
188 | });
189 |
190 | hiddenAbilityChance.Value = 25;
191 | hiddenAbilityChance.Maximum = 100;
192 | hiddenAbilityChance.Minimum = 0;
193 | hiddenAbilityChance.Location = new System.Drawing.Point(170, 235);
194 |
195 | if (sav.Generation < 5)
196 | {
197 | hiddenAbilityChance.Enabled = false;
198 | }
199 | else
200 | {
201 | hiddenAbilityChance.Enabled = true;
202 | }
203 |
204 | formControls.Add(hiddenAbilityChance);
205 |
206 | // Set up "Generate Pokemon" button
207 | createButton.Text = "Generate Pokemon";
208 | createButton.Size = new System.Drawing.Size(185, 20);
209 | createButton.Location = new System.Drawing.Point(100, 420);
210 | createButton.Click += new EventHandler(AddToBoxesButtonClick);
211 |
212 | formControls.Add(createButton);
213 |
214 | // Add everything to the form, and show it
215 | form.Controls.AddRange(formControls.ToArray());
216 | form.ShowDialog();
217 | }
218 |
219 | public void SelectOrDeselectAllTypes(Object sender, EventArgs eventArgs)
220 | {
221 | if (typeSelection.SelectedIndex == 0)
222 | {
223 | bool checkItems = false;
224 |
225 | if (typeSelection.GetItemCheckState(0) == CheckState.Checked)
226 | {
227 | checkItems = true;
228 | }
229 |
230 | for (int i = 0; i < typeSelection.Items.Count; i++)
231 | {
232 | typeSelection.SetItemChecked(i, checkItems);
233 | }
234 | }
235 | }
236 |
237 | public bool IsPokemonValidType(PKM pkmn, EvolutionTree evoTree, byte generation, GameVersion gameVersion)
238 | {
239 | bool returnValue = false;
240 | var selectedTypes = typeSelection.CheckedItems;
241 |
242 | // Type validation. The first check looks for all types being selected, no types being selected, or Select All being checked; any of these
243 | // means that any Pokemon type goes.
244 | if (selectedTypes.Contains("Select All") || !selectedTypes.Contains("Select All") && selectedTypes.Count == 18 || selectedTypes.Count == 0)
245 | {
246 | returnValue = true;
247 | }
248 | else
249 | {
250 | // Loop through each type in the types selected by the user. First check the Pokemon's primary type. If it doesn't match, check the secondary type
251 | // (provided the Pokemon has one). Finally, if the user wants to consu
252 | foreach (string type in selectedTypes)
253 | {
254 | if (pkmn.PersonalInfo.Type1 != -1 && (int)(MoveType)Enum.Parse(typeof(MoveType), type) == pkmn.PersonalInfo.Type1)
255 | {
256 | returnValue = true;
257 | }
258 | else if (pkmn.PersonalInfo.Type2 != -1 && (int)(MoveType)Enum.Parse(typeof(MoveType), type) == pkmn.PersonalInfo.Type2)
259 | {
260 | returnValue = true;
261 | }
262 | }
263 | }
264 |
265 | // If the user wants to, look through the possible future evolutions to see if any of them fulfill the type requirements
266 | if (!returnValue && considerFutureTypesYes.Checked)
267 | {
268 | var evoSpecies = evoTree.GetEvolutionsAndPreEvolutions(pkmn.Species, pkmn.Form);
269 |
270 | foreach (var species in evoSpecies)
271 | {
272 | PKM evolvedPkmn = EntityBlank.GetBlank(generation, gameVersion);
273 | evolvedPkmn.Species = species.Species;
274 | evolvedPkmn.Form = species.Form;
275 |
276 | foreach (string type in selectedTypes)
277 | {
278 | if (evolvedPkmn.PersonalInfo.Type1 != -1 && (int)(MoveType)Enum.Parse(typeof(MoveType), type) == evolvedPkmn.PersonalInfo.Type1)
279 | {
280 | returnValue = true;
281 | }
282 | else if (evolvedPkmn.PersonalInfo.Type2 != -1 && (int)(MoveType)Enum.Parse(typeof(MoveType), type) == evolvedPkmn.PersonalInfo.Type2)
283 | {
284 | returnValue = true;
285 | }
286 | }
287 | }
288 | }
289 |
290 | return returnValue;
291 | }
292 |
293 | public ushort GenerateMove(ReadOnlySpan baseMoves, ushort[] eggMoves, int moveSlot)
294 | {
295 | ushort returnValue = 0;
296 |
297 | int eggMoveRand = random.Next(1, 100);
298 |
299 | if (eggMoveRand <= eggMoveChance.Value)
300 | {
301 | ushort moveIndex = (ushort)(random.Next(0, eggMoves.Length));
302 | returnValue = eggMoves[moveIndex];
303 | }
304 | else if (moveSlot < baseMoves.Length)
305 | {
306 | returnValue = baseMoves[moveSlot];
307 | }
308 |
309 | return returnValue;
310 | }
311 |
312 | public bool IsPokemonValid(PKM pkmn, SaveFile sav)
313 | {
314 | bool returnValue = true;
315 | EvolutionTree evoTree = EvolutionTree.GetEvolutionTree(pkmn.Context);
316 | var baseForm = evoTree.GetBaseSpeciesForm(pkmn.Species, pkmn.Form);
317 |
318 | // Make sure the Pokemon actually exists in the game, AND can come from an egg. If it can't, skip it and continue the loop.
319 | if (baseForm.Species != pkmn.Species ||
320 | !sav.Personal.IsSpeciesInGame(pkmn.Species) ||
321 | !sav.Personal.IsPresentInGame(pkmn.Species, pkmn.Form) ||
322 | !Breeding.CanHatchAsEgg(pkmn.Species) ||
323 | (Species)pkmn.Species == Species.Shedinja)
324 | {
325 | returnValue = false;
326 | }
327 |
328 | // Make sure the Pokemon's type is OK
329 | if (!IsPokemonValidType(pkmn, evoTree, sav.Generation, sav.Version))
330 | {
331 | returnValue = false;
332 | }
333 |
334 | return returnValue;
335 | }
336 |
337 | public static IEncounterEgg GeneratePokemonEgg(PKM pkmn, byte generation, GameVersion version)
338 | {
339 | if (generation == 2)
340 | {
341 | return new EncounterEgg2(pkmn.Species, version);
342 | }
343 | else if (generation == 3)
344 | {
345 | return new EncounterEgg3(pkmn.Species, version);
346 | }
347 | else if (generation == 4)
348 | {
349 | return new EncounterEgg4(pkmn.Species, version);
350 | }
351 | else if (generation == 5)
352 | {
353 | return new EncounterEgg5(pkmn.Species, pkmn.Form, version);
354 | }
355 | else if (generation == 6)
356 | {
357 | return new EncounterEgg6(pkmn.Species, pkmn.Form, version);
358 | }
359 | else if (generation == 7)
360 | {
361 | return new EncounterEgg7(pkmn.Species, pkmn.Form, version);
362 | }
363 | else if (generation == 8)
364 | {
365 | return new EncounterEgg8(pkmn.Species, pkmn.Form, version);
366 | }
367 | else if (generation == 9)
368 | {
369 | return new EncounterEgg9(pkmn.Species, pkmn.Form, version);
370 | }
371 | else
372 | {
373 | throw new NotImplementedException("Egg generation is not implemented for this generation.");
374 | }
375 | }
376 |
377 | public PKM GeneratePokemon(PKM pkmn, SaveFile sav, GameVersion version, byte generation)
378 | {
379 |
380 | IEncounterEgg pkmnAsEgg = GeneratePokemonEgg(pkmn, sav.Generation, sav.Version);
381 |
382 | LegalityAnalysis legality = new LegalityAnalysis(pkmn);
383 |
384 | pkmn = pkmnAsEgg.ConvertToPKM(sav);
385 |
386 | pkmn.Nickname = SpeciesName.GetSpeciesNameGeneration(0, sav.Language, sav.Generation);
387 | pkmn.IsNicknamed = true;
388 | pkmn.OriginalTrainerFriendship = (byte)EggStateLegality.GetMinimumEggHatchCycles(pkmn);
389 | pkmn.MetLocation = 0;
390 | pkmn.Gender = pkmn.GetSaneGender();
391 |
392 | // There are a lot of quirks with how eggs are handled between generations, and even game versions; this collection of statements does some
393 | // fine-tuning to account for that.
394 | if (sav.Version == GameVersion.BD || sav.Version == GameVersion.SP || sav.Version == GameVersion.BDSP)
395 | {
396 | pkmn.MetLocation = 65535; // Eggs have no met location in BDSP
397 | }
398 | else if (sav.Version == GameVersion.Pt)
399 | {
400 | pkmn.EggLocation = 2000; // Daycare
401 | pkmn.IsNicknamed = false;
402 | pkmn.MetLevel = 0;
403 | pkmn.Version = sav.Version;
404 | }
405 | else if (sav.Generation == 4)
406 | {
407 | // HGSS Eggs aren't nicknamed
408 | pkmn.IsNicknamed = false;
409 | pkmn.Version = sav.Context.GetSingleGameVersion();
410 | pkmn.EggLocation = 2000;
411 | }
412 | else if (sav.Version == GameVersion.FRLG || sav.Version == GameVersion.FR || sav.Version == GameVersion.LG)
413 | {
414 | pkmn.MetLocation = Locations.HatchLocationFRLG; // Four Island -- if location isn't set, it defaults to Littleroot Town
415 | }
416 | else if (sav.Version == GameVersion.RSE || sav.Version == GameVersion.RS || sav.Version == GameVersion.R || sav.Version == GameVersion.S || sav.Version == GameVersion.E)
417 | {
418 | pkmn.MetLocation = Locations.HatchLocationRSE; // Route 117 -- if location isn't set, it defaults to Littleroot Town
419 | pkmn.Version = sav.Context.GetSingleGameVersion();
420 | }
421 | else if (sav.Generation == 2)
422 | {
423 | pkmn.SetNickname("EGG");
424 | }
425 | else if (sav.Generation == 9)
426 | {
427 | // Set met location to South Province Area 1
428 | pkmn.MetLocation = Locations.HatchLocation9;
429 |
430 | // Set size
431 | if (pkmn is IScaledSize s)
432 | {
433 | s.HeightScalar = PokeSizeUtil.GetRandomScalar();
434 | s.WeightScalar = PokeSizeUtil.GetRandomScalar();
435 |
436 | if (pkmn is IScaledSize3 s3)
437 | s3.Scale = PokeSizeUtil.GetRandomScalar();
438 | }
439 | // Set Tera Type
440 | if (pkmn is ITeraType tera)
441 | {
442 | var type = Tera9RNG.GetTeraTypeFromPersonal(pkmn.Species, pkmn.Form, Util.Rand.Rand64());
443 | tera.TeraTypeOriginal = (MoveType)type;
444 | }
445 | }
446 |
447 | // Set the IVs based on min/max values
448 | pkmn.IV_HP = random.Next((int)minIVValue.Value, (int)maxIVValue.Value);
449 | pkmn.IV_ATK = random.Next((int)minIVValue.Value, (int)maxIVValue.Value);
450 | pkmn.IV_DEF = random.Next((int)minIVValue.Value, (int)maxIVValue.Value);
451 | pkmn.IV_SPA = random.Next((int)minIVValue.Value, (int)maxIVValue.Value);
452 | pkmn.IV_SPD = random.Next((int)minIVValue.Value, (int)maxIVValue.Value);
453 | pkmn.IV_SPE = random.Next((int)minIVValue.Value, (int)maxIVValue.Value);
454 |
455 | // Set ability
456 | pkmn.SetAbilityIndex(random.Next(0, 2));
457 | if (sav.Generation >= 5)
458 | {
459 | int haValue = random.Next(1, 100);
460 |
461 | if (haValue <= hiddenAbilityChance.Value)
462 | {
463 | pkmn.SetAbilityIndex(2); // set hidden ability
464 | }
465 | }
466 |
467 | // Determine if Pokemon should be shiny
468 | if (shinyChance.Value > 0)
469 | {
470 | int shinyValue = random.Next(1, 100);
471 |
472 | if (shinyValue <= shinyChance.Value)
473 | {
474 | pkmn.SetIsShiny(true);
475 | }
476 | else
477 | {
478 | pkmn.SetIsShiny(false);
479 | }
480 | }
481 |
482 | // Determine moves
483 | var learnSource = GameData.GetLearnSource(sav.Version);
484 | var learnset = learnSource.GetLearnset(pkmn.Species, pkmn.Form);
485 | ReadOnlySpan baseMoves = learnset.GetBaseEggMoves(sav.Generation);
486 | ReadOnlySpan eggMoves = learnSource.GetEggMoves(pkmn.Species, pkmn.Form);
487 |
488 | // PKHeX is smart and will automatically fill in a Pokemon's moves if we don't provide them,
489 | // so there's no need to add any logic for handling situations where the user only wants base moves
490 | if (eggMoveChance.Value > 0 && eggMoves.Length > 0)
491 | {
492 | ushort nextMoveIndex = 0;
493 | int numTriesToGetMove = 0;
494 | do
495 | {
496 | nextMoveIndex = GenerateMove(baseMoves, eggMoves.ToArray(), pkmn.Moves.Length);
497 | pkmn.AddMove(nextMoveIndex);
498 | numTriesToGetMove++;
499 |
500 | } while (pkmn.MoveCount < 5 && nextMoveIndex != 0 && numTriesToGetMove < 20);
501 |
502 | pkmn.FixMoves();
503 |
504 | if (sav.Generation > 5)
505 | {
506 | pkmn.SetRelearnMoves(pkmn.Moves);
507 | }
508 |
509 | Span fixedMoves = stackalloc ushort[4];
510 | bool movesAreGood = MoveBreed.GetExpectedMoves(pkmn.Moves, pkmnAsEgg, fixedMoves);
511 |
512 | for (int j = 0; j < 4; j++)
513 | {
514 | pkmn.SetMove(j, fixedMoves[j]);
515 | }
516 |
517 | if (sav.Generation > 5)
518 | {
519 | LegalityAnalysis legalityAnalysis = new LegalityAnalysis(pkmn);
520 | Span moves = stackalloc ushort[4];
521 | MoveListSuggest.GetSuggestedRelearnMovesFromEncounter(legalityAnalysis, moves, pkmnAsEgg);
522 |
523 | for (int j = 0; j < 4; j++)
524 | {
525 | pkmn.SetMove(j, moves[j]);
526 | pkmn.SetRelearnMove(j, moves[j]);
527 | }
528 | }
529 |
530 | pkmn.HealPP();
531 | }
532 |
533 | pkmn.IsEgg = true;
534 | legality = new LegalityAnalysis(pkmn);
535 | string report = legality.Report();
536 |
537 | if (report.Contains("Hidden Ability"))
538 | {
539 | pkmn.SetAbilityIndex(random.Next(0, 2));
540 | }
541 |
542 | pkmn.Valid = legality.Valid;
543 |
544 | return pkmn;
545 | }
546 |
547 | public void AddToBoxesButtonClick(Object sender, EventArgs events)
548 | {
549 | SaveFile sav = SaveFileEditor.SAV; // current savefile
550 | List generatedPokemon = new List(); // the pokemon to add to the boxes at the end of all this
551 | int pokedexMaxNumber = sav.MaxSpeciesID;
552 | GameVersion version = sav.Version;
553 | var generator = EncounterGenerator.GetGenerator(version, sav.Generation);
554 |
555 | // Workaround for games like Ruby/Sapphire, which get counted as RS, but needs to be either R or S
556 | if (version == GameVersion.RS)
557 | {
558 | version = GameVersion.R;
559 | }
560 | // Diamond/Pearl
561 | else if (version == GameVersion.DP)
562 | {
563 | version = GameVersion.D;
564 | }
565 | else if (version == GameVersion.HGSS)
566 | {
567 | version = GameVersion.HG;
568 | }
569 |
570 | // Make sure the game can generate eggs; if it cannot, the generator will not work and inform the user as such
571 | if (!generator.CanGenerateEggs)
572 | {
573 | MessageBox.Show("Error! This game doesn't support egg generation. Please try another game. Game version " + sav.Context.GetSingleGameVersion().ToString());
574 | return;
575 | }
576 | // loop for generating pokemon
577 | for (int i = 1; i < pokedexMaxNumber; i++)
578 | {
579 | // Get species, set up legality, form, etc.
580 | PKM pkmn = EntityBlank.GetBlank(sav.Generation, version);
581 | pkmn.Species = (ushort)i;
582 | pkmn.SetSuggestedFormArgument(pkmn.Species);
583 |
584 |
585 | if (IsPokemonValid(pkmn, sav))
586 | {
587 | generatedPokemon.Add(GeneratePokemon(pkmn, sav, version, sav.Generation));
588 | }
589 | }
590 |
591 | //Re-arrange the list so Pokemon aren't added in Pokedex order
592 | var rearrangedGeneratedPokemon = generatedPokemon.OrderBy(x => random.Next()).ToList();
593 |
594 | sav.ImportPKMs(rearrangedGeneratedPokemon);
595 | SaveFileEditor.ReloadSlots();
596 |
597 | }
598 |
599 | public void NotifySaveLoaded()
600 | {
601 | Console.WriteLine($"{Name} was notified that a Save File was just loaded.");
602 | }
603 |
604 | public bool TryLoadFile(string filePath)
605 | {
606 | Console.WriteLine($"{Name} was provided with the file path, but chose to do nothing with it.");
607 | return false; // no action taken
608 | }
609 | }
610 | }
611 |
--------------------------------------------------------------------------------