Compare commits

...

1953 Commits

Author SHA1 Message Date
Kurt
83071ca7c2 Update SAV_Trainer9a.Designer.cs
Closes #4762
2026-03-22 01:39:09 -05:00
Ka-n00b
6df330e62f
Update Translations and Event Flags (#4761) 2026-03-21 19:37:53 -05:00
间辞
66df00d038
Add files via upload (#4760) 2026-03-21 02:20:20 -05:00
Kurt
4784e2de82 Update ItemStorage3E.cs
Closes #4759
2026-03-21 01:43:10 -05:00
Kurt
a43b6a5d32 Update 26.03.20 2026-03-20 22:10:14 -05:00
Kurt
48938c5e14 Minor clean 2026-03-19 20:35:58 -05:00
Kurt
0e097b1fc6 Minor slot hover performance improvements
Skip repaint on cursor moving the hover window
Cache reference to the slot interaction types and "nothing" slot image
Dispose of slot sprites when updating with a new one
If scrolling box/group, auto-update hover with the newly displayed slot's content instead of hiding
2026-03-19 17:25:06 -05:00
Kurt
56ba92b68b Handle ZA's EOL revision (2)
When throwing arg out of range, pass the value so it's obvious the number
2026-03-19 15:39:07 -05:00
Kurt
3ad376be44 Misc tweaks
Add rejections for shiny criteria for gen8+ generators

Unrelated: use recent c# lang feature for settings property field
2026-03-19 09:38:02 -05:00
Kurt
ca6fbf024c Add new virtual method for pre-applying Nickname
Results in correct trash bytes for user-Nickname'd mons as if they were nicknamed via the in-game menu.
2026-03-19 03:20:42 -05:00
Kurt
3c1e7bdc6c Misc tweaks
Overworld8a: acknowledge Nature request
8U/8N: remove unnecessary auto-mint (not applied anywhere else)
Nature: use extension properties, use the `IsFixed` check throughout codebase
Wallpaper: add PLA default to pasture (not-obvious prior behavior was removed in refactor).
Tests: fix ck3 file with OT trash bytes (now cleared)
Tests: fix pk3 file with OT trash bytes now passes (added 1 trash pattern, future work)
Trash3: initial stubs for default OT trash recognition (one included to pass above ^)
2026-03-18 01:17:17 -05:00
Kurt
b1dbc6a82b Update SAV4.cs 2026-03-17 02:23:03 -05:00
Kurt
94ad477703 Fix rs/e inventory length
Closes #4756
2026-03-16 08:20:56 -05:00
Kurt
42496def98 PKM Editor BK4 don't set BallHGSS 2026-03-16 01:57:21 -05:00
Kurt
8950613422 Add party import for hall of fame 3
Closes #4754
2026-03-15 18:25:13 -05:00
Kurt
2faa1b10b1 Revise translation form scrape
Iterate through all constructor paths
2026-03-14 22:33:44 -05:00
Professor Dirty
ff69f82234
Add CHS translation and correct frlg flags format (#4753) 2026-03-14 22:28:18 -05:00
Kurt
3317a8bfda Add trash view/edit to all Trainer forms
Make PID text entry uppercase across the program
Standardize RivalTrash => RivalNameTrash, same for Rival => RivalName
2026-03-14 22:28:00 -05:00
Kurt
94f3937f2f Refactor: extract gen3 save block structures
Changed: Inventory editor no longer needs to clone the save file on GUI open
Changed: some method signatures have moved from SAV3* to the specific block
Allows the block structures to be used without a SAV3 object
Allows the Inventory editor to open from a blank save file.
2026-03-14 13:40:17 -05:00
Victor Borges
a3e0ed29b4
Fix YlwApricorn and BluApricorn gen 2 sprite IDs (#4752) 2026-03-13 00:39:02 -05:00
Kurt
d3a4ed6ad3 Update PKMEditor.Designer.cs 2026-03-12 01:34:05 -05:00
Kurt
bb363a7a3d Re-flow Stat editor for alignment/showing all text
Some languages have localized these labels to long strings; previously they were truncated (probably frustrating); now, they all show (wrapped text is the best I can do -- better than truncating?)
2026-03-12 01:29:58 -05:00
Professor Dirty
2f95536b13
Update CHS translation of FRLG flags (#4751) 2026-03-11 12:15:47 -05:00
Kurt
301a1e7664 Allow edits for SAV OT trash bytes for gen1-5 2026-03-11 00:15:58 -05:00
Kurt
662c3db7dc Faster box hover preview
Render it manually rather than let controls be goofy with draw calls.
2026-03-09 20:28:43 -05:00
Kurt
5b42ff746d Handle handle leaks on dragdrop cursor icon
Also pass the tooltips to the components container so that they dispose of anything if needed too.
A user had a long-running script/session that drag-dropped a few thousand times, which exhausted the Windows GDI handle limit (10,000 per process).
2026-03-09 12:25:15 -05:00
sora10pls
c7f02bcc20 GO: more tweaks to 30th Anniversary event handling 2026-03-09 13:11:23 -04:00
Kurt
7617f6dfa7 Revise trash check for Japanese nickname
Closes #4750
2026-03-08 23:41:20 -05:00
Kurt
8b08f263e5 Minor tweaks
Remove GameSync/SecureValue from SAV tab (still lives in Block Data)
Remove inaccessible items from FRLG/E key items
2026-03-08 23:40:56 -05:00
Professor Dirty
d827cec5a7
Update Crystal Event Flags translation in Chinese (#4749) 2026-03-07 23:39:32 -06:00
sora10pls
e5e7cc914c Pokémon 30th Anniversary: All Out event handling 2026-03-07 18:44:33 -05:00
Kurt
ff72af52ad Update 26.03.06 2026-03-06 23:15:14 -06:00
Kurt
d24d227df4 Push more trashbyte rework 2026-03-06 22:09:40 -06:00
Kurt
5a75fe4b89 Add delete menu item for Folder Browser 2026-03-06 12:22:41 -06:00
Kurt
49d9467d3c Update ParseSettings.cs 2026-03-05 22:12:27 -06:00
Carbonara
065d329546
Adjust event flag and constant categories (#4744)
* Adjust gen 2 flag categories

GSC:
- Move bedroom accessories from * (UsefulFeature) to b (currently unassigned, but ideally a new category dedicated to bedroom accessories)

C:
- Move GS Ball flags from */r (Rebattle) to e (EventEncounter)
- Move Odd Egg flag from r (Rebattle) to g (GiftAvailable)
- Add missing Mystery Gift item line to the Japanese, Spanish, Korean and Chinese translations (untranslated)

* Adjust RSFRLG event categories

RS:
- Move the HM 08 miss flag from Rebattle to StoryProgress
- Move the Fossil flags from Rebattle to GiftAvailable
- Move the Badges from UsefulFeature to StoryProgress
- Move the Pokelot and S. S. Tidal constants from StoryProgress to Misc
- Move the Professor Birch constant from Misc to StoryProgress

FRLG:
- Move the Lapras, Magikarp, Old Amber, Eevee, Trades and Fossil flags from Misc to GiftAvailable
- Move the Shown Mystic & Aurora Ticket flags from Misc to EventEncounter
Note: regular items were already classified into the nonexistent i section, keeping it for items
- Make the Spanish, Simplified Chinese and Traditional Chinese use the same lines than the other languages. If the lines they had translated existed in the new format, or were close enough, I reused those lines, otherwise they will need to be retranslated.

* Adjust E event categories

Gen 3 E
- Move Badges and Frontier Pass flags from UsefulFeature to StoryProgress
- Move Hidden items flags from Rebattle to HiddenItem
- Move Items flags from Rebattle to Item
- Move don't spawn flags from Rebattle to Misc unless the current category makes sense
- Move Items from Rebattle to Item
- Move Pokelot & S. S. Tidal constants from StoryProgress to Misc
- Move Professor Birch constant from Misc to StoryProgress

* Adjust Spanish and Chinese E flags

Same thing than with FRLG

* Make the amount of lines be consistent

+ Fix a line jump typo in the French DP flags

* Adjust remaining events

Gen 4 DP:
- Dialga/Palkia moved from StoryProgress to Rebattle
- Hidden items moved from StoryProgress to HiddenItem
- Items moved from StoryProgress to Item
- Trendy phrase moved from StoryProgress to Useful Feature

Gen 4 PT:
- Hidden items moved from StoryProgress to HiddenItem
- Items moved from StoryProgress to Item
- Trendy phrase moved from StoryProgress to Useful Feature
- Togepi moved from Rebattle to GiftAvailable

Gen 4 HGSS:
- Spiky-eared Pichu, Kanto Starters, Togepi Egg moved from Rebattle to GiftAvailable

Gen 5 BW:
- Zorua events moved from StoryProgress to EventEncounter
- Daily Royal Unova & Fossil moved from StoryProgress to Useful feature
- Darmanitan moved from GiftAvailable to Rebattle

Gen 5 B2W2:
- Daily Royal Unova & Fossil moved from StoryProgress to Useful feature

Gen 6 XY:
- Super Unlocked moved from Misc to Useful Feature
- Statuette moved from Misc to Achievement

Gen 6 ROSA:
- Items moved from StoryProgress to Item
2026-03-05 21:57:47 -06:00
Carbonara
93b9481393
Shorten text too long to be displayed in the French translation (#4745) 2026-03-05 21:57:33 -06:00
Kurt
244b34b8d3 Misc translatable util update
Allow EntitySearchSetup to be translated (rearrange the initialization, no need to retain/defer).
Rename Gen9a's gender label (previously blacklisted as an "auto-updating" label).

Other gender/Stat labels currently blacklisted in DevUtil probably need to get refactored to be enums/etc, but I currently lack the time/patience to understand those editors well enough to properly support the refactoring needed.

json exports: add newline at end to match the default editorconfig settings and general convention. we don't want textfiles loading in as a string[] with an empty string last entry.
2026-03-05 21:57:08 -06:00
Kurt
3e33f0fc2e Add options to sav3 accessor 2026-03-03 23:12:45 -06:00
Professor Dirty
3e33521796
Update CHS translation (#4743) 2026-03-03 08:14:30 -06:00
Kurt
79a08822ea FR/LG VC: Handle unobtainable balls
https: //github.com/kwsch/PKHeX/pull/4735#issuecomment-3986152531
Co-Authored-By: Carbonara <108797333+Mimigris@users.noreply.github.com>
2026-03-03 00:35:18 -06:00
Easy World
85ad6495e6
Update zh-Hans translation (#4742) 2026-03-02 21:39:56 -06:00
Carbonara
04c2063791
Translate remaining lines of the program to French (#4735) 2026-03-02 20:12:01 -06:00
Kurt
dd0d1fc07a Misc dex state fixes
Closes #4739
Closes #4740
Closes #4741

Co-Authored-By: Michael Bond <michael@bondcodes.com>
2026-03-02 17:36:24 -06:00
间辞
c64bc65359
Add files via upload (#4738) 2026-03-02 11:17:20 -06:00
Kurt
8587a88723 Update SAV_SimplePokedex.cs 2026-03-02 00:11:38 -06:00
Kurt
e56226f046 Single row select
Previously allowed cells and allowed multiple to be selected, resulting in some issues if users selected multiple cells and tried to trigger an open via contextmenu opening.
2026-03-01 23:16:04 -06:00
Kurt
6e48856bec Handle initial OT trash bytes for enc3->pk3 2026-03-01 22:42:15 -06:00
Kurt
51a012ff78 Add x/y coordinates for SAV3 2026-03-01 22:26:22 -06:00
Kurt
bd9f64b07e Remove duplicated encounters
Previously would recognize a LeafGreen Scyther from Celadon City as valid ;D
2026-03-01 22:12:54 -06:00
Kurt
b1dd981537 Update MiscVerifierG3.cs
eggs fixed
2026-03-01 22:09:25 -06:00
Kurt
553f154657 Add search box to flag/work row search 2026-03-01 19:59:57 -06:00
Kurt
b6eb0745a3 Add sanity check for enc gender request
If you set criteria to Male, and request to generate a Nidoran-F wild encounter, ofc the program will loop forever.
Oftentimes, users won't be looking at the criteria tab, and can stumble upon this accidentally.
Prevent the freeze entirely by just sanity checking and discarding the user's input if it is impossible.
2026-03-01 12:57:47 -06:00
Kurt
f382291de4 Improve translation of Extra Slots
SAV tab shows a bunch of extra slots from miscellaneous sources. The previous logic was a little clunky with fake labels; rewrite how it works so it's a little more transparent.

Misc is no more; I've created enum members with more descriptive names.

#4735
2026-03-01 09:58:40 -06:00
间辞
df39aff5e9
Add files via upload (#4737) 2026-03-01 08:35:04 -06:00
Kurt
20a92f533b Split event flag/work groups to tabs
Closes #4719

More groups can be added to the enum, and re-defined via their type-char column.
Updating translations will automatically add those types to the list of translatables.

Fixes the Dark Mode bug where the first tab of the Event flag/work editor (LGPE) didn't respect dark mode; now that all all event editors are sub-tabbed, we use the workaround present in all (on shown flip back to the first tab).
2026-03-01 00:00:34 -06:00
Kurt
b93d57cc9a FR/LG VC: handle TM/Tutors
Select a primary/secondary source verifier for better learn indication; not really worth doing this in mainline.
2026-02-28 15:09:59 -06:00
Kurt
2939bfae48 FR/LG VC: trash byte checks, reflow dex editor
a little more ergonomic in dex editor (size increased)

in-game trades now correctly allow initial contest stats, and their special handling for OT Trash bytes.
2026-02-28 13:32:12 -06:00
间辞
0d96d75b7a
Update CHS translations (#4736)
* Add files via upload

* Add files via upload
2026-02-28 08:25:24 -06:00
Kurt
8f0672b8c5 Update GiveAll for FR/LG VC, dex edit clean flags
When/if RSE added, these workarounds will be deleted.
2026-02-27 22:57:07 -06:00
Kurt
9420dcf44d Inventory: don't GiveAll unreleased items
Wasn't implemented for Gen3-5 storages
2026-02-27 22:46:31 -06:00
Kurt
bde5729883 FR/LG VC: disallow unavailable held items
Also ban Berry Juice from being released in mainline

fix casting issue
2026-02-27 18:47:04 -06:00
Kurt
fa0ac2a9ab FR/LG VC: flag unavailable evolutions/eggs
Need to check for traded eggs hatched in RSE as well; those must pass the first check based on their assigned version value.
2026-02-27 17:21:31 -06:00
Kurt
c037829b29 Initial gen3 virtual console checks
Disables branching when virtual console is the current save file
2026-02-27 13:26:05 -06:00
Kurt
5c4d27f7e4 Update 26.02.27 2026-02-27 09:49:09 -06:00
Carbonara
5c9f97b7fd
Translate FRLG flags to French (#4733)
Also contains the following changes:

French Emerald flags:
- Upper casing consistency for a reused line

English FireRed and LeafGreen flags:
- Fix Snorlax being called by its German name Relaxo
- Fix the Dotted Hole being misspelled in some places as Dotte Hole
- Change "Camper (Male)" and "Camper (Female)" in the unused flags to use the proper trainer class terms ("Camper (Female)" is Picnicker in English, meaning there is no need to specify that it is "Camper (Male)
- Alter the unused Interviewers line (it's supposed to refer to the Interviewers class from Ruby and Sapphire, which is always plural)
- Fix some typos in the trainer names (Psychic Tyron spelled as Pyschic Tyorn, Cooltrainer Brooke as Cooltrainer Brooker)
- Fix the Swimmer class missing the w in one instance
- Change Pkmn Ranger to Pokémon Ranger for consistency
- Remove unintended double spaces
2026-02-27 09:07:24 -06:00
sora10pls
ea36292994 Unban Garchompite Z 2026-02-27 08:39:57 -05:00
Kurt
7fad9a0c47 Add special scan pity counter property 2026-02-26 22:48:21 -06:00
Ka-n00b
2f456fceb3
Update FRLG Event Flags and some translations (#4732)
* Update lang_ko.txt

* Update lang_zh-Hant.txt

* Update lang_ko.txt

* Update const_frlg_en.txt

* Update const_frlg_es-419.txt

* Update const_frlg_es.txt

* Update const_frlg_fr.txt

* Update const_frlg_ja.txt

* Update const_frlg_zh-Hans.txt

* Update lang_ko.txt

* Update lang_ko.txt

* Update lang_ko.txt
2026-02-26 11:26:00 -06:00
Kurt
306c1329ed Add gameversion for Champions (53) 2026-02-25 23:25:32 -06:00
Kurt
07a826292c Update SaveBlockAccessor9ZA.cs 2026-02-25 01:20:15 -06:00
Kurt
3371c791ef Add HyperspaceZones9a
No GUI at this time, but seed can be changed via Block editor
2026-02-25 01:15:37 -06:00
abcboy101
2559f96439
Reset clothing when changing gender in ZA (#4728) 2026-02-23 08:35:05 -06:00
Kurt
2210068013 Revise shiny stars, only show squares in Gen8
They only exist in Gen8, no point showing in other contexts.
2026-02-22 23:33:00 -06:00
Kurt
31b7b7f723 Update BatchInfo.cs
https://github.com/kwsch/PKHeX/discussions/4725#discussioncomment-15891316
2026-02-22 23:22:05 -06:00
Kurt
364e014848 PA8: more initial move mastery suggest fixes
Evolved mon's with different learnsets need to use the initial encounter data species-form rather than the most-evolved, as some can be different.
https://github.com/kwsch/PKHeX/discussions/4725#discussioncomment-15889980
2026-02-22 12:22:53 -06:00
Kurt
3fc2971df1 SCBlock: Determine exact size on serialize
No need to estimate, just loop through. Prevents a large-object allocation for the stream.
Not sure if it is worth refactoring memorystream/binarywriter to just be raw spans to eliminate that overhead. Not that this is even a hot path, or that BinaryWriter adds much of anything besides moving stack logic to the object.
2026-02-22 12:22:07 -06:00
Kurt
d690f1c5d3 Check unsaved entity on sav export
Add setting to skip the unsaved entity check
Add setting to skip the overwrite? prompt and always call Save As

Change Overwrite prompt to have distinct buttons rather than rows that can be mis-clicked.

fix some comments/strings from Pokemon=>Pokémon

add some underline shortcut key for main menu for English translation
2026-02-22 11:11:16 -06:00
Kurt
1aedb012ac Add box popout button on left side of box control
Previously "hidden" in the shortcut keys by clicking on the Box tab, this makes it more discoverable. Old hotkeys still retained.

fixed hidden 0x200E char in Fashion button text (ancient typo)

Update Program.cs
2026-02-21 21:20:12 -06:00
Kurt
395bc1b1e9 Misc GUI tweaks
database Reset filters now auto-sizes, tabs are now taller
box popup now shows the switch-view button as an actual button rather than a flat image
2026-02-21 19:41:05 -06:00
Kurt
11c4fe446e Fix met location highlight on startup
Finally found the right place to put this
2026-02-21 19:38:02 -06:00
Carbonara
ddba4dae44
Translate the RSE flags to French (#4727)
* Add French translation for the RS flags

* Translate the Emerald flags in French

- Fix Peeko, Kindler Cole, Triathlete Talia and Psychic Mariela being misspelled in the English config
- Fix Aroma Lady Rose rematches being incorrectly listed for Aroma Lady Violet
- Fix Handsome the Zigzagoon being referred as being a guy
- Fix some obvious typos
- Changed the wording of some lines the flags_rs_fr file
2026-02-21 17:13:38 -06:00
Kurt
2efa19e5e3 Refactor batch editor to smaller components
Did something similar for NHSE years ago. Allows for much easier reuse elsewhere and clearer usage.
2026-02-21 00:22:32 -06:00
Kurt
0b42f57534 PA8: More tweaks to mastery setting 2026-02-20 23:21:33 -06:00
Kurt
0757ca3a5d PA8: Skip move purchase if can naturally learn
https://github.com/kwsch/PKHeX/discussions/4725
Enhances the .MoveMastery=$suggestAll
2026-02-20 09:22:11 -06:00
Kurt
6f9daaed04 Small tweaks to HGSS ball check
small lol
would need fully implemented pal park trash byte checks, big sad
leave stuff stubbed for now, can clamp down later.

restrict some method sigs for IEncounterTemplate (rather than more-derived IEncounterable) for consistency
2026-02-20 01:36:46 -06:00
Kurt
f14cfaf08d Unban Blazikenite
Season 7 has begun
2026-02-19 09:39:01 -06:00
Kurt
b6ae27e4e8 Add optional delegate for mid-batch-modify
Currently unused by everything; allows a compiled function to be run between the Filters and Modifiers
2026-02-19 09:37:25 -06:00
Carbonara
13fc0cdfeb
Update the French readme file (#4726) 2026-02-18 16:55:47 -06:00
Kurt
aab826ef13 Misc tweaks
Rename args for better clarity
Fix invalid pokerus on gen2 unit test case (apparently pkrs in gen2 was unchecked until recent miscverifier fix)
2026-02-18 07:46:39 -06:00
ShadowMario3
a9f449ff01
Update ItemStorage3E.cs (#4723)
Fix PC Storage for Emerald. Unlike R/S, key items aren't allowed.
2026-02-17 14:48:27 -06:00
Kurt
29a08bf988 Allow box dumper to retain Main control
Allows for quickly flipping current boxes.
2026-02-16 23:33:51 -06:00
Kurt
53c684a223 Add numeric operators to StringInstruction set
okay can we stop asking for this now? your "random fudging of EXP" to make it seem more legit really is ugly, and doesn't fool anyone lol
2026-02-16 23:14:38 -06:00
Kurt
f6bae2b8d7 Improve hash inflation of NSO saves
Wow such a bottleneck in the application (literally nothing calls this, but it was fun to optimize)
2026-02-16 23:13:41 -06:00
Carbonara
1d25b78a19
Translate more flags and constants to French (#4718)
- Write événement as évènement for consistency (both spellings are valid, but évènement is the one that is used in recent Pokémon games)
- Fix Réfrigérateur being written as Réfrigirateur
Translate constants for Ruby/Sapphire, FireRed/LeafGreen, Emerald, Diamond/Pearl, Platinum, X/Y, OR/AS
Translate flags for Diamond/Pearl, Platinum, X/Y
2026-02-15 22:54:58 -06:00
Kurt
ceb420a2a1 Misc gui tweaks
Only show Square shiny in Gen8 context (sw/sh only) to avoid confusion
Fix method name typo
Close subforms in reverse to avoid allocating a temp list
Close splash screen entirely async rather than dual Task.Run
Translate the entirety of the EntitySearchSetup (comparator button/menu now translates)
Launch box popup to the right of the main form, like the Search behavior
Fix dark mode coloring of popup box editor/group viewer images
Fix dark mode RichTextBox retaining border when it should be removed (white was annoying); was early-returning due to satisfying TextBoxBase
2026-02-15 02:19:09 -06:00
Kurt
9792455f34 Refactor to use Context over Generation
Generation was always more weak; am I paranoid about potential VC3? maybe
Better indicates the move source for LGPE exclusive moves, etc.
2026-02-15 02:15:50 -06:00
Kurt
387c254aa4 Split MiscVerifier into more focused checkers
Fixes logic flow for Stadium legality check (wasn't even called)
2026-02-15 02:12:39 -06:00
9B1td0
1e5663433a
Add EUIC 2026 Yuma Kinugawa's Hisuian Typhlosion date (#4716) 2026-02-13 07:42:29 -06:00
间辞
1ec0b22a0a
Add files via upload (#4715) 2026-02-12 22:18:05 -06:00
Kurt
782ee643d6 Replace box nav arrows with images 2026-02-11 23:43:54 -06:00
Kurt
7f51c125bd Add search nav buttons to panel
TopMost => Owner
pressing enter applies search (except if entering text to advanced, or focused on a button)

Co-Authored-By: RandomGuy <69272011+RandomGuy155@users.noreply.github.com>
2026-02-11 23:22:49 -06:00
Kurt
4ecd51e826 Add reverse search, remember result
ctrl+shift => reverse
shift => forwards
2026-02-11 22:59:33 -06:00
Kurt
3fa6ab9c23 Refactor Format to search Context instead
Increase size of left / right buttons to restore << >>
might change them to be icons later
2026-02-11 22:21:01 -06:00
Kurt
20905cbe67
Add a search interface for visually filtering all slots (#4712)
* Add slot search to box editor
Alt-Click: Clears the current search.
Shift-Click: Jump to the next box with a result.
2026-02-09 22:03:18 -06:00
Carbonara
0f8321cda4
Translate SMUSUSM flags and constants to French (#4713)
* Translate SMUSUSM flags and constants to French

* Adjust the text of some SMUSUM flags and constants

Put consistency for some lines between Ultra-Sun/Ultra-Moon and Sun/moon:
- Use the Hall of Fame/Magearna line from USUM for both games (same meaning, and no reason for it to be different; the Hant line was using the Hans line for USUM, so adjusted too)
- Adjust the name of Youngster Tristan to constantly be referred as such (Korean doesn't use the title if I'm not mistaken, but the naming is consistent so not an issue)

- Change the League Fangirls event line: as far as I understand, the fangirls have 3 status (not yet present, here to give Sweet Hearts, and Sweet Hearts given/gone). Logically as such, they are only here after 2 title defenses have been done, and do not appear if only a single title defense has been done.
2026-02-08 08:55:57 -06:00
Kurt
09d7fd9e31 Minor clean
Remove unused usings from bag refactor
remove unnecessary suppression (resharper fixed the ConstantExpected trickle up)
fix gen6/7 timestamp previous offset (has been broken for 6.5 years) 1b028198ad (diff-7e597cadc592f504e9105ba631f99ef9e1fe27ea9becbe191c15c00daa3272f2L211)
2026-02-08 01:22:21 -06:00
Kurt
dd1b55cb6a Update EncounterStatic3XD.cs
https://projectpokemon.org/home/forums/topic/57375-pkhex-new-update-legality-errors-contribution-page/page/35/#findComment-299242
2026-02-07 16:24:21 -06:00
Kurt
51711bb659 Extract entity filters UserControl, add Nickname
Introduce nickname searching and a reusable EntitySearchControl UI.
SearchSettings: add Nickname property, centralize search predicate creation
Add SatisfiesFilterNickname that reads the PKM nickname (stackalloc buffer) and performs a case-insensitive substring match.
2026-02-07 02:30:03 -06:00
Kurt
0982e54dac Revise dppt/hgss ball value check: Ranger Manaphy 2026-02-07 00:32:19 -06:00
Kurt
3ef46268e9 Indicate invalid PP(ups) count on localize
Viewing the invalid mon will have the UI fix it, so at least it gives some clarity as to what is actually being flagged.
VC->Bank is the big offender here.
2026-02-05 10:04:32 -06:00
Kurt
c5b7eb4c7d Bag: reset quantity on selecting (None)
adds validation for the Item select column as well.
Validation for item=0 requiring count=0.
2026-02-05 04:33:15 -06:00
Kurt
916521f906 Keep 5 digits of text entry as enough 2026-01-31 22:26:42 -06:00
Kurt
91ac18dd34 Revise Inventory edit count entry
Previous: limited to log10(max) characters
Now: cell changed -> parse/check against item count max & replace if exceeds.

Let the validation run even for Removing all items, why not?

Resolves: allows manual entry of >=1000 Mega Shards (previous release wouldn't clamp to 999, at least).
2026-01-31 22:12:23 -06:00
Kurt
5efe8b05ea Update 26.01.31 2026-01-31 20:41:14 -06:00
Carbonara
4a3157a8a2
Add French translation for HGSS script (#4707)
- Fix School Kid Torin being listed as a Camper (English, Spanish and Korean, other languages already fixed it)
- Replace "Latias/Latios" by "Lati@s" in the HGSS flags for consistency (English only)
2026-01-31 10:34:02 -06:00
Carbonara
d266ec7b5f
Unban Swampertite (#4706)
The Swampertite is now available since the Season 6 release that occurred on Thursday.
2026-01-31 06:46:58 -06:00
Kurt
e6edb043c4
PlayerBag Abstraction (#4705)
* Refactor bag interactions

Still need to normalize the offsets for some of the games so that init-from-span can be used on un-padded RAM dumps.

* Convert offsets to relative, minor clean

b2w2 & xy MyItem type now returns the more-derived type for clarity
2026-01-30 16:17:56 -06:00
Kurt
11c0f86d80 Update SAV_BoxList.cs 2026-01-29 16:58:36 -06:00
Kurt
f816b06d97 Update check for Barb Barrage evolution move
Closes #4698
2026-01-23 23:41:02 -06:00
Kurt
ad96b048b2 ShowdownSet: Parse wrong-ordered EVs
Previously were ignored.

Thanks Claude Opus 4.5, it 1-shot the entire thing from my detailed prompt & unit test follow up request.
I added a skip-blank line for ParseLines when people import a set with a trailing newline. Rather than a blank "invalid line length {0}"
2026-01-23 16:41:16 -06:00
Kurt
fe32739494 Update 26.01.22 2026-01-22 19:07:33 -06:00
Kurt
106d09c74f SIZE_G9ZA_201 2026-01-21 20:07:39 -06:00
间辞
d9c7980fd3
Update CHS translations (#4694) 2026-01-14 09:44:06 -06:00
abcboy101
74ef6d7378
Validate Odd Egg OT when still an Egg (#4693) 2026-01-13 23:13:39 -06:00
Kurt
c7b5777068 Allow settings tab text to be translated
Remove unnecessary selection on launch (winforms bug?)
Increase first column width so OT Version doesn't wrap to 2 lines.

Remove some unused usings in other files (a result of color repointing to WinFormsUtil)

ty @randomguy155 for the OnShown workaround

Co-Authored-By: RandomGuy <69272011+RandomGuy155@users.noreply.github.com>
2026-01-13 01:45:38 -06:00
Kurt
aa2d83cda0 Update .editorconfig 2026-01-12 22:21:54 -06:00
Kurt
19655ec2ec Misc tweaks
no functional change
HaX popup now uses Task Dialog api for cleaner impl
2026-01-12 21:48:28 -06:00
Kurt
3489555f74 Add edge case handling for forgotten initial moves
bdsp/sv/swsh eggs in PLA: original egg relearn are unable to be referenced, so we need to permit all
similar for BDSP Underground special moves (egg move sharing via daycare though). Also for any oddballs in SWSH that had relearn moves for special moves.
2026-01-12 21:48:02 -06:00
Kurt
18c4f2be26 Relocate trade name fetch to EncounterUtil
Simplifies ResourceUtil to no longer have specialized methods specific to Pokémon

duplicates the gen7 zh trade files; not an issue in duplication (will compress out) and simplifies the array fetching operation to be a single method rather than many.
2026-01-12 21:07:20 -06:00
Kurt
2560b7c677 Check all languages for correct lengths
pesky eol
2026-01-12 21:05:25 -06:00
间辞
e63d514367
Update CHS Translation (#4691) 2026-01-11 01:14:24 -06:00
Kurt
6e482946e2 Add Gen4 HG/SS ball check
GUI would display the selected ball, but internally it was forced back to Poke.
Currently, the setter sanitizes both values, but if manually modified via external code, technically it could be in an invalid state.

- Add check if ball is disassociated from what is expected (modified outside of the GUI)
- GUI updates to the final (sanity checked) value regardless of what was selected.

https://projectpokemon.org/home/forums/topic/57375-pkhex-new-update-legality-errors-contribution-page/page/35/#findComment-298902
2026-01-11 00:55:05 -06:00
Kurt
3a8bc5889b Enhance H/W/S invalid messages, flag !255 alphas
https://projectpokemon.org/home/forums/topic/57375-pkhex-new-update-legality-errors-contribution-page/page/35/#findComment-298916

ty ATRociousFuBear
2026-01-11 00:37:48 -06:00
Kurt
4ea08b3403 Update MoveApplicator.cs 2026-01-10 23:27:28 -06:00
Kurt
733c829570 Minor tweak
Small reduction in allocation for a method that is ever so rarely used, but yay me
2026-01-10 23:17:47 -06:00
Kurt
06d95efc64 Moveset: add implicit ReadOnlySpan conversion
Clean up some usages where we duplicated methods. The one that remains for Relearn sequence equality is OK.
JIT compiler can lower the AsSpan to new Span(4, ptr) and give near-similar performance to InlineArray. I prefer it this way because InlineArray wouldn't work with new(1), as all 4 moves would need declaration.
2026-01-10 23:16:33 -06:00
Kurt
0fe0b704d1 Minor tweaks
Allow localizing the Legality Report and File Overwrite dialogs added in .NET 10 update

Simplify evo restriction check
2026-01-10 19:27:45 -06:00
Ka-n00b
99cb6769bd
Update GSC Event Flags and ZA Block Data (#4689)
* Update const_c_es-419.txt
* Update const_c_es.txt
* Update const_gs_es-419.txt
* Update const_gs_es.txt
* Update flags_c_es-419.txt
* Update flags_c_es.txt
* Update flags_gs_es-419.txt
* Update flags_gs_es.txt

* Update SaveBlockAccessor9ZA.cs (Rogue Mega Simulator)
2026-01-09 15:17:45 -06:00
Kurt
18f95269c0 Misc edge case tests
Gen9a Antishiny edge case
Evolve-move traversal tweaks; eager checks and more
2026-01-08 23:41:02 -06:00
André Bastos Dias
812f8e847e
Add Dragon Pulse as a Species Evolution Move for Naganadel (#4687) 2026-01-08 21:07:52 -06:00
Kurt
e9cb358c50 Improve Battle Revolution checksum calc
4.5 ms => 2.4 ms on my cpu (nearly 2x as fast), even better for lesser CPUs. Probably isn't worth parallelizing.
2026-01-08 19:50:46 -06:00
Kurt
e2c09730b5 Update 26.01.07
Sceptilite released
Revise XD eevee encounter generating for shiny requests (disregard insufficient TID/SID)
Skip "overwrite" popup if savefile was loaded from a backup (bak)
2026-01-08 00:48:28 -06:00
Kurt
3d74e763ba Misc dark mode color tweaks
Centralize remaining vibrant colors to WinFormsUtil
2026-01-08 00:26:09 -06:00
Kurt
e3fa760f52 Gift2: fix template->pk2 japanese enc
as observed in discussion #4684

now matches Gift1 implementation details

I really wish we had separate classes for PK1/PK2 for each language since the string buffers are different length, oh well this footgun exists.
2026-01-05 00:42:20 -06:00
Kurt
85abb48da3 Misc tweaks for Hoopa-1 plus flags
Merge handling with Rotom's handling
Add logic for "Require" & "Set All" operations -- if the form isn't 0.

https://projectpokemon.org/home/forums/topic/57375-pkhex-new-update-legality-errors-contribution-page/page/35/#findComment-298854
2026-01-04 23:44:57 -06:00
André Bastos Dias
7621087e7e
Fix EncounterCriteria#IsSpecifiedIVs xmldoc description (#4683) 2026-01-03 18:38:37 -06:00
Jonathan Herbert
2ea7a60f3b
Donut Timestamp Fixes + Improvements (#4681)
* Place Time After Date In Donut Editor Format

* Fix Donut Millisecond Offset Being 1970 Not 1900
2026-01-03 12:01:13 -06:00
Jonathan Herbert
fecd2a3ddb
Fix Importing Donut (#4680)
Also fix donutEditor not following the variable naming convention
2026-01-02 21:07:57 -06:00
Kurt
8c5eb6fa9f Extract some logic from QR7
Update notes, update method names.
10 years since this (apparently custom?) format was whipped up. Probably good to have more accurate documentation for this early-injection format?
2026-01-02 13:25:12 -06:00
Kurt
fb52a5ef18 Update EffortValueVerifier.cs
Closes #4679
ty @andrebastosdias !

Revise untrained EV check to flag Pokespot encounters (the only varied level range encounter in gen3/4) where min level might be less than the actual level it was obtained at. If/when pokespot correlation is better refined, can switch to evotree level min for a gen3->4 transfer, so that something at a not-minimum exact level can be flagged if it has non-Vitamin EVs.
2026-01-02 13:10:17 -06:00
Kurt
5f65b333ba Update dependencies
Minor simplification in QRCode gen
2025-12-31 02:12:20 -06:00
Kurt
fc167caea3 Update readmes per net10/c#14 2025-12-31 01:50:51 -06:00
Kurt
2c541ad422
Update to .NET 10 (#4676)
* Update to .NET 10
* Property fields
* API signature updates
* Extension method blocks

* Completed dark mode support
  Outside of my control:
- vertical tab control (pkm editor)
- datetimepicker controls
- lgpe event flags (no idea)
- some control types having white-borders when they should really be gray

Box background is 50% transparency to effectively darken the image.

* Custom legality report popup
* Event diff dialog, version select dialog
* Add quick overwrite popup for export sav
* Extension methods
* Dark Mode: glow currently editing sprite
* Add invalid encounter hint for trade evolutions
* Extension properties
* Append legality hint on hover card
* Slot image loading: clear the screen-reader description if a slot is empty/invalid, rather than retain the previous description. Changing boxes would easily confuse users on this.
2025-12-31 01:42:05 -06:00
Parnassius
4ee9e4ad31
Misc fixes for FormInfo (#4678) 2025-12-31 01:21:44 -06:00
Parnassius
af98a5b5e9
Fix FormInfo.IsBattleOnlyForm for Zygarde-Complete (#4677) 2025-12-30 12:10:40 -06:00
Parnassius
972aaba5fa
Add Zygarde to FormInfo.BattleMegas (#4667)
`IsBattleMegaForm` already checks for Mega Zygarde, but since it's only
called if the species is in `BattleMegas`, both `IsBattleOnlyForm` and
`IsMegaForm` currently return false for Mega Zygarde
2025-12-29 15:24:40 -06:00
RandomGuy
3ca4acd7d2
Add Donut Flavor Profile Display to Donut Editor (#4673)
* Add Donut Flavor Profile Display to Donut Editor

* Update profile on berry change
2025-12-29 15:20:54 -06:00
RandomGuy
870c10ea5e
Refactor SAV_Misc3 Battle Frontier editor to use object-oriented block (#4670)
* Refactor SAV_Misc3 Battle Frontier editor to use object-oriented block class

Replaces direct byte array manipulation with BattleFrontier3 struct:
- Encapsulates all offset calculations and data access
- Uses type-safe enums for facilities, modes, and stats
2025-12-29 14:56:10 -06:00
902PM
305a2733c6
Update Japanese Flags Translations (#4671)
* Update const_c_ja.txt
* Update flags_c_ja.txt
* Update flags_gs_ja.txt
* Update flags_dp_ja.txt
* Update flags_pt_ja.txt
* Update const_bw_ja.txt
* Update const_b2w2_ja.txt
* Update flags_c_ja.txt
2025-12-29 14:13:14 -06:00
Kurt
e1f6847ed9 Extend duplicate mega stone checker -> unique
Now covers primal orbs too.
Update translations, enhanced to show the item ID that they're duplicate (less to check when inspecting an output)
2025-12-29 14:11:35 -06:00
Kurt
d6cb992d11 Fix typo
happy now, cleo-caretaker?
2025-12-21 14:26:10 -06:00
Kurt
61a13fda08 Update EncounterStatic9a.cs 2025-12-21 14:20:27 -06:00
Kurt
31edf20c87 Update 25.12.21
Moves one of the Evolution deferral checks to the encounter template where it triggers; no other encounter case will trip that check so it's OK to move it there. One less thing for every other encounter to check.

Revises the "met date present but no met location" to only flag if the encounter was matched to something. It'll already yell at mismatched encounters, no need to pile on more. The check only exists for eggs (no location).
2025-12-21 12:59:55 -06:00
Kurt
7d1bcfa354 Z-A: Add Street Name side-mission string 2025-12-20 14:49:22 -06:00
Kurt
6609dd210b Misc legality fixes for Z-A alterations
- Evolving knowing move: relearnable additions in the evolved stage was bypassing the requirement (Sylveon can relearn Charm at any level, but Eevee cannot). Prune tree to only check if pre-evolutions could have learned move.
- FormArgument requiring a minimum level to actually use the move (Primeape). Probably isn't a "complete" check, since it's implemented differently compared to Qwilfish's logic. Might be worth revising in the future to be consistent (using the same as Primeape logic? if in game, and can learn, can increase from 0).
- Flag Hangry Morpeko if cannot learn Aura Wheel yet
- Flag mega evo mismatches for Tatsugiri/Magearna/Meowstic
- Permit mega meowstic gender in party

- Remap DLC TMs (I forgot this remapping was needed; pkNX dumped it but I didn't update the table until now...)
2025-12-20 14:44:08 -06:00
RandomGuy
1edfbfab0e
Add Flavor Image Display to Donut Editor (#4666) 2025-12-20 12:45:10 -06:00
hexbyt3
cdbc2b5599
Z-A: Remove outdated comment in BallUseLegality.cs (#4664)
Removed comment for Dream/Beast Ball legality in ZA. Ball is available with the introduction of the Mega Dimension DLC.
2025-12-18 16:16:52 -06:00
间辞
bc0f255ae3
Z-A: Add CHS translation for trainer editor Collect TMs button (#4663) 2025-12-18 16:14:55 -06:00
sora10pls
2bba2b2c8d Unban Baxcalibrite 2025-12-18 07:52:40 -05:00
RandomGuy
bb41fb70e6
Add Star Display to Donut Editor (#4662) 2025-12-17 23:36:49 -06:00
Kurt
311314c3d3 Update FormArgumentVerifier.cs 2025-12-17 23:11:31 -06:00
Makio
58b2c31a66
Implement flavor score and star calculation for donuts (#4661)
* Implement flavor score and star calculation for donuts

Added logic to compute the flavor score and assign star ratings based on defined thresholds in the RecalculateDonutStats method. This enhances donut stat recalculation by including flavor and star values.

120 Points: 1 star
240 Points: 2 stars
360 Points: 3 stars
700 Points: 4 stars
960 Points: 5 stars
2025-12-17 23:05:16 -06:00
Kurt
b3fc5c62bf Update IFormArgument.cs 2025-12-17 11:39:03 -06:00
Kurt
19fde890fe Update personal_za 2025-12-17 10:28:13 -06:00
Professor Dirty
ada5d76a99
Update B/W Event Flags CHS Translation (#4659) 2025-12-17 02:53:38 -06:00
Kurt
3e0ea816cb minor tweaks
No functional change
2025-12-17 02:39:32 -06:00
Kurt
7695fb05bc Add handling for rotom form change
Plus moves permitted for all forms
(Wild) Encounter recognized if form is changed
2025-12-17 02:32:56 -06:00
Kurt
1d30ad0b43 Fix Raichu-1 alpha move required 2025-12-17 02:12:00 -06:00
Kurt
64f655b5b6 Z-A: Allow farfetch'd-1/sirfetch'd formarg
Revises the API for requesting a suggested Form Argument, based on visitation to various games with different rules.
2025-12-17 02:11:51 -06:00
Kurt
b078e0e735 Z-A: Disallow trading primal orbs 2025-12-17 02:10:54 -06:00
Kurt
7c63cacebc Z-A: Disallow holding Gimmighoul Coin
Only 3 items are classified as CanNotHold -- screw, coin, and rotom catalog (key item)
2025-12-17 02:10:41 -06:00
间辞
6b23c4b7d5
Update CHS translation of donut editor (#4658)
And hyperspace survey points in trainer editor
both new additions in the most recent release
2025-12-16 02:47:51 -06:00
Kurt
e5477d6e2d Disallow cherish ball
lol, lmao even
2025-12-16 02:37:50 -06:00
Kurt
e95dddd86d Fix initial move application of low leveled mons
Relearn moves shouldn't really be applied by default (fixes honedge expecting Sacred Sword)
Add edge case for modified learnset (Espurr) pre-dlc as an untouched level 7-8 capture. Only need 3 moves.

ty abby on discord
2025-12-16 01:18:13 -06:00
Kurt
c8eb4f548f Update 25.12.15 2025-12-15 23:19:16 -06:00
Kurt
a2a209ff2c Fix parse type on different columns
ColumnValue1 is long
ColumnValue2 is ulong

a value > long.MaxValue in ColumnValue2 would popup an error message on form load.
now fixed
2025-12-15 22:41:31 -06:00
Kurt
de2c6151e6 Minor tweaks
Simplify moveset application for Z-A; less branching since they all do essentially the same thing. The API is pretty stable so the simplifications are safe.
2025-12-15 22:34:57 -06:00
Kurt
84912a16e7 Misc encounter search/mutating fixes
ty santacrab
2025-12-15 22:33:42 -06:00
Kurt
ac25835d65 Finish Donut struct (0x00 is milliseconds!)
Interlink the GUI for ticks+calendar so that modifying one updates the other if applicable.

add randomize (very simplistic, just pick a random lv3 power)
add all-shiny (sparkling, alpha/big/little, catching)
2025-12-15 22:33:11 -06:00
Kurt
d45cdcb9e0 Misc tweaks
Fixes default moves being in inverse order
2025-12-15 15:28:26 -06:00
Kurt
6cefac2656 Update Primeape/Qwilfish formarg evo logic
Needs long-form logic to be more maintainable.

Closes #4656

Co-Authored-By: Dennis <64029159+CScorpion-h@users.noreply.github.com>
2025-12-15 01:40:01 -06:00
Kurt
9840120161 Add dragdrop for donut file
Export dialog now shows localized donut name rather than "donut"
2025-12-15 00:41:46 -06:00
Kurt
89fb9b0471 last batch of 🥒
- Updates the hyperspace pkl, adds the special band mons with their dedicated spawner's higher boost level
- Updates the overworld pkl, adds the feebas spawn (2 spawners, same location & data)

Add steelix to onix's plus move permit bypass, oops.

Interesting to note that Latias/Latios had some flying band encounters, but there's no spawner to generate them?
2025-12-15 00:00:14 -06:00
Kurt
4d5480e64c Update DonutEditor9a.cs 2025-12-14 20:06:07 -06:00
Kurt
1c4070b6a8 Update hyperspace encounters, misc checks
Adds pickle from all possible random encounter sets in hyperspace
Updates some formarg checks for certain species
Updates plus move checks for movesets that were revised by DLC

Hyperspace encounters are in a separate array, with a different slot type

Add note for Teensy/Humungo for wild encounters causing a fixed scale value rather than random.

Should be noted that this is a first-stab at encounters, and things have not been tested sufficiently to ensure the level ranges/etc are actually good data. please don't use the encounters yet; this just gets it out to testers for finding more edge cases.
2025-12-14 20:01:07 -06:00
sora10pls
0bc805b973 Add initial handling for donut icon loading 2025-12-14 17:57:42 -05:00
Kurt
29d364067a Donut9a: guard against bad donuts
moldy donuts yuck
2025-12-14 14:00:47 -06:00
Kurt
47cd845d05 Add placeholder donut picturebox, update layout 2025-12-14 13:31:50 -06:00
Kurt
5d0f1c9c37 Update SAV_Misc3.cs
Closes #4652

Co-Authored-By: Ryan Gabel <98432212+rjgabel@users.noreply.github.com>
2025-12-14 10:54:10 -06:00
Kurt
189b9bece8 Hide donut editor for base game saves 2025-12-14 10:45:23 -06:00
Kurt
07eadfec16 Add import/export donut
Hold control to set to clipboard for easier copypaste between slots/RAM windows
2025-12-14 02:03:13 -06:00
Kurt
ad550da3ae Update SAV_Trainer9a.cs 2025-12-14 01:16:00 -06:00
Kurt
437e0b8e23 Fix colorful screw collection button localization
Was an oopsie from a pull request, all good, I had hidden the button with the new TM button anyway.
Now it's all fixed :D
2025-12-14 00:41:00 -06:00
Kurt
69e519602b Create SizePower9a.cs
https: //x.com/Sibuna_Switch/status/2000090054371537099
Co-Authored-By: Lusamine <30205550+Lusamine@users.noreply.github.com>
2025-12-14 00:32:44 -06:00
Kurt
ef374f2d22 Add Donut editor
thanks to everyone who watched along while I implemented this
2025-12-14 00:14:43 -06:00
Kurt
2498b27363 Add Hyperspace Survey Points editor (DLC tab) 2025-12-14 00:14:08 -06:00
sora10pls
ed7b40e215 Finish up Mega Dimension static/gift/trade encs 2025-12-13 21:35:29 -05:00
Kurt
e89bf3a416 ZA Blank save indicate as -MD
Needed to change the blank block's type. Technically I could have made SaveRevision a readonly field rather than computed...
2025-12-12 13:14:32 -06:00
Kurt
aee2d1a556 Meowstic/Magearna dex edit/set
Also fixes tatsugiri form set when giving all
2025-12-12 12:54:56 -06:00
Dennis
c148da9ab4
Unban beast and safari (#4655)
can be obtained in the Hyperspace Battle Zone.
2025-12-12 08:32:14 -06:00
Kurt
70f5b2ddaa Update translations 2025-12-12 02:19:21 -06:00
Kurt
4c5efe5ae6 Update 25.12.12
Initial partial support for DLC.
Encounters and other various QoL features to follow in future commits.
2025-12-12 01:38:06 -06:00
Kurt
34f3624b64
Changes for Legends: Z-A (Mega Dimension) support (#4653)
Refer to pull request notes and the eventual changelog for a high-level summary.

Co-authored-by: Matt <17801814+sora10pls@users.noreply.github.com>
Co-authored-by: Lusamine <30205550+Lusamine@users.noreply.github.com>
Co-authored-by: SciresM <8676005+SciresM@users.noreply.github.com>
2025-12-12 01:30:35 -06:00
sora10pls
f2d33bf0cf WA9: Alpha Charizard date range 2025-12-09 09:23:46 -05:00
sora10pls
2d8145b96c Add latest distribution raid data 🐲🔫 2025-12-04 19:02:51 -05:00
Kurt
6603984f88 Update 25.12.02 2025-12-02 08:41:49 -06:00
sora10pls
86a19e4a16 Add support for Project M 2025-12-02 08:08:48 -05:00
sora10pls
d8caf74653 Fix uncatchable Tera Raid Battle pickling 2025-12-02 08:05:58 -05:00
Kurt
def9802375 Allow seed of mastery to plus any learned move
Using a seed of mastery on any currently known move is allowed, regardless of the natural
2025-12-01 23:06:47 -06:00
Kurt
01dc5aa331 Remove duplicate line returns
No functional change, just an OCD nitpick
2025-12-01 22:53:27 -06:00
sora10pls
3e1499bdf4 Add HOME Fidough event date range
Forgot to add this for an entire month, oops
2025-12-01 09:25:54 -05:00
Kurt
72008a8e60 Add IFixedTrainer interface tag
Allows SysBot.NET to detect it more easily
2025-12-01 00:11:19 -06:00
Kurt
cc6a26a757 Update 25.11.30 2025-11-30 23:33:16 -06:00
Pasquale Nardiello
dc1818d589
Added Appearence editor for ZA and annotated hair styles and eye cuts. (#4642) 2025-11-30 21:51:12 -06:00
Carbonara
7658ba8994
Add French translation for B2W2 flags and constants (#4644)
* Create const_bw_fr.txt

* Fix an incorrect name for the P2 Laboratory event

The Scientist of the P2 Laboratory event used the name Dudley (Black 2 and White 2 scientist) instead of the name Nathan (Black and White scientist).

Chinese languages already seem to be correct, while I cannot directly fix the Korean translation myself as unlike other languages, I do not have any wiki source to find the trainer name (if you have this information, please feel free to fix it).

* Create const_b2w2_fr.txt

* Fix the Petilil-Cottonee order

* Create flags_b2w2_fr.txt
2025-11-27 10:22:32 -08:00
sora10pls
4a10b8e087 ZA 1.0.3 save file loading
One new boolean save block added, probably related to Ranked Mega Stone issue?
2025-11-27 09:42:51 -05:00
sora10pls
c4ca51dbcd ZA: Unban Dream Ball, Chesnaughtite 2025-11-26 08:59:43 -05:00
Kurt
85cf53fe8e Gen4: Add Mic Test possible IV seeds 2025-11-25 00:19:58 -08:00
Kurt
a437fecab8 Reuse EntityGender magic ratio values 2025-11-24 17:21:49 -08:00
Kurt
2da0e303a6 Extract method to allow specific forced detection 2025-11-24 17:21:31 -08:00
Kurt
2f1f08af84 gcea -> bacd_r_a
https://discord.com/channels/343093766477053953/406851200928055297/1442622561124094213
2025-11-24 17:15:51 -08:00
Professor Dirty
d0cf74b063
Add files via upload (#4643) 2025-11-23 18:57:14 -08:00
9Bitdo
01b66416c4
Add LAIC 2026 Federico Camporesi's Whimiscott date (#4641) 2025-11-21 08:39:14 -06:00
Kurt
a1c9e3a615 Gen7: hidden ability+gen4 ball on gen2-5 starters
Was mistakenly copypasted from Gen6's rules.

Thank you manolin18 for bringing this to my attention!

https://projectpokemon.org/home/forums/topic/67210-pok%C3%A9mon-starter-and-pokeball/#findComment-297255
2025-11-20 20:56:54 -06:00
sora10pls
7b091ce931 Add latest distribution raid data 🫃
Also updates existing raid/outbreak data using latest pkNX changes
2025-11-20 19:04:06 -05:00
Kurt
e8aecc85a6 Update EncounterSlot9a.cs 2025-11-18 17:01:36 -06:00
Kurt
84e5382134 Update EncounterGenerator9X.cs
Nobody cares about SW/SH for a month eh
2025-11-18 00:25:02 -06:00
Kurt
148d71e7ea Update EncounterGift9a.cs 2025-11-17 23:26:51 -06:00
sora10pls
d1574959d5 Update GO encounters per latest PGET changes
Early stages of Safari Ball handling.
2025-11-17 19:21:01 -05:00
Kurt
c74a5ef085 Alpha PlusMove for evos: use enc species
For people who mess with plus moves
2025-11-17 16:32:06 -06:00
Kurt
931276bf39 Update 25.11.16 2025-11-16 15:19:45 -06:00
Kurt
c9dbed6d8a ZA: Update handling for ability-change rules 2025-11-16 15:07:18 -06:00
Kurt
b0dfe2f57f Required move count: ignore evo/relearn 2025-11-16 15:07:03 -06:00
Kurt
af3f7f770b Update location detection result 2025-11-16 15:06:35 -06:00
Kurt
4c377d75cf Allow colorful screw quantity to save
Really need to refactor the entire Inventory handling because it isn't very flexible
2025-11-16 11:58:13 -06:00
abcboy101
a3e1d88243
Update Switch badwords to v21.0.0 (#4640) 2025-11-16 09:59:42 -06:00
Kurt
5113e5e641 Minor clean
Adds IEncounter9a to WA9 for extra metadata fetch
2025-11-15 22:25:33 -06:00
Kurt
9f21f45f25 Sanity check ability index on transfer
Passing an entity with AbilityNumber of 7 no longer throws an exception on transfer logic
2025-11-15 22:24:15 -06:00
Kurt
d138755ed0 Hide nonsensical dropdowns for gender/shiny
Internal values that were loosely bundled. The Criteria tab in the encdb shouldn't show these.
2025-11-15 22:22:44 -06:00
Kurt
e45754c830 Track more move sources
No change in coloration, but will show them for past games such as Egg Moves that it could have learned.
2025-11-15 22:21:07 -06:00
Kurt
d5bf6e67d5 Extract bonus move interface from slot6ao/8b-under 2025-11-15 22:15:25 -06:00
902PM
4d0bfa46df
Update translations (#4638)
* Update lang_ja.txt

* Update flags_c_ja.txt

* Update flags_gs_ja.txt

* Update const_e_ja.txt

* Update const_frlg_ja.txt

* Update flags_e_ja.txt

* Update flags_frlg_ja.txt

* Update flags_rs_ja.txt

* Update const_dp_ja.txt

* Update const_hgss_ja.txt

* Update const_pt_ja.txt

* Update flags_dp_ja.txt

* Update flags_hgss_ja.txt

* Update flags_pt_ja.txt

* Update flags_e_ja.txt

* Update const_b2w2_ja.txt

* Update flags_b2w2_ja.txt

* Update flags_bw_ja.txt

* Update lang_ja.txt
2025-11-13 21:37:59 -06:00
Kurt
11da2bb317 Update LegendsZAVerifier.cs 2025-11-13 20:11:31 -06:00
Kurt
89e10f0640 Allow level 100 rare candy evo for SW/SH+BD/SP
https://projectpokemon.org/home/forums/topic/57375-pkhex-new-update-legality-errors-contribution-page/page/34/#findComment-298102

ty JeongJeong
2025-11-10 18:43:18 -06:00
Professor Dirty
70a58f2a55
Update CHS translation (#4634) 2025-11-09 21:34:33 -06:00
Kurt
6a1c14af2f Revise some method signatures 2025-11-08 14:56:15 -06:00
Kurt
551c34b9ed Add latam spanish for enigma/transporter strings
#4632
2025-11-08 14:55:42 -06:00
Kurt
29629b865a Minor tweaks
MGDB shows WA9
re-add S/V PP/movetype (move 0 is 0 pp; in ZA it is 35)
2025-11-08 00:16:23 -06:00
Kurt
866d538c24 Improve randomization feel for prefill alpha IVs
Slightly slower due to random indexes, but negligible overall
2025-11-07 10:56:25 -06:00
Kurt
8b0c6c774a ZA: Revise A.Z. floette OT for spanish
uses non-breaking space instead of regular space
2025-11-07 10:34:57 -06:00
Kurt
27ca4ec76f Ignore shiny raid pidiv correlation
Was previously ignored, but the improvements to the enum return value for Z-A caused them to get flagged
2025-11-07 10:26:49 -06:00
Kurt
6e3d46142b WB8: revise date set for eggs
Closes #4571

same as PR with some rearranging/modifications (fork was archived so could not push to PR)

Egg was a direct redeem (not via HOME) so no egg date range

end result: don't set met date for IsEgg; logic flow rearranged

Co-Authored-By: HexByt3 <80122551+hexbyt3@users.noreply.github.com>
2025-11-07 10:06:23 -06:00
sora10pls
a6beda0e3b Update ItemStorage9ZA.cs 2025-11-07 10:56:39 -05:00
Kurt
5b9b9c2981 Disallow alpha mark on ZA alphas
Not set by default, no HOME connectivity to set it.
2025-11-07 03:07:26 -06:00
Kurt
bad73d8cbf Remove test code
burning the midnight oil
2025-11-07 02:46:35 -06:00
Kurt
c7427926cb Update 25.11.07 2025-11-07 02:41:29 -06:00
Kurt
9d719bdf06 Retain item values not exposed for edits
Fixes pouch getting nuked
2025-11-07 02:38:39 -06:00
Kurt
c218098f26 Add current context set ribbons only
Will clear anything picked up from other games.
Closes #4592
2025-11-07 01:15:33 -06:00
Kurt
19284fef79 Update 25.11.06
Fixes pcdata
Disabled Legal item sanitization on save (clearing unreleased mega stones)
2025-11-07 00:21:32 -06:00
Kurt
a6683e9e52 ZA: Fix item edit IsNewNotify
Behavior might be wrong in S/V but nobody reported? Either way, this fixes the behavior of old things seeming new again in Z-A.
2025-11-06 23:40:05 -06:00
sora10pls
2779f384eb Hey Sora! Get up on the Hydra's back! 2025-11-06 19:03:40 -05:00
sora10pls
89b5c12b3d ZA: Shine Bright like a Gemstone 2025-11-06 08:16:27 -05:00
Kurt
cca9781884 Add ConfigSave9a
Can set text speed to 3 for instant text.
2025-11-06 01:04:53 -06:00
Kurt
1e7fd1e918 Remove non-alpha slot if always-alpha 2025-11-05 22:39:40 -06:00
sora10pls
c1ba6f9a77 ZA: Unban Sport Ball, Delphoxite 2025-11-05 08:39:48 -05:00
Kurt
308364fb9e More tweaks/fixes in slot dumper
Fixed stale reference for slot merge
Revised AABB check to get WZ 8 matching
2025-11-05 00:55:14 -06:00
Kurt
8d604c6560 Fix level range hover indication 2025-11-05 00:54:28 -06:00
Kurt
2b2d5c2c0b ZA: Dex enhancements/fixes
Fix X/Y mega forms clearing the Y mega form
Fix Displayed form being clamped to max of 3 (Vivillon no longer an issue)
Add on-seen/capture bulk form granting
Add on-seen shiny bulk form granting
2025-11-04 18:46:26 -06:00
Kurt
5b9d7f1314 Update EncounterGift9a.cs 2025-11-04 14:26:48 -06:00
Kurt
31b48383c8 ZA: Update wild pkl from current
Solves WZ 6 & 10's secondary areas
Still has issues resolving the right area for overlapping outside areas, and potential crossovers
2025-11-04 13:28:41 -06:00
Kurt
512063239b Update Overworld8aRNG.cs 2025-11-04 13:27:53 -06:00
Kurt
3ea2ce6f38 Add EVs to batch dropdown list 2025-11-04 13:27:43 -06:00
abcboy101
724765e185
Remove es txt resource fallback logic (#4627)
No longer needed after commit 51a1caf628
2025-11-04 07:56:59 -06:00
abcboy101
ecbfe41f7f
Add LGPE/Gen 8/Gen 9 Wonder Card titles (#4625)
* Add LGPE/Gen 8/Gen 9 Wonder Card titles

* Fix CardTitleIndex
2025-11-03 19:49:30 -06:00
Kurt
51a1caf628 Duplicate es txt resources to es-419 2025-11-03 19:49:12 -06:00
sora10pls
4c9352dcbd Update MoveInfo.cs 2025-11-03 20:38:19 -05:00
Kurt
cac0ee3eaa Update PKMEditor.cs
Closes #4626
2025-11-03 15:24:21 -06:00
sora10pls
d5cc675d15 More ZA form considerations for Pumpkaboo/Gourgeist
Initial handling only really considered the form name changes for English, but in some localizations, more than just Average and Super got changed. Now include all four updated form strings for all localizations

FRA: Mini -> Petite, Maxi -> Grande
ITA: Mini -> Piccola
DEU: S -> Kleine, L -> Große
ES-ES: Pequeño -> Pequeña
2025-11-03 12:23:49 -05:00
Jonathan Herbert
6b1939deaf
Extend ZA Fashion Editor For SV Followup (#4624)
Fix Typos and Indentation Error
2025-11-02 22:00:08 -06:00
Jonathan Herbert
7e4d7773cc
Extend ZA Fashion Editor For SV (#4623) 2025-11-02 21:08:39 -06:00
Kurt
98fc80448a Show criteria in encounter db for tweaks 2025-11-02 18:43:23 -06:00
Kurt
a83ee19757 Minor clean 2025-11-02 17:54:23 -06:00
Kurt
0fc1e000e4 Add overworld playtime value 2025-11-02 17:22:07 -06:00
Kurt
b407d8f0a0 Show less-detailed scale eval in Z-A 2025-11-01 16:59:54 -05:00
Kurt
108bcf38d5 Update SAV_Trainer9a.cs 2025-11-01 10:46:19 -05:00
Kurt
5f718a9d4e Update PKMEditor.cs 2025-11-01 10:41:19 -05:00
Kurt
bde111af20 WA9: add audino, fix OT name/ID fetch
Since ZA was forked before S/V 2.0, they reverted the raw ID fix lol.
2025-11-01 10:23:01 -05:00
9Bitdo
268ce77173
Add Poké Center Fidough & Audino Birthday Gift's date (#4622) 2025-11-01 09:41:28 -05:00
Kurt
a6a532ff7a Update EncounterStatic9a.cs
fixes xy legend
2025-11-01 02:04:05 -05:00
Kurt
74a74c6749 Set alpha move in batch edit suggest 2025-10-31 22:21:25 -05:00
Kurt
46e9b6d2fc Don't modify screw count on GiveAll 2025-10-31 20:56:11 -05:00
Kurt
7013250688 Add Colorful Screw collector button-cheat
Trainer editor, press button to have them auto-collected to inventory.
Some modifiers available for those wanting to reset all or get a list of all for manual hunting :)
2025-10-31 20:39:50 -05:00
Kurt
d047f02410 Extract contest colors to static utility class
Fixes more hardcoded colors to SystemColors for easier darkmode handling
2025-10-31 20:26:51 -05:00
Kurt
b5c29b3de8 Minor tweaks
Allow dragdrop into menustrip/legal/vertical tabs to load file
ZA: Retain original criteria for cleanup application of IVs
Inline vertical tabs color choice
Simplify some expressions
2025-10-31 18:44:37 -05:00
Kurt
0e9d8db2b1 Fix plus move defer check 2025-10-31 00:51:01 -05:00
Kurt
bcd12478af Update EncounterSlot9a.cs 2025-10-31 00:47:32 -05:00
Kurt
649ba5f1f2 Fix alpha move flag check 2025-10-30 23:48:37 -05:00
Kurt
f4e6520afe Make some configurable color settings fixed
Better support for toggled color mode (Dark Mode)
Rewrites Marking sprite coloration so that Dark Mode doesn't show the marks as Black when active (instead show as the Text color).
2025-10-30 23:21:30 -05:00
Kurt
0b58c01866 Fix diff flagwork block type 2025-10-30 22:52:46 -05:00
Kurt
8933fb06d4 ZA Fashion: Less GUI lag when Set All Owned
Pretty much instant now. Requires a little bit of allocation but good enough.
Clone the save file so we don't mutate the original if the user opts to cancel.
2025-10-30 22:52:35 -05:00
Kurt
048f7cfe30 Revise PIDIV check return value to enum
Deduplicates slightly by indicating the true-"ignore" better.
Add deferral for Alpha Moves (flagged later via ZA Verifier, not needing a generic Partial error)
2025-10-30 22:50:38 -05:00
Lusamine
291d5be618 Surface LZA FieldItems block 2025-10-30 21:07:53 -05:00
Kurt
d5314a00f5 Allow game bug for trade evo's & plus moves 2025-10-29 17:40:13 -05:00
Kurt
702829bb20 Add more event block repo types
Allow bigger window
2025-10-29 16:34:10 -05:00
Kurt
4c2cc2f45f Add replace trainer name (Z)
profanity OT/nick => replace with this
not that it is currently being used, as none of the official events have bad OT names, and HOME can't transfer in stuff-yet-to-reset.
2025-10-29 16:33:49 -05:00
Kurt
e6a59740bb Manual ability ctrl-click suggest
Add control-click for the manual ability entry to auto-apply the corresponding ability based on personal info (try and detect birth ability)
2025-10-29 01:48:13 -05:00
Kurt
5bdfd4597c Fix non-English set imports of remapped forms
No need to do the English-specific remapping if the language isn't English
Closes #4613
2025-10-29 01:47:43 -05:00
Kurt
98255526d6 Update EvolutionUtil.cs 2025-10-28 23:55:48 -05:00
Kurt
70a6658835 Misc updates
ZA: Check ability number values (users were setting to 0 and it wasn't flagged)
ZA: Add Mable status for overall completion
ZA: Allow mutable slots of stored sub-event entities (such as gogoat/shuppet)
XY: Allow old man's slot to be modified (he's dead, who cares lol)
2025-10-28 23:47:05 -05:00
Kurt
bf9f585b77 Rename Work1=>CountTitle 2025-10-28 02:23:41 -05:00
Kurt
bdf3c09c2a Relabel some EventWork blocks, increase size 2025-10-27 23:33:09 -05:00
XxPhoenix1996xX
d93f76731b
Spanish localization update (#4608)
* Update const_e_es.txt

* Update flags_bw_es.txt

* Update MessageStrings_es.txt

* Update flags_b2w2_es.txt

* Update flags_frlg_es.txt

* Update flags_e_es.txt

* Update const_rs_es.txt

* Update const_dp_es.txt

* Update flags_hgss_es.txt

* Update flags_dp_es.txt

* Update flags_rs_es.txt

* Update flags_e_es.txt

* Update flags_frlg_es.txt

* Update flags_bw_es.txt

* Update flags_hgss_es.txt

* Update MessageStrings_es.txt
2025-10-27 22:25:38 -05:00
Easy World
34d154320e
Update Simplified Chinese translations (#4607)
Improved and expanded Simplified Chinese localization for battle set parsing, legality checks, and UI text. This includes more accurate terminology, better consistency, and full translation of new features for Gen 9a and related editors.
2025-10-27 22:25:15 -05:00
XxPhoenix1996xX
629714c3b2
Update lang_es.txt (#4606) 2025-10-27 19:53:34 -05:00
Kurt
404bd1036c Fix recognition of Gen9 eggs
Closes #4605
2025-10-27 19:53:10 -05:00
Kurt
b486247c1a Gen9a: remove unused Game Started entry
Trainer editor; block is unused
2025-10-27 15:34:36 -05:00
Omni-KingZeno
ec550a03fa
Add Alpha status box sort option for PLA/ZA (#4604)
Co-authored-by: Omni-KingZeno <200784099+Omni-KingZeno@users.noreply.github.com>
2025-10-27 15:30:34 -05:00
Kurt
01255dc83e Gen9a: Add "Set All Owned" button
slow, but works
hold alt to remove
hold shift to apply to all tabs
not holding shift applies only to current tab

allow form to be translated
2025-10-27 12:45:17 -05:00
Kurt
67b0217683 Fix met location duplication across sets
Kalos Gift starters when loaded to the GUI would mutate their met location to the wrong index (series 0) rather than retain the correct one (series 3). Wasn't apparent because the deduplication algorithm is different for Debug and Release builds.

Tested Release build, all unit tests now pass. Thanks pigeonsaint (discord) for reporting!
2025-10-27 12:21:08 -05:00
Manu
10d3ae0d54
Added support for .wa9 file type (#4602) 2025-10-27 11:57:03 -05:00
Kurt
29b4b6c38d Gen9a: fix dex seen gender bit set
genderless was overwriting male; ensure value is 0-2
add missing seen gender set on UpdateDex box/party slot set

Closes #4600
2025-10-27 11:36:05 -05:00
abcboy101
b5f5f35f2c
Update text resources from Z-A, support LATAM Spanish as a program language (#4599)
* Split zh text resources

* Reorganize language text resources

* Update language codes

Z-A uses the same abbreviations in all languages

* Update characteristics text from Z-A

* Update LATAM text resources from Z-A

* Support LATAM Spanish as a program language

* Handle duplicates
2025-10-27 11:03:48 -05:00
Kurt
bee3cfb657 Minor perf tweak for generating shiny slot 2025-10-27 00:28:26 -05:00
HexByt3
1b2512c16f
Fix Zygarde form handling in ShowdownParsing (#4577)
The logic for parsing Zygarde-50%-C and Zygarde-10%-C was inverted, causing 50%-C forms to be imported as 10%-C. Fixed by checking if the form string contains "10%" instead of checking if it's empty.
2025-10-27 00:11:45 -05:00
XieonGaming
45e8a76754
Revise Spanish descriptions for event 0248 - (#4591) 2025-10-27 00:11:13 -05:00
Kurt
4aefdb7627 Update MiscVerifier.cs 2025-10-27 00:09:46 -05:00
Kurt
5cd6f456f0 Minor tweaks
Fix ability index calc for generate & match
Fix message for mystery gift fateful encounter flag should be false
Add PA9 to GetBlank for anyone using the method via NuGet dll
2025-10-26 23:58:28 -05:00
Kurt
1c610c2054 full shiny cache, slot->pa9 obedience level
also alpha plus move retained on set all plus moves
2025-10-26 20:21:42 -05:00
Kurt
692d99c5cc Update 25.10.26 2025-10-26 19:07:50 -05:00
Kurt
fd1c538cc5
Changes for Legends: Z-A support (#4596)
Refer to pull request notes and the eventual changelog for a high-level summary.

Co-authored-by: Matt <17801814+sora10pls@users.noreply.github.com>
Co-authored-by: Lusamine <30205550+Lusamine@users.noreply.github.com>
Co-authored-by: SciresM <8676005+SciresM@users.noreply.github.com>
2025-10-26 19:01:44 -05:00
abcboy101
ae526a5bd5
Add Korean translation of README (#4583)
Closes #4582

Co-authored-by: scd02 <96989282+scd02@users.noreply.github.com>
2025-10-21 07:32:05 -05:00
Carbonara
fc6126a9e0
Update flags_bw_fr.txt (#4575) 2025-10-14 15:59:11 -05:00
Ka-n00b
0244b1bc6f
Fixed some CHT Event Constants formatting (#4574) 2025-10-13 22:46:20 -05:00
Ka-n00b
b34ec9de4c
Update Event Flags and translations (#4573)
* Update const_e_en.txt

* Update const_e_es.txt

* Update const_e_ja.txt

* Update const_e_zh-Hans.txt

* Update const_frlg_en.txt

* Update const_frlg_es.txt

* Update const_frlg_ja.txt

* Update const_frlg_zh-Hans.txt

* Update flags_e_en.txt

* Update flags_e_es.txt

* Update flags_e_ja.txt

* Update flags_e_zh-Hans.txt

* Update flags_e_zh-Hant.txt

* Update flags_rs_en.txt

* Update flags_rs_zh-Hans.txt

* Update const_dp_en.txt

* Update const_dp_es.txt

* Update const_dp_ja.txt

* Update const_dp_ko.txt

* Update const_dp_zh-Hans.txt

* Update const_dp_zh-Hant.txt

* Update const_hgss_en.txt

* Update const_hgss_es.txt

* Update const_hgss_ja.txt

* Update const_hgss_ko.txt

* Update const_hgss_zh-Hans.txt

* Update const_hgss_zh-Hant.txt

* Update const_pt_en.txt

* Update const_pt_es.txt

* Update const_pt_ja.txt

* Update const_dp_ko.txt

* Update const_pt_ko.txt

* Update const_pt_zh-Hans.txt

* Update const_pt_zh-Hant.txt

* Update flags_dp_en.txt

* Update flags_dp_es.txt

* Update flags_dp_ja.txt

* Update flags_dp_ko.txt

* Update flags_dp_zh-Hans.txt

* Update flags_dp_zh-Hant.txt

* Update flags_hgss_en.txt

* Update flags_hgss_es.txt

* Update flags_hgss_ja.txt

* Update flags_hgss_ko.txt

* Update const_hgss_ko.txt

* Update flags_hgss_zh-Hans.txt

* Update flags_hgss_zh-Hant.txt

* Update flags_pt_en.txt

* Update flags_pt_es.txt

* Update flags_pt_ja.txt

* Update flags_dp_ja.txt

* Update flags_pt_ko.txt

* Update flags_pt_zh-Hans.txt

* Update flags_pt_zh-Hant.txt

* Update const_b2w2_en.txt

* Update const_b2w2_es.txt

* Update const_b2w2_ja.txt

* Update const_b2w2_ko.txt

* Update const_b2w2_zh-Hans.txt

* Update const_b2w2_zh-Hant.txt

* Update const_bw_en.txt

* Update const_bw_es.txt

* Update const_bw_ja.txt

* Update const_bw_ko.txt

* Update const_bw_zh-Hans.txt

* Update const_bw_zh-Hant.txt

* Update flags_b2w2_en.txt

* Update flags_b2w2_es.txt

* Update flags_b2w2_ja.txt

* Update flags_b2w2_ko.txt

* Update flags_b2w2_zh-Hans.txt

* Update flags_b2w2_zh-Hant.txt

* Update flags_bw_en.txt

* Update flags_bw_es.txt

* Update flags_bw_fr.txt

* Update flags_b2w2_es.txt

* Update flags_bw_es.txt

* Update flags_bw_ja.txt

* Update flags_bw_ko.txt

* Update flags_bw_zh-Hans.txt

* Update flags_bw_zh-Hant.txt

* Update const_oras_zh-Hant.txt

* Update const_xy_ko.txt

* Update const_xy_zh-Hant.txt

* Update flags_oras_en.txt

* Update flags_oras_es.txt

* Update flags_oras_fr.txt

* Update flags_oras_ja.txt

* Update flags_oras_ko.txt

* Update flags_oras_zh-Hans.txt

* Update flags_oras_zh-Hant.txt

* Update flags_xy_en.txt

* Update flags_xy_ko.txt

* Update flags_xy_zh-Hant.txt

* Update const_sm_ko.txt

* Update const_usum_ko.txt

* Update const_usum_zh-Hant.txt

* Update flags_sm_ko.txt

* Update flags_sm_zh-Hant.txt

* Update flags_usum_ko.txt

* Update flags_usum_zh-Hant.txt

* Update flags_sm_zh-Hans.txt

* Update flags_sm_zh-Hant.txt

* Update flags_gs_zh-Hant.txt

* Update flags_c_zh-Hant.txt

* Update const_gs_zh-Hant.txt

* Update const_c_zh-Hant.txt

* Update flags_rs_zh-Hant.txt

* Update flags_gs_ko.txt

* Update flags_hgss_ko.txt

* Update MessageStrings_ko.txt

* Update MessageStrings_ja.txt

* Update const_b2w2_zh-Hans.txt

* Update const_b2w2_zh-Hant.txt

* Update const_b2w2_es.txt

* Update flags_gs_ko.txt

* Update flags_gs_en.txt

* Update flags_c_en.txt

* Update flags_c_fr.txt

* Update flags_c_ja.txt

* Update flags_c_zh-Hans.txt

* Update flags_c_zh-Hant.txt

* Update flags_gs_fr.txt

* Update flags_gs_ja.txt

* Update flags_gs_ko.txt

* Update flags_gs_zh-Hans.txt

* Update flags_gs_zh-Hant.txt

* Update flags_gs_es.txt

* Update flags_c_es.txt

* Update flags_gs_es.txt

* Update flags_c_es.txt

* Update const_e_ja.txt

* Update const_frlg_ja.txt

* Update flags_dp_ja.txt

* Update flags_pt_ja.txt

* Update flags_c_ja.txt

* Update flags_gs_ja.txt

* Update flags_gg_ko.txt

* Update flags_gg_zh-Hant.txt

* Update flags_gg_es.txt

* Update flags_gg_es.txt

* Update const_hgss_ko.txt

* Update const_xy_ko.txt

* Update flags_oras_ko.txt

* Update flags_c_es.txt

* Update flags_c_es.txt

* Update flags_gs_es.txt

* Update flags_gs_es.txt

* Update flags_gs_es.txt

* Update flags_c_es.txt

* Update const_c_zh-Hant.txt

* Update const_pt_ko.txt

* Update flags_usum_zh-Hant.txt

* Update flags_usum_zh-Hans.txt

* Update flags_usum_ko.txt

* Update flags_usum_ja.txt

* Update flags_usum_es.txt

* Update flags_usum_en.txt

* Update const_c_es.txt

* Update flags_bw_ja.txt

* Update flags_bw_zh-Hans.txt

* Update flags_bw_zh-Hant.txt

* Update flags_bw_zh-Hant.txt

* Update const_xy_zh-Hans.txt

* Update const_xy_zh-Hant.txt

* Update flags_gs_es.txt

* Update flags_c_es.txt

* Update const_c_es.txt

* Update const_c_es.txt

* Update const_gs_es.txt

* Update const_c_es.txt

* Update flags_gg_zh-Hant.txt

* Update flags_dp_ko.txt

* Update flags_pt_ko.txt

* Update const_gs_es.txt

* Update const_c_es.txt

* Update flags_b2w2_zh-Hans.txt

* Update flags_b2w2_zh-Hant.txt

* Update flags_dp_zh-Hans.txt

* Update flags_pt_zh-Hans.txt

* Update flags_pt_zh-Hant.txt

* Update flags_dp_zh-Hant.txt

* Update flags_dp_zh-Hans.txt

* Update flags_dp_zh-Hant.txt

* Update flags_pt_zh-Hant.txt

* Update flags_pt_zh-Hans.txt

* Update const_dp_ja.txt

* Update const_pt_ja.txt

* Update flags_bw_ko.txt

* Update flags_usum_zh-Hans.txt

* Update flags_usum_zh-Hant.txt

* Update MessageStrings_de.txt

* Update MessageStrings_it.txt

* Update MessageStrings_zh-Hant.txt

* Update MessageStrings_zh-Hans.txt

* Update MessageStrings_ko.txt

* Update MessageStrings_de.txt

* Update flags_c_es.txt

* Update flags_c_fr.txt

* Update flags_c_ja.txt

* Update flags_c_zh-Hans.txt

* Update flags_c_zh-Hant.txt

* Update flags_c_en.txt

* Update flags_gs_es.txt

* Update flags_gs_fr.txt

* Update flags_gs_ja.txt

* Update flags_gs_zh-Hans.txt

* Update flags_gs_zh-Hant.txt

* Update flags_gs_ko.txt

* Update flags_gs_en.txt

* Update flags_gs_es.txt

* Update flags_hgss_ko.txt

* Update flags_hgss_es.txt
2025-10-13 16:22:48 -05:00
sora10pls
cdf4aaecce Add latest distribution raid data... again! 2025-10-06 07:39:08 -04:00
Kurt
bff4a56f1f Add egg3/4 language restrictions, kor shaymin
Closes #4570
2025-10-05 11:23:30 -05:00
sora10pls
f113b01faf Add latest distribution raid data 🌙🤖
Thanks for not distributing these raids correctly, Game Freak!
2025-10-02 20:38:47 -04:00
Kurt
f10b6c5196 Minor tweaks 2025-09-27 18:33:15 -05:00
Kurt
73536187cf Update 25.09.25 2025-09-26 17:00:38 -05:00
9Bitdo
23e08dc73a
Add Shiny Miraidon / Koraidon Gift's date (#4567) 2025-09-26 04:37:58 -05:00
Kurt
e217979000 Misc zipreader tweaks
Closes #4566
signature changes, add some overloads, extract/simplify common logic

Co-Authored-By: Chris Dailey <nitz@users.noreply.github.com>
2025-09-25 17:27:14 -05:00
Kurt
8d0bd79708 Whitelist trade5bw for pid check
PID is forced by the encounter
Closes #4562
2025-09-24 23:35:07 -05:00
Kurt
83beeaa5d0 Add zipped save file r/w
Not happy that zipping the file is the solution for some homebrew apps, but it is what it is.

No need to select which file; it's always one file in the zip, and never multiple.
When exporting, if it originated from a zip, grab the original then update it with the revised contents.

Closes #4564

Co-Authored-By: Chris Dailey <nitz@users.noreply.github.com>
2025-09-24 23:25:15 -05:00
Kurt
d0f8c18426 Misc tweaks
extract trade restriction check logic to a separate class
Update translation for roamer3 level
2025-09-24 23:13:30 -05:00
Kurt
74870abc55 Fix item convert for gen2 showdown import
Add handling for wrong-EV format imports
2025-09-24 23:10:40 -05:00
Kurt
9cfe12bf32 Add fallback showdown parse line: happiness 2025-09-24 23:09:03 -05:00
Dave / Xieon
4b66f3780f
Added a couple of translations in French and Chinese (#4565)
* Update legality_fr.json
* Update MessageStrings_zh-Hans.txt
2025-09-24 22:28:25 -05:00
sora10pls
e8d3acb938 Update Shiny Chi-Yu Wonder Card ID 2025-09-18 20:04:08 -04:00
abcboy101
dc52331cf1
Fix PK2.ConvertToPK1 (#4561) 2025-09-14 21:40:04 -05:00
Kurt
e5705b078a Update MiscVerifier.cs
#4559
duh
2025-09-13 23:09:58 -05:00
Kurt
7e43c3d468 Gen5: fame legality check +25/-50
Closes #4559
2025-09-13 22:40:49 -05:00
Kurt
7d77c3568d Localize status type browser on hover
Toxic isn't translated, so just use "Toxic".
2025-09-13 13:35:39 -05:00
abcboy101
a75ff7b2b0
Correct Gen 5+ status conditions (#4558) 2025-09-13 12:52:17 -05:00
Kurt
efa1211c07 Keep plugin load result
Closes #4556

Co-Authored-By: Chris Dailey <602691+nitz@users.noreply.github.com>
2025-09-08 10:22:52 -05:00
Kurt
ac777ba3ec Swap gen1 trade nidoran jp/int species
Also flag * char for international encounters that aren't in-game-trades.

https://projectpokemon.org/home/forums/topic/67161-invalid-ot-from-generation-12-uses-unavailable-characters/#findComment-297053
2025-09-07 17:30:19 -05:00
Kurt
ec47d75327 Misc tweaks 2025-09-07 17:29:19 -05:00
Kurt
d774e48a56 Revise event flag block fetch
now works for redirected save files where a block is responsible
2025-09-06 15:28:49 -05:00
Kurt
63dfdab57e Fix settings editor select battle revolution ver
Selecting Battle Revolution (recently added GameVersion for BatRev rentals) isn't filtered out of the GameVersion list, so when a user selects it, it will result in an unhandled switch case. Add it, and ensure the blank save loads without errors.
2025-09-05 23:28:54 -05:00
Kurt
e1ca2ccdf8 Revise set all valid ribbons for Gen6 training rib 2025-09-05 21:05:07 -05:00
Kurt
d574ce32d1 Add slnx
Try again; delete .sln later.
2025-09-05 17:35:53 -05:00
Kurt
ee02b7c176 Update save file export extension filter get
specific extensions (like dsv/gci) would return a filter of (.gci) rather than (*.gci), leading to user error when they toggle back and forth (removing the extension).

Closes #4555

unrelated: allow folder list manual text entry to anchor to the right side (expands when form is expanded)
2025-09-05 14:54:26 -05:00
abcboy101
355262ba4d
Fix IsG2CrystalJPN (#4554) 2025-09-05 08:34:48 -05:00
Kurt
1a07618bbe Misc tweaks
Adds HP to gen3 roamer editor - closes #4553
Revises Gen3 Hall of Fame editor to allow edits (save/load methods were swapped)
Don't append Gift3 PID types if PID type is mismatched
2025-09-04 22:52:18 -05:00
sora10pls
47666d5469 Update Shiny Ting-Lu Wonder Card ID
Chi-Yu is likely 1548, but I'm not going to change it yet, because I know that if I do, then they'll break the current pattern.
2025-09-04 20:02:11 -04:00
Lusamine
6438a74940 Extend WCS Toedscool valid end date
Contrary to what the code cards stated as the end date, they could still be redeemed into August 31st in UTC+14.
Thanks to the user Pikachu from Discord from testing!
2025-09-01 16:30:35 -05:00
Carbonara
293107dbcf
Add more info for BW and ORAS flags (#4551)
* Provide more details for TransferMet

Defaulted Japanese, Korean and Chinese (Traditional and Simplified) to the English line due to not knowing well the language and not being easily fixable unlike other European languages (the line was wrong in any case so it needed a retranslation, and nearby lines are also untranslated).

Other languages than English don't seem to have a widespread term for the Crown Beasts, they use something like Legendary Beasts even for those ones, so using this term should be easier to understand.

* Give more details for some ORAS flag lines

- Specify where the Statuette is exhibited (XY and ORAS)
- Specify where the gifts and eggs are, and which Pokémon are in-use for the in-game trades
- Specify that the Diancite and Prison Bottle are part of events ()
- Specify where the Winstrate Family is located
- Add trainer location and title for rematches in English and Spanish (translated), Korean, Chinese Hant (untranslated, English lines). Not sure how to take care of it for the other Chinese translation, and the Japanese translation already mentions the location, so no need to alter it.
- Fix an issue in French where the old hot-springs visitor was partially using the English name instead of the French one

* Add more info for some bw flags
2025-09-01 09:34:17 -05:00
sora10pls
765b0b3680 Latest distribution outbreaks, Reg J ribbon legality 2025-08-31 20:05:54 -04:00
Kurt
ded9d54399 Misc tweaks
fixes legality report not showing localized
fixes pcjp5 seed->table generate
removes eternamax from go_home pkl
waiting for raids before hotfixing exe & pushing nuget
2025-08-31 17:12:20 -05:00
Carbonara
597bbbcf8d
Update the French translation (#4550)
* Update legality_fr.json

- Translate the added lines in French
- Fix Œuf (Egg) not having a capital letter for one of the lines

* Update encounter_fr.json

- A space should be present before a ":" character in French, fixed
- Translate Origin Seed (still using the English term Seed for consistency atm, and since this term is more widespread)

* Translate setparse_fr

* Update movesource_fr.json

- Move is supposed to be Capacité in French, not Attaque
- Added a space before ":"
- Changed the order when mentioning a special move to avoid confusion (would have displayed Capacité spéciale otherwise, which is the old term used for Abilities in French, unrelated to this file)

* Update flags_bw_fr.txt

- Translate remaining flags
- Add proper locations for where some of the flags are located
- Give more details for the trades

* Update flags_oras_fr

Translate all untranslated lines to French, specify the gender for legendary Pokémon only available in female, fix Famille Stratège not being accorded.
For the rematch, I added the trainer class and location for all of the lines in French (speaks more than just a name you will not memorise): let me know if you want to have this ported to other languages.

Other:
- Fix a typo where Fallarbor Town was written as Fallabor Town, Verdanturf Town as Vendanturf Town
- Fix an issue where Pikachu Cosplayeur was written as Pikachu Cosplay
- Fixed the origin message

* Update MessageStrings_fr.txt

- Translate new lines
- Fix some wordings
- Fix MsgIndexAbilityGame being duplicated from MsgIndexAbilityRange instead of being its own message

* Update lang_fr.txt

Translate some lines:
- Pass Powers, O-Powers, Gen 7 Throw Styles translated
- Misc lines translated or fixed

May or may not take care of other entries in the future, it depends on when I'm motivated and for what
2025-08-31 17:10:56 -05:00
Kurt
a0584ca5f5 Update MedalVerifier.cs 2025-08-31 01:21:24 -05:00
Kurt
9e8d8ccc62 Update 25.08.30 2025-08-31 00:01:14 -05:00
Kurt
5f5ec65c4a Add training bag effect party stat
ty Anubis
rearrange some of the comments for clarity

Co-Authored-By: Lusamine <30205550+Lusamine@users.noreply.github.com>
2025-08-31 00:01:14 -05:00
Easy World
82fdbbb777
update Chinese translation (#4547) 2025-08-28 22:26:40 -05:00
Kurt
c3b3b611aa Super Training: another look
revise criteria for ribbon
add legality check for training bag values
add localizations for distribution training regimens (never distributed)

Co-Authored-By: Lusamine <30205550+Lusamine@users.noreply.github.com>
2025-08-28 20:19:05 -05:00
Kurt
1056b04d0c Gen1: update sea slots for tentacool<->jynx remap
this area (sea surf) didn't get internal->national in my conversion script years ago... nice.

refer to dumper where they were manually fixed and re-dumped to the binlinker pkl format.
366e035034
2025-08-28 01:04:58 -05:00
Kurt
b9625c75b4 Update GUI translations
includes the splash screen disable setting that was added recently
2025-08-26 12:55:12 -05:00
Kurt
79b3bd4f74 Gen3: revise PCJP internal logic
deduplicates a little; renames the PID type label to something less confusing.
2025-08-26 12:54:53 -05:00
Kurt
ed419c49bd Gen5: Pokestar fame disallowed on ditto
transform in moves, disallow participation (can't copy the opponent!)
https://projectpokemon.org/home/forums/topic/57375-pkhex-new-update-legality-errors-contribution-page/page/33/#comment-296835
2025-08-26 12:53:53 -05:00
Kurt
82744a12ef Update dependencies 2025-08-22 16:33:04 -05:00
Kurt
dd5d6a4e39 Minor tweaks
no functional change
2025-08-22 16:33:04 -05:00
Kurt
5beabb2020 Hide slot change publisher's list, add/remove 2025-08-22 16:33:04 -05:00
Kurt
3f32ee5814 Allow SlotView to wrap a multi-entity file
Not currently used, but can be in the future.
2025-08-22 16:33:04 -05:00
Kurt
5dce5e93e9 Add misc slot legality flag skipping
For BD/SP with partially generated mons, ignore the triangle annoyance.
2025-08-22 16:29:03 -05:00
Kurt
f0423610af Add localization for ShowdownSet parse fail popup 2025-08-22 16:28:03 -05:00
sora10pls
d8853b6de6 Update Shiny Chien-Pao Wonder Card ID 2025-08-21 20:05:10 -04:00
sora10pls
c1211fe84c Add latest distribution outbreak data 🌎 2025-08-17 20:05:16 -04:00
Kurt
ea85d5e6b0 Minor clean 2025-08-16 09:21:16 -05:00
Kurt
e25b2037e2 game->version 2025-08-16 09:21:16 -05:00
Kurt
89cb15b9cd Extract GetBlankSaveFile to static class 2025-08-16 09:13:07 -05:00
9Bitdo
2aa6552312
Add WCS 2025 Toedscool & Luca Ceribelli's Farigiraf date (#4544) 2025-08-15 14:49:40 -05:00
Kurt
c3873165af Add farfetch'd apostrophe 1/2->7
the other ver 1.2 bug that was fixed in 1.3
every other diff noted in https://github.com/kwsch/PKHeX/pull/4545#issuecomment-3192383636 is an inaccessible text entry. Farfetch'd is set by the game on capture, just not enterable via text entry.

Restore 0-9 for both int/jpn as Porygon2 exists. 01[]345... are un-enterable, but whatever. Should be a text entry check rather than a transporter check.
2025-08-15 14:25:45 -05:00
drabu96
3e8c355ef7
Fix pk1/pk2 char conversion to >=pk7 (#4545)
* Fix pk1/pk2 char conversion to >=pk7

Fixed conversion of a gen1/2 "․" to ASCII "." when converting to pk7 or newer.
Added a test that verifies the fix.

Addresses the issue: https://github.com/kwsch/PKHeX/issues/4543

* Update StringTests.cs
2025-08-15 13:30:07 -05:00
Kurt
f0c8b86728 Minor startup tweaks
Allow settings to skip Splash Screen (and just launch the main form without fuss, cuz why not?)
Handle scenario where PKHeX.Core.dll fails to bind during Settings fetch -- handle via static constructor instead of Program.Main() so that errors pipe to the Release error handlers.

Run update check in another thread, after Main is shown, so that offline users don't have to wait 3 extra seconds for it to timeout and show.

Revise the startup animation to just show the Main form rather than minimize->restore. The previous "hack" was designed so that if users clicked anywhere after launching the program (thus losing focus) the Main form would re-capture it. Activate() works fine now (maybe it didn't in the past?)

Removes "dark" startup arg; do via settings. Users really won't have a separate launch config like they might for HaX via .bat
2025-08-14 23:57:20 -05:00
Kurt
d658da44c6 Minor tweaks
Fixes conversion compatibility override being reverted when settings is reloaded by user (via GUI)
2025-08-13 22:05:43 -05:00
Kurt
93a381bfde Startup: load config before Main ctor
Allows specifying Dark mode in settings now.
Extracts reusable settings objects to PKHeX.Core (drawing/GUI stuff kept in WinForms).
Updating settings now refreshes backup paths/mgdb
2025-08-13 20:59:46 -05:00
Carbonara
80487a514d
French - Update the legality file (#4542)
Fix a lot of weird translations/inaccuracies, translate untranslated lines.

Notes:
- BallEggCherish & BallEggMaster were missing the mention of normal, which is incorrect (some eggs were distributed in a Cherish Ball in 2017)
- EncStaticPIDShiny was using the term shiny-locked to describe it as if the error could only apply to non-shiny Pokémon set to be shiny, but from the English formulation I understand it as implying the opposite too (e.g. a shiny only Pokémon set to be non-shiny could display the warning), so I renamed it
- FatefulGiftMissing was mentioning the Mystery Gift DB being edited which wasn't the case in the original, edited
- G2OTGender: mention Pokémon Crystal instead of just Crystal since I struggled to understand Crystal meant the game rather than the name
- No clue if the Mood and Spirit stats have an official French name, so a direct translation from the English term was used
- MemoryArgBadItem_H1 was specifying that the Pokémon cannot have held any item rather than a specific item, fixed

- I translated HT (Handling Trainer) as Der. Dres. (Dernier Dresseur, Last Trainer), takes more space but clearer than using DD, or just an abbreviation that wouldn't mean anything out of the box
- TransferMet: no clue what Crown is supposed to mean, I changed it to be more accurate (from what I understand, it's supposed to be for Relocator Pokémon, so legendary beasts and Celebi, so I specified the expected potential encounter locations for them). If this is not correct, feel free to change it or tell me what it's supposed to be.
2025-08-13 20:40:10 -05:00
Kurt
330a6f088c Ball: gen6 roselia allow apricorn inherit (no HA)
budew was correct, somehow the flag for roselia-apricorn wasn't set like the other splitbreed species mirroring.
2025-08-12 01:12:54 -05:00
Kurt
8633344187 Fix vertical alignment of IsEgg checkbox
off by 1 pixel compared to pkrs
rearrange some margins on cosmetic/stats tab to avoid some clipping
2025-08-12 01:11:56 -05:00
Kurt
47efdf8a90 PK5: add pokestar fame edit, update translations
Remove pk4 walking mood from extrabyte list
2025-08-10 23:09:27 -05:00
abcboy101
8f9fec0b13
PBR: Add Battle Pass, Gear, Trainer Info editors (#4540)
* Fix PBR checksums

* Fix PBR desyncs between Data/Container

- CurrentSlot.set reloads Data from Container, so copy other.Data to Data after
- When editing the OT name, use Data if the requested slot is the current slot

* Correct PBR party offset/size

* Add GameVersion.BATREV

* Add Gear Editor

* Add Battle Pass/Trainer Info Editor for PBR

* Minor tweaks

* Fix ResetGear/UpdatePresetIndexes
2025-08-10 22:43:03 -05:00
Kurt
3a4fe49182 PB7: Add Spirit/Mood names, editing, checks
Official Names from guidebook: https://discord.com/channels/497890797115670539/950895799401848852/1285451683945779303
Removing from party (except starter): reset to 100 -- not ALWAYS like the previous logic once did.
Flag any non-party/starter if not 100-100.
Add separate GUI controls (not to confuse with Gen4's Walking Mood)
2025-08-10 02:16:37 -05:00
Kurt
ff0f4727dd Extract logic from SaveUtil
BlankSaveFile -> creation of blank save files
SaveFileType -> listing of all savefile types

Blank save file arg passing is now clearer
Instead of SaveFile? return, use TryGet pattern with nullable annotations to indicate success
2025-08-09 21:55:55 -05:00
Kurt
d4bbb6dd02 Misc tweaks
Ball: all ball IDs are in a sequence +1'd. No need to have an array when we can just increment within the range. Ez removal of static constructor and allocation, and better iteration (and skips index 0!)
Disallow E/FR/LG item deposits of anything besides general pouch (R/S is like G/S/C, any pouch). Confirmed via testing in-game and matches Bulbapedia's testing.
Disallow Gen2 held item being an HM; no longer considered valid as a tradeback catch rate value. Oops that HMs were "allowed" for so long!
Encode Gen2 held items to bitflag array to not need to compute the merged array. Relocate duplicated logic to a single location in ItemConverter.
Fix gender-changing marill edge case comparing the wrong ratio
2025-08-08 23:43:22 -05:00
Kurt
af416dc71a Add gen4 mood to pkm editor (cosmetic) 2025-08-08 00:42:15 -05:00
Kurt
141aa97e2b Merge branch 'master' of https://github.com/kwsch/PKHeX 2025-08-08 00:19:34 -05:00
Kurt
fd3af56ec4 Legality: add date sanity check, shinyleaf/mood
date: If location specified, ensure valid date; if no location, ensure zeroed.
shinyleaf: check bad bits, check crown has all leafs.
mood: rename from pokeathlon, now sbyte. All values possible, only flag outside of party in HG/SS.

Revise HGSS slot setter to wipe mood to match game behavior (and thus not retain mood to be flagged by the legality check).
There's currently no editor for it, but maybe I can add it in a future commit.
2025-08-08 00:19:33 -05:00
Kurt
101aa17a8d Update translations for dex revisions
see previous commits
2025-08-08 00:08:57 -05:00
Kurt
b291378e78 Localizations: better thread safety init
No need for a dictionary, just allocate an array and index in via one of the supported languages.

Updates GameInfo to use the abstraction to prevent some duplicate work on a very-hot startup. Threads repeating the same work vs Thread n++ simply waiting for previous thread to finish init is the ~same amount of time, with less overall CPU usage (so this is a positive improvement).

EnterScope is "safer" than explicit `lock (x)` due to the using syntax releasing the lock even on exception (not expected, but might alleviate issues on developer-initiated feature upgrades).

Co-Authored-By: HexByt3 <80122551+hexbyt3@users.noreply.github.com>
2025-08-07 21:15:32 -05:00
Kurt
4ed1d12859 Widen batch editor GUI
Fiddled with the spacing to be more 4px spaced rather than inconsistent.
Dropdown for property select is now wider, and doesn't cut off long names like OT memory feeling.
2025-08-07 21:15:32 -05:00
sora10pls
cfdc571f9c Update Shiny Wo-Chien Wonder Card ID 2025-08-07 20:05:25 -04:00
Kurt
28e20c4ea3 Enhance gen6 dex interactions
Uses the rewritten Gen5 object as the base rather than the old zukan abstraction
Adds National Dex unlocked flag for editing

probably best to extract an interface as there's no need to have a shared abstract Zukan class across generations.
2025-08-05 09:55:58 -05:00
Kurt
ceff28210a Fix PBR init
Closes #4534
2025-08-05 09:52:03 -05:00
Kurt
031f7f4e6c Update Zukan5.cs 2025-08-04 02:56:57 -05:00
sora10pls
d78aff9e03 Add latest distribution outbreak data ❄️ 2025-08-03 20:15:14 -04:00
Kurt
112086d85b Update SAV_Pokedex5.cs
am i gen 4 or gen 5? why not gen4.5?
2025-08-03 14:34:34 -05:00
Kurt
d732520762 Rewrite Gen5 dex editor & backend
Closes #4533
2025-08-03 14:24:22 -05:00
Kurt
1d57facd22 Minor tweaks
extract max/min level to const
fix max species ID in filtered sources
2025-08-02 21:02:11 -05:00
Kurt
720ac7ead7 Misc tweaks
add localization for ` @ lv{0}` for verbose report
2025-08-02 01:40:18 -05:00
Kurt
0f106c9c82 oops
not actually exclusive
for eggs, assume that ability inheritance flips the bit
2025-07-31 23:12:17 -05:00
Kurt
8d99a7a56d Misc gen5 PID random number updates
Use 64bit RNG for PID creation, mimic how the game generates PIDs with the impossible value quirks
shiny lock the HA eevee in castelia
remove duplicate encounters (no longer needed due to form mutation API being mature)

ty @Lusamine for obtaining some samples and testing the PID generating algo
2025-07-31 22:40:56 -05:00
HexByt3
b1464a0941
Update StartupArguments.cs (#4532)
BallDataSource => VersionDataSource
2025-07-30 15:19:38 -05:00
Kurt
4c0ad92edb Allow bulkanalysis w/o save file
secret constructor that was for tpci, now available for all /s
2025-07-30 01:25:20 -05:00
Kurt
11f33985c4 Move files, xmldoc, simplify
External check can call AddLine. The intent is to not allow it to remove previous parse results.
2025-07-30 01:24:02 -05:00
Kurt
90cfa59102 Misc tweaks
Pass Analysis to external localizer
Cache index for bulk analysis flagged slots
2025-07-29 20:12:41 -05:00
Kurt
223840f943 Revert "Convert sln to slnx"
This reverts commit 5d2ebf4d0a.
2025-07-28 21:51:16 -05:00
Kurt
efefd78caa Update dependencies
pipeline will stay broken until nuget 6.15 releases (it's been 2 month
2025-07-28 21:51:10 -05:00
Kurt
5d2ebf4d0a Convert sln to slnx 2025-07-28 19:07:25 -05:00
Kurt
47092a2df0 Misc tweaks 2025-07-28 18:45:30 -05:00
Kurt
13154d70f8 Split missing/invalid ribbon results
Generating the message is repeat work, but the deferred message is still better in the long run.
1x -> 3x, but that's worst case.
2025-07-28 18:12:14 -05:00
Kurt
c331d97e89 Fix 2 localization mis-mapped
tested 100k, no more thrown Exception's
2025-07-28 02:11:26 -05:00
Kurt
fb814ac878 Add xmldoc 2025-07-28 00:14:33 -05:00
Kurt
904fd2020c Add external legality check functionality 2025-07-27 23:47:45 -05:00
Kurt
b3d3c9e562 Minor fixup on localization resources 2025-07-27 21:49:22 -05:00
Kurt
65420b0878 Actually test the bulk check result formatting 2025-07-27 21:20:34 -05:00
Kurt
d99ec943fe Fix stragglers from dual PR merge 2025-07-27 21:03:19 -05:00
Kurt
44486fdf85 Merge branch 'master' of https://github.com/kwsch/PKHeX 2025-07-27 21:00:38 -05:00
Kurt
f370c0cc39
Memory<byte> Refactoring (#4527)
`SaveFile` and `PKM` classes now use `Memory<byte>` instead of `byte[]` to store their primary backing array data.
2025-07-27 20:57:10 -05:00
Kurt
13a4d472bc
Deferred Humanization of LegalityAnalysis (#4531) 2025-07-27 20:54:58 -05:00
Easy World
a5cdb0e27c
Update Simplified Chinese translations (#4529)
* Update translation

* Update Simplified Chinese translations

Improves and corrects Simplified Chinese translations in legality check strings, program messages, and WinForms UI. Updates terminology for better localization accuracy and user clarity.

* revert legality check strings

Will be manually updated in future commit by kwsch -- avoiding merge conflicts for now

---------

Co-authored-by: Kurt <kwsch@users.noreply.github.com>
2025-07-27 16:57:16 -05:00
sora10pls
ba2c397cb9 Add placeholder Shiny Treasures of Ruin date ranges
Wonder Card IDs to be determined, 9996-9999 until released
2025-07-27 15:49:49 -04:00
sora10pls
a49f832f3a Add placeholder Shiny Treasures of Ruin date ranges
Wonder Card IDs to be determined, 9999 until released
2025-07-27 10:06:43 -04:00
abcboy101
fef5ff3edb
Translate DataGridViewColumn header text (#4528)
* Localization update

* Translate DataGridViewColumn header text

Temporary label hack in SAV_FolderList is no longer needed

* Localization update
2025-07-26 08:43:23 -05:00
Kurt
6fd644069c GUI: swap gen3 marking visual order
https://projectpokemon.org/home/forums/topic/66916-order-of-cosmetic-markings-in-gen-3/#comment-296256
2025-07-24 14:46:59 -05:00
Kurt
747d083975 Update EncounterEnumerator5.cs 2025-07-23 23:13:53 -05:00
Kurt
a02712e375 Add gen5 spin trade as valid link trade loc
ty Cappy for reporting, and @PP-theSLAYER & @Lusamine for testing.
https://projectpokemon.org/home/forums/topic/57375-pkhex-new-update-legality-errors-contribution-page/page/32/#findComment-296214

gen4 spin trades in pt/hgss do not set the wrong value -- gen5 only bug.
2025-07-23 22:37:39 -05:00
Kurt
9d49458787 add a new 0xFFFFFFFF legality check for gen5 eggs
lol nice gamefreak
2025-07-23 22:37:39 -05:00
Fábio H. Attard
ed9d46cad9
Fix SAV1 fallback detection for Yellow version, when starter is not set yet (#4526) 2025-07-23 01:03:22 -05:00
sora10pls
025a812e2c Add latest distribution outbreak data 🍃 2025-07-22 20:15:54 -04:00
Kurt
a548614646 Safeguard moveset suggestions for Gen2->Gen1 enc
Removes all gen2 moves from a suggestion when requestor is a gen1 format mon (can't have gen2 moves). This is the only case where moves are less available (ignoring HOME's multi-format moving, which cannot transfer moves to less-available contexts anyway).

ty Asia81
https://projectpokemon.org/home/forums/topic/66877-pokemon-red-unknown-suggested-moves-for-poliwhirl/
2025-07-21 17:05:49 -05:00
Xzonn
577278b7b8
Update Chinese translations from official translations (#4525)
* Update Chinese translations from 52Poke Wiki

* Fix memory translations

* Update feelings
2025-07-21 15:30:43 -05:00
Kurt
42835e9aac Misc tweaks
Adds a debug hex file loader from clipboard
sav1 current box if empty -> set if box is desync'd
wb7: add comment note of "real" value
2025-07-16 23:02:57 -05:00
Kurt
0e9a3129b4 Misc stuff
Adds GameSync ID for Gen5
Add GiftRibbons to gen3 mainline saves
Add defined chars from Transporter update 7 (latest)
2025-07-13 01:00:43 -05:00
Kurt
b4edc389bb Rearrange gen3/4 deferral enum
null check necessary for unset deferral needing to ignore max
2025-07-12 01:29:37 -05:00
sora10pls
6a9af0a4a3 Add latest distribution raid data 🤜🤛 2025-07-10 20:05:14 -04:00
Kurt
cdb5770f5f Minor tweaks
Fixes cosmetic issue on gen4 seed example times
no functional change otherwise
2025-07-08 23:00:27 -05:00
Kurt
0b77aa5729 Add swsh string check
Replace debug assert in SWSH encounter finder
Add dir arg for pogo pickle reload for debug builds
re-add latest outbreaks
2025-07-06 18:48:49 -05:00
Kurt
16f30ebe2d Handle gen6 wc's with bad OTs
https://projectpokemon.org/home/forums/topic/41065-gen-7-compilation-of-events-that-change-ot-when-traded/
2025-07-06 17:16:11 -05:00
Kurt
a957569caa Update WC9.cs 2025-07-06 11:40:26 -05:00
Kurt
aee9171249 Remove length check
Nope, doesn't check length
2025-07-06 09:56:02 -05:00
Kurt
d0f73c96c1 Ignore h/w odds on wc9 2025-07-06 09:35:17 -05:00
Kurt
31c52b6cd2 Add HOME trade OT replacement functions 2025-07-06 09:21:13 -05:00
Kurt
157210de08 Minor tweaks
Add a mutable trainer info class that mirrors the SimpleTrainerInfo
2025-07-06 09:03:44 -05:00
Kurt
082f9cb340 Add Title to some import dialog prompts 2025-07-06 09:02:44 -05:00
Kurt
89ada33e24 Add ReplaceTrainerName api 2025-07-06 02:15:10 -05:00
Kurt
59047cda25 Update outbreak pickle
More pkNX logic fixes (just now)
2025-07-05 16:31:17 -05:00
Kurt
7864907f81
Add Misty Mark recognition & weather bleed for slots (#4519)
Closes #4036
Weather now handled upstream in pkNX, with bleed applied to individual spawn points, serialized to consumable legality binary.
Misty marks thanks to @Lusamine and her discord helpers -- also utilized https://github.com/kwsch/MistyMarkVisualize to help visualize and filter entity dumps => spawn-position.
2025-07-05 00:08:29 -05:00
Kurt
607901dda1 Misc tweaks 2025-07-04 16:06:26 -05:00
Kurt
fbde4f585d Small enhancement to encdb ui
Shift click type checkbox to un-check others
Search button disables itself if the search would return nothing
When using tabs as criteria, if hyper training is available, only require the specified imperfect IVs for the encounter

enc9: be a little nicer and allow a slight search lag by only considering actual encounterable attempts (passing slot check). Can infrequently obtain a 0-speed shiny Foongus via encDB with a few attempts.
2025-07-04 01:35:48 -05:00
Kurt
c19a4605d5 Misc tweaks
No functional change
2025-07-04 01:32:25 -05:00
sora10pls
a471f1bd8c Add latest distribution outbreak data 🐭 2025-07-03 20:06:27 -04:00
Kurt
bda5baab88 PB7: Permit GO in BelongsTo check
Closes #4518
2025-07-03 18:12:08 -05:00
Kurt
e9d299fc92 Honor more shiny requests in gen6+ encounters 2025-07-02 01:08:50 -05:00
Kurt
e69f6b05f8 Revise bdsp egg gen to follow correlation
no, detecting this correlation is not realtime, and never will be (99.99999% sure).
allow generating of shiny eggs by spoofing a link trade on them.
2025-07-01 01:07:31 -05:00
Kurt
3b4661d40d Revise dmax adv shiny PID generating to match game 2025-06-28 12:32:45 -05:00
Kurt
1258d96883 Add some CodeAnalysis attributes 2025-06-28 00:38:05 -05:00
Kurt
5e0ee30235 Split ShinyUtil.GetIsShiny into separate methods
Some past gen usages weren't passing the bitXor compare `8` and were thus using the Gen6+ 16 value. Let's be explicit.
2025-06-28 00:36:46 -05:00
sora10pls
fd5b9f7db9 Add handling for GO Pass: Ancients Recovered level ranges
Regi encounters have a range of possible levels when encountered from "Ancients Recovered Timed Research: Legendary Giants", allowing for as low as Lv. 1
2025-06-22 17:44:24 -04:00
9Bitdo
cd587828df
Add PJCS 2025 Ray Yamanaka's Amoonguss date (#4516) 2025-06-21 09:28:55 -05:00
Carbonara
15f61ef1ca
DP - Add Spear Pillar related flags (#4512)
* DP - Add Spear Pillar related flags
* HGSS - Add flags tied to the Slowpoke Well
2025-06-20 23:45:07 -05:00
9Bitdo
4153b0aab7
Add PJCS 2025 Hyuma Hara's Flutter Mane date (#4514) 2025-06-20 23:44:19 -05:00
Kurt
8c85a03d78 simplify mysterygift clone
move declaration to derived class, can return specific type now
don't use AbilityType directly, use the ability permission computed property for legality checks. probably can remove this explicit MG method in the future.
2025-06-19 23:36:59 -05:00
Kurt
870dbb1ce3 MysteryGift.Empty -> IsEmpty 2025-06-19 23:35:21 -05:00
Kurt
e3ecf7b593 Fix multifolder mgdb set
no usages currently would pass multiple folders for mgdb, but this fixes that potential bug behavior where it sets the array every folder (gradually repeating allocation work).
2025-06-19 23:33:09 -05:00
sora10pls
554321e5b7 Add latest distribution raid data ❄️ 2025-06-19 20:04:15 -04:00
Kurt
fd72b6ea46 Misc tweaks
no functional change
2025-06-18 16:23:31 -05:00
Kurt
ec4cfc807f in 2025-06-18 16:23:07 -05:00
Kurt
7ce73eb55a Revise enc5 pid/gender setpinga
Some properties weren't being honored; lift nature/ability/IV assignment out of monochrome and do in each enc type.
2025-06-18 16:22:14 -05:00
Kurt
e10d272cde Update SummaryPreviewer.cs 2025-06-16 20:45:08 -05:00
Easy World
30d51fc880
fix(zh-Hans): update legality check messages for type and evolution validation (#4511) 2025-06-15 02:50:58 -05:00
Kurt
c274ff6f3a Add more generator filtering criteria
Gen6+ slots now try to give shinies if possible & requested
Gen3/4 slots now try to respect IV requests if only a couple are requested
Removes PIDGenerator; no remaining uses (either all inlined or extracted to specialized generator classes). API usage wasn't recommended anyway, as it was incomplete and did not honor all correlations. We want to try to generate it right the first time.
2025-06-14 21:45:16 -05:00
Kurt
a57914cf27 Add MonochromeRNG (gen5) generating methods
Extracts a bunch of logic from PIDGenerator
2025-06-14 21:42:47 -05:00
Kurt
422a082f98 Split shadow team tests from shadow test
Use TheoryData to wrap instead of `object`
2025-06-14 21:41:01 -05:00
Kurt
416e519073 Misc tweaks
No functional change, just xmldoc and small inlining
2025-06-14 21:39:49 -05:00
Kurt
fae4340b39 Fix typo 2025-06-14 21:38:10 -05:00
9Bitdo
8cb059e6f6
Add NAIC 2025 Wolfe's Incineroar date (#4510) 2025-06-13 11:16:32 -05:00
Kurt
9e501ea527 Add more xmldoc, fix item9 again
really not a fan of the abstract class, probably better to rewrite everything another day to be less dumb

bug was due to the trickle-down then clearing; object references ended up being duplicated when Unobtainable item placeholders were removed from the pouch and things trickled.

don't bother removing unreleased item data if its quantity is 0.
2025-06-11 22:02:34 -05:00
Kurt
d072f14570 Update EncounterCriteria.cs 2025-06-10 18:53:29 -05:00
Kurt
56e06dcbc1 Update SAV_MysteryGiftDB.cs 2025-06-09 16:55:41 -05:00
Kurt
58ae75cc6c Update 25.06.09 2025-06-08 16:34:06 -05:00
Kurt
ccfa58e5f1 Split PathUtil from Util, add more xmldoc 2025-06-08 16:33:31 -05:00
Lusamine
5b70ca0397 Document SV block for moon phases 2025-06-08 14:21:06 -05:00
Kurt
6784d5f045 Merge branch 'master' of https://github.com/kwsch/PKHeX 2025-06-08 08:38:36 -05:00
Lusamine
0de74b44ba Add LA block for in-game time in minutes 2025-06-07 11:19:40 -05:00
Kurt
ba2245d7d8 Add more xmldoc
Updated PIDIV tests to use IV32 rather than IV sequence checks. Now marked as Obsolete to prevent myself from reusing PKM.IVs getter :)
2025-06-07 09:41:02 -05:00
9Bitdo
5c77f79d88
Add PTC 2025 홍주영's Porygon2's date (#4508) 2025-06-07 08:12:26 -05:00
sora10pls
f78e929428 Pokémon Scarlet & Violet are finally playable 2025-06-04 20:05:50 -04:00
Kurt
d2594d7867 Misc tweaks
nothing needed for 4.0.0, everything works as-is
2025-06-02 21:01:16 -05:00
Carbonara
c17fc13fd2
GSC - Give proper name for decoration and puzzle flags (#4505)
Also fixes some lines in the French translation.

For the decorations, the formatting is based on the one of the games, but with names not written in ALL CAPS. As I do not have English copies for the names, I've used the formatting present on this page, https://bulbapedia.bulbagarden.net/wiki/List_of_decorations_in_Generation_II, and I've been using the Nintendo Power formatting for the plants.

There was an error of labelling: an entry was named DECO_PLANT_4, but there are only 3 plants available. After checking, this entry is actually the Town Map, that was listed separately as PLAYERS_ROOM_POSTER. As such, the entry named PLAYERS_ROOM_POSTER is incorrect. I have no idea what this is, if it does anything or not (doesn't seem to change anything when enabled or disabled at a first glance), so this would need someone to check that. For now, I removed the entry from the list, but feel free to readd it or cancel that change if it is needed anyways or if you can figure what it is.

On a similar domain, DECO_STARMIE_DOLL is actually a Staryu Doll, though the in-game name on its own is enough to fix the issue.

The zh_hans translation had some lines not translated in the proper order, so I corrected those lines by putting them in the proper order. (checked by combining translation software, wiki page https://wiki.52poke.com/wiki/%E8%A3%85%E9%A5%B0%E7%89%A9%E5%93%81%EF%BC%88%E5%9F%8E%E9%83%BD%EF%BC%89, and manual search of the Pokémon names to ensure everything was correct).

The decoration flags don't necessarily seem to fully work: they work to unlock a decoration, they work to remove it if unlocked through PkHex, but just disabling the switch on a save file that got the decoration through normal play doesn't seem to always work from what I can see, may need to be investigated.

Documenting the changes made for the French translation also.

Some of the decorations have different names between French Gold/Silver/Crystal and French Stadium 2, documenting them here:

French Gold/Silver/Crystal:
- "Lit à Plumes"
- "Lit :Rose"
- "Tapis :Rouge"
- "Tapis :Bleu"
- "Tapis :Jaune"
- "Tapis :Vert"

French Stadium 2:
- "Lit en Plumes"
- "Lit Rose"
- "Tapis Rouge"
- "Tapis Bleu"
- "Tapis Jaune"
- "Tapis Vert"

For the French translation, I'm using "Lit à Plumes", while for the other entries I'm using the names from Stadium 2 (no need for ":" in the names, and the formatting is odd).

I'm also adding a space for "JouetPikachu Surf" to be "Jouet Pikachu Surf" (space lacking due to the lack of space in the original text - Jouet should also normally be Poupée, but was translated as Toy due to the lack of space, but I'm not renaming it to not be confusing).

Fixing other French mistakes while I'm at it.
- Bec Pointu was written as Bec Pointy in the GSC flags
- Made the flag edit warning message be shorter to be able to be fully displayed
For the Entralink lines:
- Translated w/o to sauf, and wrote No in lower letters
- Changed NOUVEAU to NOUV. to fit in the textbox
- Changed Meilleurs records: to Total meilleurs scores to be clearer and not use ":" due to being odd
- Fixed Verrouillé and Déverrouillé being misspelled
- Changed Le plus de participants to Record nbr. joueurs to fix in the textbox and be more concise
- Fixed the line Verify status which wasn't using the same formatting than another similar line, and had several typos

The translations for the Entralink may not be final, it depends on if I revisit the translation of PkHex to complete it in some categories later on or not.
2025-06-02 18:38:20 -05:00
sora10pls
bc8ea34474 Add latest distribution outbreak data 🦆🖥️ 2025-06-01 20:05:14 -04:00
Kurt
602b1b6371 Add more xmldoc 2025-06-01 11:08:07 -05:00
Kurt
675c017a56 Merge branch 'master' of https://github.com/kwsch/PKHeX 2025-05-31 22:51:57 -05:00
Kurt
bf9e53efa1 Misc tweaks
Add more xmldoc
Simplify some expressions
Reduce unnecessary logic
2025-05-31 22:51:55 -05:00
Kurt
75cf9b0934
Use more modern PluginLoader implementation (#4503)
* Use System.Runtime.Loader to load plugins

Can now unload plugins if need be. Load->Update->Unload->Load(new) ?
2025-05-31 21:03:12 -05:00
Kurt
93d9292d83 Minor clean 2025-05-30 17:41:54 -05:00
sora10pls
af92d43650 Add more bugs to Pokémon Scarlet & Violet 2025-05-29 20:06:34 -04:00
Kurt
87d55fc303 revise xd ID check to exclude PAL
ty John_0902 on discord
2025-05-29 10:16:51 -05:00
Kurt
fd766a5506 Filter db search dropdowns for current context
"why can't I find Yveltal in SV?" because it doesn't exist in the game
now matches the dropdowns of the main PKM editor
2025-05-29 10:04:06 -05:00
Kurt
c9b8a5b893 Misc tweaks
No functional change
2025-05-29 10:03:17 -05:00
abcboy101
53ddcfb0e4
Update Switch badwords to v20.1.0 (#4502) 2025-05-28 08:54:32 -05:00
Kurt
c4199b26ec Minor startup optimization (resource sizes)
-288 KB (-31%) across lvlmove/eggmove/evolve binaries
redesign the levelup bins:
- be moves_levels rather than the "official" -1 stop of the past era.
- gen1/2 reformatted from byte,byte[] to ^ to skip initialization work
redesign the eggmove bins:
- be simply moves[], rather than the "official" -1 stop of the past era; now is just a struct to keep the array readonly with no further allocation.
- same for gen2 skipping initialization byte[]->ushort[]
- for gen7/8 formtable indexed, just use the personal table indexing style of SV.
added a 16-bit version of BinLinkerAccessor as start/end offsets of <65KB files are always 16bit. Saves a fair bit of space in eggmoves/evo where there's often 0 entries for a species-form.

Obviously binlinker16 is an unofficial format, but there's no need to replicate official serialization formats if we instead use a universal & maintainable alternative. Plus they don't even use BinLinker across all their games.

Adds a debug BinLinkerWriter because I'm tired of digging up the zipping implementation :)
2025-05-25 16:27:05 -05:00
Kurt
1d9fc99413 Misc tweaks
Add Count to IPersonalTable
Revise EncounterOrigin for evo chain search to use Context instead of Version
Use ITrainerID*ReadOnly as pivot for Trainer ID verification skip
Add more xmldoc
Reconfigure pla dex task fetch to be nullable, match fbs field ordering for clarity
2025-05-24 22:59:13 -05:00
abcboy101
4963d11c14
PBR: Fix box names, add play time (#4501)
* Allow empty box names in PBR

Before copying Pokémon from a DS game, all of the box names are zeroed-out. Allow these values to be read/written normally.

* Preserve current slot when cloning PBR save

Certain editors like SAV_BoxLayout clone the save file before editing it. This preserves the selected slot in the clone instead of resetting it to 0.

* Add PBR play time accessors
2025-05-24 08:15:55 -05:00
Kurt
48954533b5 Make mysterygifts memory-backed objects 2025-05-24 00:10:57 -05:00
Kurt
be67b007d5 Misc tweaks 2025-05-24 00:10:02 -05:00
Kurt
2f77b9c2aa Add xmldoc 2025-05-23 20:44:06 -05:00
Kurt
1c03fc3e5b Minor clean 2025-05-23 20:07:03 -05:00
sora10pls
e8ce5c1317 Add latest distribution raid data 🦈🌍 2025-05-22 20:03:44 -04:00
Manu
e72995e898
Standardize IGenerateSeed32 API for gen9 raid encounters (#4497) 2025-05-20 22:53:32 -05:00
abcboy101
09f654fd34
Use correct badwords list (#4498) 2025-05-20 22:35:16 -05:00
Kurt
fb803c6e4d Revise region handling and game version mapping
Updated region handling logic in XK3.cs to treat PAL and NTSC_U as equivalent and adjusted remapping logic accordingly.
Added a check in PKMEditor.cs to map GameVersion.COLO and GameVersion.XD to GameVersion.CXD for consistency in filtered data sources.
2025-05-19 23:37:41 -05:00
Kurt
064e4293a0 Save Language first for gen3pkm
So that Nickname encoding can work as expected. Also fixes mainline int<->jpn changed edits

ty rainbowsunsetwaves on discord

Extend the fix to user-created eggs in Gen3 so that the OT name is converted.
2025-05-19 22:51:47 -05:00
Kurt
072dae8d14 Update InventoryItem9.cs
Closes #4496
2025-05-19 21:54:16 -05:00
Kurt
8c4abe0a50 Update window style flags in PokePreview
Not sure what caused the behavior change, but this ensures the window stops stealing focus, as well as being shown TopMost.
2025-05-19 11:26:10 -05:00
Kurt
0d5f7c758a Fix pla purchased flag regression
ty cerquami for checking
2025-05-18 22:53:17 -05:00
Kurt
533c870ca5 Revise stat abbreviations
Closes #4495

fix german's order (speed last)
2025-05-18 18:13:51 -05:00
Kurt
bf31d9119f Update 25.05.18 2025-05-18 02:37:40 -05:00
Kurt
47bc45d854 GetLevelLearnMove->TryGetLevelLearnMove
As alluded to in 7442e86d65
2025-05-18 01:36:05 -05:00
Kurt
5a3ebec12b Fix characteristic of all-0 IV mons when EC%6!=0
They'll always be HP.
ty Anubis for testing and SirToastyToes for reporting

Changing characteristic is a 1 in (5/6)*(32^6) chance, when no IVs are forced; aka ~1:900million
or just get a colo e-reader mon with 5/6 chance :)

Gen4/HOME(mobile): correct, unaffected
Gen5-Gen9/HOME(switch): bugged
2025-05-17 15:03:42 -05:00
Kurt
7442e86d65 level int -> byte
Might refactor the learnset level get to byte later as a TryGet so -1 is never returned.
2025-05-17 14:45:49 -05:00
Kurt
6b7938fea1 Add mousewheel event to edge Level/EXP
Scroll up Level once to increase level, scroll down EXP to be 1 exp from level up.

Apply the same mousewheel events to Friendship, IVs/EVs/AVs/GVs, with EVs being increments of 4.
I don't think it's worth overriding keypress arrow up and down to do the same.
2025-05-16 17:11:34 -05:00
Kurt
09f0462736 Refactor methods to use Try-pattern and improve code clarity
Refactored several methods across multiple files to use the Try-pattern for better null safety and clarity. Removed unused code, such as the SingleLevelRange record, and improved type handling in BatchEditing and BoxManipUtil. Adjusted constructors and properties for consistency in classes like CustomFolderPath. Minor updates to improve code readability and maintainability.
2025-05-16 16:12:48 -05:00
Kurt
83bc2bf653 Set egg form for gen5-9
Missed this in the PR
gen5 needs form for basculin
2025-05-14 01:35:11 -05:00
Kurt
fa9713a1ad Misc egg tweaks 2025-05-12 21:10:23 -05:00
Kurt
85f5950f28
Split EncounterEgg into derived classes (#4490)
Splits EncounterEgg into derived classes, allowing for fine-tuned control of each generation's egg generation & pattern matching.

Adds an interface to check if the encounter is a bred egg (useful for many scenarios when checking for move inheritance, in general).

Enhances the deferral rating for PIDIV matches in eggs based on global legality check settings.

Adds date/time indicators for Gen3/4 eggs and other Method 1 encounters.
2025-05-11 22:31:36 -05:00
Kurt
b2d70295e9 SV: fix deoxys TM flag check for altforms
ty thedominantspoon on discord
2025-05-11 19:24:07 -05:00
sora10pls
789a410272 Add latest distribution raid data 🪓 2025-05-08 20:02:42 -04:00
Kurt
dc0dbbe340 Update InventoryPouch.cs 2025-05-08 18:42:15 -05:00
Kurt
af8cb884e6 Update InventoryPouch9.cs 2025-05-08 17:31:45 -05:00
Kurt
d0a79acdb4 Update CommonEvent3Checker.cs 2025-05-08 11:22:38 -05:00
Kurt
275f5fb5df Misc tweaks
Seal some classes
Use derived pkm class for template->pk* moves (pk3/pa8)
Revise inventory9 to better handle empty slots
Cache legality for Summary report grid (does this make it faster?? seems to open instantly); add shift-quit to skip prompt
Fix handling for level range on encountercriteria passing level range for gen3/4 encounter slot method1/etc lead checks
2025-05-07 23:06:40 -05:00
hewenhan
61b38d1aa0
Fix: Disable CETCompat to prevent crash with deep InitialDirectory (#4481)
Setting CETCompat to false in the project file resolves ntdll.dll crashes observed on .NET 9 Preview when OpenFileDialog's InitialDirectory points to deep paths like MyDocuments. This allows the application to run without triggering CET violations in this specific scenario.

Temporary merge until .NET 10; CETCompat isn't necessarily required for this application so no harm disabling this feature.
2025-05-05 10:51:06 -05:00
Easy World
b656510939
Update zh-Hans localization (#4488)
* Update zh-Hans localization: improve Pokémon gender terms, AVs translation, and clarify the transfer handler legality message.

* Clarify zh-Hans translation for LTransferHTFlagRequired
2025-05-05 09:24:23 -05:00
santacrab2
d6c484295a
gen 4 nature (#4487) 2025-05-04 21:52:12 -05:00
Lusamine
e02564cc08 Remove Sandstorm from weathers for IoA Wailord
It isn't possible to spawn Wailord from any area with Sandstorm and
reach it without despawning it. All of them are too far or require a
path that is out of its spawn radius.
2025-05-04 15:42:47 -05:00
Kurt
a85f919630 ShowdownSet: handling for context-locked forms
Retconned forms like Totems and Cosplay Pikachu that only exist in a specific context now get recognized again
Current-Context has shifted from Gen6->Gen7->Gen8+, so a "get form list for species" won't return these forms.

Not a perfect implementation, but better for now.
2025-05-04 13:19:40 -05:00
Muhammad Kassar
a77e60d8d8
improve fr translation (#4486)
* improve fr translation

* trim down truncated stat names
2025-05-04 13:16:07 -05:00
Kurt
0e0d812d83 Add more xmldoc 2025-05-03 23:55:06 -05:00
Kurt
ae3bb75fe6 ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual 2025-05-03 23:54:11 -05:00
Kurt
ed67be3498 Keep Summary Previewer form above all other forms
Sometimes the z-order can get messed up when the PC/process is put to sleep by the operating system (or something else happens?) -- either way, just force it to the top since it should always be on top when shown.
2025-05-03 23:53:11 -05:00
Kurt
a7420cb3de Lift stat amp reinterpret to nature adjust
Allows retaining a stat parse amplification tracking result in visual order, unless requested in stored order. Results in slightly less logic being done on un-kept parses/IV parses.
Adds xmldoc
Extract Ability/Nature parse lines to add parse fail indicators on conflicting values.

```
Piplup
[Torrent] @ Oran Berry
Ability: Defiant
```

Will now flag the conflicting ability
2025-05-03 23:52:08 -05:00
Kurt
2514e66331 Fix stat leading zero 2025-05-02 11:04:06 -05:00
Kurt
77f1b637c9 Misc tweaks 2025-05-02 01:34:49 -05:00
Kurt
f730f7d19a
Feature: Localization of Battle Templates (Showdown Set) (#4482)
* Localization capability for each language, import & export
* Lines with stat names (IVs/EVs) can be configured to many representations (X/X/X/X/X/X, HABCDS, etc).
* Add nonstandard localizations
* Add token types for Showdown's new set format
* Add new program settings for hover & export styles. Allows users to select which presentation format they want for the hover previews, as well as the set export format.
* Revises preview hover GUI to use new settings
* Revises export events to use new settings
* Moves no longer indicate end of set
* Enhance robustness of stat parsing
* Expand all settings in settings editor on form load
* Extract clipboard -> sets operation to api for maintainability & reusability
2025-05-01 23:16:36 -05:00
abcboy101
63516fc718
Update badwords (#4483) 2025-04-30 17:04:59 -05:00
sora10pls
d4b2b2c75f Add latest distribution outbreak data 🟡 2025-04-29 20:06:04 -04:00
Gengar
8adb7e2dc9
Add Korean Ditto Serial Code Distribution Event (#4480) 2025-04-24 20:57:11 -05:00
Kurt
a6e2e08ecb Add pokepast.es url import
why not
2025-04-21 01:10:57 -05:00
Kurt
87d2f10c7f Misc tweaks
Add date validation for lgpe go park encounters (deferred, now finally remembered to implement?)
2025-04-21 00:57:23 -05:00
Kurt
154c370901 Merge branch 'master' of https://github.com/kwsch/PKHeX 2025-04-20 11:24:04 -05:00
Kurt
d6a18ddc10 Batch editing: accurately track untouched results
still feels goofy
2025-04-20 11:23:59 -05:00
abcboy101
d633202af3
Add accessors for PBR save language (#4475)
* Allow PBR saves with at least one valid half

* Add accessors for PBR save language
2025-04-20 11:17:16 -05:00
Kurt
972c432205 Minor tweaks
no functional change, just updating some style for readability
2025-04-20 11:08:10 -05:00
Kurt
b8eb980241 Enhance folder path tracking
Internal metadata, not really useful but maybe in the future.
2025-04-20 11:06:54 -05:00
Kurt
383037db43 More xmldoc updates 2025-04-20 11:06:08 -05:00
Kurt
15a7f622fc Update EncounterServerDate.cs
Co-Authored-By: 9Bitdo <157295620+9Bitdo@users.noreply.github.com>
2025-04-19 23:11:46 -05:00
Kurt
8c20444368 Lowercase bag sprite icons
Co-Authored-By: Nova <nova@zeusteam.dev>
2025-04-19 14:34:09 -05:00
Kurt
87eb8529ff Refactor Showdown team parsing methods
Renamed methods in ShowdownTeam.cs for clarity
Simplified IV validation logic in ShowdownSet.cs using AsSpan().ContainsAnyExcept, remove unnecessary xmldoc inherit
Updated references in Main.cs to reflect these changes.
2025-04-18 20:56:39 -05:00
Kurt
45d95d5742 Add showdown team import from url 2025-04-17 20:27:24 -05:00
Kurt
2d48c56200 Add seed parameter to gen3/4 GetRandom*
Rather than call Util.Rand32() inside the method, allow passing in an arbitrary seed.
2025-04-17 19:53:27 -05:00
sora10pls
73abf1a0ec Add latest distribution raid data 🎈 2025-04-17 20:02:22 -04:00
Kurt
d699b6db3e Update MethodPokeSpot.cs
Closes #4470

Co-Authored-By: santacrab2 <79347566+santacrab2@users.noreply.github.com>
2025-04-16 17:09:05 -05:00
Ka-n00b
56e2ec427e
Update Gen IV Event Constants (#4471) 2025-04-16 17:06:44 -05:00
Kurt
3a4cd9a0cd Update EncounterSlot3XD.cs
Closes #4468

Co-Authored-By: santacrab2 <79347566+santacrab2@users.noreply.github.com>
2025-04-15 23:10:15 -05:00
Kurt
ef2152cf3b Misc tweaks for cxd encounters/viewing
convert, adapt to save file on view (fixes viewing gen3 ot/nick'd encounters in cxd)
display original string in cxd format (useful for jpn->eng->ENG colo, for string matching? might need to revert)
hard-match version for colo gift (MATTLE) to not confuse 10ANIV
2025-04-15 02:50:25 -05:00
Kurt
ba4d054089 Allow Heal box on all formats
pp, hp, whatever
revise sorting behavior for custom descending sorts; have Favorite mark sort as marked first.
Add time of day sort for Gen2
2025-04-13 16:56:18 -05:00
Kurt
207135948a Remove some PP set fluff methods
just a wrapper to do the same thing... nah
2025-04-13 16:54:20 -05:00
Kurt
4da88ac063 Minor clean 2025-04-13 12:02:33 -05:00
Kurt
76f2705c9c Misc tweaks for enc->pk ctor language/version
lang->language
only pass version if relevant
2025-04-13 11:58:33 -05:00
Kurt
efa627f703 Make SimpleTrainerInfo immutable after init 2025-04-13 11:55:42 -05:00
Kurt
8a19968321 Minor clean 2025-04-13 11:55:20 -05:00
Kurt
1914c482c6 Add readonly interface for 3DS georegion 2025-04-13 11:52:51 -05:00
Kurt
be874dcc50 Extract TM lists to PersonalInfo
Fix gen3 allocating eggmoves 3x (instance) instead of just once (static)
2025-04-13 11:49:45 -05:00
Kurt
fbfa28a0cf Sunset PKX -> Latest
Relocate gender ratio info to EntityGender
farewell PKX; once a behemoth catch-all of >4000 lines, now no more. 😢
2025-04-13 11:44:28 -05:00
Kurt
57060cbea5 Rename PP arrays for consistency 2025-04-13 11:43:00 -05:00
Kurt
9218f97971 Rename item pouch legal lists, make public
No longer need to protect for immutability since they're now ReadOnlySpan.
2025-04-13 11:42:59 -05:00
Kurt
92cd8feaf1 Use entitycontext for xmldoc instead of lump 2025-04-13 11:34:17 -05:00
Mow
2f1a11faa7
Implement Gen 4 groups and group-derived values (#4467)
* Fix swarm and safari seed locations

* Implement Gen 4 group system

* Implement Gen 4 BattleTowerSeed

* Gen 4 Lotto number

* Creator -> Leader in line with in-game terminology

* Update Group4.cs
2025-04-10 20:43:23 -05:00
rganhoto
7219e39b0f
Pokedex - fix cannot unsee forms in Gen4 (#4466) 2025-04-10 13:11:44 -05:00
Carbonara
f020b3eb52
Update the French translation (#4465)
- Rework a bit the French translation on some lines; Translate some new lines; Fix some formatting (;, :, ! and ? are supposed to have a space in French before, though it is not done in Pokémon games prior to Gen 6 for space reasons); Fix some terms being incorrect or not being those expected (e.g. Pokémon Outil de lien refers to Poké Lien, super entrainement refers to SPV
- Translate all of the Funfest Missions in French. Official names and formatting are used. Changed B and W to N2 and B2 to be a bit more consistent for the initial of the game.
- Translate flags and constants for GSC in French, using official terms. Left the GSC decorations untranslated due to not wanting to figure for now which decoration is which, same thing than what is done in English.
2025-04-09 19:49:08 -05:00
Kurt
5ab6dbc0ac Add cancellation to savefile detection calls
5s timeout on detection, roughly
2025-04-06 22:25:37 -05:00
Lusamine
56ab067ad9 Standardize 'usable' in flags, correct spelling 2025-04-04 21:41:59 -05:00
Kurt
8700e11eb2 Fix flag get
flag1 would indicate true if flag2
2025-04-04 01:02:48 -05:00
Kurt
87b77f8a9d gen6: submission PP
Closes #4462
2025-03-30 22:39:19 -05:00
Kurt
a0be3f5f5f xy: Fix radar record read/write 2025-03-30 00:04:17 -05:00
sora10pls
7eda8ada4d Add latest distribution raid data 👻 2025-03-27 20:02:45 -04:00
Kurt
94b47d61da Add smeargle verifier branch
Add gen2 nosketch moves while we're here, and Stadium2's extra quirk one.
2025-03-27 18:43:37 -05:00
Kurt
542d6541c3 Extract stadium reminder moveset verifier step
Now flags as Stadium2 as source
2025-03-27 14:27:17 -05:00
Kurt
49061f0047 Add placeholder stadium2 pkl&api
private static readonly LearnsetStadium[] Stadium = LearnsetStadium.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("reminder_stad2.pkl"), "s2"u8));

https://bluemoonfalls.com/pages/general/move-reminder
ty for the easier-to-digest parse; reinterpreted further into pkl
2025-03-27 00:53:12 -05:00
Kurt
83aff8b889 Add murkrow rebattle empty set
ty Unknown Warrior on discord for reporting

enhance heracross' note
2025-03-27 00:15:49 -05:00
Carbonara
7b8d51df37
Update lang_fr.txt (#4461) 2025-03-26 12:48:21 -05:00
Lusamine
d170908a3d Standardize apostrophes in Encounters8 comments
Use the straight apostrophe which is more easily typed, which allows for searching by location name.
2025-03-25 11:38:17 -05:00
Ka-n00b
1098481c85
Update B2W2 Event Flags (#4460)
* Update flags_b2w2_en.txt

* Update flags_b2w2_es.txt

* Update flags_b2w2_ja.txt

* Update flags_b2w2_ko.txt

* Update flags_b2w2_zh-Hans.txt

* Update flags_b2w2_zh-Hant.txt
2025-03-25 07:43:22 -05:00
Kurt
b303ea90ef AbilityVerifier: gen5 ability patch for genies
and giratina
extract to a single location
abilityverifier is still needing a rewrite to clean up all the branched logic, but it's still "functional"
2025-03-24 10:29:22 -05:00
Kurt
ca03311d21 PA8: Allow un-updated mid-scale alpha floats
https://projectpokemon.org/home/forums/topic/57375-pkhex-new-update-legality-errors-contribution-page/page/31/#findComment-294285

While we're here, fix PB7->PKH to retain the ReceivedTime values. Not that it matters, because those PKH values go nowhere.
2025-03-23 13:25:54 -05:00
Kurt
5376d44de8 Add $suggest for friendship properties 2025-03-23 12:16:52 -05:00
Kurt
4c15e7fd88 Gen4: permit 0 pid for bred eggs
masuda method rolls forward the egg seed, therefore 0 is reachable
2025-03-17 22:03:04 -05:00
Kurt
9b82cdfc80 Misc tweaks
CXD: indicate shadow index on hover, pokespot area
CXD: flag eggs
CXD: enc->pk try to match shininess when requesting all IVs
make some signatures dealing with level use as byte
2025-03-15 23:15:55 -05:00
Kurt
7f33741abb PP verifier: relax check based on typeof(PKM) 2025-03-13 21:53:27 -05:00
Kurt
ee3665cbe8 Gen2: Fix spanish Aerodactyl Trade nickname
NORMA is the OT, not the nickname
2025-03-13 21:50:21 -05:00
sora10pls
cb6d4ef70f Add latest distribution raid data 💧🦆🕺 2025-03-13 20:03:26 -04:00
Kurt
32887b5659 Revise LockFinder to accept u32, not pidiv 2025-03-12 21:24:38 -05:00
Kurt
e609081892 cxd: fix enc->pk3 with criteria IVs set
not sure why this unroll was in there... now works as intended.
2025-03-12 00:57:11 -05:00
Kurt
ca81fcd250 CXD: check naming screen->TID/SID frame pattern
Adapted from e5255b1313/PokemonCoRNGLibrary/Util/LCGExtensions.cs (L15)
2025-03-10 22:25:53 -05:00
Kurt
f4cfc39173 Gen9+: Show gold Battle Memory ribbon if 7+
https://bulbapedia.bulbagarden.net/wiki/List_of_Ribbons_in_the_games#Battle_Memory_Ribbons
> As of Scarlet and Violet, Pokémon that obtained seven out of the eight possible Ribbons will also display this version, owing to the fact that the World Ability Ribbon hadn't been officially obtainable for 8 years as of the game's release
2025-03-10 00:32:15 -05:00
Kurt
c77ea8fd4c PokeSpot: add IV animation pattern check
Realign slot match check to what was documented: https://www.smogon.com/forums/threads/past-gen-rng-research.61090/post-4755981
Most RNG tools assume that both are available to spawn, as that would be an annoying setup.

IV animation pattern: 075f1340c6/PokemonXDRNGLibrary/Generators/PokeSpotGenerator.cs (L48-L56)
2025-03-10 00:11:29 -05:00
Kurt
5bf9865f3f Add pokespot reverse/forward methods
Also adds partial IV specifying for cxd generating
2025-03-09 20:09:17 -05:00
Kurt
2fbe2feb52 Fix gen4 pcd sentinel set
Closes #4407
Bug: 0th PCD slot not being provided would set PGT slot0 to "inactive", rather than setting pcd slot0.
The modeled GUI is still very clunky (editing DP just ignores the flag, and assumes if data exists it is active).

fix a typo while we're on the topic of Gen4 :) -- ty 0blivion0athkeeper on discord
2025-03-08 11:13:35 -06:00
sora10pls
fed4a6e900 Add latest raid distribution data 🔥🐊🎤 2025-03-06 19:02:10 -05:00
Kurt
0de0bd4c0b Add setting to allow colliding gen3/4 egg pidivs
For those who think it is funny to RNG abuse egg PID/IVs to match a recognizable algorithm type. Opt-in setting because 99.9% of the time it's going to be a hack.
#4347
#3894
#3092
2025-03-06 09:37:26 -06:00
Kurt
4d702d261d Add ivs->channel seed recovery
Still lack IVs/nature/shiny request for the BACD/M2 variants, maybe in the future those can be added.
2025-03-05 22:45:41 -06:00
Kurt
8b5c07b07d Update Main.cs 2025-03-05 21:31:47 -06:00
Kurt
a74d882077 Loosen SV 2.0.0/3.0.0 size checks to ranges
Too many permutations of optional blocks to want to think about.
Since we already rely on hash validity, a range check should be more than sufficient to eager-check and prevent size clashes.
2025-03-05 00:13:11 -06:00
Kurt
3f305c9135 Handler check: bypass for blank saves
Revise "past gen" message since it's not past-gen only (now applies to cross-context like SV->BDSP)
2025-03-03 00:27:26 -06:00
Kurt
1ad7a6b5fb Autosize splash screen on startup
Higher scale was truncating, let it size up
2025-03-02 22:46:13 -06:00
Kurt
a084291532 Gen3 rse swarm: fix gen on rs, match
don't complain on random discords, actually report issues k thx
2025-03-02 22:45:51 -06:00
Kurt
356db93e9f Settings: version select typeable combobox
Was annoying cycling through all these versions that start with the same character. Works well enough to just do it

bv5: ignore checksum integrity, the checksum is over the decrypted data (not while encrypted).
2025-03-02 01:11:29 -06:00
Kurt
4adf450b08 Add gen4 mg timer indicator 2025-03-02 01:08:04 -06:00
Kurt
c47e595cd3 Enhance Stadium1 box read/initialization
Detect the current box buffer, initialize boxes if not already, and hide (delete) slots that aren't present (ignore ghost slots).
Compress storage before export.
Closes #4454
2025-03-01 20:07:26 -06:00
Kurt
78ccc92dea Update SecretBaseManager3.cs
Closes #4456
2025-02-28 00:10:39 -06:00
sora10pls
bf7441602e Add latest distribution raid/outbreak data 🐈🔴🟢🔵 2025-02-27 19:04:32 -05:00
abcboy101
6668350b68
Add Mainland China Melmetal gift (#4455) 2025-02-27 00:49:57 -06:00
Kurt
4bdd673fd0 Add label for LGPE arrived datetime
Allow editing of memories while an egg (SW/SH traded eggs with memories...)
2025-02-25 20:38:13 -06:00
Kurt
dfda71dddd Rename default label names 2025-02-25 20:37:34 -06:00
Kurt
dbff2695d7 More main editor appearance pixel tweaks 2025-02-24 22:38:21 -06:00
Kurt
6c8f312d17 Update PKMEditor.cs 2025-02-24 21:39:34 -06:00
Kurt
8626619b60 move combobox ordering oops 2025-02-24 21:31:16 -06:00
Kurt
7acc86ca61 Add API method for finding a legal shiny SID
if returns false, try a different TID
2025-02-24 18:17:45 -06:00
Kurt
7bfbc948c3 Enhance main form scale sizing 2025-02-24 18:17:27 -06:00
Kurt
9fb11b6041 Rework Cosmetic pane buttons into panel 2025-02-24 08:30:42 -06:00
Easy World
cb18310cee
Update zh-Hans localization strings for date validation and export legality (#4451) 2025-02-24 00:02:44 -06:00
Kurt
53d47e5c64 Update localizations 2025-02-23 23:34:11 -06:00
Kurt
87fb241a98 Update 25.02.23
fix bvid5 checksum check
Gen7b: add ReceivedDate/Time editor to OT/Misc tab, enable Ribbon button
2025-02-23 23:16:43 -06:00
Kurt
c8ec63992b Rework entity import settings param passing
Closes #4418

Introduce a readonly record struct to contain all the settings selected. Add simple static get for none/all to disable/force the updates.

Split the UpdateRecord behavior out of UpdatePKM (adapt to save file) so that it behaves similarly to UpdateDex.
Only AdaptToSaveFile when opening a file in the PKM Editor.
2025-02-23 15:50:17 -06:00
sora10pls
8f86e3fec0 GO: Handle Road to Unova Timed Research encounters
First time Level 20 Mythicals have been encounterable in Research, need to account for the minimum IV difference should any more occur in the future.
2025-02-23 15:49:29 -05:00
Kurt
65df18ae66 Extract entity/template game presence filter 2025-02-23 11:25:47 -06:00
Kurt
acfdd9bab3 misc tweaks
reduce allocation lol
2025-02-23 11:19:50 -06:00
Kurt
7279af79b2 Extract pk5->pk6 PID mutation logic 2025-02-23 11:19:33 -06:00
Kurt
a1ea915b52 Extract CuteCharm4 api 2025-02-23 11:19:03 -06:00
Kurt
8ac937b83c Fix Coronet Feebas SlotNumber tags
#4450
0deb19d1e8
2025-02-23 11:18:04 -06:00
Kurt
8cb14379a6 Resize Gen3 RTC editor buttons
Allows for long-winded languages like German to better label button text :)

Closes #4449
2025-02-21 20:07:48 -06:00
Kurt
5716ec25e3 Misc tweaks
Add some ease of use interaction to Trainer Database objects
Throw exception on bad picturebox early, instead of writing the file and aborting
2025-02-21 20:07:42 -06:00
Manu
e93425e137
Handle HOME Keldeo & Meltan; Revise WB7 (#4446)
* WC8: Handle HOME Keldeo
* WB7: Handle HOME Meltan fixed ability, scalars, AND HeightAbsolute and WeightAbsolute
* WB7: Fix PID get/set offset
* PB7: add ribbon get/set (Meltan has Souvenir ribbon and is retained on transfer to LGP/E)
* Legality: Verify Size Absolute for WB7 Home gifts, only check absolute scale when PB7
2025-02-21 20:00:28 -06:00
9Bitdo
96d779f9b5
Add Marco's Jumpluff date (#4448) 2025-02-21 19:43:51 -06:00
Kurt
3f3d43dca5 Gen1: Add Japanese tour mews 2025-02-19 19:19:39 -06:00
ShadowMario3
757f84db9e
Update EncounterGift1.cs (#4447)
Add more OTs for Gen 1 GB Mews.
2025-02-18 13:07:32 -06:00
sora10pls
f069b950ff Add latest distribution raid data 💘🍬 2025-02-13 19:04:23 -05:00
Taylor Rodríguez
b2f962f442
Fix LGPE local time validation (#4445)
The function `IsTimeValid` checks if the received hour, minute, and
second of the Pokémon is below 24, 60, and 60 respectively. For some
reason the current code includes a "+1" for each check, which results in
some legitimate Pokémon being marked as having an invalid timestamp.

Fixed edge cases:
- Received on the 23rd hour.
- Received on the 59th minute.
- Received on the 59th second.
2025-02-13 07:46:49 +00:00
Kurt
806548a456 Update MiscVerifier.cs 2025-02-13 05:08:30 +00:00
Kurt
93179c0281 Add LGPE received datetime r/w/lc 2025-02-13 04:53:37 +00:00
sora10pls
1fd1c0f67c Add preliminary handling for Keldeo & Meltan events
Shiny Meltan marks the first (and likely only) time where a LGPE event is server date restricted, so we need to extend those checks to include WB7s.

Also revise Manaphy & Enamorus start dates due to server date assignment logic changing at some point. In the past, it was always assigned according to UTC, but it is now determined by local server time, making January 27 possible in early time zones (Mountain, Pacific, etc.)
2025-02-12 14:58:08 -05:00
9Bitdo
03627b641e
Add Pokémon Day 2025 Flying Tera Type Eevee's date (#4440)
* Add Pokémon Day 2025 Flying Tera Type Eevee's date

* Refactor: add date bias for generate

---------

Co-authored-by: Kurt <kwsch@users.noreply.github.com>
2025-02-07 22:16:39 -06:00
BlackShark
c784edbe1b
Added setting to export with box and slot index (#4443) 2025-02-07 00:21:10 -06:00
Professor Dirty
4a706bcae7
Addition and correction of CHS translation (#4439) 2025-02-07 00:20:39 -06:00
sora10pls
7c25229236 Add latest distribution outbreak data 🐦🐦🐦 2025-02-06 19:06:15 -05:00
Ka-n00b
a1914105bf
Update Event Flags/Constants (#4437)
Adds some new and fixes grammar and formatting of existing ones. Additionally, renamed 2 Sword/Shield block names.
2025-02-03 23:38:13 -06:00
Manu
cb7cfef5fa
Fix CHT OriginalTrainerName setter for crafted WCs (#4438) 2025-02-03 22:18:56 -06:00
Manu
8d5866f118
Handle Arbok Mainland China wondercard (#4436)
* Add Arbok wondercard to Mainland China Gifts

* Always pick CHS OT/nickname for Mainland China gifts
2025-02-03 22:17:58 -06:00
Kurt
f538d2c4be Skip random level check if criteria not in range 2025-02-02 23:18:15 -06:00
Kurt
9dedd33694 Duplicate locations: across lists, not each array
BD/SP has Pokémon HOME as location 30018 and 40086; need to differentiate.
Revise the unit test to share the unique-check set across all location lists of that context

Ignore "empty" location values (dashes, empty). For deduplicating, ignore empty indexes as well.
2025-02-01 19:23:11 -06:00
Kurt
e04ad2c6b2 Misc tweaks
Fixes shiny rayquaza
some lowering int->byte
Fix event3 random restricted seed fetch for gender
Fix gen8a encdb filters excluding static encounters, now will be properly excluded
2025-02-01 10:42:26 -06:00
Professor Dirty
f36963d6e6
CHS: Update gen1-3 translations of some items/flags (#4430) 2025-02-01 09:41:02 -06:00
Manu
c456e8e212
Handle generation and legality for HOME Manaphy and Enamorus (#4432)
* Home Enamorus actually uses Cherish ball

* Handle Enamorus fixed scalars

* Handle Manaphy fixed scalars
2025-01-31 22:30:35 -06:00
Kurt
8e5de20881
Add Form to EncounterCriteria, apply from seed interface (#4433)
When generating from templates with randomly correlated forms, the EncounterCriteria will now seek for a match for that feature if specified. Granted this is just a cosmetic seek for Generation 4 Unown -- Gen3 Unown is locked to a specific form for each encounter slot.

Also adds an interface to templates that expose a shortcut to SetPINGA's randomness, for those who know what seed they want to generate from. This is currently only implemented into encounters from Gen8 (Raids/8b Roamers), Gen9 (Tera Raids), and Gen3 events (Channel, as others can be found via IVs quickly -- Channel's reversal from IVs isn't performant).
2025-01-31 21:08:27 -06:00
Kurt
538374b33f FRLG: fix gift magikarp location check
The Route 4 (Pokémon Center) location (099) is only used for the Town Map tags; the gift yields Route 4 (104) only.

https://projectpokemon.org/home/forums/topic/66281-frlg-magikarp-salesman-oddity/
2025-01-29 23:10:26 -06:00
Kurt
46bb7e3d3b Add trainer ID checks for cxd/rs
Templates: Add more interfaces for trainer ID properties
Closes #4431
2025-01-29 22:34:45 -06:00
sora10pls
f3f89c1f4b Update BDSP Met4 to match SWSH/LA 2025-01-29 20:33:13 -05:00
Kurt
4fa83b905f Better clamp corrupt sav2 mail boxsize reads 2025-01-29 02:02:43 -06:00
sora10pls
4089b8197a Add date ranges for Shiny Manaphy/Enamorus events 2025-01-28 08:39:00 -05:00
Kurt
7d59e3416a Minor tweaks
== null
to
is null
2025-01-27 16:37:37 -06:00
Kurt
2592dfec19 Revise RSBox slot display order
Revises prepended box name to better indicate left/right
Closes #4429
2025-01-27 11:59:36 -06:00
Professor Dirty
f63ad4372e
Additional event flags CHS translation (#4427) 2025-01-26 11:28:50 -06:00
abcboy101
6c3eec3314
Support Gen5/3DS/Switch word filters (#4423)
* Support Gen5/3DS/Switch word filters

- Separate 3DS/Switch word filters
- Add/implement Gen5 word filter
- Adjust behavior of DisableWordFilterPastGen

* Implement halfwidth/fullwidth conversion

Only applies to alphanumeric and kana
2025-01-26 10:38:21 -06:00
Professor Dirty
b119302d67
Additional CHS translation (#4421) 2025-01-23 23:43:51 -06:00
9Bitdo
53195d0233
Add Pokémon Lucario & The Mystery of Mew Movie Gift KOR 아론's Lucario Date (#4422)
* Add Pokémon Lucario & The Mystery of Mew Movie Gift KOR 아론's Lucario Date
2025-01-23 23:43:13 -06:00
sora10pls
659e2ad099 Add latest distribution raid data 😡🎤🐧🤖 2025-01-23 19:03:37 -05:00
Kurt
0316404f46 Fix ageto antishiny recognition
Must have been omitted in the refactor

while we're here, revise the other gift3's to be records for the autogen'd ToString
2025-01-21 22:40:45 -06:00
Kurt
82d74e6950 Add TIDseed for emerald frame indicator 2025-01-18 22:40:31 -06:00
Kurt
5fbf867613 Show seed-frame-time for rsefrlg slot/static encs
Easy indication for painting-skips or changed-seeds.
2025-01-18 19:50:02 -06:00
sora10pls
c7881b5c4a GO: Permit Master Ball for Max Battles
When Max Battles first launched, the Master Ball was not available in the Poké Ball tray in the bonus challenge. This did not apply to the tutorial Wooloo or Skwovet/Wooloo from Special Research.

0.333.0, which released on September 27 2024, added a property named `canUseMasterBallPostBattle` for Max Battles. Players noted being able to use a Master Ball on Gigantamax Charizard when it debuted in the game, meaning this is the likely culprit.

Since all past Max Battles have other, less restrictive encounter methods where the Master Ball is permitted, this does not cause any potential invalid legality overlaps. Gigantamax Pokémon also can't be transferred, so they don't need to be accounted for.
2025-01-18 12:59:32 -05:00
santacrab2
74a52d946b
Encounter to PA8 generation tweak (#4419)
* use pa8's current level for determining Mastered flag for Move Shop Records to ensure all flags are checked on encounters who learn moves close to their level mins

* use met level instead of current level to reduce unnecessary calculations
2025-01-17 22:08:54 -06:00
Kurt
c1d2babc5c Set all purchased on MoveShop all 2025-01-17 22:08:38 -06:00
Kurt
2976480792 Minor tweaks
File.WriteAllBytes now accepts span on .NET 9 yay
2025-01-16 22:16:40 -06:00
Kurt
b9df2d65c6 Improve performance of adding sprite accents
Can get the native memory span via MemoryMarshal.CreateSpan; this skips allocating a 15KB buffer as well as copying 30KB for each mutation

am paranoid about mutating the original image, so that can stay for now
2025-01-16 21:34:53 -06:00
Kurt
0549cfda4a Rework SCBlock to Memory<byte> 2025-01-16 20:55:07 -06:00
sora10pls
2442165907 Add latest distribution raid/outbreak data 🌴🦩 2025-01-16 19:05:01 -05:00
Kurt
eced651024 Pokewalker: Handle 539 stroll, revise signatures
Was missing the worst case scenario; ran a bruteforce script to find a 539 after adding the final logic. We try to do the least amount of cpu instructions so this is probably the better arrangement anyway.

Revise the method signatures to exclude the scratchspace span reuse (not-obvious behavior). Just stackalloc a new span.

Add seed indication to legality formatting. #4416
2025-01-15 01:26:49 -06:00
Kurt
e9092ca702 Update MetaFilter.cs 2025-01-14 21:50:04 -06:00
Kurt
5230cba9f6 Misc tweak for alpha mark check 2025-01-13 23:47:57 -06:00
Kurt
9f0812cb8b Pokewalker: Don't check slots for stroll seeds
Closes #4416
Majority of the changelog here is additional/revised comments/xmldoc or "unused code". Only `GetFirstSeed`'s behavior has changed on line 154 (new). Since there is no longer the need to refer to Species and Course, remove from the method signatures & usages.

Refer to the discussion in the ^ mentioned issue.

Co-Authored-By: HappyLappy1 <86489014+happylappy1@users.noreply.github.com>
Co-Authored-By: NickPlayeZ <80699972+nickplayez@users.noreply.github.com>
2025-01-13 23:24:18 -06:00
Kurt
ef60ee622d Fix colo espeon pidiv double-check
forgot about the fakeID before IVs
https://projectpokemon.org/home/forums/topic/57375-pkhex-new-update-legality-errors-contribution-page/page/30/#findComment-293040
ty jacksonf !
2025-01-13 22:25:32 -06:00
Kurt
7d2c564744 Improve comment for lang restricted walker courses
Refer to Comment for context: https://github.com/kwsch/PKHeX/issues/4416#issuecomment-2588858294
2025-01-13 22:04:34 -06:00
Kurt
943223fe09 Handle snowy crossover deferrals better 2025-01-12 22:14:43 -06:00
Lusamine
09fce189d3 Filter uncatchable SV fixed encounters by AI action 2025-01-12 19:49:44 -06:00
Kurt
e99105cd78 Update hgss safari slot permutations
https://github.com/kwsch/PKHeX/issues/4416

for permutation code change, see dumper repo:
cdfafa8a31
2025-01-12 17:23:36 -06:00
Kurt
67156dd0df Misc tweaks
Fix gender filtering for gender-locked tera raids
Fix gender filtering for gen3 pcny
Make StringInstruction a record, expose Comparer in ctor
2025-01-12 11:22:30 -06:00
Kurt
b79551411b Fix batchmod compare for $rand 2025-01-11 23:20:14 -06:00
Kurt
26e06f80fb Revise minior handling
Fixed symbol isn't actually form random (previous commit reverted)
2025-01-11 17:51:04 -06:00
Kurt
9f60ff9eb7 Add settings to bypass hotkey requirement
Legality context menu requires holding control when opening the menu -- with this option enabled, don't need to do the hotkey.
2025-01-11 17:36:03 -06:00
Kurt
c17774f57c Revert "Handle fixed tera Minior from su2"
This reverts commit 20245dd512.
2025-01-11 17:35:53 -06:00
Kurt
20245dd512 Handle fixed tera Minior from su2
Thanks XD_Lele for bringing this up -- form-random handling wasn't added for this encounter type
https://projectpokemon.org/home/forums/topic/57375-pkhex-new-update-legality-errors-contribution-page/page/30/#findComment-293014
2025-01-10 22:08:39 -06:00
Kurt
20887a1b2d Gen4: allow battle use items to be held (oops)
Closes #4415
2025-01-10 12:27:37 -06:00
sora10pls
c44f6d286c Add latest distribution outbreak data 🐍 2025-01-09 19:55:13 -05:00
Professor Dirty
866f0269af
Append CHS translation (#4413) 2025-01-08 16:51:54 -06:00
Kurt
172ba0d119 Update ITrainerInfo.cs
Closes #4414
2025-01-08 16:48:44 -06:00
Kurt
46c808d118 Enhance fail-retry PID generation for raids
try with criteria -> try with critera-w/o-IVs, try without any criteria
2025-01-08 01:02:00 -06:00
Kurt
93c7b765c3 Minor tweaks
Handle ARNG egg manaphy for gen4 trainer, force hatch
add xmldoc/comments
expand expressions for version/language selection, disallow languages correctly
2025-01-08 00:36:02 -06:00
Kurt
c60efc7d14 Revise Manaphy PIDIV checks for antishiny+egg
Handles all permutations correctly
set correct version value for blank DP save (not valid saved ver, need to fall back to D)
2025-01-07 23:45:56 -06:00
Kurt
e1773e83b9 Allow B2W2 Funfest Stunky/Glameow from both games
https: //github.com/kwsch/PKHeX.EncounterSlotDumper/pull/2
Co-Authored-By: Parnassius <11491745+parnassius@users.noreply.github.com>
2025-01-06 21:36:15 -06:00
Kurt
ab215d0721
Enhance EncounterCriteria and template->pkm adherance (#4411)
Change the EncounterCriteria object to a readonly record struct, and introduce extra properties to indicate how flexible we are with random results.
2025-01-04 22:45:30 -06:00
Kurt
37e4b06a9e Misc tweaks
Make nick/trainer'd encounters use RoM instead of string[] to truly make them readonly records, and skip 1 dereference on access
add some xmldoc
fix open file suggesting main from another folder
2025-01-04 21:43:55 -06:00
Kurt
c1f0c9e7ae Update dependencies
for .NET 9.0 there's a few
2025-01-04 01:25:14 -06:00
Kurt
b94ab9acb4 Minor clean
No functional change
2025-01-04 01:24:29 -06:00
Kurt
520c849287 Fix Gen1 party+clear
Closes #4410

Also fix trash bytes persistence (only overwrite nickname with default if it visually differs from what is present in the textbox).
Add trash edits to Gen3 HoF
2025-01-03 21:33:22 -06:00
Kurt
674a778bf9 Fix form centering to non-primary screen
The first screen is configured with origin coordinates 0,0; any other screens that are arranged to the left or below will have coordinates in the negative. Negative coordinates are valid; don't sanitize.
2025-01-03 13:35:54 -06:00
Kurt
a1a15fe5b2 Fix opening Gen3 HoF editor
Closes #4409
(icon reference needed to point to the static resource)

Add SAV3 property to check for misconfigured (small) sizes, to disallow editing extdata blocks that don't exist (no exception thrown for the small saves, can't open the GUI)
Rearrange GUI, handle exception when copypasting bad PID and saving, add sprite, add Clear button instead of auto-clearing on species:0, add shiny checkbox (readonly)
2025-01-03 13:32:24 -06:00
Kurt
fdcd71a3b6 Minor clean
Enhance the API for SV's item pouch/items, if anyone bothers to use it
2025-01-02 20:59:37 -06:00
Kurt
d7ecbfe667 Move PLA verifier into MiscVerifier
Only checks PLA stats in PA8 format; no need to type check this in the main method. Might be a good idea to extract format-specific verifiers into classes.

Extract size checks to top level method. A wasted type check isn't too bad compared to all the duplicate logic.
2025-01-02 20:33:15 -06:00
Kurt
9ef46b5f8c Correctly filter dummied moves in BDSP and SV
Less noise in the user drop-downs for moves that aren't available in the game. Unlike SW/SH that has all moves from the past, there's no need to confuse the user with impossible options (unless they turn on HaX, which is unchanged).

Add performant path for dummied move list fetch for Gen8a+
Skip duplicate work for Relearn list fetch (always same as Moves, except for Gen7 relearn)
2025-01-02 20:29:58 -06:00
Kurt
abc1e785d8 Remove fullness/enjoyment from PKM, use interface
No need to carry this baggage further since SV ditched it. PLA and BDSP only have it because there's a gap in the structure that *we assume* is unused (rather than reserved), and is worth checking for legality (unused bytes shouldn't be used).
2025-01-02 20:19:29 -06:00
Kurt
30fdfb29e7 Add context to TrashByte editor
rather than just Generation, context is also important to help select glyphs for different games (Gen7b differing from Gen7?)
2025-01-02 20:17:17 -06:00
Kurt
cb967ef283 Minor adjustments
Inline some gameversion calls, replace `is IAlpha` with immutable interface instead
Add interface to get the mastery object for a given a learnset source & species-form (rather than calling it statically)
Shorten the BallUseLegality expression
Defer ec%100 context to PKX (easy to miss in a future update if any others added down the road)
Rewrite Alpha mark check logic to require entry in to HOME (tracker present) as that's the method of obtaining the mark
Make SaveUtil not allocate, and let the compiler optimize size checks.
2025-01-02 20:15:53 -06:00
Kurt
a6beb0293c Don't bother passing evos for hypertrain check
if it exists in PLA, then it can exist in SWSH/BDSP/SV (as of latest DLC!). Only need to check if it has visited (tracker present).
2025-01-02 20:04:04 -06:00
Kurt
8ec1c8cc81 Extract some magic numbers
If Legends: ZA adds more megas (likely), would have to refactor this monster. Rename the mega check, as SV doesn't have megas, but if ZA is Gen9(a?) we need to segregate somehow.
2025-01-02 19:57:38 -06:00
Kurt
92d49e858f Extract SV Runtime Language get/set
Useful for get to check if things are misaligned in a save file. If future games continue doing this, then we can reuse the enum.
2025-01-02 19:54:46 -06:00
Kurt
a41f32c66b Hide PKX.Personal behind methods
No need to have the entire master personal table visible within the assembly when it is only used for gender fetching. Add 2 methods that return the info that the previous consumers were wanting from the previous logic.
2025-01-02 19:53:29 -06:00
Kurt
88c7256841 Extract common technical record applications
Get a neat API for it to provide an "option" of what all you want set.
Ends up deduplicating some logic in batch editor as well.
2025-01-02 19:51:03 -06:00
Kurt
f77899ab73 Extract entity extension list fetch
No need to have a static member in PKM for something that is only used outside of PKM.cs (save files, GUI, aka file naming)
2025-01-02 19:48:56 -06:00
Kurt
243c4102dc Reduce some allocations in SpeciesName
implement the net9 shortcut for species name lookup from span
don't fetch japanese/spanish names twice or build dictionaries for them
2024-12-31 13:15:02 -06:00
Kurt
e2086d2a0d Misc tweaks
Allow pkm batch editor to take readonlyspan property names
concrete types over `default` for clarity
encounterverifier: use const values for egg levels for clarity
batchediting: fetch all properties only once
etrade4: reduce object size/init by having Contest as a property
2024-12-31 12:53:51 -06:00
Professor Dirty
e4f63848ad
Add new translations to CHS and remove obsolete paragraphs (#4406) 2024-12-24 10:58:40 -08:00
Kurt
83df635e04 Add more DPPt underground stats, update names
Closes #4405
2024-12-23 19:31:01 -08:00
sora10pls
e6a8df366d Add latest distribution raid/outbreak data 🐉 2024-12-19 19:03:51 -05:00
Kurt
341106d48f Gift leniency: wish m2>1/4 & frlg togepi m1>4
Closes #4404
Future improvements can add value ranges to restrict based on PIDs (e.g. M1 Wish must be below X, M4 must be above Y, or if it's a range, likewise. Or, if it's a certain %24 result for shuffling considerations).

Co-Authored-By: notblisy <50887637+notblisy@users.noreply.github.com>
2024-12-18 18:35:02 -06:00
R-YaTian
c198ab08e0
Gen 6: Fix Pokemon Link data saving, enable modify BP and PM (#4401)
* Gen6: Fix Pokemon Link data saving, enable modify BP and PM
2024-12-16 20:35:48 -06:00
Pasquale Nardiello
8e42776610
Fixed HallFame3 logic and added GUI for manipulation (#4395)
* Fixed HallFame3 logic and added GUI for manipulation
2024-12-16 20:28:42 -06:00
Pasquale Nardiello
a75e09780c
Added Initial edito for Secret Bases in RSE (#4386)
* Started making Secret base 3 editor

* Completed Initial RSE secret bases manipulation.
2024-12-16 20:23:50 -06:00
Kurt
872c79f124 Add gen1 hall of fame editor
Closes #4403

Co-Authored-By: ShadowMario3 <36941677+ShadowMario3@users.noreply.github.com>
2024-12-16 19:37:58 -06:00
Kurt
013cf4cf93 Fix cloning of gen1-3 saves in subeditors
Clone'd saves no longer retain same byte[] refs as original

evident in GUI editors and canceled changes somehow being saved
(gen3 isn't required, because it's unpacked into the blocks, but just be consistent)
2024-12-16 19:37:03 -06:00
sora10pls
e83ae026d6 Add latest distribution raid/outbreak data 😡🐵♟️🦒 2024-12-12 19:04:20 -05:00
Kurt
c6307b40ef Misc tweaks
SAV3: extract struct slice fetch
Metadata: allow setting `Memory<byte>` instead of `byte[]`
Stats: deduplicate gen1/2 mask set
remove some unnecessary spaces (lol)
2024-12-09 01:02:14 -06:00
Kurt
6397cf7723 Revise Gen7b FlagCount
Closes #4396

Co-Authored-By: Fábio H. Attard <fattard@users.noreply.github.com>
2024-12-07 08:20:03 -06:00
Professor Dirty
933c9d03cd
Added CHS translation and some corrections to the detection prompts (#4402) 2024-12-06 12:14:34 -06:00
Ka-n00b
0a27880ce6
Update Event Flags (#4398) 2024-12-06 12:13:59 -06:00
sora10pls
a2c03fc0a9 Add latest distribution raid/outbreak data 🐰🔵🟤 2024-12-05 19:04:03 -05:00
sora10pls
ff452bf5a6 Add latest distribution raid/outbreak data 🐦‍⬛🐸🥚 2024-11-28 19:15:36 -05:00
Professor Dirty
7fdd5abc52
Added CHS translation and corrected typos (#4393) 2024-11-27 17:03:06 -05:00
9Bitdo
a4b1da542c
Add Korea(KOR) Operation Get Mythical Keldeo, Zarude & Deoxys's date (#4394) 2024-11-27 17:02:35 -05:00
NimbusFox
f2dfeb20af
Fixing StatusCondition to report sleep correctly (#4397) 2024-11-27 16:45:56 -05:00
9Bitdo
a60e76f16d
Add Operation Get Mythical Keldeo, Zarude & Deoxys's date (#4392) 2024-11-21 21:43:17 -06:00
Kurt
231b3ffe57 IEncounterable: Add down-level interface 2024-11-18 21:58:09 -06:00
Kurt
8c37d9b4b5 Slot3/4: Filter met level on generate 2024-11-18 21:19:53 -06:00
Kurt
c30a335404 Update EncounterCriteria.cs 2024-11-18 20:46:50 -06:00
Kurt
594d133fae Update WA8.cs 2024-11-18 20:40:03 -06:00
Kurt
0b686d428b Change EncounterCriteria to use Level Range 2024-11-18 20:33:33 -06:00
Kurt
52351bf0e7 Generator: Gen3/4 slots w/ IVs & not-min level
Previously would only generate with minimum levels when IVs are provided
Now that it generates not-min levels, need to set the correct level.

Might be possible in the future to have IV-spec also respect a Level request, but for now it's only forceMin which nobody currently uses (no usage of the setter).
2024-11-18 18:56:18 -06:00
Kurt
5c338f5f74 Misc tweaks
Extract better signatures for BallApplicator.ApplyBallLegalRandom
Slot1: swap interface order (consistency with other classes, no impact)
IEncounterTemplate vs IEncounterable interface usage
2024-11-18 14:12:57 -06:00
Kurt
61526fad8d Add missing IV interface tag to trades 2024-11-17 21:15:07 -06:00
Kurt
4af95f63b3 Update BallApplicator.cs 2024-11-17 20:59:54 -06:00
Kurt
1a5d06d0d0 Update BallVerifier.cs 2024-11-17 18:52:42 -06:00
Kurt
076bbbbd77 Update BallApplicator.cs 2024-11-17 15:12:09 -06:00
Kurt
ceb669c112
Update to .NET 9, c# 13 (#4390) 2024-11-17 13:13:58 -06:00
Kurt
adb67922c5 Set dex skin active flag on import 2024-11-17 12:47:38 -06:00
Kurt
8b071073d8 Improve BallApplicator performance
Previous logic would check a new LegalityAnalysis for each ball attempted, and would additionally allocate a new PKM for testing the balls.

With the refactoring to BallVerifier, we can just pass in the template and ball and get a truth, skipping the entire re-check. There might be a deficiency when the current ball invalidates the encounter (GO) but I don't think that's worth considering at this time.
2024-11-17 12:08:22 -06:00
Kurt
b5e3e17987 Update BallVerifier.cs
No longer need to assign ball, can be used to check if ball can be assigned.
2024-11-17 08:59:48 -06:00
Kurt
89e705c6ae Extract ball verification result 2024-11-15 23:39:33 -06:00
Kurt
55b75e8061 Merge branch 'master' of https://github.com/kwsch/PKHeX 2024-11-15 23:07:11 -06:00
9Bitdo
46739891e7
Add Patrick's Pelipper date (#4389) 2024-11-15 10:25:05 -06:00
sora10pls
d028cb0aed Add latest distribution raid data 🐢🌲 2024-11-14 19:03:08 -05:00
Lusamine
1959eb34a5 Further refine memory 29 restrictions in SWSH 2024-11-12 21:10:21 -06:00
Lusamine
bf0e0e2a4b Further refine memory 29 restrictions in SWSH 2024-11-12 18:43:15 -06:00
Kurt
cccce007b8 Update EncounterSlot7GO.cs 2024-11-12 12:08:11 -06:00
Kurt
5955319883 Add expected PP check for stored-healed slots 2024-11-12 10:33:34 -06:00
Kurt
b60c6e5f14 Update 24.11.11 2024-11-11 23:25:02 -06:00
Cynthia Coan
f2a6abbb6c
validate nickname characters within gen4 (#4382)
pkmnclassic has recently had some pokemon traded that cause game
crashes when viewing the pokemon's information, or when trying to
remove the pokemon for these boxes. most of these pokemon were reported
legal by pkhex however. this fixes the biggest use of these we've seen
actively traded (some of the other checks require more validation, as
they seemed to be buggy, we intend to validate those & send more PRs if
needed).

this check effectively covers "NULL Bytes" within the trainers name,
or the pokemon's nickname. We have attached an example pk4 that was
traded through our service that exhibits this issue, a couple notes:

- Generation 5+ seem to not be affected and replace character names with
  '?'
- Not all screens crash inside of Generation 4, the big ones our users
  noticed were viewing the pokemons information, and removal from the
  boxes.
- I also got a crash in pokemon ranch, but my testing setup was pretty
  hacky, and I'm not confident it wasn't something else, but we know
  it's potentially possible.

- We check for the terminator character '\uffff' which the pkhex string
  converter inserts implicitly when encountering an invalid character,
  but the actual underlying character when performing a hex dump is
  `\0`.
2024-11-11 22:28:01 -06:00
Kurt
fced599119 Fix HGSS box saving (empty->not)
HGSS tracks each box if it is changed, to skip writing it on in-game save. PKHeX (and other editors) weren't updating these bitflags, so when empty boxes had Pokémon added and saved once in-game, the save file would corrupt as it was copying the checksum but not the box's data.

Saving twice in-game will cause the checksums to get updated even without the flags being set. I don't think we understand it fully, but this hacky workaround of noting all boxes as "changed" will cause the first-save to properly copy all contents of the boxes to the new primary save section. Rather than tracking which boxes we are reading/writing slots for, or fixing/comparing against a backup -- we need to account for API usage where people might write directly into the save (pcdata.bin/boxdata.bin too). Best to play it safe and let the game fix it.

Most wouldn't have noticed this issue as the box they're modifying is usually their most-recently-changed in-game box.

Closes #4368
2024-11-11 22:25:34 -06:00
Kurt
6153d6c851 Restrict gen3 PCNY OTs further
last 4 sets were only on the D memcard
add notes regarding Dragon runs using B memcard (staff)
https://pokemonhistorian.com/pcny-project/pcny-preservation-records/
2024-11-11 15:17:01 -06:00
Kurt
b0a2c7911e Fix Met tab startup binding
`ListControl._dataManager` is null until some event fires, which never happens until the Visible state is normally triggered by flipping to that tab.

The met tab Version/Location can skip Visibility changes on startup, resulting in never being initialized by the GUI framework, thus ignoring any calls to change SelectedValue. Really weird, but setting the BindingContext here forces it to create a `_dataManager` when setting a new `DataSource`.

Closes #4384
2024-11-11 15:11:47 -06:00
Kurt
5d4976303d Add manual handling for Mainland China gifts
Closes #4375
ty manu for clarifying transfer restrictions on discord
2024-11-11 10:05:17 -06:00
Kurt
52437d1712 Gen3 PCNY: remove OT gender checks
Same as PCJP, uses the recipient's save file gender. Not random.
2024-11-11 08:16:58 -06:00
Eelen
4414b34fe7
Update lang_zh.txt (#4387) 2024-11-11 00:00:34 -06:00
Kurt
8b09d9467d Misc fixes 2024-11-10 19:22:59 -06:00
Kurt
de57e197ad Fix gen3 reversal exploration for 50% of seeds
line 340 - variable reuse!

In looking at the emerald disassembly, when Pressure/Hustle/Vital Spirit fails, it reduces the max level of the slot by 1 so that the max level doesn't randomly appear. Essentially 50% flat for the max to appear, for varied slots, not 50% + (1/range). Thus we require a separate branch of logic to check for this scenario.

f8119bedd4/src/wild_encounter.c (L298)

DPPt (and assumedly HGSS) do not decrease the random range on failure.

ty TFS for bringing this to my attention on discord
2024-11-10 19:11:30 -06:00
Kurt
d1a51797c7 Fix encdb search pane scroll, pkmdb overlap 2024-11-10 18:08:58 -06:00
Kurt
947bbc7274 Fix Sirfetch'd deferral, add Tera deferral
Marill and Azurill need deferral cases as they change primary type
(normal-fairy => fairy || water-fairy => water)
2024-11-10 15:32:57 -06:00
Kurt
e4e3d929a0 Handle Gift method2 eggs better
Set PID correctly
Defer on fateful (wynaut egg clash, hot springs)
2024-11-10 15:16:19 -06:00
Kurt
c8726c4e2d Fix T2/Channel jirachi enc->pk3 generating 2024-11-10 15:00:31 -06:00
Kurt
1ffbd46e3c Extract some logic, deduplicate 2024-11-10 13:25:30 -06:00
Kurt
3c574a45f1 Revise enc1/2 -> pk1/2 nickname set
Skip the IsNicknamed evaluation, saving at least 1 string allocation on ctor. Remember the IsNicknamed state when we set false.
GUI: provide the selected language rather than recalculate
2024-11-10 09:32:33 -06:00
Kurt
51464fb4af Update PK9.cs 2024-11-09 22:34:33 -06:00
Kurt
d77d6b7ce6 Fix MethodJ enc->pkm nature check
silly differences; MethodJ should have been /A3E not %25
Make GetNature public, and have the MethodK esv modulo be uint instead of long for better clarity.
2024-11-09 21:37:03 -06:00
Pasquale Nardiello
38756b44c7
Added Lilycove museum manipulation (#4385)
* Added method to extract pokémon data from lilycove museum and experimental way to set paintings active and change their subject in order to obtain 2nd trainer star and crystal decoration
2024-11-09 18:58:23 -06:00
Squid
62b58f78e7
Labels for Gen 5 Trainer Records (plus renamed flag) (#4362) 2024-11-09 09:08:57 -06:00
Professor Dirty
4bad2e3cdf
Add CHS Event Flag translations (#4383) 2024-11-06 10:59:20 -06:00
sora10pls
50ff039c1b Adjust HOME Tandemaus gift date range
Like with Shiny Zeraora in the past, the only limitation is the distribution of the gift itself. Once added to your Gift Box, it can then be claimed at any time, giving it no end date.
2024-11-01 09:30:04 -04:00
9Bitdo
26fd23a338
Add PokéCenter Birthday Tandemaus date (#4379)
submitting the date based on

- hard copy serial code issued out at Pokemon center in japan on 1st Nov 2024 1000hr (JST)(operating hous : 1000hr), hence countries in the UTC-11 would be able to redeem it on 31th oct 2024 1400hr earliest

- Serial code expiry date is from 1st Nov 2024 - 31th Jan 2026 (JST) or 1st Feb 2026 (UTC+11 )
2024-11-01 09:28:15 -04:00
sora10pls
56bd4e6486 Add latest distribution raid data 🐊💥 2024-10-31 20:02:09 -04:00
santacrab2
2cd9632c4a
MailBox fix (#4377)
* Catches Out Of Bounds error on ListBox SelectedIndex for when the B_PartyDown_Click event is triggered and the current Selection is in LB_PCBOX.
2024-10-28 18:21:44 -05:00
sora10pls
e2d73eb866 Add latest distribution raid/outbreak data 👻🎃🍭 2024-10-27 20:05:52 -04:00
Easy World
ff24892ec2
Update and revise zh-Hans translations (#4376) 2024-10-27 00:09:11 -05:00
abcboy101
e7be55242d
Support SWSH gender/fashion item editing (#4374)
* Support SWSH gender/fashion item editing

* Update translations
2024-10-25 17:00:54 -05:00
BlackShark
693cb3a70b
Renamed DLC Editor button (#4372) 2024-10-24 11:14:25 -05:00
BlackShark
d2cc13da3d
Updated badwords.txt (#4373) 2024-10-24 11:14:01 -05:00
Lusamine
dfd23f18b3 Add more LGPE crossovers from Route 11 & Route 21
Route 11 -> Vermilion City
Route 21 -> Pallet Town
2024-10-24 00:19:27 -05:00
Lusamine
9adedf429e Remove inaccessible tree encounters from LA pkl
Trees of type "gimmick_tree10" don't have leaves and can't be activated
2024-10-22 11:58:19 -05:00
Lusamine
451fd0e0a0
Handle crossover encounters in LGPE (#4371)
* Handle crossovers in LGPE

Ty @sora10pls, @LegoFigure11, @Skadiv, and @PP-theSLAYER for research!
2024-10-22 08:13:41 -05:00
abcboy101
d19acdb6ec
Support G6/7 player gender editing (#4370) 2024-10-22 08:06:39 -05:00
Lusamine
34f0c715ef Correct Vermilion City typo in comments 2024-10-21 22:41:38 -05:00
sora10pls
ec32302a5c Add latest distribution outbreak data 🐒🐿️ 2024-10-17 20:05:26 -04:00
Kurt
48abd8d080 Revise WC8+ ribbon set logic
For making fake WC*'s, revise logic to use the ribbon span instead of the entire range of bytes.

Fix Partner Ribbon not showing up in Ribbon Editor affix list.

Closes #4369 (superset, I wanted to change the underlying usage)

Co-Authored-By: Manu <52102823+Manu098vm@users.noreply.github.com>
2024-10-16 18:44:54 -05:00
sora10pls
325649b19b HOME: Add Meloetta/Tandemaus legal date ranges 2024-10-16 18:42:01 -04:00
Kurt
bc94f7b9ec Update EncounterGift3.cs 2024-10-16 15:42:41 -05:00
Kurt
be9767f3e6 Add settings tab to pkmdb/encdb
Quicker toggling compared to main window settings changing
same object
2024-10-16 12:49:12 -05:00
Kurt
b068027ba8 Add 32bit FnvHash methods
With all the abstractions since SW/SH's release, the string hash method used by some SW/SH blocks is the 32bit method, not the 64bit one used by PLA/SV.

`FnvHash.HashFnv1a_32("ZukanData_Pokemon")` = `0x4716c404` as noted in the block accessor list.

Expose in the API as 32bit for documentation purposes. Good to know if we ever go back to bruteforce like is being done for SV file paths.
2024-10-16 00:15:30 -05:00
Kurt
b389cd3acd Rework Gen3 Event PIDIV detection & checks
Detect base algorithm (regular, regular-anti, anti, shiny) and check for sub-patterns that differ per encounter (such as OT gender).
Implement PIDIV generators within the Gift3* template, remove old branches
Add Table Weight detection (still need to test) for PCJP 5anniv
Add PCJP GCEA campaign 1-6 templates
Differentiate Mystry Mew seeds; disallow Mews that were deleted b4 cart cloning
Need to double check some gen3 events (esp hadou titans), but is 99% there

Thanks Sabresite, for your continued support
2024-10-16 00:01:10 -05:00
Lusamine
71c9c43eef Add alternate met location for LA static Unown O 2024-10-13 13:18:15 -05:00
Lusamine
a98e5137d3 Add remaining LGPE static gift event flags 2024-10-13 13:12:16 -05:00
Kurt
4f6ac58622 Add KeyData extdata, revise Link chunk set 2024-10-09 21:46:56 -05:00
Kurt
b9768c14b5 Update WB8.cs
Closes #4352
2024-10-08 21:43:44 -05:00
abcboy101
34c4d5c90f
Properly detect if G3 Mail is Japanese (#4363)
International author names are padded with spaces to at least 6 characters.
On display, it's treated as Japanese only if the name is 5 characters or less.
2024-10-07 13:27:26 -05:00
Kurt
53b9c27ec9 Add Battle Test metadata properties
Set the magic, set the flags to whatever, and refresh the checksum -> NPC recognizes you have DLC battle test ready and lets you challenge it immediately when starting a conversation with him.
Of course, it crashes immediately because we don't know what the 0x000 - 0x5C1 region data should be, but hey, this is progress for unlocking unreleased content.
2024-10-06 15:32:46 -05:00
Kurt
ec24b8b8d5 Enhance Gen5 download content I/O
Initially just C-Gear, can do better!
Adds i/o for Pokedex skins, Musicals, Battle Videos, Movies & PWT (B2W2), and a research-only tab for Battle Test (never distributed by GameFreak, so doesn't work).
2024-10-06 01:10:32 -05:00
Lusamine
dfac093e43 Add LGPE event flags for Porygon and Lapras 2024-10-06 00:15:29 -05:00
sora10pls
52c32292d2 Add latest distribution raid data 🪨🐒 2024-10-03 20:02:41 -04:00
santacrab2
efdc308bfa
resize pkm database Table Layout Panel to unhide search button. (#4364) 2024-10-03 16:14:05 -05:00
Kurt
1dc94152c3 Fix array reference
GC strings are 22 bytes long, but GBA strings are 10 & 7 bytes long. If a 7-char OT name is generated in C/XD and transferred, it would try iterating to the 8th char which is OOB for dest.
Iterate over the smaller array, which is always dest (in this method).
2024-10-03 14:44:22 -05:00
abcboy101
7632230edf
Support G2 mail in all languages (#4359)
* Support G2 mail in all languages

- Handle Japanese/Korean mail
- Handle both NPC and user-entered mail (#3470)
- Display European mail based on mail language (#4027)

* Fix shift

* Add support for Stad2 mail
2024-10-01 00:02:37 -05:00
Kurt
4aacffb99b Move Color15Bit 2024-10-01 00:00:34 -05:00
Kurt
e5c3c43bda EncounterCriteria: sbyte IVs
Narrow fields to reduce allocation < 16 bytes (maybe make this a struct?)
Add method to get combined IVs (gen3/4 format) for RNG purposes
use ^ method in RNG methods
Change GetExpectedLevel to be generic rather than interface, better codegen
Change Gender to enum for clarity

breaks ALM and potentially other plugins, just need to recompile if using IVs
2024-10-01 00:00:19 -05:00
Kurt
9479e8cb5f Better catch program init exceptions (plugins)
Discard plugins that fail to load, rather than aborting the entire plugin load operation
Add friendly message for unzipping fail (no PKHeX.Core.dll self-extracted).
2024-09-30 23:56:09 -05:00
Kurt
b187633ec6 Fix Misc7 wormhole shiny property r/w
Redirect reader to r/w the Misc block property rather than the buffer directly
2024-09-30 23:54:54 -05:00
Kurt
ee00a21f90 Minor clean 2024-09-30 23:54:03 -05:00
Kurt
a970f05a62 Too many tiles: return excess rather than clamped 2024-09-30 23:53:02 -05:00
Kurt
5b5418cd3f Update EncounterText.cs 2024-09-23 15:27:27 -05:00
Kurt
327585d1b4 Fix gender sanitization
g is `byte`, ternary returns `byte`, not `byte?`
be explicit
2024-09-23 08:21:02 -05:00
Kurt
128fcdff7a Loosen restriction on party set (quantity)
Empty array being set is a legitimate write; properly clears the party data.
2024-09-23 00:48:57 -05:00
Kurt
1c7bc9d455 Add settings to export/show record properties
For legality analysis exports to clipboard, and hovering slots in encounter db, can show all properties if desired. Was previously a debug mode only thing, but why not.
2024-09-23 00:47:43 -05:00
Lusamine
6317b7f552 Add latest distribution raid data 🍃🐍 2024-09-19 19:29:26 -05:00
Kurt
4594b0d879 Update default version fetch for Gift3 rsbox
Gen3 save file without trainer in database fetching a Gen3 gift -> Gen3/RS -> version fails to determine
just return the RS fallback; add EFL
2024-09-18 16:52:12 -05:00
Kurt
e5c4bef4b6 Add computed BaseEXP to SV personal info
51fe9491ec
2024-09-16 09:12:21 -05:00
sora10pls
1e86f80a66 GO: Add handling for Max Battles 2024-09-09 20:49:39 -04:00
Kurt
d6ca1ebee1 Add SafeTerminate string write option
Inconsistently used in Gen5 (clear, clearFF, clearSafe); if I ever get around to checking trash bytes in those formats it'll be necessary.
2024-09-05 23:58:48 -05:00
Kurt
617914b257 Allow some nidoran/volbeat to mismatch correlation
https://github.com/kwsch/PKHeX/issues/4356#issuecomment-2333031493
fix flow so it is called in format3+
Closes #4356 ty @abcboy101

add another check for EC=0 eggs from Gen4 -- game uses the PID value as the "is egg available" flag, so can't obtain a 0-pid egg from gen4!

remove python paths from gitignore, so I can call one of the test folders `Eggs`
2024-09-05 23:55:46 -05:00
sora10pls
95ffbca973 Add latest distribution raid data 🔥🐯🤼‍♂️ 2024-09-05 20:02:19 -04:00
Kurt
86d1e5ce15 Add nidoran/volbeat-illumise gen3/4 gender check
Closes #4356
adds check
revises egg->pk* logic to generate a valid pid for this case
2024-09-05 00:50:08 -05:00
abcboy101
3707ee5eb1
Standardize language codes and improve locale handling (#4353)
Use standard BCP 47 language codes
Move Culture utils into WinFormsUtil
Detect system language on first launch
2024-09-04 18:51:35 -05:00
abcboy101
42c1dee4e1
Document JP Colosseum Bonus Disc gift flags (#4350) 2024-08-28 19:04:44 -05:00
Kurt
0e4d30fd21
Rewrite C-Gear Skin conversion code (#4351)
#4337

On form open, sav->cgb->png
On file import, data->cgb->png
On image import, img->cgb->png
On form save, cgb->sav

Original data is retained when opening/importing file, rather than rebuilding png->cgb every time on form save.

Changes:
- can now adapt logic easily for pokedex skins
- importing image with too many colors/tiles will instead use color/tile 0 instead of throwing an exception, then will alert after importing.
- importing image with wrong dimensions/format will alert rather than display exception message
2024-08-28 19:04:25 -05:00
Kurt
ba7646e7a2 Fix TopMost appearance for Group Slots viewer
SAV editor gui:
Used by stadium to show registered teams, the form would misbehave when showing hover previews if you change focus between main window and the popup form.
Copy the `form.Owner=ParentForm` behavior from the popup box viewer, which behaves correctly.
2024-08-25 10:16:38 -05:00
Egzon
a75bb2c758
Spanish translations (#4348)
* Translated and revised constants and flags from gen 5 to gen 8 to Spanish

* Translated and revised some of the gen 3 and gen 4 constants and flags to Spanish

* Translated and revised the program's text strings to Spanish
2024-08-23 17:34:10 -05:00
sora10pls
984db07397 Add latest distribution raid/outbreak data 🐲🐌 2024-08-22 20:05:07 -04:00
Lusamine
19882f1c6c Add two additional LA Enamorus met locations
Easy enough to catch it in Bolderoll Slope by throwing a ball and
running that way, and there are multiple easier ways to get the generic
Crimson Mirelands location

Closes #4343
2024-08-21 01:02:49 -05:00
Kurt
76302d0803 Reorder IV bits
Closes #4346
make IV properties public for either/or usage.
2024-08-19 21:45:18 -05:00
Kurt
04466ac209 Extract some AffixedRibbon logic
const now reused across entire sln
pkm editor GUI no longer crashes on hover of out of range affixed ribbon
pkm editor GUI now indicates the quantity of available affixed ribbons on hover (does not indicate if 1 and already affixed).
2024-08-19 21:11:08 -05:00
Kurt
27b552db13 Retain existing shiny flags on complete dex
Closes #4344
2024-08-17 23:04:37 -05:00
Kurt
9d06a2bc2d Add GameVersion.EFL to learn source switch
https://projectpokemon.org/home/forums/topic/57375-pkhex-new-update-legality-errors-contribution-page/?do=findComment&comment=291016
Dumb logic but it works.
2024-08-17 22:08:28 -05:00
sora10pls
8fdcb634f0 GO: Add handling for WCS 2024 timed research encs
f2296ccedb update pickles because Necrozma encounter types had to be incremented by one
2024-08-17 13:58:04 -04:00
Kurt
8dae16102d Fix pokerus to allow evolved untransferrables
PLA sneasel -> transfer, catch virus -> PLA, evolve => was flagged. Need to check the encounter species-form, not the current species-form.
List all the species-form that are of consideration.
Wyrdeer, Kleavor, Ursaluna, Sneasler, Overqwil, Basculegion
2024-08-17 08:23:33 -05:00
Kurt
c524c37013 Misc tweaks
Remove GameVersion from IShadow3, lessening usage of fake gameversion IDs
fix sav3 with absurdly high record values (int.MaxValue -> uint.MaxValue)
2024-08-16 21:19:08 -05:00
9Bitdo
4330106d7d
Add WCS 2024 Steenee & トモヤ Sylveon date (#4342) 2024-08-16 20:29:50 -05:00
Kurt
a02e4db098 Update SAV_Misc5.cs
Fixes comboboxes from showing empty initial selections
2024-08-08 20:23:03 -05:00
Kurt
ad17f5f231 Fix Gen4/5 ribboncount get
oops offset
2024-08-08 20:22:38 -05:00
sora10pls
87762fe7f1 Add latest distribution outbreak data 🏆 2024-08-08 20:05:07 -04:00
Hernán Indíbil de la Cruz Calvo
68eb8a58d5
Added more Battle Subway fields (#4334)
* Added current set fields BW(2) subway

* Added battle subway data

* Fixed checking wrong chkbox

* Added NPC met flag
2024-08-07 18:56:39 -05:00
Kurt
97aa9805a9 Add GS Ball manual enable button
Adds a "misc" editor for Gen2 saves (only crystal for now), with a single button to enable the event (button enabled if the event is not yet enabled, lol)

746a06f1de/engine/menus/save.asm (L164-L175)

Closes #4335
2024-08-07 18:55:19 -05:00
Kurt
c9f3894076 Remove nature from FRLG Unown method loop
Only checks for Form.
c6a2f50491/src/wild_encounter.c (L238)

Closes #4332

make the private FrameCheckDetails<T> actually <T> instead of specific, to match MethodJ/K.
2024-08-07 17:03:27 -05:00
WonderSquid
1b3544ce78
Records.cs (#4330)
Fixed gen 6 index (inserted Cosmic Flips + renumbered)
2024-07-27 17:43:05 +02:00
Kurt
8578912360 Update 24.07.27 2024-07-26 15:38:03 -05:00
9Bitdo
f501c8a6b1
Add Roy's Fuecoco date (#4329)
Add Roy's Fuecoco date
2024-07-26 15:22:52 -05:00
sora10pls
fcbf521767 Add latest distribution raid/outbreak data 🐟🍣 2024-07-25 20:03:38 -04:00
Kurt
a84f440746 Update EvolutionGroupHOME.cs 2024-07-23 20:33:21 -05:00
Kurt
bf1dcf0737 Clamp level range to slot for 3/4=>5+ transfers 2024-07-22 16:09:37 -05:00
Kurt
ea4ff5714c Add targeted generator methods for CXD pidiv
If IVs are specified, will try to search for all seeds that originate them.
Same as was done for Method H/J/K of mainline games, this allows sets to be more-quickly generated if the criteria is provided.
2024-07-21 21:13:53 -05:00
Kurt
f35a8d8496 cxd blank sav: set default region for encoding 2024-07-21 01:50:08 -05:00
Kurt
d9504cd947 Add missing ribbon cases
Relevant for a new wc9 that has one of these marks...
2024-07-21 01:49:46 -05:00
9Bitdo
5702955723
Add Japan's Pokéss Summer Festival Eevee date (#4326) 2024-07-19 18:14:00 -05:00
sora10pls
6930afd2bd Add handling for GO Fest Necrozma encounters 2024-07-15 07:33:00 -04:00
Kurt
36d5e771b7 Update BoxLayout6.cs
Closes #4322
2024-07-14 17:17:10 -05:00
Ibis Liven
fc509200b7
Update SaveHandlerFooterRTC.cs (#4321)
add edge case for FlashGBX
2024-07-14 10:25:33 -05:00
Kurt
2205332326 Update EncounterSlot8b.cs 2024-07-13 16:20:56 -05:00
Kurt
3a131f61aa Add duking glyph remap check for Colo Plusle
Only applies to Spanish; other languages don't use special accent-mark chars.
make logic flow easier to understand for this if-duking case.
2024-07-13 10:34:55 -05:00
sora10pls
50930b0cc5 Add latest distribution raid/outbreak data 🐭🏄‍♂️ 2024-07-11 20:07:47 -04:00
902PM
e38481f7b0 localization (#4314)
* Update text_rsefrlg_00000_ja.txt

* Update text_gsc_00000_ja.txt

* Update flags_e_ja.txt

* Update flags_rs_ja.txt
2024-07-09 22:04:09 -05:00
Kurt
6e880f8e14 Add Emerald|FireRed|LeafGreen version group
Add new enum value to end to not break plugins using lumped values.

I think GameVersion needs to be reworked to remove all lumped values, and to instead implement a per-format enum (joy...)
2024-07-09 22:04:08 -05:00
Kurt
6568940d0b Rename WC3 to EncounterGift3, add PCNY
Adds PCJP class for whenever I get around to encoding that.
2024-07-07 01:07:02 -05:00
Kurt
298a0e6291 Allow HGSS hatch location Global Terminal
https://projectpokemon.org/home/forums/topic/65420-hgss-encounter-met-location-error-global-terminal/

only allow cross-version hatch locations if the egg location indicates it was traded
2024-07-06 12:15:44 -05:00
Kurt
27407aae60 Update XK3.cs 2024-07-06 02:45:14 -05:00
Kurt
e9a3192a95 Decouple WC3 enc from Mystery Gift, add jpn events 2024-07-06 02:36:33 -05:00
Kurt
52e22086e9 Extract IMetLevel for level!=met encounters
No longer marks Ranch gifts as Fishy for leveled @ threshold.
Encounter range check now ignores the MetLevel if it can't exist that low.
2024-07-06 01:40:12 -05:00
Kurt
99ebc47866 Extract TrainerName const to static class
Central location for where these values are defined.
2024-07-06 01:35:50 -05:00
Kurt
1dcda6d2e8 Fix sav clone metadata inclusion
Need to not include it when cloning saves into the sav constructor, need to mirror it in metadata instead.
2024-07-04 22:27:53 -05:00
Kurt
21271c2931 Fix jp oras work script filename 2024-07-04 18:43:13 -05:00
Kurt
3f49a01ae8 Sanity check save file personal info
https://projectpokemon.org/home/forums/topic/65409-2-exception-logs-found-on-latest-pkhex/?do=findComment&comment=290422
2024-07-04 18:42:27 -05:00
Kurt
66e8bf2645 Update 24.07.03
Update wc9 pkl
update dependencies (qrcoder got some perf improvements)
deduplicate HOME tracker message in SV by consolidating the methods
2024-07-02 23:51:10 -05:00
Arley Pádua
6de68ac626
feat: allow external consumers to specify AES implementation (#4311)
Allow external consumers to specify AES/MD5 implementation

HOME: Replace direct usage of transforms with built-in wrapper methods for easier API replacement.
BDSP: Replace incremental hash with one-liner for easier API replacement. Handle dirtying manually.
2024-07-02 09:12:03 -05:00
Kurt
298c83bc09 Misc tweaks
Fix dexnav move being allowed when relearn cleared and not learnable in dest game (duskull destiny bond gen9)
Move farfetch'd check to top of not-nicknamed, add deferral for GO match
2024-06-30 19:14:21 -05:00
Kurt
11b1eeb6c8 Allow HOME wrong apostrophe on farfetch'd
Stupid.
Move ash greninja check up with the CanHaveAnyLanguage as it's rectified in Gen8+.
2024-06-30 00:07:27 -05:00
Kurt
3824c2e9f7 SAV3: interact with other extdata sectors
Uninitialized sectors are now no longer updated for checksums
Checksums will be updated if not uninitialized (replacing battle video)
Not that the game really checks the checksum (lol)
2024-06-29 23:59:11 -05:00
Kurt
baac00c130 Invert export flags, fix footer inclusion gen1-3
Closes #4307
2024-06-29 12:55:13 -05:00
Kurt
dc5f1a0b17 Allow nicknaming ot-receive WC7; also WC6-link
Closes #4308
2024-06-29 11:38:30 -05:00
Kurt
37ea71eaa7 Fix pk4->rk4 convert copy 2024-06-29 11:21:37 -05:00
Kurt
00ce859584 Split pgf and wc5full
Importing had a bug where it would retain the metadata when setting to save file, rather than trimming down to the gift.
Split them into separate classes like gen6+

Remove erroneous WB7 size; 0x108 is only valid for a subsection, and won't work with the class since it needs to pull from the localization data. Nobody should be affected by that change.
2024-06-29 11:20:09 -05:00
Kurt
73c5e258b7 Move Breeding.CanGameGenerateEggs to generator
Already have a reference to it, no need to re-pivot off version.
Remove old method for checking if a Daycare exists; not all daycares give eggs, and not all egg-producing games have daycares.
2024-06-29 10:58:30 -05:00
Kurt
07a08fa328 Add some xmldoc 2024-06-29 10:55:01 -05:00
Kurt
8c21541305 Add missing SV->Gen8 transfer tracker check
SWSH format SV origin mon would report Gen9 but skipped the tracker check, no more.
2024-06-29 10:42:19 -05:00
Kurt
a84e59c0fe Use ReadOnlySpan for nest location fetch
skips allocating that big byte[][] entirely
No more static byte[][] references in PKHeX.Core !
2024-06-29 10:41:11 -05:00
Kurt
496c66bd46 Rename box storage slot flag fetch
`IsSlotX` is a little ambiguous; `IsBoxSlotX` much better.
2024-06-29 10:39:59 -05:00
sora10pls
bcb4e8374b Add latest distribution raid data 🦎🐲💞 2024-06-27 20:02:35 -04:00
sora10pls
d1416baf6c Revise SWSH dummied moves bitflag array
Prior implementation only accounted for moves with the description: "This move can't be used. It's recommended that this move is forgotten. Once forgotten, this move can't be remembered."

New implementation now checks for the CanUseMove flag, as is done with BDSP/LA/SV. This catches some oddities, like Kinesis becoming usable in Ver. 1.2.0 (Isle of Armor, Kadabra/Alakazam), as well as some signature moves for unavailable Pokémon, like Judgment and Seed Flare.
2024-06-24 12:19:23 -04:00
BlackShark
9ca0a46715
Fixed some SecretBase3 issues (#4305)
* Fixed some SecretBase3 issues

* Allow settings SpriteItems, use ushort for TID16
2024-06-21 21:27:33 -05:00
Eelen
2ab07201bd
Update CHS Translations (#4301)
Co-authored-by: Professor Dirty <103500840+wubinwww@users.noreply.github.com>
2024-06-15 21:31:59 -05:00
Kurt
6b51250f88 Misc tweaks
PK5-PKMEditor no longer sets Hidden Ability flag for Rotom under HaX -- only set if unique hidden ability.
Differentiate move source tagging for HOME, DexNav, and GB-Evolution
fix some comma spacing
2024-06-15 18:38:05 -05:00
Kurt
10f7ef9d3e Add missing fallthru for HT Language on no memory
Needed to catch the EncounterStatic9 gifts (like Starter).
Update es9->pk9 to use the property instead of duplicating that logic
2024-06-15 01:13:51 -05:00
Kurt
f6bcc8a216 Minor clean
StringBuilder.Append has some fancy interpolation handling in the latest NET8 so it's more clean than multiple append calls.
2024-06-15 00:57:44 -05:00
Kurt
0ffb256052 Add slot source legality checks
Useful for save files with misplaced data (you really have to be using the program weirdly to get these flagged).
Stuff like Eggs deposited in Daycare, non-fuseable species in the Fused slots, etc.

Allow HaX to view any slot, & Delete if Set is allowed
2024-06-15 00:14:49 -05:00
Kurt
0499caa52e Update BK4.cs
Closes #4300
2024-06-14 09:10:08 -05:00
Kurt
b68fb27ccf Update StringConverter5.cs
Closes #4299
2024-06-14 00:06:40 -05:00
Kurt
f2ca406240 Don't set time of day for gift2->pk2 2024-06-13 22:04:54 -05:00
sora10pls
fff4e8a6e6 Add latest distribution raid data 🐗🔜🥓🤤 2024-06-13 20:04:01 -04:00
abcboy101
9817b29483
Add Berry Powder, Pokéblock3, Decoration3, and Record4 editors (#4298)
* Add Berry Powder to Misc3 editor

* Add Pokéblock Case editor for Gen3

* Pokéblock/Poffin localization

* Add Decoration editor for Gen3

* Localization

* Add Record editor for Gen4
2024-06-13 00:01:49 -05:00
Kurt
be79360778 Flag slotnumber instead of Tile
Oops
pk8: check for SV for met location loss
2024-06-11 08:25:20 -05:00
Kurt
4f23efd939 Minor tweaks
Rival_Trash -> RivalTrash
BinaryCodedDecimal better method names
Gift dupe checker differentiate by species
2024-06-10 21:43:27 -05:00
Kurt
992deea183 Fix B2W2 key unlocked get/set
Closes #4295
2024-06-09 08:54:02 -05:00
Kurt
67fc0d0885 Misc tweaks
Add Cap Pikachu const value updating for USUM, also set Magearna const for USUM (not only SM)
Remove `NotDistributed` property from WC3 as only CXD events used it, no longer WC3 type.
Delete ArrayUtil; move remaining methods into the StorageUtil class that used them for compressing/inserting bulk into boxes.
Add shiny wormhole flag
Extract byte array strings into spans, faster tests and less overhead.
2024-06-09 08:52:32 -05:00
Kurt
84d42807e0 Simplify discards on gui event methods 2024-06-08 01:34:32 -05:00
Kurt
06b8ec03ae Allow startup to detect memory card single-sav
Closes #4296
2024-06-07 09:42:52 -05:00
9Bitdo
2c687e48fb
Add Nils's Porygon2 date (#4297) 2024-06-07 09:40:39 -05:00
sora10pls
2f9ab3cee0 Add latest distribution outbreak data 🦸‍♂️🌊 2024-06-06 20:07:27 -04:00
Lusamine
d065c7d0d0 Clean up "Gift" from SV MG date comments
The Project Snorlax card comment used "Gift" because using "Snorlax"
twice sounds strange, and then that kept getting propagated to cards
after it. Brings comments back in line to before.
2024-06-06 10:09:19 -05:00
9Bitdo
676819ce4c
add sophia's gyarados.wc9 to mgdb and update Kaito Arii's Talonflame comment (#4294) 2024-06-06 08:23:27 -05:00
Kurt
114ba4c991 Handle BDSP eggs not having any trash 2024-06-05 23:53:01 -05:00
Kurt
de6c1baaf2 Minor tweaks
PK9: Fix writing box bin of self-owned SV eggs clearing nickname trash, also clear any hint of trading completely
SAV1: ensure current box is index:0 if uninitialized
2024-06-05 23:46:03 -05:00
Easy World
d5e5eec082
Update translation (#4291) 2024-06-05 08:24:04 -05:00
Kurt
637a0720a4 Extract manifest reader
Expose string cache dictionary for direct use
Allow plugins to reuse the EmbeddedResourceCache class for their own assembly's resources
2024-06-05 00:50:18 -05:00
Kurt
0bbba81fca Update 24.06.03
Stream translation text when loading. No need to allocate a few thousand strings and an array.
2024-06-03 23:32:45 -05:00
902PM
3ccdba4048
Changes (#4287)
1.Unified with in-game text.(text_cxd_00000_ja.txt)
2.Add Poké Seer's text for clarity.(text_gsc_00000_ja.txt)
3.Fixed some text.(text_rsefrlg_00000_ja.txt)
2024-06-03 19:38:48 -05:00
Kurt
093fca37ec Add enum to GUI translatable list
Improve perf of GUI scrape by only making forms once, don't fully set up Main
Add method to get a list of translated Enum names
Adds translatability for size markings
Closes #4266
2024-06-03 19:37:57 -05:00
Kurt
99a514b0bc Update translatables 2024-06-02 23:19:33 -05:00
Kurt
2d0d0236e3 Handle more RTC footer types
BizHawk Gambatte stores a 0x16 byte footer, but the correlation previously used isn't always true.
Just implement a generic detector that allows for a reasonably sized footer for the Gen1-3 formats.
2024-06-02 12:22:27 -05:00
abcboy101
5014b0c891
Add country/region editor to Geonet editor (#4286)
Adds individual country/region-level editing to the Geonet/Unity Tower editors
2024-06-01 17:56:20 -05:00
9Bitdo
acd2d49cdc
Add Kaito Arii's Talonflame date (#4285)
* Add Kaito Arii's Talonflame Date
2024-06-01 17:44:55 -05:00
Kurt
7d05e12c7f Misc tweaks
Fix pk6/7 pkmeditor export, now retaining status condition instead of wiping it
Abstract b2w2 key system to a save block; better documentation on its odd mechanics
Allow gen1-3 filename language/ver detect to work if the filename is ` `->`_` (discord attachments changing spaces to underscores); also revise canadian French emerald filename pattern
others: negligible perf/using standard library functions
2024-06-01 17:44:32 -05:00
abcboy101
e6cf61330a
Add Seal, Accessory, and Backdrop editors (#4284)
- Adds editors for the Seal, Accessory, and Backdrop inventories in Gen4
- Adds a line in the Gen4 Mystery Gift editor to display which Goods, Seal, Accessory, Backdrop, Pokétch app, or Route Map is contained in a gift
- Changes the HG/SS Apricorn editor to display localized item names instead of hard-coded English strings
2024-05-30 23:42:03 -05:00
sora10pls
8935b95eb4 Add latest distribution raid data ☠️ 2024-05-30 20:02:34 -04:00
Kurt
650f23ab82 Misc tweaks
Rewrites super training editor to not need reflection and special handling for translation
Fixes trash check for traded egg
Fixes Scientist trash applying to Ultra Beasts -- much better comparison
2024-05-30 10:40:08 -05:00
Kurt
b7298a7361 Auto-clean save file names of duplicate suffixes
`main (17)` now backs up as `main [...].bak` instead of `main (17) [...]`
Adds a debug-callable method to clean the main backup folder via:
`CleanBackups(Main.BackupPath, true);`
2024-05-27 19:23:56 -05:00
Kurt
c10c2a0dc2
Add Trash Byte verification for Switch formats (#4283)
* Extract logic, finish impl for switch era
* Extract trash checks to static class
* Reduce some allocations in OT name compares
2024-05-27 18:21:11 -05:00
Kurt
ca2bd3baf4 Minor clean
No functional change
2024-05-27 10:33:25 -05:00
abcboy101
a7421a7ceb
Localization (#4281)
* Display Medal type in SAV_Misc5
* Add CHT localization for Medal names
* Add localization for Medal type names
* Update Chinese localization for DPPt Underground
* Add additional G1/G2/G3 item name localizations
* Fix some typos in the Japanese names
* Add Korean names for G3 items
* Correct e-Reader Berry name format localizations
* Italian games use the format `BACCAENIGMA` (capitalized normally as `Baccaenigma`), so the entire item name needs to be titlecased, not just the Berry name.
* Add missing Gen2/3 location name localizations
* Minor corrections to existing languages to match in-game
* Minor tweaks
2024-05-24 18:51:05 -05:00
Kurt
c493580b55 Fix type display dropdown in gen12 2024-05-24 12:22:36 -05:00
Kurt
0be5581638 Enhance type icon downscale
No need to override the interpolation settings, looks way better.
2024-05-24 12:01:06 -05:00
Kurt
a79581e965 Add EncounterTime interface for Gen2 encounters 2024-05-24 11:55:46 -05:00
Kurt
225e2e3ce5 Add type icon to move dropdown 2024-05-24 11:52:47 -05:00
BlackShark
68ea86cc72
Fixed SecretBase3Team (#4280)
* Fixed SecretBase3Team

* Fixed more offsets

* Fixed Species
2024-05-23 21:00:11 -05:00
sora10pls
d4f8c2a5e5 Add latest distribution raid data 🧲🤖 2024-05-23 20:04:05 -04:00
Kurt
cd6eadcd8d Fix daycare slot read
Closes #4279
adds writeback for daycare in gen2 as well
2024-05-23 13:31:23 -05:00
Kurt
93ae048246 Fix sav1 daycare write
was copying wrong data in, clean things up.
#4279
2024-05-23 03:54:06 -05:00
abcboy101
4cb1048139
Implement GBA/GC string conversion (#4278)
* Swap GC nickname fields

The first nickname field is used for the displayed nickname, such as for EFIGS PKM whose names get truncated in Japanese games.
The second nickname field stores the actual nickname, which is restored when a PKM is traded back to the GBA games.

* Add StringFont3GC

* Add GC/GBA string conversion tables/logic

* Refactor GC region conversion

* Minor tweaks

Skip copying nickname/OT on generic conversion method; derived impl will remap the trash for us.
Extract the DisplayNickname truncation function and deduplicate. Use buffers directly when updating
Make CurrentRegion/OriginalRegion properties GCRegion instead of int/byte

Extract local func for font check skip for clarity
2024-05-22 12:22:41 -05:00
Kurt
96bf5a6891 Misc tweaks 2024-05-19 23:29:27 -05:00
9Bitdo
1735fd142d
Add Dot's Quaxly Date (#4277) 2024-05-18 19:16:52 -05:00
Kurt
49ffac5143 Add gen8+ special chars placeholder
use remapped chars from 7->8 as the list
2024-05-18 18:10:49 -05:00
Kurt
431b35a287 Remove unnecessary sugar 2024-05-18 17:51:50 -05:00
abcboy101
94e408d445
Use straight apostrophe for Gen3/4 (#4275)
Colosseum/XD/PBR/Ranch/Gen5 all treat the apostrophe/right single quote as a straight apostrophe (U+0027).

This primarily affects unnicknamed Colo/XD Farfetch'd, which need to use `'` instead of `’` to be traded to GBA correctly.
2024-05-18 10:04:25 -05:00
Kurt
3811f8d114 Misc tweaks 2024-05-18 00:40:29 -05:00
Kurt
899ee63434 Update dependencies 2024-05-17 22:55:11 -05:00
Kurt
0674d72fae Add ITrashIntrospection, impl on PKM classes
new api allows for checking for trash byte metadata, makes it much easier to write a verifier now
2024-05-17 15:58:49 -05:00
Kurt
f5c6510b82 7->8 Handle garbage string trash
no terminator? no problem
2024-05-17 13:18:48 -05:00
Kurt
da51291364 PK1/2 edit: Use save language when detecting lang
GBPKM allow spa/ita lang as fallback (so that nicknamed species just use fallback)
SK2 GetNonNickname reuse the passed language ID instead of recomputing
2024-05-17 13:18:25 -05:00
abcboy101
417231a67c
Update how symbols are handled for Bank -> HOME transfers (#4276)
This maps the remaining (legal) symbols in the private use area that are modified on transfer from Bank -> HOME. If any of these replacements are made, any leading or trailing halfwidth spaces are trimmed. This can result in nicknames/OT names that are the empty string or consist entirely of fullwidth spaces, even though these can't normally entered.
2024-05-17 11:56:55 -05:00
Kurt
4d6ce53bce Don't bother cloning sav for Gen1-3 dex edits
No need to clone the save file and allocate 2 bool arrays. Yay old code, why not do it better now? :)
2024-05-15 23:50:25 -05:00
Kurt
99857540ae Add version hints to filter name checks
Extract result valid check to less-wide style, add extra sanity checks for Gen3 typed saves.
2024-05-15 23:29:27 -05:00
Kurt
a4a0337162 SAV1/2: Force zeroed slots to be FF
Deleting a slot shouldn't leave a level 0 nameless speciesless mon
2024-05-13 19:47:23 -05:00
Kurt
ca85fba061 Force writeback on addition to uninitialized data
Adding an entity to an uninitialized save's box data will force all data to be flushed unless the final destination was (initially zeroed and has no slots to set).
2024-05-13 19:40:55 -05:00
Kurt
0836d3c670 Mirror handler check to bulk checks 2024-05-13 18:16:26 -05:00
Kurt
bbfec1fb29 GB: Don't read ghost slots
Read count from list, instead of using the full capacity.
Malformed lists (truncated via count) with ghost slots should have those ghost slots ignored.
2024-05-13 18:14:40 -05:00
Kurt
215f892f11 SAV1: Don't reference prior-save boxdata
If the boxes are not initialized, skip reading of box data
If the boxes are empty when saving, don't write if {dest 0} or {boxes uninitialized} to retain old data.
If the boxes have any slots when saving, set the flag that boxdata is good.

Remove flag from SAV2, game is different from SAV1. Only use the boxdata, just mirror to CurrentBox data as Stadium only looks at boxdata.
2024-05-13 17:41:23 -05:00
Kurt
3dc84d6a39 Revise ActiveTrainer checks if unset (unit tests)
No longer need to disable correct-handler-state check for unit tests
Adds indication for HT not matching gender (if active trainer is set)
2024-05-13 17:38:16 -05:00
Kurt
0f7d6e1b6a Handler check for WC7 ash pika reimplemented
The handler state checks to check against current trainer are new, forgot about this edge case.
Reduce allocation of temp strings on wc7->pk7, replicate the HT miss for ash pika wc7->pk7
2024-05-13 12:35:47 -05:00
间辞
89e22337a4
Add Sophia's Gyarados Date (#4272)
* Update EncounterServerDate.cs

* Add files via upload
2024-05-13 09:01:02 -05:00
Eelen
f74efceec3
Update lang_zh.txt (#4271) 2024-05-13 09:00:38 -05:00
Kurt
8b68a07dbd SAV1: Use current box span for source of boxdata
Maybe only a GSC thing where Stadium 2 doesn't know better?
2024-05-13 00:16:29 -05:00
Kurt
5af96eab95 Extract switch-entity HT update logic, fix
Closes #4227
SlotWrite no longer revises the data incorrectly

if ot&gender mismatch, clear memories and set new values
can retain wrong values by having ot&gender shared between games
2024-05-12 23:40:50 -05:00
902PM
28add13282
Fix some typos in Japanese localization (#4264)
* Update LegalityCheckStrings_ja.txt

* Update lang_ja.txt

* Update lang_ja.txt

* Update MessageStrings_ja.txt

* Update const_hgss_ja.txt

* Update const_rs_ja.txt

* Update const_e_ja.txt

* Update text_ItemsG3_ja.txt

* Update text_ItemsG2_ja.txt

* Update text_ItemsG1_ja.txt

* Update text_Character_ja.txt

* Update lang_ja.txt

* Update lang_ja.txt

* Update lang_ja.txt

* Update MessageStrings_ja.txt

* Update LegalityCheckStrings_ja.txt

* Update flags_dp_ja.txt

* Update flags_pt_ja.txt
2024-05-12 16:02:59 -05:00
Kurt
ed8d583c08 Add hover tooltip for special char insert preview 2024-05-12 14:51:25 -05:00
Kurt
7b6abc0520 Reduce alloc for ot/nick by raw trash reads
Legality check now catches buffer overflow mons.
Now that I have each type exposing a trash length & charcount, should be easy to have some reusable trash byte measuring methods (see the old branch)
2024-05-12 14:46:58 -05:00
Kurt
08ed482555 Revise gender symbol remapping
Handles Nidoran's shenanigans as well as more clear method names
- Add normalization for PK7->PK8 (no more 0xE... usage for the gender symbols... maybe more chars?)
- Not sure if Gen3 gamecube encoding needs sanitizing. Who is transferring Nidoran to CXD? :)

Requires some silly usage of Language passing as arguments. Future improvements can be made to revise the half/full encoding determination when setting a string. Probably has issues since we're just doing a naive check without considering nicknames w/ special chars.

Closes #4174
2024-05-12 10:47:55 -05:00
Kurt
a33884895f Remove IsNative property
Not something worth retaining if we can check directly.
2024-05-11 16:20:12 -05:00
Kurt
326e790e4b Handle and ' behaviors for 4->5->6
Gen5 does not use the slanted apostrophe for anything. 4->5 converts to ' for both strings.
Gen6 fixes all to be slanted. Even nicknames.
Importing to HOME (PK7, GO) resets nicknames, and the default name is not slanted. Nicknames/OTs are unaltered; again, only the "default" species name is wrong.

Closes #4066
2024-05-11 11:35:55 -05:00
Kurt
d2e8d722e7 Add setting for pkmeditor selected tab colors 2024-05-10 23:17:22 -05:00
Kurt
410be93358 Add opt-in boxdata dragdrop toggle
Can drag from the Box tab into another window or folder to copy the entire box's contents.
Closes #3933
2024-05-10 22:36:46 -05:00
Kurt
dcb23c7981 Allow adding/hiding extra properties in Report grid
#3933 3.
top level settings for Report

1. was already implemented with the file namer settings on dump

not sure how I want to do dragdrop-multi, but I did recently add a record type for `ConcatenatedEntitySet`...
2024-05-10 19:32:28 -05:00
Kurt
e0172b601c Allow randomizing IV32
Really shouldn't use it.
2024-05-10 19:30:01 -05:00
Kurt
969f733c2c Fix pkmeditor auto-set Nickname flag
Event was stolen via impossible-chars change, so just revert & call that method so only 1 method is hooked to the event.
2024-05-10 19:26:48 -05:00
Kurt
bd4ea9656d Misc tweaks
Fix block editor GUI not showing named blocks (`Equals` is easily repointed...)
No functional change for the others, just reusing/cleaning irks.
2024-05-10 01:22:26 -05:00
sora10pls
58a6d453b8 Add latest distribution raid data 🦭 2024-05-09 20:04:38 -04:00
sora10pls
bcb7135940 Add handling for Wonder Ticket Poipole
See 1ec0f91e08
2024-05-06 13:20:51 -04:00
Easy World
e9b7ccac17
Update Chinese Translation (#4262)
* Update Chinese Translation

* Translation tweak
2024-05-06 06:05:01 -05:00
Kurt
5af6678784 Update 24.05.05
Fix devutil text update
swap tox & poison icons, move picturebox to the right
add some Legality settings util functions
2024-05-05 23:24:32 -05:00
9Bitdo
19951f10e1
Add 신여명's Flutter Mane distribution date (#4261)
* Add 신여명's Flutter Mane distribution date
2024-05-05 23:15:32 -05:00
Kurt
e72ff1a899 Misc tweaks
No noticeable change besides correctly indicating N's Pokemon in hover text (instead of Dream Radar)
2024-05-05 15:33:30 -05:00
902PM
004795b329
Changes (#4257)
* Create const_sm_ja.txt

* Update const_pt_ja.txt

* Create const_oras_ja

* Update flags_oras_ja.txt

* Update const_rs_ja.txt

* Update flags_rs_ja.txt

* Update flags_e_ja.txt

* Create const_b2w2_ja.txt

* Create const_bw_ja.txt

* Update flags_bw_ja.txt

* Update flags_b2w2_ja.txt

* Update lang_ja.txt
2024-05-04 15:10:43 -05:00
Kurt
2d61c833bc MysteryGift drop: use extension-less method if w/o
New Korean WC9 is restrict:0 so just make it behave like restrict:3 -- verifies all possible versions that way.
2024-05-04 14:48:19 -05:00
sora10pls
6855c842ca Permit Ranked Ribbon on Legendaries for Regulation G 2024-05-02 20:28:57 -04:00
Kurt
ad0c9b147d SAV6AO: Fix dexnav get/set
Was looking in the wrong segment of save data. Now points to the correct span (pokedex).

Extract a few more save blocks and interactions.
Basically try to get rid of any remaining `SAV.Data` references. Still a few left, but aren't broken.

Closes #4259
2024-05-01 22:58:47 -05:00
Kurt
0f4024952e Minor clean
indexof -> contains
trailing space
some variable reuse
pcdata/boxdata less janky handling
2024-05-01 00:49:43 -05:00
Eelen
40353c7922
Update CHS Translations (#4256)
Co-authored-by: Professor Dirty <103500840+wubinwww@users.noreply.github.com>
2024-04-28 22:47:32 -06:00
Kurt
78fde1367d Add more per-context settings 2024-04-28 18:19:55 -05:00
Kurt
9231f37be4 Move outbreak iteration after slots
essentially wild slots... can have wild marks like slots
need to check before static encounters (cannot have marks)
2024-04-28 16:44:28 -05:00
902PM
eb273d05a4
Japanese language translation updates (#4255)
Revises gen3/4 japanese strings as well as some program messages.
2024-04-28 10:27:11 -06:00
Kurt
e9de611735 Fix sav2 clone
Regression from the version-detect enhancement where we weren't expecting specific versions to be passed, only the lumped ones.
2024-04-28 11:25:46 -05:00
Kurt
e7f79be41d Minor clean 2024-04-28 00:35:26 -05:00
Kurt
bb6e45db60 Refactor out legality settings, add more settings
Extract PP verifier
Add disabling of wordfilter for formats < 6
Add context-specific bypasses for nickname/handler checks
2024-04-26 14:33:03 -05:00
Kurt
94f93d41c5 Update EvolutionGroup4.cs
Handles arceus-9 in future generations (reads as "not present in current game" via personal info if not stripped).
2024-04-26 08:35:54 -05:00
Kurt
a296403595 BeginInvoke for Folder datagrid
auto-picks the correct thread for the control/form

batch editor progressbar is useless cuz the editor is so fast for 99.99% of uses (only really matters for processing a folder on slow drives), yay speed.
2024-04-26 01:34:47 -05:00
Kurt
3358038172 Add ball deferral & met level leniency for gen3/4
Fixes some issues reported via discord
2024-04-26 01:33:19 -05:00
Kurt
4e56a2b756 Fix zhs/zht table swap
simplified is the first set, not the second
Closes #4254
2024-04-26 01:27:53 -05:00
sora10pls
9788f007a3 Add latest distribution outbreak data 🐟🚗🦁💩 2024-04-25 20:06:07 -04:00
Kurt
c0e44d6375 PA8: add missing marking interface 2024-04-25 10:24:38 -05:00
Kurt
2296d34df3 Gen1/2: Fix party read
Oopsie false->true ez
Closes #4253
2024-04-24 19:24:49 -05:00
Kurt
9ff94455b9 Add setting to retain met date on 4->5 transfer 2024-04-24 02:11:14 -05:00
Kurt
093264bc24 Add IStringConverter, impl on PKM & SaveFile
use in trash editor to translate strings, from PKM instead of SAV.
Closes #4222
2024-04-23 00:57:17 -05:00
Kurt
394f5ed707 Clamp minimum size of PKM editor popups 2024-04-22 20:47:59 -05:00
Kurt
1b294f7c97
Refactor: Split Gen1/2 string & pokelist conversion methods (#4251)
* Split Gen1/2 string & pokelist conversion methods

* Refactor pokelist to direct read/write, skip on save if blanked

* Add settings editor for SaveLanguage overrides
2024-04-22 14:42:22 -06:00
902PM
3087519c62
Translated to Japanese. (#4249)
* Update lang_ja.txt

* Update flags_hgss_ja.txt

* Create const_hgss_ja.txt

* Create const_dp_ja.txt

* Create const_pt_ja.txt

* Update lang_ja.txt
2024-04-22 14:40:03 -06:00
Kurt
f93b616ca8 SV: Fix EV yield bit ordering
Closes https://github.com/kwsch/PKHeX/issues/4252

see dcf6b0043b
2024-04-22 13:33:12 -05:00
Kurt
54525da20b Add tradeback wipe of Gen2 initial moves 2024-04-20 14:04:54 -05:00
Kurt
e1b964ad64 Use img instead of color
Still too big
2024-04-17 01:56:09 -05:00
Kurt
f630ad8271 Add setting to hide status condition view
Likely will be moved to the bottom right corner and shown with a language-less sprite. Feature preview on main :)
2024-04-17 00:44:31 -05:00
Kurt
35bf97eaf1 Add status indication & switcher popup
Shows on all tabs to make it clear.
Obviously, status condition is only saved in formats that save status condition in their slot's resting format. Gen3 box mons won't store Burn, etc.
2024-04-17 00:35:42 -05:00
Kurt
b60648627a Add setting to show Gender in Gen1
Default false, current behavior.
Previous commits (years ago) would show Gender, but only current-format properties are shown now.

Fix some form scaling settings that haven't been reported, match other controls.
PKM property copy now checks if src==dest property type; for Gen6<-Gen7 markings, converting ushort -> byte can cause the conversion to fail if more than 8 bits are used. Just ignore copying the marking value, by only copying properties that have the same name AND type.
2024-04-16 21:01:14 -05:00
Kurt
2e62e41ab1 Add gen5 trainer record edits
Refer to src (Record5) for indexes; documented a few.
Closes #4245
2024-04-13 09:06:16 -05:00
ネイ
9caab05aab
Change gen4-gen7 Japanese location names (#4246)
* Fix gen4 Japanese translations

* Add Japanese descriptions where location names are different between games
2024-04-11 22:32:29 -06:00
sora10pls
ce666680f7 PK2: Expose Fast Ship as accessible met location 2024-04-11 10:04:28 -04:00
9Bitdo
8f65874ba4
Add Marco's Iron Hands distribution date (#4238) 2024-04-10 22:01:19 -04:00
ネイ
5794ffd78a
Update text_rsefrlg_00000_ja.txt (#4242)
Translate the remaining untranslated location, from text dump.
2024-04-09 19:31:54 -06:00
ネイ
4972ab469c
Update text_cxd_00000_ja.txt (#4240)
Translate the remaining untranslated location names with reference to text dump
2024-04-09 07:28:46 -06:00
Lusamine
a6868834c8 Add alternate met location for LA static Unown N
Closes #4237
2024-04-06 15:53:59 -05:00
Kurt
0552dcd37c Crystal: Add e-speed-less dragons den dratini
VC has no move relearner so can't unlearn espeed & relearn the level up move to look the same.
Rearrange moves to match how the game gives them.
2024-04-06 09:06:45 -05:00
Kurt
9c1538f503 Disallow HOME move share w/ battleversion & <=Gen7
HOME's SWSH moveset is disqualified from having <=Gen7 moveset permissions if the battle version was applied in SW/SH.
2024-04-05 14:49:46 -05:00
sora10pls
f0b127e13e Add latest distribution raid data 🦕🔮 2024-04-04 20:02:00 -04:00
Kurt
d222d4bb2e CXD location on hover
CXD is a stored version and does not contain others
2024-04-01 17:28:05 -05:00
Kurt
5f4cf2af29 Better guard against bad gender requests
Can infinite loop gen3/4 generators by requesting from an invalid gender (eg Male Blissey) in Gen5+
2024-03-30 17:44:21 -05:00
Kurt
a2f36760e6 EncounterCriteria: ignore genderless gender values
Shedinja (genderless) was filtering when trying to generate gendered nincada
Also fix method2 reattempt & comments while we're here (comments were copypasted from Unown's method)
2024-03-30 11:28:15 -05:00
Kurt
a71d74b1ea Split gen3 memorycard into subfolder 2024-03-30 01:17:18 -05:00
Kurt
8b039ae786 Rework card 0184 deferral 2024-03-30 01:13:52 -05:00
Kurt
8f60ad546c Extract DEntry reading logic 2024-03-28 23:07:13 -05:00
sora10pls
9888479021 Add latest distribution outbreak data 👶 2024-03-28 20:07:13 -04:00
Kurt
d8689e4074 Update EncounterTrade2.cs 2024-03-28 00:41:23 -05:00
Kurt
69f7413387 Revise Gen8 HOME gift 0/0 scale rolling 2024-03-25 23:12:59 -05:00
Kurt
af228b0fec Re-add gsc rock smash
ede3296c31

also sneak in SV Ride legend having trash bytes/HT data
2024-03-25 22:28:51 -05:00
Kurt
fed6b41950 Update EncounterTrade2.cs 2024-03-25 19:27:23 -05:00
Kurt
45444578ab Handle Gen8 ability tradebacks
HOME 3.1.0 change (Teal Mask)
https://www.smogon.com/forums/threads/pokemon-home-update-adds-ability-tradebacks-to-swsh.3727789/

delete all the handling for 3.0.0 tradeback gating since it's no longer correct.
2024-03-25 17:18:44 -05:00
Kurt
bf476f4de5 Update ChannelJirachi.cs 2024-03-25 12:52:59 -05:00
Kurt
c95d56bfd6 Update 24.03.26 2024-03-25 00:46:21 -05:00
Kurt
2b4ecee899 Rename EggEncounter to IsEgg 2024-03-25 00:39:30 -05:00
Kurt
024bd85cc3 Minor tweaks
make PL6 use memory instead of byte[]
make GP1 use memory instead of byte[]
move IEncounterable properties higher to IEncounterTemplate
2024-03-24 20:12:33 -05:00
Kurt
905c80521e Trade2: hard match on nickname, not OT
Webster (RANDY) Spearow in Italian uses the same OT, with different nickname.
2024-03-24 14:46:23 -05:00
Kurt
c6f12515c6 Flag invalid mystry mew seeds, like channel 2024-03-23 21:35:46 -05:00
Kurt
acdb9f12b3 Defragment PersonalInfo backing arrays
resource fetch grabs the big byte[], just reference off that instead of creating another copy object.
2024-03-23 21:34:11 -05:00
Kurt
2ff2815dbe Add Channel Jirachi menu pattern verification
Sitting on my todo-list for far too long
see pokefinder for similar implementation
PKHeX has a forward & reverse implementation available for 100% documentation purposes :)

The seed indicated in hover previews now matches that of the true Origin seed for the encounter (pre menu & accept dialog), similar to Method H/J/K encounters.

Co-Authored-By: Admiral-Fish <24730718+Admiral-Fish@users.noreply.github.com>
2024-03-22 16:12:56 -05:00
902PM
dbed62b314
Update flags_usum_ja.txt (#4231) 2024-03-22 08:04:47 -06:00
902PM
04dda339e5
Update const_usum_ja.txt (#4232) 2024-03-22 08:04:40 -06:00
902PM
1df1101236
Create const_xy_ja.txt (#4233) 2024-03-22 08:04:33 -06:00
902PM
9c9400050f
Update flags_sm_ja.txt (#4234) 2024-03-22 08:04:28 -06:00
902PM
331e08ff2e
Update flags_xy_ja.txt (#4235) 2024-03-22 08:04:11 -06:00
Kurt
84363eb8eb Next seed if activation fails
ty santacrab for pointing this out
2024-03-21 21:22:56 -05:00
sora10pls
1faef958a7 Add latest distribution raid data 🍄👊 2024-03-21 20:48:31 -04:00
9Bitdo
f0f341f6f2
Add Liko's Sprigatito distribution date (#4228) 2024-03-21 07:57:02 -06:00
abcboy101
21a1d5fe6f
Add PBR Poké Coupons accessor (#4230) 2024-03-21 07:56:28 -06:00
Kurt
c1b98025f7 Fix gen3 HoF clamp, reject invalid sector IDs
Closes #4229
2024-03-21 08:55:48 -05:00
Kurt
205e9b433d Set ability bit, precheck for encounter activation 2024-03-21 00:18:16 -05:00
Kurt
f27326cb2e Update PK5.cs
don't count sinnoh champ in battle ribbon sum
2024-03-20 23:31:17 -05:00
Kurt
1291a40e21 sav4 dex 2024-03-20 23:28:19 -05:00
Kurt
f7900ed8b4 Enhance Text trash editor
#4222
SuggestAppend species dropdown
Indicate trash-to-be-applied when hovering on button
Fix ClickOT in pkmEditor applying English on Japanese Gen3 saves
2024-03-20 09:44:20 -05:00
Kurt
95eb008125 Misc tweaks
no functional change
2024-03-20 09:42:28 -05:00
Eelen
cc7f96951f
Update lang_zh.txt (#4225) 2024-03-19 23:42:36 -06:00
Kurt
59be36294f Re-encrypt forest data when saving
Affected saves should just open and save with the current release and it'll re-encrypt.
2024-03-20 00:42:05 -05:00
Kurt
dca76f50f7 Catch more uninitialized file call stack paths
Closes #4223
2024-03-18 19:05:22 -05:00
abcboy101
8a3a338c0b
Fix encodings for Gen 3/4/5 transfers (#4220)
Adjust Gen 3/4 encodings to be consistent with Gen 5 Unicode encodings
Gen 3 quotation marks are displayed differently based on the game language
Implement how Pal Park handles invalid characters and corrupts certain accented characters
Implement how Poké Transfer handles invalid characters
Use U+25BA BLACK RIGHT-POINTING POINTER, since this character is used as the pointer in menus/etc., rather than as a bullet or generic shape
2024-03-17 22:34:13 -06:00
Kurt
2b6f153831 Don't remove <=g7 move if swsh context 2024-03-17 17:30:04 -05:00
Kurt
9e7046564c Revise naive ability deferral logic for gen9
Closes #4221

HA->1->2 requires a 2-step check
2024-03-17 14:44:54 -05:00
Kurt
ea63d90ac4 Settings: remember last selected tab if reopening 2024-03-17 14:43:25 -05:00
Kurt
ee426e29c5 FolderList: minor clean
deduplicate constructor, obvious functions
2024-03-17 14:13:24 -05:00
Kurt
2259551ace Event Diff: move version check closer to init
Sanity Check method checks the objects that expose the flagwork, which no longer is always a save file.
2024-03-17 13:55:27 -05:00
Kurt
bc7118b493 Add specialized PIDIV generator for gen4 slots
extract gen3's to a static class
2024-03-17 13:54:36 -05:00
Kurt
3dd60890fb Setting: change max recently loaded sav file count 2024-03-17 10:40:36 -05:00
Kurt
c3c7fac86f stadium: grgs psyduck all languages 2024-03-16 20:21:44 -05:00
Kurt
9352301b65 Add special generator for Gen3 unown
Removes incorrect slots ( see 2bf963009f )
Allow Unown of any form through (unspecified in Possible generator)

Adds SlotRange get method for Gen3/4 methods.
2024-03-15 21:21:34 -05:00
Kurt
ba0c4c90d2 Indicate if the exception is from a Plugin
All these silly users reporting issues to the main PKHeX forums when it is outdated plugins breaking; give them a better hint.
2024-03-15 20:54:20 -05:00
Kurt
d43f0dadc4 Fix SAV4 daycare seed set (save export)
oops, just extract the end-data offset to be more obvious
2024-03-14 03:24:03 -05:00
Kurt
d630160665 Fix import of bv4/5 teams
Need to decrypt state
Handle pk5.Nature on import/export, unsure what the other flags are for.
2024-03-14 03:11:11 -05:00
Kurt
9c6f030861 Fix gen4 mystery gift misalignment
indicate the card type in the preview pane
2024-03-14 02:32:20 -05:00
Kurt
2f08a35a5c Re-add Mt Silver Cave headbutt tile types
https://projectpokemon.org/home/forums/topic/64867-pkhex-marks-as-illegal-some-pok%C3%A9mon/#comment-288729
2024-03-14 01:20:56 -05:00
Sakura
b7f31f335f
Add YOASOBI Pawmot Date (#4218) 2024-03-13 09:51:25 -05:00
Kurt
10e108939b Move fallback pressure check to near end 2024-03-13 00:18:58 -05:00
Kurt
1241e6eff6 Double check some bounds checks
still have some issues with gen4 mystery gifts and some groundtile flagging (to be resolved later)
2024-03-13 00:08:10 -05:00
Kurt
7ac5da37b3 Fix check order for Pressure/Hustle/Vital Spirit
what a silly set of conditions for it to matter -- we need to permit matching of boosted slots, then enforce that the boosting is valid for the slot, and disallow any other lead. If it couldn't be boosted, then ignore the slot.
https://projectpokemon.org/home/forums/topic/57375-pkhex-new-update-legality-errors-contribution-page/?do=findComment&comment=288731
2024-03-12 23:33:47 -05:00
Kurt
5b89b279d1 Update LearnGroupHOME.cs 2024-03-12 08:33:33 -05:00
Eelen
4c950760a3
Update CHS Translations (#4216)
Co-authored-by: Professor Dirty <103500840+wubinwww@users.noreply.github.com>
2024-03-12 07:33:54 -05:00
Kurt
a62e169258 Use precompiled rand for honeytree rand level
Add note that No Guard works the same as Illuminate and Arena Trap.
2024-03-12 00:46:52 -05:00
Kurt
819f6009bf Fix some gen4 fish slot check inaccuracies
Pt doesn't do Sticky Hold/Suction Cups right for fishing (not just D/P)
HGSS has a follower boost that is now implemented
lol at my GetSuperRod comparing range wrong for slot4

For HGSS follower boost, don't bother indicating differently even though it's not entirely transparent. We just assume the most permissive case (+50) even though people looking to replicate in-game might not have >= 250 friendship.
2024-03-12 00:03:09 -05:00
Jonathan Herbert
4435f032a5
Rename Obedience_Level To Match C# Naming Standard (#4215)
Change from Obedience_Level  to ObedienceLevel since it was missed when other properties we changed to match C# naming standard.
2024-03-11 20:39:31 -06:00
Jonathan Herbert
8a29320724
Minor Changes To EpochDateTime (#4214)
- Fix Epoch0000DateTime.DisplayValue  having seconds in the output even though they aren't stored
- Introduce RawYear and RawMonth in EpochDateTime to reduce duplicated logic
2024-03-11 14:07:59 -06:00
Kurt
7122c5c3f5 Fix database search returning empty w/ unfiltered
Closes #4211
Also fix box dump not creating folder and Surprise Trade block optionally existing in SV saves.
2024-03-11 13:00:08 -05:00
Kurt
353c00ef67 Fix HOME-sharing rules allowing initial egg moves
Were originally permitted because Generation == 0; now we properly indicate the origin of all Initial/Encounter/Shared moves and use that to remove anything not from the environment. Invalid/Sketch moves are still permitted to be Gen0 and skipped because their verification is a different branch.

ty notflyy (discord)
2024-03-11 12:50:38 -05:00
Kurt
b6739e5547 Fix Mystery Gift edit form save
Closes #4213
Also fixes XY/AO saves not having the button available when they should
2024-03-11 12:47:10 -05:00
Kurt
5d2c20d449 Fix DexNav max level boost
Closes #4204
flute actually applies in this case
2024-03-11 12:45:43 -05:00
Eelen
66ac64bc85
Update CHS Translations (#4212) 2024-03-11 08:35:19 -05:00
Kurt
9127f6548a Handle fixed-moveset missing move cases
Stadium Eevee has less moves than if populated by initial moves; need to use the encounter moves for moveset mons.

Co-Authored-By: ShadowMario3 <36941677+ShadowMario3@users.noreply.github.com>
2024-03-10 23:22:37 -05:00
Kurt
8e32ab5008 Misc tweaks
Gen1 VC mew OT name set
Gen1 Japanese stadium language get
Tab control edge case index redraw
Home Tracker & EC control ordering now grouped by flowlayout panel
2024-03-10 21:28:50 -05:00
Kurt
c432c56ca5 Update 24.03.10 2024-03-10 18:38:47 -05:00
Jonathan Herbert
c651c6f6cd
Deduplicate Gen 6-8 PlayTime Logic (#4208)
Slightly overengineered but a fun experiment to de-duplicate some logic.
2024-03-10 08:50:32 -06:00
BlackShark
25c7b3cc8c
Expose Gen 2 Palette to Block Editor (#4209) 2024-03-10 08:36:25 -06:00
Kurt
564025ae72 Fix ruins of alph unown form check 2024-03-09 23:10:01 -06:00
Kurt
a143c9a2ab Update SAVEditor.cs 2024-03-09 20:28:50 -06:00
Kurt
30d85c95a0 Handle radar slot bypassing / chain shiny 2024-03-09 18:13:09 -06:00
Kurt
90cf6cff84 Fix slot replace indexing for slotNum
see dumper, was giving SlotNumber as absolute index, not the replaced index (keep the original SlotNumber value now)
60668dfc9b
2024-03-09 14:16:26 -06:00
Kurt
6d8504bf82 Misc tweaks for slot4 level check 2024-03-09 13:50:15 -06:00
Jonathan Herbert
78fba23eae
Replace Epoch1900DateTimeValue Duplicate Logic Instances (#4207)
* Replace LastSaved8a With Epoch1900DateTimeValue

Since LastSaved8a basically implemented the same logic, replace it and update Epoch1900DateTimeValue.DisplayValue to not display seconds if they aren't present

* Replace PlayTime8 With PlayTime7b
2024-03-09 00:41:26 -06:00
Kurt
d86469266b Enhance bv5 checksum documentation
add for gen4 so there's no more mystery on that

change span to memory for direct injecting to a bv obj
2024-03-08 21:31:17 -06:00
Kurt
994c063537 Misc tweaks 2024-03-08 21:30:21 -06:00
Jonathan Herbert
0693825f96
Correct SM and USUM GameTime7 Offsets (#4205) 2024-03-08 01:02:15 -06:00
Kurt
6dc785da0c Add gen5 battle video reads 2024-03-08 00:53:35 -06:00
Kurt
df7c25d43f Rename battle video classes, allow bv4 recognition 2024-03-07 00:34:21 -06:00
Kurt
802974a42c Add BV4 reading for SAV4
D/P don't store battle videos, so the SAV4 get will always return null for those games. Still can return null if the extdata blocks aren't present.
2024-03-06 21:02:33 -06:00
Kurt
3e25ed9d32 Show sv surprise trade slots in misc tab 2024-03-05 22:04:13 -06:00
sora10pls
d841a7a4bb Add latest distribution raid data 🐢💧 2024-03-05 19:03:13 -05:00
Kurt
efe0f5e0cf Update WB7Records.cs 2024-03-05 10:04:04 -06:00
Kurt
f32a1ddc7a Misc fixes
event flag editor gen5-7
rs/frlg/dp/hgss enc->pkm version choice
pb7 party stats loading
daycare slot now shows when present
remove unnecessary `GameVersion.Unknown`, use Invalid instead. Might be worth removing Invalid in favor of changing `Any=0` to `None=0`.
2024-03-05 09:42:20 -06:00
Kurt
4e87fd7eca Misc fixes
ty matt & foohyfooh
2024-03-04 23:46:11 -06:00
Kurt
59ad4d749f Add handling for lgpe lumped in Contains/Gen check
ty Oval Lenin (discord)

Co-Authored-By: Ivan <15915901+ivanlonel@users.noreply.github.com>
2024-03-03 23:40:55 -06:00
Jonathan Herbert
40cea0c858
Add A Few Time Editors For LGPE, SwSh and SV (#4201)
- In SAV_Trainer7GG, add Adventure Begin and Last Saved
- In SAV_Trainer8, re-enable Last Saved
- In SAV_Trainer9, add Last Saved
2024-03-03 23:35:11 -06:00
Kurt
fa80dac2ac
Refactoring: Rework saveblock to be Memory<byte> based (#4200) 2024-03-03 23:13:16 -06:00
Lusamine
2b63c4b013 Fix Project Snorlax Mystery Gift date
End date for the promo is Feb 29th, but the code can be redeemed until March 31st.
This also adjusts some of the event comments to be more descriptive.
2024-03-02 19:57:42 -06:00
ptrstr
4f26560233
Make CSV files UTF-8 w/o BOM (#4196) 2024-03-02 13:56:10 -06:00
Kurt
0f936f88f4 Gen3: Fix jump/berry max 9999->99990 2024-03-02 13:53:00 -06:00
BlackShark
378b28bc07
Fixed national magic for FRLG (#4199) 2024-03-02 13:51:28 -06:00
sora10pls
466b5f5ea9 Add latest distribution raid data 🍃🐸 2024-02-27 19:06:24 -05:00
Sakura
4a6eab1d2f
Add PokéCenter Snorlax Date (#4198) 2024-02-27 10:41:49 -06:00
Kurt
aa841aebe9 Bounds check on invalid encounter
Magmortar from Gen3 in Gen4+ somehow requests learnset; just give it species 0.
2024-02-26 00:02:46 -06:00
Kurt
645d65bf3b Update SaveBlockAccessor7b.cs 2024-02-25 23:51:53 -06:00
Kurt
8b5e4e798b Add NSO save handler for Gen1/2 saves
Closes #3921
Not sure if the RTC handling is correct (always 0x20 length?) but at least we have a non-fixed-sized header handled with some leniency for different builds.
2024-02-24 21:53:21 -06:00
Kurt
88569d1107 RBY: Add bool toggle for silph lapras
Closes #4188
2024-02-24 20:14:04 -06:00
Kurt
5334f1ef78 Fix IsMysteryGiftUnlocked checked
Closes #4106
2024-02-24 19:51:42 -06:00
Kurt
276a1952fb GSC: Add setter for daycare occupied/egg available
Closes #3862
2024-02-24 19:47:46 -06:00
Kurt
b435f8c258 Offload gen1/2 event templates to pickles
Thanks @ShadowMario3 for doing the raw data entry for these templates -- see PKHeX.EncounterSlotDumper for the csv -> pkl conversion logic.

Reduces object size by classifying groups of events with specific features, and reduces the explicitness of defining each single encounter.

Hard-code the span of Gen1 VC mew to not need to grab the resource, and add an overload to iterate a single template (skips an array creation).

Co-Authored-By: ShadowMario3 <36941677+ShadowMario3@users.noreply.github.com>
2024-02-24 17:53:46 -06:00
Kurt
8206427d21 Add display of pla static encounter seeds
Show single shiny roll slots for PLA too

Update MiscVerifier.cs
2024-02-23 22:37:37 -06:00
Kurt
43cd9300fb Display raid seed for valid SW/SH raids
Yay leadslot for making this possible
Sure we do a little bit of extra work (checking seed twice) but it's negligible overall.
2024-02-23 20:54:56 -06:00
Kurt
8da8f34896 Use TimeProvider.System instead of manual impl
Introduced in .NET 8; use it.
Remove unnecessary [Serializable] tags (binary/XML serialization are not used), will eventually be obsoleted by c# ;)
2024-02-23 20:37:29 -06:00
Kurt
1feec26d1a Split StringFontUtil
2700 line file too big; split into context-specific font classes.
2024-02-23 20:05:50 -06:00
Kurt
034658b764 Remove extra Memory forcing
Closes #4133
Refactors most of the `Trade` methods
Fix default egg friendship to 120
Fix Version value exposed for Gen4 saves (and others)
2024-02-23 17:01:36 -06:00
Jonathan Herbert
65d8ab025d
Update PlayerGeoLocation7b To Match Changes To SaveBlock Constructor (#4194) 2024-02-23 14:32:20 -06:00
Jonathan Herbert
becf158e2e
Fix PKMEditor Using Old Names For ContestStat Properties (#4195) 2024-02-23 14:32:06 -06:00
abcboy101
32e888d871
Check nicknames/OTs against characters in font (#4146)
* Check nicknames/OTs against characters in font

* Update translations

* Do not show warning for Gen4 and earlier

* Use Gen5/7 font for Gen 3-4/1-2 transfers

* Minor style pref

* Remove font legality checks

* Add missing/update Switch fonts
2024-02-23 13:23:50 -06:00
Kurt
4f568b1497 Add slot-set Count update bypass setting 2024-02-23 13:20:59 -06:00
Jonathan Herbert
4974371100
SaveBlock Constructor Changes (#4191)
* SaveBlock Constructor Changes

- Add primary constructor for SaveBlock with default offset
- Update SaveBlock  subclasses to specify offset in contructor rather than as the constructor body

* Fix MyItem Subclasses Using SaveFile Rather Than Specific Classes
2024-02-23 13:20:24 -06:00
Jonathan Herbert
92c964d6fa
Refactor And Deduplicate Some Switch Games Common Logic (#4187)
- Improve Epoch 1900 classes using similar logic from PlayTime7b.
- Move Time Classes into non Gen specific folder since it appears the logic is shared across a few of them.
- Use Epoch1900DateTimeValue for LastSaved in PlayTime7b since the logic is the same.
- Remove TeamIndexes9 since it is a duplicate of TeamIndexes9. Use the similar pattern like Box8 where it is reused in multiple locations.
- Add BlueberryClubRoom9 to ISaveBlock9Main since it wasn't added when the class was introduced.
- Simplify RaidSevenStar9 creation since GetBlockSafe does basically what the if-else block does.
- Change SAV8SWSH Base Raid to RaidGalar to match the same way the SAV9SV does for its Base Raid.
2024-02-23 13:19:17 -06:00
Jonathan Herbert
cf274bba70
Add LGPE Adventure Begin Date (#4189) 2024-02-23 13:19:01 -06:00
Kurt
95fbf66a6e
Refactor: Gen3/4 Lead Encounters, property fixing (#4193)
In addition to the Method 1 (and other sibling PIDIV types) correlation, an encounter can only be triggered if the calls prior land on the Method {1} seed. The RNG community has dubbed these patterns as "Method J" (D/P/Pt), "Method K" (HG/SS), and "Method H" (Gen3, coined by yours truly). The basic gist of these is that they are pre-requisites, like the Shadow locks of Colosseum/XD. 

Rename/re-type a bunch of properties to get the codebase more in line with correct property names & more obvious underlying types.
2024-02-22 21:20:54 -06:00
sora10pls
eb673dcbc5 Add latest distribution raid data 🔥🪨🐧 2024-02-15 19:03:30 -05:00
Jonathan Herbert
247340dff4
Handle SwSh and SV Team Locks (#4186) 2024-02-15 08:33:25 -06:00
Jonathan Herbert
704a7036ea
Determine Scarlet and Violet Save Time (#4183) 2024-02-14 22:56:41 -06:00
Kurt
a93a626dad Add dialog message for empty/FF save files
Users incapable of saving in-game and using save states instead, or emulators not flushing save data on in-game save.
2024-02-11 21:55:37 -06:00
sora10pls
a571c2a3cb Add latest distribution raid data 💘🐟 2024-02-11 19:02:08 -05:00
Kurt
069411d42e Modulo time spent for large gaps
Taking too many seconds to beat the storyline -> overflow on ticks. Since we already shave off the days portion, use the modulo to remove the day portion from the time portion.

Closes #4184
2024-02-10 00:25:22 -06:00
Lusamine
da83f1005a Label SV Surprise Trade storage block 2024-02-09 23:43:43 -06:00
Kurt
10c5adadb8 Misc tweaks
Add more xmldoc
Move files/functions to better spot
Less allocation on hover glow
fix honeytree dppt group/slot/shake rand call (ty real.96)
2024-02-03 14:11:17 -06:00
Sakura
3eb6ff2c9e
Add latest distribution raid data🐧 (#4181) 2024-02-02 19:49:37 -05:00
Kurt
6e9ffe6390 lgpe static encounters: Check specified IVs
https://discord.com/channels/401014193211441153/679178558597496872/1202059598941671434
2024-01-30 19:16:17 -08:00
Kurt
aba7c800b3 Add LCRNG distance calculating method
Solution known for over a decade, finally reminded myself that it'd be nice to have this available. Probably will use this for displaying Method J/K/H frame info when that branch is more mature.
2024-01-30 19:14:04 -08:00
Kurt
f99e4e54f3 Batch Editor: Combine simple & complex EC rand
Just pipe everything to the complicated EC pathway and allow it to select for biases. Swap input preferences to retain a proper EC for dudun/maus.
Closes #4179
2024-01-29 17:41:12 -08:00
Kurt
3618caa4e3 Retain last batch editor instructions for session
Closes #4180
2024-01-29 17:22:06 -08:00
Kurt
48b0f0c6a3 Pt: Swap roamer indexes (moltres, articuno)
f3604018fe/include/roaming_pokemon.h (L10-L12)
Closes #4176
2024-01-29 16:14:17 -08:00
Kurt
48646fd001 Batch edit: return error code on exception
#4177
2024-01-29 16:10:25 -08:00
Kurt
5706fb2fb5 Gen6: Add mach & acro to usable key item memories 2024-01-25 18:56:57 -08:00
Kurt
ff0030e598 SV dex: Remove special handling for Alcremie 2024-01-25 18:47:21 -08:00
sora10pls
72cf3c5fb5 Add latest distribution raid/outbreak data 👻🤖🥇 2024-01-25 19:04:59 -05:00
Kurt
ab5fd54079 Narrow Dynamax Level compare for swsh den seeds
https://projectpokemon.org/home/forums/topic/57375-pkhex-new-update-legality-errors-contribution-page/?do=findComment&comment=287889

Adjust Showdown import to only set these values to PK8.
2024-01-22 19:06:38 -08:00
Kurt
26e4bf0a6a Add XY friend safari sawsbuck form bypass too
See previous commit.
AO only has Deerling, so remove Sawsbuck from the Species compare.
2024-01-22 18:58:22 -08:00
Kurt
6d2de333bf Add formarg deferral for wild annihilape/kingambit
Closes #4167
Also add form bypass for Gen6 Deerling slot that changed form in SV.
2024-01-22 18:51:55 -08:00
Kurt
560320e2cc Fix gen4 encoding of ? 2024-01-21 23:44:12 -08:00
Jonathan Herbert
3b1f8b225f
Gen 5 Misc and Gen 9 Trainer UI Edits (#4169)
Gen 5 Misc Edits
- Fix duplicate unlock all musical props button caused by me

Gen 9 Trainer Editor
- Move Gender next to Trainer Name
- Group Blueberry Quests on Blueberry Tab
- Move Indigo Disk related unlock buttons to Blueberry Tab
- Disable Unlock All Buttons after operations
2024-01-21 14:22:13 -08:00
Eelen
f06ba907b0
Update CHS Translations (#4166)
Co-authored-by: Professor Dirty <103500840+wubinwww@users.noreply.github.com>
2024-01-21 13:02:13 -06:00
Kurt
9aa31e8563 Fix phione ball breed permit (back to poke only) 2024-01-19 06:28:51 -08:00
Jonathan Herbert
a00a6a7308
Label Scarlet and Violet Team Names (#4164) 2024-01-17 20:49:38 -08:00
Kurt
c617aa206d Misc tweaks
Re-dump safari slots for HGSS; the repackaged narc accidentally shuffled files so the wrong water tables were output.
Add error log output on reporting exception fail first to always dump an error log file. Don't trust the popup/form.
Permit TID/OT duplication for in-game trades/N's pokemon.
https://projectpokemon.org/home/forums/topic/57375-pkhex-new-update-legality-errors-contribution-page/?do=findComment&comment=287395
2024-01-16 21:43:10 -08:00
Kurt
513a0d6b7a Update EncounterSlot9.cs 2024-01-16 07:10:57 -08:00
Kurt
2aec7572f8 Fix club board fetch for pre-indigo saves 2024-01-15 23:22:00 -08:00
Kurt
3ea44b5eb0 Allow Gourmand mark on Eggs
Flag it if it's affixed though.
2024-01-15 22:50:37 -08:00
Jonathan Herbert
5a5ce7c273
Scarlet and Violet Blueberry Clubroom Support Board + Style (#4162)
* Scarlet and Violet Blueberry Clubroom Support Board + Style

* Sync to master

* Fix BaseballClub2TwirlingNinja Flags

---------

Co-authored-by: Kurt <kaphotics@gmail.com>
2024-01-15 19:29:39 -08:00
Sakura
fe1a5750dc
Add コロコロ Pokémon Date (#4163) 2024-01-15 18:50:30 -08:00
Jonathan Herbert
690de20687
Add Support For SV Throw Style (#4153)
* Add Support For SV Throw Style
* Change Support Board To Club Room
The block contains more than just the support board so correcting name.
2024-01-15 18:50:17 -08:00
Kurt
13f118fffd Reduce wc3 egg strictness for match (fateful)
Closes #4161
2024-01-15 18:49:24 -08:00
Kurt
00c0aefe00 Add simple alolan evo trim for HOME+ permit
Closes #4159
2024-01-13 21:08:45 -08:00
Kurt
08104179b7 Update 24.01.12 2024-01-12 22:37:01 -08:00
Kurt
f2209ec7fd Add better handling for specific GO transfers
No formarg or bad EC evolution peek -> search for higher species rather than from the bottom.
2024-01-12 18:58:23 -08:00
Kurt
c591e9b809 Misc tweaks
Fix ug editor again
b4aeb1ac7c

make framegenerator info a struct (8 bytes); will be rewritten later anyway
2024-01-11 20:38:11 -08:00
sora10pls
e36035ebc7 Update Pecharunt sprite per battle data assets
It's smaller than the custom resized one I made from SV assets, but the compression on SV textures is atrocious. Can't win em all /:
2024-01-11 19:41:06 -05:00
sora10pls
b16e1e8746 Add latest distribution raid data 🐔🪽 2024-01-11 19:02:15 -05:00
Kurt
5272a32d40 re-add safari super rod slots
oops, missed these in the rip and no unit tests caught it :)
2024-01-11 09:30:25 -08:00
sora10pls
a39712e5cd Add Mochi Mayhem related content 2024-01-11 09:16:04 -05:00
Kurt
354721e20d Rip hgss safari slots with computed slot numbers
HGSS pickles lookin' pretty hefty
ty admiral_fish & real96 for their impl in pokefinder
2024-01-10 20:35:22 -08:00
Kurt
92a8fc9096 Update SAV_Misc4.cs
https://projectpokemon.org/home/forums/topic/64538-pkhex-unhandled-exception-on-opening-dpp-misc-edits/
2024-01-09 23:54:47 -08:00
Kurt
39c4463437 Handle two UI misbehaviors
Hover tooltip thread was throwing exceptions (silently, cuz thread); program starts with empty box slots, hovering between them would never show -> can't hide a never-shown (no handle) form
Fix quirk with stat ordering and visibility toggling
2024-01-09 23:49:00 -08:00
Kurt
b4aeb1ac7c Defensively load gen4 underground inventory/stats
https://projectpokemon.org/home/forums/topic/64536-pokemon-platinum-underground-treasure-bag-corruption/
2024-01-09 21:15:14 -08:00
Kurt
0ca20ff096 Reimplement applied Markings individual get/set
Closes #4156
Extracts to an interface, varied implementations in the appropriate PKM classes. No longer an abstract property inherited from the base `PKM` class.
2024-01-09 20:53:31 -08:00
Kurt
c66fc2f3bf Add gen4 headbutt slot numbers
All were zero, update dumper.
Extract some logic from other parts of the codebase
Fix wyrdeer level check if originated from GO
2024-01-09 19:07:32 -08:00
Kurt
671c3564ee Echoes of trash-test
Remove glyph remap for Switch gender symbols (none actually)
Might be wise to convert StringConverter to singletons...
2024-01-08 21:55:07 -08:00
Kurt
f80c2820f9 Misc tweaks
Local event db's don't need to absorb the builtin db.
More aggressive generator filtering for fuzzed PB7's from GO (can't fake a GO8 in PB7)
Fix splitbreed relearn suggestion (chain is reversed for Origin, don't bother truncating or anything)
Only check for filtered VC ot's if transferred (not still gen1/2 format)
Remove unnecessary xmldoc, add more xmldoc
2024-01-08 20:01:19 -08:00
Kurt
96aed1e3c5 Set default version ID for gen3 pkm templates
ur welcome matt
2024-01-08 19:56:51 -08:00
Kurt
d7473db6af Add plugin stub for notifying GUI language change 2024-01-08 19:56:04 -08:00
abcboy101
3ba2e8e376
Gen 1/2 VC filtered OT name legality (#4155) 2024-01-08 12:34:44 -08:00
abcboy101
01131e59f0
Modify G1/G2 character mappings (#4154)
### Treat 'ヘ' as katakana
- For consistency with ベ/ペ/リ which are treated as katakana here and in StringConverter12Transporter.cs, map 0xCD to katakana ヘ instead of hiragana へ.
- Allow input of hiragana べ/ぺ/へ/り, mapping them to katakana ベ/ペ/ヘ/リ. Previously, they would be treated as invalid and mapped to the string terminator.

### Swap mappings for 0xE8/0xF2
- Previously, 0xE8 was mapped to U+002E `.` FULL STOP, while 0xF2 was mapped to the special character U+2024 `․` ONE DOT LEADER. Since 0xF2 is used in user input while 0xE8 is only used in `MR.MIME`, swap these mappings so that normal keyboard input maps to the user-enterable character.
- Modifies SpeciesName.cs to produce U+2024 for Mr. Mime in G1/G2.
2024-01-07 23:27:03 -08:00
Kurt
018694b84f One less allocation
compiler can infer correct string concat size both ways
2024-01-07 13:13:21 -08:00
Eelen
9da0955f10
Update lang_zh.txt (#4151)
Co-authored-by: Professor Dirty <103500840+wubinwww@users.noreply.github.com>
2024-01-06 16:45:06 -08:00
Samuel Magnan
dbb4df6ddc
Add getter/setter for Sav Gen3/4 Trendy Word, Trainer Hill, Berry Blender, Accessory, Backdrop, Wallpaper (#4152) 2024-01-06 16:44:58 -08:00
Kurt
608f7ebe12 Update Roaming8bRNG.cs 2024-01-05 09:47:53 -08:00
Kurt
a4ac8240b8 Allow replacing VC1/2 default transfer version
Exposed via settings
Conversion settings relocated from Advanced; `AllowIncompatibleConversion` will need to be re-set if you want to deviate from official rules.
2024-01-04 19:57:01 -08:00
sora10pls
0777b9fbf8 Add smoother resized type icons
Not as sharp as before, but this doesn't massively distort some type icons like Normal or Psychic
2024-01-04 16:41:38 -05:00
sora10pls
5ef8cfaf2e Update SAV_Database.cs 2024-01-04 11:08:58 -05:00
sora10pls
5bf43cfc19 Master Rank Ribbon: Regulation F
Also fix Roaring Moon/Iron Valiant WC9 minimum start date (please verify all time zones!)
2024-01-04 10:47:49 -05:00
Kurt
c866bd7044 Extract GetEXP w/ Table
Less magic -1
2024-01-04 01:19:38 -08:00
Kurt
528d927a85 Handle self-form pointer
Logic is much more clear now.
2024-01-04 00:00:34 -08:00
Kurt
b15211ce1b Widen trashbyte gui
special characters table present causes a scroll bar which wrapped controls; make wider to not wrap, set wrap manually.
2024-01-03 23:46:34 -08:00
Kurt
8fa951bcd7 Refactoring
Catch_Rate => CatchRate
Make Location* classes public
Extract a few methods, make public
Merge EncounterUtil & EncounterUtil1
add xmldoc
add missing deferral for Nest8 templates
improve binlinker span fetch to a single read (-1)
2024-01-03 23:06:09 -08:00
Kurt
cca9d55013 Swap stat display (Spe+Special) for Gen1 games
Closes #4014
Show Special as "Special" instead of "SpC" because I feel it's warranted only in this case.
2024-01-02 16:26:45 -08:00
Kurt
e6ad7c12cd Wipe transparent pixels in Silvally form sprites
Closes #4103
```
	var pixels = System.Runtime.InteropServices.MemoryMarshal.Cast<byte, uint>(data);
	foreach (ref var x in pixels)
	{
		bool isTransparent = (x >> 24) == 0;
		if (isTransparent)
			x = 0; // ensure rgb is zero too
	}
	ImageUtil.GetBitmap(data, w, h).Save(path);
```
2024-01-02 15:47:06 -08:00
Kurt
424cbcff21 Misc tweaks
ShowdownSet: Fix indentation, use explicit const
ItemStorage8BDSP: Rename GetAll->GetAllHeld to match others
EncounterLearn: Guard against >4 length enumerables, use explicit versions for S/V
EggMoves: Read u16[] directly rather than manually
SaveFinder: simplify expression
SAV_Database: extract func
SAV_Encounters: use RoM to match Moveset generator
2024-01-02 15:45:35 -08:00
Kurt
d56eb0e3c4 Rely on deferral ability check instead of exact
Closes #4144
2023-12-31 13:13:51 -08:00
Kurt
1b176eec53 Only ChangeType if enum is defined
Allows setting 99 (Stellar) to TeraTypeOverride now
2023-12-31 11:03:04 -08:00
Kurt
c021f893f8 Inject rule bypass logic for SV's level100 lvlup
Closes #4143
2023-12-31 11:02:23 -08:00
Kurt
9d83ce1855 Auto-affix might mark for 7star enc->pkm
Missed this encounter type when changing the behaviors for others (see 5316ad6b37 )
2023-12-30 20:26:47 -08:00
Kurt
d25fab0e87 Change box dump to use a form
Closes #4122
2023-12-30 14:36:33 -08:00
Kurt
a24e6e9cef Misc tweaks
Add notransfer for SV-ride legend
Replace dummy '0' in gen3 chartable to match Bulbapedia -- inaccessible char for entry anyways, and the byte never fetched via IndexOf due to the previous occurrence of '0'.
Enhance file namer interface to tag with a display name
2023-12-30 11:41:45 -08:00
Kurt
581d5158dc Pass ribbon temp struct byref
Quicker than creating defensive copies for each Parse call. Do the same for IV-set passing.
Not worth for binlinker as it's never passed multiple times / deref'd often.
2023-12-30 11:40:10 -08:00
Sakura
581f0e2e1b
Add 윈터페스타 Baxcalibur Date (#4140) 2023-12-29 23:30:38 -08:00
Kurt
d3a48968e0 Revise runtime localization language setter
Closes #4138
ty @fattard
2023-12-29 10:17:50 -08:00
Kurt
9897630b08 Fix off-by-1 for Hidden Power hover preview card 2023-12-29 10:07:37 -08:00
Kurt
50209c4f0d Misc tweaks
Fix ranch save load (index out of range on party stat fetch due to bad truncation)
Folder Browser: Reapply filter if active on column sort
Fix inverted VC1/2 cross-check, add missing derived type and ignore Generation==0 (empty/invalid) moves.
2023-12-28 22:39:42 -08:00
rcyggdra
6b1e967a74
Update text_Forms_zh.txt (#4137) 2023-12-28 22:35:05 -08:00
Lugiad
4ce354915d
Update lang_fr.txt (#4135) 2023-12-28 16:22:07 -08:00
Kurt
f7a888cc57 Extract some logic to SAV4
Closes #4128
I don't want to decipher to manual interactions to the Battle Frontier structures now. Prints were just work values, and fly flags were event flags.
2023-12-28 00:11:56 -08:00
Kurt
61df1981ce Include Ability in fixed pkl, beldum raid moves
Encode beldum's raid moves manually since it's the only one with empty moves (default -> learnset fetch). See pkNX for associated workarounds in pickle build.
2023-12-27 00:29:16 -08:00
Kurt
572b5e98e0 g9 raid beldum moves, g9 shiny trades, g7 kchart
Beldum: Default moves
Gen9 trades were defaulting to Random, not Never
KChart: >= instead of >
2023-12-26 22:30:40 -08:00
sora10pls
807b9031b1 Add more save block labels, Necrozma fusion move handling 2023-12-26 09:36:13 -05:00
sora10pls
007aed07ae Add latest distribution outbreak data 2023-12-24 19:05:00 -05:00
Kurt
92a3f71033 Update 23.12.22 2023-12-21 19:41:54 -08:00
Kurt
5316ad6b37 Set affixed for encounters w/ Gen9 ribbon/mark
Matches S/V 3.0.0 behavior, better to match
2023-12-21 19:04:58 -08:00
Kurt
6d0b4f77e4 Fix Gen2 quick hidden power calc 2023-12-21 19:04:08 -08:00
Kurt
740da4edde Update FormArgumentVerifier.cs 2023-12-21 18:01:03 -08:00
Kurt
35e46c4c4b Add mark deferral for partial match TimeOfDay 2023-12-21 17:42:11 -08:00
Kurt
9f1ad9f6fc Update EncounterSlot9.cs 2023-12-21 16:50:14 -08:00
Kurt
2a13874aee Rewrite evo move double-check
If (evolved into {species}), must have known {move}
I think Swinub->Mamoswine and Mankey->Annihilape might have evaded the check prior; was the only other 3-stage evo (now +Hydrapple).
2023-12-21 16:20:01 -08:00
Kurt
245f267b03 4.5 surskit quirk
int.TryParse no longer correct; `4.5` is a valid `4` per the game filtering behavior.
Surskit: `1,4.5,7`
2023-12-21 16:06:49 -08:00
sora10pls
52d55101d4 Add latest distribution raid/outbreak data 🐧🤖 2023-12-21 19:02:58 -05:00
Kurt
42f809b216 Update EncounterSlot9.cs 2023-12-21 15:39:50 -08:00
Kurt
6e3598d87b Recompute BDSP allowed hatch location list
Dunno what goofiness caused the original bad table.
Took the original hashsets from git and rewrote the table gen, now we have what should be correct.
2023-12-21 15:29:49 -08:00
Kurt
a45215b00d Update SpeciesCategory.cs
#4126
2023-12-21 12:31:10 -08:00
Kurt
7759596ffd Correct minior out-of-battle form 2023-12-21 11:31:37 -08:00
Jonathan Herbert
24a5b05fcb
Handle Seven Star Raids Change From Teal Mask Update (#4119)
* Handle Seven Star Raids Change From Teal Mask Update

* Refer To RaidSevenStarCaptured9.CountAll Rather Than Recalculate It

* Fix Wrong SevenStarRaid Size Variable Referenced

While they are the same value correcting the mistake so that there isn't confusion while looking at the code.
2023-12-21 10:51:09 -08:00
Jonathan Herbert
3ce726087c
Fix Language Files Not Matching DLC Raid Buttons (#4125)
* Fix Language Files Matching DLC Raid Buttons

Fix translation issue caused by PR #4112

* Update lang_es.txt
2023-12-21 10:50:51 -08:00
Kurt
d4eccd92ca Update raid minior-0, fixed spawns (stellar/under) 2023-12-20 23:42:20 -08:00
Kurt
2fe0f24301 Fix broken unit tests (location, item, evo order) 2023-12-20 14:40:13 -08:00
Kurt
fb87e9645a Update IHyperTrain.cs 2023-12-20 14:20:37 -08:00
Kurt
5da1ae4849 Handle Gold Bottle Cap 31IV bugfix
HOME fixes anything with 31IVs flagged. S/V no longer in error. Would be an incredible headache to detect "has visited S/V prior to 3.0.0" so just flag it.
It's up to the checker to know about this.
2023-12-20 11:29:50 -08:00
Kurt
e885a7d4f4 Play chatter without temp file
Direct from a MemoryStream, no need for temp file
2023-12-20 08:58:40 -08:00
Jonathan Herbert
54d3f377e9
Make Raid Editors Edit Copies Rather Than Origin Save's Data (#4118)
Use an enum to know which raids to use rather than passing the specific raids so that the save changes copy back operation only happens when a user saves modifications and thus won't flag the save as modified if nothing was changed.
2023-12-19 20:53:10 -08:00
Samuel Magnan
90d000d640
Add villa furniture getter/setter for Platinum (#4120)
* Add VillaFurniture to SAV4Pt
2023-12-19 20:50:32 -08:00
sora10pls
41c830fa2c Add BCAT Milcery outbreaks 2023-12-19 22:34:42 -05:00
Kurt
a89b13027b Update gen9 sketch disallow 2023-12-18 20:27:57 -08:00
Jonathan Herbert
80faf97425
Use Explicit Types Instead Of SaveFile For Forms (#4116) 2023-12-18 20:24:24 -08:00
Jonathan Herbert
53bbebae6a
Consolidate DLC Raid Buttons (#4112)
Given that the raid button is used for both games, consolidate the buttons for DLC raids.
2023-12-18 20:24:09 -08:00
Eelen
5ad7a31171
Update CHS Translations (#4117)
Co-authored-by: Professor Dirty <103500840+wubinwww@users.noreply.github.com>
2023-12-18 20:23:58 -08:00
Kurt
10b158f82b Update EncounterTrade9.cs
Update EncounterTrade9.cs
2023-12-18 10:42:22 -08:00
Kurt
cbc63ef0a0 Handle non-nicknamed partner trades & weight/scale 2023-12-18 10:19:47 -08:00
Kurt
e371e87f1b Restrict cavern & labyrinth to inside 2023-12-18 08:03:50 -08:00
Kurt
9c0e9c31da Add weather marks, rebuild fixed encs
Still a few stragglers, but better overall.
2023-12-18 08:00:21 -08:00
sora10pls
09590a46b4 Update Encounters9.cs 2023-12-18 10:47:05 -05:00
Eelen
6f815c975c
Update CHS Translations (#4115)
Co-authored-by: Professor Dirty <103500840+wubinwww@users.noreply.github.com>
2023-12-18 01:27:17 -08:00
Kurt
d95f424c62 Add another sav size 2023-12-18 00:47:53 -08:00
Kurt
f2419db092 Update PersonalInfo9SV.cs 2023-12-17 21:14:46 -08:00
Kurt
61d958e07a Fix blueberry raids & stellar showdown parse 2023-12-17 19:45:13 -08:00
Kurt
b8d370b661 Fix new tm bitflags, torque move check
ty roc for info
2023-12-17 17:51:55 -08:00
Kurt
c892225a55 Oops 2023-12-17 17:26:38 -08:00
Kurt
10cae1316e Update 23.12.18 2023-12-17 17:06:39 -08:00
Kurt
01c82e472e
Add support for Indigo Disk (#4111) 2023-12-17 16:41:15 -08:00
Sakura
4f56aa27f7
Update WC9 Dates (#4102)
* Add Paldea Gimmighoul Date

* Add コロコロ Roaring Moon and Iron Valiant Date

* Update
2023-12-17 16:39:26 -08:00
abcboy101
69cd3455be
Add Chatter Editor (#4101)
* Add Chatter accessors

* Add Chatter Editor

* Update translations
2023-12-17 16:39:15 -08:00
Eelen
30f3354b94
Update CHS Translations (#4100)
Co-authored-by: Professor Dirty <103500840+wubinwww@users.noreply.github.com>
2023-12-17 16:39:03 -08:00
Jonathan Herbert
07cd05d1c1
Fix Some Missed Conversions To Collection Expressions (#4096) 2023-12-17 16:38:53 -08:00
Kurt
59e409bbd7 Update pk3.HeldMailID when not holding mail
https://projectpokemon.org/home/forums/topic/64361-bugs-when-editing-g3-japanese-version-pokemon/?do=findComment&comment=287038
2023-12-17 08:57:56 -08:00
Kurt
0769300803 Handle language-specific fateful bitflag location
Closes #4072
ty @Lusamine for checking japanese and english for complete clarity on this multi-year flip-flop.
historically, we used 0xFB.bit0 until jpn complained, changing it to 0xC9.bit4 to match the pk3 struct. Both were correct, and neither were correct.
2023-12-10 20:58:58 -08:00
Kurt
1fe2b4f29b ArgumentOutOfRangeException
Use the new NET8 API
2023-12-09 15:21:10 -08:00
abcboy101
edf28f74ff
Fix 3DS country/region names (#4095) 2023-12-09 06:22:05 -08:00
Kurt
b3684c58b2 Update EncounterDist9.cs 2023-12-08 17:30:41 -08:00
902PM
52be5b3d40 Update gen2/3 flags 2023-12-07 23:21:53 -08:00
Kurt
09018a4eaa Update EncounterDist9.cs 2023-12-07 23:20:49 -08:00
sora10pls
99b9911aa1 Add latest distribution raid/outbreak data 🌌🌸 2023-12-07 20:17:22 -05:00
Sakura
87efd747c4
Add Darkrai and Shiny Lucario Date (#4094) 2023-12-07 12:57:26 -05:00
Kurt
ef68886554 Add more annotations
Fix typo in swsh block name
2023-12-07 00:07:55 -08:00
902PM
61ab70f6bf
Update const_frlg_ja.txt (#4088)
Translated " const_frlg_ja" into Japanese.
2023-12-06 21:15:39 -08:00
902PM
1085a31a56
Update const_e_ja.txt (#4087)
Translated " const_e_ja" into Japanese.
2023-12-06 21:15:29 -08:00
902PM
50878846bf
Update flags_e_ja.txt (#4086)
Translated " flags_e_ja" into Japanese.
2023-12-06 21:15:20 -08:00
902PM
0be8873b89
Update flags_frlg_ja.txt (#4084)
Translated into Japanese.
2023-12-06 18:27:31 -08:00
Jonathan Herbert
d3452deb80
Document Dojo Watt Donation Related Flags and Values (#4085) 2023-12-06 18:27:17 -08:00
Kurt
762a2a0c41 Fix gen4 complete dex operation for female-only
Closes #4047
SetSeen(species) will set the seen flag but leave both bits 0-0; old code would SetSeen and fail to revise it to 1-1. Since we're setting both to their "complete" state, a "complete" state for a single gender is just a "new" registration operation.
2023-12-05 18:51:32 -08:00
Kurt
c09890366b Fix wc no nickname check, gen3 bvid return
Closes #4081
2023-12-04 18:38:05 -08:00
Kurt
61b4cb67b2 Update readmes
#4082
2023-12-03 21:25:44 -08:00
Kurt
d47bb1d297
Update .NET Runtime to .NET 8.0 (#4082)
With the new version of Visual Studio bringing C# 12, we can revise our logic for better readability as well as use new methods/APIs introduced in the .NET 8.0 BCL.
2023-12-03 20:13:20 -08:00
Kurt
a71597f3a8 Handle gen4 manaphy egg gift pid checks correctly
ty lincoln for bringing this to my attention and explaining the possible actions & outcomes

game checks an un-updated ID32 (original recipient, different if traded hatcher), not "oops Ranger is no longer stored"
2023-12-01 22:33:08 -08:00
Momonopopotee
dcb72b0993
Update Gen5 Event Flags (#4079) 2023-11-30 16:40:19 -08:00
Eelen
41a3196161
Update CHS Translations (#4075)
Co-authored-by: Professor Dirty <103500840+wubinwww@users.noreply.github.com>
2023-11-26 11:48:19 -08:00
Jonathan Herbert
39dddf1605
Update Switch Backup Paths With JKSV Path (#4076) 2023-11-26 11:46:42 -08:00
Fábio H. Attard
ef941dc0e6
Naming useful SV blocks (#4060)
* Named a few useful blocks

* Named a few useful blocks

* Renames for consistency as per suggestion

* Withdrawing hidden items blocks for more investigations

* Identification for Hidden Items blocks
2023-11-24 07:50:25 -08:00
Jonathan Herbert
e0fb60cdea
Update Pokétch Unlocked Count (#4073)
Closes #4067
2023-11-24 07:50:04 -08:00
Momonopopotee
4f998e2fe0
Add B2W2 Volcarona Event Flags (#4074)
* Update flags_b2w2_en.txt

* Update flags_b2w2_es.txt

* Update flags_b2w2_ja.txt

* Update flags_b2w2_ko.txt

* Update flags_b2w2_zh.txt

* Update flags_b2w2_zh2.txt
2023-11-24 07:49:40 -08:00
Kurt
2e736a1d4c Allow Colo Heracross lock-in skip
First = no locks; would be duplicate with the original Heracross locks, so just use the First since it's most permissive.

https://projectpokemon.org/home/forums/topic/64263-invalid-encounter-type-pid-mismatch/

ty Johh !
2023-11-23 23:32:06 -08:00
sora10pls
088bc29691 Ow, the edge 2023-11-23 19:06:30 -05:00
Sakura
ef52c390df
Add Team Star Revavroom Date (#4071) 2023-11-22 12:21:29 -05:00
Kurt
48d9368d3d Fix CountTutor3
ty chris
2023-11-21 18:47:14 -08:00
Sakura
684ea363d8
Add Alex Dragapult Date (#4065) 2023-11-18 21:45:30 -05:00
sora10pls
9b8cda39e7 Add Gen5 Musical Prop editor
Closes #4069
Chinese localizations copied from English (was not an available language until Gen7), contributions welcome!

Co-Authored-By: Jonathan Herbert <3344332+foohyfooh@users.noreply.github.com>
2023-11-18 21:41:10 -05:00
sora10pls
2e1e2b74f7 Add B2W2 Medal localizations
Closes #4068
Chinese localizations copied from English (was not an available language until Gen7), contributions welcome!

Fix typo categries -> categories
2023-11-18 21:15:38 -05:00
Fábio H. Attard
002d1d015f
Secondary heuristic to distinguish SAV1 save files between versions RB and YW earlier, if the Starter value was not set yet (#4064) 2023-11-17 10:25:31 -08:00
sora10pls
aec0367085 Add Eevee Day raid/outbreak BCAT data 2023-11-16 19:07:20 -05:00
Jonathan Herbert
876f4f4737
Document Location of Scarlet and Violet Rental Team Codes (#4062) 2023-11-14 19:36:23 -08:00
Kurt
c505b5a49d
Enhanced Slot Hover Preview (#4059)
Adds a new primary Hover Preview tooltip form. Users can change setting to use the old tooltip if they want.

When the user hovers over a slot in their Box / Party, PKHeX displays a tooltip indicating details about the Pokémon. This text tooltip shows the Showdown text (with some localization based on program setting), and includes details about the encounter the legality check matched it to.
2023-11-14 19:36:11 -08:00
sora10pls
d43b78349c Remove unreleased Altering Cave (E) enc slots
See 149c3fec20
2023-11-13 11:30:44 -05:00
Fábio H. Attard
5ede6feb38
SAV1 can implement IEventWorkArray<byte> (#4061) 2023-11-12 23:05:55 -08:00
Kurt
9df8f16ed3 Misc tweaks 2023-11-12 21:11:11 -08:00
Kurt
f0cacef8e2 Use met level if available for Gen2-C enc moves
Closes #4046
gbera allows move relearning via Stadium which hid the issue; old code just used the enc min level instead of actual. If a move was learned between min & actual, was flagged incorrectly.

pass the pkm thru the call chain
2023-11-11 23:02:15 -08:00
Kurt
a01c9ff5c6 Permit Headbutt from Water (Route 47)
See associated dumper commit for unprocessed change:
c06dc6893f

Closes #4058 ty @MaxAkito !
2023-11-11 19:57:56 -08:00
Kurt
40b6c97358 Fix Gen3 min sheen check
referred to wrong const value
https://projectpokemon.org/home/forums/topic/57375-pkhex-new-update-legality-errors-contribution-page/?do=findComment&comment=286554
2023-11-10 19:06:52 -08:00
Kurt
64ed92a566 Minor clean 2023-11-09 22:02:23 -08:00
abcboy101
894ea1d628
Add conversion for Gen 4 saves between KO and JP/INTL formats (#4057)
* Refactor Gen 4 extra blocks

* Replace FetchHallBlock with extra block getter

* Add UI to convert save to/from Korean

* Do not modify uninitialized General/Storage blocks

* Detect invalid extra blocks
2023-11-08 23:33:40 -08:00
Jonathan Herbert
1f6d2de891
Add GUI For XY Roamer (#4056)
* Add GUI For XY Roamer

* Account For Roamer Not Set In XY
2023-11-08 23:33:02 -08:00
Kurt
3df5478d11 Minor tweaks, add Gigantamax info class 2023-11-08 23:32:41 -08:00
Sakura
5ce9619690
Add PokéCenter Pawmi&Charcadet、Korea Bundle Fidough Date (#4052)
Co-authored-by: Joseph11024 <83472295+Joseph11024@users.noreply.github.com>
2023-11-05 14:27:41 -08:00
Kurt
5cf90deaee Use Shiny state in Glow
Closes #4055
Glow the shiny star for all formats.
2023-11-05 14:24:26 -08:00
Kurt
59dc7fb694 Misc tweaks 2023-11-05 14:20:35 -08:00
abcboy101
62df64e6f5
Gen 6/7/8/9 map position/rotation (#4054)
* Add Rotation to Gen 6 map position

* Keep 7 digits of precision for XYZ coordinates

* Scale coordinates in Trainer Data Editor

* Localization for SAV_Trainer.L_R

* Add SWSH map position

* Add LA map position

* Add Rotation to SV map position

* Add GG map position
2023-11-05 12:24:08 -08:00
Jonathan Herbert
9cdb08155e
Document Sword and Shield Swords Of Justice Flags and Values (#4053)
- Rename capture flags to more appropriate name since it is just if they are in the overworld or not.
- Document progress values for each Sword of Justice.
2023-11-05 10:00:53 -08:00
sora10pls
5708310f6f Add latest distribution raid data 🔥👻 2023-11-02 20:03:32 -04:00
Kurt
bced546c63
Memoize Ball Breeding permission tables (#4050)
O(1) lookup for arbitrary species, some edge handling rules for specific game islands/forms. With HOME, there's only 3 islands of permissions. No allocation besides the singletons which aren't really necessary.

No longer need to peek within multiple hashsets, just fetch the "is possible" bit from the species listing and check if the bit is set.

Will be fun if ball shell swaps are added 🤞, get to set all bits for anything that can enter game with that feature (if ever added).

Add unit test for gen67 no-patch exclusion
2023-11-02 15:55:26 -07:00
abcboy101
8e28ca8cf4
Fix Gen 7 map position (#4049)
* Prevent UpdateOverworldCoordinates from corrupting FieldMoveModelSave

* Fix Gen 7 player coordinates

* Fix Gen 7 player rotation
2023-11-02 15:32:45 -07:00
Kurt
e0cf4447ff Explicitly handle mark8 presence (wc9/static9)
Move some files around
WC9 fidough gift sets Classic AND Uncommon, and it doesn't set the lowest ribbon indexes. Nice GUI.
2023-11-01 19:19:50 -07:00
Kurt
be574948db Add more xmldoc, conditional flawless IVs
Rerolling IVs now rerolls correctly for SOS hidden ability encounters.
2023-10-29 20:27:42 -07:00
Kurt
65bc027f22 Improve perf of dummied move check
O(1) of a not-allocated array is faster than O(1) of an allocated HashSet.
2023-10-29 20:25:23 -07:00
Kurt
e448679f4a Minor clean
No alloc EncounterEgg relearn moves (use span instead)
Move HoneyTreeUtil out of root Saves folder
Minor readability tweaks
2023-10-28 22:07:58 -07:00
Kurt
06c36130c5 Add IFixedIVSet; better reroll IVs for fixed sets 2023-10-28 22:02:12 -07:00
Kurt
9cc36578c5 Add GameConsole enum and EncounterDate pivot
Can now fetch a valid date for a context
2023-10-28 22:01:26 -07:00
Kurt
b088a29c88 Gen4 Pt: remove invalid Korean event flag rows 2023-10-28 22:00:14 -07:00
Kokaikokai
a351672721
Add one more SV DLC1 save size (#4045)
Signed-off-by: Kokaikokai <fianity.gani@gmail.com>
2023-10-28 11:27:32 -07:00
Samuel Magnan
be576be88b Add Crystal Mystery Gift item in Simple Editor (#3983)
For Pokémon Crystal international release, add the option to edit the Mystery Gift item in the Simple Editor.
2023-10-27 21:19:13 -07:00
Samuel Magnan
fda815c3e5
Gen2: Add MysteryGiftIsUnlocked flag in Simple Editor (#4007)
The flag indicates whether the option "Mystery Gift" has been unlocked and it is displayed in the start menu.
Note: It is unlocked after talking with the little girl on the fifth floor of the Goldenrod Department Store.
2023-10-27 21:09:15 -07:00
Jonathan Herbert
0015beb8d8
Add Accessors for Black 2 And White 2 Medals (#4043)
Thanks @suloku as the logic this uses comes from their BW_tool.
2023-10-27 21:02:04 -07:00
Jonathan Herbert
fd1f00e1a6
Handle Sun and Moon Legendary At Altar Flags Correctly (#4044)
According to the research by IgnisSpace shown on https://projectpokemon.org/home/forums/topic/64103-sm-battle-flags-and-others-misc-flags/ , Solgaleo and Lunala have different flags for if they are at the Altar of the Sunne/Moone.
2023-10-27 21:01:27 -07:00
Kurt
5c7ce2c0a6 Misc tweaks
Fix g12 str set (jp/en was swapped, oops)
add more xmldoc
minor readability/short circuiting
2023-10-27 21:01:11 -07:00
sora10pls
7fd1c9da1e Add Halloween raid/outbreak data 2023-10-26 21:12:14 -04:00
Kurt
16aba45371 EncDB: Fix colo starter yield
Improve enc->ck3 creation, inline the shiny/gender reroll logic instead of trying again outside the loop.
Rewrite the pidiv logic to be more obvious

colo starters now show up in encdb list
2023-10-26 01:07:20 -07:00
Kurt
325f75e3d3 Gen12 strings: span instead of runtime dictionary
More performant byte->str performance (no longer needing to hash byte and fetch bucket), unmeasured str->byte performance, but it is the same implementation as Gen3 which is not a bottleneck.
Reduces the dll size by 80KB, and RAM usage by an unmeasured (likely similar amount). Better startup speeds since multiple dictionaries do not need to be allocated and created.

Moves Gen1 trainer names (0x5D) to Transporter class, and remove Korean entry (not legally obtainable since there's only Gen2 Korean games and Gen2 Korean VC cannot trade with international).

Hard verify Gen1 trainer name for language, since 0x5D is the ROM transfer language. Nickname can still be from any of the connected games.

This refactor makes it easier to use a different charmap for byte<->str for Boxes (see #4027) and the language differences. We have a master table that "works" for all text name entry, but localizations differ for some glyphs accessible in the box naming UI.
2023-10-25 16:34:11 -07:00
Zazsona
1816aefc25
Ranch level indexing fixes and general API usability improvements (#4041) 2023-10-20 18:28:43 -07:00
Lusamine
8d409be8ba Fix translation scrape for TechRecordEditor 2023-10-15 10:35:01 -05:00
Lusamine
7efa575eb0 Update translateables 2023-10-15 10:30:46 -05:00
Eelen
90039f62b6
Update lang_zh.txt (#4037) 2023-10-14 23:42:13 -05:00
Kurt
da27814504 Minor tweaks
I quite like the DeSmuME footer check simplification.
2023-10-14 19:28:46 -07:00
Kurt
738c51d596 Save/Open file dialog init
Initialize outside of object initializer, solves warnings if any property sets throw exceptions
2023-10-14 19:26:56 -07:00
Kurt
0da8c33c52 Use byte span for BinLinker check
Need to change ConstantExpected to Length when NET8 comes out next month
2023-10-14 19:25:39 -07:00
Kurt
b3bbc044ca Misc jp Gen1/2 vc fixes
Fix iterator jump
Allow bu dragonair encounter -- should probably rip out all the old catchrate compare stuff anyway.
2023-10-13 18:39:25 -07:00
Kai
6978db5e9a
Add Trixie Mimikyu Eligible Date Range (#4035) 2023-10-13 19:28:11 -04:00
Jonathan Herbert
63b8257315
Fix Typo In 23.10.11 Changelog (#4034) 2023-10-11 22:37:33 -07:00
Kurt
4877c63750 Update 23.10.11
Add sizes for 2.0.2
Add crossover Unown-C location for PLA - Closes #4031
2023-10-11 20:12:51 -07:00
Kurt
0491ab2372 SAV3E: Max Item ID +2
Closes #4033
2023-10-11 19:30:26 -07:00
Kurt
b6a42d414b RNG: Move files & extract some logic
Rearrange some folder organization to extract some common/reusable logic for dealing with RNG operations for specific gens/games.
2023-10-11 19:28:51 -07:00
Kurt
40ce70b9e8 Change OT name of test cases
Used to be `KKK` but now that trips a more modern wordfilter.
2023-10-11 19:27:31 -07:00
BlackShark
bf8a5451ed
Updated badwords.txt (#4032)
* Updated badwords.txt

* More clean up
2023-10-10 15:18:45 -07:00
Kurt
abcaaa44cd Extract pokewalker logic from template ctor
Actually searching (instead of brute-forcing) for a spread will forever be haunting.

Add to Legality Check matching with vague partial match
2023-10-07 23:14:34 -07:00
Jonathan Herbert
176d0d670a
Enable Game Start Date Editor For Sword and Shield (#4029) 2023-10-07 10:20:54 -07:00
Hendi48
1c097598e7
HGSS: Add support for editing Pokeathlon Points (#4030) 2023-10-07 08:57:38 -07:00
Kurt
707898d4e2 Add Pokewalker IV validation methods
Not yet hooked into the legality analysis (MethodFinder doesn't know about encounter template info).
2023-10-07 00:00:36 -07:00
Kurt
fac682bcad Update WordFilter.cs
Reduces startup alloc by 24KB if we don't allocate an array to store all regex strings
kinda small but ez free
2023-10-06 23:58:13 -07:00
sora10pls
bb5cc7cff1 Add latest distribution raid data 🦉🥋 2023-10-05 20:03:16 -04:00
Lusamine
e64205e504 Fix unsafe camera config block find
Closes #4026
2023-10-05 10:11:34 -05:00
Kurt
c23bae5180 Add Distribution Outbreak type & recognition 2023-10-04 22:49:56 -07:00
Kurt
965039e258 Misc tweaks, fix slot8b ball
Fix Slot8b fallback ball not being Poké (was 0).

Swap eevee & none move sentinels; branch-if-zero easier for compiler.

Lift EV-any-above100 to be checked first; fetching personal and getting EXP is slower than checking 6 indexes in stack. Both branches would check. Most pokemon won't have EVs >100 as users flip through boxes in gen3/4.
2023-10-02 20:54:17 -07:00
sora10pls
e58a1565ad Add ConfigCamera9 2023-10-02 15:55:19 -04:00
sora10pls
8b2b858e69 Update RibbonRules.cs
Closes #4023
2023-10-02 08:07:14 -04:00
sora10pls
57292b23c8 Update Master Rank Ribbon check for Regulation E
Sinnoh starters permitted, Kitakami dex permitted, Legendary/Mythical still disallowed.

Also check for Battle Version on GO8 encounters in SWSH, which while are Gen 8 encounters, require Battle Version since they didn't originate from SWSH.
2023-10-01 09:17:51 -04:00
Kurt
17e0f1feee Prefer cave when finding Fixed origin AreaInfo
see pkNX commit
2023-09-29 22:33:43 -07:00
sora10pls
07191e6aad Label a whole bunch of delivery outbreak blocks
Paldea, Kitakami, Blueberry, BCAT Paldea, BCAT Kitakami, BCAT Blueberry
2023-09-28 21:26:27 -04:00
Kurt
96e74a05d0 Add HoneyTree API 2023-09-28 17:35:29 -07:00
Kurt
963eaaaa40 Merge branch 'master' of https://github.com/kwsch/PKHeX 2023-09-28 16:40:17 -07:00
sora10pls
3debc89e3c Fix sudachi outbreak subjugation block keys
Co-Authored-By: santacrab2 <79347566+santacrab2@users.noreply.github.com>
Co-Authored-By: notzyro <62817135+zyro670@users.noreply.github.com>
Co-Authored-By: Manu <52102823+Manu098vm@users.noreply.github.com>
2023-09-28 10:34:48 -04:00
BlackShark
cfadf86d27
Load plugins before initial files (#4020) 2023-09-28 07:04:12 -07:00
Kurt
12ee201431 Extract EV limit logic 2023-09-27 22:15:59 -07:00
Kurt
0cda774b97 Misc tweaks
Closes #4016
Closes #4018

Adds interface to fetch 64bit correlation seeds from template
Improves re-entry performance for 64bit seed search
2023-09-26 09:32:49 -07:00
Kurt
35696c97ed Update GUI translations 2023-09-24 21:27:56 -07:00
Kurt
c00657f845 Update 23.09.25 2023-09-24 21:19:53 -07:00
Kurt
b191e962bf Merge branch 'master' of https://github.com/kwsch/PKHeX 2023-09-24 21:03:03 -07:00
Eelen
35d435db77
Update lang_zh.txt (#4015)
Co-authored-by: Professor Dirty <103500840+wubinwww@users.noreply.github.com>
2023-09-24 21:02:44 -07:00
Kurt
88a5a1c9e5 Update WC9 ID32 handling for 2.0.1
Seems like
2023-09-24 20:56:35 -07:00
Kurt
f6ee2b1ffa Kitakami Placeholder area weather table 2023-09-24 17:11:52 -07:00
Kurt
bc18cd0ab4 Kitakami: Fixed spawn subzone & crossover 2023-09-24 17:02:42 -07:00
Kurt
b4b72a8fda Clarify Tera Raid crystal middle number 2023-09-23 20:41:50 -07:00
Kurt
7a28a23d5e Add subzone encounters
Yay untagged locations with exclusive encounters
2023-09-23 20:41:00 -07:00
Kurt
dd541e0bf2 Add TryGetBlock fetch method
Improves performance; only a single binary search for the block (instead of one or two).

Remove allcoation for SV flyhashes
2023-09-23 15:50:11 -07:00
sora10pls
0b60161379 Label more sudachi1 save blocks 2023-09-23 15:28:35 -04:00
sora10pls
9510272149 SV TM/Fly cheats: do not modify non-existent blocks
New fly location and TM Machine flags do not exist on older save revisions, which would've thrown an exception
2023-09-23 11:08:25 -04:00
Leidenfrosty
703e2ed5ad
Other Additional G9 DLC Save Sizes (#4008) 2023-09-21 19:47:20 -07:00
sora10pls
26894eb6fa Add latest distribution raid data 🔥🐛 2023-09-21 20:07:11 -04:00
Kurt
a45a7cc2d3 Misc fixes
Pokewalker: defer match if shiny
Jacq egg: recognize unhatched egg, traded
sv-pokedex-old: don't add rows for >1010 species (out of range)
static9: correctly flag scale mismatch

SAV4HGSS: expose pokegear #'s as span instead of alloc
2023-09-21 13:14:34 -07:00
sora10pls
99d4a5ccdc Add sudachi1/2 encount outbreak save blocks
Co-Authored-By: Lusamine <30205550+Lusamine@users.noreply.github.com>
2023-09-20 22:28:20 -04:00
sora10pls
10147334e6 Add Terastal Cap, add remaining missing fashion items
Co-Authored-By: Pasquale Nardiello <pasqualeomeglio@gmail.com>
2023-09-19 20:35:50 -04:00
Kurt
5fcf82c827 Split meta filter from property filters
Closes #4009
was never implemented to work with SAV_Database... until now!
2023-09-18 20:13:23 -07:00
Manu
ce9a3d0104
RibbonExtensions public accessors (#4006) 2023-09-18 15:56:53 -07:00
Eelen
b68899e0dd
Update CHS CHT Translations (#4005)
Co-authored-by: Professor Dirty <103500840+wubinwww@users.noreply.github.com>
2023-09-18 10:01:54 -07:00
Lusamine
11145c2432 Label SV BC outbreak blocks 2023-09-18 09:37:07 -05:00
sora10pls
af8194f241 Add unreleased Tracksuit fashion items 2023-09-18 08:44:43 -04:00
Kurt
685e4cfd95 Add new Raid display requirements (3/4) 2023-09-17 20:23:00 -07:00
Kurt
9ce715f70c Copy phione disallowed bred ball rule from BDSP
only pokeball
2023-09-17 19:49:53 -07:00
Kurt
96c0e199e1 Add Kitakami Raid edits, new dex block edits
Closes #3992

Co-Authored-By: Lusamine <30205550+Lusamine@users.noreply.github.com>
Co-Authored-By: Matt <17801814+sora10pls@users.noreply.github.com>
2023-09-17 19:49:26 -07:00
Kurt
05f9073f31 Add API for home tracker state checking
Correct poltchageist masterpiece comments
swap order of Ogerpon (more likely to be Level 70)
Add methods to filter gameversions by context
add 3 more sizes
2023-09-16 16:53:06 -07:00
sora10pls
e9fdfae6d0 Update README.md 2023-09-16 19:17:31 -04:00
sora10pls
ed6674791a Fix Ogerpon-5 and Ogerpon-6 ability text 2023-09-16 15:40:09 -04:00
sora10pls
5c1d165f2d Enhance SV fashion unlocker for DLC 1
No point in having a billion categorizations for fashion, makes things too bloated as more items get introduced.
Add in DLC items, fix giving opposite-gender starting uniforms.
Closes #3994
2023-09-16 15:34:21 -04:00
ReignOfComputer
8084102eb9
Additional G9 DLC Save Sizes (#3995)
* Additional G9 DLC Save Sizes

* Additional Size and Adjusted Diff

* Another Additional Size
2023-09-16 09:40:09 -07:00
Kurt
80eafc5b40 Check fixed nature when fixed for static encounters
Closes #3996
2023-09-16 09:39:50 -07:00
Kurt
71c4b1a97b Update SaveUtil.cs 2023-09-16 00:47:47 -07:00
Kurt
7703576088 Update 23.09.16 - Teal Mask
Co-Authored-By: Matt <17801814+sora10pls@users.noreply.github.com>
Co-Authored-By: SciresM <8676005+SciresM@users.noreply.github.com>
Co-Authored-By: Lusamine <30205550+Lusamine@users.noreply.github.com>
2023-09-15 23:36:21 -07:00
Kurt
c09a4646a1 Fix incorrect move reference (Aipom evolve) 2023-09-15 20:35:02 -07:00
Jonathan Herbert
988a0b5718
Fix MsgMysteryGiftSlotAlternate Error In MessageStrings_en.txt (#3987)
Close #3986
2023-09-12 18:37:11 -07:00
Kurt
e735d92218 Generator Tests: Add tests for LGPE->SV
Quite a few broken ctor's in the test result output pane, but these will be addressed later (Teal Mask DLC just dropped).
2023-09-12 18:15:44 -07:00
Kurt
9f340b592b Evolve-With-Move: Refactor to be memory-like
Evolving with a move requires once knowing the move, should be a much better approximation solution here. Ideally we check in specific contexts as Pokemon may learn their requisite moves after evolution, but this should catch most for now.
2023-09-12 18:14:22 -07:00
Kurt
21725aa1c0 Misc enc template ctor fixes
Static8: Use flawless IVs
Slot8b: set safari ball when required
GBRestrictions: use struct to bypass boxing allocation
2023-09-12 18:12:44 -07:00
Kurt
3a28eee2e9 Remove old GeneratePKMs api methods
Use the GenerateEncounters methods instead, just a single line extra for each consumer.

PokemonGenerationReturnsLegalPokemon now works for all Gen1-7 encounters->PKM (except for Korean WC6 Arceus form mismatch), still disabled from auto-test due to it taking about a minute to generate everything. Future updates can add special methods for SW/SH, PLA, BD/SP, and S/V if the species/form exists in the game.
2023-09-11 18:20:22 -07:00
Kurt
fe7012c3eb Allow hatched Gift2 eggs to have met info
Closes #3985
2023-09-11 01:00:24 -07:00
Kurt
e3f8091971 Misc fixes
Ignore slot match for Swarm3
Honey Tree only yield possible slots via TID/SID
Pick min level for EncounterTrade1
Set nickname for CXD Elekid trade
Set held item for Pt Giratina
Set egg location for Ranch gift hatched egg
Set hidden ability flag for N's Darmanitan
Set XY Vivillon form based on generated geolocation
Force evolve Trade species if encounter requires it
Correctly recognize pikachu-colored-pichu Gen4 gift PID type
Set hatched manaphy egg's met location & date
2023-09-11 00:54:40 -07:00
Kurt
b536388d0d Misc tweaks
Add xmldoc
Simplify some casts
Demote priority of Gen2 event yielding
Remove old EncounterMatchUtil code
Repoint DateTime.Now to console specific date provider stubs
2023-09-10 21:17:47 -07:00
Kai
c7aa497e73
Add Cetitan Eligible Date Range (#3981)
Update wc9.pkl
Closes #3982
Closes #3984
2023-09-09 21:02:58 -07:00
Kurt
19acce85c8 Update LegalityAnalysis.cs 2023-09-07 12:39:13 -07:00
Kurt
ec4eee6047 Update Encounters8.cs 2023-09-06 21:29:27 -07:00
Kurt
7fcde8a447 More Gen1/2 considerations for tree pruning
Need to prune tree for template according to the correct generation (disallow baby pre-evolutions for Gen1)
Also disallow Evolution moves for Gen2 if the evo is the enc species.
2023-09-04 20:34:46 -07:00
Kurt
45216dfa1b Set template version as byte
Be consistent with other templates

Also sets OT details for Static8a
Closes #3979
2023-09-04 18:50:35 -07:00
Kurt
4e0f23cdc0 Handle get moveset for Gen1 Red Trade Jynx
Need to not devolve into Smoochum as the rules are restrictive: no relearn, but we want to allow Jynx to "permit" moves it learns from levels 0-29.
2023-09-04 18:23:10 -07:00
Kurt
91d4ba5f03 More template tweaks
VC1/2: templates with non-hidden return correct ability permissions
PLA set met date (oops)
2023-09-04 14:57:39 -07:00
Kurt
e0e6c51498 Set template moves if provided 2023-09-04 10:15:20 -07:00
Kurt
2b55232998 Add Vespiquen as combee-genderbranch
Remove allocation in SetRandomChainShinyPID

Add Salazzle for completeness
2023-09-04 08:53:06 -07:00
Kurt
3fdfd29e4b Misc tweaks
Gift1: use IVs if required
Gift2: Enforce 0 TimeOfDay
Egg9: Require Egg Location to match encounter
PGT: keep requested gender in the event of antishiny
PGT: never yield Korean Ranger Manaphy for Format4

Simplify ushort range checks
Rename `EReader` bool to `IsEReader`
Extract Gender/Nature fetch for unfixed encounters
Remove Unown PIDIV branch when template has no Unown
Remove unnecessary loop for Pokewalker PID creation
2023-09-04 00:50:26 -07:00
Kurt
63c027879f Account for Snorunt->Froslass Pokewalker PIDIV 2023-09-02 16:46:23 -07:00
Kurt
e1c8f52a07 Misc tweaks
Set met dates for gen9 static encounters (oops)
Check interface implementations for HasProperty
2023-09-02 15:53:51 -07:00
Kurt
0f7e623842 Use Gen8 toxel gift IVs in ctor
ty anubis
2023-09-02 09:30:43 -07:00
Kurt
93ddee4be4 Skip dmax level check for rank 2023-09-01 22:07:25 -07:00
sora10pls
3346e2f895 Mewtwo the Unrivaled 2023-08-31 20:05:01 -04:00
Kurt
1b22453870 IsIVsCompatible -> IsIVsCompatibleSpeedLast
Fix implementation as all uses are checking IVs Speed Last, but the method wasn't thinking like that.

Improve base ROM raids for SWSH to ensure the Rank roll matches the output.
2023-08-30 19:08:00 -07:00
sora10pls
2f0dd1e94f Pokémon GO: Adventures Abound
Permit Great Ball / Ultra Ball for Sprigatito, Fuecoco, and Quaxly
2023-08-30 17:29:50 -04:00
santacrab2
9e230b19f8
Assign fixed nature/Genders to EncounterTrade9 instead of random (#3971) 2023-08-30 07:49:10 -07:00
Kurt
0aa4a15851 Fix cgear skin import loop
byte 255->0 overflow infinite loop if too many tiles are unique
https://projectpokemon.org/home/forums/topic/63828-c-gear-skin-editor-crash/?do=findComment&comment=285181
2023-08-30 04:44:40 -07:00
Kurt
78e7536459 Handle setting egg flag in cxd saves
No longer throws an exception due to EncounterInvalid having an invalid version ID (fails to fetch personal/learnset).
2023-08-29 22:47:07 -07:00
Kurt
978bcfa56f Misc enc template tweaks
Skip string fetch for Trade1
Set gender for Trade8
Add record to wrap Gen8 raid generate params, return & use bool for filtering results, fallback to unrestricted if none succeed
Fix gender comparison for Entree5 slot
Simplify gen2 ResetKey fetch (byte sum of Money/ID/OT(used)
Closes #3970 by filtering gender inside the method like Encounter9RNG with an early return
Adds IV filtering inside the method with an early return like Encounter9RNG
2023-08-29 22:22:23 -07:00
Kurt
5222fdc6ad Add more tagging for IFixedGender, IFixedNature 2023-08-28 21:09:52 -07:00
Kurt
fd02b97ce1 Misc tweaks
Allow nickname->species with span
add ConstantExpected annotations
Correct Gen8b ability set calling CommonEdits instead of direct setter
Slightly better perf on gen5/gen8+ location fetch
Misc pkm ctor fixes for b2w2 trade & wc3 eggs
wurmple evo now has an enum to be more explicit
no recursion for gen5 generator
fix showdown line length check allowing 86 instead of 80
2023-08-27 23:05:50 -07:00
Alexander3a
afc29fb203
Fixed span for PokeDexEntry9SV being too large (#3969)
* Fixed span for PokeDexEntry9SV being too large

Prevents .Clear() from wiping data it shouldnt
2023-08-25 10:08:43 -07:00
Kurt
00bb8ec7de Fix misc matching regressions
Gen4/8b Muchlax tree (&& -> ||)
Colo Umbreon w/ Bite (IMoveset)
WC3 unhatched -> use MysteryGift validator

Remove wurmple defer check for Static3 (no wurmple encounters)
Remove unnecessary spaces for colo enc definitions
2023-08-23 22:03:36 -07:00
Kurt
a7f4d572e7 Set Japanese console region for pk1/pk2->pk7
Also simplifies some logic to reuse methods / use more straightforward. No longer fetches PersonalInfo twice
2023-08-23 20:47:38 -07:00
Kurt
4e13a0261d Update 23.08.23 2023-08-22 21:27:29 -07:00
Kurt
108708dcda Add gsc union cave B2F shore fishing slots
6180be355e/data/maps/maps.asm (L115-L117)

https://projectpokemon.org/home/forums/topic/57375-pkhex-new-update-legality-errors-contribution-page/?do=findComment&comment=285012

Also handle the SID==0 fishy for VC (which is fine)
2023-08-21 20:09:35 -07:00
Kurt
50608c1b15 Misc encounter tweaks (extract/pass personal)
Removes doublefetch for personal, maybe faster ctor.
2023-08-21 20:07:53 -07:00
Kurt
8dd9e0803d Update dex minmax read offsets
Closes #3943

Also exposes the boolean flag in between the height/weight tuple.
Pretty sure this struct has an unused second half, and is just {byte, bool, byte}[2].
2023-08-20 13:48:01 -07:00
Kurt
86459812c1 Misc tweaks
Ensure GO IVs are respecting the min floor
Add some comments, reuse PersonalInfo reference instead of refetch
Fix Hoopa Unbound SlotWrite changing to 3
2023-08-20 00:31:54 -07:00
Kurt
2777a3e039 Tag some encounter descriptions with more info 2023-08-19 16:43:05 -07:00
santacrab2
aef2cc7066
Add missing fateful setters (#3967) 2023-08-19 15:57:07 -07:00
Kurt
f7d66db7e2 Misc tweaks
Check HT move memories in Gen9 appropriately for deferral cases
Add missing ID32 set for GO8->PKM
Slight pre-work for Mightiest Mark Mew method signatures
2023-08-19 11:13:20 -07:00
sora10pls
910415ea8d Clean up Encounters9 encounter definitions
Pad species IDs to 4 digits, maintain a consistent order of fields
Also re-order trades to match the order seen in the ROM (more to be added in DLC)
2023-08-19 11:10:21 -04:00
Kurt
15faf8197a Update EncounterStatic4.cs 2023-08-18 11:48:49 -07:00
Kurt
0b2d167465 Update EncounterStatic8a.cs 2023-08-18 08:38:00 -07:00
Kurt
07042233be Allow US/UM new moves in Relearn for S/M 2023-08-17 17:43:11 -07:00
Kurt
213234b4d2 Fix USUM max move ID usage for HOME learn
Closes #3966
Closes #3954 (original)
2023-08-17 17:36:39 -07:00
sora10pls
7dc40735f3 Mighty Mewtwo: A Show of Supporters! 2023-08-17 20:04:33 -04:00
Kurt
c4b8cab9cd Set form when appropriate 2023-08-17 11:23:22 -07:00
Kurt
392dfbbb4e Gen4 trade gift nickname 2023-08-17 10:23:29 -07:00
Kurt
ff3081408a Change Gen4 roamer default met tile
the groundtile bit permission was causing the Route 201 to set Water which isn't possible to encounter them on, so use the lowest bit instead of highest bit
2023-08-17 10:15:21 -07:00
Kurt
bfc24dd5c2 Update EncounterTrade4RanchGift.cs 2023-08-17 08:26:50 -07:00
Kurt
9855382b0a Misc encounter tweaks, annotations
EncounterEgg now indicates FixedBall(Poke) for Gen2-5
EncounterSlot2 now indicates Form Random for Gen2 Unown, database
EncounterStatic3 now generates FRLG Gen2 Roamers with correct location
EncounterCriteria now used more heavily for requested IVs
EncounterPossible3 now correctly skips Eggs if eggs are not requested
EncounterGift3Colo now generates Japanese Bonus disk gifts only in Japanese, doesn't validate non-Japanese
2023-08-17 00:07:54 -07:00
santacrab2
57e23cc860
- misc template -> pk tweaks (#3964)
* - misc template -> pk tweaks
2023-08-16 18:57:44 -07:00
Kurt
5637b1775d Fix psk tile choice encode/decode, XY flip
Closes #3953
Add in more annotations & clean up things slightly
2023-08-16 18:51:32 -07:00
Kurt
6a373ee5db Tweak pokerus load indication
Closes #3957
2023-08-15 17:47:12 -07:00
Kurt
0a9300874b Check current level on GB Era event gift eggs
Closes #3963
2023-08-15 16:09:08 -07:00
BlackShark
218a29a16a
Fixed FRLGE Enigma Berry size (#3962) 2023-08-14 23:23:39 -07:00
Buggz Evol
e1499398f6
Add Eduardo Gastrodon eligible date (#3959) 2023-08-14 20:13:46 -07:00
Kurt
c6a961bda6
Add Xoroshiro128Plus Solver, SW/SH Raid verification (#3961)
Due to how the game generates the Pokémon data, the first two (or three) RNG calls are used to set the 32-bit `EncryptionConstant` and `PID`. With 2x 32-bit and 1x 64-bit values, we can algorithmically reverse the movement & manipulation of bits to recover the initial seed. Notably, certain bits of the initial state are not captured by our first two (or three) outputs, so we must brute-force guess at the initial state, and verify the RNG's output yields the expected values.

**With the ability for real-time Xoroshiro128+ seed reversal, we can now validate RNG correlations for SW/SH raid encounters natively within the program.** For now, the legality fail error message is extremely vague and any validated seed won't be "remembered" for the Legality Parse like other RNG methods. These seeds are 64bit, while every other "remembered" `PID/IV` seed-info is 32-bit.

Co-Authored-By: SciresM <8676005+SciresM@users.noreply.github.com>
2023-08-14 20:01:38 -07:00
Kurt
534dc02154 Misc enc template -> pkm fixes 2023-08-13 17:23:16 -07:00
Kurt
3afcfcdfa3 Add setters for Dynamax/Gmax static8 2023-08-12 16:45:43 -07:00
Kurt
f632aedd15
Encounter Templates: Searching and Creating (#3955)
We implement simple state machine iterators to iterate through every split type encounter array, and more finely control the path we iterate through. And, by using generics, we can have the compiler generate optimized code to avoid virtual calls.

In addition to this, we shift away from the big-5 encounter types and not inherit from an abstract class. This allows for creating a PK* of a specific type and directly writing properties (no virtual calls). Plus we can now fine-tune each encounter type to call specific code, and not have to worry about future game encounter types bothering the generation routines.
2023-08-12 16:01:16 -07:00
Eelen
18812550e8
Update CHS Translations (#3956)
Update CHS Translations

Co-authored-by: Professor Dirty <103500840+wubinwww@users.noreply.github.com>
2023-08-12 11:35:42 -07:00
Momonopopotee
6d098cc848
Update gen 2 event flags and PLA blocks (#3944) 2023-08-09 00:44:34 -07:00
Eelen
89eff1fe19
Add Foongus and Amoonguss flags (#3952)
Add Foongus and Amoongus flags

Fix translation errors

Translation New flags in CHS

Co-authored-by: Professor Dirty <103500840+wubinwww@users.noreply.github.com>
2023-08-09 00:43:59 -07:00
Kurt
d647f15f23 Fix indexing check for tile rotation
Closes #3950
Does not resolve #3953 , separate issue as caused by the April refactor. Need to debug old & new and see where the data is being copied wrong
2023-08-09 00:43:29 -07:00
sora10pls
a483bf9a24 Mighty Mewtwo: Prepare the Offense! 2023-08-08 20:07:21 -04:00
sora10pls
3e13b1bc29 Add Tatsugiri/Mew eligible date ranges 2023-08-08 17:58:12 -04:00
alvv-z
498391ea04
Add battle subway trophy flags (BW) (#3949)
* Add battle subway trophy flags

* Spelling: posess -> possess
2023-08-02 14:03:21 -04:00
sora10pls
96fa89188a Add latest distribution raid data 🦍 2023-07-27 20:03:28 -04:00
Manu
4ca9f2a905
Add Nontaro's Shiny Grimmsnarl Server Date (#3946)
* Add Nontaro's Shiny Grimmsnarl Server Date

* Corrected end date typo
2023-07-27 07:37:46 -04:00
Kurt
22aaea63df Update LearnGroupHOME.cs 2023-07-21 19:54:19 -07:00
Lusamine
92d65dc6a4 Add legal date range for Dark Tera Charizard 2023-07-21 21:05:58 -05:00
Lusamine
cf4de45c03 BDSP Valley Windworks Drifloon is 3 IV guaranteed 2023-07-21 21:05:06 -05:00
Kurt
c596804578 Minor clean
Inline logic for Meowstic form branch evo
Pass levelup byte value directly instead of recompute
With EvolutionSet all shared, reorder the constructor so the primary ctor doesn't need manually ordered properties (8 byte total struct size)
2023-07-21 15:19:12 -07:00
Kurt
171ee0d222 Misc tweaks 2023-07-21 13:41:19 -07:00
Kurt
c5d35fbce5 Handle Volt Tackle for HOME sharing GetAllMoves 2023-07-21 12:24:31 -07:00
Kurt
75ec6ca38d Use PKH as PK7->PK8 route
Slightly less efficient as we need an intermediary object, but this mimics the official implementation and will catch any future modifications HOME does (like the current Height/Weight rerolling from 0).
2023-07-21 12:04:11 -07:00
Kurt
e3b62cf80d Update EncounterMovesetGenerator.cs 2023-07-21 11:48:29 -07:00
Kurt
6c6d02fad2 Fix oopsie
partially reverted change forgot to undo adding these
2023-07-16 20:42:51 -07:00
Kurt
06720dec83 FormArg handling for Hoopa visiting SV 2023-07-15 14:17:03 -07:00
Kurt
8c5773969a Add HOME hoopa move share, add form specific check
Hoopa cannot know both moves due to form reversion
Kyurem cannot know both moves due to form reversion
Kyurem cannot know Scary Face when fused in past games

Add dexnav for AtAnyTime/HOME lookback when original moveset deleted (no longer in local relearn moves)
BDSP Underground encounters w/ egg moves can be shared by other means
2023-07-15 13:58:41 -07:00
Kurt
18fd790657 Minor perf tweaks
Relocate checksum adders to Checksums class, improve performance by eliminating slice calls
Improve HOME sharing for GO8 and IMoveset encounters: actually sanity check GO8, and skip other non-PK7/PB7 cases.
2023-07-15 11:22:48 -07:00
Lusamine
2aef48172e Allow SV Mini & Jumbo Pikachu to be valid until Aug 1 2023-07-14 15:25:02 -05:00
Kurt
ee9ae63c22 Misc tweaks
Move enum -> ushort instead of int
Redo handling of HOME Volt Tackle (disallow on SWSH Cap Pikachu)
Pass spans instead of strings to use span methods
Reset encounter filters on early abort
2023-07-13 22:18:34 -07:00
Kurt
b212ef42b5 Add HOME rejuvenations for SV and SWSH
When we lack data for HOME transfers on their original data, we must fabricate something instead of defaulting to whatever the HOME mutation logic gives us. Stuff like the original Relearn moves, etc.

Reset tera types and relearn for SV
Reset relearn for BDSP, SWSH
2023-07-12 21:49:16 -07:00
Kurt
3a88190613 Unify EvolutionSet deserialization
Reformat all pickles to the same serialization format (latest)
(format ignores all empty evo entries, to skip allocation of dead entries)
346KB -> 60.3KB, -200 lines of code
2023-07-12 19:09:23 -07:00
Kurt
292ea5ba44 More encountertrade workarounds
probably not entirely correct, but better than flagging incorrectly
2023-07-11 16:25:05 -07:00
Kurt
75e7965803 Update GameDataPK8.cs 2023-07-11 12:46:49 -07:00
Kurt
a49508b354 Check scale copy in non-SV formats
Rather than ignoring it
2023-07-11 11:05:55 -07:00
Kurt
4f560c3ba6 Indicate moves currently known in TechRecordEditor 2023-07-11 09:12:24 -07:00
Kurt
6a5dc5caa5 Revise baby species-form check for SWSH/SV
Evolutions fail to reverse Alolan Raichu in these local contexts, so revise the sanity check to use Personal instead since seedpoke data is available.
2023-07-11 08:52:46 -07:00
Kurt
4f014a9275 Gen4: Load altform tutor bits
They can be different between alt forms.
2023-07-10 19:17:09 -07:00
Kurt
0876c8044a Set TR flags for all evos, indicate yellow 2023-07-10 18:41:56 -07:00
Kurt
2e0a2ed7eb Adjust head evo max level for 345 vc7 transfers
https://projectpokemon.org/home/forums/topic/63635-pkhex-230709-bugs/
2023-07-09 21:06:47 -07:00
Kurt
478413e8cd Discard Fairy type Arceus when reversing
https://projectpokemon.org/home/forums/topic/63635-pkhex-230709-bugs/
2023-07-09 20:22:01 -07:00
Kurt
7fb5dc2fde Update go pickles, add fr bdsp chatot goof
Nintendo + ILCA = no nicknames allowed ;)
While we're here, streamline Gen4/5 evo pickles too -- trim out unused bytes, no longer need to scan for array length (perf). Adjust method IDs for gen4 so we can use the same code for gen4 & gen5.
2023-07-09 19:59:37 -07:00
Kurt
26cabd022b Update 23.07.09 2023-07-09 14:10:40 -07:00
Zazsona
b340e2327a
Crystal (International) Checksum 2 Fix (#3939)
* Correct invalid OverallChecksumPosition2 offset for International Crystal

* Update SAV2 Checksum validation to check against both Checksum values
2023-07-08 20:21:43 -07:00
Kurt
2ea6ee4e10 Misc fixes
Add sound for adding all XY fashion items (was silent)

Fix Gen7 Resort PKM loading (4 bytes extra per slot)
Fix Gen6 HallOfFame TID/SID validation dropping leading zeroes
Fix Gen6 Best Friends ribbon validation
Treat GO8 as server date restricted, give detailed message outside window
HasTracker => must have HT name
2023-07-08 20:00:41 -07:00
Kurt
04e7baaeee Fix Stat Editor randomize buttons centering
Hiding and Showing doesn't trigger an update to the FlowLayoutPanel's width; force a layout to happen so the size updates prior to re-centering controls. Only move if it is a different X coordinate (perf?)
2023-07-08 19:29:46 -07:00
Kurt
b5f0296fe6 Skip yielding GO8+ encounters if PB7
No backwards transfer to LGP/E, and this behavior is only encountered if you turn off specific filtering settings as the generator filters them out afterwards.
2023-07-08 18:28:09 -07:00
Kurt
cbb5ab0971 Fix Memory editor Gen7 editor of Geolocation
set up binding for both comboboxes before setting data source
skip setting up if entity does not expose these
2023-07-08 18:21:17 -07:00
sora10pls
7bfaad8e20 Update tera type icons
now sourced from HOME assets, which have far less compression
2023-07-08 19:03:33 -04:00
Kurt
a0fb01db23 More evo path restrictions
Early abort for bdsp spinda nincada
Skip caching visitation for nonvisit instead of post-work zeroing
Fix slowpoke-galar evo banning
2023-07-08 14:51:45 -07:00
Kurt
6d4cd60461 More fixes/improvements
Improve cctor times for Breeding; direct calls for splitbreed checks; inlined binary searches via generated IL instead of hashset
Fix permitting alpha ribbon on non-alphas in Gen8/8a/8b, disallow Gen8a/8b ribbons for Gen7 Alolan Raichu
Improve some IL generation of EvoChain logic
Add xmldoc for new evotree additions
2023-07-08 12:46:46 -07:00
Kurt
79729de90c Misc fixes
ty matt
2023-07-08 08:40:57 -07:00
Kurt
1b2efef864 Add GO masterball check
go ahead and waste them
2023-07-07 21:29:51 -07:00
Kurt
014cd8c738 Ban voltorb-1 ball inheritance 2023-07-07 21:20:15 -07:00
Kurt
c52b49de08 Update BallVerifier.cs 2023-07-07 20:57:01 -07:00
Kurt
164d517ef1 Allow move crossover from GO initial movesets
GO imports that are created with their PK7 and PB7 movesets can bleed from their initial moves.

Example: Muk-Alola level 20 with Minimize can bleed into SV at level 20 (below the normal learn at level 21).
2023-07-07 19:32:38 -07:00
Kurt
00b3e111f8 Extract some CGear logic
More span based interaction, less allocation.
2023-07-06 22:20:25 -07:00
Kurt
158b952265 Fix Box/Slot filter comparisons for gr/gre/lt/lte
Closes #3934
2023-07-06 20:02:07 -07:00
Kurt
41693b0142 Misc fixes 2023-07-06 19:41:27 -07:00
sora10pls
30df059544 Add raid data for raids that got delayed by 2 months 2023-07-06 20:04:09 -04:00
Kurt
0b9ec529d5 Update LearnSource2GS.cs 2023-07-05 22:08:03 -07:00
Kurt
dcc0e79435
Evotree: Evolution Traversal Enhancements (#3936)
Like move validation, evolutions are the earliest thing we wish to traverse when determining what encounters may have originated the current Pokémon. To determine the permitted species-form-levels a Pokémon could originate with, we must devolve a Pokémon by traveling down-generation to origin. Once we have an encounter, we can then evolve it to the current species, traversing upwards from origin to the current format.
2023-07-05 21:14:09 -07:00
Lusamine
e02b33ef2c Add Wedgehurst as valid general location to gen 8 sign memory 2023-07-05 19:40:17 -05:00
sora10pls
cc7b500566 Add legal date range for Paul Chua's Arcanine
Closes #3931
2023-06-30 19:39:00 -04:00
sora10pls
e7d60a88de Add latest distribution raid data 🪙 2023-06-21 19:31:51 -04:00
James Park
0ba64a8da2
Pokemon location tooltip fix for PLA + miscellaneous comment fixes (#3920)
* Fix Pokemon tooltip for LA incorrectly referring to SwSh location table

* Fixes to comments referring to incorrect generation/version/game

---------

Co-authored-by: James Park <5295838+pencilethics@users.noreply.github.com>
2023-06-20 10:36:28 -07:00
Eelen
84694c63df
Update CHS translations (#3915)
Update CHS translations

Co-authored-by: Leo <103500840+wubinwww@users.noreply.github.com>
2023-06-18 23:43:21 -07:00
sora10pls
be210ceab2 Add 정원석's Gastrodon Server Date
Closes #3919

Co-Authored-By: Sakura <106369723+xiaolong11123@users.noreply.github.com>
2023-06-17 08:22:49 -04:00
Zazsona
38c7e755db
Correct SAV4Ranch's SetChecksums() passing the wrong pkEnd offset argument to UpdateMetadata() (#3918) 2023-06-16 15:45:09 -04:00
sora10pls
02eabfb10c Add latest distribution raid data (for real this time) 🐘
it took them a month but they did it
2023-06-15 20:03:19 -04:00
Kurt
e06368ea1d Don't add tickets on full pouch
-1 for first empty index => full
clean up initial sanity checks
https://projectpokemon.org/home/forums/topic/63498-pkhex-230603/?do=findComment&comment=283990
2023-06-13 10:18:58 -07:00
Kurt
6964d48adb Move location/capture to Encount
Shared for both roamers
https://projectpokemon.org/home/forums/topic/63405-suggestionbug-gen-5-roamers-block-data-only-shows-info-on-roaming-thundurus/?do=findComment&comment=283912
2023-06-11 11:12:34 -07:00
Kurt
8cc1220056 Minor clean 2023-06-11 10:38:51 -07:00
Manu
147eda0c74
Add そらみつ's Bronzong Server Date (#3911) 2023-06-11 13:16:15 -04:00
abcboy101
b0da14b71e
Add Geonet/Unity Tower editor (#3909)
Adds a basic editor for recorded Geonet/Unity Tower locations for the Gen 4/5 games, building on this [post by Danius88](https://projectpokemon.org/home/forums/topic/62055-bw-b2w2-unity-tower-geonet-and-passerby-research/).
So far, I've implemented buttons that set all locations (including unused ones), set all legal locations, and clear all recorded locations; and checkboxes to toggle whether the whole globe is visible (used in Japanese games) and whether the ferry to Unity Tower is unlocked.

Haven't implemented any UI for editing the status of individual locations, since I'm not sure how to lay it out.
Also haven't implemented anything related to how the data of the other players in Unity Tower is stored.
2023-06-11 09:38:24 -07:00
sora10pls
acba041ef8 Reorganize WC8 HOME gifts, fix Jewel of Life date ranges
tfw time zones
2023-06-11 12:24:07 -04:00
Manu
ee2c8945d3
Handle Rev 2 Home WC8 cards (#3910) 2023-06-11 09:03:39 -07:00
Kurt
11a4846b9e lower evolution stage and higher level
why did I do <= instead of <
the world may never know
2023-06-10 21:40:30 -07:00
Kurt
cd22cc64ce Restrict old WC8 HOME gifts for 0 EC
Restores strict checks for stuff prior to May 30th that were removed via 36a696e446

Add sim cards for Gen9 HOME starters

Allow tracker-less transfer matching for WC8

Helpful for generating bots who can't assign a legitimate tracker for direct-in-Gen9 injects.
2023-06-10 21:40:30 -07:00
Kurt
36a696e446 Misc tweaks
Prep for WC8 HOME Gifts having random PID/EC
2023-06-09 01:59:04 -07:00
Kurt
05be679d9a LearnPossible: Add len check for relearn move
Out of bounds relearn move IDs weren't sanity checked; now they are.
Closes #3906
2023-06-07 18:11:08 -07:00
Eelen
f94c4f4420
Update CHS Translations (#3903)
Update CHS Translations

Co-authored-by: Leo <103500840+wubinwww@users.noreply.github.com>
2023-06-04 23:43:09 -07:00
Kurt
5c21e4c73b Permit non-0 h/w on WC8 HOME gifts
Yay home rerolling these when transferred back in.
Ex: magearna gift
2023-06-03 21:03:17 -07:00
Kurt
35e3608dad PKH: Fix ribbon copy invert 2023-06-03 20:59:13 -07:00
Chris
1f68ca6fb6
add pichu wc9 dist. date (#3900)
* add pichu wc9 dist. dates

* Update EncounterServerDate.cs
2023-06-03 20:40:45 -07:00
Kurt
fa8e65f9e5 Extract Characteristic calc to static class
Reused logic, easier unit testing, better performance.
Old method would do max of 6 properties (that each fetch 32bits and bitshift themselves); now we just fetch once and shift calc accordingly.
2023-06-03 18:52:05 -07:00
Kurt
e3d8d167be Update 23.06.03 2023-06-03 18:29:07 -07:00
Kurt
1174e354b1
Initial support for Pokémon HOME 3.0.0 (#3899)
* Heavily rewrites the `PKH` abstractions.
* Uses HOME's core-side classes as the transfer middlemen instead of direct A->B transfers.
* Revises logic to account for most of HOME's quirks (scale/height copying, safe refuge PLA)

Future revisions hinge on better handling of evotree (need better metadata about existing as specific evolutions in each game).

---------

Co-authored-by: sora10pls <17801814+sora10pls@users.noreply.github.com>
Co-authored-by: Lusamine <30205550+Lusamine@users.noreply.github.com>
2023-06-03 18:19:16 -07:00
sora10pls
92258f6755 Label B2W2 available fossil event const 2023-05-29 09:59:14 -04:00
sora10pls
72ab75410b Add custom SV sprites for Partner Pikachu/Eevee 2023-05-29 09:58:17 -04:00
Kurt
941a8fa13e Update SAV_Inventory.cs
Closes #3892
2023-05-28 02:01:36 -07:00
James Park
a8154cfa49
Fix Griseous Orb being incorrectly removed from Pt inventory (#3891)
* Fix Griseous Orb being incorrectly removed from Pt inventory
2023-05-28 01:05:44 -07:00
Pasquale Nardiello
3ca16b435c
[SV] Give All Clothing button (#3876)
* Mapped Fashion items for S/V and added injection logic
* Added 'Unlock Clothes' button in Trainer Editor's misc section alongside ComboBox to select type to add all.
2023-05-26 10:24:27 -07:00
sora10pls
6c3a8b8377 Update MoveInfo5.cs
Was missing Fusion Bolt, oops
2023-05-26 09:02:17 -04:00
Kurt
ae07937de6 Enhance TechRecord editor
Closes #3888
Closes #3879

Adds type sprite indication and green if the flag is legal to be set.
Allow sort by has-flag, index, type, and move name.
Sorting by Type will sort by type->valid->name for a nicely ordered view.
2023-05-25 20:26:58 -07:00
sora10pls
61fbdfe5f6 GO: Separate Mythical encounters depending on HUD visibility
Also add in details for Shadow Raids now that they're known
2023-05-23 17:18:29 -04:00
sora10pls
2c0f4d0e91 Update ItemStorage7SM.cs 2023-05-20 15:17:41 -04:00
sora10pls
302f0593e6 RAID: Shadow Legends
Need to differentiate these from regular raid encounters so that they can be excluded from LGPE GO encounter data (purified can only be sent to HOME).
2023-05-20 11:56:19 -04:00
Kurt
b08843fcc5 Allow BDSP Slate/Shard to be given via "Give All"
https://projectpokemon.org/home/forums/topic/63429-bdsp-slate/

The "Give All" cheat will skip adding unobtainable items.
Since the BDSP-exclusive Treasure (slate/shard) are unable to held, they were also skipped. Revise the logic to allow them to be added via "Give All", but still disallowed as held items (separate arrays needed).

Also condense the logic for BCAT-Dmax crystal IDs since they're all sequential. Just iterate a range instead of a span.
2023-05-18 19:30:39 -07:00
sora10pls
1de4e4eaab Add latest distribution raid data 🐘 2023-05-18 20:04:39 -04:00
Kurt
ad739f1db7 Handle mutliple HM removal & downshift reorder 2023-05-17 17:20:39 -07:00
Kurt
be88ec387b More span for PKH rebuild 2023-05-17 16:57:13 -07:00
Kurt
53348bd646 Add TM91/92 + HM01-08
Oops
Closes #3883
2023-05-17 16:55:53 -07:00
Kurt
5acae160c6 Fix 2byte misalign on PH1
Closes #3882
2023-05-14 18:48:29 -07:00
sora10pls
2bdee8c87d Permit Unknown Dungeon/Secret Base hatch locations 2023-05-14 15:16:31 -04:00
Kurt
3b697a5da1 Update G8PKM.cs 2023-05-13 12:41:50 -07:00
Kurt
d2bed713e4 Change Totem sprite indication to orange aura
Closes #3880

Co-Authored-By: Jonathan Herbert <3344332+foohyfooh@users.noreply.github.com>
2023-05-12 14:57:32 -07:00
Kurt
1ed6efd465 Remove HighDpiMode.PerMonitorV2 2023-05-12 08:26:42 -07:00
Lusamine
e77b39635b Precisely realign Gen 9 Trainer Editor GUI elements 2023-05-12 01:26:32 -05:00
Kurt
401c545ca8 Update 23.05.11 2023-05-11 21:44:36 -07:00
Kurt
ad7e05d66e Update translatables 2023-05-11 21:08:09 -07:00
Kurt
34f4df3d4b Misc tweaks
Closes #3878

Pictures are Zoom instead of AutoSize so there's no need to downscale or dynamically position the smaller 2.
Fix my oopsie on docked tabs (hid the Cancel/Save button!), change sizing.
2023-05-11 20:56:45 -07:00
sora10pls
54b663ae6f Add latest distribution raid data 🪨 2023-05-11 20:04:24 -04:00
sora10pls
caf4a103dd GO ahead and add latest pickles 🥒 2023-05-11 16:07:56 -04:00
Pasquale Nardiello
d915bb6039
Img visualizer (#3875)
* Added Boxes to view Current Trainer photo and Trainer Icons

* Adjusted Images Visualization

* Updated Pictures decompression to final DXT1 version
2023-05-10 23:50:50 -07:00
sora10pls
1e985d6b26 Expose Gimmighoul form argument in editing tabs 2023-05-10 21:05:43 -07:00
Kurt
8d5672d9c4 Remove date restriction on HOME WC8 shiny gifts
Stuff back in 2020 could be shiny too (as TSV=0 was possible), so the antishiny mechanism existed from the start.
2023-05-10 21:05:43 -07:00
Kurt
8a2f1d56c2 Misc tweaks 2023-05-09 20:17:15 -07:00
Kurt
709da45449 Add synthesis for GameDataPK9->GameDataPK8
Code duplication -> identified the root pattern. Looks quite simple now; hopefully this is the eventual pattern.
2023-05-09 20:00:13 -07:00
Kurt
e265c18fa2 Span-ify HOME data structures
Much easier to read.
Optionals no longer store the 3-byte header in their memory reference.
If they were smart, they could track per-game visitation date/time in a future header format...

Fixes adding new side-formats and an updated header not writing the first time.
Fixes GameDataPK8.CopyTo where PokeJob data copies from the wrong segment
2023-05-09 19:13:58 -07:00
Kurt
a0200ec7e5 Remove more autoscale override settings
68c94a93b2
2023-05-09 12:22:43 -07:00
Kurt
3bde807bf2 Gen5: Add second roamer slot 2023-05-09 11:45:19 -07:00
Kurt
41762f4f67 Make Dpi scaling the default
Have settings toggle to fall back to Font based scaling
2023-05-09 10:50:15 -07:00
sora10pls
ea052fb6d1 Update ContextMenuSAV.cs 2023-05-08 08:15:24 -04:00
Kurt
4aeea9e956 Allow TSV=0 for home accounts 2023-05-08 00:03:45 -07:00
Kurt
ec586683d6 Disallow crossover slots for Mesagoza (8)
Crossovers into Mesagoza should be disallowed since everything is too low in the water, and there's no way to get anything into a gate.
2023-05-07 22:03:44 -07:00
Kurt
e32410279e Flag invalid gimmighoul formarg values 2023-05-07 15:39:37 -07:00
Kurt
77c9438eb8 Add bulk renamer API for renaming PKM files 2023-05-07 15:30:03 -07:00
Kurt
a09a6263c7 Extend filename species 000's to 4 digits
Now that we're past the 999 count...
2023-05-07 15:29:40 -07:00
Kurt
70020dc7e4 Minor behavior tweak for apply all/none
Button closes form when modifying all indexes, so don't bother loading them back to the GUI. The form will immediately close after.
Only read indexes from the GUI if they're kept.
2023-05-07 13:36:04 -07:00
Kurt
5c70e41b76 Devirtualize GetMatchingSlots 2023-05-07 13:34:47 -07:00
Kurt
e75da5d3b7 Misc tweaks
More readonlyspan for the rest of the defined ushort[] arrays, less dictionary/hashset
Simplify some slot-empty checks, makes it easier to see an api for slot interaction (future?)
2023-05-07 13:32:29 -07:00
Kurt
f9ac5eae15 Pass box-to-show to ResetBoxNames
Previous commit changed the behavior to not check SaveFile's CurrentBox, only keep current index. If we want a specific index (like a new SaveFile's latest box), we must pass it.
2023-05-06 19:42:48 -07:00
Kurt
8e177708b7 Skip box name repop if same names 2023-05-05 14:16:58 -07:00
Kurt
68c94a93b2
Make all AutoScaleMode.Inherit (#3874)
Also changes the Main form from Dpi to Font

Attempt to resolve:
https://projectpokemon.org/home/forums/topic/63374-text-sizing-with-windows-settings-ease-of-access-make-text-bigger/
2023-05-03 21:45:58 -07:00
sora10pls
043618cf86 Update PokeSizeDetailed.cs
Different from 1.0.0 / 1.1.0
Thanks iD3M0N1C!
2023-05-02 20:56:16 -04:00
Kurt
3219e03dc6 XK3: Load Purified state from savefile
Closes #3872
2023-05-01 17:09:45 -07:00
Kurt
3285ecada9 Add MemoryCard detection as latest sav, pkmdb 2023-05-01 16:51:17 -07:00
sora10pls
e5cc0ffd6b SAV5: expose number of unlocked boxes 2023-04-30 18:59:51 -04:00
sora10pls
d55bf21466 Minor fix to LGPE fashion cheat
oops
2023-04-30 12:14:05 -04:00
sora10pls
6eb82e3847 Add unlock all fashion cheat for LGPE 2023-04-30 12:01:08 -04:00
sora10pls
7e2552d918 Expose LGPE current fashion/appearance 2023-04-28 21:14:24 -04:00
sora10pls
94f98d66f5 Add latest distribution raid data 🦎 2023-04-27 20:03:51 -04:00
Eelen
ce431d1df4
Update CHS translations (#3869)
Co-authored-by: Leo <103500840+wubinwww@users.noreply.github.com>
2023-04-27 15:35:10 -07:00
Kurt
583a036d65 Remove eager check of Current
The SaveUtil's ienumerable can be of different collection types, and the result is usually is a collection with >0 count
I think I was initially confused (years and years ago) about "MoveNext" if it discarded the first result.

Remove the eager check of the enumerator's current value because it hasn't yet been moved from the uninitialized state.

Closes #3871

Dispose of the enumerator on the early empty return, since the later async code will dispose once all have been iterated.
2023-04-26 21:41:38 -07:00
sora10pls
d4d353c087 Pokémon GO 0.269 Premier Ball Considerations
Perhaps one of the silliest things PKHeX could account for when it comes to legality 🥒🚫

Data has been added to PGET, but the issue has not been fixed yet, and Beast Ball is still up in the air. More to come?
2023-04-23 22:40:52 -04:00
Kurt
de17753733 Extract iterator flag mask check logic, reuse 2023-04-23 16:43:47 -07:00
Kurt
907ed0b32f struct CheckResult
tiny object, eliminate allocation for them since they're 10-16 bytes at most.
2023-04-23 16:04:04 -07:00
Kurt
b417964193 Condense some expressions for move fetch 2023-04-23 15:51:48 -07:00
Jonathan Herbert
599387e7aa
Fix ORAS Held Items Using XY Held Items By Mistake (#3866)
- Add HeldItems_XY
- Fix that HeldItem_AO pointed to what should have been HeldItems_XY
- Fix HeldItem_AO being inconsistent with the rest of the HeldItems
- Correct HeldItems_AO only having XY  held items and not ones added in ORAS
2023-04-23 01:35:26 -07:00
Kurt
f2ffd2ad1f Allow pointer property references for filters
=Stat_ATK=*Stat_DEF
.Species=0

^ will delete a pkm if the defense stat is equal to the attack stat.
the `*`* allows you to use the value from that property, rather than a value from the instruction

already worked for property-modify (aka copy property value), this just allows you to compare-filter with properties.
2023-04-22 17:58:36 -07:00
Kurt
998b8f1ce0 Minor tweaks
Add unit test for VC kata/hiragana edge case
De-duplicate some slot change notifications
Span in more spots
2023-04-22 17:51:32 -07:00
Lusamine
62173f4deb Add dates for Pokémon Center Mini/Jumbo Pikachu events 2023-04-22 10:00:29 -05:00
Lusamine
62597d17e1 Update Designer automatic code generation 2023-04-21 23:29:50 -05:00
Lusamine
18cbb8158c Standardize MinDate/MaxDate values
3DS ranges from 1/1/2000 to 12/31/2050. After that, it rolls to
1/1/2050. Switch ranges from 1/1/2000 to 12/31/2060 as the settable
range. However, date continues advancing normally after 12/31/2060.
Allow the maximum Switch date range to be 12/31/2099 to match maximum
Met date. This should be comfortably far in advance for anyone who is
alive today.
2023-04-21 22:27:22 -05:00
Kurt
0c1221eae1 Update Pokerus.cs 2023-04-21 00:22:44 -07:00
Kurt
5bdc6b9ef8
Privatize some static fields, more robust legal helper classes. (#3865)
* Uses LearnSource more throughout the codebase when appropriate, rather than loosely coupled pivot methods.
* Hides Learnset/EggMove data inside the LearnSource classes.
* Extracts functionality from the large Legal class & partial Table*.cs files into better performing helper classes.
* Cleans up some code from prior LearnSource commits.
2023-04-20 21:23:15 -07:00
Kurt
9a768dded3 Permit pkrs strain 0 & 8 on Gen3+ context
Ruby & Sapphire had a bug that only looped when (rand) == 0 !!! instead of (rand & 7) == 0.
End result is that the pkrs giving method yields strains 0 & 8 with 30/255 & 1/255 rarity.

Gen2: z80 assembly does NOT work as intended, and has a separate bug that causes strains 9+ to never be obtainable. So close to a neat statistical separation.

Revise the GUI to disable the events on field loading, and allow for selecting Strain0 w/ days !0.
VC2->7 does not transfer pkrs; not that it matters since Gen3++ transfers can spread every strain.

Thanks @SNBeast for clarification on Gen2's assembly logic and repro!

Co-Authored-By: SNBeast <21327530+snbeast@users.noreply.github.com>
2023-04-20 21:20:25 -07:00
Lusamine
b08581aecf Final adjustments to SV Trainer Editor, update localization files 2023-04-16 18:16:41 -05:00
Lusamine
78031f09f1 Better align components of gen 9 Trainer Data Editor 2023-04-16 18:16:41 -05:00
sora10pls
8d452d1cc8 Add pre-Gen 6 move types
Curse unaccounted for because ??? type has no icon, better off displaying as Ghost in all cases
2023-04-16 18:58:59 -04:00
Jonathan Herbert
8fca2075d9
Add Game Start Time Editor For Scarlet and Violet (#3861) 2023-04-16 13:16:34 -07:00
Kurt
fa9a809751
Encapsulate item pouch arrays/etc for finer control (#3860)
* Extract/encapsulate inventory legal arrays to interface+class

Hiding them behind methods allows these to be left as ReadOnlySpan<ushort> and thus never allocate on the heap.
Also refactors out some logic for checking if an item is legal.

End result feels more maintainable and is less bloaty (no more passing in a nullable func)

Batch editing
* Add HasType filter

```
=HasType=11
.HeldItem=Meadow Plate
```

slaps a meadow plate on any pkm with grass type
Use `=PersonalType1=11` for only primary grass types; only-secondary-type grass will not match it.
2023-04-16 12:58:07 -07:00
Jonathan Herbert
52a24a7bb0
Expose FashionUnlock8 From SAV8SWSH (#3859)
Also document what the relevant regions are as constants.
2023-04-16 12:32:25 -07:00
Kurt
5b484f5712 Use byte[] instead of int[] for tile sel 2023-04-15 11:28:35 -07:00
sora10pls
f433d02052 Label profile picture/trainer icon save blocks 2023-04-15 11:59:55 -04:00
Kurt
c73264d4f3 Minor minor perf
Small changes to reduce some allocations
2023-04-15 01:58:37 -07:00
sora10pls
e2fd292ffa Add Gavin's Palafin eligible date range 2023-04-14 08:16:52 -04:00
sora10pls
47f5799fef Add latest distribution raids
Scale: 0
tiny boi
2023-04-13 20:04:00 -04:00
sora10pls
f0a34f68ad Disallow form argument on Gimmighoul
Gimmighoul can't increment its form argument counter, it can only ever be 999 on Gholdengo after evolving.

Also reorder switch cases according to National Dex indexes.
2023-04-13 11:28:05 -04:00
Kurt
ecef31f167 Minor clean 2023-04-13 00:05:10 -07:00
Kurt
b5262d69b0 Dispose of file-loaded bitmap after use
Image.FromFile locks the file until the object is disposed; adjust all uses of Image.FromFile to dispose when finished.

Closes #3857 ty @SNBeast !
2023-04-12 23:22:33 -07:00
Kurt
4caf749064 Misc tweaks
do things a little more directly

blank ctor: we already have the ctor, just invoke it ourselves instead of having to rediscover it
2023-04-10 01:26:54 -07:00
Kurt
ebd061dcd9 location list public (class still internal) 2023-04-10 00:50:11 -07:00
Kurt
6d0c2d9520 Add more overload methods
Learnset: slightly quicker check.
SCBlock: assists when you have Key separate from the encoded data.
2023-04-10 00:49:39 -07:00
Kurt
6182f889a6 Add learnset moves similar to games
Instead of looping, if the moveset is full and a new move is added, the game shifts all arr[1..] down one slot then adds the move at the end.
Since we don't need to keep track of PP/PP Ups, we can just defer the shifting and do n % 4 rotations at the end instead of n rotations (one on each move added).
2023-04-08 12:20:18 -07:00
Kurt
7b0313aa6a Update 23.04.06 2023-04-06 21:52:23 -07:00
Jonathan Herbert
7ea0e21b7d
Add Button To Unlock All Fashion in BDSP Misc Edits (#3856) 2023-04-06 20:54:42 -07:00
Kurt
0dfb012e5c Add Ditto distribution raids 👛
Adds a helper method for SCBlock to measure a size from header.
2023-04-06 19:28:10 -07:00
Kurt
340578b671 Update PGT.cs 2023-04-04 15:03:14 -07:00
Kurt
aeb6595887 Show move elemental type in PKM Editor
Note this is only SV types, so moves that had their type changed (=>dark/steel/fairy) will be inaccurate in past game formats.

Would need time to dump the info.
2023-04-04 14:38:07 -07:00
Manu
43c67fd455
Add wc5full compatibility (#3854)
* Add WC5Full compatibility

* Add .wc5full to mystery gift filter
2023-04-04 07:19:54 -07:00
Kurt
e53ca4b1af Allow setting to 11th slot for !PCD
Oops
2023-04-03 15:43:13 -07:00
Kurt
d6ec64fb00 Update MoveListSuggest.cs 2023-04-02 16:19:56 -07:00
Kurt
fcad4f8e08 Handle WC9.Scale spec per v1.2.0 patch changes
Unnoticed, undocumented, now both.
Pivot based on March 1st for the 4 gift-poke cards that already existed. On that day onward, games must be on v1.2.0 in order to redeem, and thus must follow the fixed scale spec.
2023-04-02 01:19:33 -07:00
Kurt
37eb076bd4 Use placeholder sprite for not-yet-affixed
ty matt
2023-04-01 17:46:10 -07:00
Kurt
c3e51d414d Add click event for affixing ribbons easier
Click affix ribbon via cosmetic tab to open Ribbons (same as Ribbons Button). None affixed, but ribbons/marked available, will show an empty square.
Click possessed ribbon to affix.
2023-04-01 17:23:04 -07:00
Kurt
3b54ac6ecc Show affixedribbon next to markings
new Visual Studio version made my designer code ugly
maybe I should refactor the GUI like they recommend :P
2023-04-01 16:52:12 -07:00
Kurt
0d3bbe15b7 Add lechonk wc9 to pickle 2023-04-01 15:20:14 -07:00
Lusamine
d6e1019233 Add met dates for TCG Lechonk and end date for Fly Pikachu 2023-04-01 09:34:15 -05:00
sora10pls
2f5b66f49a Allow Master Rank Ribbon on Treasures of Ruin 2023-03-31 20:04:52 -04:00
Kurt
c81c0d84d4 Add GSC swarm fishing slots
https://projectpokemon.org/home/forums/topic/57375-pkhex-new-update-legality-errors-contribution-page/?do=findComment&comment=282691
9f52f8fb7c

Thanks SegNin !
2023-03-31 13:02:27 -07:00
Kurt
4384cadefc Add more xmldoc 2023-03-31 13:00:34 -07:00
sora10pls
3bfd302633 Add latest distribution raid data 🦦 2023-03-30 20:07:19 -04:00
Jonathan Herbert
5cb31d660d
Add Enrollment Date To SaveBlockAccessor9SV and SAV9SV (#3851)
Add Epoch1900Value based on Epoch1970Value's API and the method @Lusamine described in #3800 to access the Enrollment Date From Scarlet and Violet Save
2023-03-29 20:44:04 -07:00
sora10pls
bd05da2b94 Revise SV stake collection cheat
Chains in front of the ruins are now removed when activating the cheat
Also reorder and label more blocks
2023-03-28 18:25:56 -04:00
sora10pls
6b5d4ec3ea Add, label, and reorder more SV save blocks
Co-Authored-By: Lusamine <30205550+Lusamine@users.noreply.github.com>
2023-03-28 16:01:21 -04:00
sora10pls
dd157813c4 Misc changes
Re-order tables according to National Dex ID instead of Dev ID
Label unknown fields for SV player appearance/fashion
2023-03-28 16:00:44 -04:00
Kurt
79ca670044 Replace party/boxdata get with more performant use
Read directly from the save file.
Batch Editor no longer updates dex, HT details, or updates acquired counts when placing back in the SAV.
2023-03-28 12:49:21 -07:00
Kurt
e6ebd20d83 Minor tweaks
Fix boxbin load being ignored
2023-03-28 11:29:29 -07:00
Kurt
5c13183d02 Less allocation, minor tweaks
NET 8 will have a Shuffle method, which can get rid of the overload in Util.
Batch Editor no longer crashes the program when selecting OT_Trash/HT_Trash/Nickname_Trash via dropdown.
2023-03-27 00:11:42 -07:00
Kurt
82309cf99d Use Memory<byte> for Gen4 saves
Instead of allocating a buffer for Storage/General, just point within the raw Data, as a Memory<byte> reference. Most of the uses were already using it as span.
2023-03-26 15:16:12 -07:00
Lusamine
0e16b08482 Label step counters in SWSH for egg cycle and party friendship 2023-03-26 16:10:26 -05:00
Kurt
aa13c93e65 Add misc generic constraints
Resolves some allocation analyzer issues
2023-03-25 23:28:28 -07:00
Kurt
0087ccb44f Use span for savefile slot API
No more offset fetch
2023-03-25 23:14:50 -07:00
Kurt
5ce3e734b8
Skip initial cctor alloc on non-byte primitives (#3850)
Runtime/jit repoints these to the dll rather than heap if we're Little Endian (always, otherwise will allocate like before).

Eliminates quite a few static constructors, so even faster startup. Items later.
2023-03-25 17:55:55 -07:00
Kurt
34c1473966 Misc tweaks 2023-03-25 17:48:49 -07:00
Kurt
081a48bbf6 Update LevelVerifier.cs 2023-03-24 23:48:35 -07:00
sora10pls
a68165cdd8 Add latest distribution raid data 🥚 2023-03-23 20:06:11 -04:00
Kurt
4615e3577a Span-ify MoveSetApplicator 2023-03-21 21:02:13 -07:00
Kurt
46de8c4b06 Minor optimizations
Only fetch egg moves when using them
Localize KOR gen2 string directly instead of switch expression
Remap FRE gen4 chars directly, hot path instead of dictionary lookup
Uppercase chars in place
2023-03-21 17:20:13 -07:00
BlackShark
6247a61bd3
Fixed offset for MysteryEvent3RS checksum (#3847)
* Fixed offset for ME3RS checksum

* Added Subtitle for WonderCard3
2023-03-21 16:45:37 -07:00
BlackShark
a31217ef6a
Various changes to Gen 3 Mystery Data (#3842)
* Various changes to Gen 3 Mystery Data

* Update Gen3MysteryData.cs
2023-03-20 16:26:23 -07:00
Manu
8ab36f70fd
fix SetValue for SByte blocks (#3843) 2023-03-20 15:54:47 -07:00
Kurt
224283b7f8 Better spanify nth entry tsv fetch
Closes #3846
2023-03-20 15:52:27 -07:00
Manu
f611c4a728
Update Italian translation (#3845) 2023-03-20 07:13:05 -07:00
Manu
f9c55320f4
Label Active Outbreaks Number Save Block (#3844) 2023-03-20 06:07:30 -07:00
Kurt
30e3c94c8a GB: Clamp ev stat to 255 again
https://github.com/kwsch/PKHeX/issues/3841#issuecomment-1475373250
2023-03-19 12:40:50 -07:00
javierhimura
7af65a4683
Filter Block Dump data (#3840)
* Allow the filter results in BlockDump combobox. Write a text in the dropdown
2023-03-19 12:33:12 -07:00
Kurt
ecbf6c06de Permit pokewalker courses for all languages
Wrong: #3594
Pokewalkers can interact with games they're not paired to (and receive gifts!).

Closes #3837
2023-03-19 12:21:43 -07:00
Kurt
c8909a4326 GB stats: Ceil instead of +1
Wrong: #2265
Edge case (exact sqrt); Closes #3841
2023-03-19 12:18:10 -07:00
javierhimura
66d897c41a
ResetBoxNames in UpdateBoxViewers (#3839) 2023-03-19 10:33:55 -07:00
Eelen
994685cad5
Update CHS translations (#3836)
Co-authored-by: Leo <103500840+wubinwww@users.noreply.github.com>
2023-03-19 10:11:04 -07:00
notzyro
df481c9b25
Label SV GO Vivillon Save Blocks (#3838)
- Rename FSYS_Vivillon for uniformity
- Labels bool for GOVivillon form is enabled
- Labels unix time stamp for GOVivillon postcard expiration
2023-03-18 16:38:11 -07:00
sora10pls
61a87333c0 Re-ban impossible SV Egg Moves
See e20b5cb325
2023-03-18 09:05:34 -04:00
Kurt
7e3af18b2e Fix inverted bdsp footprint ribbon check
Net7 must have rewritten the logic and I partially reverted
https://projectpokemon.org/home/forums/topic/57375-pkhex-new-update-legality-errors-contribution-page/?do=findComment&comment=282543
2023-03-16 21:05:42 -07:00
sora10pls
5110c62d25 Add latest distribution raid data 🦉 2023-03-16 20:05:39 -04:00
Lusamine
c0796780fa Tweak BulkAnalysis output 2023-03-15 21:24:03 -05:00
sora10pls
cef70fe874 Update sprites for 1009/1010
These finally got pushed to their servers last night. The Walking Wake sprite on HOME's servers is different from the one in SV 1.2.0, the one in-game likely be updated with the first wave of DLC.

Also remove accidental extra Alcremie sprite (formarg 7 does not exist).
2023-03-15 09:05:57 -04:00
Kurt
662cc99141 Rework mail slot swapping
Closes #3831
2023-03-10 19:49:35 -08:00
Kurt
d13b39231a Fix unset geoloc gen4/5
Closes #3833
2023-03-10 19:34:49 -08:00
Jonathan Herbert
547ec8ad12
Fix SwSh Treasure Inventory Tab Mislabeled As Mail (#3832) 2023-03-08 11:52:18 -08:00
Kurt
7b07334a5a Update Vivillon3DS.cs 2023-03-05 12:27:17 -08:00
Kurt
827b7455c0 Update Vivillon 3DS pattern handling
Follow up to #3235, this time closing the book for good.

From a bulk analysis perspective, having multiple patterns[0, 17] of Vivillon for the same Gen6/7 OT is illegal. Maybe not for traded eggs?
2023-03-05 01:39:13 -08:00
Kurt
29b3b695bd Pre-allocate some dictionary sizes
Skips the bucket array resizing steps
a 256 key dict will garbage 5KB; StringConverter2KOR wastes 100KB of garbage.

Zukan7b: save 144 bytes and eliminate cctor
2023-03-04 20:00:27 -08:00
Kurt
2c405f98b1 Deduplicate zh Gen5 met locations
StringQualityTests now passes again.
2023-03-04 19:31:03 -08:00
Kurt
284dd6d995 Update EncounterEgg.cs 2023-03-04 15:10:34 -08:00
Kurt
81bcd95dc1 Revise Vivillon form sanity check
Previously, slots were hard-matched to 18; now since they're "random", we need to flag out of bounds values.
2023-03-04 14:53:01 -08:00
Kurt
94d5ca491f FormRandom SV Vivillon 2023-03-04 14:48:40 -08:00
Eelen
5246d67284
Update CHS Translations (#3825)
Co-authored-by: Leo <103500840+wubinwww@users.noreply.github.com>
2023-03-04 13:22:06 -08:00
Ammako
0a1b4196b1
Fix Wishmaker Jirachi (#3824)
Needs ID32 because Wishmaker Jirachi is a fixed 00000 SID, unlike Channel Jirachi.
2023-03-04 13:21:57 -08:00
BlackShark
cb0ef52676
Minor UI fixes (#3823)
* Resized SettingsEditor

* Fixed mislabeled Gen 3 MiscEditor
2023-03-03 00:52:19 -08:00
Kurt
569a4a7832 Add postcard before multiplayer size 2023-02-28 20:03:46 -08:00
Kurt
57d32456f6 Update 23.02.27 2023-02-27 19:21:56 -08:00
Kurt
0f1fba86f7
S/V 1.2.0 Support (#3819) 2023-02-27 19:12:27 -08:00
Eelen
353ca40f42
Update CHS Translations (#3802)
Co-authored-by: wu professor <103500840+wubinwww@users.noreply.github.com>
2023-02-27 18:15:13 -08:00
Manu
5ef3837d3f
Fix Jiseok Garganacl start date (#3815) 2023-02-27 16:18:39 -08:00
Manu
8de4b8b90f
Update Italian translation for Copy Raid button (#3814) 2023-02-27 05:52:18 -08:00
Kurt
7b93491025 Tab separated country/region tables
https://github.com/kwsch/PKHeX/pull/3810#issuecomment-1445519631

SuggestAppend for gen4/5 trainer country/region editor
Add country:0 for 3DS locale for consistency
Use — for "None"
2023-02-26 17:41:22 -08:00
abcboy101
30aa1cc9aa
Add Geonet location editing for Gen 4/5 (#3810)
* Add Geonet location to SAV4 and SAV5

* Allow commas to be escaped in subregion names

* Add missing 3DS subregions

* Add Geonet location editing

* Ensure null values are kept as the first option in GetCountryRegionList

* Add Geonet locations in CHS/CHT
2023-02-26 16:33:53 -08:00
Koi-3088
724915d5a8
Add build timestamp. (#3796) 2023-02-26 16:26:01 -08:00
Kurt
52f1bf081c Add SizeType9 for fixed range Scale values 2023-02-26 16:14:11 -08:00
Kurt
b4b1310934 Rework jagged crypt operation
Fixes checksums for Colo

Closes #3803
2023-02-26 15:18:32 -08:00
Kurt
839a76d6a3 Slightly better perf for HiddenPower/Nickname
No more stackalloc for temp string trimming/rebuilding. Really jank strings with multiple parens shouldn't need to be recognized.
Handle bad TeraType pkm values
Handle unrecognized Hidden Power types (Fairy), don't show "Normal"
Hide Tera Type import if requested for non-Tera formats, and gen8 props too.
2023-02-26 13:51:58 -08:00
sora10pls
0a07edb705 Add latest distribution raid data 🐭💧 2023-02-23 19:08:08 -05:00
Lusamine
0fa4ec8dce Fully update button naming/localization for propagating SV raids
Standardizes capitalization of "raid" in the editor and updates
shortcuts to indicate holding Shift to propagate seed.

Closes #3807
2023-02-23 11:08:33 -06:00
Kurt
812aa0a2d3 ThrowIfNull net7 2023-02-23 01:07:46 -08:00
Kurt
552676ed3a Add counter overflow check
Never will happen, but not worth arguing over because this is essentially what the ROM does. Entry to this method requires both OK.
91c040b081/src/save.c (L587-L605)
Closes #3805
2023-02-23 00:48:03 -08:00
Kurt
77cac48d34 Add sanity check for min level eggs
Closes #3797
2023-02-18 23:19:50 -08:00
Lusamine
3e5c31a7b5 Add server dates for Jiseok's Garganacl event 2023-02-17 02:22:39 -06:00
mi-ya1987
f3719e1d32
Fix Emerald flag Japanese localization (#3792)
* Update const_e_ja.txt
2023-02-17 00:09:06 -08:00
Manu
1d2c8d27ba
Fix MetDate for WC9 templates (#3790)
* fix flabébé wc9 details

* add JumpFesta Gyarados EncounterServerDate

* Fix MetDate when generating from WC9 template
2023-02-16 23:57:03 -08:00
Kurt
6daad3a1d1 Update CGearBackground.cs
Closes #3789
2023-02-16 23:51:15 -08:00
mi-ya1987
07fdae71f1
Fix extensions of Japanese RSE flag files (#3791)
* Rename const_e_ja to const_e_ja.txt

* Rename const_rs_ja to const_rs_ja.txt

* Rename flags_e_ja to flags_e_ja.txt

* Rename flags_rs_ja to flags_rs_ja.txt
2023-02-16 22:11:04 -08:00
mi-ya1987
db32f960c2
Create const_e_ja (#3783) 2023-02-16 19:39:44 -08:00
mi-ya1987
6ab1474523
Create flags_e_ja (#3782) 2023-02-16 19:39:32 -08:00
mi-ya1987
4de0844b84
Create const_rs_ja (#3781) 2023-02-16 19:39:16 -08:00
mi-ya1987
735916ab8a
Create flags_rs_ja (#3780) 2023-02-16 19:39:06 -08:00
mi-ya1987
0140f86add
Update flags_e_en.txt (#3787) 2023-02-16 19:35:15 -08:00
mi-ya1987
e12878fc48
Update flags_frlg_ja.txt (#3785) 2023-02-16 19:33:44 -08:00
mi-ya1987
c7917cea9a
Update const_frlg_ja.txt (#3788) 2023-02-16 19:32:16 -08:00
Lusamine
0d9d2e6a3d Add Sylveon and Garchomp Distribution Tera Raids 2023-02-16 20:06:01 -06:00
Manu
4c8831011d
fix wc9 dev id (#3786) 2023-02-16 05:54:48 -08:00
sora10pls
00bc21f8ff Add latest distribution raids 🫃 2023-02-12 19:47:12 -05:00
Zazsona
386c7078db
Update "SetStringKeepTerminatorStyle" to resolve "?" OT bug. (#3778) 2023-02-12 11:28:51 -08:00
Eelen
92b841881d
Update CHS translations (#3776)
Co-authored-by: wu professor <103500840+wubinwww@users.noreply.github.com>
2023-02-12 11:16:56 -08:00
mi-ya1987
76e014614c
Update flags_e_en.txt (#3773) 2023-02-10 23:29:25 -08:00
mi-ya1987
1989373bb3
Update flags_e_en.txt (#3772) 2023-02-10 23:29:04 -08:00
sora10pls
3e6a036c54 Add SV style menu sprites for ALL species and forms
only exception is Eternamax Eternatus, rest in Galar

Co-Authored-By: SciresM <8676005+SciresM@users.noreply.github.com>
2023-02-09 19:58:12 -05:00
Eelen
d1016714ce
Update CHS translations (#3769)
* Update CHS Translations
2023-02-08 23:36:32 -08:00
mi-ya1987
0118a1880b
Update flags_frlg_ja.txt (#3767) 2023-02-08 23:35:51 -08:00
Kurt
39a7007541 Extract Bulk Analysis components 2023-02-08 17:24:47 -08:00
Kurt
f77e605cae Update AreaWeather9.cs 2023-02-08 08:19:38 -08:00
Kurt
ad31ebbbe2 Update TransferVerifier.cs 2023-02-07 22:30:33 -08:00
Kurt
aa430b48ca Disallow LGPE event gift OT handling 2023-02-07 21:56:25 -08:00
Kurt
b8b499a797 +1char lgpe CP textbox
Now 5 digits visible.
2023-02-07 21:52:31 -08:00
Kurt
ad0f604841 Add pokeflute to key XY
Temporary key item
https://gaming.stackexchange.com/questions/179667/how-to-get-the-pokeflute-in-pokemon-x-and-y
2023-02-07 21:52:06 -08:00
Lusamine
59e4ad61fb Add white outline to alpha icon 2023-02-06 20:33:22 -06:00
Kurt
3871b2f316 Add focus border setting
Can set to -1 to remove it
2023-02-06 18:11:30 -08:00
Lusamine
376e8f2d4f Always use white outlines on box shiny icons 2023-02-06 19:36:01 -06:00
Lusamine
8f64f719ad Update shiny stars to modern game icon
This swaps the old red star to a red two-star icon from Switch games.
Improves visibility of the square shiny icon (red/white stars).

Closes #3749
2023-02-06 18:54:40 -06:00
Kurt
163ffac671 Fix gender GUI click event double toggle
Private event already toggles it
2023-02-06 09:05:34 -08:00
Kurt
dde075432b More accessibility tweaks
#3758
2023-02-05 15:00:31 -08:00
Zazsona
c6e133cff8
Add Toys from the My Pokémon Ranch Platinum Update (#3766)
* Add Ranch Toys from Platinum Update

* Add ToyType validation to SAV4Ranch
2023-02-05 11:47:35 -08:00
mi-ya1987
a16b2cbd3a
Create flags_frlg_ja.txt (#3763) 2023-02-05 11:46:59 -08:00
mi-ya1987
6a8907398a
Update flags_frlg_en.txt (#3760) 2023-02-05 11:46:48 -08:00
mi-ya1987
7da2313db5
Create const_frlg_ja.txt (#3764) 2023-02-05 11:46:32 -08:00
Eelen
e77244957d
Update CHS translations (#3765)
-Update flags_bw_zh.txt
-Update flags_oras_zh.txt
* Fix CHS translation errors
* Update lang_zh.txt
* Update CHS translations
-Update flags_b2w2_zh.txt

Co-authored-by: wu professor <103500840+wubinwww@users.noreply.github.com>
2023-02-05 11:46:16 -08:00
mi-ya1987
ac833f0365
Update flags_frlg_en.txt (#3762) 2023-02-05 11:45:29 -08:00
mi-ya1987
11b0f96f08
Update flags_frlg_en.txt (#3761) 2023-02-05 11:45:18 -08:00
Kurt
839433792d Tweak gender accessibility
#3758
2023-02-05 11:31:12 -08:00
Kurt
3d7bfcfe11 Revise some GUI interactions, accessibility
https://github.com/kwsch/PKHeX/issues/3758#issuecomment-1417458988

Removes the Level textbox behavior that resets the text to "1" when the textbox is empty. Can be left empty, and will be treated as 1 when values are stored.

Adds an accessibility label for the "Make Shiny" button
Adds an accessibility description and enter/space keypress to toggle similar to the click event.

For determining if something is shiny, if the Shiny button is not tab-able, it is shiny. I wasn't able to get the Shiny star/square indicator to be able to be tabbed to for some reason.
2023-02-05 11:18:05 -08:00
Kurt
79a05caff5 Accessibility: narrate pictureboxes
Pipes the Showdown Set description or object detail dump into the accessible property. When the control Enter event is fired, send an Automation notification so that the Narrator narrates what was just tabbed to.

Closes #3758
2023-02-05 00:42:37 -08:00
mi-ya1987
627965e6ed
Update flags_frlg_en.txt (#3759)
mistake in the spelling corrected
2023-02-05 00:42:20 -08:00
Kurt
d8b8646de6 Use selectable picturebox
Closes #3757
Allows tabbing between windows, or pressing the keyboard's contextmenu key to pop up the context menu without ever needing to use the mouse.

changes pkmeditor dragout, box slot displays, sav-mystery gift r/w, and all the encounter/pkm db's
2023-02-04 14:52:38 -08:00
Kurt
31733671ec Fix string screening (str->int) 2023-02-03 20:44:11 -08:00
Kurt
fe2cbeac31 Add date restrictions for WC9, pika diff 2023-02-03 20:30:41 -08:00
Kurt
60a53b6afa Skip closing other-thread popups on sav load
Plugins may have popups that were created on another thread as diagnostic messages; by trying to close these on our main GUI thread, the program crashes.

Just leave these other-thread forms open when we change save files instead of crashing or closing them on their proper thread.
2023-02-03 12:52:52 -08:00
Eelen
5bee6d0d0d
Update CHS translations (#3755)
* Update CHS translations
* Update GEN8 GEN7 and GEN6.XY flags translations
* Update flags_gg_zh.txt

Co-authored-by: wu professor <103500840+wubinwww@users.noreply.github.com>
2023-02-03 12:45:19 -08:00
mi-ya1987
c6bfaa0709
Update text_ItemsG3_ja.txt (#3751) 2023-02-03 12:44:40 -08:00
frefire
f7179a7713
Gen 5 enhancements. (#3756)
* Gen 5 enhancements:
 -- BW: Dump/import black city/white forest data from each other.
 -- BW: Reset thundurus/tornadus flags so they can be roaming again by visiting the cabin at route 7.
      Reset this flag and switch your BW version with the same save file, you can catch the other pokemon that's limited to a sole version.

* Use work 192, fc5 ext

Adds a translatable message string to indicate the provided input size is incorrect.
2023-02-03 12:44:21 -08:00
sora10pls
3ade5794f2 Add latest distribution raid data 🧍‍♂️🔥 2023-02-02 20:01:44 -05:00
Kurt
8279615040 Fix bit wrap met location matching
Use ulong bitmask not u32
2023-02-02 15:12:18 -08:00
Kurt
3d29cc5a19 Misc tweaks
- Allow paradox species to receive master rank ribbon
 - Resize & center IV/AV rand button (localizations
were too wide for AVs)
 - Highlight blue the most recently toggled Ribbon
2023-02-01 16:55:38 -08:00
Kurt
9ecb8981a9 Expose criteria optimization for trade/headbutt
If the criteria template is optimized, we will be able to source moves from sibling game pairs that require indications of it being traded (yay strict logic...)

ex: encgenerator now yields X/Y eggs when ORAS tutors requested in moveset and game is X/Y
2023-01-31 23:23:12 -08:00
Kurt
c5ec20ef5d Emit sv scatterbug eggs as form18 2023-01-31 18:31:55 -08:00
Kurt
39fe454840 Revise natureamp clicks
now behaves correctly without off-by-1 behavior
2023-01-31 18:31:40 -08:00
Kurt
6bf3d4ce9f More descriptive version indication wc6/wc7
Skip yield if not matching version
Handle defer/partial for gen7
2023-01-30 22:57:18 -08:00
Kurt
10613fd070 Improve hidden power mutation logic
No more heap algo span copy mutation. Finish distilling the problem down to bits.
2023-01-30 19:49:31 -08:00
Kurt
81487af9ba Revise update linklabel click
https://stackoverflow.com/a/53245993
2023-01-30 18:51:17 -08:00
Manu
0dc9deb137
Fix PKMEditor sidebar scaling (#3752)
Closes #3750
2023-01-30 10:22:02 -08:00
Kurt
5265643f3e Update 23.01.30 2023-01-29 23:39:42 -08:00
mi-ya1987
80dc35898a
Update text_ItemsG3_ja.txt (#3747) 2023-01-29 23:31:57 -08:00
mi-ya1987
9d3da25a02
Update MessageStrings_ja.txt (#3746) 2023-01-29 23:31:19 -08:00
Eelen
9fb1ec2c86
Update CHS translations (#3748) 2023-01-29 09:54:45 -08:00
Kurt
93c4abbbd5 Misc tweaks
Un-fix main panel, guess this might help for scaling the GUI via OS settings. Add an overload to rescale ItemSize for the vertical tab control; don't think it is ever called though.

Make EvoChain get method public for archit
2023-01-28 19:23:43 -08:00
Kurt
15925b1368 Fix mysterygift string ext cmp 2023-01-28 19:22:31 -08:00
Kurt
03be38c8df Handle remapped zukan species
The one critical spot it was needed :(
Revise SeenAll for genderflag set; just wipe -> reapply.

Closes #3745
2023-01-28 19:18:10 -08:00
Eelen
19b7e0fda3
Update CHS translations (#3742) 2023-01-27 09:21:05 -08:00
fi
24c9c388b0
Corrected incorrect Japanese translation (#3740)
A sentence that was incorrect as a Japanese translation has been corrected.
2023-01-27 09:20:42 -08:00
Kurt
3572097c96 Update 23.01.26
Hello .NET 7
2023-01-26 21:58:04 -08:00
Uiharu
0e897dc1f1
Update CHT and Japanese translations (#3738)
Update CHT and Japanese translations.
2023-01-26 19:03:28 -08:00
Manu
1205a2c0ae
update italian translation (#3737) 2023-01-26 19:03:19 -08:00
Kurt
9ddfe3f629 Misc fixes
Allow dragon ascent bitfetch for gen6/7
Fix hidden power type parse/trim
Remove */ from hidden power type calc
allow longer set lines (full EVs specified for Gen2 is 74 chars
allow set lines of length 1-2 to fully support trash sets for all languages
Tweak pb8->pk8 to be more straightforward
2023-01-26 19:03:06 -08:00
Kurt
387ab6d546 Pass species/form as param; toxtricity nature
Need to know the original encounter species rather than current species. Gotta pass this information in.

Closes #3739 ty @javierhimura !
2023-01-26 18:07:27 -08:00
sora10pls
8419ee017d Add latest distribution raid data 🐸🥷☠️ 2023-01-26 19:03:59 -05:00
Kurt
5aca291a7a Update translatables via devutil dump 2023-01-25 20:36:47 -08:00
Uiharu
3cbd662ade
Remove the useless blank line in text_Natures_zh2.txt (#3736)
* Remove the useless blank line in text_Natures_zh2.txt

Remove the useless blank line to fix the Natures drop-down selection menu issue, which may be introduced from the last modification.

* Update some translation texts for better display

Update some translation texts for better display.
2023-01-25 19:29:35 -08:00
Easy World
c8e0d1d012
Translation revise (#3735) 2023-01-25 19:29:27 -08:00
Kurt
1c626ca412 Tweak tab order 2023-01-24 23:45:09 -08:00
Kurt
a804ca95c7 Misc gui tweaks 2023-01-24 20:10:08 -08:00
Kurt
6a09311394 Use generated json serializer
Generated (de)serializer faster!
Shaves off a few non-trivial ms for startup
2023-01-23 23:31:41 -08:00
Manu
c5a9f0d6e3
update italian translation (#3734)
* it text updates

* update italian translation

* mistakenly renamed string in fr file
2023-01-23 10:48:19 -08:00
Uiharu
b47dd9c877
Update CHT translation (#3732) 2023-01-22 22:16:32 -08:00
Jimmy
f89d146462
Update text_Natures_zh2.txt (#3733)
In traditional Chinese, Relaxed should be translated as "悠閒" rather than "悠閑"
This is a translation issue.
2023-01-22 22:16:05 -08:00
BlackShark
38daf0975e
Updated German translation (#3731)
* Updated German language files

* More German translations
2023-01-22 14:58:04 -08:00
Kurt
71eeb7072b Fix gen4 set all TMHM bits
oops
make other personalinfo consistent
show legality triangles if previous parse failed (oops)
2023-01-22 14:55:35 -08:00
Easy World
f96de7d38c
i18n resource update (#3730)
* i18n resource update

* add chs and cht translation
* add other languages placeholders

Co-authored-by: Eelen <37260264+ppllouf@users.noreply.github.com>
2023-01-22 13:05:17 -08:00
Kurt
146dbad387 More fixes
pk3->pk4 trash length copy
cross-thread main preview dragdrop (continue from same GUI thread)
rearrange csproj again, seems like SelfContained needs to go after the Publish content.
2023-01-22 11:43:13 -08:00
Kurt
1baba56a4f File scoped namespace program.cs
Also trim the Version for parsing
2023-01-21 23:32:18 -08:00
Eelen
dfcc22c453
Translation tweak (#3722)
Repair form translation format
remove redundant Rotom form translation
2023-01-21 22:02:40 -08:00
Kurt
8537bb56e1 Replace "Set All 6 Stars" with "Copy To Others"
Closes #3727
Hold shift to copy the seed too, otherwise it will keep the existing seed randomness.
2023-01-21 22:02:21 -08:00
Kurt
0b5824321b Disable selfcontained for debug builds too
Cleaner folder.
2023-01-21 22:02:05 -08:00
Kurt
5de825de51 Disallow matching if notradeback loc wiped
Closes #3710
2023-01-21 22:01:50 -08:00
Kurt
7b910504ce Use Current Level instead of Stat Level for pk1/2
Closes #3704
all other impl of LoadStats get CurrentLevel
2023-01-21 22:01:30 -08:00
Kurt
88830e0d00
Update from .NET Framework 4.6 to .NET 7 (#3729)
Updates from net46->net7, dropping support for mono in favor of using the latest runtime (along with the performance/API improvements). Releases will be posted as 64bit only for now.

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

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

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

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

Fixed: Tatsugiri and Paldean Tauros now output Showdown form names as Showdown expects
Changed: Gen9 species now interact based on the confirmed National Dex IDs (closes #3724)
Fixed: Pokedex set all no longer clears species with unavailable non-base forms (closes #3720)
Changed: Hyper Training suggestions now apply for level 50 in SV. (closes #3714)
Fixed: B2/W2 hatched egg met locations exclusive to specific versions are now explicitly checked (closes #3691)
Added: Properties for ribbon/mark count (closes #3659)
Fixed: Traded SV eggs are now checked correctly (closes #3692)
2023-01-21 20:02:33 -08:00
sora10pls
4265407d45 Add latest distribution raid data 👻 2023-01-19 19:04:06 -05:00
ButanE
5fdde713a5
Add README-zh.md (#3723)
Add Simplified Chinese version of "README" file
2023-01-14 01:19:49 -08:00
Kermalis
5958b4ee73
Fix tera-type typo in Encounter9RNG (#3725) 2023-01-14 01:17:52 -08:00
Kurt
260559c7e6 Update EggStateLegality.cs 2023-01-07 14:17:34 -08:00
Kurt
dbbb4eba62 Allow fall through for Gen9 marks 2023-01-07 14:01:38 -08:00
sora10pls
9e1df853b0 Add latest distribution raid data 2023-01-05 19:05:44 -05:00
Easy World
d4af464109
Translation tweak (#3717)
* Translation tweak

* add Main.B_RaidsSevenStar translation in 9 languages
* modify Main.B_Raids translation in chs and cht
* remove redundant Rotom form translation

* Add SAV_RaidSevenStar9 translation

Add SAV_RaidSevenStar9 language resources
2023-01-05 08:25:41 -08:00
sora10pls
67ac1e7378 Add Cinderace raid handling 2022-12-29 19:20:01 -05:00
sora10pls
854a8d1f5a SV block key label updates 2022-12-29 18:51:45 -05:00
sora10pls
b8f6b5f434 Add Delibird distribution raids 🎁 2022-12-22 19:04:35 -05:00
Kurt
b1f31e8198 Update EntityPID.cs 2022-12-22 12:12:13 -08:00
Kurt
a8be90be31
Update WC9.cs
Closes #3693
2022-12-19 18:56:12 -08:00
Kurt
f5ea250975
Show star square for everything in Gen9 (#3689) 2022-12-18 00:16:29 -08:00
Kurt
1e086798fb Update 22.12.18 2022-12-18 00:06:04 -08:00
Easy World
8c8f636769
Update text_Forms_zh.txt (#3688) 2022-12-17 23:45:12 -08:00
Kurt
36031ad51f Update PKM.cs 2022-12-17 23:38:04 -08:00
Kurt
e3b1a29ad6 Update Core.cs 2022-12-17 23:09:00 -08:00
Kurt
1c980a0434 Fetch form entries for gen4-6 learnsets
Silly Wormadam with different form permissions.
2022-12-17 20:36:44 -08:00
Kurt
c776d8db43 Treat level2 yungoos as scripted encounter 2022-12-17 14:54:57 -08:00
Kurt
d9982bcd47 Reset Version to 0 if SV IsEgg 2022-12-17 14:42:41 -08:00
Kurt
ee9f8f0558 Allow click correcting Gender icon 2022-12-17 14:04:22 -08:00
Kurt
1de869f541 Flag pp ups on non-PP up move IDs
Previous logic wouldn't flag "None" move w/ ppup>0
Also now flags sketch & revival blessing.
2022-12-17 13:29:06 -08:00
Kurt
80c856b618 Move complicated EC get to Common Edits
Rerolling EC will match Maushold and Dudunsparce current form.
2022-12-17 13:17:24 -08:00
Kurt
ae2d13432b Aggressively hide formarg label on format change 2022-12-17 13:04:37 -08:00
Kurt
62204a81fc Level 2 wild yungoos game start handling 2022-12-17 00:12:33 -08:00
Kurt
41930dd2bd Handle crossover flying dragonite
Update pkl from pkNX
2022-12-16 22:34:01 -08:00
Lusamine
46d96f2822 Document blocks for Gimmighoul, fixed symbols, and records 2022-12-15 13:14:26 -06:00
sora10pls
7c1a343216 Add SM/USUM IV Judge event flag 2022-12-13 17:54:31 -05:00
Kurt
7d4a4846f7 Disallow area0 crossover
Coordinates are nearby, but the points are on distinct loaded maps.
2022-12-12 23:56:25 -08:00
sora10pls
4eb7858028 Fix former titan Iron Treads moveset
also rearrange to story order
2022-12-12 20:59:09 -05:00
Kurt
8190a12d13 Label properties for language and entity count 2022-12-10 19:55:24 -08:00
Kurt
3ef12c3ebe
Encounter Crossover/Wander tolerance (30f) (#3683)
* Update encounter_wild_paldea.pkl

* Add weather/time mark checks

* Set tolerance to 30f, swap crabrawler evos
2022-12-10 19:53:59 -08:00
Kurt
d4835392a0 Update SAV3GCMemoryCard.cs
Move SaveGameCount field reset outside of the loop.
Closes #3682 ty @TheZett !

Make if-if-if an if-else since entry to one will never enter the others.
2022-12-10 14:01:59 -08:00
sora10pls
715391e3b0 Hide 7 Star Raid editor on non-SAV9SV 2022-12-09 19:08:26 -05:00
Kurt
07ce14403f Update EncounterFixed9.cs 2022-12-09 07:50:50 -08:00
Yarkis
5b1daaea33
Improvement of the French translation (#3677)
* Fixed some translation issues

* Update lang_fr.txt
2022-12-08 23:13:51 -08:00
Kurt
89b10aec7e Min size clamp fixed tera
Co-Authored-By: Lusamine <30205550+Lusamine@users.noreply.github.com>
2022-12-08 23:03:56 -08:00
dependabot[bot]
4cb4c1ab9b
Bump Newtonsoft.Json from 13.0.1 to 13.0.2 in /PKHeX.WinForms (#3679)
Bumps [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json) from 13.0.1 to 13.0.2.
- [Release notes](https://github.com/JamesNK/Newtonsoft.Json/releases)
- [Commits](https://github.com/JamesNK/Newtonsoft.Json/compare/13.0.1...13.0.2)

---
updated-dependencies:
- dependency-name: Newtonsoft.Json
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-08 20:46:30 -08:00
sora10pls
b2cbcd5139 Clamp picnic table accessory counts 2022-12-08 22:59:23 -05:00
sora10pls
af660dd299 Add Tyranitar/Salamence distribution raids 2022-12-08 19:04:29 -05:00
Kurt
b29d9a65e1 Allow static encounter deerling form change match
Closes #3678
2022-12-07 23:28:47 -08:00
sora10pls
baa21a6f49 Update GameData.cs 2022-12-07 18:15:11 -05:00
Kurt
650ee8ae40 Don't yield SV version eggs, egg OT gender check 2022-12-06 21:34:14 -08:00
Kurt
16595d2af8 Extract PID gen, handle battle&->capture mutations
Co-Authored-By: Lusamine <30205550+Lusamine@users.noreply.github.com>
2022-12-06 20:56:50 -08:00
Kurt
0c0651f719 Show ride legend stored data
Hidden in box 33. All alone.
2022-12-05 21:05:16 -08:00
Kurt
72877b7f0c More fallback behavior for Met list population
Should probably rewrite this into an API that isn't so janky.

Closes #3669
2022-12-05 19:02:43 -08:00
Kurt
9f480dcbe2 Set fixed gender for static9
Closes #3671
2022-12-05 14:10:00 -08:00
sora10pls
1493bb45cb SV: Remove unobtainable Egg Moves 2022-12-05 11:29:36 -05:00
sora10pls
40701ced73 Add Mass Outbreak save blocks 2022-12-04 20:14:43 -05:00
MewTracker
703ddb866a
Added spawn points to raid editor (#3668) 2022-12-04 07:52:31 -08:00
Kurt
f7bcb211b2 Improve ForceHatchPKM when Version=0 (SV)
BoxManipModifyComplex finally used after 3-4 years of wait
2022-12-04 00:43:44 -08:00
Korados
3ab744d027
Update MessageStrings_de.txt (#3666) 2022-12-04 00:10:54 -08:00
Kurt
339132e5dc Move HA egg check back in gen8 format check
51875d27b2
Previous change ^ was short circuiting logic for gen5 eggs (unavailable HA for Serperior)

probably need to rewrite this method or maybe just delete these lines since they should fall through.
2022-12-04 00:10:43 -08:00
Kurt
3f4aa2f9f2 Extract master ribbon check
Check visitation via API method instead of in verifier
2022-12-04 00:09:16 -08:00
Kurt
67c458d321 Only remark HT memory if gen6/swsh origin
Closes #3667
2022-12-04 00:03:47 -08:00
Kurt
50e7e3204c Show egg TID format based on SAV, egg met list=sav
Generation of version-less eggs is -1, so just default to the format it is.
Met location lists weren't reloading if the version was 0; could load a non-SV entity followed by an egg and it would keep the old lists. Fall back to the save file's group if no list is available.
2022-12-03 21:00:25 -08:00
sora10pls
046ffae536 Remove commented Gimmighoul encounters
Everything is accounted for
2022-12-03 18:49:44 -05:00
sora10pls
670b8e3c9f Update Master Rank Ribbon restrictions for SV
All Pokémon in the Paldea Pokédex are eligible, except for Legendaries, Sub-Legendaries, and Paradox.

Currently ineligible foreigners are Charmander/Charmeleon/Charizard, Wooper-0/Quagsire, Meowth-2/Perrserker
2022-12-03 10:53:48 -05:00
sora10pls
fcff2925f1 Add 7 Star Raid record editor 2022-12-02 22:20:43 -05:00
Kurt
2e67526db1 Update 22.12.01 2022-12-01 22:40:11 -08:00
Kurt
f10c914eac Swap RSBox pay day skitty moves 1 & 2
Thanks Soniktts & ICanSnake !
https://projectpokemon.org/home/forums/topic/62640-pokemon-box-skitty-egg-illegal/
2022-11-30 18:47:35 -08:00
Kurt
2ae32c6beb Use first tera type on set import if unspecified 2022-11-29 20:06:35 -08:00
sora10pls
a4abd94724 Unban Starf Berry
151 rounds in ESP Exercise Knockout
2022-11-29 21:53:34 -05:00
sora10pls
1ffd6872a8 Unban Lansat Berry 2022-11-29 21:47:42 -05:00
Manu
370ba7753d
Generate proper TID and SID from WC9 (#3661)
* Pull proper TID and SID from WC9

* Add clarification, de-magic 0xF4240
2022-11-29 02:36:51 -08:00
Easy World
aab554d1b6
add missing language resources (#3663) 2022-11-28 09:36:08 -08:00
Kurt
4761a07311 Suggest min formarg for Mankey/Pawniard->stage3 2022-11-27 18:47:18 -08:00
sora10pls
d64fdb68ad Label event raid identifier save block
0 for no event active
20221125 for Eevee
2022-11-27 21:31:04 -05:00
Kurt
eab43ad59b Remove "None" version from pkmdb/encdb/settings cb 2022-11-27 16:13:31 -08:00
RaigaX9
c64efa4137
Added Jetbrains and updated Python in gitignore (#3660) 2022-11-27 14:57:10 -08:00
Kurt
c790212291 Add Gen9 to pkmdb generation dropdown 2022-11-27 13:40:25 -08:00
Kurt
607cbc5eb6 Check pre-evolution TM flag permit if highest not 2022-11-27 12:51:05 -08:00
Kurt
7e0aa44a9a Update MiscVerifier.cs 2022-11-27 12:38:32 -08:00
Kurt
5f12ba78c9 Update MiscVerifier.cs 2022-11-27 11:30:02 -08:00
Kurt
5d8bfb6d76 Fix pk9 file startup failing to get fallback sav9 2022-11-27 11:22:53 -08:00
Yarkis
ce0251004f
Improvement of the French translation (#3658)
* Update lang_fr.txt

* Update LegalityCheckStrings_fr.txt

* Update MessageStrings_fr.txt

* Update lang_fr.txt
2022-11-27 10:56:41 -08:00
Easy World
d22d5786a1
More translations (#3654) 2022-11-27 10:56:31 -08:00
Kurt
6876997110 Use old bitmap layering method for full background
Closes #3656
Better to show stray pixels than to overwrite-blend semi-transparent downscaled images.
2022-11-27 10:56:16 -08:00
Kurt
6002611c80 Fix block value set cast on change language
Closes #3657
2022-11-27 10:52:01 -08:00
Kurt
2fe8b63589 Disallow Avalugg+Lilligant hisui eggs 2022-11-27 10:43:21 -08:00
Kurt
762f7171cc Fix single-chain ability revert check 2022-11-26 22:21:33 -08:00
6289 changed files with 383587 additions and 170763 deletions

View File

@ -5,30 +5,22 @@ root = true
charset = utf-8
indent_style = space
indent_size = 4
tab_width = 4
insert_final_newline = true
trim_trailing_whitespace = true
# Solution Files
[*.sln]
indent_style = space
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true
[*.txt]
insert_final_newline = false
# XML Project Files
[*.csproj]
indent_style = space
[*.{slnx,csproj}]
indent_size = 2
tab_width = 2
# Code Files
[*.cs]
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 4
tab_width = 4
[*.{cs,vb}]
end_of_line = crlf
csharp_prefer_braces = when_multiline:warning
dotnet_diagnostic.IDE0047.severity = none
dotnet_diagnostic.IDE0048.severity = none
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:suggest
@ -36,10 +28,72 @@ dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:sug
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:suggest
dotnet_style_parentheses_in_other_operators = always_for_clarity:suggest
[*.{cs,vb}]
csharp_indent_labels = one_less_than_current
csharp_prefer_braces = when_multiline:warning
csharp_prefer_simple_using_statement = true:suggestion
csharp_prefer_system_threading_lock = true:suggestion
csharp_style_namespace_declarations = block_scoped:silent
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_prefer_top_level_statements = true:silent
csharp_style_prefer_primary_constructors = true:suggestion
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_properties = true:silent
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = false:silent
csharp_using_directive_placement = outside_namespace:silent
dotnet_style_operator_placement_when_wrapping = beginning_of_line
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
dotnet_style_prefer_auto_properties = true:silent
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
dotnet_style_prefer_conditional_expression_over_return = true:silent
dotnet_style_explicit_tuple_names = true:suggestion
#### Naming styles ####
# Naming rules
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
# Symbol specifications
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
# Naming styles
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.capitalization = pascal_case
dotnet_naming_style.pascal_case.capitalization = pascal_case
# IDE0130: Namespace does not match folder structure
dotnet_diagnostic.IDE0130.severity = none
# WFO1000: Property does not configure the code serialization for its property content.
dotnet_diagnostic.WFO1000.severity = none

10
.github/README-de.md vendored
View File

@ -24,13 +24,13 @@ PKHeX erwartet entschlüsselte Spielstände. Da diese konsolenspezifisch verschl
## Screenshots
![Main Window](https://i.imgur.com/uXdJfRj.png)
![Main Window](https://i.imgur.com/0KYz0rO.png)
## Erstellen
PKHeX ist eine Windows Forms Anwendung, die das [.NET Framework v4.6](https://www.microsoft.com/de-de/download/details.aspx?id=48137) benötigt, mit experimenteller Unterstützung für [.NET 6.0](https://dotnet.microsoft.com/download/dotnet/6.0).
PKHeX ist eine Windows Forms Anwendung, welche die [.NET 10.0](https://dotnet.microsoft.com/download/dotnet/10.0) runtime benötigt.
Die Anwendung kann mit jedem Kompiler erstellt werden, der C# 10 unterstützt.
Die Anwendung kann mit jedem Kompiler erstellt werden, der C# 14 unterstützt.
### Erstell Konfiguration
@ -47,7 +47,3 @@ PKHeXs Pokémon Legends: Arceus Sprites kommen vom [National Pokédex - Icon Dex
### IDE
PKHeX kann mit IDEs wie [Visual Studio](https://visualstudio.microsoft.com/de/downloads/) durch das Öffnen der .sln oder .csproj Dateien geöffnet werden.
### GNU/Linux
Da GNU/Linux nicht das Hauptbetriebssystem der PKHeX Entwickler ist können Bugs auftreten. Manche kommen möglicherweise von GNU/Linux spezifischem Code in Mono/Wine und können deshalb nicht von jedem reproduziert werden kann.

12
.github/README-es.md vendored
View File

@ -18,19 +18,19 @@ La interfaz gráfica puede ser traducida con archivos de texto externos para dar
Pokémon Showdown asigna un código QR que puede ser importado/exportado para ayudar al compartir.
PKHeX espera archivos de guardado que no estén cifrados con las claves específicas de la consola. Use un gestor de archivos de guardado para importar y exportar información de la consola ([Checkpoint](https://github.com/FlagBrew/Checkpoint), save_manager, [JKSM](https://github.com/J-D-K/JKSM), o SaveDataFiler).
PKHeX espera archivos de guardado que no estén cifrados con las claves específicas de la consola. Use un gestor de archivos de guardado para importar y exportar información de la consola ([Checkpoint](https://github.com/FlagBrew/Checkpoint) o [JKSM](https://github.com/J-D-K/JKSM)).
**No apoyamos ni toleramos las trampas a expensas de otros. No uses un Pokémon modificado significativamente en batalla o en intercambios con quienes no estén al tanto de que estás usando un Pokémon modificado.**
## Capturas de Pantalla
![Pantalla principal](https://i.imgur.com/hN1pwQa.png)
![Pantalla principal](https://i.imgur.com/JFKIhnz.png)
## Building
PKHeX es una aplicación de Windows Forms que requiere [.NET Framework v4.6](https://www.microsoft.com/es-es/download/details.aspx?id=48137), con soporte experimental para [.NET 6.0](https://dotnet.microsoft.com/download/dotnet/6.0).
PKHeX es una aplicación de Windows Forms que requiere [.NET 10.0](https://dotnet.microsoft.com/download/dotnet/10.0).
El archivo ejecutable puede ser construido con cualquier compilador que soporte C# 10.
El archivo ejecutable puede ser construido con cualquier compilador que soporte C# 14.
### Configuraciones del Build
@ -47,7 +47,3 @@ PKHeX's Pokémon Legends: Arceus sprite collection is taken from the [National P
### IDE
PKHeX se puede abrir con un IDE como [Visual Studio](https://visualstudio.microsoft.com/es/downloads/), abriendo los archivos .sln o .csproj.
### GNU/Linux
GNU/Linux no es el sistema operativo principal de los desarrolladores de este proyecto, así que probablemente haya errores o bugs; de los cuales algunos pueden provenir de código no específico de GNU/Linux desde Mono o de Wine, con lo cual puede haber otros usuarios que no puedan reproducir ese error.

52
.github/README-fr.md vendored
View File

@ -1,52 +1,48 @@
PKHeX
=====
![License](https://img.shields.io/badge/License-GPLv3-blue.svg)
![Licence](https://img.shields.io/badge/License-GPLv3-blue.svg)
Éditeur de sauvegarde de la série de base Pokémon, programmé en [C#](https://fr.wikipedia.org/wiki/C_sharp).
Éditeur de sauvegarde des jeux principaux de la série Pokémon, programmé en [C#](https://fr.wikipedia.org/wiki/C_sharp).
Prend en charge les fichiers suivants :
* Enregistrer les fichiers ("main", \*.sav, \*.dsv, \*.dat, \*.gci, \*.bin)
* Fichiers de carte mémoire GameCube (\*.raw, \*.bin) contenant des sauvegardes de Pokémon GC.
Les fichiers suivants sont pris en charge :
* Fichiers de sauvegarde (« main », \*.sav, \*.dsv, \*.dat, \*.gci, \*.bin)
* Fichiers de carte mémoire GameCube (\*.raw, \*.bin) contenant des sauvegardes de jeux Pokémon GameCube.
* Fichiers d'entités Pokémon individuels (.pk\*, \*.ck3, \*.xk3, \*.pb7, \*.sk2, \*.bk4, \*.rk4)
* Fichiers de cadeau mystère (\*.pgt, \*.pcd, \*.pgf, .wc\*) y compris la conversion en .pk\*
* Importation d'entités GO Park (\*.gp1) incluant la conversion en .pb7
* Importation d'équipes à partir de 3DS Battle Videos
* Transfert d'une génération à l'autre, conversion des formats en cours de route.
* Fichiers de Cadeau Mystère (\*.pgt, \*.pcd, \*.pgf, .wc\*), incluant la conversion en .pk\*
* Importation d'entités GO Park (\*.gp1), incluant la conversion en .pb7
* Importation d'équipes à partir de vidéos de combat 3DS déchiffrées
* Transfert d'une génération à l'autre, avec une conversion du format au passage.
Les données sont affichées dans une vue qui peut être modifiée et enregistrée. L'interface peut être traduite avec des fichiers de ressources/textes externes afin que différentes langues puissent être prises en charge.
Les données sont affichées sur une interface graphique, permettant de faire des modifications et des sauvegardes. L'interface peut être traduite avec des fichiers de ressources/textes externes afin que différentes langues puissent être prises en charge.
Les ensembles Pokémon Showdown et les QR codes peuvent être importés/exportés pour faciliter le partage.
Les sets Pokémon Showdown! et les QR codes peuvent être importés/exportés pour faciliter le partage.
PKHeX attend des fichiers de sauvegarde qui ne sont pas chiffrés avec des clés spécifiques à la console. Utilisez un gestionnaire de données enregistrées pour importer et exporter des données enregistrées à partir de la console ([Checkpoint](https://github.com/FlagBrew/Checkpoint), save_manager, [JKSM](https://github.com/J-D-K/JKSM) ou SaveDataFiler).
PKHeX demande des fichiers de sauvegarde qui ne sont pas chiffrés par des clés spécifiques aux consoles. Utilisez un gestionnaire de sauvegardes pour importer et exporter des sauvegardes depuis une console ([Checkpoint](https://github.com/FlagBrew/Checkpoint), save_manager, [JKSM](https://github.com/J-D-K/JKSM), ou SaveDataFiler).
**Nous ne soutenons ni ne tolérons la tricherie aux dépens des autres. N'utilisez pas de Pokémon piratés de manière significative au combat ou dans des échanges avec ceux qui ne savent pas que des Pokémon piratés sont en cours d'utilisation.**
**Nous ne soutenons ni ne tolérons la tricherie aux dépens des autres. N'utilisez pas de Pokémon piratés de manière significative en combat ou en échanges avec ceux qui ne savent pas que des Pokémon piratés sont utilisés.**
## Captures d'écran
## Capture d'écran
![Main Window](https://i.imgur.com/d63DD3I.png)
![Fenêtre principale](https://i.imgur.com/CpUzqmY.png)
## Construction
## Compilation
PKHeX est une application Windows Forms qui nécessite [.NET Framework v4.6](https://www.microsoft.com/fr-fr/download/details.aspx?id=48137), avec une prise en charge expérimentale de [.NET 6.0.](https://dotnet.microsoft.com/download/dotnet/6.0)
PKHeX est une application Windows Forms qui nécessite [.NET 10](https://dotnet.microsoft.com/download/dotnet/10.0).
L'exécutable peut être construit avec n'importe quel compilateur prenant en charge C# 10.
L'exécutable peut être compilé avec n'importe quel compilateur prenant en charge C# 14.
### Construire les configurations
### Configurations de la compilation
Utilisez les configurations Debug ou Release lors de la construction. Il n'y a pas de code spécifique à la plate-forme à craindre!
Utilisez la configuration Debug ou Release lors de la compilation. Aucun code spécifique à une plateforme n'est utilisée dans le programme, donc soyez sans crainte !
## Dépendances
Le code de génération du QR code de PKHeX est extrait de [QRCoder](https://github.com/codebude/QRCoder), qui est [sous licence MIT](https://github.com/codebude/QRCoder/blob/master/LICENSE.txt).
Le code de génération des QR codes de PKHeX est extrait de [QRCoder](https://github.com/codebude/QRCoder), qui est [sous licence MIT](https://github.com/codebude/QRCoder/blob/master/LICENSE.txt).
La collection de sprites shiny de PKHeX est tirée de [pokesprite](https://github.com/msikma/pokesprite), qui est [sous licence MIT](https://github.com/msikma/pokesprite/blob/master/LICENSE).
La collection de sprites chromatiques de PKHeX est tirée de [pokesprite](https://github.com/msikma/pokesprite), qui est [sous licence MIT](https://github.com/msikma/pokesprite/blob/master/LICENSE).
PKHeX's Pokémon Legends: Arceus sprite collection is taken from the [National Pokédex - Icon Dex](https://www.deviantart.com/pikafan2000/art/National-Pokedex-Version-Delta-Icon-Dex-824897934) project and its abundance of collaborators and contributors.
La collection de sprites Légendes Pokémon : Arceus de PKHeX est tirée du projet [National Pokédex - Icon Dex](https://www.deviantart.com/pikafan2000/art/National-Pokedex-Version-Delta-Icon-Dex-824897934), avec son abondance de collaborateurs et de contributeurs.
## IDE
PKHeX peut être ouvert avec des IDE tels que [Visual Studio](https://visualstudio.microsoft.com/fr/downloads/) en ouvrant le fichier .sln ou .csproj.
## GNU/Linux
GNU/Linux n'est pas le système d'exploitation principal des développeurs de ce programme, il peut donc y avoir des bugues; certains peuvent provenir de code non spécifique à GNU/Linux de Mono/Wine, donc d'autres utilisateurs peuvent ne pas être en mesure de reproduire l'erreur que vous rencontrez.
PKHeX peut être ouvert avec des IDEs tels que [Visual Studio](https://visualstudio.microsoft.com/fr/downloads/) en ouvrant le fichier .sln ou .csproj.

24
.github/README-it.md vendored
View File

@ -4,37 +4,37 @@ PKHeX
Editor di Salvataggi Pokémon per la serie principale, programmato in [C#](https://it.wikipedia.org/wiki/C_sharp).
Supporta i seguenti file:
Supporta i seguenti tipi di file:
* File di salvataggio ("main", \*.sav, \*.dsv, \*.dat, \*.gci, \*.bin)
* File di Memory Card GameCube (\*.raw, \*.bin) contenenti File di Salvataggio Pokémon.
* File di Entità Pokémon individuali (.pk\*, \*.ck3, \*.xk3, \*.pb7, \*.sk2, \*.bk4, \*.rk4)
* File di Dono Segreto (\*.pgt, \*.pcd, \*.pgf, .wc\*) inclusa conversione in .pk\*
* Importazione di Entità del Go Park (\*.gp1) inclusa conversione in .pb7
* Importazione di Entità del GO Park (\*.gp1) inclusa conversione in .pb7
* Importazione di squadre da Video Lotta del 3DS decriptati
* Trasferimento da una generazione all'altra, convertendo i formati propriamente.
I dati sono mostrati in una finestra che può essere modificata e salvata.
L'interfaccia può essere tradotta con risorse/file di testo esterni, così che sia possibile supportare diversi linguaggi.
L'interfaccia può essere tradotta tramite risorse/file di testo esterni, così che sia possibile supportare diverse lingue.
Set di Pokémon Showdown e QR Code possono essere importati/esportati per agevolare la condivisione di file.
PKHeX si aspetta file di salvataggio non criptati con le chiavi specifiche della console. È possibile usare un gestore di salvataggi per importare ed esportare dati di salvataggio dalla console ([Checkpoint](https://github.com/FlagBrew/Checkpoint), save_manager, [JKSM](https://github.com/J-D-K/JKSM), o SaveDataFiler).
PKHeX si aspetta file di salvataggio non criptati con le chiavi specifiche della console. È possibile utilizzare un gestore di salvataggi per importare ed esportare dati di salvataggio dalla console (come [Checkpoint](https://github.com/FlagBrew/Checkpoint) o [JKSM](https://github.com/J-D-K/JKSM)).
**Non supportiamo e non perdoniamo l'utilizzo di cheat a scapito di altri giocatori. Non utilizzare Pokémon modificati significativamente in lotte o scambi con giocatori che non ne sono a conoscenza.**
**Non supportiamo e non perdoniamo l'utilizzo di cheat a scapito di altri giocatori. Non utilizzare Pokémon alterati in lotte o scambi con giocatori che non ne sono a conoscenza.**
## Screenshots
![Main Window](https://i.imgur.com/QyQYtFg.png)
![Main Window](https://i.imgur.com/vrWs9Xq.png)
## Building
PKHeX è un applicazione Windows Form e necessita [.NET Framework v4.6](https://www.microsoft.com/it-it/download/details.aspx?id=48137), con supporto sperimentale per [.NET 6.0](https://dotnet.microsoft.com/download/dotnet/6.0).
PKHeX è un applicazione Windows Form che necessita del [.NET Desktop Runtime 10.0](https://dotnet.microsoft.com/download/dotnet/10.0).
L'eseguibile può essere compilato con qualsiasi compiler che supporti C# 10.
L'eseguibile può essere compilato con qualsiasi compiler che supporti C# 14.
### Configurazioni di Build
Puoi utilizzare la configurazione Debug o la configurazione Release per compilare. Non c'è alcun codice specifico per piattaforma di cui doversi preoccupare!
È possibile utilizzare la configurazione Debug o la configurazione Release per compilare la soluzione. Non c'è alcun codice specifico per piattaforma di cui doversi preoccupare!
## Dipendenze
@ -42,12 +42,8 @@ Il codice per la generazione di QR Code è preso da [QRCoder](https://github.com
La collezione di sprite shiny è presa da [pokesprite](https://github.com/msikma/pokesprite), concessa in licenza sotto [the MIT license](https://github.com/msikma/pokesprite/blob/master/LICENSE).
La collezione di sprite per Leggende Pokémon: Arceus è presa dal [National Pokédex - Icon Dex](https://www.deviantart.com/pikafan2000/art/National-Pokedex-Version-Delta-Icon-Dex-824897934) project grazie all'abbondanza dei collaboratori e dei contribuenti.
La collezione di sprite per Leggende Pokémon: Arceus è presa dal progetto [National Pokédex - Icon Dex](https://www.deviantart.com/pikafan2000/art/National-Pokedex-Version-Delta-Icon-Dex-824897934), grazie a tutti i collaboratori e contribuenti.
### IDE
PKHeX può essere aperto con IDE come [Visual Studio](https://visualstudio.microsoft.com/it/downloads/) aprendo il file .sln o il file .csproj.
### GNU/Linux
GNU/Linux non è il Sistema Operativo principale dei developer di questo programma, quindi potrebbero esserci bug; alcuni potrebbero provenire da codice GNU/Linux non specifico da Mono/Wine, per cui alcuni utenti potrebbero non essere in grado di riprodurre l'errore riscontrato.

49
.github/README-ko.md vendored Normal file
View File

@ -0,0 +1,49 @@
PKHeX(포케헥스)
=====
![License](https://img.shields.io/badge/License-GPLv3-blue.svg)
포켓몬 코어 시리즈 세이브 에디터, [C#](https://en.wikipedia.org/wiki/C_Sharp_%28programming_language%29)으로 프로그래밍됨.
다음 파일을 지원합니다:
* 세이브 파일 ("main", \*.sav, \*.dsv, \*.dat, \*.gci, \*.bin)
* GC 포켓몬 세이브 게임이 들어 있는 게임큐브 메모리 카드 파일(\*.raw, \*.bin) 포함
* 개별 포켓몬 엔티티 파일 (.pk\*, \*.ck3, \*.xk3, \*.pb7, \*.sk2, \*.bk4, \*.rk4)
* 이상한소포 파일(\*.pgt, \*.pcd, \*.pgf, .wc\*)을 .pk로 변환하는 기능 포함
* GO 파크 엔티티 가져오기 (\*.gp1) .pb7로 변환 포함
* Decrypted 3DS Battle Videos에서 팀 가져오기
* 한 세대에서 다른 세대로 이동하면서 그 과정에서 형식이 변환됩니다.
데이터는 편집하고 저장할 수 있는 보기로 표시됩니다.
인터페이스는 리소스/외부 텍스트 파일로 번역할 수 있어 다양한 언어를 지원할 수 있습니다.
포켓몬 쇼다운 세트와 QR 코드를 가져오고 내보낼 수 있어 공유에 도움을 줄 수 있습니다.
PKHeX는 콘솔 전용 키로 암호화되지 않은 세이브 파일을 요구합니다. ([Checkpoint](https://github.com/FlagBrew/Checkpoint), save_manager, [JKSM](https://github.com/J-D-K/JKSM), 또는 SaveDataFiler)를 사용하여 콘솔에서 세이브 데이터를 가져오고 내보낼 수 있습니다.
**저희는 타인을 희생시키는 부정행위를 지지하거나 묵인하지 않습니다. 해킹된 포켓몬이 사용 중이라는 사실을 모르는 사람들과의 배틀 또는 통신에서 심각하게 해킹된 포켓몬을 사용하지 마십시오.**
## 스크린샷
![Main Window](https://i.imgur.com/vDiaS7k.png)
## 빌드
PKHeX는 [.NET 10.0](https://dotnet.microsoft.com/download/dotnet/10.0)이 필요한 Windows Forms 애플리케이션입니다.
실행 파일은 C# 14을 지원하는 모든 컴파일러로 빌드할 수 있습니다.
### 빌드 구성
빌드할 때 디버그 또는 릴리스 빌드 구성을 사용하세요. 플랫폼 전용 코드는 걱정할 필요가 없습니다!
## 종속성
PKHeX의 QR 코드 생성 코드는 [the MIT license](https://github.com/codebude/QRCoder/blob/master/LICENSE.txt)에 따라 라이선스가 부여된 [QRCoder](https://github.com/codebude/QRCoder) 에서 가져왔습니다.
PKHeX의 이로치(색이다른) 스프라이트 컬렉션은 [the MIT license](https://github.com/msikma/pokesprite/blob/master/LICENSE)에 따라 라이선스가 부여된 [pokesprite](https://github.com/msikma/pokesprite)에서 가져왔습니다.
PKheX의 Pokémon LEGENDS 아르세우스 스프라이트 컬렉션은 [National Pokédex - Icon Dex](https://www.deviantart.com/pikafan2000/art/National-Pokedex-Version-Delta-Icon-Dex-824897934) 프로젝트와 수많은 협력자 및 기여자의 도움을 받아 만들어졌습니다.
### IDE(통합 개발 환경)
PKHeX는 .sln 또는 .csproj 파일을 열어 [Visual Studio](https://visualstudio.microsoft.com/downloads/)와 같은 IDE(통합 개발 환경)로 열 수 있습니다.

48
.github/README-zh-Hans.md vendored Normal file
View File

@ -0,0 +1,48 @@
PKHeX
=====
![License](https://img.shields.io/badge/License-GPLv3-blue.svg)
本程序是基于 [C#](https://zh.wikipedia.org/wiki/C♯) 语言进行编写的宝可梦核心系列游戏存档编辑器。
支持以下存档类型:
* 存档文件 ("main", \*.sav, \*.dsv, \*.dat, \*.gci, \*.bin)
* GameCube 宝可梦游戏存档包含 GameCube 记忆存档 (\*.raw, \*.bin)
* 单个宝可梦实体文件 (.pk\*, \*.ck3, \*.xk3, \*.pb7, \*.sk2, \*.bk4, \*.rk4)
* 神秘礼物文件 (\*.pgt, \*.pcd, \*.pgf, .wc\*) 并转换为 .pk\*
* 导入 GO Park存档 (\*.gp1) 并转换为 .pb7
* 从已破解的 3DS 对战视频中导入队伍
* 支持宝可梦在不同世代的间转移,并转换文件格式
各项数据显示于窗口界面上以便编辑及保存。
该界面可以通过 内部/外部 文本文件进行翻译,以便支持不同的语言。
可以导入/导出宝可梦Showdown代码和二维码以协助共享。
PKHeX 所读取存档文件必须是未经主机唯一密钥加密,因此请使用存档管理软件(如 [Checkpoint](https://github.com/FlagBrew/Checkpoint) save_manager [JKSM](https://github.com/J-D-K/JKSM)或 SaveDataFiler)以从主机中导入导出存档 .
**我们反对且不会纵容通过作弊手段损害他人利益的行为。切勿将魔法宝可梦用于对战,或通过连接交换给不知情的训练家手中。**
## 截图
![主介面](https://i.imgur.com/MPN4Hk9.png)
## 构建
PKHeX 是 Windows 窗口应用程序,依赖于 [.NET 10.0](https://dotnet.microsoft.com/download/dotnet/10.0)。
可以使用任何支持 C# 14 的编译器生成可执行文件。
### 构建配置
请使用 Debug 及 Release 配置通道进行构建,无须担心任何平台独有源代码相关问题!
## 依赖库
PKHeX 的 QR 码生成库来源于 [QRCoder](https://github.com/codebude/QRCoder) 依据 [MIT 许可证](https://github.com/codebude/QRCoder/blob/master/LICENSE.txt) 进行授权。
PKHeX 的异色精灵图示集合库来源于 [pokesprite](https://github.com/msikma/pokesprite) 依据 [MIT 许可证](https://github.com/codebude/QRCoder/blob/master/LICENSE.txt) 进行授权。
PKHeX 的“宝可梦传说:阿尔宙斯”精灵图片集来源于 [National Pokédex - Icon Dex](https://www.deviantart.com/pikafan2000/art/National-Pokedex-Version-Delta-Icon-Dex-824897934) 项目,及其多位各界协作者和贡献者。
### IDE
PKHeX 可以通过打开 .sln 或 .csproj 文件来使用 [Visual Studio](https://visualstudio.microsoft.com/downloads/) 等 IDE 打开。

48
.github/README-zh-Hant.md vendored Normal file
View File

@ -0,0 +1,48 @@
PKHeX
=====
![License](https://img.shields.io/badge/License-GPLv3-blue.svg)
本程式係基於 [C#](https://zh.wikipedia.org/wiki/C♯) 語言進行編寫之寶可夢核心系列遊戲儲存資料編輯器。
本程式支援下述檔案:
* 儲存資料檔 ("main", \*.sav, \*.dsv, \*.dat, \*.gci, \*.bin)
* 含 GameCube 寶可夢游戲儲存資料檔之 GameCube 記憶卡檔 (\*.raw, \*.bin)
* 單個寶可夢資料檔 (.pk\*, \*.ck3, \*.xk3, \*.pb7, \*.sk2, \*.bk4, \*.rk4)
* 神秘禮物檔 (\*.pgt, \*.pcd, \*.pgf, .wc\*) 並轉換至 .pk\*
* 匯入 Go 公園檔 (\*.gp1) 並轉換至 .pb7
* 從已解密 3DS 對戰視訊中匯入隊伍信息
* 將寶可夢從當前世代往後傳送,並順次轉換檔案格式
各項數據將顯示於介面上以便編輯及儲存。
介面亦可透過挂載內置/外置文字檔案以支援顯示多種語言。
可匯入/匯出 配置資訊及 QR 碼以便進行共有分享。
PKHeX 所讀取檔案須未經主機唯一密鑰加密,因而請使用儲存資料管理軟體(如 [Checkpoint](https://github.com/FlagBrew/Checkpoint) [JKSM](https://github.com/J-D-K/JKSM))以從主機中匯入匯出儲存資料 .
**我們反對亦不會縱容透過作弊手段損害他人利益之行為。切勿將魔法寶可夢用於對戰,或連線交換至不知情之訓練家手中。**
## 螢幕擷取截圖
![主介面](https://i.imgur.com/8IQx2jo.png)
## 構建
PKHeX 係 Windows 窗體應用程式,其須依賴於 [.NET 10.0](https://dotnet.microsoft.com/download/dotnet/10.0)。
程式可透過任意支援 C# 14 之編譯器構建。
### 構建配置
請使用 Debug 及 Release 配置通道進行構建,無須擔心任何平台獨有源代碼相關問題!
## 依賴庫
PKHeX 之 QR 碼生成庫來源於 [QRCoder](https://github.com/codebude/QRCoder) 其以 [MIT 許可證](https://github.com/codebude/QRCoder/blob/master/LICENSE.txt) 進行分發。
PKHeX 之異色精靈圖示集合庫來源於 [pokesprite](https://github.com/msikma/pokesprite) 其以 [MIT 許可證](https://github.com/msikma/pokesprite/blob/master/LICENSE) 進行分發。
PKHeX 之「寶可夢傳説:阿爾宙斯」精靈圖示集合庫來源於 [National Pokédex - Icon Dex](https://www.deviantart.com/pikafan2000/art/National-Pokedex-Version-Delta-Icon-Dex-824897934) 項目,及其多位各界協作者和貢獻者。
### IDE
PKHeX 可透過如 [Visual Studio](https://visualstudio.microsoft.com/downloads/) 等各類 IDE ,開啓 .sln 或 .csproj 檔案以打開。

View File

@ -1,52 +0,0 @@
PKHeX
=====
![License](https://img.shields.io/badge/License-GPLv3-blue.svg)
本程式係基於 [C#](https://zh.wikipedia.org/wiki/C♯) 語言進行編寫之寶可夢核心系列遊戲儲存資料編輯器。
本程式支援下述檔案:
* 儲存資料檔 ("main", \*.sav, \*.dsv, \*.dat, \*.gci, \*.bin)
* 含 GameCube 寶可夢游戲儲存資料檔之 GameCube 記憶卡檔 (\*.raw, \*.bin)
* 單個寶可夢資料檔 (.pk\*, \*.ck3, \*.xk3, \*.pb7, \*.sk2, \*.bk4, \*.rk4)
* 神秘禮物檔 (\*.pgt, \*.pcd, \*.pgf, .wc\*) 並轉換至 .pk\*
* 匯入 Go 公園檔 (\*.gp1) 並轉換至 .pb7
* 從已解密 3DS 對戰視訊中匯入隊伍信息
* 將寶可夢從當前世代往後傳送,並順次轉換檔案格式
各項數據將顯示於介面上以便編輯及儲存。
介面亦可透過挂載內置/外置文字檔案以支援顯示多種語言。
可匯入/匯出 配置資訊及 QR 碼以便進行共有分享。
PKHeX 所讀取檔案須未經主機唯一密鑰加密,因而請使用儲存資料管理軟體(如 [Checkpoint](https://github.com/FlagBrew/Checkpoint) save_manager [JKSM](https://github.com/J-D-K/JKSM) 抑或是 SaveDataFiler)以從主機中匯入匯出儲存資料 .
**我們反對亦不會縱容透過作弊手段損害他人利益之行為。切勿將魔法寶可夢用於對戰,或連線交換至不知情之訓練家手中。**
## 螢幕擷取截圖
![主介面](https://i.imgur.com/CHgFTXb.png)
## 構建
PKHeX 係 Windows 窗體應用程式,其須依賴於 [.NET Framework v4.6](https://www.microsoft.com/en-us/download/details.aspx?id=48137) 而運行,同時本程式亦實驗式地支援 [.NET 6.0](https://dotnet.microsoft.com/download/dotnet/6.0)。
程式可透過任意支援 C# 10 之編譯器構建。
### 構建配置
請使用 Debug 及 Release 配置通道進行構建,無須擔心任何平台獨有源代碼相關問題!
## 依賴庫
PKHeX 之 QR 碼生成庫來源於 [QRCoder](https://github.com/codebude/QRCoder) 其以 [MIT 許可證](https://github.com/codebude/QRCoder/blob/master/LICENSE.txt) 進行分發。
PKHeX 之異色精靈圖示集合庫來源於 [pokesprite](https://github.com/msikma/pokesprite) 其以 [MIT 許可證](https://github.com/msikma/pokesprite/blob/master/LICENSE) 進行分發。
PKHeX 之「寶可夢傳説:阿爾宙斯」精靈圖示集合庫來源於 [National Pokédex - Icon Dex](https://www.deviantart.com/pikafan2000/art/National-Pokedex-Version-Delta-Icon-Dex-824897934) 項目,及其多位各界協作者和貢獻者。
### IDE
PKHeX 可透過如 [Visual Studio](https://visualstudio.microsoft.com/downloads/) 等各類 IDE ,開啓 .sln 或 .csproj 檔案以打開。
### GNU/Linux
GNU/Linux 非本程式開發者主要作業系統,因而於 GNU/Linux 平臺上運行本程式時可能存在 Bug 部分 Bug 亦可能由 Mono/Wine 等非 GNU/Linux 特定源代碼所引入,因而其他使用者可能無法復現你所遇到之 Bug。

42
.gitignore vendored
View File

@ -169,6 +169,17 @@ UpgradeLog*.htm
App_Data/*.mdf
App_Data/*.ldf
# Local History for Visual Studio
.localhistory/
# Local History for Visual Studio Code
.history/
#################
## Jetbrains
#################
.idea/
*.sln.iml
#############
## Qt Creator
@ -216,37 +227,6 @@ $RECYCLE.BIN/
pingme.txt
#############
## Python
#############
*.py[co]
# Packages
*.egg
*.egg-info
dist/
build/
eggs/
parts/
var/
sdist/
develop-eggs/
.installed.cfg
# Installer logs
pip-log.txt
# Unit test / coverage reports
.coverage
.tox
# Translations
*.mo
# Mr Developer
.mr.developer.cfg
#################
## MonoDevelop
#################

View File

@ -1,12 +1,13 @@
<Project>
<PropertyGroup>
<Version>22.11.26</Version>
<LangVersion>10</LangVersion>
<Version>26.03.20</Version>
<LangVersion>14</LangVersion>
<Nullable>enable</Nullable>
<NeutralLanguage>en</NeutralLanguage>
<Product>PKHeX</Product>
<Company>Project Pokémon</Company>
<Authors>Kaphotics</Authors>
<Copyright>Kaphotics</Copyright>
<SourceRevisionId>$([System.DateTime]::UtcNow.ToString("yyMMddHHmmss"))</SourceRevisionId>
</PropertyGroup>
</Project>

View File

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using static PKHeX.Core.Ball;
namespace PKHeX.Core;
@ -9,189 +8,149 @@ namespace PKHeX.Core;
/// </summary>
public static class BallApplicator
{
private const Ball BallMin = Master; // first defined Enum value
private const Ball BallMax = LAOrigin; // all indexes up to and including LAOrigin are defined Enum values.
/// <summary>
/// Maximum number of <see cref="Ball"/> values that can be returned in a span.
/// </summary>
public const byte MaxBallSpanAlloc = (byte)BallMax + 1;
private static IEncounterTemplate Get(LegalityAnalysis la) => la.EncounterOriginal;
/// <remarks>
/// Requires checking the <see cref="LegalityAnalysis"/>.
/// </remarks>
/// <inheritdoc cref="GetLegalBalls(Span{Ball}, PKM, IEncounterTemplate)"/>
public static int GetLegalBalls(Span<Ball> result, PKM pk) => GetLegalBalls(result, pk, new LegalityAnalysis(pk));
/// <inheritdoc cref="GetLegalBalls(Span{Ball}, PKM, IEncounterTemplate)"/>
public static int GetLegalBalls(Span<Ball> result, PKM pk, LegalityAnalysis la) => GetLegalBalls(result, pk, Get(la));
/// <summary>
/// Gets all balls that are legal for the input <see cref="PKM"/>.
/// </summary>
/// <remarks>
/// Requires checking the <see cref="LegalityAnalysis"/> for every <see cref="Ball"/> that is tried.
/// </remarks>
/// <param name="result">Result storage.</param>
/// <param name="pk">Pokémon to retrieve a list of valid balls for.</param>
/// <returns>Enumerable list of <see cref="Ball"/> values that the <see cref="PKM"/> is legal with.</returns>
public static IEnumerable<Ball> GetLegalBalls(PKM pk)
/// <param name="enc">Encounter matched to.</param>
/// <returns>Count of <see cref="Ball"/> values that the <see cref="PKM"/> is legal with.</returns>
public static int GetLegalBalls(Span<Ball> result, PKM pk, IEncounterTemplate enc)
{
var clone = pk.Clone();
foreach (var b in BallList)
{
var ball = (int)b;
clone.Ball = ball;
if (clone.Ball != ball)
continue; // Some setters guard against out of bounds values.
if (new LegalityAnalysis(clone).Valid)
yield return b;
}
if (enc is EncounterInvalid)
return 0;
if (enc.Species is (ushort)Species.Nincada && pk.Species is (ushort)Species.Shedinja)
return GetLegalBallsEvolvedShedinja(result, pk, enc);
return LoadLegalBalls(result, pk, enc);
}
private static ReadOnlySpan<Ball> ShedinjaEvolve4 => [Sport, Poke];
private static int GetLegalBallsEvolvedShedinja(Span<Ball> result, PKM pk, IEncounterTemplate enc)
{
switch (enc)
{
case EncounterSlot4 s4 when IsNincadaEvolveInOrigin(pk, s4):
ShedinjaEvolve4.CopyTo(result);
return ShedinjaEvolve4.Length;
case EncounterSlot3 s3 when IsNincadaEvolveInOrigin(pk, s3):
return LoadLegalBalls(result, pk, enc);
}
result[0] = Poke;
return 1;
}
private static bool IsNincadaEvolveInOrigin(PKM pk, IEncounterTemplate enc)
{
// Rough check to see if Nincada evolved in the origin context (Gen3/4).
// Does not do PID/IV checks to know the original met level.
var current = pk.CurrentLevel;
var met = pk.MetLevel;
if (pk.Format == enc.Generation)
return current > met;
return enc.LevelMin != met && current > enc.LevelMin;
}
private static int LoadLegalBalls(Span<Ball> result, PKM pk, IEncounterTemplate enc)
{
int ctr = 0;
for (var b = BallMin; b <= BallMax; b++)
{
if (BallVerifier.VerifyBall(enc, b, pk).IsValid)
result[ctr++] = b;
}
return ctr;
}
/// <remarks>
/// Requires checking the <see cref="LegalityAnalysis"/>.
/// </remarks>
/// <inheritdoc cref="ApplyBallLegalRandom(PKM, IEncounterTemplate)"/>
public static byte ApplyBallLegalRandom(PKM pk) => ApplyBallLegalRandom(pk, new LegalityAnalysis(pk));
/// <inheritdoc cref="ApplyBallLegalRandom(PKM, IEncounterTemplate)"/>
public static byte ApplyBallLegalRandom(PKM pk, LegalityAnalysis la) => ApplyBallLegalRandom(pk, Get(la));
/// <summary>
/// Applies a random legal ball value if any exist.
/// </summary>
/// <remarks>
/// Requires checking the <see cref="LegalityAnalysis"/> for every <see cref="Ball"/> that is tried.
/// </remarks>
/// <param name="pk">Pokémon to modify.</param>
public static int ApplyBallLegalRandom(PKM pk)
/// <param name="enc">Encounter matched to.</param>
public static byte ApplyBallLegalRandom(PKM pk, IEncounterTemplate enc)
{
Span<Ball> balls = stackalloc Ball[MaxBallSpanAlloc];
var count = GetBallListFromColor(pk, balls);
var count = GetLegalBalls(balls, pk, enc);
balls = balls[..count];
Util.Shuffle(balls);
return ApplyFirstLegalBall(pk, balls);
Util.Rand.Shuffle(balls);
return ApplyFirstLegalBall(pk, balls, []);
}
/// <remarks>
/// Requires checking the <see cref="LegalityAnalysis"/>.
/// </remarks>
/// <inheritdoc cref="ApplyBallLegalByColor(PKM, IEncounterTemplate, PersonalColor)"/>
public static byte ApplyBallLegalByColor(PKM pk) => ApplyBallLegalByColor(pk, PersonalColorUtil.GetColor(pk));
/// <inheritdoc cref="ApplyBallLegalByColor(PKM, IEncounterTemplate, PersonalColor)"/>
public static byte ApplyBallLegalByColor(PKM pk, PersonalColor color) => ApplyBallLegalByColor(pk, new LegalityAnalysis(pk), color);
/// <inheritdoc cref="ApplyBallLegalByColor(PKM, IEncounterTemplate, PersonalColor)"/>
public static byte ApplyBallLegalByColor(PKM pk, LegalityAnalysis la, PersonalColor color) => ApplyBallLegalByColor(pk, Get(la), color);
/// <summary>
/// Applies a legal ball value if any exist, ordered by color.
/// </summary>
/// <remarks>
/// Requires checking the <see cref="LegalityAnalysis"/> for every <see cref="Ball"/> that is tried.
/// </remarks>
/// <param name="pk">Pokémon to modify.</param>
public static int ApplyBallLegalByColor(PKM pk)
/// <param name="enc">Encounter matched to.</param>
/// <param name="color">Color preference to order by.</param>
public static byte ApplyBallLegalByColor(PKM pk, IEncounterTemplate enc, PersonalColor color)
{
Span<Ball> balls = stackalloc Ball[MaxBallSpanAlloc];
GetBallListFromColor(pk, balls);
return ApplyFirstLegalBall(pk, balls);
var count = GetLegalBalls(balls, pk, enc);
balls = balls[..count];
var prefer = PersonalColorUtil.GetPreferredByColor(enc, color);
return ApplyFirstLegalBall(pk, balls, prefer);
}
/// <summary>
/// Applies a random ball value in a cyclical manner.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
public static int ApplyBallNext(PKM pk)
private static byte ApplyFirstLegalBall(PKM pk, Span<Ball> legal, ReadOnlySpan<Ball> prefer)
{
Span<Ball> balls = stackalloc Ball[MaxBallSpanAlloc];
GetBallList(pk.Ball, balls);
var next = balls[0];
return pk.Ball = (int)next;
}
private static int ApplyFirstLegalBall(PKM pk, Span<Ball> balls)
{
foreach (var b in balls)
foreach (var ball in prefer)
{
pk.Ball = (int)b;
if (new LegalityAnalysis(pk).Valid)
break;
if (Contains(legal, ball))
return pk.Ball = (byte)ball;
}
return pk.Ball;
}
private static int GetBallList(int ball, Span<Ball> result)
{
var balls = BallList;
var currentBall = (Ball)ball;
return GetCircularOnce(balls, currentBall, result);
}
private static int GetBallListFromColor(PKM pk, Span<Ball> result)
{
// Gen1/2 don't store color in personal info
var pi = pk.Format >= 3 ? pk.PersonalInfo : PersonalTable.USUM.GetFormEntry(pk.Species, 0);
var color = (PersonalColor)pi.Color;
var balls = BallColors[color];
var currentBall = (Ball)pk.Ball;
return GetCircularOnce(balls, currentBall, result);
}
private static int GetCircularOnce<T>(T[] items, T current, Span<T> result)
{
var currentIndex = Array.IndexOf(items, current);
if (currentIndex < 0)
currentIndex = items.Length - 2;
int ctr = 0;
for (int i = currentIndex + 1; i < items.Length; i++)
result[ctr++] = items[i];
for (int i = 0; i <= currentIndex; i++)
result[ctr++] = items[i];
return ctr;
}
private static readonly Ball[] BallList = (Ball[])Enum.GetValues(typeof(Ball));
private static int MaxBallSpanAlloc => BallList.Length;
static BallApplicator()
{
Span<Ball> exclude = stackalloc Ball[] {None, Poke};
Span<Ball> end = stackalloc Ball[] {Poke};
Span<Ball> all = stackalloc Ball[BallList.Length - exclude.Length];
all = all[..FillExcept(all, exclude, BallList)];
var colors = (PersonalColor[])Enum.GetValues(typeof(PersonalColor));
foreach (var c in colors)
foreach (var ball in legal)
{
// Replace the array reference with a new array that appends non-matching values, followed by the end values.
var defined = BallColors[c];
Span<Ball> match = (BallColors[c] = new Ball[all.Length + end.Length]);
defined.CopyTo(match);
FillExcept(match[defined.Length..], defined, all);
end.CopyTo(match[^end.Length..]);
if (!Contains(prefer, ball))
return pk.Ball = (byte)ball;
}
return pk.Ball; // fail
static int FillExcept(Span<Ball> result, ReadOnlySpan<Ball> exclude, ReadOnlySpan<Ball> all)
static bool Contains(ReadOnlySpan<Ball> balls, Ball ball)
{
int ctr = 0;
foreach (var b in all)
foreach (var b in balls)
{
if (Contains(exclude, b))
continue;
result[ctr++] = b;
}
return ctr;
static bool Contains(ReadOnlySpan<Ball> arr, Ball b)
{
foreach (var a in arr)
{
if (a == b)
return true;
}
return false;
if (b == ball)
return true;
}
return false;
}
}
/// <summary>
/// Priority Match ball IDs that match the color ID in descending order
/// </summary>
private static readonly Dictionary<PersonalColor, Ball[]> BallColors = new()
{
[PersonalColor.Red] = new[] { Cherish, Repeat, Fast, Heal, Great, Dream, Lure },
[PersonalColor.Blue] = new[] { Dive, Net, Great, Beast, Lure },
[PersonalColor.Yellow] = new[] { Level, Ultra, Repeat, Quick, Moon },
[PersonalColor.Green] = new[] { Safari, Friend, Nest, Dusk },
[PersonalColor.Black] = new[] { Luxury, Heavy, Ultra, Moon, Net, Beast },
[PersonalColor.Brown] = new[] { Level, Heavy },
[PersonalColor.Purple] = new[] { Master, Love, Dream, Heal },
[PersonalColor.Gray] = new[] { Heavy, Premier, Luxury },
[PersonalColor.White] = new[] { Premier, Timer, Luxury, Ultra },
[PersonalColor.Pink] = new[] { Love, Dream, Heal },
};
/// <summary>
/// Personal Data color IDs
/// </summary>
private enum PersonalColor : byte
{
Red,
Blue,
Yellow,
Green,
Black,
Brown,
Purple,
Gray,
White,
Pink,
}
}

View File

@ -1,39 +1,56 @@
namespace PKHeX.Core;
/// <summary>
/// Logic for applying a <see cref="PK1.Catch_Rate"/> value.
/// Logic for applying a <see cref="PK1.CatchRate"/> value.
/// </summary>
public static class CatchRateApplicator
{
public static int GetSuggestedCatchRate(PK1 pk1, SaveFile sav)
/// <summary>
/// Gets the suggested <see cref="PK1.CatchRate"/> for the entity.
/// </summary>
public static int GetSuggestedCatchRate(PK1 pk, SaveFile sav)
{
var la = new LegalityAnalysis(pk1);
return GetSuggestedCatchRate(pk1, sav, la);
var la = new LegalityAnalysis(pk);
return GetSuggestedCatchRate(pk, sav, la);
}
public static byte GetSuggestedCatchRate(PK1 pk1, SaveFile sav, LegalityAnalysis la)
/// <summary>
/// Gets the suggested <see cref="PK1.CatchRate"/> for the entity.
/// </summary>
public static byte GetSuggestedCatchRate(PK1 pk, SaveFile sav, LegalityAnalysis la)
{
// If it is already valid, just use the current value.
if (la.Valid)
return pk1.Catch_Rate;
return pk.CatchRate;
// If it has ever visited generation 2, the Held Item can be removed prior to trade back.
if (la.Info.Generation == 2)
return 0;
var v = la.EncounterMatch;
switch (v)
// Return the encounter's original value.
var enc = la.EncounterMatch;
switch (enc)
{
case EncounterTrade1 c:
return c.GetInitialCatchRate();
case EncounterStatic1E { Version: GameVersion.Stadium, Species: (int)Species.Psyduck}:
return pk1.Japanese ? (byte)167 : (byte)168; // Amnesia Psyduck has different catch rates depending on language
case EncounterGift1 { Trainer: EncounterGift1.TrainerType.Stadium, Species: (int)Species.Psyduck }:
return pk.Japanese ? (byte)167 : (byte)168; // Amnesia Psyduck has different catch rates depending on language
default:
{
if (sav.Version.Contains(v.Version) || v.Version.Contains(sav.Version))
return (byte)sav.Personal[v.Species].CatchRate;
if (!GameVersion.RB.Contains(v.Version))
return (byte)PersonalTable.Y[v.Species].CatchRate;
return (byte)PersonalTable.RB[v.Species].CatchRate;
}
var pt = GetPersonalTable(sav, enc.Version);
var pi = pt[enc.Species];
return pi.CatchRate;
}
}
private static PersonalTable1 GetPersonalTable(SaveFile sav, GameVersion version)
{
if (sav.Personal is PersonalTable1 pt)
{
var other = sav.Version;
if (other.Contains(version) || version.Contains(other))
return pt;
}
if (!GameVersion.RB.Contains(version))
return PersonalTable.Y;
return PersonalTable.RB;
}
}

View File

@ -7,72 +7,78 @@ namespace PKHeX.Core;
/// </summary>
public static class GenderApplicator
{
/// <summary>
/// Sets the <see cref="PKM.Gender"/> value, with special consideration for the <see cref="PKM.Format"/> values which derive the <see cref="PKM.Gender"/> value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="gender">Desired <see cref="PKM.Gender"/> value to set.</param>
/// <remarks>Has special logic for an unspecified gender.</remarks>
public static void SetSaneGender(this PKM pk, int gender)
extension(PKM pk)
{
int g = gender == -1 ? pk.GetSaneGender() : gender;
pk.SetGender(g);
}
/// <summary>
/// Sets the <see cref="PKM.Gender"/> value, with special consideration for the <see cref="PKM.Format"/> values which derive the <see cref="PKM.Gender"/> value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="gender">Desired <see cref="PKM.Gender"/> value to set.</param>
public static void SetGender(this PKM pk, int gender)
{
gender = Math.Min(2, Math.Max(0, gender));
if (pk.Gender == gender)
return;
if (pk.Format <= 2)
/// <summary>
/// Sets the <see cref="PKM.Gender"/> value, with special consideration for the <see cref="PKM.Format"/> values which derive the <see cref="PKM.Gender"/> value.
/// </summary>
/// <param name="gender">Desired <see cref="PKM.Gender"/> value to set.</param>
/// <remarks>Has special logic for an unspecified gender.</remarks>
public void SetSaneGender(byte gender)
{
pk.SetAttackIVFromGender(gender);
var g = gender > 2 ? pk.GetSaneGender() : gender;
pk.SetGender(g);
}
else if (pk.Format <= 5)
{
pk.SetPIDGender(gender);
pk.Gender = gender;
}
else
{
pk.Gender = gender;
}
}
/// <summary>
/// Sanity checks the provided <see cref="PKM.Gender"/> value, and returns a sane value.
/// </summary>
/// <param name="pk"></param>
/// <returns>Most-legal <see cref="PKM.Gender"/> value</returns>
public static int GetSaneGender(this PKM pk)
{
int gt = pk.PersonalInfo.Gender;
switch (gt)
/// <inheritdoc cref="SetSaneGender(PKM, byte)"/>
public void SetSaneGender(byte? gender)
{
case PersonalInfo.RatioMagicGenderless: return 2;
case PersonalInfo.RatioMagicFemale: return 1;
case PersonalInfo.RatioMagicMale: return 0;
var g = gender ?? pk.GetSaneGender();
pk.SetGender(g);
}
if (!pk.IsGenderValid())
return EntityGender.GetFromPIDAndRatio(pk.PID, gt);
return pk.Gender;
}
/// <summary>
/// Updates the <see cref="PKM.IV_ATK"/> for a Generation 1/2 format <see cref="PKM"/>.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="gender">Desired <see cref="PKM.Gender"/>.</param>
public static void SetAttackIVFromGender(this PKM pk, int gender)
{
var rnd = Util.Rand;
while (pk.Gender != gender)
pk.IV_ATK = rnd.Next(16);
/// <summary>
/// Sets the <see cref="PKM.Gender"/> value, with special consideration for the <see cref="PKM.Format"/> values which derive the <see cref="PKM.Gender"/> value.
/// </summary>
/// <param name="gender">Desired <see cref="PKM.Gender"/> value to set.</param>
public void SetGender(byte gender)
{
gender = Math.Clamp(gender, (byte)0, (byte)2);
if (pk.Gender == gender)
return;
if (pk.Format <= 2)
{
pk.SetAttackIVFromGender(gender);
}
else if (pk.Format <= 5)
{
pk.SetPIDGender(gender);
pk.Gender = gender;
}
else
{
pk.Gender = gender;
}
}
/// <summary>
/// Sanity checks the provided <see cref="PKM.Gender"/> value, and returns a sane value.
/// </summary>
/// <returns>Most-legal <see cref="PKM.Gender"/> value</returns>
public byte GetSaneGender()
{
var gt = pk.PersonalInfo.Gender;
switch (gt)
{
case PersonalInfo.RatioMagicGenderless: return 2;
case PersonalInfo.RatioMagicFemale: return 1;
case PersonalInfo.RatioMagicMale: return 0;
}
if (!pk.IsGenderValid())
return EntityGender.GetFromPIDAndRatio(pk.PID, gt);
return pk.Gender;
}
/// <summary>
/// Updates the <see cref="PKM.IV_ATK"/> for a Generation 1/2 format <see cref="PKM"/>.
/// </summary>
/// <param name="gender">Desired <see cref="PKM.Gender"/>.</param>
public void SetAttackIVFromGender(byte gender)
{
var rnd = Util.Rand;
while (pk.Gender != gender)
pk.IV_ATK = rnd.Next(16);
}
}
}

View File

@ -7,23 +7,24 @@ namespace PKHeX.Core;
/// </summary>
public static class HiddenPowerApplicator
{
/// <summary>
/// Sets the <see cref="PKM.IVs"/> to match a provided <see cref="hiddenPowerType"/>.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="hiddenPowerType">Desired Hidden Power typing.</param>
public static void SetHiddenPower(this PKM pk, int hiddenPowerType)
extension(PKM pk)
{
Span<int> IVs = stackalloc int[6];
pk.GetIVs(IVs);
HiddenPower.SetIVsForType(hiddenPowerType, IVs, pk.Context);
pk.SetIVs(IVs);
}
/// <summary>
/// Sets the <see cref="PKM.IVs"/> to match a provided <see cref="hiddenPowerType"/>.
/// </summary>
/// <param name="hiddenPowerType">Desired Hidden Power typing.</param>
public void SetHiddenPower(int hiddenPowerType)
{
Span<int> IVs = stackalloc int[6];
pk.GetIVs(IVs);
HiddenPower.SetIVsForType(hiddenPowerType, IVs, pk.Context);
pk.SetIVs(IVs);
}
/// <summary>
/// Sets the <see cref="PKM.IVs"/> to match a provided <see cref="hiddenPowerType"/>.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="hiddenPowerType">Desired Hidden Power typing.</param>
public static void SetHiddenPower(this PKM pk, MoveType hiddenPowerType) => pk.SetHiddenPower((int)hiddenPowerType);
/// <summary>
/// Sets the <see cref="PKM.IVs"/> to match a provided <see cref="hiddenPowerType"/>.
/// </summary>
/// <param name="hiddenPowerType">Desired Hidden Power typing.</param>
public void SetHiddenPower(MoveType hiddenPowerType) => pk.SetHiddenPower((int)hiddenPowerType);
}
}

View File

@ -1,9 +1,9 @@
using System;
using System;
namespace PKHeX.Core;
/// <summary>
/// Logic for modifying the <see cref="PKM.MarkValue"/>.
/// Logic for modifying the <see cref="IAppliedMarkings"/>.
/// </summary>
public static class MarkingApplicator
{
@ -14,21 +14,42 @@ public static class MarkingApplicator
public static Func<PKM, Func<int, int, int>> MarkingMethod { get; set; } = FlagHighLow;
/// <summary>
/// Sets the <see cref="PKM.MarkValue"/> to indicate flawless (or near-flawless) <see cref="PKM.IVs"/>.
/// Sets the applied Markings to indicate flawless (or near-flawless) <see cref="PKM.IVs"/>.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
public static void SetMarkings(this PKM pk)
{
if (pk.MarkingCount < 6)
if (pk is not IAppliedMarkings { MarkingCount: 6 })
return; // insufficient marking indexes
if (pk is IAppliedMarkings<MarkingColor> c)
c.SetMarkings(pk);
else if (pk is IAppliedMarkings<bool> b)
b.SetMarkings(pk);
}
/// <inheritdoc cref="SetMarkings(PKM)"/>
public static void SetMarkings(this IAppliedMarkings<bool> mark, PKM pk)
{
var method = MarkingMethod(pk);
pk.SetMarking(0, method(pk.IV_HP , 0));
pk.SetMarking(1, method(pk.IV_ATK, 1));
pk.SetMarking(2, method(pk.IV_DEF, 2));
pk.SetMarking(3, method(pk.IV_SPA, 3));
pk.SetMarking(4, method(pk.IV_SPD, 4));
pk.SetMarking(5, method(pk.IV_SPE, 5));
mark.SetMarking(0, method(pk.IV_HP , 0) == 1);
mark.SetMarking(1, method(pk.IV_ATK, 1) == 1);
mark.SetMarking(2, method(pk.IV_DEF, 2) == 1);
mark.SetMarking(3, method(pk.IV_SPA, 3) == 1);
mark.SetMarking(4, method(pk.IV_SPD, 4) == 1);
mark.SetMarking(5, method(pk.IV_SPE, 5) == 1);
}
/// <inheritdoc cref="SetMarkings(PKM)"/>
public static void SetMarkings(this IAppliedMarkings<MarkingColor> mark, PKM pk)
{
var method = MarkingMethod(pk);
mark.SetMarking(0, (MarkingColor)method(pk.IV_HP, 0));
mark.SetMarking(1, (MarkingColor)method(pk.IV_ATK, 1));
mark.SetMarking(2, (MarkingColor)method(pk.IV_DEF, 2));
mark.SetMarking(3, (MarkingColor)method(pk.IV_SPA, 3));
mark.SetMarking(4, (MarkingColor)method(pk.IV_SPD, 4));
mark.SetMarking(5, (MarkingColor)method(pk.IV_SPE, 5));
}
/// <summary>
@ -37,18 +58,19 @@ public static void SetMarkings(this PKM pk)
/// <param name="pk">Pokémon to modify.</param>
/// <param name="index">Marking index to toggle</param>
/// <returns>Current marking value</returns>
public static int ToggleMarking(this PKM pk, int index)
public static void ToggleMarking(this PKM pk, int index)
{
var marking = pk.GetMarking(index);
var revised = NextMarking(pk.Format, marking);
pk.SetMarking(index, revised);
return revised;
if (pk is IAppliedMarkings<MarkingColor> c)
c.SetMarking(index, c.GetMarking(index).Next());
else if (pk is IAppliedMarkings<bool> b)
b.SetMarking(index, !b.GetMarking(index));
}
private static int NextMarking(int format, int marking) => format switch
private static MarkingColor Next(this MarkingColor value) => value switch
{
<= 6 => marking ^ 1, // toggle : 0 (off) | 1 (on)
_ => (marking + 1) % 3, // cycle 0->1->2->0... : 0 (none) | 1 (blue) | 2 (pink)
MarkingColor.Blue => MarkingColor.Pink,
MarkingColor.Pink => MarkingColor.None,
_ => MarkingColor.Blue,
};
private static Func<int, int, int> FlagHighLow(PKM pk)

View File

@ -1,50 +1,52 @@
namespace PKHeX.Core;
namespace PKHeX.Core;
/// <summary>
/// Logic for modifying the Memory parameters of a <see cref="PKM"/>.
/// </summary>
public static class MemoryApplicator
{
/// <summary>
/// Sets all Memory related data to the default value (zero).
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
public static void ClearMemories(this PKM pk)
extension(PKM pk)
{
if (pk is IAffection a)
a.OT_Affection = a.HT_Affection = 0;
if (pk is IMemoryOT o)
o.ClearMemoriesOT();
if (pk is IMemoryHT h)
h.ClearMemoriesHT();
}
/// <summary>
/// Sets the Memory details to a Hatched Egg's memories.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
public static void SetHatchMemory6(this PKM pk)
{
if (pk is IMemoryOT o)
/// <summary>
/// Sets all Memory related data to the default value (zero).
/// </summary>
public void ClearMemories()
{
o.OT_Memory = 2;
o.OT_Feeling = MemoryContext6.GetRandomFeeling6(2);
o.OT_Intensity = 1;
o.OT_TextVar = pk.XY ? (ushort)43 : (ushort)27; // riverside road : battling spot
if (pk is IAffection a)
a.OriginalTrainerAffection = a.HandlingTrainerAffection = 0;
if (pk is IMemoryOT o)
o.ClearMemoriesOT();
if (pk is IMemoryHT h)
h.ClearMemoriesHT();
}
/// <summary>
/// Sets the Memory details to a Hatched Egg's memories specific to <see cref="EntityContext.Gen6"/> origin.
/// </summary>
public void SetHatchMemory6()
{
if (pk is IMemoryOT o)
{
o.OriginalTrainerMemory = 2;
o.OriginalTrainerMemoryFeeling = MemoryContext6.GetRandomFeeling6(2);
o.OriginalTrainerMemoryIntensity = 1;
o.OriginalTrainerMemoryVariable = pk.XY ? (ushort)43 : (ushort)27; // riverside road : battling spot
}
if (pk is IAffection a)
a.OriginalTrainerAffection = 0;
}
if (pk is IAffection a)
a.OT_Affection = 0;
}
/// <summary>
/// Sets a random memory specific to <see cref="GameVersion.Gen6"/> locality.
/// Sets a random memory specific to <see cref="EntityContext.Gen6"/> origin.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
public static void SetRandomMemory6(this PK6 pk)
{
// for lack of better randomization :)
pk.OT_Memory = 63;
pk.OT_Intensity = 6;
pk.OT_Feeling = MemoryContext6.GetRandomFeeling6(pk.OT_Memory);
const byte memory = 63; // almost got lost when it explored a forest with {Trainer}
pk.OriginalTrainerMemory = memory;
pk.OriginalTrainerMemoryFeeling = MemoryContext6.GetRandomFeeling6(memory);
pk.OriginalTrainerMemoryIntensity = MemoryContext6.MaxIntensity;
}
}

View File

@ -7,104 +7,77 @@ namespace PKHeX.Core;
/// </summary>
public static class MoveApplicator
{
/// <summary>
/// Sets the individual PP Up count values depending if a Move is present in the move's slot or not.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="moves"><see cref="PKM.Moves"/> to use.</param>
public static void SetMaximumPPUps(this PKM pk, ReadOnlySpan<ushort> moves)
extension(PKM pk)
{
pk.Move1_PPUps = GetPPUpCount(moves[0]);
pk.Move2_PPUps = GetPPUpCount(moves[1]);
pk.Move3_PPUps = GetPPUpCount(moves[2]);
pk.Move4_PPUps = GetPPUpCount(moves[3]);
pk.SetMaximumPPCurrent(moves);
static int GetPPUpCount(ushort moveID) => moveID != 0 ? 3 : 0;
}
/// <summary>
/// Sets the individual PP Up count values depending if a Move is present in the move slot or not.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
public static void SetMaximumPPUps(this PKM pk)
{
Span<ushort> moves = stackalloc ushort[4];
pk.GetMoves(moves);
pk.SetMaximumPPUps(moves);
}
/// <summary>
/// Updates the <see cref="PKM.Moves"/> and updates the current PP counts.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="input"><see cref="PKM.Moves"/> to set.</param>
/// <param name="maxPP">Option to maximize PP Ups</param>
public static void SetMoves(this PKM pk, ReadOnlySpan<ushort> input, bool maxPP = false)
{
Span<ushort> moves = stackalloc ushort[4];
if (input.Length <= 4)
input.CopyTo(moves);
else
input[..4].CopyTo(moves);
// Remote all indexes with a value above the maximum move ID allowed by the format.
var max = pk.MaxMoveID;
for (int i = 0; i < moves.Length; i++)
/// <summary>
/// Sets the individual PP Up count values depending on if a Move is present in the move's slot or not.
/// </summary>
/// <param name="moves"><see cref="PKM.Moves"/> to use.</param>
public void SetMaximumPPUps(ReadOnlySpan<ushort> moves)
{
if (moves[i] > max)
moves[i] = 0;
pk.Move1_PPUps = GetPPUpCount(moves[0]);
pk.Move2_PPUps = GetPPUpCount(moves[1]);
pk.Move3_PPUps = GetPPUpCount(moves[2]);
pk.Move4_PPUps = GetPPUpCount(moves[3]);
pk.SetMaximumPPCurrent(moves);
static int GetPPUpCount(ushort moveID)
{
if (Legal.IsPPUpAvailable(moveID))
return 3;
return 0;
}
}
pk.SetMoves(moves);
if (maxPP && Legal.IsPPUpAvailable(pk))
/// <summary>
/// Sets the individual PP Up count values depending on if a Move is present in the move slot or not.
/// </summary>
public void SetMaximumPPUps()
{
Span<ushort> moves = stackalloc ushort[4];
pk.GetMoves(moves);
pk.SetMaximumPPUps(moves);
else
pk.SetMaximumPPCurrent(moves);
pk.FixMoves();
}
}
/// <summary>
/// Updates the individual PP count values for each move slot based on the maximum possible value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="moves"><see cref="PKM.Moves"/> to use (if already known). Will fetch the current <see cref="PKM.Moves"/> if not provided.</param>
public static void SetMaximumPPCurrent(this PKM pk, ReadOnlySpan<ushort> moves)
{
pk.Move1_PP = moves.Length == 0 ? 0 : pk.GetMovePP(moves[0], pk.Move1_PPUps);
pk.Move2_PP = moves.Length <= 1 ? 0 : pk.GetMovePP(moves[1], pk.Move2_PPUps);
pk.Move3_PP = moves.Length <= 2 ? 0 : pk.GetMovePP(moves[2], pk.Move3_PPUps);
pk.Move4_PP = moves.Length <= 3 ? 0 : pk.GetMovePP(moves[3], pk.Move4_PPUps);
}
/// <summary>
/// Updates the <see cref="PKM.Moves"/> and updates the current PP counts.
/// </summary>
/// <param name="input"><see cref="PKM.Moves"/> to set.</param>
/// <param name="maxPP">Option to maximize PP Ups</param>
public void SetMoves(ReadOnlySpan<ushort> input, bool maxPP = false)
{
Span<ushort> moves = stackalloc ushort[4];
if (input.Length <= 4)
input.CopyTo(moves);
else
input[..4].CopyTo(moves);
/// <summary>
/// Updates the individual PP count values for each move slot based on the maximum possible value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="moves"><see cref="PKM.Moves"/> to use (if already known). Will fetch the current <see cref="PKM.Moves"/> if not provided.</param>
public static void SetMaximumPPCurrent(this PKM pk, Moveset moves)
{
pk.Move1_PP = moves.Move1 == 0 ? 0 : pk.GetMovePP(moves.Move1, pk.Move1_PPUps);
pk.Move2_PP = moves.Move2 == 0 ? 0 : pk.GetMovePP(moves.Move2, pk.Move2_PPUps);
pk.Move3_PP = moves.Move3 == 0 ? 0 : pk.GetMovePP(moves.Move3, pk.Move3_PPUps);
pk.Move4_PP = moves.Move4 == 0 ? 0 : pk.GetMovePP(moves.Move4, pk.Move4_PPUps);
}
// Remote all indexes with a value above the maximum move ID allowed by the format.
var max = pk.MaxMoveID;
for (int i = 0; i < moves.Length; i++)
{
if (moves[i] > max)
moves[i] = 0;
}
/// <summary>
/// Updates the individual PP count values for each move slot based on the maximum possible value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
public static void SetMaximumPPCurrent(this PKM pk)
{
Span<ushort> moves = stackalloc ushort[4];
pk.GetMoves(moves);
pk.SetMaximumPPCurrent(moves);
}
pk.SetMoves(moves);
if (maxPP && Legal.IsPPUpAvailable(pk))
pk.SetMaximumPPUps(moves);
pk.FixMoves();
}
/// <summary>
/// Refreshes the Move PP for the desired move.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="index">Move PP to refresh.</param>
public static void SetSuggestedMovePP(this PKM pk, int index) => pk.HealPPIndex(index);
/// <summary>
/// Updates the individual PP count values for each move slot based on the maximum possible value.
/// </summary>
/// <param name="moves"><see cref="PKM.Moves"/> to use (if already known).</param>
public void SetMaximumPPCurrent(ReadOnlySpan<ushort> moves)
{
// In some games, move[i] == 0` *should* set 0, but the game's configuration has a non-zero PP for `(None)`
// (I'm looking at you, S/V and Z-A)
pk.Move1_PP = pk.GetMovePP(moves.Length > 0 ? moves[0] : (ushort)0, pk.Move1_PPUps);
pk.Move2_PP = pk.GetMovePP(moves.Length > 1 ? moves[1] : (ushort)0, pk.Move2_PPUps);
pk.Move3_PP = pk.GetMovePP(moves.Length > 2 ? moves[2] : (ushort)0, pk.Move3_PPUps);
pk.Move4_PP = pk.GetMovePP(moves.Length > 3 ? moves[3] : (ushort)0, pk.Move4_PPUps);
}
}
}

View File

@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace PKHeX.Core;
@ -9,99 +7,109 @@ namespace PKHeX.Core;
/// </summary>
public static class MoveSetApplicator
{
/// <summary>
/// Gets a moveset for the provided <see cref="PKM"/> data.
/// </summary>
/// <param name="pk">PKM to generate for</param>
/// <param name="random">Full movepool &amp; shuffling</param>
/// <returns>4 moves</returns>
public static ushort[] GetMoveSet(this PKM pk, bool random = false)
extension(PKM pk)
{
var la = new LegalityAnalysis(pk);
var moves = la.GetMoveSet(random);
if (random)
return moves;
var clone = pk.Clone();
clone.SetMoves(moves);
clone.SetMaximumPPCurrent(moves);
var newLa = new LegalityAnalysis(clone);
// ReSharper disable once TailRecursiveCall
return newLa.Valid ? moves : GetMoveSet(pk, true);
}
/// <summary>
/// Gets a moveset for the provided <see cref="PKM"/> data.
/// </summary>
/// <param name="la">Precomputed optional</param>
/// <param name="random">Full movepool &amp; shuffling</param>
/// <returns>4 moves</returns>
public static ushort[] GetMoveSet(this LegalityAnalysis la, bool random = false)
{
var m = la.GetSuggestedCurrentMoves(random ? MoveSourceType.All : MoveSourceType.Encounter);
if (random && !la.Entity.IsEgg)
Util.Shuffle(m.AsSpan());
const int count = 4;
if (m.Length > count)
return m.AsSpan(m.Length - count).ToArray();
Array.Resize(ref m, count);
return m;
}
/// <summary>
/// Fetches <see cref="PKM.RelearnMoves"/> based on the provided <see cref="LegalityAnalysis"/>.
/// </summary>
/// <param name="legal"><see cref="LegalityAnalysis"/> which contains parsed information pertaining to legality.</param>
/// <param name="enc">Encounter the relearn moves should be suggested for. If not provided, will use the original encounter from the analysis. </param>
/// <returns><see cref="PKM.RelearnMoves"/> best suited for the current <see cref="PKM"/> data.</returns>
public static IReadOnlyList<ushort> GetSuggestedRelearnMoves(this LegalityAnalysis legal, IEncounterTemplate? enc = null)
{
enc ??= legal.EncounterOriginal;
var m = legal.GetSuggestedRelearnMovesFromEncounter(enc);
if (m.Any(z => z != 0))
return m;
if (enc is MysteryGift or EncounterEgg)
return m;
if (enc is EncounterSlot6AO {CanDexNav: true} dn)
/// <summary>
/// Applies a new legal moveset to the <see cref="pk"/>, with option to apply random moves instead.
/// </summary>
/// <param name="random">True to apply a random moveset, false to apply a level-up moveset.</param>
public void SetMoveset(bool random = false)
{
var moves = legal.Info.Moves;
for (int i = 0; i < moves.Length; i++)
{
if (!moves[i].ShouldBeInRelearnMoves())
continue;
var move = legal.Entity.GetMove(i);
if (dn.CanBeDexNavMove(move))
return new ushort[] { move, 0, 0, 0 };
}
Span<ushort> moves = stackalloc ushort[4];
pk.GetMoveSet(moves, random);
pk.SetMoves(moves);
}
if (enc is EncounterSlot8b { IsUnderground: true } ug)
/// <summary>
/// Applies the suggested Relearn Moves to the <see cref="pk"/>.
/// </summary>
/// <param name="la">Legality Analysis to use.</param>
public void SetRelearnMoves(LegalityAnalysis la)
{
var moves = legal.Info.Moves;
for (int i = 0; i < moves.Length; i++)
{
if (!moves[i].ShouldBeInRelearnMoves())
continue;
var move = legal.Entity.GetMove(i);
if (ug.CanBeUndergroundMove(move))
return new ushort[] { move, 0, 0, 0 };
}
if (ug.GetBaseEggMove(out var any))
return new ushort[] { any, 0, 0, 0 };
Span<ushort> moves = stackalloc ushort[4];
la.GetSuggestedRelearnMoves(moves);
pk.SetRelearnMoves(moves);
}
var encounter = EncounterSuggestion.GetSuggestedMetInfo(legal.Entity);
if (encounter is IRelearn {Relearn: {HasMoves:true} r})
return r.ToArray();
/// <summary>
/// Gets a moveset for the provided <see cref="PKM"/> data.
/// </summary>
/// <param name="moves">Result storage</param>
/// <param name="random">Full movepool &amp; shuffling</param>
/// <returns>4 moves</returns>
public void GetMoveSet(Span<ushort> moves, bool random = false)
{
var la = new LegalityAnalysis(pk);
la.GetMoveSet(moves, random);
return m;
if (random)
return;
var clone = pk.Clone();
clone.SetMoves(moves);
var newLa = new LegalityAnalysis(clone);
if (!newLa.Valid)
newLa.GetMoveSet(moves, true);
}
}
extension(LegalityAnalysis la)
{
/// <summary>
/// Gets a moveset for the provided <see cref="PKM"/> data.
/// </summary>
/// <param name="moves">Result storage</param>
/// <param name="random">Full movepool &amp; shuffling</param>
/// <returns>4 moves</returns>
public void GetMoveSet(Span<ushort> moves, bool random = false)
{
la.GetSuggestedCurrentMoves(moves, random ? MoveSourceType.All : MoveSourceType.Encounter);
if (random && !la.Entity.IsEgg)
Util.Rand.Shuffle(moves);
}
/// <summary>
/// Fetches <see cref="PKM.RelearnMoves"/> based on the provided <see cref="LegalityAnalysis"/>.
/// </summary>
/// <param name="moves">Result storage</param>
/// <param name="enc">Encounter the relearn moves should be suggested for. If not provided, will use the original encounter from the analysis. </param>
/// <returns><see cref="PKM.RelearnMoves"/> best suited for the current <see cref="PKM"/> data.</returns>
public void GetSuggestedRelearnMoves(Span<ushort> moves, IEncounterTemplate? enc = null)
{
enc ??= la.EncounterOriginal;
la.GetSuggestedRelearnMovesFromEncounter(moves, enc);
if (moves[0] != 0)
return;
if (enc is MysteryGift or IEncounterEgg)
return;
if (enc is ISingleMoveBonus {IsMoveBonusPossible: true} bonus)
{
var chk = la.Info.Moves;
for (int i = 0; i < chk.Length; i++)
{
if (!chk[i].ShouldBeInRelearnMoves())
continue;
var move = la.Entity.GetMove(i);
if (!bonus.IsMoveBonus(move))
continue;
moves.Clear();
moves[0] = move;
return;
}
if (bonus.IsMoveBonusRequired && bonus.TryGetRandomMoveBonus(out var bonusMove))
{
moves.Clear();
moves[0] = bonusMove;
return;
}
}
var encounter = EncounterSuggestion.GetSuggestedMetInfo(la.Entity);
if (encounter is IRelearn {Relearn: {HasMoves:true} r})
r.CopyTo(moves);
}
}
}

View File

@ -7,114 +7,183 @@ namespace PKHeX.Core;
/// </summary>
public static class MoveShopRecordApplicator
{
public static void ClearMoveShopFlags(this IMoveShop8 shop)
extension(IMoveShop8 shop)
{
var bits = shop.MoveShopPermitFlags;
for (int i = 0; i < bits.Length; i++)
shop.SetPurchasedRecordFlag(i, false);
if (shop is IMoveShop8Mastery m)
m.ClearMoveShopFlagsMastered();
}
public static void ClearMoveShopFlagsMastered(this IMoveShop8Mastery shop)
{
var bits = shop.MoveShopPermitFlags;
for (int i = 0; i < bits.Length; i++)
shop.SetMasteredRecordFlag(i, false);
}
public static void SetMoveShopFlags(this IMoveShop8Mastery shop, PKM pk)
{
Span<ushort> moves = stackalloc ushort[4];
pk.GetMoves(moves);
shop.SetMoveShopFlags(moves, pk);
}
public static void SetMoveShopFlags(this IMoveShop8Mastery shop, ReadOnlySpan<ushort> moves, PKM pk)
{
var index = PersonalTable.LA.GetFormIndex(pk.Species, pk.Form);
var learn = Legal.LevelUpLA[index];
var mastery = Legal.MasteryLA[index];
var level = pk.CurrentLevel;
shop.SetMoveShopFlags(moves, learn, mastery, level);
}
public static void SetMoveShopFlagsAll(this IMoveShop8Mastery shop, PKM pk)
{
var index = PersonalTable.LA.GetFormIndex(pk.Species, pk.Form);
var learn = Legal.LevelUpLA[index];
var mastery = Legal.MasteryLA[index];
var level = pk.CurrentLevel;
shop.SetMoveShopFlagsAll(learn, mastery, level);
}
public static void SetMoveShopFlagsAll(this IMoveShop8Mastery shop, Learnset learn, Learnset mastery, int level)
{
var possible = shop.MoveShopPermitIndexes;
var permit = shop.MoveShopPermitFlags;
for (int index = 0; index < possible.Length; index++)
/// <summary>
/// Clears all the "purchased" and "mastered" move shop flags.
/// </summary>
public void ClearMoveShopFlags()
{
var move = possible[index];
var allowed = permit[index];
if (!allowed)
continue;
var bits = shop.Permit;
for (int i = 0; i < bits.RecordCountUsed; i++)
shop.SetPurchasedRecordFlag(i, false);
SetMasteredFlag(shop, learn, mastery, level, index, move);
if (shop is IMoveShop8Mastery m)
m.ClearMoveShopFlagsMastered();
}
}
public static void SetMoveShopFlags(this IMoveShop8Mastery shop, ReadOnlySpan<ushort> moves, Learnset learn, Learnset mastery, int level)
extension(IMoveShop8Mastery shop)
{
var possible = shop.MoveShopPermitIndexes;
var permit = shop.MoveShopPermitFlags;
foreach (var move in moves)
/// <summary>
/// Clears all the "mastered" move shop flags.
/// </summary>
public void ClearMoveShopFlagsMastered()
{
var index = possible.IndexOf(move);
if (index == -1)
continue;
if (!permit[index])
continue;
SetMasteredFlag(shop, learn, mastery, level, index, move);
}
}
public static void SetMasteredFlag(this IMoveShop8Mastery shop, Learnset learn, Learnset mastery, int level, int index, ushort move)
{
if (shop.GetMasteredRecordFlag(index))
return;
if (level < (uint)learn.GetMoveLevel(move)) // Can't learn it yet; must purchase.
{
shop.SetPurchasedRecordFlag(index, true);
shop.SetMasteredRecordFlag(index, true);
return;
var bits = shop.Permit;
for (int i = 0; i < bits.RecordCountUsed; i++)
shop.SetMasteredRecordFlag(i, false);
}
if (level < (uint)mastery.GetMoveLevel(move)) // Can't master it yet; must Seed of Mastery
shop.SetMasteredRecordFlag(index, true);
}
public static void SetEncounterMasteryFlags(this IMoveShop8Mastery shop, ReadOnlySpan<ushort> moves, Learnset mastery, int level)
{
var possible = shop.MoveShopPermitIndexes;
var permit = shop.MoveShopPermitFlags;
foreach (var move in moves)
/// <summary>
/// Sets the required move shop flags for the requested entity.
/// </summary>
public void SetMoveShopFlags(PKM pk)
{
var index = possible.IndexOf(move);
if (index == -1)
continue;
if (!permit[index])
continue;
Span<ushort> moves = stackalloc ushort[4];
pk.GetMoves(moves);
shop.SetMoveShopFlags(moves, pk);
}
// If the Pokémon is caught with any move shop move in its learnset
// and it is high enough level to master it, the game will automatically
// give it the "Mastered" flag but not the "Purchased" flag
// For moves that are not in the learnset, it returns -1 which is true, thus set as mastered.
if (level >= mastery.GetMoveLevel(move))
shop.SetMasteredRecordFlag(index, true);
/// <summary>
/// Sets the required move shop flags for the requested entity.
/// </summary>
public void SetMoveShopFlags(ReadOnlySpan<ushort> moves, PKM pk)
{
var (learn, mastery) = LearnSource8LA.GetLearnsetAndMastery(pk.Species, pk.Form);
shop.SetMoveShopFlags(moves, learn, mastery, pk.CurrentLevel);
}
/// <summary>
/// Sets all possible move shop flags for the requested entity.
/// </summary>
public void SetMoveShopFlagsAll(PKM pk)
{
var (learn, mastery) = LearnSource8LA.GetLearnsetAndMastery(pk.Species, pk.Form);
shop.SetMoveShopFlagsAll(learn, mastery, pk.CurrentLevel);
}
/// <summary>
/// Sets all possible move shop flags for the requested entity.
/// </summary>
public void SetMoveShopFlagsAll(Learnset learn, Learnset mastery, byte level)
{
var permit = shop.Permit;
var possible = permit.RecordPermitIndexes;
for (int index = 0; index < permit.RecordCountUsed; index++)
{
var allowed = permit.IsRecordPermitted(index);
if (!allowed)
continue;
var move = possible[index];
shop.SetMasteredFlag(learn, mastery, level, index, move);
}
}
/// <summary>
/// Sets all move shop flags for the currently known moves.
/// </summary>
public void SetMoveShopFlags(ReadOnlySpan<ushort> moves, Learnset learn, Learnset mastery, byte level)
{
var permit = shop.Permit;
var possible = permit.RecordPermitIndexes;
foreach (var move in moves)
{
var index = possible.IndexOf(move);
if (index == -1)
continue;
if (!permit.IsRecordPermitted(index))
continue;
shop.SetMasteredFlag(learn, mastery, level, index, move);
}
}
/// <summary>
/// Sets the "mastered" move shop flag for the requested move.
/// </summary>
public void SetMasteredFlag(Learnset learn, Learnset mastery, byte level, int index, ushort move)
{
if (shop.GetMasteredRecordFlag(index))
return;
if (learn.TryGetLevelLearnMove(move, out var learnLevel))
{
if (level < learnLevel) // Can't learn it yet; must purchase.
{
shop.SetPurchasedRecordFlag(index, true);
shop.SetMasteredRecordFlag(index, true);
return;
}
if (mastery.TryGetLevelLearnMove(move, out var masterLevel) && level < masterLevel) // Can't master it yet; must Seed of Mastery
shop.SetMasteredRecordFlag(index, true);
// Otherwise, is innately mastered, no need to force the flag.
}
else // Can't learn it without purchasing.
{
if (shop.GetPurchasedRecordFlag(index))
shop.SetMasteredRecordFlag(index, true);
}
}
/// <summary>
/// Sets the "mastered" move shop flag for the encounter.
/// </summary>
public void SetEncounterMasteryFlags(ReadOnlySpan<ushort> moves, Learnset mastery, byte metLevel, ushort alphaMove)
{
var permit = shop.Permit;
var possible = permit.RecordPermitIndexes;
foreach (var move in moves)
{
var index = possible.IndexOf(move);
if (index == -1)
continue;
if (!permit.IsRecordPermitted(index))
continue;
// If the Pokémon is caught with any move shop move in its learnset,
// and it is high enough level to master it, the game will automatically
// give it the "Mastered" flag but not the "Purchased" flag
// For moves that are not in the learnset, set as mastered.
if (!mastery.TryGetLevelLearnMove(move, out var masteryLevel) || metLevel >= masteryLevel)
shop.SetMasteredRecordFlag(index, true);
}
if (alphaMove != 0)
{
var index = possible.IndexOf(alphaMove);
if (index != -1)
shop.SetMasteredRecordFlag(index, true);
}
}
/// <summary>
/// Sets the "purchased" move shop flag for all possible moves.
/// </summary>
public void SetPurchasedFlagsAll(PKM pk)
{
var (learn, _) = LearnSource8LA.GetLearnsetAndMastery(pk.Species, pk.Form);
var level = pk.CurrentLevel;
var alpha = pk is PA8 pa ? pa.AlphaMove : (ushort)0;
var permit = shop.Permit;
for (int index = 0; index < permit.RecordCountUsed; index++)
{
var allowed = permit.IsRecordPermitted(index);
if (!allowed)
continue;
// If it can learn it naturally, it can't be purchased anymore.
var move = permit.RecordPermitIndexes[index];
if (learn.TryGetLevelLearnMove(move, out var learnLevel) && learnLevel <= level)
continue;
// Skip purchasing alpha moves, even though it was possible on early versions of the game.
if (move == alpha)
continue;
shop.SetPurchasedRecordFlag(index, true);
}
}
}
}

View File

@ -0,0 +1,67 @@
using System;
using static PKHeX.Core.Ball;
using static PKHeX.Core.PersonalColor;
namespace PKHeX.Core;
public static class PersonalColorUtil
{
public static PersonalColor GetColor(PKM pk)
{
// Gen1/2 don't store color in personal info
if (pk.Format < 3)
return (PersonalColor)PersonalTable.USUM[pk.Species, 0].Color;
return (PersonalColor)pk.PersonalInfo.Color;
}
public static PersonalColor GetColor(IEncounterTemplate enc)
{
// Gen1/2 don't store color in personal info
if (enc.Generation < 3)
return (PersonalColor)PersonalTable.USUM[enc.Species, 0].Color;
var pt = GameData.GetPersonal(enc.Version);
var pi = pt[enc.Species, enc.Form];
return (PersonalColor)pi.Color;
}
public static ReadOnlySpan<Ball> GetPreferredByColor(IEncounterTemplate enc) => GetPreferredByColor(enc, GetColor(enc));
public static ReadOnlySpan<Ball> GetPreferredByColor<T>(T enc, PersonalColor color) where T : IContext
{
if (enc.Context is EntityContext.Gen8a)
return GetPreferredByColorLA(color);
return GetPreferredByColor(color);
}
/// <summary>
/// Priority Match ball IDs that match the color ID
/// </summary>
public static ReadOnlySpan<Ball> GetPreferredByColor(PersonalColor color) => color switch
{
Red => [Repeat, Fast, Heal, Great, Dream, Lure],
Blue => [Dive, Net, Great, Lure, Beast],
Yellow => [Level, Ultra, Repeat, Quick, Moon],
Green => [Safari, Friend, Nest, Dusk],
Black => [Luxury, Heavy, Ultra, Moon, Net, Beast],
Brown => [Level, Heavy],
Purple => [Master, Love, Heal, Dream],
Gray => [Heavy, Premier, Luxury],
White => [Premier, Timer, Luxury, Ultra],
_ => [Love, Heal, Dream],
};
public static ReadOnlySpan<Ball> GetPreferredByColorLA(PersonalColor color) => color switch
{
Red => [LAPoke],
Blue => [LAFeather, LAGreat, LAJet],
Yellow => [LAUltra],
Green => [LAPoke],
Black => [LAGigaton, LALeaden, LAHeavy, LAUltra],
Brown => [LAPoke],
Purple => [LAPoke],
Gray => [LAGigaton, LALeaden, LAHeavy],
White => [LAWing, LAJet],
_ => [LAPoke],
};
}

View File

@ -0,0 +1,211 @@
using System;
namespace PKHeX.Core;
/// <summary>
/// Logic for modifying the Plus Record flags of a <see cref="PA9"/>.
/// </summary>
public static class PlusRecordApplicator
{
extension(IPlusRecord record)
{
/// <summary>
/// Sets all the Plus Record flags for the <see cref="record"/> to the given value.
/// </summary>
/// <param name="count">Total count of flags to modify [0,x).</param>
/// <param name="value">Value to set for each record.</param>
public void SetPlusFlagsAll(int count, bool value)
{
for (int i = 0; i < count; i++)
record.SetMovePlusFlag(i, value);
}
/// <summary>
/// Clears the Plus Record flags for the <see cref="record"/>.
/// </summary>
/// <param name="count">Total count of flags to modify [0,x).</param>
public void ClearPlusFlags(int count) => record.SetPlusFlagsAll(count, false);
/// <summary>
/// Sets the Plus Record flags for the <see cref="record"/> based on the legality of learning moves.
/// </summary>
/// <param name="permit">Sanity check to retrieve plus record indexes.</param>
/// <param name="la">Legality analysis of the Pokémon.</param>
/// <param name="seedOfMastery">Use a Seed of Mastery to bypass the level requirement of mastering the move.</param>
/// <param name="tm">Apply TM flags as Plus too.</param>
public void SetPlusFlags(IPermitPlus permit, LegalityAnalysis la, bool seedOfMastery, bool tm)
{
// Hopefully this is only called for Legends: Z-A format entities.
var entity = la.Entity;
var context = entity.Context;
var evos = la.Info.EvoChainsAllGens.Get(context);
switch (la.Entity)
{
case PA9 pa9:
{
var learn = LearnSource9ZA.Instance;
record.SetPlusFlagsNatural(permit, evos, learn, seedOfMastery);
if (pa9 is { IsAlpha: true, ZA: true })
{
var table = PersonalTable.ZA;
var enc = la.EncounterMatch;
var epi = table[enc.Species, enc.Form];
pa9.SetPlusFlagsSpecific(epi, epi.AlphaMove);
}
if (tm)
{
var table = PersonalTable.ZA;
record.SetPlusFlagsTM<PersonalTable9ZA, PersonalInfo9ZA>(permit, evos, table);
}
break;
}
default:
throw new Exception("Format not supported.");
}
}
public void SetPlusFlags(PKM pk, IPermitPlus permit, PlusRecordApplicatorOption option)
{
record.ClearPlusFlags(permit.PlusCountTotal);
if (option is PlusRecordApplicatorOption.None)
return;
if (option is PlusRecordApplicatorOption.ForceAll)
{
record.SetPlusFlagsAll(permit.PlusCountUsed, true);
return;
}
var la = new LegalityAnalysis(pk);
record.SetPlusFlagsInternal(permit, option, la);
}
public void SetPlusFlags(IPermitPlus permit, PlusRecordApplicatorOption option, LegalityAnalysis la)
{
record.ClearPlusFlags(permit.PlusCountTotal);
if (option is PlusRecordApplicatorOption.None)
return;
if (option is PlusRecordApplicatorOption.ForceAll)
{
record.SetPlusFlagsAll(permit.PlusCountUsed, true);
return;
}
record.SetPlusFlagsInternal(permit, option, la);
}
public void SetPlusFlagsNatural<TSource>(IPermitPlus permit, ReadOnlySpan<EvoCriteria> evos, TSource source, bool seedOfMastery) where TSource : ILearnSourceBonus
{
var indexes = permit.PlusMoveIndexes;
foreach (var evo in evos)
{
record.SetPlusFlagsNatural(indexes, evo, source, seedOfMastery);
if (evo.Form != 0 && evo.Species is (int)Species.Rotom or (int)Species.Hoopa)
record.SetPlusFlagsNatural(indexes, evo with { Form = 0 }, source, seedOfMastery);
}
}
private void SetPlusFlagsNatural<TSource>(ReadOnlySpan<ushort> indexes, EvoCriteria evo, TSource source, bool seedOfMastery) where TSource : ILearnSourceBonus
{
var (levelUp, plus) = source.GetLearnsetAndOther(evo.Species, evo.Form);
var set = seedOfMastery ? levelUp : plus;
var levels = set.GetAllLevels();
var moves = set.GetAllMoves();
for (int i = 0; i < levels.Length; i++)
{
if (evo.LevelMax < levels[i])
break;
var move = moves[i];
var index = indexes.IndexOf(move);
record.SetMovePlusFlag(index);
}
}
/// <summary>
/// Sets all moves that would be learned and naturally available as Plus based on the given level
/// </summary>
/// <param name="permit">Permit to use</param>
/// <param name="plus">Learnset to use</param>
/// <param name="level">Current level</param>
/// <param name="extra">Extra moves to set as Plus</param>
public void SetPlusFlagsEncounter(IPermitPlus permit, Learnset plus, byte level, params ReadOnlySpan<ushort> extra)
{
var indexes = permit.PlusMoveIndexes;
var levels = plus.GetAllLevels();
var moves = plus.GetAllMoves();
for (int i = 0; i < levels.Length; i++)
{
if (level < levels[i])
break;
var move = moves[i];
var index = indexes.IndexOf(move);
record.SetMovePlusFlag(index);
}
if (extra.Length != 0)
record.SetPlusFlagsSpecific(permit, extra);
}
public void SetPlusFlagsSpecific(IPermitPlus permit, ushort move)
{
var indexes = permit.PlusMoveIndexes;
var index = indexes.IndexOf(move);
record.SetMovePlusFlag(index);
}
public void SetPlusFlagsSpecific(IPermitPlus permit, params ReadOnlySpan<ushort> extra)
{
var indexes = permit.PlusMoveIndexes;
foreach (var move in extra)
{
var index = indexes.IndexOf(move);
record.SetMovePlusFlag(index);
}
}
private void SetPlusFlagsInternal(IPermitPlus permit, PlusRecordApplicatorOption option, LegalityAnalysis la)
{
if (option is PlusRecordApplicatorOption.LegalCurrent)
record.SetPlusFlags(permit, la, false, false);
else if (option is PlusRecordApplicatorOption.LegalCurrentTM)
record.SetPlusFlags(permit, la, false, true);
else if (option is PlusRecordApplicatorOption.LegalSeedTM)
record.SetPlusFlags(permit, la, true, true);
}
public void SetPlusFlagsTM<TTable, TInfo>(IPermitPlus permit, ReadOnlySpan<EvoCriteria> evos, TTable table)
where TTable : IPersonalTable<TInfo>
where TInfo : IPersonalInfo, IPersonalInfoTM
{
var indexes = permit.PlusMoveIndexes;
foreach (var evo in evos)
{
var pi = table[evo.Species, evo.Form];
for (int index = 0; index < indexes.Length; index++)
{
var move = indexes[index];
var tmIndex = permit.RecordPermitIndexes.IndexOf(move);
if (tmIndex != -1 && pi.GetIsLearnTM(tmIndex))
record.SetMovePlusFlag(index);
}
}
}
}
public static void SetPlusFlags<T>(this T pk, IPermitPlus permit, PlusRecordApplicatorOption option)
where T : PKM, IPlusRecord
=> pk.SetPlusFlags(pk, permit, option);
}
public enum PlusRecordApplicatorOption
{
None,
ForceAll,
LegalCurrent,
LegalCurrentTM,
LegalSeedTM,
}

View File

@ -14,15 +14,34 @@ public static class RibbonApplicator
public static void SetAllValidRibbons(PKM pk) => SetAllValidRibbons(new LegalityAnalysis(pk));
/// <inheritdoc cref="SetAllValidRibbons(PKM)"/>
public static void SetAllValidRibbons(LegalityAnalysis la)
public static void SetAllValidRibbons(LegalityAnalysis la) => SetAllValidRibbons(la.Entity, la.EncounterMatch, la.Info.EvoChainsAllGens);
/// <inheritdoc cref="SetAllValidRibbons(PKM)"/>
public static void SetAllValidRibbons(PKM pk, IEncounterTemplate enc, EvolutionHistory history)
{
var args = new RibbonVerifierArguments(la.Entity, la.EncounterMatch, la.Info.EvoChainsAllGens);
var args = new RibbonVerifierArguments(pk, enc, history);
SetAllRibbonState(args, true);
FixInvalidRibbons(args);
// Ribbon Deadlock
if (la.Entity is IRibbonSetCommon6 c6)
if (pk.IsEgg)
return;
if (pk is IRibbonSetCommon6 c6)
{
// Medal Deadlock
if (pk is ISuperTrain s && history.HasVisitedGen6)
{
s.SuperTrainBitFlags = RibbonRules.SetSuperTrainSupremelyTrained(s.SuperTrainBitFlags);
if (pk.Format == 6) // cleared on 6->7 transfer; only set in Gen6.
{
s.SecretSuperTrainingUnlocked = true;
s.SuperTrainSupremelyTrained = true;
}
c6.RibbonTraining = true;
}
// Ribbon Deadlock
InvertDeadlockContest(c6, true);
}
}
/// <summary>
@ -34,7 +53,16 @@ public static void SetAllValidRibbons(LegalityAnalysis la)
/// <inheritdoc cref="RemoveAllValidRibbons(PKM)"/>
public static void RemoveAllValidRibbons(LegalityAnalysis la)
{
var args = new RibbonVerifierArguments(la.Entity, la.EncounterMatch, la.Info.EvoChainsAllGens);
var pk = la.Entity;
var enc = la.EncounterMatch;
var history = la.Info.EvoChainsAllGens;
RemoveAllValidRibbons(pk, enc, history);
}
/// <inheritdoc cref="RemoveAllValidRibbons(PKM)"/>
public static void RemoveAllValidRibbons(PKM pk, IEncounterTemplate enc, EvolutionHistory history)
{
var args = new RibbonVerifierArguments(pk, enc, history);
SetAllRibbonState(args, false);
FixInvalidRibbons(args);
}
@ -42,7 +70,7 @@ public static void RemoveAllValidRibbons(LegalityAnalysis la)
/// <summary>
/// Parses the Entity for all ribbons, then fixes any ribbon that was invalid.
/// </summary>
public static void FixInvalidRibbons(RibbonVerifierArguments args)
public static void FixInvalidRibbons(in RibbonVerifierArguments args)
{
Span<RibbonResult> result = stackalloc RibbonResult[RibbonVerifier.MaxRibbonCount];
var count = RibbonVerifier.GetRibbonResults(args, result);
@ -50,7 +78,7 @@ public static void FixInvalidRibbons(RibbonVerifierArguments args)
ribbon.Fix(args);
}
private static void SetAllRibbonState(RibbonVerifierArguments args, bool desiredState)
private static void SetAllRibbonState(in RibbonVerifierArguments args, bool desiredState)
{
for (RibbonIndex3 r = 0; r < RibbonIndex3.MAX_COUNT; r++)
r.Fix(args, desiredState);
@ -59,7 +87,7 @@ private static void SetAllRibbonState(RibbonVerifierArguments args, bool desired
if (desiredState)
{
// Skip Marks, don't set them.
// Skip personality marks (Encounter specific, never required); don't set them.
for (RibbonIndex r = 0; r <= RibbonIndex.MasterRank; r++)
r.Fix(args, desiredState);
for (RibbonIndex r = RibbonIndex.Hisui; r < RibbonIndex.MAX_COUNT; r++)
@ -75,6 +103,7 @@ private static void SetAllRibbonState(RibbonVerifierArguments args, bool desired
private static void InvertDeadlockContest(IRibbonSetCommon6 c6, bool desiredState)
{
// Contest Star is a deadlock ribbon with the Master ribbons, as it needs all five Master ribbons to be true.
if (desiredState)
c6.RibbonContestStar = c6.HasAllContestRibbons();
}

View File

@ -1,63 +1,212 @@
using System;
using static PKHeX.Core.TechnicalRecordApplicatorOption;
namespace PKHeX.Core;
/// <summary>
/// Logic for modifying the Technical Record flags of a <see cref="PK8"/>.
/// Logic for modifying the Technical Record flags of a <see cref="ITechRecord"/>.
/// </summary>
public static class TechnicalRecordApplicator
{
/// <summary>
/// Sets the Technical Record flags for the <see cref="pk"/>.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="value">Value to set for the record.</param>
/// <param name="max">Max record to set.</param>
public static void SetRecordFlags(this ITechRecord pk, bool value, int max)
extension(ITechRecord record)
{
for (int i = 0; i < max; i++)
pk.SetMoveRecordFlag(i, value);
}
/// <summary>
/// Clears the Technical Record flags for the <see cref="pk"/>.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
public static void ClearRecordFlags(this ITechRecord pk) => pk.SetRecordFlags(false, pk.RecordCountTotal);
/// <summary>
/// Sets the Technical Record flags for the <see cref="pk"/> based on the current moves.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="moves">Moves to set flags for. If a move is not a Technical Record, it is skipped.</param>
public static void SetRecordFlags(this ITechRecord pk, ReadOnlySpan<ushort> moves)
{
var permit = pk.TechRecordPermitFlags;
var moveIDs = pk.TechRecordPermitIndexes;
if (permit.Length != moveIDs.Length)
return;
foreach (var m in moves)
/// <summary>
/// Sets the Technical Record flags for the <see cref="record"/>.
/// </summary>
/// <param name="value">Value to set for the record.</param>
/// <param name="max">Max record to set.</param>
public void SetRecordFlagsAll(bool value, int max)
{
var index = moveIDs.IndexOf(m);
if (index == -1)
continue;
if (permit[index])
pk.SetMoveRecordFlag(index);
for (int i = 0; i < max; i++)
record.SetMoveRecordFlag(i, value);
}
/// <summary>
/// Clears the Technical Record flags for the <see cref="record"/>.
/// </summary>
public void ClearRecordFlags() => record.SetRecordFlagsAll(false, record.Permit.RecordCountTotal);
/// <summary>
/// Sets the Technical Record flags for the <see cref="record"/> based on the current moves.
/// </summary>
/// <param name="moves">Moves to set flags for. If a move is not a Technical Record, it is skipped.</param>
public void SetRecordFlags(ReadOnlySpan<ushort> moves)
{
var permit = record.Permit;
record.SetRecordFlags(moves, permit);
}
private void SetRecordFlags<TTable, TInfo>(ReadOnlySpan<ushort> moves, ReadOnlySpan<EvoCriteria> evos, TTable pt)
where TTable : IPersonalTable<TInfo> where TInfo : IPersonalInfo, IPermitRecord
{
foreach (var evo in evos)
record.SetRecordFlags(moves, pt[evo.Species, evo.Form]);
}
/// <inheritdoc cref="SetRecordFlagsAll(ITechRecord)"/>
private void SetRecordFlagsAll<TTable, TInfo>(ReadOnlySpan<EvoCriteria> evos, TTable pt)
where TTable : IPersonalTable<TInfo> where TInfo : IPersonalInfo, IPermitRecord
{
foreach (var evo in evos)
record.SetRecordFlagsAll(pt[evo.Species, evo.Form]);
}
/// <inheritdoc cref="SetRecordFlags(ITechRecord, ReadOnlySpan{ushort})"/>
public void SetRecordFlags(ReadOnlySpan<ushort> moves, ReadOnlySpan<EvoCriteria> evos)
{
if (record is PK9 pk9)
pk9.SetRecordFlags<PersonalTable9SV, PersonalInfo9SV>(moves, evos, PersonalTable.SV);
else if (record is PA9 pa9)
pa9.SetRecordFlags<PersonalTable9ZA, PersonalInfo9ZA>(moves, evos, PersonalTable.ZA);
else if (record is PK8 pk8)
pk8.SetRecordFlags<PersonalTable8SWSH, PersonalInfo8SWSH>(moves, evos, PersonalTable.SWSH);
}
/// <inheritdoc cref="SetRecordFlagsAll(ITechRecord)"/>
public void SetRecordFlagsAll(ReadOnlySpan<EvoCriteria> evos)
{
if (record is PK9 pk9)
pk9.SetRecordFlagsAll<PersonalTable9SV, PersonalInfo9SV>(evos, PersonalTable.SV);
else if (record is PA9 pa9)
pa9.SetRecordFlagsAll<PersonalTable9ZA, PersonalInfo9ZA>(evos, PersonalTable.ZA);
else if (record is PK8 pk8)
pk8.SetRecordFlagsAll<PersonalTable8SWSH, PersonalInfo8SWSH>(evos, PersonalTable.SWSH);
}
/// <inheritdoc cref="IPermitRecord.IsRecordPermitted"/>
public bool IsRecordPermitted(ReadOnlySpan<EvoCriteria> evos, int index) => record switch
{
PK9 => IsRecordPermitted<PersonalTable9SV, PersonalInfo9SV>(evos, PersonalTable.SV, index),
PA9 => IsRecordPermitted<PersonalTable9ZA, PersonalInfo9ZA>(evos, PersonalTable.ZA, index),
PK8 => IsRecordPermitted<PersonalTable8SWSH, PersonalInfo8SWSH>(evos, PersonalTable.SWSH, index),
_ => false,
};
/// <inheritdoc cref="SetRecordFlags(ITechRecord, PKM, TechnicalRecordApplicatorOption, LegalityAnalysis)"/>
public void SetRecordFlags(PKM pk, TechnicalRecordApplicatorOption option)
{
record.ClearRecordFlags();
if (option is None)
return;
if (option is ForceAll)
{
record.SetRecordFlagsAll(true, record.Permit.RecordCountUsed);
return;
}
var la = new LegalityAnalysis(pk);
SetRecordFlagsInternal(record, pk, option, la);
}
/// <summary>
/// Applies the Technical Record flags based on the <see cref="option"/>.
/// </summary>
/// <param name="pk">Object to apply to, but base type for other logic.</param>
/// <param name="option">Option to apply.</param>
/// <param name="la">Legality analysis to use for the option.</param>
public void SetRecordFlags(PKM pk, TechnicalRecordApplicatorOption option, LegalityAnalysis la)
{
record.ClearRecordFlags();
if (option is None)
return;
if (option is ForceAll)
{
record.SetRecordFlagsAll(true, record.Permit.RecordCountUsed);
return;
}
SetRecordFlagsInternal(record, pk, option, la);
}
/// <summary>
/// Sets all the Technical Record flags for the <see cref="record"/> if they are permitted to be learned in-game.
/// </summary>
public void SetRecordFlagsAll()
{
var permit = record.Permit;
record.SetRecordFlagsAll(permit);
}
/// <inheritdoc cref="SetRecordFlagsAll(PKHeX.Core.ITechRecord)"/>"/>
public void SetRecordFlagsAll(IPermitRecord permit)
{
for (int i = 0; i < permit.RecordCountUsed; i++)
{
if (permit.IsRecordPermitted(i))
record.SetMoveRecordFlag(i);
}
}
/// <inheritdoc cref="SetRecordFlags(ITechRecord, ReadOnlySpan{ushort})"/>
public void SetRecordFlags(ReadOnlySpan<ushort> moves, IPermitRecord permit)
{
var moveIDs = permit.RecordPermitIndexes;
foreach (var m in moves)
{
var index = moveIDs.IndexOf(m);
if (index == -1)
continue;
if (permit.IsRecordPermitted(index))
record.SetMoveRecordFlag(index);
}
}
}
/// <summary>
/// Sets all the Technical Record flags for the <see cref="pk"/> if they are permitted to be learned in-game.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
public static void SetRecordFlags(this ITechRecord pk)
/// <inheritdoc cref="IPermitRecord.IsRecordPermitted"/>
private static bool IsRecordPermitted<TTable, TInfo>(ReadOnlySpan<EvoCriteria> evos, TTable pt, int index)
where TTable : IPersonalTable<TInfo> where TInfo : IPersonalInfo, IPermitRecord
{
var permit = pk.TechRecordPermitFlags;
for (int i = 0; i < permit.Length; i++)
foreach (var evo in evos)
{
if (permit[i])
pk.SetMoveRecordFlag(i);
if (pt[evo.Species, evo.Form].IsRecordPermitted(index))
return true;
}
return false;
}
/// <inheritdoc cref="SetRecordFlags(ITechRecord, PKM, TechnicalRecordApplicatorOption, LegalityAnalysis)"/>
public static void SetRecordFlags<T>(this T pk, TechnicalRecordApplicatorOption option)
where T : PKM, ITechRecord
=> pk.SetRecordFlags(pk, option);
private static void SetRecordFlagsInternal(ITechRecord record, PKM pk, TechnicalRecordApplicatorOption option, LegalityAnalysis la)
{
if (option is LegalCurrent)
{
Span<ushort> moves = stackalloc ushort[4];
pk.GetMoves(moves);
var evos = la.Info.EvoChainsAllGens.Get(pk.Context);
record.SetRecordFlags(moves, evos);
}
else if (option is LegalAll)
{
var evos = la.Info.EvoChainsAllGens.Get(pk.Context);
record.SetRecordFlagsAll(evos);
}
}
}
/// <summary>
/// Options for applying Technical Record flags.
/// </summary>
public enum TechnicalRecordApplicatorOption
{
/// <summary>
/// Do not apply any flags. Clear all flags.
/// </summary>
None,
/// <summary>
/// Apply all flags, regardless of legality.
/// </summary>
ForceAll,
/// <summary>
/// Apply legal flags based on the current moves.
/// </summary>
LegalCurrent,
/// <summary>
/// Apply legal flags based on all moves able to learn in the game it resides in.
/// </summary>
LegalAll,
}

View File

@ -0,0 +1,280 @@
using System;
using System.Text;
namespace PKHeX.Core;
/// <summary>
/// Grammar and prefix/suffix tokens for <see cref="IBattleTemplate"/> localization.
/// </summary>
public sealed record BattleTemplateConfig
{
public sealed record BattleTemplateTuple(BattleTemplateToken Token, string Text);
/// <summary> Prefix tokens - e.g. Friendship: {100} </summary>
public required BattleTemplateTuple[] Left { get; init; }
/// <summary> Suffix tokens - e.g. {Timid} Nature </summary>
public required BattleTemplateTuple[] Right { get; init; }
/// <summary> Tokens that always display the same text, with no value - e.g. Shiny: Yes </summary>
public required BattleTemplateTuple[] Center { get; init; }
/// <summary>
/// Stat names, ordered with speed in the middle (not last).
/// </summary>
public required StatDisplayConfig StatNames { get; init; }
/// <summary>
/// Stat names, ordered with speed in the middle (not last).
/// </summary>
public required StatDisplayConfig StatNamesFull { get; init; }
public required string Male { get; init; }
public required string Female { get; init; }
/// <summary>
/// Gets the stat names in the requested format.
/// </summary>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public StatDisplayConfig GetStatDisplay(StatDisplayStyle style = StatDisplayStyle.Abbreviated) => style switch
{
StatDisplayStyle.Abbreviated => StatNames,
StatDisplayStyle.Full => StatNamesFull,
StatDisplayStyle.HABCDS => StatDisplayConfig.HABCDS,
StatDisplayStyle.Raw => StatDisplayConfig.Raw,
StatDisplayStyle.Raw00 => StatDisplayConfig.Raw00,
_ => throw new ArgumentOutOfRangeException(nameof(style), style, null),
};
public static ReadOnlySpan<char> GetMoveDisplay(MoveDisplayStyle style = MoveDisplayStyle.Fill) => style switch
{
MoveDisplayStyle.Fill => "----",
MoveDisplayStyle.Directional => "↑→←↓",
_ => throw new ArgumentOutOfRangeException(nameof(style), style, null),
};
public static bool IsMovePrefix(char c) => c is '-' or '' or '↑' or '←' or '↓' or '→';
public static ReadOnlySpan<BattleTemplateToken> CommunityStandard =>
[
BattleTemplateToken.FirstLine,
BattleTemplateToken.Ability,
BattleTemplateToken.Level,
BattleTemplateToken.Shiny,
BattleTemplateToken.Friendship,
BattleTemplateToken.DynamaxLevel,
BattleTemplateToken.Gigantamax,
BattleTemplateToken.TeraType,
BattleTemplateToken.EVs,
BattleTemplateToken.Nature,
BattleTemplateToken.IVs,
BattleTemplateToken.Moves,
];
public static ReadOnlySpan<BattleTemplateToken> Showdown => CommunityStandard;
public static ReadOnlySpan<BattleTemplateToken> ShowdownNew =>
[
BattleTemplateToken.FirstLine,
BattleTemplateToken.AbilityHeldItem,
BattleTemplateToken.Moves,
BattleTemplateToken.EVsAppendNature,
BattleTemplateToken.IVs,
BattleTemplateToken.Level,
BattleTemplateToken.Shiny,
BattleTemplateToken.Friendship,
BattleTemplateToken.DynamaxLevel,
BattleTemplateToken.Gigantamax,
BattleTemplateToken.TeraType,
];
public static ReadOnlySpan<BattleTemplateToken> DefaultHover =>
[
// First line is handled manually.
BattleTemplateToken.HeldItem,
BattleTemplateToken.Ability,
BattleTemplateToken.Level,
BattleTemplateToken.Shiny,
BattleTemplateToken.DynamaxLevel,
BattleTemplateToken.Gigantamax,
BattleTemplateToken.TeraType,
BattleTemplateToken.EVs,
BattleTemplateToken.IVs,
BattleTemplateToken.Nature,
BattleTemplateToken.Moves,
// Other tokens are handled manually (Ganbaru, Awakening) as they are not stored by the battle template interface, only entity objects.
];
/// <summary>
/// Tries to parse the line for a token and value, if applicable.
/// </summary>
/// <param name="line">Line to parse</param>
/// <param name="value">Value for the token, if applicable</param>
/// <returns>Token type that was found</returns>
public BattleTemplateToken TryParse(ReadOnlySpan<char> line, out ReadOnlySpan<char> value)
{
value = default;
if (line.Length == 0)
return BattleTemplateToken.None;
foreach (var tuple in Left)
{
if (!line.StartsWith(tuple.Text, StringComparison.OrdinalIgnoreCase))
continue;
value = line[tuple.Text.Length..];
return tuple.Token;
}
foreach (var tuple in Right)
{
if (!line.EndsWith(tuple.Text, StringComparison.OrdinalIgnoreCase))
continue;
value = line[..^tuple.Text.Length];
return tuple.Token;
}
foreach (var tuple in Center)
{
if (!line.Equals(tuple.Text, StringComparison.OrdinalIgnoreCase))
continue;
return tuple.Token;
}
return BattleTemplateToken.None;
}
private string GetToken(BattleTemplateToken token, out bool isLeft)
{
foreach (var tuple in Left)
{
if (tuple.Token != token)
continue;
isLeft = true;
return tuple.Text;
}
foreach (var tuple in Right)
{
if (tuple.Token != token)
continue;
isLeft = false;
return tuple.Text;
}
foreach (var tuple in Center)
{
if (tuple.Token != token)
continue;
isLeft = false;
return tuple.Text;
}
throw new ArgumentException($"Token {token} not found in config");
}
/// <summary>
/// Gets the string representation of the token. No value is combined with it.
/// </summary>
public string Push(BattleTemplateToken token) => GetToken(token, out _);
/// <summary>
/// Gets the string representation of the token, and combines the value with it.
/// </summary>
public string Push<T>(BattleTemplateToken token, T value)
{
var str = GetToken(token, out var isLeft);
if (isLeft)
return $"{str}{value}";
return $"{value}{str}";
}
/// <inheritdoc cref="Push{T}(BattleTemplateToken,T)"/>
public void Push<T>(BattleTemplateToken token, T value, StringBuilder sb)
{
var str = GetToken(token, out var isLeft);
if (isLeft)
sb.Append(str).Append(value);
else
sb.Append(value).Append(str);
}
/// <summary>
/// Checks all representations of the stat name for a match.
/// </summary>
/// <param name="stat">Stat name</param>
/// <returns>-1 if not found, otherwise the index of the stat</returns>
public int GetStatIndex(ReadOnlySpan<char> stat)
{
var index = StatNames.GetStatIndex(stat);
if (index != -1)
return index;
index = StatNamesFull.GetStatIndex(stat);
if (index != -1)
return index;
foreach (var set in StatDisplayConfig.Custom)
{
index = set.GetStatIndex(stat);
if (index != -1)
return index;
}
return -1;
}
public StatParseResult TryParseStats(ReadOnlySpan<char> message, Span<int> bestResult)
{
var result = ParseInternal(message, bestResult);
ReorderSpeedNotLast(bestResult);
return result;
}
private StatParseResult ParseInternal(ReadOnlySpan<char> message, Span<int> bestResult)
{
Span<int> original = stackalloc int[bestResult.Length];
bestResult.CopyTo(original);
var result = StatNames.TryParse(message, bestResult);
if (result.IsParseClean)
return result;
// Check if the others get a better result
int bestCount = result.CountParsed;
Span<int> tmp = stackalloc int[bestResult.Length];
// Check Long Stat names
{
original.CopyTo(tmp); // restore original defaults
var other = StatNamesFull.TryParse(message, tmp);
if (other.IsParseClean)
{
tmp.CopyTo(bestResult);
return other;
}
if (other.CountParsed > bestCount)
{
bestCount = other.CountParsed;
tmp.CopyTo(bestResult);
}
}
// Check custom parsers
foreach (var set in StatDisplayConfig.Custom)
{
original.CopyTo(tmp); // restore original defaults
var other = set.TryParse(message, tmp);
if (other.IsParseClean)
{
tmp.CopyTo(bestResult);
return other;
}
if (other.CountParsed > bestCount)
{
bestCount = other.CountParsed;
tmp.CopyTo(bestResult);
}
}
return result;
}
private static void ReorderSpeedNotLast<T>(Span<T> arr)
{
ArgumentOutOfRangeException.ThrowIfLessThan(arr.Length, 6);
var speed = arr[5];
arr[5] = arr[4];
arr[4] = arr[3];
arr[3] = speed;
}
}

View File

@ -0,0 +1,12 @@
namespace PKHeX.Core;
/// <summary>
/// Token order for displaying the battle template.
/// </summary>
public enum BattleTemplateDisplayStyle : sbyte
{
Custom = -1,
Showdown = 0, // default
Legacy,
Brief, // default preview hover style
}

View File

@ -0,0 +1,96 @@
using System;
namespace PKHeX.Core;
/// <summary>
/// Settings for exporting a battle template.
/// </summary>
public readonly ref struct BattleTemplateExportSettings
{
/// <summary>
/// Order of the tokens in the export.
/// </summary>
public ReadOnlySpan<BattleTemplateToken> Order { get; init; }
/// <summary>
/// Localization for the battle template.
/// </summary>
public BattleTemplateLocalization Localization { get; }
/// <summary>
/// Display style for the EVs.
/// </summary>
public StatDisplayStyle StatsEVs { get; init; }
/// <summary>
/// Display style for the IVs.
/// </summary>
public StatDisplayStyle StatsIVs { get; init; }
public StatDisplayStyle StatsOther { get; init; }
/// <summary>
/// Display style for the moves.
/// </summary>
public MoveDisplayStyle Moves { get; init; }
public static BattleTemplateExportSettings Showdown => new(BattleTemplateConfig.Showdown);
public static BattleTemplateExportSettings CommunityStandard => new(BattleTemplateConfig.CommunityStandard);
public BattleTemplateExportSettings(string language) : this(BattleTemplateConfig.Showdown, language) { }
public BattleTemplateExportSettings(LanguageID language) : this(BattleTemplateConfig.Showdown, language) { }
public BattleTemplateExportSettings(ReadOnlySpan<BattleTemplateToken> order, string language = BattleTemplateLocalization.DefaultLanguage)
{
Localization = BattleTemplateLocalization.GetLocalization(language);
Order = order;
}
public BattleTemplateExportSettings(ReadOnlySpan<BattleTemplateToken> order, LanguageID language)
{
Localization = BattleTemplateLocalization.GetLocalization(language);
Order = order;
}
/// <summary>
/// Checks if the token is in the export.
/// </summary>
public bool IsTokenInExport(BattleTemplateToken token)
{
foreach (var t in Order)
{
if (t == token)
return true;
}
return false;
}
/// <summary>
/// Gets the index of the token in the export.
/// </summary>
public int GetTokenIndex(BattleTemplateToken token)
{
for (int i = 0; i < Order.Length; i++)
{
if (Order[i] == token)
return i;
}
return -1;
}
/// <summary>
/// Checks if the token is in the export.
/// </summary>
/// <remarks>Should be a static method, but is not because it feels better this way.</remarks>
/// <param name="token">Token to check</param>
/// <param name="tokens">Tokens to check against</param>
public bool IsTokenInExport(BattleTemplateToken token, ReadOnlySpan<BattleTemplateToken> tokens)
{
foreach (var t in tokens)
{
if (t == token)
return true;
}
return false;
}
}

View File

@ -0,0 +1,74 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace PKHeX.Core;
/// <summary>
/// Provides information for localizing <see cref="IBattleTemplate"/> sets.
/// </summary>
/// <param name="Strings">In-game strings</param>
/// <param name="Config">Grammar and prefix/suffix tokens</param>
public sealed record BattleTemplateLocalization(GameStrings Strings, BattleTemplateConfig Config)
{
public const string DefaultLanguage = GameLanguage.DefaultLanguage; // English
private static readonly Dictionary<string, BattleTemplateLocalization> Cache = new(1);
private static readonly BattleTemplateConfigContext Context = new(LocalizationStorage<BattleTemplateConfig>.Options);
public static readonly LocalizationStorage<BattleTemplateConfig> ConfigCache = new("battle", Context.BattleTemplateConfig);
public static readonly BattleTemplateLocalization Default = GetLocalization(DefaultLanguage);
/// <summary>
/// Gets the localization for the requested language.
/// </summary>
/// <param name="language">Language code</param>
public static BattleTemplateConfig GetConfig(string language) => ConfigCache.Get(language);
/// <param name="language"><see cref="LanguageID"/> index</param>
/// <inheritdoc cref="GetLocalization(string)"/>
public static BattleTemplateLocalization GetLocalization(LanguageID language) =>
GetLocalization(language.GetLanguageCode());
/// <summary>
/// Gets the localization for the requested language.
/// </summary>
/// <param name="language">Language code</param>
public static BattleTemplateLocalization GetLocalization(string language)
{
if (Cache.TryGetValue(language, out var result))
return result;
var strings = GameInfo.GetStrings(language);
var cfg = GetConfig(language);
result = new BattleTemplateLocalization(strings, cfg);
Cache[language] = result;
return result;
}
/// <summary>
/// Force loads all localizations.
/// </summary>
public static bool ForceLoadAll()
{
bool anyLoaded = false;
foreach (var lang in GameLanguage.AllSupportedLanguages)
{
if (Cache.ContainsKey(lang))
continue;
_ = GetLocalization(lang);
anyLoaded = true;
}
return anyLoaded;
}
/// <summary>
/// Gets all localizations.
/// </summary>
public static IReadOnlyDictionary<string, BattleTemplateLocalization> GetAll()
{
_ = ForceLoadAll();
return Cache;
}
}
[JsonSerializable(typeof(BattleTemplateConfig))]
public sealed partial class BattleTemplateConfigContext : JsonSerializerContext;

View File

@ -0,0 +1,49 @@
using System.Text.Json.Serialization;
namespace PKHeX.Core;
/// <summary>
/// Enum for the different tokens used in battle templates.
/// </summary>
/// <remarks>
/// Each token represents a specific aspect of a Pokémon's battle template.
/// One token per line. Each token can have specific grammar rules depending on the language.
/// </remarks>
[JsonConverter(typeof(JsonStringEnumConverter<BattleTemplateToken>))]
public enum BattleTemplateToken : byte
{
None = 0, // invalid, used as a magic value to signal that a token is not recognized
// Standard tokens
Shiny,
Ability,
Nature,
Friendship,
EVs,
IVs,
Level,
DynamaxLevel,
Gigantamax,
TeraType,
// Tokens that can appear multiple times
Moves,
// When present, first line will not contain values for these tokens (instead outputting on separate token line)
// Not part of the standard export format, but can be recognized/optionally used in the program
HeldItem,
Nickname,
Gender,
// Manually appended, not stored or recognized on import
AVs,
GVs,
// Future Showdown propositions
AbilityHeldItem, // [Ability] Item
EVsWithNature, // +/-
EVsAppendNature, // +/- and .. (Nature)
// Omitting the first line (species) shouldn't be done unless it is manually added in the presentation/export.
FirstLine = byte.MaxValue,
}

View File

@ -0,0 +1,76 @@
using System;
using System.ComponentModel;
namespace PKHeX.Core;
public sealed class BattleTemplateSettings
{
[LocalizedDescription("Settings for showing details when hovering a slot.")]
public BattleTemplateTypeSetting Hover { get; set; } = new(BattleTemplateDisplayStyle.Brief, LanguageID.None, MoveDisplayStyle.Directional);
[LocalizedDescription("Settings for showing details when exporting a slot.")]
public BattleTemplateTypeSetting Export { get; set; } = new(BattleTemplateDisplayStyle.Showdown, LanguageID.English);
}
[TypeConverter(typeof(ExpandableObjectConverter))]
public sealed class BattleTemplateTypeSetting
{
[LocalizedDescription("Language to use when exporting a battle template. If not specified in settings, will use current language.")]
public LanguageID Language { get; set; }
public StatDisplayStyle StyleStatEVs { get; set; }
public StatDisplayStyle StyleStatIVs { get; set; }
public StatDisplayStyle StyleStatOther { get; set; }
public MoveDisplayStyle StyleMove { get; set; }
[LocalizedDescription("Custom stat labels and grammar.")]
public StatDisplayConfig StatsCustom { get; set; } = StatDisplayConfig.HABCDS;
[LocalizedDescription("Display format to use when exporting a battle template from the program.")]
public BattleTemplateDisplayStyle TokenOrder { get; set; }
[LocalizedDescription("Custom ordering for exporting a set, if chosen via export display style.")]
public BattleTemplateToken[] TokenOrderCustom { get; set; } = BattleTemplateConfig.Showdown.ToArray();
public BattleTemplateTypeSetting() { }
public BattleTemplateTypeSetting(BattleTemplateDisplayStyle style, LanguageID lang, MoveDisplayStyle move = MoveDisplayStyle.Fill)
{
TokenOrder = style;
Language = lang;
StyleMove = move;
}
public override string ToString() => $"{TokenOrder} {Language}";
private LanguageID GetLanguageExport(LanguageID program) => GetLanguage(Language, program);
public BattleTemplateExportSettings GetSettings(LanguageID programLanguage, EntityContext context) => new(GetOrder(TokenOrder, TokenOrderCustom), GetLanguageExport(programLanguage))
{
StatsEVs = StyleStatEVs,
StatsIVs = StyleStatIVs,
StatsOther = StyleStatOther,
Moves = GetMoveDisplayStyle(StyleMove, context),
};
private static LanguageID GetLanguage(LanguageID choice, LanguageID program)
{
if (choice != LanguageID.None)
return choice;
if (program == LanguageID.None)
return LanguageID.English;
return program;
}
private static ReadOnlySpan<BattleTemplateToken> GetOrder(BattleTemplateDisplayStyle style, ReadOnlySpan<BattleTemplateToken> custom) => style switch
{
BattleTemplateDisplayStyle.Legacy => BattleTemplateConfig.CommunityStandard,
BattleTemplateDisplayStyle.Brief => BattleTemplateConfig.DefaultHover,
BattleTemplateDisplayStyle.Custom => custom,
_ => BattleTemplateConfig.Showdown,
};
private static MoveDisplayStyle GetMoveDisplayStyle(MoveDisplayStyle style, EntityContext context) => style switch
{
MoveDisplayStyle.Directional when context is EntityContext.Gen9a => MoveDisplayStyle.Directional,
_ => MoveDisplayStyle.Fill,
};
}

View File

@ -0,0 +1,6 @@
namespace PKHeX.Core;
public readonly record struct BattleTemplateParseError(BattleTemplateParseErrorType Type, string Value)
{
public string Humanize(BattleTemplateParseErrorLocalization localization) => Type.Humanize(localization, Value);
}

View File

@ -0,0 +1,55 @@
using System.Text.Json.Serialization;
namespace PKHeX.Core;
/// <summary>
/// Localized strings for <see cref="BattleTemplateParseErrorType"/> values.
/// Each enum member maps 1:1 to a property for JSON (de)serialization.
/// </summary>
public sealed class BattleTemplateParseErrorLocalization
{
private static readonly BattleTemplateParseErrorLocalizationContext Context = new(LocalizationStorage<BattleTemplateParseErrorLocalization>.Options);
public static readonly LocalizationStorage<BattleTemplateParseErrorLocalization> Cache = new("setparse", Context.BattleTemplateParseErrorLocalization);
public static BattleTemplateParseErrorLocalization Get(string language = GameLanguage.DefaultLanguage) => Cache.Get(language);
public static BattleTemplateParseErrorLocalization Get(LanguageID language) => Cache.Get(language.GetLanguageCode());
// General / structural
public required string LineLength { get; init; } = "Line exceeded the maximum supported length: {0}";
// Token issues
public required string TokenUnknown { get; init; } = "Unrecognized: {0}";
public required string TokenFailParse { get; init; } = "Token could not be parsed: {0}";
// Move issues
public required string MoveCountTooMany { get; init; } = "Too many moves specified: {0}";
public required string MoveSlotAlreadyUsed { get; init; } = "Move slot already used: {0}";
public required string MoveDuplicate { get; init; } = "Duplicate move specified: {0}";
public required string MoveUnrecognized { get; init; } = "Move not recognized: {0}";
// Item
public required string ItemUnrecognized { get; init; } = "Held item not recognized: {0}";
// Ability
public required string AbilityDeclaration { get; init; } = "Ability already declared: {0}";
public required string AbilityUnrecognized { get; init; } = "Ability not recognized: {0}";
public required string AbilityAlreadySpecified { get; init; } = "Ability already specified: {0}";
// Nature
public required string NatureUnrecognized { get; init; } = "Nature not recognized: {0}";
public required string NatureAlreadySpecified { get; init; } = "Nature already specified: {0}";
// Hidden Power
public required string HiddenPowerUnknownType { get; init; } = "Hidden Power type not recognized: {0}";
public required string HiddenPowerIncompatibleIVs { get; init; } = "Hidden Power type incompatible with IVs: {0}";
// EffortValue Nature Amp (Stat modifiers with + / - )
public required string NatureEffortAmpDeclaration { get; init; } = "Nature / effort amp already declared: {0}";
public required string NatureEffortAmpUnknown { get; init; } = "Unknown nature effort amp token: {0}";
public required string NatureEffortAmpAlreadySpecified { get; init; } = "Nature effort amp already specified: {0}";
public required string NatureEffortAmpConflictNature { get; init; } = "Declared effort amp conflicts with previously specified nature.";
public required string NatureAmpNoPlus { get; init; } = "Missing '+' nature amp token.";
public required string NatureAmpNoMinus { get; init; } = "Missing '-' nature amp token.";
}
[JsonSerializable(typeof(BattleTemplateParseErrorLocalization))]
public sealed partial class BattleTemplateParseErrorLocalizationContext : JsonSerializerContext;

View File

@ -0,0 +1,75 @@
using System;
using static PKHeX.Core.BattleTemplateParseErrorType;
namespace PKHeX.Core;
public enum BattleTemplateParseErrorType : byte
{
None = 0,
LineLength,
TokenUnknown,
TokenFailParse,
MoveCountTooMany,
MoveSlotAlreadyUsed,
MoveDuplicate,
MoveUnrecognized,
ItemUnrecognized,
AbilityDeclaration,
AbilityUnrecognized,
AbilityAlreadySpecified,
NatureUnrecognized,
NatureAlreadySpecified,
HiddenPowerUnknownType,
HiddenPowerIncompatibleIVs,
NatureEffortAmpDeclaration,
NatureEffortAmpUnknown,
NatureEffortAmpAlreadySpecified,
NatureEffortAmpConflictNature,
NatureAmpNoPlus,
NatureAmpNoMinus,
}
public static class BattleTemplateParseErrorExtensions
{
/// <summary>
/// Returns the localized string for the provided <paramref name="type"/>.
/// Falls back to the enum name if no mapping exists.
/// </summary>
public static string Humanize(this BattleTemplateParseErrorType type, BattleTemplateParseErrorLocalization localization, string value)
{
var template = GetTemplate(type, localization);
if (value.Length == 0)
return template;
return string.Format(template, value);
}
private static string GetTemplate(BattleTemplateParseErrorType type, BattleTemplateParseErrorLocalization localization) => type switch
{
None => "",
LineLength => localization.LineLength,
TokenUnknown => localization.TokenUnknown,
TokenFailParse => localization.TokenFailParse,
MoveCountTooMany => localization.MoveCountTooMany,
MoveSlotAlreadyUsed => localization.MoveSlotAlreadyUsed,
MoveDuplicate => localization.MoveDuplicate,
MoveUnrecognized => localization.MoveUnrecognized,
ItemUnrecognized => localization.ItemUnrecognized,
AbilityDeclaration => localization.AbilityDeclaration,
AbilityUnrecognized => localization.AbilityUnrecognized,
AbilityAlreadySpecified => localization.AbilityAlreadySpecified,
NatureUnrecognized => localization.NatureUnrecognized,
NatureAlreadySpecified => localization.NatureAlreadySpecified,
HiddenPowerUnknownType => localization.HiddenPowerUnknownType,
HiddenPowerIncompatibleIVs => localization.HiddenPowerIncompatibleIVs,
NatureEffortAmpDeclaration => localization.NatureEffortAmpDeclaration,
NatureEffortAmpUnknown => localization.NatureEffortAmpUnknown,
NatureEffortAmpAlreadySpecified => localization.NatureEffortAmpAlreadySpecified,
NatureEffortAmpConflictNature => localization.NatureEffortAmpConflictNature,
NatureAmpNoPlus => localization.NatureAmpNoPlus,
NatureAmpNoMinus => localization.NatureAmpNoMinus,
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null),
};
}

View File

@ -18,11 +18,12 @@ public interface IBattleTemplate : ISpeciesForm, IGigantamaxReadOnly, IDynamaxLe
/// <summary>
/// <see cref="PKM.Gender"/> name of the Set entity.
/// </summary>
int Gender { get; }
byte? Gender { get; }
/// <summary>
/// <see cref="PKM.HeldItem"/> of the Set entity.
/// </summary>
/// <remarks>Depends on <see cref="Context"/> for context-specific item lists.</remarks>
int HeldItem { get; }
/// <summary>
@ -33,7 +34,7 @@ public interface IBattleTemplate : ISpeciesForm, IGigantamaxReadOnly, IDynamaxLe
/// <summary>
/// <see cref="PKM.CurrentLevel"/> of the Set entity.
/// </summary>
int Level { get; }
byte Level { get; }
/// <summary>
/// <see cref="PKM.CurrentLevel"/> of the Set entity.
@ -43,7 +44,7 @@ public interface IBattleTemplate : ISpeciesForm, IGigantamaxReadOnly, IDynamaxLe
/// <summary>
/// <see cref="PKM.CurrentFriendship"/> of the Set entity.
/// </summary>
int Friendship { get; }
byte Friendship { get; }
/// <summary>
/// <see cref="PKM.Form"/> name of the Set entity, stored in PKHeX style (instead of Showdown's)
@ -53,7 +54,7 @@ public interface IBattleTemplate : ISpeciesForm, IGigantamaxReadOnly, IDynamaxLe
/// <summary>
/// <see cref="PKM.HPType"/> of the Set entity.
/// </summary>
int HiddenPowerType { get; }
sbyte HiddenPowerType { get; }
/// <summary>
/// <see cref="EffortValues"/> of the Set entity.

View File

@ -0,0 +1,17 @@
namespace PKHeX.Core;
/// <summary>
/// Style to display moves.
/// </summary>
public enum MoveDisplayStyle : byte
{
/// <summary>
/// Moves are slots 1-4, with no empty slots, and correspond to the rectangular grid without empty spaces.
/// </summary>
Fill,
/// <summary>
/// Move slots are assigned to the directional pad, and unused directional slots are not displayed.
/// </summary>
Directional,
}

View File

@ -0,0 +1,37 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace PKHeX.Core;
/// <summary>
/// Logic for retrieving teams from URLs.
/// </summary>
public static class BattleTemplateTeams
{
/// <summary>
/// Tries to check if the input text is a valid URL for a team, and if so, retrieves the team data.
/// </summary>
/// <param name="text">The input text to check.</param>
/// <param name="content">When the method returns, contains the retrieved team data if the text is a valid URL; otherwise, null.</param>
/// <returns><see langword="true"/> if the text is a valid URL and the team data was successfully retrieved; otherwise, <see langword="false"/>.</returns>
public static bool TryGetSetLines(string text, [NotNullWhen(true)] out string? content)
{
if (ShowdownTeam.IsURL(text, out var url))
return ShowdownTeam.TryGetSets(url, out content);
if (PokepasteTeam.IsURL(text, out url))
return PokepasteTeam.TryGetSets(url, out content);
content = text;
return false;
}
/// <summary>
/// Attempts to retrieve sets from the provided text. If the text is a valid URL, it retrieves the team data from the URL.
/// </summary>
/// <param name="text">The input text to check.</param>
/// <returns>An enumerable collection of <see cref="ShowdownSet"/> objects representing the sets.</returns>
public static IEnumerable<ShowdownSet> TryGetSets(string text)
{
var ingest = TryGetSetLines(text, out var many) ? many : text;
return ShowdownParsing.GetShowdownSets(ingest);
}
}

View File

@ -0,0 +1,94 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
namespace PKHeX.Core;
/// <summary>
/// Logic for retrieving Showdown teams from URLs.
/// </summary>
/// <remarks>
/// <see href="https://pokepast.es/"/>
/// </remarks>
public static class PokepasteTeam
{
/// <summary>
/// Generates the raw URL for retrieving a team based on the supplied team identifier.
/// </summary>
/// <param name="team">The numeric identifier of the team.</param>
/// <returns>A string containing the full URL to access the team data.</returns>
public static string GetURL(ulong team) => $"https://pokepast.es/{team:x16}/raw";
/// <inheritdoc cref="GetURL"/>
/// <remarks>For legacy team indexes (first 255 or so), shouldn't ever be triggered non-test team indexes.</remarks>
public static string GetURLOld(int team) => $"https://pokepast.es/{team}/raw";
/// <summary>
/// Attempts to retrieve the Showdown team data from a specified URL, and reformats it.
/// </summary>
/// <param name="url">The URL to retrieve the team data from.</param>
/// <param name="content">When the method returns, contains the processed team data if retrieval and formatting succeed; otherwise, null.</param>
/// <returns><see langword="true"/> if the team data is successfully retrieved and reformatted; otherwise, <see langword="false"/>.</returns>
public static bool TryGetSets(string url, [NotNullWhen(true)] out string? content)
{
content = null;
if (!Uri.TryCreate(url, UriKind.Absolute, out var uriResult) || (uriResult.Scheme != Uri.UriSchemeHttp && uriResult.Scheme != Uri.UriSchemeHttps))
return false;
content = NetUtil.GetStringFromURL(uriResult);
return content != null;
}
/// <summary>
/// Determines if the provided text is a valid Showdown team URL. If valid, returns a normalized API URL.
/// </summary>
/// <param name="text">The text to evaluate.</param>
/// <param name="url">When the method returns, contains the normalized API URL if the text represents a valid Showdown team URL; otherwise, null.</param>
/// <returns><see langword="true"/> if the text is a valid Showdown team URL; otherwise, <see langword="false"/>.</returns>
public static bool IsURL(ReadOnlySpan<char> text, [NotNullWhen(true)] out string? url)
{
text = text.Trim();
url = null;
if (!text.StartsWith("https://pokepast.es/")) // short link
return false;
return TryCheckWeb(text, out url);
}
/// <summary>
/// Attempts to extract the team identifier from a Showdown web URL and converts it to a standard API URL.
/// </summary>
/// <param name="text">The Showdown web URL as a read-only span of characters.</param>
/// <param name="url">When the method returns, contains the standardized API URL if extraction is successful; otherwise, null.</param>
/// <returns><see langword="true"/> if the team index is successfully extracted and converted; otherwise, <see langword="false"/>.</returns>
public static bool TryCheckWeb(ReadOnlySpan<char> text, [NotNullWhen(true)] out string? url)
{
// if ends with `/`, remove.
if (text.EndsWith('/'))
text = text[..^1]; // remove trailing slash
// if ends with `/raw`, remove.
if (text.EndsWith("/raw"))
text = text[..^4]; // remove trailing /raw
url = null;
// seek back to `/`
int start = text.LastIndexOf('/'); // seek back to /
if (start == -1)
return false;
// get the substring after
var number = text[(start + 1)..];
switch (number.Length)
{
case 16 when ulong.TryParse(number, NumberStyles.HexNumber, null, out var hash):
url = GetURL(hash);
return true;
case <= 8 when int.TryParse(number, out var team):
url = GetURLOld(team);
return true;
default:
return false;
}
}
}

View File

@ -0,0 +1,476 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using static PKHeX.Core.Species;
namespace PKHeX.Core;
/// <summary>
/// Logic for parsing details for <see cref="ShowdownSet"/> objects.
/// </summary>
public static class ShowdownParsing
{
private static readonly string[] genderForms = ["", "F", ""];
/// <inheritdoc cref="ShowdownSet.DefaultListAllocation"/>
private const int DefaultListAllocation = ShowdownSet.DefaultListAllocation;
/// <summary>
/// Gets the Form ID from the input <see cref="name"/>.
/// </summary>
/// <param name="name">Form name to find the form index of</param>
/// <param name="strings">Localized string source to fetch with</param>
/// <param name="species">Species ID the form belongs to</param>
/// <param name="context">Format the form name should appear in</param>
/// <returns>Zero (base form) if no form matches the input string.</returns>
public static byte GetFormFromString(ReadOnlySpan<char> name, GameStrings strings, ushort species, EntityContext context)
{
if (name.Length == 0)
return 0;
var forms = FormConverter.GetFormList(species, strings.Types, strings.forms, genderForms, context);
if (forms.Length < 1)
return 0;
// Find first matching index that matches any case, ignoring dashes interchanged with spaces.
for (byte i = 0; i < forms.Length; i++)
{
if (IsFormEquivalent(forms[i], name))
return i;
}
// No match, assume default 0 form.
return 0;
}
private static bool IsFormEquivalent(ReadOnlySpan<char> reference, ReadOnlySpan<char> input)
{
if (input.Length != reference.Length)
return false;
for (int i = 0; i < input.Length; i++)
{
var c1 = input[i];
var c2 = reference[i];
if (char.ToUpperInvariant(c1) == char.ToUpperInvariant(c2))
continue;
if (c1 is ' ' or '-' && c2 is ' ' or '-')
continue;
return false;
}
return true;
}
/// <summary>
/// Converts a Form ID to string.
/// </summary>
/// <param name="form">Form to get the form name of</param>
/// <param name="strings">Localized string source to fetch with</param>
/// <param name="species">Species ID the form belongs to</param>
/// <param name="context">Format the form name should appear in</param>
public static string GetStringFromForm(byte form, GameStrings strings, ushort species, EntityContext context)
{
if (form == 0)
return string.Empty;
var result = FormConverter.GetStringFromForm(species, form, strings, genderForms, context);
if (result.Length == 0)
return string.Empty;
// Showdown uses a non-standard representation for some forms, and uses interstitial dashes instead of spaces.
if (strings.Language != LanguageID.English)
return result;
return GetShowdownFormName(species, result);
}
private const string MiniorFormName = "Meteor";
/// <summary>
/// Converts the PKHeX standard form name to Showdown's form name.
/// </summary>
/// <param name="species">Species ID</param>
/// <param name="form">PKHeX form name</param>
public static string GetShowdownFormName(ushort species, string form)
{
if (form.Length == 0)
{
return species switch
{
(int)Minior => MiniorFormName,
_ => form,
};
}
return species switch
{
(int)Basculin when form is "Blue" => "Blue-Striped",
(int)Vivillon when form is "Poké Ball" => "Pokeball",
(int)Zygarde => form.Replace("-C", string.Empty).Replace("50%", string.Empty),
(int)Minior when form.StartsWith("M-", StringComparison.OrdinalIgnoreCase) => MiniorFormName,
(int)Minior => form.Replace("C-", string.Empty),
(int)Necrozma when form is "Dusk" => $"{form}-Mane",
(int)Necrozma when form is "Dawn" => $"{form}-Wings",
(int)Polteageist or (int)Sinistea => form == "Antique" ? form : string.Empty,
(int)Maushold when form is "Family of Four" => "Four",
(int)Greninja or (int)Rockruff or (int)Koraidon or (int)Miraidon => string.Empty,
_ => FormInfo.HasTotemForm(species) && form == "Large"
? species is (int)Raticate or (int)Marowak ? "Alola-Totem" : "Totem"
: form.Replace(' ', '-'),
};
}
public static bool IsTotemForm(ReadOnlySpan<char> formName) =>
formName.Equals("Totem", StringComparison.OrdinalIgnoreCase) ||
formName.Equals("Alola-Totem", StringComparison.OrdinalIgnoreCase) ||
formName.Equals("Large", StringComparison.OrdinalIgnoreCase);
public static bool IsCosplayPikachu(ReadOnlySpan<char> formName, ReadOnlySpan<string> formNames)
=> FormConverter.IsCosplayPikachu(formName, formNames);
/// <summary>
/// Converts the Showdown form name to PKHeX's form name.
/// </summary>
/// <param name="species">Species ID</param>
/// <param name="form">Showdown form name</param>
/// <param name="ability">Showdown ability ID</param>
public static string GetFormNameFromShowdownFormName(ushort species, string form, int ability)
{
if (form.Length != 0)
form = form.Replace(' ', '-'); // inconsistencies are great
return species switch
{
(int)Basculin when form is "Blue-Striped" => "Blue",
(int)Vivillon when form is "Pokeball" => "Poké Ball",
(int)Necrozma when form is "Dusk-Mane" => "Dusk",
(int)Necrozma when form is "Dawn-Wings" => "Dawn",
(int)Toxtricity when form is "Low-Key" => "Low Key",
(int)Darmanitan when form is "Galar-Zen" => "Galar Zen",
(int)Minior when form is not MiniorFormName => $"C-{form}",
(int)Zygarde when form is "Complete" => form,
(int)Zygarde when ability == 211 => $"{(form.Contains("10%") ? "10%" : "50%")}-C",
(int)Greninja when ability == 210 => "Ash", // Battle Bond
(int)Rockruff when ability == 020 => "Dusk", // Rockruff-1
(int)Maushold when form is "Four" => "Family of Four",
(int)Urshifu or (int)Pikachu or (int)Alcremie => form.Replace('-', ' '), // Strike and Cosplay
(int)Pumpkaboo or (int)Gourgeist when form is "Average" => "Medium",
(int)Pumpkaboo or (int)Gourgeist when form is "Super" => "Jumbo",
_ => FormInfo.HasTotemForm(species) && form.EndsWith("Totem", StringComparison.OrdinalIgnoreCase) ? "Large" : form,
};
}
/// <summary>
/// Fetches <see cref="ShowdownSet"/> data from the input <see cref="lines"/>.
/// </summary>
/// <param name="lines">Raw lines containing numerous multi-line set data.</param>
/// <param name="localization">Localization data for the set.</param>
/// <returns><see cref="ShowdownSet"/> objects until <see cref="lines"/> is consumed.</returns>
public static IEnumerable<ShowdownSet> GetShowdownSets(IEnumerable<string> lines, BattleTemplateLocalization localization)
{
// exported sets always have >4 moves; new List will always require 1 resizing, allocate 2x to save 1 reallocation.
// intro, nature, ability, (ivs, evs, shiny, level) 4*moves
var setLines = new List<string>(DefaultListAllocation);
foreach (var line in lines)
{
if (!string.IsNullOrWhiteSpace(line))
{
setLines.Add(line);
continue;
}
if (setLines.Count == 0)
continue;
yield return new ShowdownSet(setLines, localization);
setLines.Clear();
}
if (setLines.Count != 0)
yield return new ShowdownSet(setLines, localization);
}
/// <inheritdoc cref="GetShowdownSets(IEnumerable{string},BattleTemplateLocalization)"/>
public static IEnumerable<ShowdownSet> GetShowdownSets(IEnumerable<string> lines)
{
var setLines = new List<string>(DefaultListAllocation);
foreach (var line in lines)
{
if (!string.IsNullOrWhiteSpace(line))
{
setLines.Add(line);
continue;
}
if (setLines.Count == 0)
continue;
yield return TryParseAnyLanguage(setLines, out var set) ? set : new ShowdownSet(setLines);
setLines.Clear();
}
if (setLines.Count != 0)
yield return TryParseAnyLanguage(setLines, out var set) ? set : new ShowdownSet(setLines);
}
/// <inheritdoc cref="GetShowdownSets(IEnumerable{string},BattleTemplateLocalization)"/>
public static IEnumerable<ShowdownSet> GetShowdownSets(ReadOnlyMemory<char> text, BattleTemplateLocalization localization)
{
int start = 0;
do
{
var span = text.Span;
var slice = span[start..];
var set = GetShowdownSet(slice, localization, out int length);
if (set.Species == 0)
break;
yield return set;
start += length;
}
while (start < text.Length);
}
/// <inheritdoc cref="GetShowdownSets(IEnumerable{string},BattleTemplateLocalization)"/>
/// <summary>
/// Language-unknown version of <see cref="GetShowdownSets(IEnumerable{string},BattleTemplateLocalization)"/>.
/// </summary>
public static IEnumerable<ShowdownSet> GetShowdownSets(ReadOnlyMemory<char> text)
{
int start = 0;
do
{
var span = text.Span;
var slice = span[start..];
var set = GetShowdownSet(slice, out int length);
if (set.Species == 0)
break;
yield return set;
start += length;
}
while (start < text.Length);
}
/// <inheritdoc cref="GetShowdownSets(ReadOnlyMemory{char},BattleTemplateLocalization)"/>
public static IEnumerable<ShowdownSet> GetShowdownSets(string text, BattleTemplateLocalization localization) => GetShowdownSets(text.AsMemory(), localization);
private static int GetLength(ReadOnlySpan<char> text)
{
// Find the end of the Showdown Set lines.
// The end is implied when:
// - we see a complete whitespace or empty line
int length = 0;
while (true)
{
var newline = text.IndexOf('\n');
if (newline == -1)
return length + text.Length;
var slice = text[..newline];
var used = newline + 1;
length += used;
if (slice.IsWhiteSpace())
return length;
text = text[used..];
}
}
/// <summary>
/// Attempts to parse the input <see cref="text"/> into a <see cref="ShowdownSet"/> object.
/// </summary>
/// <param name="text">Input string to parse.</param>
/// <param name="localization">Input localization to use.</param>
/// <param name="length">Amount of characters consumed from the input string.</param>
/// <returns>Parsed <see cref="ShowdownSet"/> object if successful, otherwise might be a best-match with some/all unparsed lines.</returns>
public static ShowdownSet GetShowdownSet(ReadOnlySpan<char> text, BattleTemplateLocalization localization, out int length)
{
length = GetLength(text);
var slice = text[..length];
var set = new ShowdownSet(slice, localization);
while (length < text.Length && text[length] is '\r' or '\n' or ' ')
length++;
return set;
}
/// <inheritdoc cref="GetShowdownSet(ReadOnlySpan{char},BattleTemplateLocalization,out int)"/>
public static ShowdownSet GetShowdownSet(ReadOnlySpan<char> text, out int length)
{
length = GetLength(text);
var slice = text[..length];
if (!TryParseAnyLanguage(slice, out var set))
set = new ShowdownSet(slice); // should never fall back
while (length < text.Length && text[length] is '\r' or '\n' or ' ')
length++;
return set;
}
/// <inheritdoc cref="GetShowdownSets(ReadOnlyMemory{char},BattleTemplateLocalization)"/>
public static IEnumerable<ShowdownSet> GetShowdownSets(string text) => GetShowdownSets(text.AsMemory());
/// <inheritdoc cref="GetShowdownText(PKM, in BattleTemplateExportSettings)"/>
public static string GetShowdownText(PKM pk) => GetShowdownText(pk, BattleTemplateExportSettings.Showdown);
/// <summary>
/// Converts the <see cref="PKM"/> data into an importable set format for Pokémon Showdown.
/// </summary>
/// <param name="pk">PKM to convert to string</param>
/// <param name="settings">Import localization/style setting</param>
/// <returns>Multi line set data</returns>
public static string GetShowdownText(PKM pk, in BattleTemplateExportSettings settings)
{
if (pk.Species == 0)
return string.Empty;
var set = new ShowdownSet(pk, settings.Localization);
set.InterpretAsPreview(pk);
return set.GetText(settings);
}
/// <summary>
/// Fetches ShowdownSet lines from the input <see cref="PKM"/> data.
/// </summary>
/// <param name="data">Pokémon data to summarize.</param>
/// <param name="settings">Export localization/style setting</param>
/// <returns>Consumable list of <see cref="ShowdownSet.Text"/> lines.</returns>
public static IEnumerable<string> GetShowdownText(IEnumerable<PKM> data, in BattleTemplateExportSettings settings)
{
List<string> result = new(1);
var sets = GetShowdownSets(data);
foreach (var set in sets)
result.Add(set.GetText(settings));
return result;
}
/// <summary>
/// Fetches ShowdownSet lines from the input <see cref="PKM"/> data.
/// </summary>
/// <param name="data">Pokémon data to summarize.</param>
/// <returns>Consumable list of <see cref="ShowdownSet.Text"/> lines.</returns>
public static IEnumerable<ShowdownSet> GetShowdownSets(IEnumerable<PKM> data)
{
foreach (var pk in data)
{
if (pk.Species == 0)
continue;
yield return new ShowdownSet(pk);
}
}
/// <inheritdoc cref="GetShowdownSets(IEnumerable{string},BattleTemplateLocalization)"/>
public static string GetShowdownSets(IEnumerable<PKM> data, string separator) => string.Join(separator, GetShowdownText(data, BattleTemplateExportSettings.Showdown));
/// <summary>
/// Fetches ShowdownSet lines from the input <see cref="PKM"/> data, and combines it into one string.
/// </summary>
/// <param name="data">Pokémon data to summarize.</param>
/// <param name="separator">Splitter between each set.</param>
/// <param name="settings">Import localization/style setting</param>
/// <returns>Single string containing all <see cref="ShowdownSet.Text"/> lines.</returns>
public static string GetShowdownSets(IEnumerable<PKM> data, string separator, in BattleTemplateExportSettings settings) => string.Join(separator, GetShowdownText(data, settings));
/// <summary>
/// Gets a localized string preview of the provided <see cref="pk"/>.
/// </summary>
/// <param name="pk">Pokémon data</param>
/// <param name="settings">Export settings</param>
/// <returns>Multi-line string</returns>
public static string GetLocalizedPreviewText(PKM pk, in BattleTemplateExportSettings settings)
{
var set = new ShowdownSet(pk, settings.Localization);
set.InterpretAsPreview(pk);
return set.GetText(settings);
}
/// <summary>
/// Tries to parse the input string into a <see cref="ShowdownSet"/> object.
/// </summary>
/// <param name="message">Input string to parse.</param>
/// <param name="set">Parsed <see cref="ShowdownSet"/> object if successful, otherwise might be a best-match with some unparsed lines.</param>
/// <returns>True if the input was parsed successfully, false otherwise.</returns>
public static bool TryParseAnyLanguage(ReadOnlySpan<char> message, [NotNullWhen(true)] out ShowdownSet? set)
{
set = null;
if (message.Length == 0)
return false;
var invalid = int.MaxValue;
var all = BattleTemplateLocalization.GetAll();
foreach (var lang in all)
{
var local = lang.Value;
var tmp = new ShowdownSet(message, local);
var bad = tmp.InvalidLines.Count;
if (bad == 0)
{
set = tmp;
return true;
}
// Check for invalid lines
if (bad >= invalid)
continue;
// Best so far.
invalid = bad;
set = tmp;
}
if (set is null)
return false;
return set.Species != 0;
}
/// <inheritdoc cref="TryParseAnyLanguage(ReadOnlySpan{char}, out ShowdownSet?)"/>
public static bool TryParseAnyLanguage(IReadOnlyList<string> setLines, [NotNullWhen(true)] out ShowdownSet? set)
{
set = null;
if (setLines.Count == 0)
return false;
var invalid = int.MaxValue;
var all = BattleTemplateLocalization.GetAll();
foreach (var lang in all)
{
var local = lang.Value;
var tmp = new ShowdownSet(setLines, local);
var bad = tmp.InvalidLines.Count;
if (bad == 0)
{
set = tmp;
return true;
}
// Check for invalid lines
if (bad >= invalid)
continue;
// Best so far.
invalid = bad;
set = tmp;
}
return false;
}
/// <summary>
/// Tries to translate the input battle template <see cref="message"/> into a localized string.
/// </summary>
/// <param name="message">Input string to parse.</param>
/// <param name="outputSettings">Export settings</param>
/// <param name="translated">Translated string if successful.</param>
/// <returns><see langword="true"/> if the input was translated successfully, <see langword="false"/> otherwise.</returns>
public static bool TryTranslate(ReadOnlySpan<char> message, BattleTemplateExportSettings outputSettings, [NotNullWhen(true)] out string? translated)
{
translated = null;
if (!TryParseAnyLanguage(message, out var set))
return false;
translated = set.GetText(outputSettings);
return true;
}
/// <inheritdoc cref="TryTranslate(ReadOnlySpan{char}, BattleTemplateExportSettings, out string?)"/>
public static bool TryTranslate(IReadOnlyList<string> message, BattleTemplateExportSettings outputSettings, [NotNullWhen(true)] out string? translated)
{
translated = null;
if (!TryParseAnyLanguage(message, out var set))
return false;
translated = set.GetText(outputSettings);
return true;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,175 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Text;
namespace PKHeX.Core;
/// <summary>
/// Logic for retrieving Showdown teams from URLs.
/// </summary>
/// <remarks>
/// <see href="https://play.pokemonshowdown.com/"/>
/// </remarks>
public static class ShowdownTeam
{
/// <summary>
/// Generates the API URL for retrieving a Showdown team based on the supplied team identifier.
/// </summary>
/// <param name="team">The numeric identifier of the team.</param>
/// <returns>A string containing the full URL to access the team data via the API.</returns>
public static string GetURL(int team) => $"https://play.pokemonshowdown.com/api/getteam?teamid={team}&raw=1";
/// <summary>
/// Attempts to retrieve the Showdown team data from a specified URL, and reformats it.
/// </summary>
/// <param name="url">The URL to retrieve the team data from.</param>
/// <param name="content">When the method returns, contains the processed team data if retrieval and formatting succeed; otherwise, null.</param>
/// <returns><see langword="true"/> if the team data is successfully retrieved and reformatted; otherwise, <see langword="false"/>.</returns>
public static bool TryGetSets(string url, [NotNullWhen(true)] out string? content)
{
content = null;
if (!Uri.TryCreate(url, UriKind.Absolute, out var uriResult) || (uriResult.Scheme != Uri.UriSchemeHttp && uriResult.Scheme != Uri.UriSchemeHttps))
return false;
content = NetUtil.GetStringFromURL(uriResult);
if (content == null)
return false;
return GetFromReply(ref content);
}
/// <summary>
/// Extracts the team data from the API reply and reformats it by replacing escaped newline
/// characters with system-specific line breaks.
/// </summary>
/// <param name="content">
/// A reference to the API response string. On successful extraction, the value is replaced
/// with the reformatted team data; otherwise, it remains unchanged.
/// </param>
/// <returns>
/// <see langword="true"/> if the team data is successfully extracted and reformatted; otherwise, <see langword="false"/>.
/// </returns>
public static bool GetFromReply(ref string content)
{
// reformat
const string startText = """
"team":"
""";
var start = content.IndexOf(startText, StringComparison.Ordinal);
if (start == -1)
return false;
start += startText.Length; // skip to the start of the team
var end = content.LastIndexOf("\\n", StringComparison.Ordinal);
if (end == -1)
return false;
var length = end - start;
if (length < 5) // arbitrary length check
return false;
var sb = new StringBuilder();
sb.Append(content, start, length);
sb.Replace("\\n", Environment.NewLine);
content = sb.ToString();
return true;
}
/// <summary>
/// Determines if the provided text is a valid Showdown team URL. If valid, returns a normalized API URL.
/// </summary>
/// <param name="text">The text to evaluate.</param>
/// <param name="url">When the method returns, contains the normalized API URL if the text represents a valid Showdown team URL; otherwise, null.</param>
/// <returns><see langword="true"/> if the text is a valid Showdown team URL; otherwise, <see langword="false"/>.</returns>
public static bool IsURL(ReadOnlySpan<char> text, [NotNullWhen(true)] out string? url)
{
text = text.Trim();
if (text.StartsWith("https://psim.us/t/") || // short link
text.StartsWith("https://teams.pokemonshowdown.com/"))
{
return TryCheckWeb(text, out url);
}
if (text.StartsWith("https://play.pokemonshowdown.com/api/getteam?teamid="))
return TryCheckAPI(text, out url);
url = null;
return false;
}
/// <summary>
/// Attempts to extract the team identifier from a Showdown web URL and converts it to a standard API URL.
/// </summary>
/// <param name="text">The Showdown web URL as a read-only span of characters.</param>
/// <param name="url">When the method returns, contains the standardized API URL if extraction is successful; otherwise, null.</param>
/// <returns><see langword="true"/> if the team index is successfully extracted and converted; otherwise, <see langword="false"/>.</returns>
public static bool TryCheckWeb(ReadOnlySpan<char> text, [NotNullWhen(true)] out string? url)
{
url = null;
if (!TryGetIndexWeb(text, out var team))
return false;
url = GetURL(team);
return true;
}
/// <summary>
/// Attempts to extract the team identifier from a Showdown API URL and returns a standardized API URL.
/// </summary>
/// <param name="text">The Showdown API URL as a read-only span of characters.</param>
/// <param name="url">When the method returns, contains the standardized API URL if extraction is successful; otherwise, null.</param>
/// <returns><see langword="true"/> if the team index is successfully extracted and the URL normalized; otherwise, <see langword="false"/>.</returns>
public static bool TryCheckAPI(ReadOnlySpan<char> text, [NotNullWhen(true)] out string? url)
{
url = null;
if (!TryGetIndexAPI(text, out var team))
return false;
url = GetURL(team);
return true;
}
/// <summary>
/// Extracts the team identifier from a Showdown web URL.
/// </summary>
/// <param name="text">The Showdown web URL provided as a read-only span of characters.</param>
/// <param name="team">When the method returns, contains the extracted team identifier if successful; otherwise, zero.</param>
/// <returns><see langword="true"/> if the team identifier is successfully extracted; otherwise, <see langword="false"/>.</returns>
public static bool TryGetIndexWeb(ReadOnlySpan<char> text, out int team)
{
team = 0;
if (text.EndsWith('/'))
text = text[..^1]; // remove trailing slash
if (text.EndsWith("/raw"))
text = text[..^4]; // remove trailing /raw
int start = text.LastIndexOf('/'); // seek back to =
if (start == -1)
return false;
var number = text[(start + 1)..];
if (!int.TryParse(number, out team))
return false;
return true;
}
/// <summary>
/// Extracts the team identifier from a Showdown API URL.
/// </summary>
/// <param name="text">The Showdown API URL as a read-only span of characters.</param>
/// <param name="team">When the method returns, contains the extracted team identifier if successful; otherwise, zero.</param>
/// <returns><see langword="true"/> if the team identifier is successfully extracted; otherwise, <see langword="false"/>.</returns>
public static bool TryGetIndexAPI(ReadOnlySpan<char> text, out int team)
{
team = 0;
if (!text.EndsWith("&raw=1"))
return false;
text = text[..^6];
int start = text.LastIndexOf('='); // seek back to =
if (start == -1)
return false;
var number = text[(start + 1)..];
if (!int.TryParse(number, out team))
return false;
return true;
}
}

View File

@ -0,0 +1,411 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
namespace PKHeX.Core;
/// <summary>
/// Configuration for displaying stats.
/// </summary>
[TypeConverter(typeof(ExpandableObjectConverter))]
public sealed class StatDisplayConfig
{
/// <summary>
/// Stat names are displayed without localization; H:X A:X B:X C:X D:X S:X
/// </summary>
public static readonly StatDisplayConfig HABCDS = new()
{
Names = ["H", "A", "B", "C", "D", "S"],
Separator = " ",
ValueGap = ":",
IsLeft = true,
AlwaysShow = true,
};
/// <summary>
/// Stat names are displayed without localization; X/X/X/X/X/X
/// </summary>
/// <remarks>
/// Same as <see cref="Raw00"/> but with no leading zeroes.
/// </remarks>
public static readonly StatDisplayConfig Raw = new()
{
Names = [],
Separator = "/",
ValueGap = string.Empty,
AlwaysShow = true,
};
/// <summary>
/// Stat names are displayed without localization; XX/XX/XX/XX/XX/XX
/// </summary>
/// <remarks>
/// Same as <see cref="Raw"/> but with 2 digits (leading zeroes).
/// </remarks>
public static readonly StatDisplayConfig Raw00 = new()
{
Names = [],
Separator = "/",
ValueGap = string.Empty,
AlwaysShow = true,
MinimumDigits = 2,
};
/// <summary>
/// List of stat display styles that are commonly used and not specific to a localization.
/// </summary>
public static List<StatDisplayConfig> Custom { get; } = [HABCDS, Raw]; // Raw00 parses equivalent to Raw
/// <summary>List of stat names to display</summary>
public required string[] Names { get; init; }
/// <summary>Separator between each stat+value declaration</summary>
public string Separator { get; init; } = " / ";
/// <summary>Separator between the stat name and value</summary>
public string ValueGap { get; init; } = " ";
/// <summary><see langword="true"/> if the text is displayed on the left side of the value</summary>
public bool IsLeft { get; init; }
/// <summary><see langword="true"/> if the stat is always shown, even if the value is default</summary>
public bool AlwaysShow { get; init; }
/// <summary>Minimum number of digits to show for the stat value.</summary>
public int MinimumDigits { get; init; }
/// <summary>
/// Gets the index of the displayed stat name (in visual order) via a case-insensitive search.
/// </summary>
/// <param name="stat">Stat name, trimmed.</param>
/// <returns>-1 if not found, otherwise the index of the stat name.</returns>
public int GetStatIndex(ReadOnlySpan<char> stat)
{
for (int i = 0; i < Names.Length; i++)
{
if (stat.Equals(Names[i], StringComparison.OrdinalIgnoreCase))
return i;
}
return -1;
}
public override string ToString() => string.Join(Separator, Names);
/// <summary>
/// Formats a stat value into a string builder.
/// </summary>
/// <param name="sb">Result string builder</param>
/// <param name="statIndex">Display index of the stat</param>
/// <param name="statValue">Stat value</param>
/// <param name="valueSuffix">Optional suffix for the value, to display a stat amplification request</param>
/// <param name="skipValue"><see langword="true"/> to skip the value, only displaying the stat name and amplification (if provided)</param>
public void Format<T>(StringBuilder sb, int statIndex, T statValue, ReadOnlySpan<char> valueSuffix = default, bool skipValue = false)
{
var statName = statIndex < Names.Length ? Names[statIndex] : "";
var length = GetStatSize(statName, statValue, valueSuffix, skipValue);
if (sb.Length + length > sb.Capacity)
sb.EnsureCapacity(sb.Length + length);
Append(sb, statName, statValue, valueSuffix, skipValue);
}
private void Append<T>(StringBuilder sb, ReadOnlySpan<char> statName, T statValue, ReadOnlySpan<char> valueSuffix, bool skipValue)
{
int start = sb.Length;
if (!skipValue)
{
sb.Append(statValue);
var length = sb.Length - start;
if (length < MinimumDigits)
sb.Insert(start, "0", MinimumDigits - length);
}
sb.Append(valueSuffix);
if (IsLeft)
{
sb.Insert(start, ValueGap);
sb.Insert(start, statName);
}
else
{
sb.Append(ValueGap);
sb.Append(statName);
}
}
private int GetStatSize<T>(ReadOnlySpan<char> statName, T statValue, ReadOnlySpan<char> valueSuffix, bool skipValue)
{
var length = statName.Length + ValueGap.Length + valueSuffix.Length;
if (!skipValue)
length += (int)Math.Max(MinimumDigits, Math.Floor(Math.Log10(Convert.ToDouble(statValue)) + 1));
return length;
}
/// <summary>
/// Gets the separator character used for parsing.
/// </summary>
private char GetSeparatorParse() => GetSeparatorParse(Separator);
private static char GetSeparatorParse(ReadOnlySpan<char> sep) => sep.Length switch
{
0 => ' ',
1 => sep[0],
_ => sep.Trim()[0]
};
/// <summary>
/// Imports a list of stats from a string.
/// </summary>
/// <param name="message">Input string</param>
/// <param name="result">Result storage</param>
/// <returns>Parse result</returns>
public StatParseResult TryParse(ReadOnlySpan<char> message, Span<int> result)
{
var separator = GetSeparatorParse();
var gap = ValueGap.AsSpan().Trim();
// If stats are not labeled, parse with the straightforward parser.
if (Names.Length == 0)
return TryParseRaw(message, result, separator);
else if (IsLeft)
return TryParseIsLeft(message, result, separator, gap);
else
return TryParseRight(message, result, separator, gap);
}
private StatParseResult TryParseIsLeft(ReadOnlySpan<char> message, Span<int> result, char separator, ReadOnlySpan<char> valueGap)
{
// Parse left-to-right by splitting on separator, then identifying which stat each segment contains.
// Format: "StatName Value / StatName Value / ..."
var rec = new StatParseResult();
while (message.Length != 0)
{
// Get the next segment
ReadOnlySpan<char> segment;
var indexSeparator = message.IndexOf(separator);
if (indexSeparator != -1)
{
segment = message[..indexSeparator].Trim();
message = message[(indexSeparator + 1)..].TrimStart();
}
else
{
segment = message.Trim();
message = default;
}
if (segment.Length == 0)
{
rec.MarkDirty(); // empty segment
continue;
}
// Find which stat name this segment contains (should be at the start for IsLeft)
var statIndex = TryFindStatNameAtStart(segment, out var statNameLength);
if (statIndex == -1)
{
rec.MarkDirty(); // unrecognized stat
continue;
}
// Extract the value after the stat name
var value = segment[statNameLength..].TrimStart();
if (valueGap.Length > 0 && value.StartsWith(valueGap))
value = value[valueGap.Length..].TrimStart();
if (value.Length != 0)
{
var amped = TryPeekAmp(ref value, ref rec, statIndex);
if (amped && value.Length == 0)
rec.MarkParsed(statIndex);
else
TryParse(result, ref rec, value, statIndex);
}
else if (rec.WasParsed(statIndex))
{
rec.MarkDirty(); // duplicate stat
}
}
rec.FinishParse(Names.Length);
return rec;
}
/// <summary>
/// Tries to find a stat name at the start of the segment.
/// </summary>
/// <param name="segment">Segment to search</param>
/// <param name="length">Length of the matched stat name</param>
/// <returns>Stat index if found, -1 otherwise</returns>
private int TryFindStatNameAtStart(ReadOnlySpan<char> segment, out int length)
{
for (int i = 0; i < Names.Length; i++)
{
var name = Names[i];
if (segment.StartsWith(name, StringComparison.OrdinalIgnoreCase))
{
length = name.Length;
return i;
}
}
length = 0;
return -1;
}
/// <summary>
/// Tries to find a stat name at the end of the segment.
/// </summary>
/// <param name="segment">Segment to search</param>
/// <param name="length">Length of the matched stat name</param>
/// <returns>Stat index if found, -1 otherwise</returns>
private int TryFindStatNameAtEnd(ReadOnlySpan<char> segment, out int length)
{
for (int i = 0; i < Names.Length; i++)
{
var name = Names[i];
if (segment.EndsWith(name, StringComparison.OrdinalIgnoreCase))
{
length = name.Length;
return i;
}
}
length = 0;
return -1;
}
private StatParseResult TryParseRight(ReadOnlySpan<char> message, Span<int> result, char separator, ReadOnlySpan<char> valueGap)
{
// Parse left-to-right by splitting on separator, then identifying which stat each segment contains.
// Format: "Value StatName / Value StatName / ..."
var rec = new StatParseResult();
while (message.Length != 0)
{
// Get the next segment
ReadOnlySpan<char> segment;
var indexSeparator = message.IndexOf(separator);
if (indexSeparator != -1)
{
segment = message[..indexSeparator].Trim();
message = message[(indexSeparator + 1)..].TrimStart();
}
else
{
segment = message.Trim();
message = default;
}
if (segment.Length == 0)
{
rec.MarkDirty(); // empty segment
continue;
}
// Find which stat name this segment contains (should be at the end for Right/English style)
var statIndex = TryFindStatNameAtEnd(segment, out var statNameLength);
if (statIndex == -1)
{
rec.MarkDirty(); // unrecognized stat
continue;
}
// Extract the value before the stat name
var value = segment[..^statNameLength].TrimEnd();
if (valueGap.Length > 0 && value.EndsWith(valueGap))
value = value[..^valueGap.Length].TrimEnd();
if (value.Length != 0)
{
var amped = TryPeekAmp(ref value, ref rec, statIndex);
if (amped && value.Length == 0)
rec.MarkParsed(statIndex);
else
TryParse(result, ref rec, value, statIndex);
}
else if (rec.WasParsed(statIndex))
{
rec.MarkDirty(); // duplicate stat
}
}
rec.FinishParse(Names.Length);
return rec;
}
/// <summary>
/// Parses a raw stat string.
/// </summary>
/// <param name="message">Input string</param>
/// <param name="result">Output storage</param>
/// <param name="separator">Separator character</param>
public static StatParseResult TryParseRaw(ReadOnlySpan<char> message, Span<int> result, char separator)
{
var rec = new StatParseResult();
// Expect the message to contain all entries of `result` separated by the separator and an arbitrary amount of spaces permitted.
// The message is split by the separator, and each part is trimmed of whitespace.
for (int i = 0; i < result.Length; i++)
{
var index = message.IndexOf(separator);
ReadOnlySpan<char> value;
if (index != -1)
{
value = message[..index].TrimEnd();
message = message[(index + 1)..].TrimStart();
}
else // no further iterations to be done
{
value = message;
message = default;
}
if (value.Length == 0)
{
rec.MarkDirty(); // Something is wrong with the message, as we have an empty stat.
continue; // Maybe it's a duplicate separator; keep parsing and hope that the required amount are parsed.
}
var amped = TryPeekAmp(ref value, ref rec, i);
if (amped && value.Length == 0)
rec.MarkParsed(index);
else
TryParse(result, ref rec, value, i);
}
if (!message.IsWhiteSpace()) // shouldn't be anything left in the message to parse
rec.MarkDirty();
rec.FinishParseOnly(result.Length);
return rec;
}
private static void TryParse(Span<int> result, ref StatParseResult rec, ReadOnlySpan<char> value, int statIndex)
{
if (!int.TryParse(value, out var stat) || stat < 0)
{
rec.MarkDirty();
return;
}
result[statIndex] = stat;
rec.MarkParsed(statIndex);
}
private static bool TryPeekAmp(ref ReadOnlySpan<char> value, ref StatParseResult rec, int statIndex)
{
var last = value[^1];
if (last == '+')
{
rec.Plus = (sbyte)statIndex;
value = value[..^1].TrimEnd();
return true;
}
if (last == '-')
{
rec.Minus = (sbyte)statIndex;
value = value[..^1].TrimEnd();
return true;
}
return false;
}
}

View File

@ -0,0 +1,37 @@
namespace PKHeX.Core;
/// <summary>
/// Style to display stat names.
/// </summary>
public enum StatDisplayStyle : sbyte
{
Custom = -1,
/// <summary>
/// Stat names are displayed in abbreviated (2-3 characters) localized text.
/// </summary>
Abbreviated,
/// <summary>
/// Stat names are displayed in full localized text.
/// </summary>
Full,
/// <summary>
/// Stat names are displayed as a single character.
/// </summary>
/// <remarks>
/// This is the typical format used by the Japanese community; HABCDS.
/// </remarks>
HABCDS,
/// <summary>
/// Stat names are displayed without localization; X/X/X/X/X/X
/// </summary>
Raw,
/// <summary>
/// Stat names are displayed without localization; XX/XX/XX/XX/XX/XX
/// </summary>
Raw00,
}

View File

@ -0,0 +1,132 @@
using System;
namespace PKHeX.Core;
/// <summary>
/// Value result object of parsing a stat string.
/// </summary>
public record struct StatParseResult()
{
private const uint MaxStatCount = 6; // Number of stats in the game
public const sbyte NoStatAmp = -1;
/// <summary>
/// Count of parsed stats.
/// </summary>
public byte CountParsed { get; private set; } = 0; // could potentially make this a computed value (popcnt), but it's not worth it
/// <summary>
/// Bitflag indexes of parsed stats, indexed in visual order.
/// </summary>
public byte IndexesParsed { get; private set; } = 0;
/// <summary>
/// Stat index of increased stat, indexed in visual order.
/// </summary>
public sbyte Plus { get; set; } = NoStatAmp;
/// <summary>
/// Stat index of decreased stat, indexed in visual order.
/// </summary>
public sbyte Minus { get; set; } = NoStatAmp;
/// <summary>
/// Indicates if the parsing was clean (no un-parsed text).
/// </summary>
public bool IsParseClean { get; private set; } = true;
/// <summary>
/// Indicates if all stat indexes available were parsed.
/// </summary>
public bool IsParsedAllStats { get; private set; }
/// <summary>
/// Marks the stat index as parsed, and updates the count of parsed stats.
/// </summary>
/// <param name="statIndex">Visual index of the stat to mark as parsed.</param>
/// <returns>True if the stat had not been parsed before, false if it was already parsed.</returns>
public bool MarkParsed(int statIndex)
{
// Check if the stat index is valid (0-5)
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)statIndex, MaxStatCount);
if (WasParsed(statIndex))
return false;
// Mark the stat index as parsed
IndexesParsed |= (byte)(1 << statIndex);
++CountParsed;
return true;
}
/// <summary>
/// Checks if the stat index was parsed.
/// </summary>
/// <param name="statIndex">Visual index of the stat to check.</param>
/// <returns>True if the stat was parsed, false otherwise.</returns>
public readonly bool WasParsed(int statIndex)
{
// Check if the stat index is valid (0-5)
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)statIndex, MaxStatCount);
return (IndexesParsed & (1 << statIndex)) != 0;
}
/// <summary>
/// Marks the parsing as finished, and updates the internal state to indicate if all stats were parsed.
/// </summary>
/// <remarks>
/// This is used when not all stats are required to be parsed.
/// </remarks>
/// <param name="expect"></param>
public void FinishParse(int expect)
{
if (CountParsed == 0 && !HasAmps)
MarkDirty();
IsParsedAllStats = CountParsed == expect || IsParseClean;
}
/// <summary>
/// Marks the parsing as finished, and updates the internal state to indicate if all stats were parsed.
/// </summary>
/// <remarks>
/// This is used when a specific number of stats is expected.
/// </remarks>
/// <param name="expect"></param>
public void FinishParseOnly(int expect) => IsParsedAllStats = CountParsed == expect;
/// <summary>
/// Marks the parsing as dirty, indicating that the string was not a clean input string (user modified or the syntax doesn't match the spec).
/// </summary>
public void MarkDirty() => IsParseClean = false;
/// <summary>
/// Indicates if any stat has any amplified (+/-) requested, indicative of nature.
/// </summary>
public readonly bool HasAmps => Plus != NoStatAmp || Minus != NoStatAmp;
/// <summary>
/// Reorders the speed stat to be in the middle of the stats.
/// </summary>
/// <remarks>
/// Speed is visually represented as the last stat in the list, but it is actually the 3rd stat stored.
/// </remarks>
public void TreatAmpsAsSpeedNotLast()
{
Plus = GetSpeedMiddleIndex(Plus);
Minus = GetSpeedMiddleIndex(Minus);
}
/// <summary>
/// Adjusts stat indexes from visual to stored, and ignoring HP's index.
/// </summary>
/// <param name="amp">Visual index of the stat to get the adjusted value for.</param>
/// <returns>Stored index of the stat.</returns>
private static sbyte GetSpeedMiddleIndex(sbyte amp) => amp switch
{
// 0 => NoStatAmp -- handle via default case
1 => 0, // Atk
2 => 1, // Def
3 => 3, // SpA
4 => 4, // SpD
5 => 2, // Spe
_ => NoStatAmp,
};
}

View File

@ -0,0 +1,535 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Numerics;
using System.Reflection;
using static PKHeX.Core.BatchEditingUtil;
namespace PKHeX.Core;
/// <summary>
/// Base logic for editing entities with user provided <see cref="StringInstruction"/> list.
/// </summary>
/// <remarks>
/// Caches reflection results for the provided types, and provides utility methods for fetching properties and applying instructions.
/// </remarks>
public abstract class BatchEditingBase<TObject, TMeta> : IBatchEditor<TObject> where TObject : notnull
{
private readonly Type[] _types;
private readonly string[] _customProperties;
private readonly Dictionary<string, PropertyInfo>.AlternateLookup<ReadOnlySpan<char>>[] _props;
private readonly Lazy<string[][]> _properties;
protected BatchEditingBase(Type[] types, string[] customProperties, int expectedMax)
{
_types = types;
_customProperties = customProperties;
_props = GetPropertyDictionaries(types, expectedMax);
_properties = new Lazy<string[][]>(() => GetPropArray(_props, customProperties));
}
/// <summary>
/// Property names, indexed by <see cref="Types"/>.
/// </summary>
public string[][] Properties => _properties.Value;
/// <summary>
/// Gets the list of supported entity types.
/// </summary>
public IReadOnlyList<Type> Types => _types;
protected abstract TMeta CreateMeta(TObject entity);
protected abstract bool ShouldModify(TObject entity);
protected abstract bool TryHandleSetOperation(StringInstruction cmd, TMeta info, TObject entity, out ModifyResult result);
protected abstract bool TryHandleFilter(StringInstruction cmd, TMeta info, TObject entity, out bool isMatch);
/// <summary>
/// Tries to fetch the entity property from the cache of available properties.
/// </summary>
public bool TryGetHasProperty(TObject entity, ReadOnlySpan<char> name, [NotNullWhen(true)] out PropertyInfo? pi)
=> TryGetHasProperty(entity.GetType(), name, out pi);
/// <summary>
/// Tries to fetch the entity property from the cache of available properties.
/// </summary>
public bool TryGetHasProperty(Type type, ReadOnlySpan<char> name, [NotNullWhen(true)] out PropertyInfo? pi)
{
var index = _types.IndexOf(type);
if (index < 0)
{
pi = null;
return false;
}
var localProps = _props[index];
return localProps.TryGetValue(name, out pi);
}
/// <summary>
/// Gets a list of entity types that implement the requested property.
/// </summary>
public IEnumerable<string> GetTypesImplementing(string property)
{
for (int i = 0; i < _types.Length; i++)
{
var type = _types[i];
var localProps = _props[i];
if (!localProps.TryGetValue(property, out var pi))
continue;
yield return $"{type.Name}: {pi.PropertyType.Name}";
}
}
/// <summary>
/// Gets the type of the entity property using the saved cache of properties.
/// </summary>
public bool TryGetPropertyType(string propertyName, [NotNullWhen(true)] out string? result, int typeIndex = 0)
{
if (_customProperties.Contains(propertyName))
{
result = "Custom";
return true;
}
result = null;
if (typeIndex == 0)
{
foreach (var p in _props)
{
if (!p.TryGetValue(propertyName, out var pi))
continue;
result = pi.PropertyType.Name;
return true;
}
return false;
}
int index = typeIndex - 1;
if ((uint)index >= _props.Length)
index = 0;
var pr = _props[index];
if (!pr.TryGetValue(propertyName, out var info))
return false;
result = info.PropertyType.Name;
return true;
}
/// <summary>
/// Checks if the entity is filtered by the provided filters.
/// </summary>
public bool IsFilterMatch(IEnumerable<StringInstruction> filters, TObject entity)
{
var info = CreateMeta(entity);
var localProps = GetProps(entity);
foreach (var filter in filters)
{
if (!IsFilterMatch(filter, info, entity, localProps))
return false;
}
return true;
}
/// <summary>
/// Tries to modify the entity.
/// </summary>
public bool TryModifyIsSuccess(TObject entity, IEnumerable<StringInstruction> filters, IEnumerable<StringInstruction> modifications, Func<TObject, bool>? modifier = null)
=> TryModify(entity, filters, modifications, modifier) is ModifyResult.Modified;
/// <summary>
/// Tries to modify the entity using instructions and a custom modifier delegate.
/// </summary>
public ModifyResult TryModify(TObject entity, IEnumerable<StringInstruction> filters, IEnumerable<StringInstruction> modifications, Func<TObject, bool>? modifier = null)
{
if (!ShouldModify(entity))
return ModifyResult.Skipped;
var info = CreateMeta(entity);
var localProps = GetProps(entity);
foreach (var cmd in filters)
{
try
{
if (!IsFilterMatch(cmd, info, entity, localProps))
return ModifyResult.Filtered;
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
return ModifyResult.Error;
}
}
var error = false;
var result = ModifyResult.Skipped;
if (modifier is { } func)
{
try
{
if (!func(entity))
return ModifyResult.Skipped;
result = ModifyResult.Modified;
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
return ModifyResult.Error;
}
}
foreach (var cmd in modifications)
{
try
{
var tmp = SetProperty(cmd, entity, info, localProps);
if (tmp == ModifyResult.Error)
error = true;
else if (tmp != ModifyResult.Skipped)
result = tmp;
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
error = true;
}
}
if (error)
result |= ModifyResult.Error;
return result;
}
private static Dictionary<string, PropertyInfo>.AlternateLookup<ReadOnlySpan<char>>[] GetPropertyDictionaries(ReadOnlySpan<Type> types, int expectedMax)
{
var result = new Dictionary<string, PropertyInfo>.AlternateLookup<ReadOnlySpan<char>>[types.Length];
for (int i = 0; i < types.Length; i++)
result[i] = GetPropertyDictionary(types[i], ReflectUtil.GetAllPropertyInfoPublic, expectedMax).GetAlternateLookup<ReadOnlySpan<char>>();
return result;
}
private static Dictionary<string, PropertyInfo> GetPropertyDictionary(Type type, Func<Type, IEnumerable<PropertyInfo>> selector, int expectedMax)
{
var dict = new Dictionary<string, PropertyInfo>(expectedMax);
var localProps = selector(type);
foreach (var p in localProps)
dict.TryAdd(p.Name, p);
return dict;
}
private static string[][] GetPropArray<T>(Dictionary<string, T>.AlternateLookup<ReadOnlySpan<char>>[] types, ReadOnlySpan<string> extra)
{
var result = new string[types.Length + 2][];
var p = result.AsSpan(1, types.Length);
for (int i = 0; i < p.Length; i++)
{
var type = types[i].Dictionary;
string[] combine = [..type.Keys, ..extra];
combine.Sort();
p[i] = combine;
}
var first = p[0];
var any = new HashSet<string>(first);
var all = new HashSet<string>(first);
foreach (var set in p[1..])
{
any.UnionWith(set);
all.IntersectWith(set);
}
var arrAny = any.ToArray();
arrAny.Sort();
result[0] = arrAny;
var arrAll = all.ToArray();
arrAll.Sort();
result[^1] = arrAll;
return result;
}
private Dictionary<string, PropertyInfo>.AlternateLookup<ReadOnlySpan<char>> GetProps(TObject entity)
{
var type = entity.GetType();
var typeIndex = _types.IndexOf(type);
return _props[typeIndex];
}
private bool IsFilterMatch(StringInstruction cmd, TMeta info, TObject entity, Dictionary<string, PropertyInfo>.AlternateLookup<ReadOnlySpan<char>> localProps)
{
if (TryHandleFilter(cmd, info, entity, out var isMatch))
return isMatch;
return IsPropertyFiltered(cmd, entity, localProps);
}
private static bool IsPropertyFiltered(StringInstruction cmd, TObject entity, Dictionary<string, PropertyInfo>.AlternateLookup<ReadOnlySpan<char>> localProps)
{
if (!localProps.TryGetValue(cmd.PropertyName, out var pi))
return false;
if (!pi.CanRead)
return false;
var val = cmd.PropertyValue;
if (val.StartsWith(PointerToken) && localProps.TryGetValue(val.AsSpan(1), out var opi))
{
var result = opi.GetValue(entity) ?? throw new NullReferenceException();
return cmd.Comparer.IsCompareOperator(pi.CompareTo(entity, result));
}
return cmd.Comparer.IsCompareOperator(pi.CompareTo(entity, val));
}
private ModifyResult SetProperty(StringInstruction cmd, TObject entity, TMeta info, Dictionary<string, PropertyInfo>.AlternateLookup<ReadOnlySpan<char>> localProps)
{
if (cmd.Operation == InstructionOperation.Set && TryHandleSetOperation(cmd, info, entity, out var result))
return result;
if (!localProps.TryGetValue(cmd.PropertyName, out var pi))
return ModifyResult.Error;
if (!pi.CanWrite)
return ModifyResult.Error;
if (cmd.Operation != InstructionOperation.Set)
return ApplyNumericOperation(entity, cmd, pi, localProps);
if (!TryResolveOperandValue(cmd, entity, localProps, out var value))
return ModifyResult.Error;
ReflectUtil.SetValue(pi, entity, value);
return ModifyResult.Modified;
}
private static ModifyResult ApplyNumericOperation(TObject entity, StringInstruction cmd, PropertyInfo pi, Dictionary<string, PropertyInfo>.AlternateLookup<ReadOnlySpan<char>> localProps)
{
if (!pi.CanRead)
return ModifyResult.Error;
if (!TryGetNumericType(pi.PropertyType, out var numericType))
return ModifyResult.Error;
var currentValue = pi.GetValue(entity);
if (currentValue is null)
return ModifyResult.Error;
if (!TryResolveOperandValue(cmd, entity, localProps, out var operandValue))
return ModifyResult.Error;
if (!TryApplyNumericOperation(numericType, cmd.Operation, currentValue, operandValue, out var value))
return ModifyResult.Error;
ReflectUtil.SetValue(pi, entity, value);
return ModifyResult.Modified;
}
private static bool TryResolveOperandValue(StringInstruction cmd, TObject entity, Dictionary<string, PropertyInfo>.AlternateLookup<ReadOnlySpan<char>> localProps, [NotNullWhen(true)] out object? value)
{
if (cmd.Random)
{
value = cmd.RandomValue;
return true;
}
var propertyValue = cmd.PropertyValue;
if (propertyValue.StartsWith(PointerToken) && localProps.TryGetValue(propertyValue.AsSpan(1), out var opi))
{
value = opi.GetValue(entity);
return value is not null;
}
value = propertyValue;
return true;
}
private static bool TryGetNumericType(Type type, out Type numericType)
{
numericType = Nullable.GetUnderlyingType(type) ?? type;
// bool isNullable = type != numericType;
return numericType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(INumber<>));
}
private static bool TryApplyNumericOperation(Type numericType, InstructionOperation operation, object currentValue, object operandValue, [NotNullWhen(true)] out object? result)
{
result = null;
if (numericType == typeof(byte))
return ApplyBinaryInteger<byte>(currentValue, operandValue, operation, out result);
if (numericType == typeof(sbyte))
return ApplyBinaryInteger<sbyte>(currentValue, operandValue, operation, out result);
if (numericType == typeof(short))
return ApplyBinaryInteger<short>(currentValue, operandValue, operation, out result);
if (numericType == typeof(ushort))
return ApplyBinaryInteger<ushort>(currentValue, operandValue, operation, out result);
if (numericType == typeof(int))
return ApplyBinaryInteger<int>(currentValue, operandValue, operation, out result);
if (numericType == typeof(uint))
return ApplyBinaryInteger<uint>(currentValue, operandValue, operation, out result);
if (numericType == typeof(long))
return ApplyBinaryInteger<long>(currentValue, operandValue, operation, out result);
if (numericType == typeof(ulong))
return ApplyBinaryInteger<ulong>(currentValue, operandValue, operation, out result);
if (numericType == typeof(nint))
return ApplyBinaryInteger<nint>(currentValue, operandValue, operation, out result);
if (numericType == typeof(nuint))
return ApplyBinaryInteger<nuint>(currentValue, operandValue, operation, out result);
if (numericType == typeof(BigInteger))
return ApplyBinaryInteger<BigInteger>(currentValue, operandValue, operation, out result);
if (numericType == typeof(float))
return ApplyNumeric<float>(currentValue, operandValue, operation, out result);
if (numericType == typeof(double))
return ApplyNumeric<double>(currentValue, operandValue, operation, out result);
if (numericType == typeof(decimal))
return ApplyNumeric<decimal>(currentValue, operandValue, operation, out result);
return false;
}
private static bool ApplyNumeric<T>(object currentValue, object operandValue, InstructionOperation operation, [NotNullWhen(true)] out object? result)
where T : INumber<T>
{
if (operation.IsBitwise)
{
result = null;
return false;
}
var success = TryApplyNumericOperationCore<T>(operation, currentValue, operandValue, out var typed);
result = typed;
return success;
}
private static bool ApplyBinaryInteger<T>(object currentValue, object operandValue, InstructionOperation operation, [NotNullWhen(true)] out object? result)
where T : IBinaryInteger<T>
{
var success = operation.IsBitwise
? TryApplyBinaryIntegerOperationCore<T>(operation, currentValue, operandValue, out var typed)
: TryApplyNumericOperationCore(operation, currentValue, operandValue, out typed);
result = typed;
return success;
}
private static bool TryApplyNumericOperationCore<T>(InstructionOperation operation, object currentValue, object operandValue, [NotNullWhen(true)] out T? result)
where T : INumber<T>
{
if (!TryConvertNumeric<T>(currentValue, out var left) || !TryConvertNumeric<T>(operandValue, out var right))
{
result = default;
return false;
}
return TryApplyNumericOperationCore(operation, left, right, out result);
}
private static bool TryApplyNumericOperationCore<T>(InstructionOperation operation, T left, T right, [NotNullWhen(true)] out T? result)
where T : INumber<T>
{
try
{
result = operation switch
{
InstructionOperation.Add => left + right,
InstructionOperation.Subtract => left - right,
InstructionOperation.Multiply => left * right,
InstructionOperation.Divide => left / right,
InstructionOperation.Modulo => left % right,
_ => right,
};
return true;
}
catch (DivideByZeroException)
{
result = default;
return false;
}
}
private static bool TryApplyBinaryIntegerOperationCore<T>(InstructionOperation operation, object currentValue, object operandValue, [NotNullWhen(true)] out T? result)
where T : IBinaryInteger<T>
{
if (!TryConvertNumeric<T>(currentValue, out var left) || !TryConvertNumeric<T>(operandValue, out var right))
{
result = default;
return false;
}
return TryApplyBinaryIntegerOperationCore(operation, left, right, out result);
}
private static bool TryApplyBinaryIntegerOperationCore<T>(InstructionOperation operation, T left, T right, [NotNullWhen(true)] out T? result)
where T : IBinaryInteger<T>
{
try
{
switch (operation)
{
case InstructionOperation.BitwiseAnd:
result = left & right;
return true;
case InstructionOperation.BitwiseOr:
result = left | right;
return true;
case InstructionOperation.BitwiseXor:
result = left ^ right;
return true;
case InstructionOperation.BitwiseShiftLeft:
result = left << int.CreateChecked(right);
return true;
case InstructionOperation.BitwiseShiftRight:
result = left >> int.CreateChecked(right);
return true;
default:
result = default;
return false;
}
}
catch (OverflowException)
{
result = default;
return false;
}
}
private static bool TryConvertNumeric<T>(object value, [NotNullWhen(true)] out T? result) where T : INumber<T>
{
if (value is T typed)
{
result = typed;
return true;
}
if (value is string text)
{
if (T.TryParse(text, CultureInfo.InvariantCulture, out var parsed))
{
result = parsed;
return true;
}
result = default;
return false;
}
if (value is IConvertible)
{
try
{
var converted = Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture);
if (converted is T convertedValue)
{
result = convertedValue;
return true;
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
result = default;
return false;
}
}

View File

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace PKHeX.Core;
public static class BatchEditingUtil
{
public const string PROP_TYPENAME = "ObjectType";
public const char PointerToken = '*';
/// <summary>
/// Checks if the object is filtered by the provided <see cref="filters"/>.
/// </summary>
/// <remarks>
/// Does not use cached reflection; less performant than a cached <see cref="BatchEditingBase{TObject,TMeta}"/> implementation.
/// </remarks>
/// <param name="filters">Filters which must be satisfied.</param>
/// <param name="obj">Object to check.</param>
/// <returns>True if <see cref="obj"/> matches all filters.</returns>
public static bool IsFilterMatch<T>(IEnumerable<StringInstruction> filters, T obj) where T : notnull
{
foreach (var cmd in filters)
{
var name = cmd.PropertyName;
var value = cmd.PropertyValue;
if (name is PROP_TYPENAME)
{
var type = obj.GetType();
var typeName = type.Name;
if (!cmd.Comparer.IsCompareEquivalence(value == typeName))
return false;
continue;
}
if (!ReflectUtil.HasProperty(obj, name, out var pi))
return false;
try
{
if (cmd.Comparer.IsCompareOperator(pi.CompareTo(obj, value)))
continue;
}
// User provided inputs can mismatch the type's required value format, and fail to be compared.
catch (Exception e)
{
Debug.WriteLine($"Unable to compare {name} to {value}.");
Debug.WriteLine(e.Message);
}
return false;
}
return true;
}
}

View File

@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
namespace PKHeX.Core;
/// <summary>
/// Provides batch editing helpers for an entity type.
/// </summary>
public interface IBatchEditor<TObject> where TObject : notnull
{
/// <summary>
/// Gets the list of supported entity types.
/// </summary>
IReadOnlyList<Type> Types { get; }
/// <summary>
/// Gets the property names, indexed by <see cref="Types"/>.
/// </summary>
string[][] Properties { get; }
/// <summary>
/// Tries to fetch the entity property from the cache of available properties.
/// </summary>
bool TryGetHasProperty(TObject entity, ReadOnlySpan<char> name, [NotNullWhen(true)] out PropertyInfo? pi);
/// <summary>
/// Tries to fetch the entity property from the cache of available properties.
/// </summary>
bool TryGetHasProperty(Type type, ReadOnlySpan<char> name, [NotNullWhen(true)] out PropertyInfo? pi);
/// <summary>
/// Gets a list of entity types that implement the requested property.
/// </summary>
IEnumerable<string> GetTypesImplementing(string property);
/// <summary>
/// Gets the type of the entity property using the saved cache of properties.
/// </summary>
bool TryGetPropertyType(string propertyName, [NotNullWhen(true)] out string? result, int typeIndex = 0);
/// <summary>
/// Checks if the entity is filtered by the provided filters.
/// </summary>
bool IsFilterMatch(IEnumerable<StringInstruction> filters, TObject entity);
/// <summary>
/// Tries to modify the entity.
/// </summary>
bool TryModifyIsSuccess(TObject entity, IEnumerable<StringInstruction> filters, IEnumerable<StringInstruction> modifications, Func<TObject, bool>? modifier = null);
/// <summary>
/// Tries to modify the entity using instructions and a custom modifier delegate.
/// </summary>
ModifyResult TryModify(TObject entity, IEnumerable<StringInstruction> filters, IEnumerable<StringInstruction> modifications, Func<TObject, bool>? modifier = null);
}

View File

@ -0,0 +1,71 @@
using System;
using static PKHeX.Core.InstructionComparer;
namespace PKHeX.Core;
/// <summary>
/// Value comparison type
/// </summary>
public enum InstructionComparer : byte
{
None,
IsEqual,
IsNotEqual,
IsGreaterThan,
IsGreaterThanOrEqual,
IsLessThan,
IsLessThanOrEqual,
}
/// <summary>
/// Extension methods for <see cref="InstructionComparer"/>
/// </summary>
public static class InstructionComparerExtensions
{
extension(InstructionComparer comparer)
{
/// <summary>
/// Indicates if the <see cref="comparer"/> is supported by the logic.
/// </summary>
/// <returns>True if supported, false if unsupported.</returns>
public bool IsSupported => comparer switch
{
IsEqual => true,
IsNotEqual => true,
IsGreaterThan => true,
IsGreaterThanOrEqual => true,
IsLessThan => true,
IsLessThanOrEqual => true,
_ => false,
};
/// <summary>
/// Checks if the compare operator is satisfied by a boolean comparison result.
/// </summary>
/// <param name="compareResult">Result from Equals comparison</param>
/// <returns>True if satisfied</returns>
/// <remarks>Only use this method if the comparison is boolean only. Use the <see cref="IsCompareOperator"/> otherwise.</remarks>
public bool IsCompareEquivalence(bool compareResult) => comparer switch
{
IsEqual => compareResult,
IsNotEqual => !compareResult,
_ => false,
};
/// <summary>
/// Checks if the compare operator is satisfied by the <see cref="IComparable{T}.CompareTo"/> result.
/// </summary>
/// <param name="compareResult">Result from CompareTo</param>
/// <returns>True if satisfied</returns>
public bool IsCompareOperator(int compareResult) => comparer switch
{
IsEqual => compareResult is 0,
IsNotEqual => compareResult is not 0,
IsGreaterThan => compareResult > 0,
IsGreaterThanOrEqual => compareResult >= 0,
IsLessThan => compareResult < 0,
IsLessThanOrEqual => compareResult <= 0,
_ => false,
};
}
}

View File

@ -0,0 +1,37 @@
using static PKHeX.Core.InstructionOperation;
namespace PKHeX.Core;
/// <summary>
/// Operation type for applying a modification.
/// </summary>
public enum InstructionOperation : byte
{
Set,
Add,
Subtract,
Multiply,
Divide,
Modulo,
BitwiseAnd,
BitwiseOr,
BitwiseXor,
BitwiseShiftRight,
BitwiseShiftLeft,
}
public static class InstructionOperationExtensions
{
extension(InstructionOperation operation)
{
public bool IsBitwise => operation switch
{
BitwiseAnd => true,
BitwiseOr => true,
BitwiseXor => true,
BitwiseShiftRight => true,
BitwiseShiftLeft => true,
_ => false,
};
}
}

View File

@ -0,0 +1,30 @@
using System;
namespace PKHeX.Core;
/// <summary>
/// Batch Editor Modification result for an individual processing operation.
/// </summary>
[Flags]
public enum ModifyResult
{
/// <summary>
/// No modifications were performed as a filter excluded it.
/// </summary>
Filtered,
/// <summary>
/// Not a suitable candidate for modification.
/// </summary>
Skipped,
/// <summary>
/// One or more modifications was successfully applied.
/// </summary>
Modified,
/// <summary>
/// An error was occurred while attempting modifications.
/// </summary>
Error = 0x80,
}

View File

@ -0,0 +1,380 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using static PKHeX.Core.InstructionComparer;
namespace PKHeX.Core;
/// <summary>
/// Batch Editing instruction
/// </summary>
/// <remarks>
/// Can be a filter (skip), or a modification instruction (modify)
/// </remarks>
/// <see cref="FilterNotEqual"/>
/// <see cref="FilterEqual"/>
/// <see cref="Apply"/>
/// <param name="PropertyName">Property to modify.</param>
/// <param name="PropertyValue">Value to set to the property.</param>
/// <param name="Comparer">Filter Comparison Type</param>
public sealed record StringInstruction(string PropertyName, string PropertyValue, InstructionComparer Comparer, InstructionOperation Operation = InstructionOperation.Set)
{
public string PropertyValue { get; private set; } = PropertyValue;
/// <summary>
/// Sets the <see cref="PropertyValue"/> to the index of the value in the input <see cref="arr"/>, if it exists.
/// </summary>
/// <param name="arr">List of values to search for the <see cref="PropertyValue"/>.</param>
/// <returns>True if the value was found and set, false otherwise.</returns>
public bool SetScreenedValue(ReadOnlySpan<string> arr)
{
int index = arr.IndexOf(PropertyValue);
if ((uint)index >= arr.Length)
return false;
PropertyValue = index.ToString();
return true;
}
/// <summary>
/// Valid prefixes that are recognized for <see cref="InstructionComparer"/> value comparison types.
/// </summary>
public static ReadOnlySpan<char> Prefixes =>
[
Apply,
FilterEqual, FilterNotEqual, FilterGreaterThan, FilterGreaterThanOrEqual, FilterLessThan, FilterLessThanOrEqual,
ApplyAdd, ApplySubtract, ApplyMultiply, ApplyDivide, ApplyModulo,
ApplyBitwiseAnd, ApplyBitwiseOr, ApplyBitwiseXor, ApplyBitwiseShiftRight, ApplyBitwiseShiftLeft,
];
public static bool IsFilterInstruction(char c) => c switch
{
FilterEqual => true,
FilterNotEqual => true,
FilterGreaterThan => true,
FilterLessThan => true,
FilterGreaterThanOrEqual => true,
FilterLessThanOrEqual => true,
_ => false,
};
public static bool IsMutationInstruction(char c) => !IsFilterInstruction(c);
private const char Apply = '.';
private const char ApplyAdd = '+';
private const char ApplySubtract = '-';
private const char ApplyMultiply = '*';
private const char ApplyDivide = '/';
private const char ApplyModulo = '%';
private const char ApplyBitwiseAnd = '&';
private const char ApplyBitwiseOr = '|';
private const char ApplyBitwiseXor = '^';
private const char ApplyBitwiseShiftRight = '»';
private const char ApplyBitwiseShiftLeft = '«';
private const char SplitRange = ',';
private const char FilterEqual = '=';
private const char FilterNotEqual = '!';
private const char FilterGreaterThan = '>';
private const char FilterLessThan = '<';
private const char FilterGreaterThanOrEqual = '≥';
private const char FilterLessThanOrEqual = '≤';
/// <summary>
/// Character which divides a property and a value.
/// </summary>
/// <remarks>
/// Example:
/// =Species=1
/// The second = is the split.
/// </remarks>
public const char SplitInstruction = '=';
// Extra Functionality
private int RandomMinimum, RandomMaximum;
/// <summary>
/// Apply a <see cref="RandomValue"/> instead of fixed value, based on the <see cref="RandomMinimum"/> and <see cref="RandomMaximum"/> values.
/// </summary>
public bool Random { get; private set; }
/// <summary>
/// Gets a <see cref="Random"/> value, based on the <see cref="RandomMinimum"/> and <see cref="RandomMaximum"/> values.
/// </summary>
public int RandomValue => Util.Rand.Next(RandomMinimum, RandomMaximum + 1);
/// <summary>
/// Checks if the input <see cref="str"/> is a valid "random range" specification.
/// </summary>
public static bool IsRandomRange(ReadOnlySpan<char> str)
{
// Need at least one character on either side of the splitter char.
int index = str.IndexOf(SplitRange);
return index > 0 && index < str.Length - 1;
}
/// <summary>
/// Sets a "random range" specification to the instruction.
/// </summary>
/// <exception cref="ArgumentException">When the splitter is not present.</exception>
public void SetRandomRange(ReadOnlySpan<char> str)
{
var index = str.IndexOf(SplitRange);
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(index);
var min = str[..index];
var max = str[(index + 1)..];
_ = int.TryParse(min, out RandomMinimum);
_ = int.TryParse(max, out RandomMaximum);
if (RandomMinimum == RandomMaximum)
{
PropertyValue = RandomMinimum.ToString();
Debug.WriteLine($"{PropertyName} randomization range Min/Max same?");
}
else
{
Random = true;
}
}
/// <summary>
/// Gets a list of <see cref="StringInstruction"/>s from the input <see cref="text"/>.
/// </summary>
public static List<StringInstruction> GetFilters(ReadOnlySpan<char> text) => GetFilters(text.EnumerateLines());
/// <summary>
/// Gets a list of <see cref="StringInstruction"/> filters from the input <see cref="lines"/>.
/// </summary>
public static List<StringInstruction> GetFilters(ReadOnlySpan<string> lines)
{
var result = new List<StringInstruction>(lines.Length);
foreach (var line in lines)
{
if (TryParseFilter(line, out var entry))
result.Add(entry);
}
return result;
}
/// <summary>
/// Gets a list of <see cref="StringInstruction"/> filters from the input <see cref="lines"/>.
/// </summary>
public static List<StringInstruction> GetFilters(SpanLineEnumerator lines)
{
var result = new List<StringInstruction>();
foreach (var line in lines)
{
if (TryParseFilter(line, out var entry))
result.Add(entry);
}
return result;
}
/// <summary>
/// Gets a list of <see cref="StringInstruction"/> filters from the input <see cref="lines"/>.
/// </summary>
public static List<StringInstruction> GetFilters(IReadOnlyList<string> lines)
{
var result = new List<StringInstruction>(lines.Count);
foreach (var line in lines)
{
if (TryParseFilter(line, out var entry))
result.Add(entry);
}
return result;
}
/// <summary>
/// Gets a list of <see cref="StringInstruction"/> filters from the input <see cref="lines"/>.
/// </summary>
public static List<StringInstruction> GetFilters(IEnumerable<string> lines)
{
var result = new List<StringInstruction>();
foreach (var line in lines)
{
if (TryParseFilter(line, out var entry))
result.Add(entry);
}
return result;
}
/// <summary>
/// Gets a list of <see cref="StringInstruction"/> instructions from the input <see cref="text"/>.
/// </summary>
public static List<StringInstruction> GetInstructions(ReadOnlySpan<char> text) => GetInstructions(text.EnumerateLines());
/// <summary>
/// Gets a list of <see cref="StringInstruction"/> instructions from the input <see cref="lines"/>.
/// </summary>
public static List<StringInstruction> GetInstructions(ReadOnlySpan<string> lines)
{
var result = new List<StringInstruction>(lines.Length);
foreach (var line in lines)
{
if (TryParseInstruction(line, out var entry))
result.Add(entry);
}
return result;
}
/// <summary>
/// Gets a list of <see cref="StringInstruction"/> instructions from the input <see cref="lines"/>.
/// </summary>
public static List<StringInstruction> GetInstructions(SpanLineEnumerator lines)
{
var result = new List<StringInstruction>();
foreach (var line in lines)
{
if (TryParseInstruction(line, out var entry))
result.Add(entry);
}
return result;
}
/// <summary>
/// Gets a list of <see cref="StringInstruction"/> instructions from the input <see cref="lines"/>.
/// </summary>
public static List<StringInstruction> GetInstructions(IReadOnlyList<string> lines)
{
var result = new List<StringInstruction>(lines.Count);
foreach (var line in lines)
{
if (TryParseInstruction(line, out var entry))
result.Add(entry);
}
return result;
}
/// <summary>
/// Gets a list of <see cref="StringInstruction"/> instructions from the input <see cref="lines"/>.
/// </summary>
public static List<StringInstruction> GetInstructions(IEnumerable<string> lines)
{
var result = new List<StringInstruction>();
foreach (var line in lines)
{
if (TryParseInstruction(line, out var entry))
result.Add(entry);
}
return result;
}
/// <summary>
/// Tries to parse a <see cref="StringInstruction"/> filter from the input <see cref="line"/>.
/// </summary>
public static bool TryParseFilter(ReadOnlySpan<char> line, [NotNullWhen(true)] out StringInstruction? entry)
{
entry = null;
if (line.Length is 0)
return false;
var comparer = GetComparer(line[0]);
if (!comparer.IsSupported)
return false;
return TryParseSplitTuple(line[1..], ref entry, comparer);
}
/// <summary>
/// Tries to parse a <see cref="StringInstruction"/> instruction from the input <see cref="line"/>.
/// </summary>
public static bool TryParseInstruction(ReadOnlySpan<char> line, [NotNullWhen(true)] out StringInstruction? entry)
{
entry = null;
if (line.Length is 0 || !TryGetOperation(line[0], out var operation))
return false;
return TryParseSplitTuple(line[1..], ref entry, default, operation);
}
/// <summary>
/// Tries to split a <see cref="StringInstruction"/> tuple from the input <see cref="tuple"/>.
/// </summary>
public static bool TryParseSplitTuple(ReadOnlySpan<char> tuple, [NotNullWhen(true)] ref StringInstruction? entry, InstructionComparer eval = default, InstructionOperation operation = InstructionOperation.Set)
{
if (!TryParseSplitTuple(tuple, out var name, out var value))
return false;
entry = new StringInstruction(name.ToString(), value.ToString(), eval, operation);
return true;
}
/// <summary>
/// Tries to split a <see cref="StringInstruction"/> tuple from the input <see cref="tuple"/>.
/// </summary>
public static bool TryParseSplitTuple(ReadOnlySpan<char> tuple, out ReadOnlySpan<char> name, out ReadOnlySpan<char> value)
{
name = default;
value = default;
var splitIndex = tuple.IndexOf(SplitInstruction);
if (splitIndex <= 0)
return false;
name = tuple[..splitIndex];
if (name.IsWhiteSpace())
return false;
value = tuple[(splitIndex + 1)..];
var noExtra = value.IndexOf(SplitInstruction);
return noExtra == -1;
}
/// <summary>
/// Gets the <see cref="InstructionComparer"/> from the input <see cref="opCode"/>.
/// </summary>
public static InstructionComparer GetComparer(char opCode) => opCode switch
{
FilterEqual => IsEqual,
FilterNotEqual => IsNotEqual,
FilterGreaterThan => IsGreaterThan,
FilterLessThan => IsLessThan,
FilterGreaterThanOrEqual => IsGreaterThanOrEqual,
FilterLessThanOrEqual => IsLessThanOrEqual,
_ => None,
};
/// <summary>
/// Gets the <see cref="InstructionOperation"/> from the input <see cref="opCode"/>.
/// </summary>
public static bool TryGetOperation(char opCode, out InstructionOperation operation)
{
switch (opCode)
{
case Apply:
operation = InstructionOperation.Set;
return true;
case ApplyAdd:
operation = InstructionOperation.Add;
return true;
case ApplySubtract:
operation = InstructionOperation.Subtract;
return true;
case ApplyMultiply:
operation = InstructionOperation.Multiply;
return true;
case ApplyDivide:
operation = InstructionOperation.Divide;
return true;
case ApplyModulo:
operation = InstructionOperation.Modulo;
return true;
case ApplyBitwiseAnd:
operation = InstructionOperation.BitwiseAnd;
return true;
case ApplyBitwiseOr:
operation = InstructionOperation.BitwiseOr;
return true;
case ApplyBitwiseXor:
operation = InstructionOperation.BitwiseXor;
return true;
case ApplyBitwiseShiftRight:
operation = InstructionOperation.BitwiseShiftRight;
return true;
case ApplyBitwiseShiftLeft:
operation = InstructionOperation.BitwiseShiftLeft;
return true;
default:
operation = default;
return false;
}
}
}

View File

@ -0,0 +1,161 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace PKHeX.Core;
/// <summary>
/// Processes input of strings into a list of valid Filters and Instructions.
/// </summary>
public sealed class StringInstructionSet
{
/// <summary>
/// Filters to check if the object should be modified.
/// </summary>
public readonly IReadOnlyList<StringInstruction> Filters;
/// <summary>
/// Instructions to modify the object.
/// </summary>
public readonly IReadOnlyList<StringInstruction> Instructions;
private const char SetSeparatorChar = ';';
public StringInstructionSet(IReadOnlyList<StringInstruction> filters, IReadOnlyList<StringInstruction> instructions)
{
Filters = filters;
Instructions = instructions;
}
public StringInstructionSet(ReadOnlySpan<char> text)
{
var set = text.EnumerateLines();
Filters = StringInstruction.GetFilters(set);
Instructions = StringInstruction.GetInstructions(set);
}
public StringInstructionSet(SpanLineEnumerator set)
{
Filters = StringInstruction.GetFilters(set);
Instructions = StringInstruction.GetInstructions(set);
}
public StringInstructionSet(ReadOnlySpan<string> set)
{
Filters = StringInstruction.GetFilters(set);
Instructions = StringInstruction.GetInstructions(set);
}
/// <summary>
/// Checks if the input <see cref="text"/> is potentially formatted incorrectly.
/// </summary>
/// <remarks>Normally, no blank lines should be present in the input.</remarks>
/// <returns>True if a blank line is found in the input.</returns>
public static bool HasEmptyLine(ReadOnlySpan<char> text) => HasEmptyLine(text.EnumerateLines());
/// <inheritdoc cref="HasEmptyLine(ReadOnlySpan{char})"/>
public static bool HasEmptyLine(SpanLineEnumerator lines)
{
foreach (var line in lines)
{
if (line.IsEmpty || line.IsWhiteSpace())
return true;
}
return false;
}
/// <summary>
/// Gets a list of <see cref="StringInstructionSet"/>s from the input <see cref="lines"/>.
/// </summary>
public static StringInstructionSet[] GetBatchSets(ReadOnlySpan<string> lines)
{
int ctr = 0;
int start = 0;
while (start < lines.Length)
{
var slice = lines[start..];
var count = GetInstructionSetLength(slice);
ctr++;
start += count + 1;
}
var result = new StringInstructionSet[ctr];
ctr = 0;
start = 0;
while (start < lines.Length)
{
var slice = lines[start..];
var count = GetInstructionSetLength(slice);
var set = slice[..count];
result[ctr++] = new StringInstructionSet(set);
start += count + 1;
}
return result;
}
/// <summary>
/// Gets a list of <see cref="StringInstructionSet"/>s from the input <see cref="text"/>.
/// </summary>
public static StringInstructionSet[] GetBatchSets(ReadOnlySpan<char> text)
{
int ctr = 0;
int start = 0;
while (start < text.Length)
{
var slice = text[start..];
var count = GetInstructionSetLength(slice);
ctr++;
start += count + 1;
}
var result = new StringInstructionSet[ctr];
ctr = 0;
start = 0;
while (start < text.Length)
{
var slice = text[start..];
var count = GetInstructionSetLength(slice);
var set = slice[..count];
result[ctr++] = new StringInstructionSet(set);
start += count + 1;
}
return result;
}
/// <summary>
/// Scans through the <see cref="text"/> to count the amount of characters to consume.
/// </summary>
/// <param name="text">Multi line string</param>
/// <returns>Amount of characters comprising a set of instructions</returns>
public static int GetInstructionSetLength(ReadOnlySpan<char> text)
{
int start = 0;
while (start < text.Length)
{
var line = text[start..];
if (line.Length != 0 && line[0] == SetSeparatorChar)
return start;
var next = line.IndexOf('\n');
if (next == -1)
return text.Length;
start += next + 1;
}
return start;
}
/// <summary>
/// Scans through the <see cref="lines"/> to count the amount of valid lines to consume.
/// </summary>
/// <returns>Amount of lines comprising a set of instructions.</returns>
public static int GetInstructionSetLength(ReadOnlySpan<string> lines)
{
int start = 0;
while (start < lines.Length)
{
var line = lines[start++];
if (line.StartsWith(SetSeparatorChar))
return start;
}
return start;
}
}

View File

@ -1,506 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using static PKHeX.Core.MessageStrings;
using static PKHeX.Core.BatchModifications;
namespace PKHeX.Core;
/// <summary>
/// Logic for editing many <see cref="PKM"/> with user provided <see cref="StringInstruction"/> list.
/// </summary>
public static class BatchEditing
{
public static readonly Type[] Types =
{
typeof (PK9),
typeof (PK8), typeof (PA8), typeof (PB8),
typeof (PB7),
typeof (PK7), typeof (PK6), typeof (PK5), typeof (PK4), typeof(BK4), typeof(RK4),
typeof (PK3), typeof (XK3), typeof (CK3),
typeof (PK2), typeof (SK2), typeof (PK1),
};
/// <summary>
/// Extra properties to show in the list of selectable properties (GUI)
/// </summary>
public static readonly List<string> CustomProperties = new()
{
PROP_LEGAL, PROP_TYPENAME, PROP_RIBBONS, PROP_CONTESTSTATS, PROP_MOVEMASTERY,
IdentifierContains, nameof(ISlotInfo.Slot), nameof(SlotInfoBox.Box),
};
/// <summary>
/// Property names, indexed by <see cref="Types"/>.
/// </summary>
public static string[][] Properties => GetProperties.Value;
private static readonly Lazy<string[][]> GetProperties = new(() => GetPropArray(Types, CustomProperties));
private static readonly Dictionary<string, PropertyInfo>[] Props = GetPropertyDictionaries(Types);
private static Dictionary<string, PropertyInfo>[] GetPropertyDictionaries(IReadOnlyList<Type> types)
{
var result = new Dictionary<string, PropertyInfo>[types.Count];
for (int i = 0; i < types.Count; i++)
result[i] = GetPropertyDictionary(types[i], ReflectUtil.GetAllPropertyInfoPublic);
return result;
}
private static Dictionary<string, PropertyInfo> GetPropertyDictionary(Type type, Func<Type, IEnumerable<PropertyInfo>> selector)
{
const int expectedMax = 0x200; // currently 0x160 as of 2022
var dict = new Dictionary<string, PropertyInfo>(expectedMax);
var props = selector(type);
foreach (var p in props)
{
if (!dict.ContainsKey(p.Name))
dict.Add(p.Name, p);
}
return dict;
}
internal const string CONST_RAND = "$rand";
internal const string CONST_SHINY = "$shiny";
internal const string CONST_SUGGEST = "$suggest";
private const string CONST_BYTES = "$[]";
private const char CONST_POINTER = '*';
internal const char CONST_SPECIAL = '$';
internal const string PROP_LEGAL = "Legal";
internal const string PROP_TYPENAME = "ObjectType";
internal const string PROP_RIBBONS = "Ribbons";
internal const string PROP_EVS = "EVs";
internal const string PROP_CONTESTSTATS = "ContestStats";
internal const string PROP_MOVEMASTERY = "MoveMastery";
internal const string IdentifierContains = nameof(IdentifierContains);
private static string[][] GetPropArray(IReadOnlyList<Type> types, IReadOnlyList<string> extra)
{
var result = new string[types.Count + 2][];
var p = result.AsSpan(1, types.Count);
for (int i = 0; i < p.Length; i++)
{
var props = ReflectUtil.GetPropertiesPublic(types[i]);
p[i] = props.Concat(extra).OrderBy(a => a).ToArray();
}
// Properties for any PKM
// Properties shared by all PKM
var first = p[0];
var any = new HashSet<string>(first);
var all = new HashSet<string>(first);
for (int i = 1; i < p.Length; i++)
{
any.UnionWith(p[i]);
all.IntersectWith(p[i]);
}
result[0] = any.OrderBy(z => z).ToArray();
result[^1] = all.OrderBy(z => z).ToArray();
return result;
}
/// <summary>
/// Tries to fetch the <see cref="PKM"/> property from the cache of available properties.
/// </summary>
/// <param name="pk">Pokémon to check</param>
/// <param name="name">Property Name to check</param>
/// <param name="pi">Property Info retrieved (if any).</param>
/// <returns>True if has property, false if does not.</returns>
public static bool TryGetHasProperty(PKM pk, string name, [NotNullWhen(true)] out PropertyInfo? pi)
{
var type = pk.GetType();
return TryGetHasProperty(type, name, out pi);
}
/// <summary>
/// Tries to fetch the <see cref="PKM"/> property from the cache of available properties.
/// </summary>
/// <param name="type">Type to check</param>
/// <param name="name">Property Name to check</param>
/// <param name="pi">Property Info retrieved (if any).</param>
/// <returns>True if has property, false if does not.</returns>
public static bool TryGetHasProperty(Type type, string name, [NotNullWhen(true)] out PropertyInfo? pi)
{
var index = Array.IndexOf(Types, type);
if (index < 0)
{
pi = null;
return false;
}
var props = Props[index];
return props.TryGetValue(name, out pi);
}
/// <summary>
/// Gets a list of <see cref="PKM"/> types that implement the requested <see cref="property"/>.
/// </summary>
public static IEnumerable<string> GetTypesImplementing(string property)
{
for (int i = 0; i < Types.Length; i++)
{
var type = Types[i];
var props = Props[i];
if (!props.TryGetValue(property, out var pi))
continue;
yield return $"{type.Name}: {pi.PropertyType.Name}";
}
}
/// <summary>
/// Gets the type of the <see cref="PKM"/> property using the saved cache of properties.
/// </summary>
/// <param name="propertyName">Property Name to fetch the type for</param>
/// <param name="typeIndex">Type index (within <see cref="Types"/>. Leave empty (0) for a nonspecific format.</param>
/// <returns>Short name of the property's type.</returns>
public static string? GetPropertyType(string propertyName, int typeIndex = 0)
{
if (CustomProperties.Contains(propertyName))
return "Custom";
if (typeIndex == 0) // Any
{
foreach (var p in Props)
{
if (p.TryGetValue(propertyName, out var pi))
return pi.PropertyType.Name;
}
return null;
}
int index = typeIndex - 1 >= Props.Length ? 0 : typeIndex - 1; // All vs Specific
var pr = Props[index];
if (!pr.TryGetValue(propertyName, out var info))
return null;
return info.PropertyType.Name;
}
/// <summary>
/// Initializes the <see cref="StringInstruction"/> list with a context-sensitive value. If the provided value is a string, it will attempt to convert that string to its corresponding index.
/// </summary>
/// <param name="il">Instructions to initialize.</param>
public static void ScreenStrings(IEnumerable<StringInstruction> il)
{
foreach (var i in il.Where(i => !i.PropertyValue.All(char.IsDigit)))
{
string pv = i.PropertyValue;
if (pv.StartsWith(CONST_SPECIAL) && !pv.StartsWith(CONST_BYTES, StringComparison.Ordinal) && pv.Contains(','))
i.SetRandRange(pv);
SetInstructionScreenedValue(i);
}
}
/// <summary>
/// Initializes the <see cref="StringInstruction"/> with a context-sensitive value. If the provided value is a string, it will attempt to convert that string to its corresponding index.
/// </summary>
/// <param name="i">Instruction to initialize.</param>
private static void SetInstructionScreenedValue(StringInstruction i)
{
switch (i.PropertyName)
{
case nameof(PKM.Species): i.SetScreenedValue(GameInfo.Strings.specieslist); return;
case nameof(PKM.HeldItem): i.SetScreenedValue(GameInfo.Strings.itemlist); return;
case nameof(PKM.Ability): i.SetScreenedValue(GameInfo.Strings.abilitylist); return;
case nameof(PKM.Nature): i.SetScreenedValue(GameInfo.Strings.natures); return;
case nameof(PKM.Ball): i.SetScreenedValue(GameInfo.Strings.balllist); return;
case nameof(PKM.Move1) or nameof(PKM.Move2) or nameof(PKM.Move3) or nameof(PKM.Move4):
case nameof(PKM.RelearnMove1) or nameof(PKM.RelearnMove2) or nameof(PKM.RelearnMove3) or nameof(PKM.RelearnMove4):
i.SetScreenedValue(GameInfo.Strings.movelist); return;
}
}
/// <summary>
/// Checks if the object is filtered by the provided <see cref="filters"/>.
/// </summary>
/// <param name="filters">Filters which must be satisfied.</param>
/// <param name="pk">Object to check.</param>
/// <returns>True if <see cref="pk"/> matches all filters.</returns>
public static bool IsFilterMatch(IEnumerable<StringInstruction> filters, PKM pk) => filters.All(z => IsFilterMatch(z, pk, Props[Array.IndexOf(Types, pk.GetType())]));
/// <summary>
/// Checks if the object is filtered by the provided <see cref="filters"/>.
/// </summary>
/// <param name="filters">Filters which must be satisfied.</param>
/// <param name="pk">Object to check.</param>
/// <returns>True if <see cref="pk"/> matches all filters.</returns>
public static bool IsFilterMatchMeta(IEnumerable<StringInstruction> filters, SlotCache pk)
{
foreach (var i in filters)
{
foreach (var filter in BatchFilters.FilterMeta)
{
if (!filter.IsMatch(i.PropertyName))
continue;
if (!filter.IsFiltered(pk, i))
return false;
break;
}
}
return true;
}
/// <summary>
/// Checks if the object is filtered by the provided <see cref="filters"/>.
/// </summary>
/// <param name="filters">Filters which must be satisfied.</param>
/// <param name="obj">Object to check.</param>
/// <returns>True if <see cref="obj"/> matches all filters.</returns>
public static bool IsFilterMatch(IEnumerable<StringInstruction> filters, object obj)
{
foreach (var cmd in filters)
{
if (cmd.PropertyName is PROP_TYPENAME)
{
if ((obj.GetType().Name == cmd.PropertyValue) != cmd.Evaluator)
return false;
continue;
}
if (!ReflectUtil.HasProperty(obj, cmd.PropertyName, out var pi))
return false;
try
{
if (pi.IsValueEqual(obj, cmd.PropertyValue) == cmd.Evaluator)
continue;
}
// User provided inputs can mismatch the type's required value format, and fail to be compared.
catch (Exception e)
{
Debug.WriteLine($"Unable to compare {cmd.PropertyName} to {cmd.PropertyValue}.");
Debug.WriteLine(e.Message);
}
return false;
}
return true;
}
/// <summary>
/// Tries to modify the <see cref="PKM"/>.
/// </summary>
/// <param name="pk">Object to modify.</param>
/// <param name="filters">Filters which must be satisfied prior to any modifications being made.</param>
/// <param name="modifications">Modifications to perform on the <see cref="pk"/>.</param>
/// <returns>Result of the attempted modification.</returns>
public static bool TryModify(PKM pk, IEnumerable<StringInstruction> filters, IEnumerable<StringInstruction> modifications)
{
var result = TryModifyPKM(pk, filters, modifications);
return result == ModifyResult.Modified;
}
/// <summary>
/// Tries to modify the <see cref="BatchInfo"/>.
/// </summary>
/// <param name="pk">Command Filter</param>
/// <param name="filters">Filters which must be satisfied prior to any modifications being made.</param>
/// <param name="modifications">Modifications to perform on the <see cref="pk"/>.</param>
/// <returns>Result of the attempted modification.</returns>
internal static ModifyResult TryModifyPKM(PKM pk, IEnumerable<StringInstruction> filters, IEnumerable<StringInstruction> modifications)
{
if (!pk.ChecksumValid || pk.Species == 0)
return ModifyResult.Invalid;
var info = new BatchInfo(pk);
var pi = Props[Array.IndexOf(Types, pk.GetType())];
foreach (var cmd in filters)
{
try
{
if (!IsFilterMatch(cmd, info, pi))
return ModifyResult.Filtered;
}
// Swallow any error because this can be malformed user input.
catch (Exception ex)
{
Debug.WriteLine(MsgBEModifyFailCompare + " " + ex.Message, cmd.PropertyName, cmd.PropertyValue);
return ModifyResult.Error;
}
}
ModifyResult result = ModifyResult.Modified;
foreach (var cmd in modifications)
{
try
{
var tmp = SetPKMProperty(cmd, info, pi);
if (tmp != ModifyResult.Modified)
result = tmp;
}
// Swallow any error because this can be malformed user input.
catch (Exception ex)
{
Debug.WriteLine(MsgBEModifyFail + " " + ex.Message, cmd.PropertyName, cmd.PropertyValue);
}
}
return result;
}
/// <summary>
/// Sets the if the <see cref="BatchInfo"/> should be filtered due to the <see cref="StringInstruction"/> provided.
/// </summary>
/// <param name="cmd">Command Filter</param>
/// <param name="info">Pokémon to check.</param>
/// <param name="props">PropertyInfo cache (optional)</param>
/// <returns>True if filtered, else false.</returns>
private static ModifyResult SetPKMProperty(StringInstruction cmd, BatchInfo info, IReadOnlyDictionary<string, PropertyInfo> props)
{
var pk = info.Entity;
if (cmd.PropertyValue.StartsWith(CONST_BYTES, StringComparison.Ordinal))
return SetByteArrayProperty(pk, cmd);
if (cmd.PropertyValue.StartsWith(CONST_SUGGEST, StringComparison.OrdinalIgnoreCase))
return SetSuggestedPKMProperty(cmd.PropertyName, info, cmd.PropertyValue);
if (cmd.PropertyValue == CONST_RAND && cmd.PropertyName == nameof(PKM.Moves))
return SetMoves(pk, info.Legality.GetMoveSet(true));
if (SetComplexProperty(pk, cmd))
return ModifyResult.Modified;
if (!props.TryGetValue(cmd.PropertyName, out var pi))
return ModifyResult.Error;
if (!pi.CanWrite)
return ModifyResult.Error;
object val;
if (cmd.Random)
val = cmd.RandomValue;
else if (cmd.PropertyValue.StartsWith(CONST_POINTER) && props.TryGetValue(cmd.PropertyValue[1..], out var opi))
val = opi.GetValue(pk);
else
val = cmd.PropertyValue;
ReflectUtil.SetValue(pi, pk, val);
return ModifyResult.Modified;
}
/// <summary>
/// Checks if the <see cref="BatchInfo"/> should be filtered due to the <see cref="StringInstruction"/> provided.
/// </summary>
/// <param name="cmd">Command Filter</param>
/// <param name="info">Pokémon to check.</param>
/// <param name="props">PropertyInfo cache (optional)</param>
/// <returns>True if filter matches, else false.</returns>
private static bool IsFilterMatch(StringInstruction cmd, BatchInfo info, IReadOnlyDictionary<string, PropertyInfo> props)
{
var match = BatchFilters.FilterMods.Find(z => z.IsMatch(cmd.PropertyName));
if (match != null)
return match.IsFiltered(info, cmd);
return IsPropertyFiltered(cmd, info.Entity, props);
}
/// <summary>
/// Checks if the <see cref="PKM"/> should be filtered due to the <see cref="StringInstruction"/> provided.
/// </summary>
/// <param name="cmd">Command Filter</param>
/// <param name="pk">Pokémon to check.</param>
/// <param name="props">PropertyInfo cache (optional)</param>
/// <returns>True if filter matches, else false.</returns>
private static bool IsFilterMatch(StringInstruction cmd, PKM pk, IReadOnlyDictionary<string, PropertyInfo> props)
{
var match = BatchFilters.FilterMods.Find(z => z.IsMatch(cmd.PropertyName));
if (match != null)
return match.IsFiltered(pk, cmd);
return IsPropertyFiltered(cmd, pk, props);
}
/// <summary>
/// Checks if the <see cref="PKM"/> should be filtered due to the <see cref="StringInstruction"/> provided.
/// </summary>
/// <param name="cmd">Command Filter</param>
/// <param name="pk">Pokémon to check.</param>
/// <param name="props">PropertyInfo cache</param>
/// <returns>True if filtered, else false.</returns>
private static bool IsPropertyFiltered(StringInstruction cmd, PKM pk, IReadOnlyDictionary<string, PropertyInfo> props)
{
if (!props.TryGetValue(cmd.PropertyName, out var pi))
return false;
if (!pi.CanRead)
return false;
return pi.IsValueEqual(pk, cmd.PropertyValue) == cmd.Evaluator;
}
/// <summary>
/// Sets the <see cref="PKM"/> data with a suggested value based on its <see cref="LegalityAnalysis"/>.
/// </summary>
/// <param name="name">Property to modify.</param>
/// <param name="info">Cached info storing Legal data.</param>
/// <param name="propValue">Suggestion string which starts with <see cref="CONST_SUGGEST"/></param>
private static ModifyResult SetSuggestedPKMProperty(string name, BatchInfo info, string propValue)
{
var first = BatchMods.SuggestionMods.Find(z => z.IsMatch(name, propValue, info));
if (first != null)
return first.Modify(name, propValue, info);
return ModifyResult.Error;
}
/// <summary>
/// Sets the <see cref="PKM"/> byte array property to a specified value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="cmd">Modification</param>
private static ModifyResult SetByteArrayProperty(PKM pk, StringInstruction cmd)
{
switch (cmd.PropertyName)
{
case nameof(PKM.Nickname_Trash): ConvertToBytes(cmd.PropertyValue).CopyTo(pk.Nickname_Trash); return ModifyResult.Modified;
case nameof(PKM.OT_Trash): ConvertToBytes(cmd.PropertyValue).CopyTo(pk.OT_Trash); return ModifyResult.Modified;
case nameof(PKM.HT_Trash): ConvertToBytes(cmd.PropertyValue).CopyTo(pk.HT_Trash); return ModifyResult.Modified;
default:
return ModifyResult.Error;
}
static byte[] ConvertToBytes(string str)
{
var arr = str[CONST_BYTES.Length..].Split(',');
return Array.ConvertAll(arr, z => Convert.ToByte(z.Trim(), 16));
}
}
/// <summary>
/// Sets the <see cref="PKM"/> property to a non-specific smart value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="cmd">Modification</param>
/// <returns>True if modified, false if no modifications done.</returns>
private static bool SetComplexProperty(PKM pk, StringInstruction cmd)
{
if (cmd.PropertyName.StartsWith("IV", StringComparison.Ordinal) && cmd.PropertyValue == CONST_RAND)
{
SetRandomIVs(pk, cmd);
return true;
}
var match = BatchMods.ComplexMods.Find(z => z.IsMatch(cmd.PropertyName, cmd.PropertyValue));
if (match == null)
return false;
match.Modify(pk, cmd);
return true;
}
/// <summary>
/// Sets the <see cref="PKM"/> IV(s) to a random value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="cmd">Modification</param>
private static void SetRandomIVs(PKM pk, StringInstruction cmd)
{
if (cmd.PropertyName == nameof(PKM.IVs))
{
pk.SetRandomIVs();
return;
}
if (TryGetHasProperty(pk, cmd.PropertyName, out var pi))
ReflectUtil.SetValue(pi, pk, Util.Rand.Next(pk.MaxIV + 1));
}
}

View File

@ -1,85 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using static PKHeX.Core.MessageStrings;
namespace PKHeX.Core;
/// <summary>
/// Carries out a batch edit and contains information summarizing the results.
/// </summary>
public sealed class BatchEditor
{
private int Modified { get; set; }
private int Iterated { get; set; }
private int Failed { get; set; }
/// <summary>
/// Tries to modify the <see cref="PKM"/>.
/// </summary>
/// <param name="pk">Object to modify.</param>
/// <param name="filters">Filters which must be satisfied prior to any modifications being made.</param>
/// <param name="modifications">Modifications to perform on the <see cref="pk"/>.</param>
/// <returns>Result of the attempted modification.</returns>
public bool Process(PKM pk, IEnumerable<StringInstruction> filters, IEnumerable<StringInstruction> modifications)
{
if (pk.Species == 0)
return false;
if (!pk.Valid)
{
Iterated++;
const string reason = "Not Valid.";
Debug.WriteLine($"{MsgBEModifyFailBlocked} {reason}");
return false;
}
var result = BatchEditing.TryModifyPKM(pk, filters, modifications);
if (result != ModifyResult.Invalid)
Iterated++;
if (result == ModifyResult.Error)
Failed++;
if (result != ModifyResult.Modified)
return false;
pk.RefreshChecksum();
Modified++;
return true;
}
/// <summary>
/// Gets a message indicating the overall result of all modifications performed across multiple Batch Edit jobs.
/// </summary>
/// <param name="sets">Collection of modifications.</param>
/// <returns>Friendly (multi-line) string indicating the result of the batch edits.</returns>
public string GetEditorResults(ICollection<StringInstructionSet> sets)
{
if (sets.Count == 0)
return MsgBEInstructionNone;
int ctr = Modified / sets.Count;
int len = Iterated / sets.Count;
string maybe = sets.Count == 1 ? string.Empty : "~";
string result = string.Format(MsgBEModifySuccess, maybe, ctr, len);
if (Failed > 0)
result += Environment.NewLine + maybe + string.Format(MsgBEModifyFailError, Failed);
return result;
}
public static BatchEditor Execute(IList<string> lines, IEnumerable<PKM> data)
{
var editor = new BatchEditor();
var sets = StringInstructionSet.GetBatchSets(lines).ToArray();
foreach (var pk in data)
{
foreach (var set in sets)
editor.Process(pk, set.Filters, set.Instructions);
}
return editor;
}
public void AddSkipped()
{
++Iterated;
}
}

View File

@ -1,33 +0,0 @@
using System.Collections.Generic;
using static PKHeX.Core.BatchEditing;
namespace PKHeX.Core;
/// <summary>
/// Filters for Batch Editing
/// </summary>
public static class BatchFilters
{
public static readonly List<IComplexFilter> FilterMods = new()
{
new ComplexFilter(PROP_LEGAL,
(pk, cmd) => bool.TryParse(cmd.PropertyValue, out var b) && (b == new LegalityAnalysis(pk).Valid) == cmd.Evaluator,
(info, cmd) => bool.TryParse(cmd.PropertyValue, out var b) && (b == info.Legality.Valid) == cmd.Evaluator),
new ComplexFilter(PROP_TYPENAME,
(pk, cmd) => (pk.GetType().Name == cmd.PropertyValue) == cmd.Evaluator,
(info, cmd) => (info.Entity.GetType().Name == cmd.PropertyValue) == cmd.Evaluator),
};
public static readonly List<IComplexFilterMeta> FilterMeta = new()
{
new MetaFilter(IdentifierContains,
(obj, cmd) => obj is SlotCache s && s.Identify().Contains(cmd.PropertyValue) == cmd.Evaluator),
new MetaFilter(nameof(SlotInfoBox.Box),
(obj, cmd) => obj is SlotCache { Source: SlotInfoBox b } && int.TryParse(cmd.PropertyValue, out var box) && b.Box + 1 == box),
new MetaFilter(nameof(ISlotInfo.Slot),
(obj, cmd) => obj is SlotCache s && int.TryParse(cmd.PropertyValue, out var slot) && s.Source.Slot + 1 == slot),
};
}

View File

@ -1,18 +0,0 @@
using System.Collections.Generic;
namespace PKHeX.Core;
/// <summary>
/// Information wrapper used for Batch Editing to apply suggested values.
/// </summary>
public sealed class BatchInfo
{
internal PKM Entity { get; }
internal BatchInfo(PKM pk) => Entity = pk;
private LegalityAnalysis? la;
internal LegalityAnalysis Legality => la ??= new LegalityAnalysis(Entity);
public bool Legal => Legality.Valid;
internal IReadOnlyList<ushort> SuggestedRelearn => Legality.GetSuggestedRelearnMoves();
}

View File

@ -1,126 +0,0 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using static PKHeX.Core.BatchEditing;
namespace PKHeX.Core;
/// <summary>
/// Modifications for Batch Editing
/// </summary>
public static class BatchMods
{
public static readonly List<ISuggestModification> SuggestionMods = new()
{
// Interface Specific
new TypeSuggestion<ICombatPower>(nameof(ICombatPower.Stat_CP), p => p.ResetCP()),
new TypeSuggestion<IScaledSizeValue>(nameof(IScaledSizeValue.HeightAbsolute), p => p.ResetHeight()),
new TypeSuggestion<IScaledSizeValue>(nameof(IScaledSizeValue.WeightAbsolute), p => p.ResetWeight()),
new TypeSuggestion<IHyperTrain>(nameof(Extensions.HyperTrainClear), p => p.HyperTrainClear()),
new TypeSuggestion<IGeoTrack>(nameof(Extensions.ClearGeoLocationData), p => p.ClearGeoLocationData()),
new TypeSuggestion<IAwakened>(nameof(AwakeningUtil.AwakeningClear), p => p.AwakeningClear()),
new TypeSuggestion<IAwakened>(nameof(AwakeningUtil.AwakeningMax), p => p.AwakeningMax()),
new TypeSuggestion<IGanbaru>(nameof(GanbaruExtensions.ClearGanbaruValues), p => p.ClearGanbaruValues()),
new TypeSuggestion<IGanbaru>(nameof(GanbaruExtensions.SetSuggestedGanbaruValues), p => p.SetSuggestedGanbaruValues((PKM)p)),
// Date Copy
new TypeSuggestion<PKM>(nameof(PKM.EggMetDate), p => p.EggMetDate = p.MetDate),
new TypeSuggestion<PKM>(nameof(PKM.MetDate), p => p.MetDate = p.EggMetDate),
new TypeSuggestion<PKM>(nameof(PKM.Nature), p => p.Format >= 8, p => p.Nature = p.StatNature),
new TypeSuggestion<PKM>(nameof(PKM.StatNature), p => p.Format >= 8, p => p.StatNature = p.Nature),
new TypeSuggestion<PKM>(nameof(PKM.Stats), p => p.ResetPartyStats()),
new TypeSuggestion<PKM>(nameof(PKM.Ball), p => BallApplicator.ApplyBallLegalByColor(p)),
new TypeSuggestion<PKM>(nameof(PKM.Heal), p => p.Heal()),
new TypeSuggestion<PKM>(nameof(PKM.HealPP), p => p.HealPP()),
new TypeSuggestion<PKM>(nameof(IHyperTrain.HyperTrainFlags), p => p.SetSuggestedHyperTrainingData()),
new TypeSuggestion<PKM>(nameof(PKM.Move1_PP), p => p.SetSuggestedMovePP(0)),
new TypeSuggestion<PKM>(nameof(PKM.Move2_PP), p => p.SetSuggestedMovePP(1)),
new TypeSuggestion<PKM>(nameof(PKM.Move3_PP), p => p.SetSuggestedMovePP(2)),
new TypeSuggestion<PKM>(nameof(PKM.Move4_PP), p => p.SetSuggestedMovePP(3)),
new ComplexSuggestion(nameof(PKM.Moves), (_, _, info) => BatchModifications.SetMoves(info.Entity, info.Legality.GetMoveSet())),
new ComplexSuggestion(PROP_EVS, (_, _, info) => BatchModifications.SetEVs(info.Entity)),
new ComplexSuggestion(nameof(PKM.RelearnMoves), (_, value, info) => BatchModifications.SetSuggestedRelearnData(info, value)),
new ComplexSuggestion(PROP_RIBBONS, (_, value, info) => BatchModifications.SetSuggestedRibbons(info, value)),
new ComplexSuggestion(nameof(PKM.Met_Location), (_, _, info) => BatchModifications.SetSuggestedMetData(info)),
new ComplexSuggestion(nameof(PKM.CurrentLevel), (_, _, info) => BatchModifications.SetMinimumCurrentLevel(info)),
new ComplexSuggestion(PROP_CONTESTSTATS, p => p is IContestStats, (_, value, info) => BatchModifications.SetContestStats(info.Entity, info.Legality, value)),
new ComplexSuggestion(PROP_MOVEMASTERY, (_, value, info) => BatchModifications.SetSuggestedMasteryData(info, value)),
};
private static DateTime ParseDate(string val) => DateTime.ParseExact(val, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None);
public static readonly List<IComplexSet> ComplexMods = new()
{
// Date
new ComplexSet(nameof(PKM.MetDate), (pk, cmd) => pk.MetDate = ParseDate(cmd.PropertyValue)),
new ComplexSet(nameof(PKM.EggMetDate), (pk, cmd) => pk.EggMetDate = ParseDate(cmd.PropertyValue)),
// Value Swap
new ComplexSet(nameof(PKM.EncryptionConstant), value => value == nameof(PKM.PID), (pk, _) => pk.EncryptionConstant = pk.PID),
new ComplexSet(nameof(PKM.PID), value => value == nameof(PKM.EncryptionConstant), (pk, _) => pk.PID = pk.EncryptionConstant),
// Realign to Derived Value
new ComplexSet(nameof(PKM.Ability), value => value.Length == 2 && value.StartsWith(CONST_SPECIAL), (pk, cmd) => pk.RefreshAbility(Convert.ToInt16(cmd.PropertyValue[1]) - 0x30)),
new ComplexSet(nameof(PKM.AbilityNumber), value => value.Length == 2 && value.StartsWith(CONST_SPECIAL), (pk, cmd) => pk.RefreshAbility(Convert.ToInt16(cmd.PropertyValue[1]) - 0x30)),
// Random
new ComplexSet(nameof(PKM.EncryptionConstant), value => value == CONST_RAND, (pk, _) => pk.EncryptionConstant = Util.Rand32()),
new ComplexSet(nameof(PKM.PID), value => value == CONST_RAND, (pk, _) => pk.PID = Util.Rand32()),
new ComplexSet(nameof(PKM.Gender), value => value == CONST_RAND, (pk, _) => pk.SetPIDGender(pk.Gender)),
new ComplexSet(PROP_EVS, value => value == CONST_RAND, (pk, _) => SetRandomEVs(pk)),
// Shiny
new ComplexSet(nameof(PKM.PID),
value => value.StartsWith(CONST_SHINY, true, CultureInfo.CurrentCulture),
(pk, cmd) => CommonEdits.SetShiny(pk, GetRequestedShinyState(cmd.PropertyValue))),
new ComplexSet(nameof(PKM.Species), value => value == "0", (pk, _) => Array.Clear(pk.Data, 0, pk.Data.Length)),
new ComplexSet(nameof(PKM.IsNicknamed), value => string.Equals(value, "false", StringComparison.OrdinalIgnoreCase), (pk, _) => pk.SetDefaultNickname()),
// Complicated
new ComplexSet(nameof(PKM.EncryptionConstant), value => value.StartsWith(CONST_RAND), (pk, cmd) => SetComplicatedEC(pk, cmd.PropertyValue[^1])),
};
private static void SetComplicatedEC(PKM pk, char option)
{
var rng = Util.Rand;
uint rand = rng.Rand32();
uint mod = 1, noise = 0;
if (pk.Species is >= (int)Species.Wurmple and <= (int)Species.Dustox)
{
mod = 10;
bool lower = option is '0' or 'B' or 'S' || WurmpleUtil.GetWurmpleEvoGroup(pk.Species) == 0;
noise = (lower ? 0u : 5u) + (uint)rng.Next(0, 5);
}
else if (pk.Species is (int)Species.Dunsparce or (int)Species.Dudunsparce or (int)Species.Tandemaus or (int)Species.Maushold)
{
mod = 100;
noise = option is '0' or '3' ? 0u : (uint)rng.Next(1, 100);
}
else if (option is >= '0' and <= '5')
{
mod = 6;
noise = (uint)(option - '0');
}
pk.EncryptionConstant = unchecked(rand - (rand % mod) + noise);
}
private static void SetRandomEVs(PKM pk)
{
Span<int> evs = stackalloc int[6];
EffortValues.SetRandom(evs, pk.Format);
pk.SetEVs(evs);
}
private static Shiny GetRequestedShinyState(string text) => text.Length == 0 ? Shiny.Random : GetRequestedShinyState(text[^1]);
private static Shiny GetRequestedShinyState(char last) => last switch
{
'0' => Shiny.AlwaysSquare,
'1' => Shiny.AlwaysStar,
_ => Shiny.Random,
};
}

View File

@ -0,0 +1,23 @@
using System.Diagnostics.CodeAnalysis;
namespace PKHeX.Core;
/// <summary>
/// Default property provider that uses an <see cref="IBatchEditor{TObject}"/> for reflection.
/// </summary>
public class BatchPropertyProvider<TEditor, TObject>(TEditor editor) : IPropertyProvider<TObject> where TObject : notnull where TEditor : IBatchEditor<TObject>
{
/// <summary>
/// Initializes a new instance of the <see cref="BatchPropertyProvider{TEditor, TObject}"/> class with the specified editor.
/// </summary>
public bool TryGetProperty(TObject obj, string prop, [NotNullWhen(true)] out string? result)
{
result = null;
if (!editor.TryGetHasProperty(obj, prop, out var pi))
return false;
var value = pi.GetValue(obj);
result = value?.ToString();
return result is not null;
}
}

View File

@ -1,25 +0,0 @@
using System;
namespace PKHeX.Core;
/// <inheritdoc cref="IComplexFilter"/>
public sealed class ComplexFilter : IComplexFilter
{
private readonly string Property;
private readonly Func<PKM, StringInstruction, bool> FilterPKM;
private readonly Func<BatchInfo, StringInstruction, bool> FilterBulk;
public ComplexFilter(
string property,
Func<PKM, StringInstruction, bool> filterPkm,
Func<BatchInfo, StringInstruction, bool> filterBulk)
{
Property = property;
FilterPKM = filterPkm;
FilterBulk = filterBulk;
}
public bool IsMatch(string prop) => prop == Property;
public bool IsFiltered(PKM pk, StringInstruction value) => FilterPKM(pk, value);
public bool IsFiltered(BatchInfo info, StringInstruction value) => FilterBulk(info, value);
}

View File

@ -1,23 +0,0 @@
using System;
namespace PKHeX.Core;
/// <inheritdoc cref="IComplexSet"/>
public sealed class ComplexSet : IComplexSet
{
public readonly string PropertyName;
public readonly Func<string, bool> IsValueCompatible = _ => true;
private readonly Action<PKM, StringInstruction> Action;
public ComplexSet(string propertyName, Action<PKM, StringInstruction> modify)
{
PropertyName = propertyName;
Action = modify;
}
public ComplexSet(string propertyName, Func<string, bool> criteria, Action<PKM, StringInstruction> modify) : this(propertyName, modify) => IsValueCompatible = criteria;
public bool IsMatch(string name, string value) => name == PropertyName && IsValueCompatible(value);
public void Modify(PKM pk, StringInstruction instr) => Action(pk, instr);
}

View File

@ -1,11 +0,0 @@
namespace PKHeX.Core;
/// <summary>
/// Complex filter of data based on a string value.
/// </summary>
public interface IComplexFilter
{
bool IsMatch(string prop);
bool IsFiltered(PKM pk, StringInstruction value);
bool IsFiltered(BatchInfo info, StringInstruction value);
}

View File

@ -1,10 +0,0 @@
namespace PKHeX.Core;
/// <summary>
/// Complex filter of data based on a string value.
/// </summary>
public interface IComplexFilterMeta
{
bool IsMatch(string prop);
bool IsFiltered(object cache, StringInstruction value);
}

View File

@ -1,10 +0,0 @@
namespace PKHeX.Core;
/// <summary>
/// Complex modification of data to a string value.
/// </summary>
public interface IComplexSet
{
bool IsMatch(string name, string value);
void Modify(PKM pk, StringInstruction instr);
}

View File

@ -1,21 +0,0 @@
using System;
namespace PKHeX.Core;
/// <inheritdoc cref="IComplexFilter"/>
public sealed class MetaFilter : IComplexFilterMeta
{
private readonly string Property;
private readonly Func<object, StringInstruction, bool> FilterPKM;
public MetaFilter(
string property,
Func<object, StringInstruction, bool> filterPkm)
{
Property = property;
FilterPKM = filterPkm;
}
public bool IsMatch(string prop) => prop == Property;
public bool IsFiltered(object pk, StringInstruction value) => FilterPKM(pk, value);
}

View File

@ -0,0 +1,51 @@
using System.Collections.Generic;
using static PKHeX.Core.EntityBatchEditor;
namespace PKHeX.Core;
/// <summary>
/// Filters for Batch Editing
/// </summary>
public static class BatchFilters
{
/// <summary>
/// Filters to use for <see cref="EntityBatchEditor"/> that are derived from the <see cref="PKM"/> data.
/// </summary>
public static readonly List<IComplexFilter> FilterMods =
[
new ComplexFilter(PROP_LEGAL,
(pk, cmd) => bool.TryParse(cmd.PropertyValue, out var b) && cmd.Comparer.IsCompareEquivalence(b == new LegalityAnalysis(pk).Valid),
(info, cmd) => bool.TryParse(cmd.PropertyValue, out var b) && cmd.Comparer.IsCompareEquivalence(b == info.Legality.Valid)),
new ComplexFilter(BatchEditingUtil.PROP_TYPENAME,
(pk, cmd) => cmd.Comparer.IsCompareEquivalence(pk.GetType().Name == cmd.PropertyValue),
(info, cmd) => cmd.Comparer.IsCompareEquivalence(info.Entity.GetType().Name == cmd.PropertyValue)),
new ComplexFilter(PROP_TYPE1,
(pk, cmd) => byte.TryParse(cmd.PropertyValue, out var b) && cmd.Comparer.IsCompareEquivalence(b == pk.PersonalInfo.Type1),
(info, cmd) => byte.TryParse(cmd.PropertyValue, out var b) && cmd.Comparer.IsCompareEquivalence(b == info.Entity.PersonalInfo.Type1)),
new ComplexFilter(PROP_TYPE2,
(pk, cmd) => byte.TryParse(cmd.PropertyValue, out var b) && cmd.Comparer.IsCompareEquivalence(b == pk.PersonalInfo.Type2),
(info, cmd) => byte.TryParse(cmd.PropertyValue, out var b) && cmd.Comparer.IsCompareEquivalence(b == info.Entity.PersonalInfo.Type2)),
new ComplexFilter(PROP_TYPEEITHER,
(pk, cmd) => byte.TryParse(cmd.PropertyValue, out var b) && cmd.Comparer.IsCompareEquivalence(pk.PersonalInfo.IsType(b)),
(info, cmd) => byte.TryParse(cmd.PropertyValue, out var b) && cmd.Comparer.IsCompareEquivalence(info.Entity.PersonalInfo.IsType(b))),
];
/// <summary>
/// Filters to use for <see cref="EntityBatchEditor"/> that are derived from the <see cref="PKM"/> source.
/// </summary>
public static readonly List<IComplexFilterMeta> FilterMeta =
[
new MetaFilter(IdentifierContains,
(obj, cmd) => obj is SlotCache s && cmd.Comparer.IsCompareEquivalence(s.Identify().Contains(cmd.PropertyValue))),
new MetaFilter(nameof(SlotInfoBox.Box),
(obj, cmd) => obj is SlotCache { Source: SlotInfoBox b } && int.TryParse(cmd.PropertyValue, out var box) && cmd.Comparer.IsCompareOperator((b.Box + 1).CompareTo(box))),
new MetaFilter(nameof(ISlotInfo.Slot),
(obj, cmd) => obj is SlotCache s && int.TryParse(cmd.PropertyValue, out var slot) && cmd.Comparer.IsCompareOperator((s.Source.Slot + 1).CompareTo(slot))),
];
}

View File

@ -0,0 +1,19 @@
namespace PKHeX.Core;
/// <summary>
/// Information wrapper used for Batch Editing to apply suggested values.
/// </summary>
/// <param name="Entity"> Entity to be modified. </param>
public sealed record BatchInfo(PKM Entity)
{
/// <summary>
/// Legality analysis of the entity.
/// </summary>
/// <remarks>
/// Eagerly evaluate on ctor, so that the initial state is remembered before any modifications may disturb matching.
/// </remarks>
public readonly LegalityAnalysis Legality = new(Entity);
/// <inheritdoc cref="LegalityAnalysis.Valid"/>
public bool Legal => Legality.Valid;
}

View File

@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using static PKHeX.Core.EntityBatchEditor;
namespace PKHeX.Core;
/// <summary>
/// Modifications for Batch Editing
/// </summary>
public static class BatchMods
{
public static readonly List<ISuggestModification> SuggestionMods =
[
// Interface Specific
new TypeSuggestion<ICombatPower>(nameof(ICombatPower.Stat_CP), p => p.ResetCP()),
new TypeSuggestion<IScaledSizeValue>(nameof(IScaledSizeValue.HeightAbsolute), p => p.ResetHeight()),
new TypeSuggestion<IScaledSizeValue>(nameof(IScaledSizeValue.WeightAbsolute), p => p.ResetWeight()),
new TypeSuggestion<IHyperTrain>(nameof(Extensions.HyperTrainClear), p => p.HyperTrainClear()),
new TypeSuggestion<IGeoTrack>(nameof(Extensions.ClearGeoLocationData), p => p.ClearGeoLocationData()),
new TypeSuggestion<IAwakened>(nameof(AwakeningUtil.AwakeningClear), p => p.AwakeningClear()),
new TypeSuggestion<IAwakened>(nameof(AwakeningUtil.AwakeningMinimum), p => p.AwakeningMinimum()),
new TypeSuggestion<IAwakened>(nameof(AwakeningUtil.AwakeningMaximize), p => p.AwakeningMaximize()),
new TypeSuggestion<IGanbaru>(nameof(GanbaruExtensions.ClearGanbaruValues), p => p.ClearGanbaruValues()),
new TypeSuggestion<IGanbaru>(nameof(GanbaruExtensions.SetSuggestedGanbaruValues), p => p.SetSuggestedGanbaruValues((PKM)p)),
// Date Copy
new TypeSuggestion<PKM>(nameof(PKM.EggMetDate), p => p.EggMetDate = p.MetDate),
new TypeSuggestion<PKM>(nameof(PKM.MetDate), p => p.MetDate = p.EggMetDate),
new TypeSuggestion<PKM>(nameof(PKM.Nature), p => p.Format >= 8, p => p.Nature = p.StatNature),
new TypeSuggestion<PKM>(nameof(PKM.StatNature), p => p.Format >= 8, p => p.StatNature = p.Nature),
new TypeSuggestion<PKM>(nameof(PKM.Stats), p => p.ResetPartyStats()),
new TypeSuggestion<PKM>(nameof(PKM.Ball), p => BallApplicator.ApplyBallLegalByColor(p)),
new TypeSuggestion<PKM>(nameof(PKM.Heal), p => p.Heal()),
new TypeSuggestion<PKM>(nameof(PKM.HealPP), p => p.HealPP()),
new TypeSuggestion<PKM>(nameof(IHyperTrain.HyperTrainFlags), p => p.SetSuggestedHyperTrainingData()),
new TypeSuggestion<PKM>(nameof(PKM.Move1_PP), p => p.HealPPIndex(0)),
new TypeSuggestion<PKM>(nameof(PKM.Move2_PP), p => p.HealPPIndex(1)),
new TypeSuggestion<PKM>(nameof(PKM.Move3_PP), p => p.HealPPIndex(2)),
new TypeSuggestion<PKM>(nameof(PKM.Move4_PP), p => p.HealPPIndex(3)),
new ComplexSuggestion(nameof(PKM.CurrentFriendship), (_, _, info) => BatchModifications.SetSuggestedCurrentFriendship(info)),
new ComplexSuggestion(nameof(PKM.OriginalTrainerFriendship), (_, _, info) => BatchModifications.SetSuggestedOriginalTrainerFriendship(info)),
new ComplexSuggestion(nameof(PKM.HandlingTrainerFriendship), (_, _, info) => BatchModifications.SetSuggestedHandlingTrainerFriendship(info)),
new ComplexSuggestion(nameof(PKM.Moves), (_, _, info) => BatchModifications.SetSuggestedMoveset(info)),
new ComplexSuggestion(PROP_EVS, (_, _, info) => BatchModifications.SetEVs(info.Entity)),
new ComplexSuggestion(nameof(PKM.RelearnMoves), (_, value, info) => BatchModifications.SetSuggestedRelearnData(info, value)),
new ComplexSuggestion(PROP_RIBBONS, (_, value, info) => BatchModifications.SetSuggestedRibbons(info, value)),
new ComplexSuggestion(nameof(PKM.MetLocation), (_, _, info) => BatchModifications.SetSuggestedMetData(info)),
new ComplexSuggestion(nameof(PKM.CurrentLevel), (_, _, info) => BatchModifications.SetMinimumCurrentLevel(info)),
new ComplexSuggestion(PROP_CONTESTSTATS, p => p is IContestStats, (_, value, info) => BatchModifications.SetContestStats(info.Entity, info.Legality, value)),
new ComplexSuggestion(PROP_MOVEMASTERY, (_, value, info) => BatchModifications.SetSuggestedMasteryData(info, value)),
new ComplexSuggestion(PROP_MOVEPLUS, (_, value, info) => BatchModifications.SetSuggestedMovePlusData(info, value)),
];
private static DateOnly ParseDate(ReadOnlySpan<char> val) => DateOnly.ParseExact(val, "yyyyMMdd", CultureInfo.InvariantCulture);
public static readonly List<IComplexSet> ComplexMods =
[
new ComplexSet(nameof(PKM.MetDate), (pk, cmd) => pk.MetDate = ParseDate(cmd.PropertyValue)),
new ComplexSet(nameof(PKM.EggMetDate), (pk, cmd) => pk.EggMetDate = ParseDate(cmd.PropertyValue)),
// Realign to Derived Value
new ComplexSet(nameof(PKM.Ability), value => value.Length == 2 && value.StartsWith(CONST_SPECIAL), (pk, cmd) => pk.RefreshAbility(cmd.PropertyValue[1] - 0x30)),
new ComplexSet(nameof(PKM.AbilityNumber), value => value.Length == 2 && value.StartsWith(CONST_SPECIAL), (pk, cmd) => pk.RefreshAbility(cmd.PropertyValue[1] - 0x30)),
// Random
new ComplexSet(nameof(PKM.PID), value => value is CONST_RAND, (pk, _) => pk.PID = Util.Rand32()),
new ComplexSet(nameof(PKM.Gender), value => value is CONST_RAND, (pk, _) => pk.SetPIDGender(pk.Gender)),
new ComplexSet(PROP_EVS, value => value is CONST_RAND, (pk, _) => SetRandomEVs(pk)),
new ComplexSet(nameof(ITeraType.TeraTypeOverride), value => value is CONST_RAND, (pk, _) => SetRandomTeraType(pk)),
// Shiny
new ComplexSet(nameof(PKM.PID),
value => value.StartsWith(CONST_SHINY),
(pk, cmd) => pk.SetShiny(GetRequestedShinyState(cmd.PropertyValue))),
new ComplexSet(nameof(PKM.Species), value => value is "0", (pk, _) => pk.Data.Clear()),
new ComplexSet(nameof(PKM.IsNicknamed), value => value.Equals("false", StringComparison.OrdinalIgnoreCase), (pk, _) => pk.SetDefaultNickname()),
// Complicated
new ComplexSet(nameof(PKM.EncryptionConstant), value => value.StartsWith(CONST_RAND), (pk, cmd) => pk.EncryptionConstant = CommonEdits.GetComplicatedEC(pk, option: GetOptionSuffix(cmd.PropertyValue, CONST_RAND))),
];
private static char GetOptionSuffix(ReadOnlySpan<char> str, ReadOnlySpan<char> prefix)
=> str.Length == prefix.Length ? CommonEdits.OptionNone : str[^1];
private static void SetRandomTeraType(PKM pk)
{
if (pk is ITeraType t)
t.TeraTypeOverride = (MoveType)Util.Rand.Next(0, TeraTypeUtil.MaxType + 1);
}
private static void SetRandomEVs(PKM pk)
{
Span<int> evs = stackalloc int[6];
EffortValues.SetRandom(evs, pk.Format);
pk.SetEVs(evs);
}
private static Shiny GetRequestedShinyState(ReadOnlySpan<char> text) => text.Length == 0 ? Shiny.Random : GetRequestedShinyState(text[^1]);
private static Shiny GetRequestedShinyState(char last) => last switch
{
'0' => Shiny.AlwaysSquare,
'1' => Shiny.AlwaysStar,
_ => Shiny.Random,
};
}

View File

@ -0,0 +1,16 @@
using System;
using System.Diagnostics.CodeAnalysis;
namespace PKHeX.Core;
/// <inheritdoc cref="IComplexFilter"/>
public sealed class ComplexFilter(
[ConstantExpected] string Property,
Func<PKM, StringInstruction, bool> FilterPKM,
Func<BatchInfo, StringInstruction, bool> FilterBulk)
: IComplexFilter
{
public bool IsMatch(ReadOnlySpan<char> prop) => prop.SequenceEqual(Property);
public bool IsFiltered(PKM pk, StringInstruction value) => FilterPKM(pk, value);
public bool IsFiltered(BatchInfo info, StringInstruction value) => FilterBulk(info, value);
}

View File

@ -0,0 +1,19 @@
using System;
using System.Diagnostics.CodeAnalysis;
namespace PKHeX.Core;
/// <inheritdoc cref="IComplexSet"/>
public sealed class ComplexSet([ConstantExpected] string PropertyName, Action<PKM, StringInstruction> Action) : IComplexSet
{
public readonly string PropertyName = PropertyName;
public readonly Func<ReadOnlySpan<char>, bool> IsValueCompatible = _ => true;
public ComplexSet([ConstantExpected] string PropertyName, Func<ReadOnlySpan<char>, bool> criteria, Action<PKM, StringInstruction> Action)
: this(PropertyName, Action) => IsValueCompatible = criteria;
public bool IsMatch(ReadOnlySpan<char> name, ReadOnlySpan<char> value)
=> name.SequenceEqual(PropertyName) && IsValueCompatible(value);
public void Modify(PKM pk, StringInstruction instr) => Action(pk, instr);
}

View File

@ -0,0 +1,13 @@
using System;
namespace PKHeX.Core;
/// <summary>
/// Complex filter of data based on a string value.
/// </summary>
public interface IComplexFilter
{
bool IsMatch(ReadOnlySpan<char> prop);
bool IsFiltered(PKM pk, StringInstruction value);
bool IsFiltered(BatchInfo info, StringInstruction value);
}

View File

@ -0,0 +1,12 @@
using System;
namespace PKHeX.Core;
/// <summary>
/// Complex filter of data based on a string value.
/// </summary>
public interface IComplexFilterMeta
{
bool IsMatch(ReadOnlySpan<char> prop);
bool IsFiltered(object cache, StringInstruction value);
}

View File

@ -0,0 +1,12 @@
using System;
namespace PKHeX.Core;
/// <summary>
/// Complex modification of data to a string value.
/// </summary>
public interface IComplexSet
{
bool IsMatch(ReadOnlySpan<char> name, ReadOnlySpan<char> value);
void Modify(PKM pk, StringInstruction instr);
}

View File

@ -0,0 +1,14 @@
using System;
using System.Diagnostics.CodeAnalysis;
namespace PKHeX.Core;
/// <inheritdoc cref="IComplexFilter"/>
public sealed class MetaFilter(
[ConstantExpected] string Property,
Func<object, StringInstruction, bool> FilterPKM)
: IComplexFilterMeta
{
public bool IsMatch(ReadOnlySpan<char> prop) => prop.SequenceEqual(Property);
public bool IsFiltered(object pk, StringInstruction value) => FilterPKM(pk, value);
}

View File

@ -0,0 +1,304 @@
using System;
using System.Collections.Generic;
using System.Linq;
using static PKHeX.Core.BatchModifications;
namespace PKHeX.Core;
/// <summary>
/// Logic for editing many <see cref="PKM"/> with user provided <see cref="StringInstruction"/> list.
/// </summary>
public sealed class EntityBatchEditor() : BatchEditingBase<PKM, BatchInfo>(EntityTypes, EntityCustomProperties, expectedMax: 0x200)
{
private static readonly Type[] EntityTypes =
[
typeof (PK9), typeof (PA9),
typeof (PK8), typeof (PA8), typeof (PB8),
typeof (PB7),
typeof (PK7), typeof (PK6), typeof (PK5), typeof (PK4), typeof(BK4), typeof(RK4),
typeof (PK3), typeof (XK3), typeof (CK3),
typeof (PK2), typeof (SK2), typeof (PK1),
];
/// <summary>
/// Extra properties to show in the list of selectable properties (GUI) with special handling.
/// </summary>
/// <remarks>
/// These are not necessarily properties of the <see cref="PKM"/> themselves,
/// but can be any context-sensitive value related to the <see cref="PKM"/> or its legality,
/// such as "Legal" or "HasType". The handling of these properties must be implemented in the <see cref="TryHandleSetOperation"/> and <see cref="TryHandleFilter"/> methods.
/// </remarks>
private static readonly string[] EntityCustomProperties =
[
// General
BatchEditingUtil.PROP_TYPENAME,
// Entity/PersonalInfo
PROP_LEGAL, PROP_RIBBONS, PROP_EVS, PROP_CONTESTSTATS, PROP_MOVEMASTERY, PROP_MOVEPLUS,
PROP_TYPE1, PROP_TYPE2, PROP_TYPEEITHER,
// SlotCache
IdentifierContains, nameof(ISlotInfo.Slot), nameof(SlotInfoBox.Box),
];
public static EntityBatchEditor Instance { get; } = new();
// Custom Identifiers for special handling.
private const string CONST_BYTES = "$[]"; // Define a byte array with separated hex byte values, e.g. "$[]FF,02,03" or "$[]A0 02 0A FF"
// Custom Values to apply.
internal const string CONST_RAND = "$rand";
internal const string CONST_SHINY = "$shiny";
internal const string CONST_SUGGEST = "$suggest";
internal const char CONST_SPECIAL = '$';
// Custom Properties to change.
internal const string PROP_LEGAL = "Legal";
internal const string PROP_TYPEEITHER = "HasType";
internal const string PROP_TYPE1 = "PersonalType1";
internal const string PROP_TYPE2 = "PersonalType2";
internal const string PROP_RIBBONS = "Ribbons";
internal const string PROP_EVS = "EVs";
internal const string PROP_CONTESTSTATS = "ContestStats";
internal const string PROP_MOVEMASTERY = "MoveMastery";
internal const string PROP_MOVEPLUS = "PlusMoves";
internal const string IdentifierContains = nameof(IdentifierContains);
/// <summary>
/// Initializes the <see cref="StringInstruction"/> list with a context-sensitive value. If the provided value is a string, it will attempt to convert that string to its corresponding index.
/// </summary>
/// <param name="il">Instructions to initialize.</param>
public static void ScreenStrings(IEnumerable<StringInstruction> il)
{
foreach (var i in il)
{
var pv = i.PropertyValue;
if (pv.All(char.IsDigit))
continue;
if (pv.StartsWith(CONST_SPECIAL) && !pv.StartsWith(CONST_BYTES, StringComparison.Ordinal))
{
var str = pv.AsSpan(1);
if (StringInstruction.IsRandomRange(str))
{
i.SetRandomRange(str);
continue;
}
}
SetInstructionScreenedValue(i);
}
}
/// <summary>
/// Initializes the <see cref="StringInstruction"/> with a context-sensitive value. If the provided value is a string, it will attempt to convert that string to its corresponding index.
/// </summary>
/// <param name="i">Instruction to initialize.</param>
private static void SetInstructionScreenedValue(StringInstruction i)
{
ReadOnlySpan<string> set;
switch (i.PropertyName)
{
case nameof(PKM.Species): set = GameInfo.Strings.specieslist; break;
case nameof(PKM.HeldItem): set = GameInfo.Strings.itemlist; break;
case nameof(PKM.Ability): set = GameInfo.Strings.abilitylist; break;
case nameof(PKM.Nature): set = GameInfo.Strings.natures; break;
case nameof(PKM.Ball): set = GameInfo.Strings.balllist; break;
case nameof(PKM.Move1) or nameof(PKM.Move2) or nameof(PKM.Move3) or nameof(PKM.Move4):
case nameof(PKM.RelearnMove1) or nameof(PKM.RelearnMove2) or nameof(PKM.RelearnMove3) or nameof(PKM.RelearnMove4):
set = GameInfo.Strings.movelist; break;
default:
return;
}
i.SetScreenedValue(set);
}
/// <summary>
/// Checks if the object is filtered by the provided <see cref="filters"/>.
/// </summary>
/// <param name="filters">Filters which must be satisfied.</param>
/// <param name="pk">Object to check.</param>
/// <returns>True if <see cref="pk"/> matches all filters.</returns>
public static bool IsFilterMatchMeta(IEnumerable<StringInstruction> filters, SlotCache pk)
{
foreach (var i in filters)
{
foreach (var filter in BatchFilters.FilterMeta)
{
if (!filter.IsMatch(i.PropertyName))
continue;
if (!filter.IsFiltered(pk, i))
return false;
break;
}
}
return true;
}
protected override BatchInfo CreateMeta(PKM entity) => new(entity);
protected override bool ShouldModify(PKM entity) => entity.ChecksumValid && entity.Species != 0;
protected override bool TryHandleSetOperation(StringInstruction cmd, BatchInfo info, PKM entity, out ModifyResult result)
{
if (cmd.PropertyValue.StartsWith(CONST_BYTES, StringComparison.Ordinal))
{
result = SetByteArrayProperty(entity, cmd);
return true;
}
if (cmd.PropertyValue.StartsWith(CONST_SUGGEST, StringComparison.OrdinalIgnoreCase))
{
result = SetSuggestedProperty(cmd.PropertyName, info, cmd.PropertyValue);
return true;
}
if (cmd is { PropertyValue: CONST_RAND, PropertyName: nameof(PKM.Moves) })
{
result = SetSuggestedMoveset(info, true);
return true;
}
if (SetComplexProperty(info, cmd))
{
result = ModifyResult.Modified;
return true;
}
result = ModifyResult.Skipped;
return false;
}
protected override bool TryHandleFilter(StringInstruction cmd, BatchInfo info, PKM entity, out bool isMatch)
{
var match = BatchFilters.FilterMods.Find(z => z.IsMatch(cmd.PropertyName));
if (match is null)
{
isMatch = false;
return false;
}
isMatch = match.IsFiltered(info, cmd);
return true;
}
/// <summary>
/// Sets the <see cref="PKM"/> data with a suggested value based on its <see cref="LegalityAnalysis"/>.
/// </summary>
/// <param name="name">Property to modify.</param>
/// <param name="info">Cached info storing Legal data.</param>
/// <param name="propValue">Suggestion string which starts with <see cref="CONST_SUGGEST"/></param>
private static ModifyResult SetSuggestedProperty(ReadOnlySpan<char> name, BatchInfo info, ReadOnlySpan<char> propValue)
{
foreach (var mod in BatchMods.SuggestionMods)
{
if (mod.IsMatch(name, propValue, info))
return mod.Modify(name, propValue, info);
}
return ModifyResult.Error;
}
/// <summary>
/// Sets the <see cref="PKM"/> byte array property to a specified value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="cmd">Modification</param>
private static ModifyResult SetByteArrayProperty(PKM pk, StringInstruction cmd)
{
Span<byte> dest;
switch (cmd.PropertyName)
{
case nameof(PKM.NicknameTrash) or nameof(PKM.Nickname): dest = pk.NicknameTrash; break;
case nameof(PKM.OriginalTrainerTrash): dest = pk.OriginalTrainerTrash; break;
case nameof(PKM.HandlingTrainerTrash): dest = pk.HandlingTrainerTrash; break;
default:
return ModifyResult.Error;
}
var src = cmd.PropertyValue.AsSpan(CONST_BYTES.Length); // skip prefix
StringUtil.LoadHexBytesTo(src, dest, 3);
return ModifyResult.Modified;
}
/// <summary>
/// Sets the <see cref="PKM"/> property to a non-specific smart value.
/// </summary>
/// <param name="info">Pokémon to modify.</param>
/// <param name="cmd">Modification</param>
/// <returns>True if modified, false if no modifications done.</returns>
private bool SetComplexProperty(BatchInfo info, StringInstruction cmd)
{
ReadOnlySpan<char> name = cmd.PropertyName;
ReadOnlySpan<char> value = cmd.PropertyValue;
if (name.StartsWith("IV") && value is CONST_RAND)
{
SetRandomIVs(info, name);
return true;
}
foreach (var mod in BatchMods.ComplexMods)
{
if (!mod.IsMatch(name, value))
continue;
mod.Modify(info.Entity, cmd);
return true;
}
return false;
}
/// <summary>
/// Sets the <see cref="PKM"/> IV(s) to a random value.
/// </summary>
/// <param name="info">Pokémon to modify.</param>
/// <param name="propertyName">Property to modify</param>
private void SetRandomIVs(BatchInfo info, ReadOnlySpan<char> propertyName)
{
var pk = info.Entity;
if (propertyName is nameof(PKM.IVs))
{
var la = info.Legality;
var enc = la.EncounterMatch;
if (enc is IFlawlessIVCount { FlawlessIVCount: not 0 } fc)
pk.SetRandomIVs(fc.FlawlessIVCount);
else if (enc is IFixedIVSet { IVs: {IsSpecified: true} iv})
pk.SetRandomIVs(iv);
else if (enc is IFlawlessIVCountConditional c && c.GetFlawlessIVCount(pk) is { Max: not 0 } x)
pk.SetRandomIVs(Util.Rand.Next(x.Min, x.Max + 1));
else
pk.SetRandomIVs();
return;
}
if (TryGetHasProperty(pk, propertyName, out var pi))
{
const string IV32 = nameof(PK9.IV32);
if (propertyName is IV32)
{
var value = (uint)Util.Rand.Next(0x3FFFFFFF + 1);
if (pk is BK4 bk) // Big Endian, reverse IV ordering
{
value <<= 2; // flags are the lowest bits, and our random value is still fine.
value |= bk.IV32 & 3; // preserve the flags
bk.IV32 = value;
return;
}
var exist = ReflectUtil.GetValue(pk, IV32);
value |= exist switch
{
uint iv => iv & (3u << 30), // preserve the flags
_ => 0,
};
ReflectUtil.SetValue(pi, pk, value);
}
else
{
var value = Util.Rand.Next(pk.MaxIV + 1);
ReflectUtil.SetValue(pi, pk, value);
}
}
}
}

View File

@ -0,0 +1,97 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using static PKHeX.Core.MessageStrings;
namespace PKHeX.Core;
/// <summary>
/// Carries out a batch edit and contains information summarizing the results.
/// </summary>
public sealed class EntityBatchProcessor
{
private int Modified { get; set; }
private int Iterated { get; set; }
private int Failed { get; set; }
private static EntityBatchEditor Editor => EntityBatchEditor.Instance;
/// <summary>
/// Tries to modify the <see cref="PKM"/> using instructions and a custom modifier delegate.
/// </summary>
/// <param name="pk">Object to modify.</param>
/// <param name="filters">Filters which must be satisfied prior to any modifications being made.</param>
/// <param name="modifications">Modifications to perform on the <see cref="pk"/>.</param>
/// <param name="modifier">Custom modifier delegate.</param>
/// <returns>Result of the attempted modification.</returns>
public bool Process(PKM pk, IEnumerable<StringInstruction> filters, IEnumerable<StringInstruction> modifications, Func<PKM, bool>? modifier = null)
{
if (pk.Species == 0)
return false;
if (!pk.Valid)
{
Iterated++;
const string reason = "Not Valid.";
Debug.WriteLine($"{MsgBEModifyFailBlocked} {reason}");
return false;
}
var result = Editor.TryModify(pk, filters, modifications, modifier);
if (result != ModifyResult.Skipped)
Iterated++;
if (result.HasFlag(ModifyResult.Error))
{
Failed++;
result &= ~ModifyResult.Error;
}
if (result != ModifyResult.Modified)
return false;
pk.RefreshChecksum();
Modified++;
return true;
}
/// <summary>
/// Gets a message indicating the overall result of all modifications performed across multiple Batch Edit jobs.
/// </summary>
/// <param name="sets">Collection of modifications.</param>
/// <returns>Friendly (multi-line) string indicating the result of the batch edits.</returns>
public string GetEditorResults(IReadOnlyCollection<StringInstructionSet> sets)
{
if (sets.Count == 0)
return MsgBEInstructionNone;
int ctr = Modified / sets.Count;
int len = Iterated / sets.Count;
string maybe = sets.Count == 1 ? string.Empty : "~";
string result = string.Format(MsgBEModifySuccess, maybe, ctr, len);
if (Failed > 0)
result += Environment.NewLine + maybe + string.Format(MsgBEModifyFailError, Failed);
return result;
}
/// <summary>
/// Executes the batch instruction <see cref="lines"/> on the input <see cref="data"/>
/// </summary>
/// <param name="lines">Batch instruction line(s)</param>
/// <param name="data">Entities to modify</param>
/// <param name="modifier">Custom modifier delegate.</param>
/// <returns>Editor object if follow-up modifications are desired.</returns>
public static EntityBatchProcessor Execute(ReadOnlySpan<string> lines, IEnumerable<PKM> data, Func<PKM, bool>? modifier = null)
{
var editor = new EntityBatchProcessor();
var sets = StringInstructionSet.GetBatchSets(lines);
foreach (var pk in data)
{
foreach (var set in sets)
editor.Process(pk, set.Filters, set.Instructions, modifier);
}
return editor;
}
public void AddSkipped()
{
++Iterated;
}
}

View File

@ -0,0 +1,218 @@
using System;
namespace PKHeX.Core;
/// <summary>
/// Modifications using <see cref="BatchInfo"/> legality.
/// </summary>
internal static class BatchModifications
{
private static bool IsAll(ReadOnlySpan<char> p) => p.EndsWith("All", StringComparison.OrdinalIgnoreCase);
private static bool IsNone(ReadOnlySpan<char> p) => p.EndsWith("None", StringComparison.OrdinalIgnoreCase);
/// <summary>
/// Sets a suggested legal moveset for the Entity.
/// </summary>
public static ModifyResult SetSuggestedMoveset(BatchInfo info, bool random = false)
{
Span<ushort> moves = stackalloc ushort[4];
info.Legality.GetMoveSet(moves, random);
return SetMoves(info.Entity, moves);
}
/// <summary>
/// Sets a suggested legal relearn moveset for the Entity.
/// </summary>
public static ModifyResult SetSuggestedRelearnData(BatchInfo info, ReadOnlySpan<char> propValue)
{
var pk = info.Entity;
if (pk is ITechRecord t)
{
if (IsNone(propValue))
t.SetRecordFlags(pk, TechnicalRecordApplicatorOption.None);
else if (IsAll(propValue))
t.SetRecordFlags(pk, TechnicalRecordApplicatorOption.LegalAll, info.Legality);
else
t.SetRecordFlags(pk, TechnicalRecordApplicatorOption.LegalCurrent, info.Legality);
}
pk.SetRelearnMoves(info.Legality);
return ModifyResult.Modified;
}
/// <summary>
/// Sets all legal Move Mastery flag data for the Entity.
/// </summary>
/// <remarks>Only applicable for <see cref="IMoveShop8Mastery"/>.</remarks>
public static ModifyResult SetSuggestedMasteryData(BatchInfo info, ReadOnlySpan<char> propValue)
{
var pk = info.Entity;
if (pk is not IMoveShop8Mastery t)
return ModifyResult.Skipped;
t.ClearMoveShopFlags();
if (IsNone(propValue))
return ModifyResult.Modified;
var enc = info.Legality.EncounterMatch;
if (enc is IMasteryInitialMoveShop8 shop)
shop.SetInitialMastery(pk, enc);
if (IsAll(propValue))
{
t.SetPurchasedFlagsAll(pk);
t.SetMoveShopFlagsAll(pk);
}
else
{
t.SetMoveShopFlags(pk);
}
return ModifyResult.Modified;
}
/// <summary>
/// Sets all legal Plus Move flag data for the Entity.
/// </summary>
/// <remarks>Only applicable for <see cref="IPlusRecord"/>.</remarks>
public static ModifyResult SetSuggestedMovePlusData(BatchInfo info, ReadOnlySpan<char> value)
{
var pk = info.Entity;
if (pk is not IPlusRecord t || pk.PersonalInfo is not IPermitPlus p)
return ModifyResult.Skipped;
PlusRecordApplicatorOption option;
if (IsNone(value))
option = PlusRecordApplicatorOption.None;
else if (IsAll(value))
option = PlusRecordApplicatorOption.LegalSeedTM;
else
option = PlusRecordApplicatorOption.LegalCurrent;
t.SetPlusFlags(p, option, info.Legality);
return ModifyResult.Modified;
}
/// <summary>
/// Sets suggested ribbon data for the Entity.
/// </summary>
/// <remarks>If None, removes all ribbons possible.</remarks>
public static ModifyResult SetSuggestedRibbons(BatchInfo info, ReadOnlySpan<char> value)
{
if (IsNone(value))
RibbonApplicator.RemoveAllValidRibbons(info.Legality);
else if (IsAll(value))
RibbonApplicator.SetAllValidRibbons(info.Legality);
else // Only for current context
RibbonApplicator.SetAllValidRibbons(info.Entity, info.Legality.EncounterMatch, info.Legality.Info.EvoChainsAllGens.AsSingle(info.Entity.Context));
return ModifyResult.Modified;
}
/// <summary>
/// Sets suggested met data for the Entity.
/// </summary>
public static ModifyResult SetSuggestedMetData(BatchInfo info)
{
var pk = info.Entity;
var encounter = EncounterSuggestion.GetSuggestedMetInfo(pk);
if (encounter is null)
return ModifyResult.Error;
var location = encounter.Location;
var level = encounter.LevelMin;
var minimumLevel = EncounterSuggestion.GetLowestLevel(pk, level);
var current = Math.Max(minimumLevel, level);
if (pk.MetLevel == level && pk.MetLocation == location && pk.CurrentLevel == current)
return ModifyResult.Skipped;
pk.MetLevel = level;
pk.MetLocation = location;
pk.CurrentLevel = current;
return ModifyResult.Modified;
}
/// <summary>
/// Sets the lowest current level for the Entity.
/// </summary>
public static ModifyResult SetMinimumCurrentLevel(BatchInfo info)
{
var result = EncounterSuggestion.IterateMinimumCurrentLevel(info.Entity, info.Legal);
return result ? ModifyResult.Modified : ModifyResult.Skipped;
}
/// <summary>
/// Sets the provided moves in a random order.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="moves">Moves to apply.</param>
public static ModifyResult SetMoves(PKM pk, ReadOnlySpan<ushort> moves)
{
Span<ushort> current = stackalloc ushort[4];
pk.GetMoves(current);
if (current.SequenceEqual(moves))
return ModifyResult.Skipped;
pk.SetMoves(moves);
return ModifyResult.Modified;
}
public static ModifyResult SetEVs(PKM pk)
{
Span<int> evs = stackalloc int[6];
EffortValues.SetMax(evs, pk);
Span<int> current = stackalloc int[6];
pk.GetEVs(current);
if (current.SequenceEqual(evs))
return ModifyResult.Skipped;
pk.SetEVs(evs);
return ModifyResult.Modified;
}
/// <summary>
/// Sets the contests stats as requested.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="la">Legality Information matched to.</param>
/// <param name="option">Option to apply with</param>
public static ModifyResult SetContestStats(PKM pk, LegalityAnalysis la, ReadOnlySpan<char> option)
{
if (option.Length != 0 && option[EntityBatchEditor.CONST_SUGGEST.Length..] is not "0")
pk.SetMaxContestStats(la.EncounterMatch, la.Info.EvoChainsAllGens);
else
pk.SetSuggestedContestStats(la.EncounterMatch, la.Info.EvoChainsAllGens);
return ModifyResult.Modified;
}
public static ModifyResult SetSuggestedCurrentFriendship(BatchInfo info)
{
var pk = info.Entity;
var value = HistoryVerifier.GetSuggestedFriendshipCurrent(pk, info.Legality.EncounterMatch);
if (pk.CurrentFriendship == value)
return ModifyResult.Skipped;
pk.CurrentFriendship = value;
return ModifyResult.Modified;
}
public static ModifyResult SetSuggestedOriginalTrainerFriendship(BatchInfo info)
{
var pk = info.Entity;
var value = HistoryVerifier.GetSuggestedFriendshipOT(pk, info.Legality.EncounterMatch);
if (pk.OriginalTrainerFriendship == value)
return ModifyResult.Skipped;
pk.OriginalTrainerFriendship = value;
return ModifyResult.Modified;
}
public static ModifyResult SetSuggestedHandlingTrainerFriendship(BatchInfo info)
{
var pk = info.Entity;
var value = HistoryVerifier.GetSuggestedFriendshipHT(pk);
if (pk.HandlingTrainerFriendship == value)
return ModifyResult.Skipped;
pk.HandlingTrainerFriendship = value;
return ModifyResult.Modified;
}
}

View File

@ -0,0 +1,33 @@
using System;
using System.Diagnostics.CodeAnalysis;
namespace PKHeX.Core;
/// <inheritdoc cref="ISuggestModification"/>
public sealed class ComplexSuggestion(
[ConstantExpected] string Keyword,
Func<ReadOnlySpan<char>, ReadOnlySpan<char>, BatchInfo, ModifyResult> Action)
: ISuggestModification
{
public readonly string Keyword = Keyword;
public readonly Func<PKM, bool> Criteria = _ => true;
public readonly Func<ReadOnlySpan<char>, ReadOnlySpan<char>, BatchInfo, ModifyResult> Action = Action;
public ComplexSuggestion(
[ConstantExpected] string Keyword,
Func<PKM, bool> criteria,
Func<ReadOnlySpan<char>, ReadOnlySpan<char>, BatchInfo, ModifyResult> action) : this(Keyword, action)
{
Criteria = criteria;
}
public bool IsMatch(ReadOnlySpan<char> name, ReadOnlySpan<char> value, BatchInfo info)
{
return name.SequenceEqual(Keyword) && Criteria(info.Entity);
}
public ModifyResult Modify(ReadOnlySpan<char> name, ReadOnlySpan<char> value, BatchInfo info)
{
return Action(name, value, info);
}
}

View File

@ -0,0 +1,12 @@
using System;
namespace PKHeX.Core;
/// <summary>
/// Modifies a property to have a "correct" value based on derived legality.
/// </summary>
public interface ISuggestModification
{
public bool IsMatch(ReadOnlySpan<char> name, ReadOnlySpan<char> value, BatchInfo info);
public ModifyResult Modify(ReadOnlySpan<char> name, ReadOnlySpan<char> value, BatchInfo info);
}

View File

@ -0,0 +1,34 @@
using System;
using System.Diagnostics.CodeAnalysis;
namespace PKHeX.Core;
/// <inheritdoc cref="ISuggestModification"/>
/// <typeparam name="T">Specific (or not) type</typeparam>
public sealed class TypeSuggestion<T>([ConstantExpected] string Keyword, Action<T> Action) : ISuggestModification
{
public readonly string Keyword = Keyword;
public readonly Action<T, ReadOnlySpan<char>> Action = (pk, _) => Action(pk);
public readonly Func<T, bool> Criteria = _ => true;
public TypeSuggestion([ConstantExpected] string Keyword, Func<T, bool> criteria, Action<T> action) : this(Keyword, action)
{
Criteria = criteria;
}
public bool IsMatch(ReadOnlySpan<char> name, ReadOnlySpan<char> value, BatchInfo info)
{
return name.SequenceEqual(Keyword) && info.Entity is T;
}
public ModifyResult Modify(ReadOnlySpan<char> name, ReadOnlySpan<char> value, BatchInfo info)
{
var pk = info.Entity;
if (pk is not T x)
return ModifyResult.Skipped;
if (!Criteria(x))
return ModifyResult.Skipped;
Action(x, value);
return ModifyResult.Modified;
}
}

View File

@ -0,0 +1,18 @@
using System.Diagnostics.CodeAnalysis;
namespace PKHeX.Core;
/// <summary>
/// Interface for retrieving properties from a <see cref="T"/>.
/// </summary>
public interface IPropertyProvider<in T> where T : notnull
{
/// <summary>
/// Attempts to retrieve a property's value (as string) from an entity instance.
/// </summary>
/// <param name="obj">Entity to retrieve the property from.</param>
/// <param name="prop">Property name to retrieve.</param>
/// <param name="result">Property value as string.</param>
/// <returns><see langword="true"/> if the property was found and retrieved successfully; otherwise, <see langword="false"/>.</returns>
bool TryGetProperty(T obj, string prop, [NotNullWhen(true)] out string? result);
}

View File

@ -1,27 +0,0 @@
namespace PKHeX.Core;
/// <summary>
/// Batch Editor Modification result for an individual <see cref="PKM"/>.
/// </summary>
public enum ModifyResult
{
/// <summary>
/// The <see cref="PKM"/> has invalid data and is not a suitable candidate for modification.
/// </summary>
Invalid,
/// <summary>
/// An error was occurred while iterating modifications for this <see cref="PKM"/>.
/// </summary>
Error,
/// <summary>
/// The <see cref="PKM"/> was skipped due to a matching Filter.
/// </summary>
Filtered,
/// <summary>
/// The <see cref="PKM"/> was modified.
/// </summary>
Modified,
}

View File

@ -1,123 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace PKHeX.Core;
/// <summary>
/// Batch Editing instruction
/// </summary>
/// <remarks>
/// Can be a filter (skip), or a modification instruction (modify)
/// </remarks>
/// <see cref="Exclude"/>
/// <see cref="Require"/>
/// <see cref="Apply"/>
public sealed class StringInstruction
{
public string PropertyName { get; }
public string PropertyValue { get; private set; }
/// <summary> True if ==, false if != </summary>
public bool Evaluator { get; private init; }
public StringInstruction(string name, string value)
{
PropertyName = name;
PropertyValue = value;
}
public void SetScreenedValue(string[] arr)
{
int index = Array.IndexOf(arr, PropertyValue);
PropertyValue = index > -1 ? index.ToString() : PropertyValue;
}
public static readonly IReadOnlyList<char> Prefixes = new[] { Apply, Require, Exclude };
private const char Exclude = '!';
private const char Require = '=';
private const char Apply = '.';
private const char SplitRange = ',';
/// <summary>
/// Character which divides a property and a value.
/// </summary>
/// <remarks>
/// Example:
/// =Species=1
/// The second = is the split.
/// </remarks>
public const char SplitInstruction = '=';
// Extra Functionality
private int RandomMinimum, RandomMaximum;
public bool Random { get; private set; }
public int RandomValue => Util.Rand.Next(RandomMinimum, RandomMaximum + 1);
public void SetRandRange(string pv)
{
string str = pv[1..];
var split = str.Split(SplitRange);
int.TryParse(split[0], out RandomMinimum);
int.TryParse(split[1], out RandomMaximum);
if (RandomMinimum == RandomMaximum)
{
PropertyValue = RandomMinimum.ToString();
Debug.WriteLine($"{PropertyName} randomization range Min/Max same?");
}
else
{
Random = true;
}
}
public static IEnumerable<StringInstruction> GetFilters(IEnumerable<string> lines)
{
foreach (var line in lines)
{
if (line.Length is 0 || line[0] is not (Exclude or Require))
continue;
const int start = 1;
var splitIndex = line.IndexOf(SplitInstruction, start);
if (splitIndex == -1)
continue;
var noExtra = line.IndexOf(SplitInstruction, splitIndex + 1);
if (noExtra != -1)
continue;
var name = line.AsSpan(start, splitIndex - start);
if (name.IsWhiteSpace())
continue;
bool eval = line[0] == Require;
var value = line[(splitIndex + 1)..];
yield return new StringInstruction(name.ToString(), value) { Evaluator = eval };
}
}
public static IEnumerable<StringInstruction> GetInstructions(IEnumerable<string> lines)
{
foreach (var line in lines)
{
if (line.Length is 0 || line[0] is not Apply)
continue;
const int start = 1;
var splitIndex = line.IndexOf(SplitInstruction, start);
if (splitIndex == -1)
continue;
var noExtra = line.IndexOf(SplitInstruction, splitIndex + 1);
if (noExtra != -1)
continue;
var name = line.AsSpan(start, splitIndex - start);
if (name.IsWhiteSpace())
continue;
var value = line[(splitIndex + 1)..];
yield return new StringInstruction(name.ToString(), value);
}
}
}

View File

@ -1,38 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace PKHeX.Core;
/// <summary>
/// Processes input of strings into a list of valid Filters and Instructions.
/// </summary>
public sealed class StringInstructionSet
{
public readonly IReadOnlyList<StringInstruction> Filters;
public readonly IReadOnlyList<StringInstruction> Instructions;
private const string SetSeparator = ";";
public StringInstructionSet(IReadOnlyList<StringInstruction> filters, IReadOnlyList<StringInstruction> instructions)
{
Filters = filters;
Instructions = instructions;
}
public StringInstructionSet(ICollection<string> set)
{
Filters = StringInstruction.GetFilters(set).ToList();
Instructions = StringInstruction.GetInstructions(set).ToList();
}
public static IEnumerable<StringInstructionSet> GetBatchSets(IList<string> lines)
{
int start = 0;
while (start < lines.Count)
{
var list = lines.Skip(start).TakeWhile(_ => !lines[start++].StartsWith(SetSeparator, StringComparison.Ordinal)).ToList();
yield return new StringInstructionSet(list);
}
}
}

View File

@ -1,122 +0,0 @@
using System;
namespace PKHeX.Core;
/// <summary>
/// Modifications using <see cref="BatchInfo"/> legality.
/// </summary>
internal static class BatchModifications
{
private static bool IsAll(string p) => p.EndsWith("All", StringComparison.OrdinalIgnoreCase);
private static bool IsNone(string p) => p.EndsWith("None", StringComparison.OrdinalIgnoreCase);
public static ModifyResult SetSuggestedRelearnData(BatchInfo info, string propValue)
{
var pk = info.Entity;
if (pk is ITechRecord t)
{
t.ClearRecordFlags();
if (IsAll(propValue))
{
t.SetRecordFlags(); // all
}
else if (!IsNone(propValue))
{
Span<ushort> moves = stackalloc ushort[4];
pk.GetMoves(moves);
t.SetRecordFlags(moves); // whatever fit the current moves
}
}
pk.SetRelearnMoves(info.SuggestedRelearn);
return ModifyResult.Modified;
}
public static ModifyResult SetSuggestedMasteryData(BatchInfo info, string propValue)
{
var pk = info.Entity;
if (pk is not IMoveShop8Mastery t)
return ModifyResult.Invalid;
t.ClearMoveShopFlags();
if (IsNone(propValue))
return ModifyResult.Modified;
var e = info.Legality.EncounterMatch;
if (e is IMasteryInitialMoveShop8 enc)
enc.SetInitialMastery(pk);
if (IsAll(propValue))
t.SetMoveShopFlagsAll(pk);
else
t.SetMoveShopFlags(pk);
return ModifyResult.Modified;
}
public static ModifyResult SetSuggestedRibbons(BatchInfo info, string value)
{
if (IsNone(value))
RibbonApplicator.RemoveAllValidRibbons(info.Legality);
else // All
RibbonApplicator.SetAllValidRibbons(info.Legality);
return ModifyResult.Modified;
}
public static ModifyResult SetSuggestedMetData(BatchInfo info)
{
var pk = info.Entity;
var encounter = EncounterSuggestion.GetSuggestedMetInfo(pk);
if (encounter == null)
return ModifyResult.Error;
int level = encounter.LevelMin;
int location = encounter.Location;
int minimumLevel = EncounterSuggestion.GetLowestLevel(pk, encounter.LevelMin);
pk.Met_Level = level;
pk.Met_Location = location;
pk.CurrentLevel = Math.Max(minimumLevel, level);
return ModifyResult.Modified;
}
public static ModifyResult SetMinimumCurrentLevel(BatchInfo info)
{
var result = EncounterSuggestion.IterateMinimumCurrentLevel(info.Entity, info.Legal);
return result ? ModifyResult.Modified : ModifyResult.Filtered;
}
/// <summary>
/// Sets the provided moves in a random order.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="moves">Moves to apply.</param>
public static ModifyResult SetMoves(PKM pk, ReadOnlySpan<ushort> moves)
{
pk.SetMoves(moves);
pk.HealPP();
return ModifyResult.Modified;
}
public static ModifyResult SetEVs(PKM pk)
{
Span<int> evs = stackalloc int[6];
EffortValues.SetMax(evs, pk);
pk.SetEVs(evs);
return ModifyResult.Modified;
}
/// <summary>
/// Sets the contests stats as requested.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="la">Legality Information matched to.</param>
/// <param name="option">Option to apply with</param>
public static ModifyResult SetContestStats(PKM pk, LegalityAnalysis la, string option)
{
if (option.Length != 0 && option[BatchEditing.CONST_SUGGEST.Length..] is not "0")
pk.SetMaxContestStats(la.EncounterMatch, la.Info.EvoChainsAllGens);
else
pk.SetSuggestedContestStats(la.EncounterMatch, la.Info.EvoChainsAllGens);
return ModifyResult.Modified;
}
}

View File

@ -1,37 +0,0 @@
using System;
namespace PKHeX.Core;
/// <inheritdoc cref="ISuggestModification"/>
public sealed class ComplexSuggestion : ISuggestModification
{
public readonly string Keyword;
public readonly Func<PKM, bool> Criteria = _ => true;
public readonly Func<string, string, BatchInfo, ModifyResult> Action;
public ComplexSuggestion(
string keyword,
Func<PKM, bool> criteria,
Func<string, string, BatchInfo, ModifyResult> action) : this(keyword, action)
{
Criteria = criteria;
}
public ComplexSuggestion(
string keyword,
Func<string, string, BatchInfo, ModifyResult> action)
{
Keyword = keyword;
Action = action;
}
public bool IsMatch(string name, string value, BatchInfo info)
{
return name == Keyword && Criteria(info.Entity);
}
public ModifyResult Modify(string name, string value, BatchInfo info)
{
return Action(name, value, info);
}
}

View File

@ -1,10 +0,0 @@
namespace PKHeX.Core;
/// <summary>
/// Modifies a property to have a "correct" value based on derived legality.
/// </summary>
public interface ISuggestModification
{
public bool IsMatch(string name, string value, BatchInfo info);
public ModifyResult Modify(string name, string value, BatchInfo info);
}

View File

@ -1,39 +0,0 @@
using System;
namespace PKHeX.Core;
/// <inheritdoc cref="ISuggestModification"/>
/// <typeparam name="T">Specific (or not) type</typeparam>
public sealed class TypeSuggestion<T> : ISuggestModification
{
public readonly string Keyword;
public readonly Action<T, string> Action;
public readonly Func<T, bool> Criteria = _ => true;
public TypeSuggestion(string keyword, Action<T> action)
{
Keyword = keyword;
Action = (pk, _) => action(pk);
}
public TypeSuggestion(string keyword, Func<T, bool> criteria, Action<T> action) : this(keyword, action)
{
Criteria = criteria;
}
public bool IsMatch(string name, string value, BatchInfo info)
{
return name == Keyword && info.Entity is T;
}
public ModifyResult Modify(string name, string value, BatchInfo info)
{
var pk = info.Entity;
if (pk is not T x)
return ModifyResult.Invalid;
if (!Criteria(x))
return ModifyResult.Invalid;
Action(x, value);
return ModifyResult.Modified;
}
}

View File

@ -8,7 +8,7 @@ namespace PKHeX.Core;
public static class CommonEdits
{
/// <summary>
/// Setting which enables/disables automatic manipulation of <see cref="PKM.MarkValue"/> when importing from a <see cref="IBattleTemplate"/>.
/// Setting which enables/disables automatic manipulation of <see cref="IAppliedMarkings"/> when importing from a <see cref="IBattleTemplate"/>.
/// </summary>
public static bool ShowdownSetIVMarkings { get; set; } = true;
@ -17,433 +17,478 @@ public static class CommonEdits
/// </summary>
public static bool ShowdownSetBehaviorNature { get; set; }
/// <summary>
/// Sets the <see cref="PKM.Nickname"/> to the provided value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="nick"><see cref="PKM.Nickname"/> to set. If no nickname is provided, the <see cref="PKM.Nickname"/> is set to the default value for its current language and format.</param>
public static void SetNickname(this PKM pk, string nick)
extension(PKM pk)
{
if (nick.Length == 0)
/// <summary>
/// Sets the <see cref="PKM.Nickname"/> to the provided value.
/// </summary>
/// <param name="nick"><see cref="PKM.Nickname"/> to set. If no nickname is provided, the <see cref="PKM.Nickname"/> is set to the default value for its current language and format.</param>
public void SetNickname(string nick)
{
pk.ClearNickname();
return;
}
pk.IsNicknamed = true;
pk.Nickname = nick;
}
if (nick.Length == 0)
{
pk.ClearNickname();
return;
}
/// <summary>
/// Clears the <see cref="PKM.Nickname"/> to the default value.
/// </summary>
/// <param name="pk"></param>
public static string ClearNickname(this PKM pk)
{
pk.IsNicknamed = false;
string nick = SpeciesName.GetSpeciesNameGeneration(pk.Species, pk.Language, pk.Format);
pk.Nickname = nick;
if (pk is GBPKM pk12)
pk12.SetNotNicknamed();
return nick;
}
/// <summary>
/// Sets the <see cref="PKM.Ability"/> value by sanity checking the provided <see cref="PKM.Ability"/> against the possible pool of abilities.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="abil">Desired <see cref="PKM.Ability"/> to set.</param>
public static void SetAbility(this PKM pk, int abil)
{
if (abil < 0)
return;
var index = pk.PersonalInfo.GetIndexOfAbility(abil);
index = Math.Max(0, index);
pk.SetAbilityIndex(index);
}
/// <summary>
/// Sets the <see cref="PKM.Ability"/> value based on the provided ability index (0-2)
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="index">Desired <see cref="PKM.AbilityNumber"/> (shifted by 1) to set.</param>
public static void SetAbilityIndex(this PKM pk, int index)
{
if (pk is PK5 pk5 && index == 2)
pk5.HiddenAbility = true;
else if (pk.Format <= 5)
pk.PID = EntityPID.GetRandomPID(Util.Rand, pk.Species, pk.Gender, pk.Version, pk.Nature, pk.Form, (uint)(index * 0x10001));
pk.RefreshAbility(index);
}
/// <summary>
/// Sets a Random <see cref="PKM.EncryptionConstant"/> value. The <see cref="PKM.EncryptionConstant"/> is not updated if the value should match the <see cref="PKM.PID"/> instead.
/// </summary>
/// <remarks>Accounts for Wurmple evolutions.</remarks>
/// <param name="pk">Pokémon to modify.</param>
public static void SetRandomEC(this PKM pk)
{
int gen = pk.Generation;
if (gen is 3 or 4 or 5)
{
pk.EncryptionConstant = pk.PID;
return;
pk.PrepareNickname();
pk.Nickname = nick;
pk.IsNicknamed = true;
}
int wIndex = WurmpleUtil.GetWurmpleEvoGroup(pk.Species);
if (wIndex != -1)
/// <summary>
/// Sets the <see cref="PKM.Nickname"/> to the default value of the current species and language.
/// </summary>
/// <returns>Default nickname for the current species and language.</returns>
public string ClearNickname()
{
pk.EncryptionConstant = WurmpleUtil.GetWurmpleEncryptionConstant(wIndex);
return;
pk.IsNicknamed = false;
string nick = SpeciesName.GetSpeciesNameGeneration(pk.Species, pk.Language, pk.Format);
pk.SetString(pk.NicknameTrash, nick, nick.Length, StringConverterOption.None);
if (pk is GBPKM pk12)
pk12.SetNotNicknamed();
return nick;
}
pk.EncryptionConstant = Util.Rand32();
}
/// <summary>
/// Sets the <see cref="PKM.IsShiny"/> derived value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="shiny">Desired <see cref="PKM.IsShiny"/> state to set.</param>
public static bool SetIsShiny(this PKM pk, bool shiny) => shiny ? SetShiny(pk) : pk.SetUnshiny();
/// <summary>
/// Makes a <see cref="PKM"/> shiny.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="type">Shiny type to force. Only use Always* or Random</param>
/// <returns>Returns true if the <see cref="PKM"/> data was modified.</returns>
public static bool SetShiny(PKM pk, Shiny type = Shiny.Random)
{
if (pk.IsShiny && type.IsValid(pk))
return false;
if (type == Shiny.Random || pk.FatefulEncounter || pk.Version == (int)GameVersion.GO || pk.Format <= 2)
/// <summary>
/// Sets the <see cref="PKM.Ability"/> value by sanity checking the provided <see cref="PKM.Ability"/> against the possible pool of abilities.
/// </summary>
/// <param name="abilityID">Desired <see cref="Ability"/> value to set.</param>
public void SetAbility(int abilityID)
{
pk.SetShiny();
if (abilityID < 0)
return;
var index = pk.PersonalInfo.GetIndexOfAbility(abilityID);
if (index < 0)
return; // leave original value
pk.SetAbilityIndex(index);
}
/// <summary>
/// Sets the <see cref="PKM.Ability"/> value based on the provided ability index (0-2)
/// </summary>
/// <param name="index">Desired <see cref="PKM.AbilityNumber"/> (shifted by 1) to set.</param>
public void SetAbilityIndex(int index)
{
if (pk is PK5 pk5 && index == 2)
pk5.HiddenAbility = true;
else if (pk.Format <= 5)
pk.PID = EntityPID.GetRandomPID(Util.Rand, pk.Species, pk.Gender, pk.Version, pk.Nature, pk.Form, (uint)(index * 0x10001));
pk.RefreshAbility(index);
}
/// <summary>
/// Sets a Random <see cref="PKM.EncryptionConstant"/> value. The <see cref="PKM.EncryptionConstant"/> is not updated if the value should match the <see cref="PKM.PID"/> instead.
/// </summary>
/// <remarks>Accounts for Wurmple evolutions.</remarks>
public void SetRandomEC()
{
var gen = pk.Generation;
if (gen is 3 or 4 or 5)
{
pk.EncryptionConstant = pk.PID;
return;
}
pk.EncryptionConstant = GetComplicatedEC(pk);
}
/// <summary>
/// Sets the <see cref="PKM.IsShiny"/> derived value.
/// </summary>
/// <param name="shiny">Desired <see cref="PKM.IsShiny"/> state to set.</param>
public bool SetIsShiny(bool shiny) => shiny ? SetShiny(pk) : pk.SetUnshiny();
/// <summary>
/// Makes a <see cref="PKM"/> shiny.
/// </summary>
/// <param name="type">Shiny type to force. Only use Always* or Random</param>
/// <returns>Returns true if the <see cref="PKM"/> data was modified.</returns>
public bool SetShiny(Shiny type = Shiny.Random)
{
if (pk.IsShiny && type.IsValid(pk))
return false;
if (type == Shiny.Random || pk.FatefulEncounter || pk.Version == GameVersion.GO || pk.Format <= 2)
{
pk.SetShiny();
return true;
}
do { pk.SetShiny(); }
while (!type.IsValid(pk));
return true;
}
do { pk.SetShiny(); }
while (!type.IsValid(pk));
return true;
}
/// <summary>
/// Makes a <see cref="PKM"/> not-shiny.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <returns>Returns true if the <see cref="PKM"/> data was modified.</returns>
public static bool SetUnshiny(this PKM pk)
{
if (!pk.IsShiny)
return false;
pk.SetPIDGender(pk.Gender);
return true;
}
/// <summary>
/// Sets the <see cref="PKM.Nature"/> value, with special consideration for the <see cref="PKM.Format"/> values which derive the <see cref="PKM.Nature"/> value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="nature">Desired <see cref="PKM.Nature"/> value to set.</param>
public static void SetNature(this PKM pk, int nature)
{
var value = Math.Min((int)Nature.Quirky, Math.Max((int)Nature.Hardy, nature));
var format = pk.Format;
if (format >= 8)
pk.StatNature = value;
else if (format is 3 or 4)
pk.SetPIDNature(value);
else
pk.Nature = value;
}
/// <summary>
/// Copies <see cref="IBattleTemplate"/> details to the <see cref="PKM"/>.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="Set"><see cref="IBattleTemplate"/> details to copy from.</param>
public static void ApplySetDetails(this PKM pk, IBattleTemplate Set)
{
pk.Species = Math.Min(pk.MaxSpeciesID, Set.Species);
pk.Form = Set.Form;
if (Set.Moves[0] != 0)
pk.SetMoves(Set.Moves, true);
pk.ApplyHeldItem(Set.HeldItem, Set.Context);
pk.CurrentLevel = Set.Level;
pk.CurrentFriendship = Set.Friendship;
pk.SetIVs(Set.IVs);
if (pk is GBPKM gb)
/// <summary>
/// Makes a <see cref="PKM"/> not-shiny.
/// </summary>
/// <returns>Returns true if the <see cref="PKM"/> data was modified.</returns>
public bool SetUnshiny()
{
// In Generation 1/2 Format sets, when IVs are not specified with a Hidden Power set, we might not have the hidden power type.
// Under this scenario, just force the Hidden Power type.
if (Array.IndexOf(Set.Moves, (ushort)Move.HiddenPower) != -1 && pk.HPType != Set.HiddenPowerType)
{
if (Array.Exists(Set.IVs, static iv => iv >= 30))
pk.SetHiddenPower(Set.HiddenPowerType);
}
if (!pk.IsShiny)
return false;
// In Generation 1/2 Format sets, when EVs are not specified at all, it implies maximum EVs instead!
// Under this scenario, just apply maximum EVs (65535).
if (Array.TrueForAll(Set.EVs, static ev => ev == 0))
gb.MaxEVs();
pk.SetPIDGender(pk.Gender);
return true;
}
/// <summary>
/// Sets the <see cref="PKM.Nature"/> value, with special consideration for the <see cref="PKM.Format"/> values which derive the <see cref="PKM.Nature"/> value.
/// </summary>
/// <param name="nature">Desired <see cref="PKM.Nature"/> value to set.</param>
public void SetNature(Nature nature)
{
if (!nature.IsFixed)
nature = 0; // default valid
var format = pk.Format;
if (format >= 8)
pk.StatNature = nature;
else if (format is 3 or 4)
pk.SetPIDNature(nature);
else
pk.SetEVs(Set.EVs);
}
else
{
pk.SetEVs(Set.EVs);
pk.Nature = nature;
}
// IVs have no side effects such as hidden power type in gen 8
// therefore all specified IVs are deliberate and should not be Hyper Trained for pokemon met in gen 8
if (!pk.Gen8)
pk.SetSuggestedHyperTrainingData(Set.IVs);
if (ShowdownSetIVMarkings)
pk.SetMarkings();
pk.SetNickname(Set.Nickname);
pk.SetSaneGender(Set.Gender);
if (Legal.IsPPUpAvailable(pk))
pk.SetMaximumPPUps(Set.Moves);
if (pk.Format >= 3)
/// <summary>
/// Copies <see cref="IBattleTemplate"/> details to the <see cref="PKM"/>.
/// </summary>
/// <param name="set"><see cref="IBattleTemplate"/> details to copy from.</param>
public void ApplySetDetails(IBattleTemplate set)
{
pk.SetAbility(Set.Ability);
pk.SetNature(Set.Nature);
}
pk.Species = Math.Min(pk.MaxSpeciesID, set.Species);
pk.Form = set.Form;
pk.SetIsShiny(Set.Shiny);
pk.SetRandomEC();
ReadOnlySpan<ushort> moves = set.Moves;
if (moves[0] != 0)
pk.SetMoves(moves, true);
if (Legal.IsPPUpAvailable(pk))
pk.SetMaximumPPUps(moves);
if (pk is IAwakened a)
{
a.SetSuggestedAwakenedValues(pk);
if (pk is PB7 b)
pk.ApplyHeldItem(set.HeldItem, set.Context);
pk.CurrentLevel = set.Level;
pk.CurrentFriendship = set.Friendship;
ReadOnlySpan<int> ivs = set.IVs;
ReadOnlySpan<int> evs = set.EVs;
pk.SetIVs(ivs);
if (pk is GBPKM gb)
{
for (int i = 0; i < 6; i++)
b.SetEV(i, 0);
b.ResetCalculatedValues();
// In Generation 1/2 Format sets, when IVs are not specified with a Hidden Power set, we might not have the hidden power type.
// Under this scenario, just force the Hidden Power type.
if (moves.Contains((ushort)Move.HiddenPower) && gb.HPType != set.HiddenPowerType)
{
if (ivs.ContainsAny(30, 31))
gb.SetHiddenPower(set.HiddenPowerType);
}
// In Generation 1/2 Format sets, when EVs are not specified at all, it implies maximum EVs instead!
// Under this scenario, just apply maximum EVs (65535).
if (!evs.ContainsAnyExcept(0))
gb.MaxEVs();
else if (evs.ContainsAnyExceptInRange(0, 252)) // Any specified above 252
gb.SetEVs(evs);
else
gb.SetSqrtEVs(evs);
}
else
{
pk.SetEVs(evs);
}
// IVs have no side effects such as hidden power type in gen 8
// therefore all specified IVs are deliberate and should not be Hyper Trained for Pokémon met in gen 8
if (pk.Generation < 8)
pk.SetSuggestedHyperTrainingData(ivs);
if (ShowdownSetIVMarkings)
pk.SetMarkings();
pk.SetNickname(set.Nickname);
pk.SetSaneGender(set.Gender);
if (pk.Format >= 3)
{
pk.SetAbility(set.Ability);
pk.SetNature(set.Nature);
}
pk.SetIsShiny(set.Shiny);
pk.SetRandomEC();
if (pk is IAwakened a)
{
a.SetSuggestedAwakenedValues(pk);
if (pk is PB7 b)
{
for (int i = 0; i < 6; i++)
b.SetEV(i, 0);
b.ResetCalculatedValues();
}
}
if (pk is IGanbaru g)
g.SetSuggestedGanbaruValues(pk);
if (pk is IGigantamax c)
c.CanGigantamax = set.CanGigantamax;
if (pk is IDynamaxLevel d)
d.DynamaxLevel = d.GetSuggestedDynamaxLevel(pk, requested: set.DynamaxLevel);
if (pk is ITeraType tera)
{
var type = set.TeraType == MoveType.Any ? (MoveType)pk.PersonalInfo.Type1 : set.TeraType;
tera.SetTeraType(type);
}
if (pk is IMoveShop8Mastery s)
s.SetMoveShopFlags(set.Moves, pk);
if (ShowdownSetBehaviorNature && pk.Format >= 8)
pk.Nature = pk.StatNature;
var legal = new LegalityAnalysis(pk);
if (pk is ITechRecord t)
{
t.ClearRecordFlags();
t.SetRecordFlags(set.Moves, legal.Info.EvoChainsAllGens.Get(pk.Context));
}
if (pk is IPlusRecord plus && pk.PersonalInfo is IPermitPlus permit)
{
plus.ClearPlusFlags(permit.PlusCountTotal);
plus.SetPlusFlags(permit, legal, true, true);
}
if (legal.Parsed && !MoveResult.AllValid(legal.Info.Relearn))
pk.SetRelearnMoves(legal);
pk.ResetPartyStats();
pk.RefreshChecksum();
}
if (pk is IGanbaru g)
g.SetSuggestedGanbaruValues(pk);
if (pk is IGigantamax c)
c.CanGigantamax = Set.CanGigantamax;
if (pk is IDynamaxLevel d)
d.DynamaxLevel = d.GetSuggestedDynamaxLevel(pk, requested: Set.DynamaxLevel);
if (pk is ITeraType tera)
tera.SetTeraType(Set.TeraType);
if (pk is ITechRecord t)
/// <summary>
/// Sets the <see cref="PKM.HeldItem"/> value depending on the current format and the provided item index &amp; format.
/// </summary>
/// <param name="item">Held Item to apply</param>
/// <param name="context">Format required for importing</param>
public void ApplyHeldItem(int item, EntityContext context)
{
t.ClearRecordFlags();
t.SetRecordFlags(Set.Moves);
item = ItemConverter.GetItemForFormat(item, context, pk.Context);
pk.HeldItem = ((uint)item > pk.MaxItemID) ? 0 : item;
}
if (pk is IMoveShop8Mastery s)
s.SetMoveShopFlags(Set.Moves, pk);
if (ShowdownSetBehaviorNature && pk.Format >= 8)
pk.Nature = pk.StatNature;
/// <summary>
/// Sets one of the <see cref="EffortValues"/> based on its index within the array.
/// </summary>
/// <param name="index">Index to set to</param>
/// <param name="value">Value to set</param>
public int SetEV(int index, int value) => index switch
{
0 => pk.EV_HP = value,
1 => pk.EV_ATK = value,
2 => pk.EV_DEF = value,
3 => pk.EV_SPE = value,
4 => pk.EV_SPA = value,
5 => pk.EV_SPD = value,
_ => throw new ArgumentOutOfRangeException(nameof(index)),
};
var legal = new LegalityAnalysis(pk);
if (legal.Parsed && !MoveResult.AllValid(legal.Info.Relearn))
pk.SetRelearnMoves(legal.GetSuggestedRelearnMoves());
pk.ResetPartyStats();
pk.RefreshChecksum();
}
/// <summary>
/// Sets one of the <see cref="PKM.IVs"/> based on its index within the array.
/// </summary>
/// <param name="index">Index to set to</param>
/// <param name="value">Value to set</param>
public int SetIV(int index, int value) => index switch
{
0 => pk.IV_HP = value,
1 => pk.IV_ATK = value,
2 => pk.IV_DEF = value,
3 => pk.IV_SPE = value,
4 => pk.IV_SPA = value,
5 => pk.IV_SPD = value,
_ => throw new ArgumentOutOfRangeException(nameof(index)),
};
/// <summary>
/// Sets the <see cref="PKM.HeldItem"/> value depending on the current format and the provided item index &amp; format.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="item">Held Item to apply</param>
/// <param name="context">Format required for importing</param>
public static void ApplyHeldItem(this PKM pk, int item, EntityContext context)
{
item = ItemConverter.GetItemForFormat(item, context, pk.Context);
pk.HeldItem = ((uint)item > pk.MaxItemID) ? 0 : item;
}
/// <summary>
/// Fetches the highest value the provided <see cref="EffortValues"/> index can be while considering others.
/// </summary>
/// <param name="index">Index to fetch for</param>
/// <returns>Highest value the value can be.</returns>
public int GetMaximumEV(int index)
{
if (pk.Format < 3)
return EffortValues.Max12;
/// <summary>
/// Sets one of the <see cref="EffortValues"/> based on its index within the array.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="index">Index to set to</param>
/// <param name="value">Value to set</param>
public static int SetEV(this PKM pk, int index, int value) => index switch
{
0 => pk.EV_HP = value,
1 => pk.EV_ATK = value,
2 => pk.EV_DEF = value,
3 => pk.EV_SPE = value,
4 => pk.EV_SPA = value,
5 => pk.EV_SPD = value,
_ => throw new ArgumentOutOfRangeException(nameof(index)),
};
var sum = pk.EVTotal - pk.GetEV(index);
int remaining = EffortValues.Max510 - sum;
return Math.Clamp(remaining, 0, EffortValues.Max252);
}
/// <summary>
/// Sets one of the <see cref="PKM.IVs"/> based on its index within the array.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="index">Index to set to</param>
/// <param name="value">Value to set</param>
public static int SetIV(this PKM pk, int index, int value) => index switch
{
0 => pk.IV_HP = value,
1 => pk.IV_ATK = value,
2 => pk.IV_DEF = value,
3 => pk.IV_SPE = value,
4 => pk.IV_SPA = value,
5 => pk.IV_SPD = value,
_ => throw new ArgumentOutOfRangeException(nameof(index)),
};
/// <summary>
/// Fetches the highest value the provided <see cref="PKM.IVs"/>.
/// </summary>
/// <param name="index">Index to fetch for</param>
/// <param name="allow30">Causes the returned value to be dropped down -1 if the value is already at a maximum.</param>
/// <returns>Highest value the value can be.</returns>
public int GetMaximumIV(int index, bool allow30 = false)
{
if (pk.GetIV(index) == pk.MaxIV && allow30)
return pk.MaxIV - 1;
return pk.MaxIV;
}
/// <summary>
/// Fetches the highest value the provided <see cref="EffortValues"/> index can be while considering others.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="index">Index to fetch for</param>
/// <returns>Highest value the value can be.</returns>
public static int GetMaximumEV(this PKM pk, int index)
{
if (pk.Format < 3)
return ushort.MaxValue;
var sum = pk.EVTotal - pk.GetEV(index);
int remaining = 510 - sum;
return Math.Min(Math.Max(remaining, 0), 252);
}
/// <summary>
/// Fetches the highest value the provided <see cref="PKM.IVs"/>.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="index">Index to fetch for</param>
/// <param name="allow30">Causes the returned value to be dropped down -1 if the value is already at a maximum.</param>
/// <returns>Highest value the value can be.</returns>
public static int GetMaximumIV(this PKM pk, int index, bool allow30 = false)
{
if (pk.GetIV(index) == pk.MaxIV && allow30)
return pk.MaxIV - 1;
return pk.MaxIV;
}
/// <summary>
/// Force hatches a PKM by applying the current species name and a valid Met Location from the origin game.
/// </summary>
/// <param name="pk">PKM to apply hatch details to</param>
/// <param name="reHatch">Re-hatch already hatched <see cref="PKM"/> inputs</param>
public static void ForceHatchPKM(this PKM pk, bool reHatch = false)
{
if (!pk.IsEgg && !reHatch)
return;
pk.IsEgg = false;
pk.ClearNickname();
pk.CurrentFriendship = pk.PersonalInfo.BaseFriendship;
if (pk.IsTradedEgg)
pk.Egg_Location = pk.Met_Location;
var loc = EncounterSuggestion.GetSuggestedEggMetLocation(pk);
if (loc >= 0)
pk.Met_Location = loc;
pk.MetDate = DateTime.Today;
if (pk.Gen6)
pk.SetHatchMemory6();
}
/// <summary>
/// Force hatches a PKM by applying the current species name and a valid Met Location from the origin game.
/// </summary>
/// <param name="pk">PKM to apply hatch details to</param>
/// <param name="origin">Game the egg originated from</param>
/// <param name="dest">Game the egg is currently present on</param>
public static void SetEggMetData(this PKM pk, GameVersion origin, GameVersion dest)
{
bool traded = origin != dest;
var today = pk.MetDate = DateTime.Today;
pk.Egg_Location = EncounterSuggestion.GetSuggestedEncounterEggLocationEgg(pk.Generation, origin, traded);
pk.EggMetDate = today;
}
/// <summary>
/// Maximizes the <see cref="PKM.CurrentFriendship"/>. If the <see cref="PKM.IsEgg"/>, the hatch counter is set to 1.
/// </summary>
/// <param name="pk">PKM to apply hatch details to</param>
public static void MaximizeFriendship(this PKM pk)
{
if (pk.IsEgg)
pk.OT_Friendship = 1;
else
pk.CurrentFriendship = byte.MaxValue;
if (pk is ICombatPower pb)
pb.ResetCP();
}
/// <summary>
/// Maximizes the <see cref="PKM.CurrentLevel"/>. If the <see cref="PKM.IsEgg"/>, the <see cref="PKM"/> is ignored.
/// </summary>
/// <param name="pk">PKM to apply hatch details to</param>
public static void MaximizeLevel(this PKM pk)
{
if (pk.IsEgg)
return;
pk.CurrentLevel = 100;
if (pk is ICombatPower pb)
pb.ResetCP();
}
/// <summary>
/// Sets the <see cref="PKM.Nickname"/> to its default value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="la">Precomputed optional</param>
public static void SetDefaultNickname(this PKM pk, LegalityAnalysis la)
{
if (la.Parsed && la.EncounterOriginal is EncounterTrade {HasNickname: true} t)
pk.SetNickname(t.GetNickname(pk.Language));
else
/// <summary>
/// Force hatches a PKM by applying the current species name and a valid Met Location from the origin game.
/// </summary>
/// <param name="tr">Trainer to force hatch with if Version is not currently set.</param>
/// <param name="reHatch">Re-hatch already hatched <see cref="PKM"/> inputs</param>
public void ForceHatchPKM(ITrainerInfo? tr = null, bool reHatch = false)
{
if (!pk.IsEgg && !reHatch)
return;
pk.IsEgg = false;
pk.ClearNickname();
}
pk.OriginalTrainerFriendship = Math.Min(pk.OriginalTrainerFriendship, EggStateLegality.GetEggHatchFriendship(pk.Context));
if (pk.IsTradedEgg)
pk.EggLocation = pk.MetLocation;
if (pk.Version == 0)
pk.Version = EggStateLegality.GetEggHatchVersion(pk, tr?.Version ?? RecentTrainerCache.Version);
var loc = EncounterSuggestion.GetSuggestedEggMetLocation(pk);
if (loc != EncounterSuggestion.LocationNone)
pk.MetLocation = loc;
if (pk.Format >= 4)
pk.MetDate = EncounterDate.GetDate(pk.Context.Console);
if (pk.Gen6)
pk.SetHatchMemory6();
}
/// <summary>
/// Sets the <see cref="PKM.Nickname"/> to its default value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
public static void SetDefaultNickname(this PKM pk) => pk.SetDefaultNickname(new LegalityAnalysis(pk));
/// <summary>
/// Force hatches a PKM by applying the current species name and a valid Met Location from the origin game.
/// </summary>
/// <param name="origin">Game the egg originated from</param>
/// <param name="dest">Game the egg is currently present on</param>
public void SetEggMetData(GameVersion origin, GameVersion dest)
{
if (pk.Format < 4)
return;
private static readonly string[] PotentialUnicode = { "★☆☆☆", "★★☆☆", "★★★☆", "★★★★" };
private static readonly string[] PotentialNoUnicode = { "+", "++", "+++", "++++" };
var console = pk.Context.Console;
var date = EncounterDate.GetDate(console);
var today = pk.MetDate = date;
bool traded = origin != dest;
pk.EggLocation = EncounterSuggestion.GetSuggestedEncounterEggLocationEgg(pk.Generation, origin, traded);
pk.EggMetDate = today;
}
/// <summary>
/// Gets the Potential evaluation of the input <see cref="pk"/>.
/// </summary>
/// <param name="pk">Pokémon to analyze.</param>
/// <param name="unicode">Returned value is unicode or not</param>
/// <returns>Potential string</returns>
public static string GetPotentialString(this PKM pk, bool unicode = true)
{
var arr = unicode ? PotentialUnicode : PotentialNoUnicode;
return arr[pk.PotentialRating];
/// <summary>
/// Maximizes the <see cref="PKM.CurrentFriendship"/>. If the <see cref="PKM.IsEgg"/>, the hatch counter is set to 1.
/// </summary>
public void MaximizeFriendship()
{
if (pk.IsEgg)
pk.OriginalTrainerFriendship = 1;
else
pk.CurrentFriendship = byte.MaxValue;
if (pk is ICombatPower pb)
pb.ResetCP();
}
/// <summary>
/// Maximizes the <see cref="PKM.CurrentLevel"/>. If the <see cref="PKM.IsEgg"/>, the <see cref="PKM"/> is ignored.
/// </summary>
public void MaximizeLevel()
{
if (pk.IsEgg)
return;
pk.CurrentLevel = Experience.MaxLevel;
if (pk is ICombatPower pb)
pb.ResetCP();
}
/// <summary>
/// Sets the <see cref="PKM.Nickname"/> to its default value.
/// </summary>
/// <param name="la">Precomputed optional</param>
public void SetDefaultNickname(LegalityAnalysis la)
{
if (la is { Parsed: true, EncounterOriginal: IFixedNickname {IsFixedNickname: true} t })
pk.SetNickname(t.GetNickname(pk.Language));
else
pk.ClearNickname();
}
/// <summary>
/// Sets the <see cref="PKM.Nickname"/> to its default value.
/// </summary>
public void SetDefaultNickname() => pk.SetDefaultNickname(new LegalityAnalysis(pk));
/// <summary>
/// Gets the Location Name for the <see cref="PKM"/>
/// </summary>
/// <param name="eggmet">Location requested is the egg obtained location, not met location.</param>
/// <returns>Location string</returns>
public string GetLocationString(bool eggmet)
{
if (pk.Format < 2)
return string.Empty;
ushort location = eggmet ? pk.EggLocation : pk.MetLocation;
return GameInfo.GetLocationName(eggmet, location, pk.Format, pk.Generation, pk.Version);
}
}
// Extensions
/// <summary>
/// Gets the Location Name for the <see cref="PKM"/>
/// </summary>
/// <param name="pk">PKM to fetch data for</param>
/// <param name="eggmet">Location requested is the egg obtained location, not met location.</param>
/// <returns>Location string</returns>
public static string GetLocationString(this PKM pk, bool eggmet)
{
if (pk.Format < 2)
return string.Empty;
int location = eggmet ? pk.Egg_Location : pk.Met_Location;
return GameInfo.GetLocationName(eggmet, location, pk.Format, pk.Generation, (GameVersion)pk.Version);
public const char OptionNone = '\0';
/// <summary>
/// Gets a <see cref="PKM.EncryptionConstant"/> to match the requested option.
/// </summary>
public static uint GetComplicatedEC(ISpeciesForm pk, char option = OptionNone)
{
var species = pk.Species;
var form = pk.Form;
return GetComplicatedEC(species, form, option);
}
/// <inheritdoc cref="GetComplicatedEC(ISpeciesForm,char)"/>
public static uint GetComplicatedEC(ushort species, byte form, char option = OptionNone)
{
var rng = Util.Rand;
uint rand = rng.Rand32();
uint mod, noise;
if (species is >= (int)Species.Wurmple and <= (int)Species.Dustox)
{
mod = 10;
bool lower = option is '0' or 'B' or 'S' || WurmpleUtil.GetWurmpleEvoGroup(species) == 0;
noise = (lower ? 0u : 5u) + (uint)rng.Next(0, 5);
}
else if (species is (int)Species.Dunsparce or (int)Species.Dudunsparce or (int)Species.Tandemaus or (int)Species.Maushold)
{
mod = 100;
noise = species switch
{
// Retain requisite correlation to allow for evolving into this species too.
(int)Species.Dudunsparce => form == 1 ? 0 : (uint)rng.Next(1, 100), // 3 Segment
(int)Species.Maushold => form == 0 ? 0 : (uint)rng.Next(1, 100), // Family of 3
// Otherwise, check if one is preferred, and if not, just make it the more common outcome.
_ => option switch
{
'0' or '3' => 0u,
_ => (uint)rng.Next(1, 100),
},
};
}
else if (option is >= '0' and <= '5')
{
mod = 6;
noise = (uint)(option - '0');
}
else
{
return rand;
}
return unchecked(rand - (rand % mod) + noise);
}
}

View File

@ -1,5 +1,7 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
namespace PKHeX.Core;
@ -8,7 +10,34 @@ namespace PKHeX.Core;
/// </summary>
public sealed class TrainerDatabase
{
private readonly Dictionary<GameVersion, List<ITrainerInfo>> Database = new();
private readonly Dictionary<GameVersion, List<ITrainerInfo>> Database = [];
/// <summary>
/// Gets the number of unique versions in the database.
/// </summary>
public int CountVersions => Database.Count;
/// <summary>
/// Gets the number of trainers in the database.
/// </summary>
public int CountTrainers => Database.Sum(z => z.Value.Count);
/// <summary>
/// Checks if the database contains any trainers for the specified <see cref="version"/>.
/// </summary>
/// <param name="version"></param>
public bool HasVersion(GameVersion version) => Database.ContainsKey(version);
/// <summary>
/// Gets all trainers from the database for the specified saved <see cref="version"/>.
/// </summary>
/// <param name="version">Saved Version to fetch trainers for</param>
public ReadOnlySpan<ITrainerInfo> GetTrainers(GameVersion version)
{
if (Database.TryGetValue(version, out var list))
return CollectionsMarshal.AsSpan(list);
return default;
}
/// <summary>
/// Fetches an appropriate trainer based on the requested <see cref="version"/>.
@ -16,81 +45,77 @@ public sealed class TrainerDatabase
/// <param name="version">Version the trainer should originate from</param>
/// <param name="language">Language to request for</param>
/// <returns>Null if no trainer found for this version.</returns>
public ITrainerInfo? GetTrainer(int version, LanguageID? language = null) => GetTrainer((GameVersion)version, language);
/// <summary>
/// Fetches an appropriate trainer based on the requested <see cref="ver"/>.
/// </summary>
/// <param name="ver">Version the trainer should originate from</param>
/// <param name="language">Language to request for</param>
/// <returns>Null if no trainer found for this version.</returns>
public ITrainerInfo? GetTrainer(GameVersion ver, LanguageID? language = null)
public ITrainerInfo? GetTrainer(GameVersion version, LanguageID? language = null)
{
if (ver <= 0)
if (version <= 0)
return null;
if (!ver.IsValidSavedVersion())
return GetTrainerFromGroup(ver, language);
if (!version.IsValidSavedVersion())
return GetTrainerFromGroup(version, language);
if (Database.TryGetValue(ver, out var list))
return GetRandomChoice(list);
if (Database.TryGetValue(version, out var list))
return list[GetRandomIndex(list.Count)];
return null;
}
private static T GetRandomChoice<T>(IReadOnlyList<T> list)
{
if (list.Count == 1)
return list[0];
return list[Util.Rand.Next(list.Count)];
}
private static int GetRandomIndex(int count) => count == 1 ? 0 : Util.Rand.Next(count);
/// <summary>
/// Fetches an appropriate trainer based on the requested <see cref="ver"/> group.
/// Fetches an appropriate trainer based on the requested <see cref="version"/> group.
/// </summary>
/// <param name="ver">Version the trainer should originate from</param>
/// <param name="version">Version the trainer should originate from</param>
/// <param name="lang">Language to request for</param>
/// <returns>Null if no trainer found for this version.</returns>
private ITrainerInfo? GetTrainerFromGroup(GameVersion ver, LanguageID? lang = null)
{
var possible = Database.Where(z => ver.Contains(z.Key)).ToList();
if (lang != null)
{
possible = possible.Select(z =>
{
var filtered = z.Value.Where(x => x.Language == (int)lang).ToList();
return new KeyValuePair<GameVersion, List<ITrainerInfo>>(z.Key, filtered);
}).Where(z => z.Value.Count != 0).ToList();
}
return GetRandomTrainer(possible);
}
/// <summary>
/// Fetches an appropriate trainer based on the requested <see cref="generation"/>.
/// </summary>
/// <param name="generation">Generation the trainer should inhabit</param>
/// <param name="lang">Language to request for</param>
/// <returns>Null if no trainer found for this version.</returns>
public ITrainerInfo? GetTrainerFromGen(int generation, LanguageID? lang = null)
{
var possible = Database.Where(z => z.Key.GetGeneration() == generation).ToList();
if (lang != null)
{
possible = possible.Select(z =>
{
var filtered = z.Value.Where(x => x.Language == (int)lang).ToList();
return new KeyValuePair<GameVersion, List<ITrainerInfo>>(z.Key, filtered);
}).Where(z => z.Value.Count != 0).ToList();
}
return GetRandomTrainer(possible);
}
private static ITrainerInfo? GetRandomTrainer(IReadOnlyList<KeyValuePair<GameVersion, List<ITrainerInfo>>> possible)
private ITrainerInfo? GetTrainerFromGroup(GameVersion version, LanguageID? lang = null)
{
var possible = Database.Where(z => version.Contains(z.Key)).ToList();
if (possible.Count == 0)
return null;
var group = GetRandomChoice(possible);
return GetRandomChoice(group.Value);
if (lang is not null)
{
possible = possible.Select(z =>
{
var filtered = z.Value.Where(x => x.Language == (int)lang).ToList();
return new KeyValuePair<GameVersion, List<ITrainerInfo>>(z.Key, filtered);
}).Where(z => z.Value.Count != 0).ToList();
}
var span = CollectionsMarshal.AsSpan(possible);
return GetRandomTrainer(span);
}
/// <summary>
/// Fetches an appropriate trainer based on the requested <see cref="context"/>.
/// </summary>
/// <param name="context">Generation the trainer should inhabit</param>
/// <param name="lang">Language to request for</param>
/// <returns>Null if no trainer found for this version.</returns>
public ITrainerInfo? GetTrainerFromContext(EntityContext context, LanguageID? lang = null)
{
var possible = Database.Where(z => z.Key.Context == context).ToList();
if (possible.Count == 0)
return null;
if (lang is not null)
{
possible = possible.Select(z =>
{
var filtered = z.Value.Where(x => x.Language == (int)lang).ToList();
return new KeyValuePair<GameVersion, List<ITrainerInfo>>(z.Key, filtered);
}).Where(z => z.Value.Count != 0).ToList();
}
var span = CollectionsMarshal.AsSpan(possible);
return GetRandomTrainer(span);
}
private static ITrainerInfo? GetRandomTrainer(ReadOnlySpan<KeyValuePair<GameVersion, List<ITrainerInfo>>> possible)
{
if (possible.Length == 0)
return null;
var group = possible[GetRandomIndex(possible.Length)];
var span = group.Value;
return span[GetRandomIndex(span.Count)];
}
/// <summary>
@ -99,12 +124,10 @@ private static T GetRandomChoice<T>(IReadOnlyList<T> list)
/// <param name="trainer">Trainer details to add.</param>
public void Register(ITrainerInfo trainer)
{
var ver = (GameVersion)trainer.Game;
if (ver <= 0 && trainer is SaveFile s)
ver = s.Version;
if (!Database.TryGetValue(ver, out var list))
var version = trainer.Version;
if (!Database.TryGetValue(version, out var list))
{
Database.Add(ver, new List<ITrainerInfo> { trainer });
Database.Add(version, [trainer]);
return;
}
@ -127,21 +150,36 @@ public void Register(ITrainerInfo trainer)
/// <remarks>A copy of the object will be made to prevent modifications, just in case.</remarks>
public void RegisterCopy(ITrainerInfo info) => Register(new SimpleTrainerInfo(info));
private static ITrainerInfo GetTrainerReference(PKM pk)
private static SimpleTrainerInfo GetTrainerReference(PKM pk)
{
var result = new SimpleTrainerInfo((GameVersion)pk.Version)
var (cr, c, r) = GetRegion3DS(pk);
return GetTrainerReference(pk, cr, c, r);
}
private static SimpleTrainerInfo GetTrainerReference(PKM pk, byte cr, byte c, byte r) => new(pk.Version)
{
TID16 = pk.TID16,
SID16 = pk.SID16,
OT = pk.OriginalTrainerName,
Gender = pk.OriginalTrainerGender,
Language = pk.Language,
Generation = pk.Generation,
ConsoleRegion = cr,
Country = c,
Region = r,
};
private static (byte ConsoleRegion, byte Country, byte Region) GetRegion3DS(PKM pk)
{
if (pk is IRegionOriginReadOnly x)
return (x.ConsoleRegion, x.Country, x.Region);
if (pk.Version.IsGen6() || pk.Version.IsGen7())
{
TID = pk.TID, SID = pk.SID, OT = pk.OT_Name, Gender = pk.OT_Gender,
Language = pk.Language,
Generation = pk.Generation,
};
if (pk is IRegionOrigin r)
r.CopyRegionOrigin(result);
else
result.SetDefaultRegionOrigins();
return result;
if (pk.Language == (int)LanguageID.Japanese)
return (0, 1, 0);
return (1, 7, 49);
}
return default;
}
/// <summary>

View File

@ -1,5 +1,4 @@
using System;
using System.Runtime.CompilerServices;
namespace PKHeX.Core;
@ -16,7 +15,7 @@ public static class HiddenPower
/// <param name="context">Generation format</param>
public static int GetType(ReadOnlySpan<int> IVs, EntityContext context)
{
if (context.Generation() <= 2)
if (context.IsEraGameBoy)
return GetTypeGB(IVs);
return GetType(IVs);
}
@ -31,11 +30,84 @@ public static int GetType(ReadOnlySpan<int> IVs)
int hp = 0;
for (int i = 0; i < 6; i++)
hp |= (IVs[i] & 1) << i;
hp *= 0xF;
hp /= 0x3F;
return hp;
return SixBitType[hp];
}
/// <summary>
/// Gets the current Hidden Power Type of the input IVs for Generations 3+
/// </summary>
/// <param name="u32">32-bit value of the IVs</param>
/// <returns>Hidden Power Type of the IVs</returns>
public static int GetType(uint u32)
{
uint hp = 0;
for (int i = 0; i < 6; i++)
{
hp |= (u32 & 1) << i;
u32 >>= 5;
}
return SixBitType[(int)hp];
}
/// <summary>
/// Gets the current Hidden Power Type of the input IVs for Generations 3+
/// </summary>
/// <param name="u32">32-bit value of the IVs</param>
/// <remarks>IVs are stored in reverse order in the 32-bit value</remarks>
/// <returns>Hidden Power Type of the IVs</returns>
public static int GetTypeBigEndian(uint u32)
{
uint hp = 0;
for (int i = 0; i < 6; i++)
{
hp |= (u32 & 1) << (5 - i);
u32 >>= 5;
}
return SixBitType[(int)hp];
}
/// <summary>
/// Count of unique Hidden Power Types
/// </summary>
public const int TypeCount = 16;
/// <summary>
/// Checks if the input Hidden Power Type is not one of the 15 valid types.
/// </summary>
/// <param name="type">Hidden Power Type</param>
/// <returns><see langword="true"/> if the input Hidden Power Type is not one of the 15 valid types.</returns>
public static bool IsInvalidType(int type) => (uint)type >= TypeCount;
/// <summary>
/// Gets the Type Name index of the input Hidden Power Type
/// </summary>
/// <param name="type">Fetched Hidden Power Type</param>
/// <param name="index">Type Name index</param>
/// <returns>True if the input Hidden Power Type is valid</returns>
public static bool TryGetTypeIndex(int type, out byte index)
{
if (IsInvalidType(type))
{
index = 0;
return false;
}
index = (byte)(type + 1); // Normal type is not a valid Hidden Power type
return true;
}
private static ReadOnlySpan<byte> SixBitType =>
[
// (low-bit mash) * 15 / 63
00, 00, 00, 00, 00, 01, 01, 01,
01, 02, 02, 02, 02, 03, 03, 03,
03, 04, 04, 04, 04, 05, 05, 05,
05, 05, 06, 06, 06, 06, 07, 07,
07, 07, 08, 08, 08, 08, 09, 09,
09, 09, 10, 10, 10, 10, 10, 11,
11, 11, 11, 12, 12, 12, 12, 13,
13, 13, 13, 14, 14, 14, 14, 15,
];
/// <summary>
/// Gets the current Hidden Power Type of the input <see cref="IVs"/> for Generations 1 &amp; 2
/// </summary>
@ -48,6 +120,9 @@ public static int GetTypeGB(ReadOnlySpan<int> IVs)
return ((atk & 3) << 2) | (def & 3);
}
/// <inheritdoc cref="GetTypeGB(ReadOnlySpan{int})"/>
public static int GetTypeGB(ushort u16) => ((u16 >> 10) & 0b1100) | ((u16 >> 8) & 0b11);
/// <summary>
/// Modifies the provided <see cref="IVs"/> to have the requested <see cref="hiddenPowerType"/> for Generations 1 &amp; 2
/// </summary>
@ -56,11 +131,19 @@ public static int GetTypeGB(ReadOnlySpan<int> IVs)
/// <returns>True if the Hidden Power of the <see cref="IVs"/> is obtained, with or without modifications</returns>
public static bool SetTypeGB(int hiddenPowerType, Span<int> IVs)
{
IVs[1] = (IVs[1] & ~3) | (hiddenPowerType >> 2);
IVs[2] = (IVs[2] & ~3) | (hiddenPowerType & 3);
IVs[1] = (IVs[1] & 0b1100) | (hiddenPowerType >> 2);
IVs[2] = (IVs[2] & 0b1100) | (hiddenPowerType & 3);
return true;
}
/// <inheritdoc cref="SetTypeGB(int, Span{int})"/>
public static ushort SetTypeGB(int hiddenPowerType, ushort current)
{
// Extract bits from ATK and DEF.
var u16 = ((hiddenPowerType & 0b1100) << 10) | ((hiddenPowerType & 0b11) << 8);
return (ushort)((current & 0b1100_1100_1111_1111) | u16);
}
/// <summary>
/// Modifies the provided <see cref="IVs"/> to have the requested <see cref="hiddenPowerType"/>.
/// </summary>
@ -70,7 +153,7 @@ public static bool SetTypeGB(int hiddenPowerType, Span<int> IVs)
/// <returns>True if the Hidden Power of the <see cref="IVs"/> is obtained, with or without modifications</returns>
public static bool SetIVsForType(int hiddenPowerType, Span<int> IVs, EntityContext context)
{
if (context.Generation() <= 2)
if (context.IsEraGameBoy)
return SetTypeGB(hiddenPowerType, IVs);
return SetIVsForType(hiddenPowerType, IVs);
}
@ -83,6 +166,10 @@ public static bool SetIVsForType(int hiddenPowerType, Span<int> IVs, EntityConte
/// <returns>True if the Hidden Power of the <see cref="IVs"/> is obtained, with or without modifications</returns>
public static bool SetIVsForType(int hpVal, Span<int> IVs)
{
int current = GetType(IVs);
if (current == hpVal)
return true; // no mods necessary
int flawlessCount = IVs.Count(31);
if (flawlessCount == 0)
return false;
@ -93,101 +180,116 @@ public static bool SetIVsForType(int hpVal, Span<int> IVs)
return true;
}
int current = GetType(IVs);
if (current == hpVal)
return true; // no mods necessary
// Required HP type doesn't match IVs. Make currently-flawless IVs flawed.
Span<int> scratch = stackalloc int[IVs.Length];
Span<int> result = stackalloc int[IVs.Length];
var success = GetSuggestedHiddenPowerIVs(hpVal, IVs, scratch, result);
if (!success)
var bits = GetSuggestedHiddenPowerIVs(hpVal, IVs);
if (bits == NoResult)
return false; // can't force hidden power?
// set IVs back to array
result.CopyTo(IVs);
ForceLowBits(IVs, bits);
return true;
}
// Non-recursive https://en.wikipedia.org/wiki/Heap%27s_algorithm
private static bool GetSuggestedHiddenPowerIVs(int hpVal, ReadOnlySpan<int> original, Span<int> ivs, Span<int> best)
private const byte NoResult = byte.MaxValue;
private static byte GetSuggestedHiddenPowerIVs(int hpVal, ReadOnlySpan<int> IVs)
{
const int max = 31;
// Iterate through all bit combinations that yield our Hidden Power Type.
// There's at most 5 we need to check, so brute force is fine.
// Prefer the least amount of IVs changed (31 -> 30).
// Get a list of indexes that can be mutated
Span<int> indexes = stackalloc int[original.Length];
int flaw = 0;
for (int i = 0; i < original.Length; i++)
{
if (original[i] == max)
indexes[flaw++] = i;
}
indexes = indexes[..flaw];
Span<int> c = stackalloc int[indexes.Length];
// Get the starting index from our 64 possible bit states.
int index = SixBitType.IndexOf((byte)hpVal);
if (index == -1)
return NoResult;
int mutated = c.Length + 1; // result tracking
for (int i = 1; i < c.Length;)
var bestIndex = NoResult;
var bestIndexFlaws = 6;
do
{
ref int ci = ref c[i];
if (i <= ci) // Reset the state and simulate popping the stack by incrementing the pointer.
{
ci = 0;
++i;
var flaws = GetFlawedBitCount(IVs, index);
if (flaws >= bestIndexFlaws)
continue;
}
var x = (i & 1) * ci; // if lowest bit set, ci : 0 (branch-less)
Swap(ref indexes[i], ref indexes[x]);
// Inlined continuance check
original.CopyTo(ivs);
var q = Math.Min(indexes.Length, mutated);
for (var j = 0; j < q; j++)
{
ivs[indexes[j]] ^= 1;
if (hpVal != GetType(ivs))
continue;
var ct = j + 1;
if (ct >= mutated)
break; // any further flaws are always worse
mutated = ct;
ivs.CopyTo(best);
if (j == 0) // nothing will be better than only 1 flaw
return true;
break; // any further flaws are always worse
}
ci++;
i = 1;
}
return mutated <= c.Length; // did we actually find a suitable result?
bestIndex = (byte)index;
bestIndexFlaws = flaws;
} while (++index < SixBitType.Length && SixBitType[index] == hpVal);
return bestIndex;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Swap<T>(ref T a, ref T b) => (a, b) = (b, a);
private static int GetFlawedBitCount(ReadOnlySpan<int> ivs, int bitValue)
{
const int max = 31;
int flaws = 0;
for (int i = 0; i < ivs.Length; i++)
{
var iv = ivs[i];
if ((iv & 1) == (bitValue & (1 << i)))
continue; // ok
if (iv != max)
return NoResult;
flaws++;
}
return flaws;
}
/// <summary>Calculate the Hidden Power Type of the entered IVs.</summary>
/// <param name="type">Hidden Power Type</param>
/// <param name="ivs">Individual Values (H/A/B/S/C/D)</param>
/// <param name="context">Generation specific format</param>
public static void SetIVs(int type, Span<int> ivs, EntityContext context = PKX.Context)
public static void SetIVs(int type, Span<int> ivs, EntityContext context = Latest.Context)
{
if (context.Generation() <= 2)
if (context.IsEraGameBoy)
{
ivs[1] = (ivs[1] & ~3) | (type >> 2);
ivs[2] = (ivs[2] & ~3) | (type & 3);
ivs[1] = (ivs[1] & 0b1100) | (type >> 2);
ivs[2] = (ivs[2] & 0b1100) | (type & 3);
}
else
{
var bits = DefaultLowBits[type];
for (int i = 0; i < 6; i++)
ivs[i] = (ivs[i] & 0x1E) + ((bits >> i) & 1);
ForceLowBits(ivs, DefaultLowBits[type]);
}
}
private static void ForceLowBits(Span<int> ivs, byte bits)
{
for (int i = 0; i < ivs.Length; i++)
ivs[i] = (ivs[i] & 0b11110) | ((bits >> i) & 1);
}
/// <inheritdoc cref="SetIVs(int,Span{int},EntityContext)"/>
public static uint SetIVs(int type, uint ivs)
{
var bits = DefaultLowBits[type];
for (int i = 0; i < 6; i++)
{
var bit = (bits >> i) & 1;
var bitIndex = i * 5;
var mask = (1u << bitIndex);
if (bit == 0)
ivs &= ~mask;
else
ivs |= mask;
}
return ivs;
}
/// <inheritdoc cref="SetIVs(int,uint)"/>
/// <remarks>IVs are stored in reverse order in the 32-bit value</remarks>
public static uint SetIVsBigEndian(int type, uint ivs)
{
var bits = DefaultLowBits[type];
for (int i = 0; i < 6; i++)
{
var bit = (bits >> i) & 1;
var bitIndex = (5 - i) * 5;
var mask = (1u << bitIndex);
if (bit == 0)
ivs &= ~mask;
else
ivs |= mask;
}
return ivs;
}
/// <summary>
/// Hidden Power IV values (even or odd) to achieve a specified Hidden Power Type
/// </summary>
@ -196,8 +298,8 @@ public static void SetIVs(int type, Span<int> ivs, EntityContext context = PKX.C
/// These are just precomputed for fast modification.
/// Individual Values (H/A/B/S/C/D)
/// </remarks>
public static readonly byte[] DefaultLowBits =
{
public static ReadOnlySpan<byte> DefaultLowBits =>
[
0b000011, // Fighting
0b001000, // Flying
0b001011, // Poison
@ -214,5 +316,14 @@ public static void SetIVs(int type, Span<int> ivs, EntityContext context = PKX.C
0b111001, // Ice
0b111101, // Dragon
0b111111, // Dark
};
];
/// <summary>
/// Gets the suggested low-bits for the input Hidden Power Type
/// </summary>
public static byte GetLowBits(int type)
{
var arr = DefaultLowBits;
return (uint)type < arr.Length ? arr[type] : (byte)0;
}
}

View File

@ -1,4 +1,4 @@
namespace PKHeX.Core;
namespace PKHeX.Core;
/// <summary>
/// Simple interface representing a <see cref="PKM"/> viewer.
@ -44,4 +44,10 @@ public interface IPKMView
/// <param name="focus">Cause the viewer to give focus to itself.</param>
/// <param name="skipConversionCheck">Cause the viewer to skip converting the data. Faster if it is known that the format is the same as the previous format.</param>
void PopulateFields(PKM pk, bool focus = true, bool skipConversionCheck = false);
/// <summary>
/// Messages back that the entity has been saved.
/// </summary>
/// <param name="pk">Pokémon data that was saved.</param>
void NotifyWasExported(PKM pk);
}

View File

@ -1,4 +1,4 @@
namespace PKHeX.Core;
namespace PKHeX.Core;
/// <summary>
/// Plugin interface used by an editor to notify third-party code providers.
@ -26,6 +26,13 @@ public interface IPlugin
/// </summary>
void NotifySaveLoaded();
/// <summary>
/// Notifies the plugin that the display language has changed.
/// </summary>
/// <param name="language">Short code for language name</param>
/// <remarks>Useful to translate controls if any added.</remarks>
void NotifyDisplayLanguageChanged(string language) { }
/// <summary>
/// Attempts to load a file using the plugin.
/// </summary>

View File

@ -9,16 +9,14 @@ public interface ISpriteBuilder<T>
/// <summary>
/// Gets a sprite using the requested parameters.
/// </summary>
T GetSprite(ushort species, byte form, int gender, uint formarg, int heldItem, bool isEgg, Shiny shiny,
int generation = -1,
SpriteBuilderTweak tweak = SpriteBuilderTweak.None);
T GetSprite(ushort species, byte form, byte gender, uint formarg, int heldItem, bool isEgg, Shiny shiny,
EntityContext context = EntityContext.None);
/// <summary>
/// Revises the sprite using the requested parameters.
/// </summary>
T GetSprite(T baseSprite, ushort species, int heldItem, bool isEgg, Shiny shiny,
int generation = -1,
SpriteBuilderTweak tweak = SpriteBuilderTweak.None);
EntityContext context = EntityContext.None);
/// <summary>
/// Initializes the implementation with the context details from the <see cref="sav"/>.

View File

@ -8,12 +8,12 @@ public static class LocationEdits
/// <summary>
/// Gets the "None" location index for a specific <see cref="PKM"/> context.
/// </summary>
public static int GetNoneLocation(PKM pk) => GetNoneLocation(pk.Context);
public static ushort GetNoneLocation(PKM pk) => GetNoneLocation(pk.Context);
/// <summary>
/// Gets the "None" location index for a specific <see cref="PKM"/> context.
/// </summary>
public static int GetNoneLocation(EntityContext context) => context switch
public static ushort GetNoneLocation(EntityContext context) => context switch
{
EntityContext.Gen8b => Locations.Default8bNone,
_ => 0,

View File

@ -1,4 +1,4 @@
using System;
using System;
using static PKHeX.Core.NatureAmpRequest;
namespace PKHeX.Core;
@ -8,43 +8,92 @@ namespace PKHeX.Core;
/// </summary>
public static class NatureAmp
{
/// <summary>
/// Mutate the nature amp indexes to match the request
/// </summary>
/// <param name="type">Request type to modify the provided <see cref="statIndex"/></param>
/// <param name="statIndex">Stat Index to mutate</param>
/// <param name="currentNature">Current nature to derive the current amps from</param>
/// <returns>New nature value</returns>
public static int GetNewNature(this NatureAmpRequest type, int statIndex, int currentNature)
extension(NatureAmpRequest type)
{
if (currentNature > (int)Nature.Quirky)
return -1;
var (up, dn) = GetNatureModification(currentNature);
return GetNewNature(type, statIndex, up, dn);
}
/// <inheritdoc cref="GetNewNature(PKHeX.Core.NatureAmpRequest,int,int)"/>
public static int GetNewNature(NatureAmpRequest type, int statIndex, int up, int dn)
{
//
switch (type)
/// <summary>
/// Mutate the nature amp indexes to match the request
/// </summary>
/// <param name="statIndex">Stat Index to mutate</param>
/// <param name="currentNature">Current nature to derive the current amps from</param>
/// <returns>New nature value</returns>
public Nature GetNewNature(int statIndex, Nature currentNature)
{
case Increase when up != statIndex:
up = statIndex;
break;
case Decrease when dn != statIndex:
dn = statIndex;
break;
case Neutral when up != statIndex && dn != statIndex:
up = dn = statIndex;
break;
default:
return -1; // failure
if ((uint)currentNature >= NatureCount)
return Nature.Random;
var (up, dn) = currentNature.GetNatureModification();
return type.GetNewNature(statIndex, up, dn);
}
return CreateNatureFromAmps(up, dn);
/// <inheritdoc cref="GetNewNature(NatureAmpRequest,int,Nature)"/>
public Nature GetNewNature(int statIndex, int up, int dn)
{
//
switch (type)
{
case Increase when up != statIndex:
up = statIndex;
break;
case Decrease when dn != statIndex:
dn = statIndex;
break;
case Neutral when up != statIndex && dn != statIndex:
up = dn = statIndex;
break;
default:
return Nature.Random; // failure
}
return CreateNatureFromAmps(up, dn);
}
}
extension(Nature nature)
{
/// <summary>
/// Decompose the nature to the two stat indexes that are modified
/// </summary>
public (int up, int dn) GetNatureModification()
{
var up = ((byte)nature / 5);
var dn = ((byte)nature % 5);
return (up, dn);
}
/// <inheritdoc cref="IsNeutralOrInvalid(Nature, int, int)"/>
public bool IsNeutralOrInvalid()
{
var (up, dn) = nature.GetNatureModification();
return nature.IsNeutralOrInvalid(up, dn);
}
/// <summary>
/// Checks if the nature is out of range or the stat amplifications are not neutral.
/// </summary>
/// <param name="up">Increased stat</param>
/// <param name="dn">Decreased stat</param>
/// <returns>True if nature modification values are equal or the Nature is out of range.</returns>
public bool IsNeutralOrInvalid(int up, int dn)
{
return up == dn || (byte)nature >= 25; // invalid
}
/// <summary>
/// Updates stats according to the specified nature.
/// </summary>
/// <param name="stats">Current stats to amplify if appropriate</param>
public void ModifyStatsForNature(Span<ushort> stats)
{
var (up, dn) = nature.GetNatureModification();
if (nature.IsNeutralOrInvalid(up, dn))
return;
ref var upStat = ref stats[up + 1];
ref var dnStat = ref stats[dn + 1];
upStat = (ushort)((upStat * 11) / 10);
dnStat = (ushort)((dnStat * 9) / 10);
}
}
/// <summary>
@ -53,54 +102,69 @@ public static int GetNewNature(NatureAmpRequest type, int statIndex, int up, int
/// <param name="up">Increased stat</param>
/// <param name="dn">Decreased stat</param>
/// <returns>Nature</returns>
public static int CreateNatureFromAmps(int up, int dn)
public static Nature CreateNatureFromAmps(int up, int dn)
{
if ((uint)up > 5 || (uint)dn > 5)
return Nature.Random;
return (Nature)((up * 5) + dn);
}
/// <summary>
/// Nature Amplification Table
/// </summary>
/// <remarks>-1 is 90%, 0 is 100%, 1 is 110%.</remarks>
public static ReadOnlySpan<sbyte> Table =>
[
0, 0, 0, 0, 0, // Hardy
1,-1, 0, 0, 0, // Lonely
1, 0, 0, 0,-1, // Brave
1, 0,-1, 0, 0, // Adamant
1, 0, 0,-1, 0, // Naughty
-1, 1, 0, 0, 0, // Bold
0, 0, 0, 0, 0, // Docile
0, 1, 0, 0,-1, // Relaxed
0, 1,-1, 0, 0, // Impish
0, 1, 0,-1, 0, // Lax
-1, 0, 0, 0, 1, // Timid
0,-1, 0, 0, 1, // Hasty
0, 0, 0, 0, 0, // Serious
0, 0,-1, 0, 1, // Jolly
0, 0, 0,-1, 1, // Naive
-1, 0, 1, 0, 0, // Modest
0,-1, 1, 0, 0, // Mild
0, 0, 1, 0,-1, // Quiet
0, 0, 0, 0, 0, // Bashful
0, 0, 1,-1, 0, // Rash
-1, 0, 0, 1, 0, // Calm
0,-1, 0, 1, 0, // Gentle
0, 0, 0, 1,-1, // Sassy
0, 0,-1, 1, 0, // Careful
0, 0, 0, 0, 0, // Quirky
];
private const byte NatureCount = 25;
private const int AmpWidth = 5;
public static int AmplifyStat(Nature nature, int index, int initial) => GetNatureAmp(nature, index) switch
{
1 => 110 * initial / 100, // 110%
-1 => 90 * initial / 100, // 90%
_ => initial,
};
private static sbyte GetNatureAmp(Nature nature, int index)
{
if ((uint)nature >= NatureCount)
return -1;
return (up * 5) + dn;
var amps = GetAmps(nature);
return amps[index];
}
/// <summary>
/// Decompose the nature to the two stat indexes that are modified
/// </summary>
public static (int up, int dn) GetNatureModification(int nature)
public static ReadOnlySpan<sbyte> GetAmps(Nature nature)
{
var up = (nature / 5) + 1;
var dn = (nature % 5) + 1;
return (up, dn);
}
/// <summary>
/// Checks if the nature is out of range or the stat amplifications are not neutral.
/// </summary>
/// <param name="nature">Nature</param>
/// <param name="up">Increased stat</param>
/// <param name="dn">Decreased stat</param>
/// <returns>True if nature modification values are equal or the Nature is out of range.</returns>
public static bool IsNeutralOrInvalid(int nature, int up, int dn)
{
return up == dn || nature >= 25; // invalid
}
/// <inheritdoc cref="IsNeutralOrInvalid(int, int, int)"/>
public static bool IsNeutralOrInvalid(int nature)
{
var (up, dn) = GetNatureModification(nature);
return IsNeutralOrInvalid(nature, up, dn);
}
/// <summary>
/// Updates stats according to the specified nature.
/// </summary>
/// <param name="stats">Current stats to amplify if appropriate</param>
/// <param name="nature">Nature</param>
public static void ModifyStatsForNature(Span<ushort> stats, int nature)
{
var (up, dn) = GetNatureModification(nature);
if (IsNeutralOrInvalid(nature, up, dn))
return;
stats[up] *= 11; stats[up] /= 10;
stats[dn] *= 9; stats[dn] /= 10;
if ((uint)nature >= NatureCount)
nature = 0;
return Table.Slice(AmpWidth * (byte)nature, AmpWidth);
}
}

View File

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using static PKHeX.Core.MessageStrings;
@ -9,16 +9,32 @@ namespace PKHeX.Core;
/// </summary>
public static class EntitySuggestionUtil
{
public static List<string> GetMetLocationSuggestionMessage(PKM pk, int level, int location, int minimumLevel)
public static List<string> GetMetLocationSuggestionMessage(PKM pk, byte level, ushort location, int minimumLevel, IEncounterable? enc)
{
var suggestion = new List<string> { MsgPKMSuggestionStart };
if (pk.Format >= 3)
{
var metList = GameInfo.GetLocationList((GameVersion)pk.Version, pk.Context, egg: false);
var metList = GameInfo.GetLocationList(pk.Version, pk.Context, egg: false);
var locationName = metList.First(loc => loc.Value == location).Text;
suggestion.Add($"{MsgPKMSuggestionMetLocation} {locationName}");
suggestion.Add($"{MsgPKMSuggestionMetLevel} {level}");
}
else if (pk is ICaughtData2)
{
var metList = GameInfo.GetLocationList(GameVersion.C, pk.Context);
string locationName;
if (enc?.Version.Contains(GameVersion.C) == true)
{
locationName = metList.First(loc => loc.Value == location).Text;
}
else
{
locationName = metList[0].Text;
level = 0;
}
suggestion.Add($"{MsgPKMSuggestionMetLocation} {locationName}");
suggestion.Add($"{MsgPKMSuggestionMetLevel} {level}");
}
if (pk.CurrentLevel < minimumLevel)
suggestion.Add($"{MsgPKMSuggestionLevel} {minimumLevel}");
return suggestion;

View File

@ -1,117 +1,122 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace PKHeX.Core;
/// <summary>
/// Bindable summary object that can fetch strings that summarize a <see cref="PKM"/>.
/// </summary>
public class EntitySummary // do NOT seal, allow inheritance
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
public class EntitySummary : IFatefulEncounterReadOnly // do NOT seal, allow inheritance
{
private static readonly IReadOnlyList<string> GenderSymbols = GameInfo.GenderSymbolASCII;
private readonly GameStrings Strings;
private readonly ushort[] Stats;
protected readonly PKM pk; // protected for children generating extra properties
public readonly PKM Entity; // protected for children generating extra properties
public virtual string Position => "???";
public string Nickname => pk.Nickname;
public string Species => Get(Strings.specieslist, pk.Species);
public string Nature => Get(Strings.natures, pk.StatNature);
public string Gender => Get(GenderSymbols, pk.Gender);
public string ESV => pk.PSV.ToString("0000");
public string HP_Type => Get(Strings.types, pk.HPType + 1);
public string Ability => Get(Strings.abilitylist, pk.Ability);
public string Move1 => Get(Strings.movelist, pk.Move1);
public string Move2 => Get(Strings.movelist, pk.Move2);
public string Move3 => Get(Strings.movelist, pk.Move3);
public string Move4 => Get(Strings.movelist, pk.Move4);
public string HeldItem => GetSpan(Strings.GetItemStrings(pk.Context), pk.HeldItem);
public string Nickname => Entity.Nickname;
public string Species => Get(Strings.specieslist, Entity.Species);
public string Nature => Get(Strings.natures, (byte)Entity.StatNature);
public string Gender => Get(GenderSymbols, Entity.Gender);
public string ESV => Entity.PSV.ToString("0000");
public string HP_Type => GetSpan(Strings.HiddenPowerTypes, Entity.HPType);
public string Ability => Get(Strings.abilitylist, Entity.Ability);
public string Move1 => Get(Strings.movelist, Entity.Move1);
public string Move2 => Get(Strings.movelist, Entity.Move2);
public string Move3 => Get(Strings.movelist, Entity.Move3);
public string Move4 => Get(Strings.movelist, Entity.Move4);
public string HeldItem => GetSpan(Strings.GetItemStrings(Entity.Context), Entity.HeldItem);
public string HP => Stats[0].ToString();
public string ATK => Stats[1].ToString();
public string DEF => Stats[2].ToString();
public string SPA => Stats[4].ToString();
public string SPD => Stats[5].ToString();
public string SPE => Stats[3].ToString();
public string MetLoc => pk.GetLocationString(eggmet: false);
public string EggLoc => pk.GetLocationString(eggmet: true);
public string Ball => Get(Strings.balllist, pk.Ball);
public string OT => pk.OT_Name;
public string Version => Get(Strings.gamelist, pk.Version);
public string OTLang => ((LanguageID)pk.Language).ToString();
public string Legal { get { var la = new LegalityAnalysis(pk); return la.Parsed ? la.Valid.ToString() : "-"; } }
public string MetLoc => Entity.GetLocationString(eggmet: false);
public string EggLoc => Entity.GetLocationString(eggmet: true);
public string Ball => Get(Strings.balllist, Entity.Ball);
public string OT => Entity.OriginalTrainerName;
public string Version => Get(Strings.gamelist, (int)Entity.Version);
public string OTLang => ((LanguageID)Entity.Language).ToString();
public string Legal => Legality.Parsed ? Legality.Valid.ToString() : "-";
public string EncounterType => Legality.EncounterMatch.LongName;
private LegalityAnalysis Legality { get; }
#region Extraneous
public string EC => pk.EncryptionConstant.ToString("X8");
public string PID => pk.PID.ToString("X8");
public int HP_IV => pk.IV_HP;
public int ATK_IV => pk.IV_ATK;
public int DEF_IV => pk.IV_DEF;
public int SPA_IV => pk.IV_SPA;
public int SPD_IV => pk.IV_SPD;
public int SPE_IV => pk.IV_SPE;
public uint EXP => pk.EXP;
public int Level => pk.CurrentLevel;
public int HP_EV => pk.EV_HP;
public int ATK_EV => pk.EV_ATK;
public int DEF_EV => pk.EV_DEF;
public int SPA_EV => pk.EV_SPA;
public int SPD_EV => pk.EV_SPD;
public int SPE_EV => pk.EV_SPE;
public int Cool => pk is IContestStatsReadOnly s ? s.CNT_Cool : 0;
public int Beauty => pk is IContestStatsReadOnly s ? s.CNT_Beauty : 0;
public int Cute => pk is IContestStatsReadOnly s ? s.CNT_Cute : 0;
public int Smart => pk is IContestStatsReadOnly s ? s.CNT_Smart : 0;
public int Tough => pk is IContestStatsReadOnly s ? s.CNT_Tough : 0;
public int Sheen => pk is IContestStatsReadOnly s ? s.CNT_Sheen : 0;
public int Markings => pk.MarkValue;
public string EC => Entity.EncryptionConstant.ToString("X8");
public string PID => Entity.PID.ToString("X8");
public int IV_HP => Entity.IV_HP;
public int IV_ATK => Entity.IV_ATK;
public int IV_DEF => Entity.IV_DEF;
public int IV_SPA => Entity.IV_SPA;
public int IV_SPD => Entity.IV_SPD;
public int IV_SPE => Entity.IV_SPE;
public uint EXP => Entity.EXP;
public byte Level => Entity.CurrentLevel;
public int EV_HP => Entity.EV_HP;
public int EV_ATK => Entity.EV_ATK;
public int EV_DEF => Entity.EV_DEF;
public int EV_SPA => Entity.EV_SPA;
public int EV_SPD => Entity.EV_SPD;
public int EV_SPE => Entity.EV_SPE;
public int Cool => Entity is IContestStatsReadOnly s ? s.ContestCool : 0;
public int Beauty => Entity is IContestStatsReadOnly s ? s.ContestBeauty : 0;
public int Cute => Entity is IContestStatsReadOnly s ? s.ContestCute : 0;
public int Smart => Entity is IContestStatsReadOnly s ? s.ContestSmart : 0;
public int Tough => Entity is IContestStatsReadOnly s ? s.ContestTough : 0;
public int Sheen => Entity is IContestStatsReadOnly s ? s.ContestSheen : 0;
public string NotOT => pk.Format > 5 ? pk.HT_Name : "N/A";
public string NotOT => Entity.Format > 5 ? Entity.HandlingTrainerName : "N/A";
public int AbilityNum => pk.Format > 5 ? pk.AbilityNumber : -1;
public int GenderFlag => pk.Gender;
public byte Form => pk.Form;
public int PKRS_Strain => pk.PKRS_Strain;
public int PKRS_Days => pk.PKRS_Days;
public int MetLevel => pk.Met_Level;
public int OT_Gender => pk.OT_Gender;
public int AbilityNum => Entity.Format > 5 ? Entity.AbilityNumber : -1;
public byte GenderFlag => Entity.Gender;
public byte Form => Entity.Form;
public int PokerusStrain => Entity.PokerusStrain;
public int PokerusDays => Entity.PokerusDays;
public byte MetLevel => Entity.MetLevel;
public byte OriginalTrainerGender => Entity.OriginalTrainerGender;
public bool FatefulFlag => pk.FatefulEncounter;
public bool IsEgg => pk.IsEgg;
public bool IsNicknamed => pk.IsNicknamed;
public bool IsShiny => pk.IsShiny;
public bool FatefulEncounter => Entity.FatefulEncounter;
public bool IsEgg => Entity.IsEgg;
public bool IsNicknamed => Entity.IsNicknamed;
public bool IsShiny => Entity.IsShiny;
public int TID => pk.DisplayTID;
public int SID => pk.DisplaySID;
public int TSV => pk.TSV;
public int Move1_PP => pk.Move1_PP;
public int Move2_PP => pk.Move2_PP;
public int Move3_PP => pk.Move3_PP;
public int Move4_PP => pk.Move4_PP;
public int Move1_PPUp => pk.Move1_PPUps;
public int Move2_PPUp => pk.Move2_PPUps;
public int Move3_PPUp => pk.Move3_PPUps;
public int Move4_PPUp => pk.Move4_PPUps;
public string Relearn1 => Get(Strings.movelist, pk.RelearnMove1);
public string Relearn2 => Get(Strings.movelist, pk.RelearnMove2);
public string Relearn3 => Get(Strings.movelist, pk.RelearnMove3);
public string Relearn4 => Get(Strings.movelist, pk.RelearnMove4);
public ushort Checksum => pk is ISanityChecksum s ? s.Checksum : Checksums.CRC16_CCITT(pk.Data.AsSpan(pk.SIZE_STORED));
public int Friendship => pk.OT_Friendship;
public int Egg_Year => pk.EggMetDate.GetValueOrDefault().Year;
public int Egg_Month => pk.EggMetDate.GetValueOrDefault().Month;
public int Egg_Day => pk.EggMetDate.GetValueOrDefault().Day;
public int Met_Year => pk.MetDate.GetValueOrDefault().Year;
public int Met_Month => pk.MetDate.GetValueOrDefault().Month;
public int Met_Day => pk.MetDate.GetValueOrDefault().Day;
public ushort TID16 => Entity.TID16;
public ushort SID16 => Entity.SID16;
public uint TSV => Entity.TSV;
public int Move1_PP => Entity.Move1_PP;
public int Move2_PP => Entity.Move2_PP;
public int Move3_PP => Entity.Move3_PP;
public int Move4_PP => Entity.Move4_PP;
public int Move1_PPUp => Entity.Move1_PPUps;
public int Move2_PPUp => Entity.Move2_PPUps;
public int Move3_PPUp => Entity.Move3_PPUps;
public int Move4_PPUp => Entity.Move4_PPUps;
public string Relearn1 => Get(Strings.movelist, Entity.RelearnMove1);
public string Relearn2 => Get(Strings.movelist, Entity.RelearnMove2);
public string Relearn3 => Get(Strings.movelist, Entity.RelearnMove3);
public string Relearn4 => Get(Strings.movelist, Entity.RelearnMove4);
public ushort Checksum => Entity is ISanityChecksum s ? s.Checksum : Checksums.CRC16_CCITT(Entity.Data[Entity.SIZE_STORED..]);
public int Friendship => Entity.OriginalTrainerFriendship;
public int EggYear => Entity.EggMetDate.GetValueOrDefault().Year;
public int EggMonth => Entity.EggMetDate.GetValueOrDefault().Month;
public int EggDay => Entity.EggMetDate.GetValueOrDefault().Day;
public int MetYear => Entity.MetDate.GetValueOrDefault().Year;
public int MetMonth => Entity.MetDate.GetValueOrDefault().Month;
public int MetDay => Entity.MetDate.GetValueOrDefault().Day;
#endregion
protected EntitySummary(PKM p, GameStrings strings)
protected EntitySummary(PKM pk, GameStrings strings)
{
pk = p;
Entity = pk;
Strings = strings;
Stats = pk.GetStats(pk.PersonalInfo);
Stats = Entity.GetStats(Entity.PersonalInfo);
Legality = new LegalityAnalysis(Entity);
}
/// <summary>

View File

@ -1,5 +1,3 @@
using System;
namespace PKHeX.Core;
/// <summary>
@ -17,22 +15,20 @@ public static void TemplateFields(PKM pk, ITrainerInfo tr)
pk.Move1 = (int)Move.Pound;
pk.HealPP();
pk.Ball = 4;
pk.MetDate = DateTime.Today;
if (tr.Game >= 0)
pk.Version = tr.Game;
if (pk.Format >= 4)
pk.MetDate = EncounterDate.GetDate(pk.Context.Console);
pk.Version = GetTemplateVersion(tr);
pk.Species = GetTemplateSpecies(pk, tr);
pk.Language = GetTemplateLanguage(tr);
pk.Gender = pk.GetSaneGender();
pk.ClearNickname();
pk.OT_Name = tr.OT;
pk.OT_Gender = tr.Gender;
pk.TID = tr.TID;
pk.SID = tr.SID;
if (tr is IRegionOrigin o && pk is IRegionOrigin gt)
pk.OriginalTrainerName = tr.OT;
pk.OriginalTrainerGender = tr.Gender;
pk.ID32 = tr.ID32;
if (tr is IRegionOriginReadOnly o && pk is IRegionOrigin gt)
{
gt.ConsoleRegion = o.ConsoleRegion;
gt.Country = o.Country;
@ -43,6 +39,17 @@ public static void TemplateFields(PKM pk, ITrainerInfo tr)
pk.RefreshChecksum();
}
private static GameVersion GetTemplateVersion(ITrainerInfo tr)
{
var version = tr.Version;
if (version.IsValidSavedVersion())
return version;
version = version.GetSingleVersion();
if (version.IsValidSavedVersion())
return version;
return default; // 0
}
private static void ApplyTrashBytes(PKM pk, ITrainerInfo tr)
{
// Copy OT trash bytes for sensitive games (Gen1/2)
@ -51,17 +58,17 @@ private static void ApplyTrashBytes(PKM pk, ITrainerInfo tr)
switch (tr)
{
case SAV1 s1:
s1.OT_Trash.CopyTo(pk12.OT_Trash);
s1.OriginalTrainerTrash.CopyTo(pk12.OriginalTrainerTrash);
break;
case SAV2 s2:
s2.OT_Trash.CopyTo(pk12.OT_Trash);
s2.OriginalTrainerTrash.CopyTo(pk12.OriginalTrainerTrash);
break;
}
}
private static ushort GetTemplateSpecies(PKM pk, ITrainerInfo tr)
{
ushort species = tr is IGameValueLimit s ? s.MaxSpeciesID : ((GameVersion)pk.Version).GetMaxSpeciesID();
ushort species = tr is IGameValueLimit s ? s.MaxSpeciesID : pk.Version.GetMaxSpeciesID();
if (species == 0)
species = pk.MaxSpeciesID;
return species;

View File

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace PKHeX.Core;
@ -10,16 +9,16 @@ namespace PKHeX.Core;
public sealed class LegalMoveComboSource : ILegalMoveDisplaySource<ComboItem>
{
private readonly bool[] IsMoveBoxOrdered = new bool[4];
private ComboItem[] MoveDataAllowed = Array.Empty<ComboItem>();
private ComboItem[] MoveDataAllowed = [];
public IReadOnlyList<ComboItem> DataSource => (ComboItem[])MoveDataAllowed.Clone();
public IReadOnlyList<ComboItem> DataSource => [.. MoveDataAllowed]; // copy
/// <summary>
/// Resets the <see cref="MoveDataAllowed"/> data source with an updated collection.
/// </summary>
public void ReloadMoves(IReadOnlyList<ComboItem> moves)
{
MoveDataAllowed = moves.ToArray();
MoveDataAllowed = [.. moves]; // copy
ClearUpdateCheck();
}
@ -35,7 +34,7 @@ public void ReloadMoves(LegalMoveInfo info)
private void SortMoves(LegalMoveInfo info) => Array.Sort(MoveDataAllowed, (i1, i2) => Compare(i1, i2, info.CanLearn));
// defer re-population until dropdown is opened; handled by dropdown event
private void ClearUpdateCheck() => Array.Clear(IsMoveBoxOrdered, 0, IsMoveBoxOrdered.Length);
private void ClearUpdateCheck() => IsMoveBoxOrdered.AsSpan().Clear();
private static int Compare(ComboItem i1, ComboItem i2, Func<ushort, bool> check)
{

View File

@ -1,5 +1,6 @@
using System;
using System.Buffers;
using static PKHeX.Core.IndicatedSourceType;
namespace PKHeX.Core;
@ -8,16 +9,16 @@ namespace PKHeX.Core;
/// </summary>
public sealed class LegalMoveInfo
{
// Use a bool array instead of a HashSet; we have a limited range of moves.
// Use a byte array instead of a HashSet; we have a limited range of moves.
// This implementation is faster (no hashcode or bucket search) with lower memory overhead (1 byte per move ID).
private readonly bool[] AllowedMoves = new bool[(int)Move.MAX_COUNT + 1];
private readonly IndicatedSourceType[] AllowedMoves = new IndicatedSourceType[(int)Move.MAX_COUNT + 1];
/// <summary>
/// Checks if the requested <see cref="move"/> is legally able to be learned.
/// </summary>
/// <param name="move">Move to check if can be learned</param>
/// <returns>True if can learn the move</returns>
public bool CanLearn(ushort move) => AllowedMoves[move];
/// <param name="move">Move to check if it can be learned</param>
/// <returns>True if it can learn the move</returns>
public bool CanLearn(ushort move) => AllowedMoves[move] != None;
/// <summary>
/// Reloads the legality sources to permit the provided legal info.
@ -25,16 +26,89 @@ public sealed class LegalMoveInfo
/// <param name="la">Details of analysis, moves to allow</param>
public bool ReloadMoves(LegalityAnalysis la)
{
var rent = ArrayPool<bool>.Shared.Rent(AllowedMoves.Length);
var span = rent.AsSpan(0, AllowedMoves.Length);
LearnPossible.Get(la.Entity, la.EncounterOriginal, la.Info.EvoChainsAllGens, span);
var rentLearn = ArrayPool<bool>.Shared.Rent(AllowedMoves.Length);
var spanLearn = rentLearn.AsSpan(0, AllowedMoves.Length);
var rentEval = ArrayPool<IndicatedSourceType>.Shared.Rent(spanLearn.Length);
var spanEval = rentEval.AsSpan(0, spanLearn.Length);
try
{
LearnPossible.Get(la.Entity, la.EncounterOriginal, la.Info.EvoChainsAllGens, spanLearn);
ComputeEval(spanEval, spanLearn, la);
if (spanEval.SequenceEqual(AllowedMoves))
return false;
spanEval.CopyTo(AllowedMoves);
return true;
}
catch
{
if (Array.TrueForAll(AllowedMoves, z => z == None))
return false;
AllowedMoves.AsSpan().Clear();
return true;
}
finally
{
spanLearn.Clear();
spanEval.Clear();
ArrayPool<IndicatedSourceType>.Shared.Return(rentEval);
ArrayPool<bool>.Shared.Return(rentLearn);
}
}
// check prior move-pool to not needlessly refresh the data set
bool diff = !span.SequenceEqual(AllowedMoves);
if (diff) // keep
span.CopyTo(AllowedMoves);
span.Clear();
ArrayPool<bool>.Shared.Return(rent);
return diff;
private static void ComputeEval(Span<IndicatedSourceType> type, ReadOnlySpan<bool> learn, LegalityAnalysis la)
{
for (int i = 0; i < type.Length; i++)
type[i] = learn[i] ? Learn : None;
if (!la.Entity.IsOriginalMovesetDeleted())
AddEncounterMoves(type, la.EncounterOriginal);
type[0] = None; // Move ID 0 is always None
}
private static void AddEncounterMoves(Span<IndicatedSourceType> type, IEncounterTemplate enc)
{
if (enc is IEncounterEgg egg)
{
var moves = egg.Learn.GetEggMoves(enc.Species, enc.Form);
foreach (var move in moves)
type[move] = Egg;
}
else if (enc is IMoveset {Moves: {HasMoves: true} set})
{
foreach (var move in set.AsSpan())
{
if (type[move] == None)
type[move] = Encounter;
}
}
else if (enc is ISingleMoveBonus single)
{
var moves = single.GetMoveBonusPossible();
foreach (var move in moves)
{
if (type[move] == None)
type[move] = EncounterSingle;
}
}
if (enc is IRelearn { Relearn: {HasMoves: true} relearn})
{
foreach (var move in relearn.AsSpan())
{
if (type[move] == None)
type[move] = Relearn;
}
}
}
}
public enum IndicatedSourceType : byte
{
None = 0,
Learn,
Egg,
Encounter,
EncounterSingle,
Relearn,
}

View File

@ -5,12 +5,10 @@ namespace PKHeX.Core;
/// <summary>
/// Legal Move information for a single <see cref="PKM"/>, for indicating if a move is legal or not.
/// </summary>
public sealed class LegalMoveSource<T>
public sealed class LegalMoveSource<T>(ILegalMoveDisplaySource<T> Display)
{
public LegalMoveInfo Info { get; } = new();
public readonly ILegalMoveDisplaySource<T> Display;
public LegalMoveSource(ILegalMoveDisplaySource<T> display) => Display = display;
public readonly ILegalMoveDisplaySource<T> Display = Display;
public void ReloadMoves(LegalityAnalysis source)
{

Some files were not shown because too many files have changed in this diff Show More