Compare commits

...
Author SHA1 Message Date
whatsy 48ff865c55 cleanup 2026-10-17 19:33:20 -04:00
whatsy e0abdb900d im running out of commit message ideas 2026-10-13 22:58:32 -04:00
whatsy 1b74c2906d Merge remote-tracking branch 'origin/pc-multiselect' into pc-multiselect
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/gui/pc/PCGUI.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/gui/pc/StorageWidget.kt
2026-10-13 20:12:50 -04:00
whatsy d3a616e308 tweaks + bugfixes 2026-10-13 14:14:26 -04:00
whatsy 9a06463871 mass release 2026-10-13 10:56:45 -04:00
whatsy 46b5a046ed highlight selected slots 2026-10-13 10:56:45 -04:00
whatsy ab811ead56 you can now batch move pokemon in the pc 2026-10-13 10:56:45 -04:00
whatsy 09a549ffcd implement draggable selection box in PC GUI 2026-10-13 10:56:45 -04:00
whatsy b393293025 mass release 2026-10-13 10:52:35 -04:00
Hiroku 2f87d53af6 Merge remote-tracking branch 'origin/lang-updates' 2026-10-13 17:34:12 +10:00
Polymeta 542839ba69 bump neoforge version
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-10-12 14:36:24 +01:00
Landon efab1bba08 Merge branch 'fix/custom-choice-feature-tab-completion' into 'main'
fix: choice properties must always tab complete

See merge request cable-mc/cobblemon!2483
2026-10-12 08:42:42 +00:00
Landon 8f986bc326 Merge branch 'fix/molang-fn-changes' into 'main'
fix: behavior of `q.pokemon.nature` and `q.entity.is_looking_at`

Closes #2113

See merge request cable-mc/cobblemon!2482
2026-10-12 08:41:14 +00:00
Polymeta e155d32aaf small pokeball fix
Closes #1601

Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-10-12 08:54:52 +01:00
Polymeta 743e0b7d66 changelog [skip-ci]
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-10-12 07:49:14 +01:00
Polymeta 1518d703f9 Merge branch 'restore-the-menu' into 'main'
Fixed Mixins for Recipe Book causing conflict issues with other mods.

See merge request cable-mc/cobblemon!2484
2026-10-12 06:44:16 +00:00
Gertimoshka af9d851d67 Fixed Mixins being aggresive, causing massive issues with mod compatibility 2026-10-12 00:17:09 +02:00
Polymeta 20ca91f1c9 fix friendship booster item tag location
Closes #2125

Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-10-11 21:15:53 +01:00
Polymeta 6fb3c0e49a more evo dex fixes and crashes
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-10-11 21:12:22 +01:00
Watuneni iteiteandWeblate 5cb4ff5f5f Translated using Weblate (Japanese)
Currently translated at 100.0% (9464 of 9464 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-10-11 15:26:12 +00:00
Gitoido d035c08a5c chore: tidy up imports 2026-10-11 13:43:38 +03:00
Gitoido fda7b13f50 fix: choice properties must always tab complete 2026-10-11 13:41:21 +03:00
Gitoido f595724971 fix: q.entity.is_looking_at now returns proper results based on target passed 2026-10-11 10:55:46 +03:00
Gitoido 0511ba147b fix: use serialized name in q.pokemon.nature molang fn 2026-10-11 10:49:23 +03:00
Polymeta 760f48d850 Merge branch 'evo-tree-fumble' into 'main'
Evo tree now throws a warning instead of crashing when failing to build a mon's evo tree

See merge request cable-mc/cobblemon!2481
2026-10-11 06:41:08 +00:00
lionandPolymeta bf296b4921 Evo tree now throws a warning instead of crashing when failing to build a mon's evo tree 2026-10-11 06:41:08 +00:00
Hiroku 49886389cf Fine the snapshot can be true again 2026-10-11 12:57:43 +10:00
Hiroku 643bf9068b But what if the snapshot was false 2026-10-11 10:54:55 +10:00
Hiroku 53f3258246 Merge remote-tracking branch 'origin/lang-updates' 2026-10-11 10:53:42 +10:00
Hiroku 1e9895ec84 buncha changelog updates [skip-ci] 2026-10-11 10:49:11 +10:00
Polymeta 52dacb03ae Merge branch 'evo-tree-pls-behave' into 'main'
Evo tree small adjustments

See merge request cable-mc/cobblemon!2479
2026-10-10 23:47:53 +00:00
lionandPolymeta 85b0f3f393 Evo tree small adjustments 2026-10-10 23:47:53 +00:00
whatsy 68a760a595 highlight selected slots 2026-10-10 18:18:24 -04:00
Polymeta a04a91bf24 Merge branch 'feature/evolution-mania' into 'main'
tweak some evo requirements

See merge request cable-mc/cobblemon!2476
2026-10-10 21:25:13 +00:00
Polymeta 5e814f7ab5 tweak some evo requirements 2026-10-10 21:25:13 +00:00
Polymeta 2780f491d6 Merge branch 'implement_pokemon' into 'main'
implement_pokemon

See merge request cable-mc/cobblemon!2478
2026-10-10 19:44:59 +00:00
ewwww 2d3ec7aff1 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-10-10 14:51:28 -04:00
ewwww cb7baf2696 changelog 2026-10-10 14:51:12 -04:00
Polymeta 5cc069df78 Merge branch 'implement_pokemon' into 'main'
Dipplin and Hydrapple shiny fixes

See merge request cable-mc/cobblemon!2477
2026-10-10 16:03:26 +00:00
Polymeta 9222476c1f Merge branch 'fix/qa-bundle' into 'main'
fix: an assortment of qa bugs

Closes #2070

See merge request cable-mc/cobblemon!2473
2026-10-10 15:40:51 +00:00
Kenji_64 f220755f99 Dipplin and Hydrapple shiny fixes 2026-10-10 17:31:21 +02:00
Gitoido d55bbf237c chore: move scale changes to other place for now 2026-10-10 18:22:31 +03:00
Watuneni iteiteandWeblate 83a9d44c3a Translated using Weblate (Japanese)
Currently translated at 100.0% (9472 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-10-10 14:22:54 +00:00
Polymeta c5a802eaf6 changelog
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-10-10 15:04:26 +01:00
Polymeta 2e27875460 Merge branch 'lang-updates' 2026-10-10 15:04:06 +01:00
Polymeta cd66887faa [skip-ci] tidy up changelog
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-10-10 14:11:50 +01:00
Polymeta d665c87c21 more spawn tweaks
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-10-10 14:02:26 +01:00
Polymeta 044d3102b6 Merge branch 'fix-evo-tree-crash' into 'main'
Fix grotle evolution crashing evo tree and species features not synching to the client

See merge request cable-mc/cobblemon!2456
2026-10-10 12:51:04 +00:00
lionandPolymeta c421a93b06 Fix grotle evolution crashing evo tree and species features not synching to the client 2026-10-10 12:51:03 +00:00
Polymeta 2865aa2272 Merge branch 'implement_pokemon' into 'main'
poser tweaks + jellicent placeholders

See merge request cable-mc/cobblemon!2474
2026-10-10 12:15:04 +00:00
Polymeta 7c4aae3e9d Merge branch '1.8.2-tag-fixes' into 'main'
Fixes to regional tags.

See merge request cable-mc/cobblemon!2475
2026-10-10 12:14:09 +00:00
Veraxiel d9f671f2a8 Fixes to regional tags. 2026-10-10 07:00:14 -04:00
ewwww 69733b530b poser tweaks + jellicent placeholders 2026-10-10 05:44:57 -04:00
Gitoido d4471c30bc fix: use effective scale instead of scale modifier to get a size category for summary/scanner/molang 2026-10-10 09:38:02 +03:00
Gitoido 185f7e03f5 chore: changelog skip-ci 2026-10-10 09:24:24 +03:00
Gitoido a45eb0eb21 fix: close and open pc gui silently via command 2026-10-10 09:18:30 +03:00
Polymeta ed67142379 Merge branch '1.8.2-tag-fixes' into 'main'
Updates to some tags to fix some errors and add future biome support.

See merge request cable-mc/cobblemon!2472
2026-10-10 05:46:36 +00:00
Gitoido 13169c6848 fix: align oricorio pau form translation keys 2026-10-10 08:35:25 +03:00
whatsy ae7bc6e681 you can now batch move pokemon in the pc 2026-10-09 22:57:48 -04:00
Veraxiel 8be134ee2f More fixes. 2026-10-09 21:13:26 -04:00
whatsy ed007fe429 implement draggable selection box in PC GUI 2026-10-09 20:51:04 -04:00
Veraxiel 8c3206a413 Updates to some tags to fix some errors and add future biome support. 2026-10-09 20:03:32 -04:00
Watuneni iteiteandWeblate 4d4c071b95 Translated using Weblate (Japanese)
Currently translated at 100.0% (9472 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-10-09 23:21:40 +00:00
Polymeta b82c27754f Merge branch 'fix/minior-random-core-color' into 'main'
fix: minior core color should be decided randomly

See merge request cable-mc/cobblemon!2471
2026-10-09 16:57:27 +00:00
Polymeta 63afec184e Merge branch 'implement_pokemon' into 'main'
implement_pokemon

See merge request cable-mc/cobblemon!2463
2026-10-09 16:41:24 +00:00
Gitoido 65699f045e fix: minior core color should be decided randomly 2026-10-09 19:38:34 +03:00
JakotensandJakotens 90dc489547 Minor poser fixes for Mime Jr. and slight model adjustments to Mime Jr. and Corsola (Johtonian form only).
Signed-off-by: Jakotens <jjakobdupuyy@gmail.com>
2026-10-08 13:47:42 -06:00
ewwww b2142fed1d frog tweaks 2026-10-08 03:07:03 -04:00
ewwww fef21516e9 lang 2026-10-08 03:05:45 -04:00
ewwww 95d43de79f various minior tweaks 2026-10-08 02:16:38 -04:00
ewwww 92bccf620f Merge branch 'main' into implement_pokemon 2026-10-07 15:40:14 -04:00
ewwww 15f8a9ee71 Merge branch 'main' into implement_pokemon 2026-10-07 15:39:42 -04:00
ewwww e5750cbba0 lang fixes for groudon, kyogre, and oricorio 2026-10-07 12:58:20 -04:00
ewwww c6482c82cd various minior tweaks 2026-10-07 12:57:06 -04:00
Kenji_64 c470894bd3 Fixed Politoed's hitbox 2026-10-07 15:29:52 +02:00
Kenji_64 a940e32f8d Fixed a texture error with Shiny Greninja 2026-10-07 15:29:21 +02:00
ewwww b6b59eccad vivillon dex tweak 2026-10-07 06:52:25 -04:00
ewwww b520b94fae minior lang fixes + tweaks to form & dex data 2026-10-07 06:52:16 -04:00
YasitoandWeblate 1969af65b9 Translated using Weblate (French (Canada))
Currently translated at 95.1% (9016 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2026-09-30 09:25:23 +00:00
SultanandWeblate 905d63e098 Translated using Weblate (Russian)
Currently translated at 85.6% (8110 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-09-21 16:38:11 +00:00
Nico MerenqandWeblate 1ea8ab7dcc Translated using Weblate (Russian)
Currently translated at 85.6% (8110 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-09-21 16:38:11 +00:00
geumuandWeblate a45d21b279 Translated using Weblate (Korean)
Currently translated at 99.6% (9436 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ko/
2026-09-21 16:38:10 +00:00
geumuandWeblate efe8f34413 Translated using Weblate (Korean)
Currently translated at 99.6% (9436 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ko/
2026-09-20 13:11:32 +00:00
Nico MerenqandWeblate 6bb23582c4 Translated using Weblate (Russian)
Currently translated at 85.5% (8102 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-09-19 04:42:37 +00:00
SultanandWeblate 56ccb43997 Translated using Weblate (Russian)
Currently translated at 85.5% (8102 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-09-19 04:42:36 +00:00
Dmytro BunnuaandWeblate 5fbbcb5707 Translated using Weblate (Ukrainian)
Currently translated at 75.9% (7194 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/uk/
2026-09-15 09:37:39 +00:00
Gustavo PuerariandWeblate 580a24eac3 Translated using Weblate (Portuguese (Brazil))
Currently translated at 96.8% (9175 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/pt_BR/
2026-09-13 13:18:28 +00:00
136 changed files with 5200 additions and 2197 deletions
+16 -169
View File
@@ -1,200 +1,47 @@
# Changelog
## [1.8.2 (MONTH xth, 2026)](#1-8-2)
## [1.8.3 (MONTH xth, 2026)](#1-8-3)
### Additions
- The following items can now be placed on the ground as blocks:
- King's Rock, Galarica Cuff, Unremarkable Teacup, Masterpiece Teacup, Chipped Pot, Cracked Pot, Sachet, Metal Powder, Quick Powder, Bright Powder, Soft Sand, Electirizer, Magmarizer, Icy Rock, Heat Rock, Smooth Rock, Damp Rock, Destiny Knot, Luminous Moss, Never-Melt Ice, Wide Lens, Scope Lens, and Zoom Lens.
- Iron Ball, Metal Coat, Throat Spray, Terrain Extender, Medicinal Brew, Moomoo Milk, HP Up, Protein, Iron, Carbos, Calcium, Zinc, PP Up, PP Max, Upgrade, Dubious Disc, and Loaded Dice.
- The following items can now be placed on various surfaces:
- Galarica Wreath, Magnet, Flame Orb, Life Orb, Toxic Orb, Light Ball, and Smoke Ball.
- The following items can now be placed as blocks and have additional functionality:
- Ring Target, Metronome, and Eject Button.
- The following items can now be worn by Players and Pokémon:
- Galarica Wreath, Cleanse Tag, Spell Tag, Sharp Beak, Covert Cloak, Shell Helmet, and Never-Melt Ice.
- Wearable items can now be equipped by a player by holding it and right-clicking (or rebound use/interact)
- Wearable items that can be placed down are exempt from this behaviour.
- Mint Leaves can now be placed on the ground as Leaf Litter, or in Flower Pots
- Added Oricorio Flowers
- Added Terrain Plants
- Berries can now be placed in Flower Pots
- The evolution tree can now be viewed on the Pokédex, showing a Pokémon's full family line and what's needed to evolve into each stage.
- Added custom sounds to a bunch of items that can be placed as blocks: liquid bottles, pill bottles, powders and dice.
- New Cosmetic Item for Pikachu: Paper
- Burmy changes its cloak after a battle it took part in, based on the biome: Trash Cloak in the Deep Dark, Nether and End, Plant Cloak in lush biomes such as forests, jungles, plains and swamps, and Sandy Cloak in sandy biomes, beaches and caves.
- You can now drag to select multiple Pokémon inside the PC menu.
### Fixes
- Medicinal Leeks can now be eaten when they aren't being planted, matching their food properties.
- Sweeping attacks no longer set owned Pokémon on fire (e.g. with Fire Aspect) or knock them back.
- Fixed #updateMovesOnFormChange not properly initializing a form-swapped Pokemon's non-form_change moves.
### TEST
### Pokémon Added
#### Generation 4
- Snover
- Abomasnow
- Burmy
- Wormadam
- Mothim
#### Generation 5
- Tympole
- Palpitoad
- Seismitoad
#### Generation 6
- Helioptile
- Heliolisk
#### Generation 7
- Grubbin
- Charjabug
- Vikavolt
- Minior
#### Generation 8
- Eternatus
#### Generation 9
- Greavard
- Houndstone
### Model updates for the following Pokémon
- Sudowoodo
- Spheal
- Cacnea
- Cacturne
- Binacle
- Barbaracle
- Glimmet
- Glimmora
- Deerling
- Sawsbuck (All forms)
- Absol
- Poliwag
- Politoed
- Ekans
- Oddish
- Gloom
- Vileplume
- Bellossom
- Shroomish
- Chespin
- Quilladin
- Chesnaught
- Fennekin
- Braixen
- Delphox
- Froakie
- Frogadier
- Greninja
- Shinx
- Luxio
- Luxray
- Slakoth
- Riolu
- Lucario
### Animation updates for the following Pokémon
- Treecko (New Shoulder Mount!)
- Bonsly (New Shoulder Mount!)
- Sudowoodo
- Cacnea
- Cacturne
- Ekans
- Arbok
- Oddish
- Gloom
- Vileplume
- Bellossom
- Pichu
- Pikachu
- Raichu (+ Alolan)
- Greninja
- Slakoth
### Changes
- Terrain Seeds can now be planted
- Terrain Seeds are now consumed during PvP and NPC battles
- Glow Item Frames can be crafted using Bright Powder as an alternative to Glow Ink Sacs
- Bright Powder can be used on Signs and Hanging Signs to make text glow, as an alternative to Glow Ink Sacs
- Updated various recipes to display in Recipe Groups within the Recipe Book
- Updated most recipes to display more logically/consistently within the Recipe Book's categories
- Updated the following recipes
- Never-Melt Ice, Focus Sash, Blunder Policy, Weakness Policy, Blank TM
- Make ride rotations less jittery in multiplayer.
- Changes Disc Shelf TMs to use textures instead of tinting and overlays to line up with item display
- Blank TM can now be inserted into the Disc Shelf
- Added TM creative tab
- Recall animation now renders ball in hand with the player arm raised.
- Made regional and Vivillon split-evo locations now mutually exclusive
- Item evolutions now unlock **all** locked evolutions whose requirements are met instead of stopping after the first.
- Exceptions being "forced" evolutions which will continue to stop after the first one
- Removed Mega Drain as a TM
- Vivillon's patterns are now viewable as forms with form names and unique Pokedex entries displayed.
- Slowed the speed of Type Gem growth, and revamped their generation to grow in a more crystal-like shape
- PokeSnacks (and other area spawns) will now start the vertical offset checking from the center rather than base coordinate
- This will help PokeSnacks to work inside caves or tighter spaces better
### Fixes
- Correctly grants "They're Minerals!" advancement when planting a type gem
- Shoulder mounted Pokémon now follow player body animations, improving their positioning with custom player animations and default actions.
- Shoulder mounted Pokémon now sit slightly higher with chest armor equipped for better alignment, with a configurable blacklist.
- Type Gem Blocks now have advancements to give you the recipe in the recipe book (the data for the advancements were inside the recipes somehow??)
- Some other recipes without advancements have also been corrected
- Corrected the TM Recipes for Charge Beam and Payday
- Type Gem blocks now properly generate clusters off of the block.
- Starter Screen Categories will no longer stop rendering if they are bigger than the viewport, and you are not viewing top or bottom edges
- Random Starter option now properly handled in height calculation, so now it won't run off into other categories or be un-accessible due to scroll limits if it causes a new row
- Mochi and Feathers no longer drop glass bottles on use
- Fixed a rare set of concurrency bugs related to reloading packs on NeoForge; fixes a ModernFix issue
- Removed unnecessary conditions for Mimikyu's standing pose. Hopefully fixes the rare instance of it using its shoulder animations during battle in multiplayer scenarios.
- Fixed Rockruff line not blinking
- Fixed Flygon sometimes using its running animation instead of walking
- Fixed Bramblin always sleeping outside of battle
- Fixed Dragonair using its battle idle as a normal idle
- Fixed Lycanroc's head movement
- Added head movement to Archaludon
- Fixed Duraludon line not having any sleep animations
- Fixed Diglett line not having their faint animations
- Fixed Kingambit moving its legs while walking
- Fixed Alolan Diglett's cry not playing
- Fixed shiny alpha Snorlax using the non-shiny texture
- Various Structure fixes
- Fixed shiny Pokémon under certain circumstances forgetting taught TMs
- Fixed certain Pokémon sounds sometimes being doubled when played at specific times in their animations
- Fixed ball capture beam origin coming from behind the ball.
- Fixed Safari Ball catch rate outside of battle
- Fixed alpha eye (and other) particles not respecting client particle density setting in video options
- Fixed TM-Machine UI not closing when player is moving away from it
- Fixed config category for alpha/shiny chance config fields for fossil machine and honey log slathering to Spawning
- Fixed crashes for addon species with non-Cobblemon namespaces, in evolution, fishing bait and Pokédex lookups.
- Bees now grow Apricorns and the Galarica Nut Bush, which were in the bee growables tag but never handled, and correctly spend their nectar when they do
- Saccharine Leaves can now be bone mealed
- Fixed equipping a stack of items instead of one item of a stack
- Fixed TM stack consumption in TMM screen when player teaches a move to species
- Fixed every Miltank giving mushroom stew, not only mushroom variants
- Fixed datapacks with item interaction evolutions that don't specify an item crashing the world on load.
- Fixed the Pokédex evolution tree crashing when a datapack's dex entry is missing an evolution's form.
- Fixed the Pokédex evolution tree crashing with a stack overflow on evolutions that loop back on themselves.
- Fixed the Pokédex evolution tree crashing when looking at a dex that is not part of the "National Dex" aggregate.
- Fixed evolution trees not building properly when a pokemon has several pokedex entries for different regions.
- Fixed form-changing evolutions sometimes resolving to the source form in the Pokédex evolution tree.
- Broken datapack evolutions are now logged and skipped in the Pokédex evolution tree instead of crashing.
- Fixed friendship booster items not being recognised as such
- Fixed conflicts with other mods that would make use of recipe books in certain ways (Farmer's Delight Cooking Pot)
- Fixed Pokémon battles stalling when a sidemod cancels the `THROWN_POKEBALL_HIT` hit event
- Fixed choice feature tab completion in `spawnpokemon/pokegive/pokeedit` commands
### Developer
- Evolution requirements can now set `pokedexIcon` and `pokedexTooltip` directly in their JSON to control how they're shown on the Pokédex's evolution tree, for example:
```json
{
"variant": "attack_defence_ratio",
"pokedexIcon": "minecraft:diamond_sword",
"pokedexTooltip": "cobblemon.ui.pokedex.requirement.attack_defence_ratio.tooltip"
}
```
- Fixed wrong order of operations in the code flow prior to the `FOSSIL_REVIVED` event causing pokemon to appear without entity link
- Fixed TestPartySlot command not correctly comparing the provided spec with the target Pokémon
- Bumped NeoForge to `21.1.195` to get access to MixinExtras `0.5` and the expressions annotation
### Molang & Datapacks
- A new `negate` variant for evolution requirements has been added, which will negate (invert) the provided inner condition.
- Added Molang functions for individual Pokédex ids, wherein a subdex is passed in as a string to `dex_caught_count` etc.
- Added a `battle_ended` callback, backed by the new `BATTLE_ENDED` event, which fires whenever a battle ends for any reason, alongside a multitude of flags not previously covered by `BATTLE_VICTORY`.
- Added `battle_pokemon` to battle actor structs and `has_faced_opponents` to battle Pokémon structs.
- Added the `cobblemon:form_change/bagworm_cloak/plant_cloak`, `sandy_cloak` and `trash_cloak` biome tags, which control Burmy's cloak after battle.
### Particles
#### New Move Animations
- Fixed `q.pokemon.nature` returning class instance instead of proper string value (`cobblemon:adamant`)
- Fixed `q.entity.is_looking_at` result being always `1` for whatever entity passed as target
### Localization
- Updated translations for:
+4 -1
View File
@@ -36,6 +36,7 @@
- Added `cobblemon:catch_alpha_pokemon` advancement trigger and `totalAlphaCaptureCount` stat for captured Alpha Pokémon.
### New rideable Pokémon
- Pidgeot
- Crobat
- Skarmory
- Milotic
@@ -106,7 +107,9 @@
- Avalugg (Hisuian Form)
### Model updates for the following Pokémon
- Pidgey
- Pidgeotto
- Pidgeot
- Sandshrew
- Sandslash
- Nidoking
+211
View File
@@ -0,0 +1,211 @@
# Changelog
## [1.8.2 (October 11th, 2026)](#1-8-2)
### Additions
- The following items can now be placed on the ground as blocks:
- King's Rock, Galarica Cuff, Unremarkable Teacup, Masterpiece Teacup, Chipped Pot, Cracked Pot, Sachet, Metal Powder, Quick Powder, Bright Powder, Soft Sand, Electirizer, Magmarizer, Icy Rock, Heat Rock, Smooth Rock, Damp Rock, Destiny Knot, Luminous Moss, Never-Melt Ice, Wide Lens, Scope Lens, and Zoom Lens.
- I wasn't finished. Iron Ball, Metal Coat, Throat Spray, Terrain Extender, Medicinal Brew, Moomoo Milk, HP Up, Protein, Iron, Carbos, Calcium, Zinc, PP Up, PP Max, Upgrade, Dubious Disc, and Loaded Dice.
- The following items can now be placed on various surfaces:
- Galarica Wreath, Magnet, Flame Orb, Life Orb, Toxic Orb, Light Ball, and Smoke Ball.
- The following items can now be placed as blocks and have additional functionality:
- Ring Target, Metronome, and Eject Button.
- The following items can now be worn by Players and Pokémon:
- Galarica Wreath, Cleanse Tag, Spell Tag, Sharp Beak, Covert Cloak, Shell Helmet, and Never-Melt Ice.
- Wearable items can now be equipped by a player by holding it and right-clicking (or whatever you've rebound use/interact to).
- Wearable items that can be placed down are exempt from this behaviour.
- Mint Leaves can now be placed on the ground as Leaf Litter, or in Flower Pots.
- The recall animation now renders the ball in hand with the player's arm raised.
- Added Oricorio Flowers.
- Added Terrain Plants, four plants grown from Electric, Grassy, Misty, and Psychic Seeds, which glow under specific environmental conditions and can be farmed for more of their seeds.
- Berries can now be placed in Flower Pots.
- The evolution tree for a Pokémon can now be viewed in the Pokédex, showing the Pokémon's full family line and what's needed to evolve into each stage.
- Added custom sounds to a bunch of items that can be placed as blocks: liquid bottles, pill bottles, powders, and dice.
- Added paper as a new cosmetic item for Pikachu.
- Burmy changes its cloak after a battle it took part in, based on the biome.
- Trash Cloak in the Deep Dark, Nether and End.
- Plant Cloak in lush biomes such as forests, jungles, plains and swamps.
- Sandy Cloak in sandy biomes, beaches and caves.
### Pokémon Added
#### Generation 4
- Snover
- Abomasnow
- Burmy
- Wormadam
- Mothim
#### Generation 5
- Tympole
- Palpitoad
- Seismitoad
#### Generation 6
- Helioptile
- Heliolisk
#### Generation 7
- Grubbin
- Charjabug
- Vikavolt
- Minior
#### Generation 8
- Eternatus
#### Generation 9
- Greavard
- Houndstone
### Model updates for the following Pokémon
- Sudowoodo
- Spheal
- Cacnea
- Cacturne
- Binacle
- Barbaracle
- Glimmet
- Glimmora
- Deerling
- Sawsbuck (All forms)
- Absol
- Poliwag
- Politoed
- Ekans
- Oddish
- Gloom
- Vileplume
- Bellossom
- Shroomish
- Chespin
- Quilladin
- Chesnaught
- Fennekin
- Braixen
- Delphox
- Froakie
- Frogadier
- Greninja
- Shinx
- Luxio
- Luxray
- Slakoth
- Riolu
- Lucario
- Corsola
- Mime Jr.
### Animation updates for the following Pokémon
- Treecko (New Shoulder Mount!)
- Bonsly (New Shoulder Mount!)
- Sudowoodo
- Cacnea
- Cacturne
- Ekans
- Arbok
- Oddish
- Gloom
- Vileplume
- Bellossom
- Pichu
- Pikachu
- Raichu (+ Alolan)
- Greninja
- Slakoth
### Changes
- Slowed the speed of Type Gem growth, and revamped their generation to grow in a more crystal-like shape.
- Terrain Seeds can now be planted.
- Terrain Seeds are now consumed during PvP and NPC battles.
- Glow Item Frames can be crafted using Bright Powder as an alternative to Glow Ink Sacs.
- Bright Powder can be used on Signs and Hanging Signs to make text glow, as an alternative to Glow Ink Sacs.
- Updated various recipes to display in Recipe Groups within the Recipe Book.
- Updated most recipes to display more logically and consistently within the Recipe Book's categories.
- Updated the following recipes
- Never-Melt Ice, Focus Sash, Blunder Policy, Weakness Policy, Blank TM.
- Made ride rotations less jittery in multiplayer.
- Changes Disc Shelf TMs to use textures instead of tinting and overlays to line up with item display.
- Blank TM can now be inserted into the Disc Shelf.
- Added a TM creative tab.
- Made regional and Vivillon split-evo locations mutually exclusive.
- Vivillon's patterns are now viewable as forms with form names and unique Pokédex entries displayed.
- Item evolutions now unlock **all** locked evolutions whose requirements are met instead of stopping after the first.
- Exceptions being "forced" evolutions which stop after the first one.
- Removed Mega Drain as a TM.
- PokeSnacks (and other area spawns) will now start the vertical offset checking from the center rather than base coordinate
- This will help PokeSnacks to work inside caves or tighter spaces better.
### Fixes
- Type Gem blocks now properly generate clusters off of the block.
- Correctly grant "They're Minerals!" advancement when planting a Type Gem.
- Shoulder mounted Pokémon now follow player body animations, improving their positioning with custom player animations and default actions.
- Shoulder mounted Pokémon now sit slightly higher with chest armor equipped for better alignment, with a configurable blacklist.
- Type Gem Blocks now have advancements to give you the recipe in the recipe book (the data for the advancements were inside the recipes somehow??).
- Some other recipes without advancements have also been corrected.
- Corrected the TM Recipes for Charge Beam and Payday.
- Starter Screen Categories will no longer stop rendering if they are bigger than the viewport and you are not viewing the top or bottom edges.
- The Random Starter option no longer runs off into other categories or becomes unaccessible due to scroll limits if it causes a new row.
- Mochi and Feathers no longer drop glass bottles on use.
- Fixed a rare set of concurrency bugs related to reloading packs on NeoForge; fixes a ModernFix compatibility issue.
- Removed unnecessary conditions for Mimikyu's standing pose. Hopefully fixes the rare instance of it using its shoulder animations during battle in multiplayer scenarios.
- Fixed Rockruff line not blinking.
- Fixed Flygon sometimes using its running animation instead of walking.
- Fixed Bramblin always sleeping outside of battle.
- Fixed Dragonair using its battle idle as a normal idle.
- Fixed Lycanroc's head movement.
- Added head movement to Archaludon.
- Fixed Duraludon line not having any sleep animations.
- Fixed Diglett line not having their faint animations.
- Fixed Kingambit moving its legs while walking.
- Fixed Alolan Diglett's cry not playing.
- Fixed shiny alpha Snorlax using the non-shiny texture.
- Fixed Crabominable's head breaking when looking around.
- Fixed Shiny Dipplin and Hydrapple's textures.
- Various structure fixes.
- Fixed shiny Pokémon under certain circumstances forgetting taught TMs.
- Fixed certain Pokémon sounds sometimes being doubled when played at specific times in their animations.
- Fixed ball capture beam origin coming from behind the ball.
- Fixed Safari Ball catch rate outside of battle.
- Fixed alpha eye (and other) particles not respecting the client particle density setting in video options.
- Fixed the TM Machine UI not closing when player is moving away from it.
- Moved the config category for alpha/shiny chance config fields for fossil machine and honey log slathering to Spawning.
- Fixed crashes for addon species with non-Cobblemon namespaces in evolution, fishing bait, and Pokédex lookups.
- Bees will now grow Apricorns and the Galarica Nut Bush, which were in the bee growables tag but never handled, and correctly spend their nectar when they do.
- Saccharine Leaves can now be bone mealed.
- Fixed equipping a stack of wearable items instead of one item of a stack.
- Fixed TM stack consumption in TM Machine screen when a player teaches a move to a Pokémon.
- Fixed every Miltank giving mushroom stew, not only mushroom variants.
- Medicinal Leeks can now be eaten when they aren't being planted, matching their food properties.
- Sweeping attacks no longer set owned Pokémon on fire (e.g. with Fire Aspect) or knock them back. Friendly Fire is now off.
- Fixed Pokémon not losing their previous form's signature moves when changing forms (e.g. Rotom keeping Overheat after leaving its Heat form).
- Form-specific moves gained from a form change are now only granted if the Pokémon is high enough level to learn them, and no longer create duplicates in benched moves.
- Spinda now learns Teeter Dance at level 32 instead of via TM.
- Fixed Oricorio Pa'u form description in the Pokédex.
- Fixed PC playing sound on close when opened via command.
### Developer
- Evolution requirements can now set `pokedexIcon` and `pokedexTooltip` directly in their JSON to control how they're shown on the Pokédex's evolution tree, for example:
```json
{
"variant": "attack_defence_ratio",
"pokedexIcon": "minecraft:diamond_sword",
"pokedexTooltip": "cobblemon.ui.pokedex.requirement.attack_defence_ratio.tooltip"
}
```
- Fixed the wrong order of operations in the code flow prior to the `FOSSIL_REVIVED` event causing Pokémon to appear without the entity link.
- Fixed TestPartySlot command not correctly comparing the provided spec with the target Pokémon.
### Molang & Datapacks
- A new `negate` variant for evolution requirements has been added, which will negate (invert) the provided inner condition.
- Added Molang functions for individual Pokédex ids, wherein a subdex is passed in as a string to `dex_caught_count` etc.
- Added a `battle_ended` callback, backed by the new `BATTLE_ENDED` event, which fires whenever a battle ends for any reason, alongside a multitude of flags not previously covered by `BATTLE_VICTORY`.
- Added `battle_pokemon` to battle actor structs and `has_faced_opponents` to battle Pokémon structs.
- Added the `cobblemon:form_change/bagworm_cloak/plant_cloak`, `sandy_cloak` and `trash_cloak` biome tags, which control Burmy's cloak after battle.
### Localization
- Updated translations for:
- Brazilian Portuguese
- Canadian French
- Japanese
- Korean
- Russian
- Ukrainian
@@ -16,6 +16,7 @@ import com.cobblemon.mod.common.item.crafting.CookingPotShapelessRecipe;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.client.ClientRecipeBook;
import net.minecraft.client.RecipeBookCategories;
import net.minecraft.client.gui.screens.recipebook.RecipeCollection;
@@ -63,23 +64,12 @@ public abstract class ClientRecipeBookMixin {
}
}
@Inject(method = "setupCollections", at = @At("RETURN"))
private void addCustomAggregateCategories(Iterable<RecipeHolder<?>> recipes, RegistryAccess registryAccess, CallbackInfo ci) {
Map<RecipeBookCategories, List<List<RecipeHolder<?>>>> map = categorizeAndGroupRecipes(recipes);
Map<RecipeBookCategories, List<RecipeCollection>> map2 = Maps.newHashMap();
ImmutableList.Builder<RecipeCollection> builder = ImmutableList.builder();
map.forEach((recipeBookCategories, list) -> {
List<RecipeCollection> collections = list.stream()
.map(listx -> new RecipeCollection(registryAccess, listx))
.peek(builder::add)
.collect(ImmutableList.toImmutableList());
map2.put(recipeBookCategories, collections);
});
RecipeBookCategories.AGGREGATE_CATEGORIES.forEach((recipeBookCategories, list) -> map2.put(recipeBookCategories, (List) list.stream().flatMap((recipeBookCategoriesx) -> ((List) map2.getOrDefault(recipeBookCategoriesx, ImmutableList.of())).stream()).collect(ImmutableList.toImmutableList())));
@Inject(method = "setupCollections",
at = @At(
value = "INVOKE",
target = "Lcom/google/common/collect/ImmutableMap;copyOf(Ljava/util/Map;)Lcom/google/common/collect/ImmutableMap;"
))
private void addCustomAggregateCategories(Iterable<RecipeHolder<?>> recipes, RegistryAccess registryAccess, CallbackInfo ci, @Local(name = "map2") Map<RecipeBookCategories, List<RecipeCollection>> map2) {
CobblemonRecipeCategories.Companion.getCustomAggregateCategories().forEach((recipeBookCategories, list) -> map2.put(recipeBookCategories, (List) list.stream().flatMap((recipeBookCategoriesx) -> ((List) map2.getOrDefault(recipeBookCategoriesx, ImmutableList.of())).stream()).collect(ImmutableList.toImmutableList())));
this.collectionsByTab = ImmutableMap.copyOf(map2);
this.allCollections = builder.build();
}
}
@@ -9,55 +9,25 @@
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.CobblemonRecipeCategories;
import net.minecraft.client.ClientRecipeBook;
import net.minecraft.client.Minecraft;
import com.llamalad7.mixinextras.expression.Definition;
import com.llamalad7.mixinextras.expression.Expression;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.client.RecipeBookCategories;
import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent;
import net.minecraft.client.gui.screens.recipebook.RecipeBookPage;
import net.minecraft.client.gui.screens.recipebook.RecipeBookTabButton;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(RecipeBookComponent.class)
public class RecipeBookComponentMixin {
@Shadow private ClientRecipeBook book;
@Shadow protected Minecraft minecraft;
@Shadow private int width;
@Shadow private int xOffset;
@Shadow private int height;
@Shadow @Final private RecipeBookPage recipeBookPage;
@Inject(method = "updateTabs", at = @At("HEAD"), cancellable = true)
private void customUpdateTabs(CallbackInfo ci) {
RecipeBookComponent instance = (RecipeBookComponent) (Object) this;
int i = (width - 147) / 2 - xOffset - 30;
int j = (height - 166) / 2 + 3;
int k = 27;
int l = 0;
for (RecipeBookTabButton recipeBookTabButton : instance.tabButtons) {
RecipeBookCategories recipeBookCategories = recipeBookTabButton.getCategory();
if (recipeBookCategories != RecipeBookCategories.CRAFTING_SEARCH && recipeBookCategories != RecipeBookCategories.FURNACE_SEARCH && recipeBookCategories != CobblemonRecipeCategories.COOKING_POT_SEARCH.toVanillaCategory()) {
if (recipeBookTabButton.updateVisibility(book)) {
recipeBookTabButton.setPosition(i, j + 27 * l++);
recipeBookTabButton.startAnimation(minecraft);
}
} else {
recipeBookTabButton.visible = true;
recipeBookTabButton.setPosition(i, j + k * l++);
}
}
ci.cancel();
@Definition(id = "recipeBookCategories", local = @Local(name = "recipeBookCategories"))
@Definition(id = "FURNACE_SEARCH", field = "Lnet/minecraft/client/RecipeBookCategories;FURNACE_SEARCH:Lnet/minecraft/client/RecipeBookCategories;")
@Expression("recipeBookCategories != FURNACE_SEARCH")
@ModifyExpressionValue(
method = "updateTabs",
at = @At(value = "MIXINEXTRAS:EXPRESSION")
)
private boolean notCookingPotSearch(boolean original, @Local(name = "recipeBookCategories") RecipeBookCategories recipeBookCategories) {
return original && recipeBookCategories != CobblemonRecipeCategories.COOKING_POT_SEARCH.toVanillaCategory();
}
}
@@ -425,7 +425,7 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(MovesRegistrySyncPacket.ID, MovesRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(BerryRegistrySyncPacket.ID, BerryRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(SpeciesRegistrySyncPacket.ID, SpeciesRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(SpeciesEvolutionSyncPacket.ID, SpeciesEvolutionSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(DexEvolutionDisplaySyncPacket.ID, DexEvolutionDisplaySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(PropertiesCompletionRegistrySyncPacket.ID, PropertiesCompletionRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(StandardSpeciesFeatureSyncPacket.ID, StandardSpeciesFeatureSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(GlobalSpeciesFeatureSyncPacket.ID, GlobalSpeciesFeatureSyncPacket::decode, DataRegistrySyncPacketHandler()))
@@ -566,6 +566,7 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(SwapPartyPokemonPacket.ID, SwapPartyPokemonPacket::decode, SwapPartyPokemonHandler))
list.add(PacketRegisterInfo(MovePCPokemonPacket.ID, MovePCPokemonPacket::decode, MovePCPokemonHandler))
list.add(PacketRegisterInfo(MovePCPokemonBatchPacket.ID, MovePCPokemonBatchPacket::decode, MovePCPokemonBatchHandler))
list.add(PacketRegisterInfo(MovePartyPokemonPacket.ID, MovePartyPokemonPacket::decode, MovePartyPokemonHandler))
list.add(PacketRegisterInfo(SwapPCPartyPokemonPacket.ID, SwapPCPartyPokemonPacket::decode, SwapPCPartyPokemonHandler))
@@ -11,9 +11,6 @@ package com.cobblemon.mod.common.api.conditional
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonPrimitive
import com.google.gson.JsonSerializationContext
import com.google.gson.JsonSerializer
import java.lang.reflect.Type
/**
@@ -24,19 +21,11 @@ import java.lang.reflect.Type
* @author Hiroku
* @since July 17th, 2022
*/
interface RegistryLikeAdapter<B> : JsonDeserializer<RegistryLikeCondition<B>>, JsonSerializer<RegistryLikeCondition<B>> {
interface RegistryLikeAdapter<B> : JsonDeserializer<RegistryLikeCondition<B>> {
val registryLikeConditions: MutableList<(JsonElement) -> RegistryLikeCondition<B>?>
override fun deserialize(json: JsonElement, type: Type, ctx: JsonDeserializationContext): RegistryLikeCondition<B> {
return registryLikeConditions.firstNotNullOfOrNull { it(json) }
?: throw IllegalArgumentException("Unable to deserialize $json")
}
override fun serialize(src: RegistryLikeCondition<B>, typeOfSrc: Type, ctx: JsonSerializationContext): JsonElement {
return when (src) {
is RegistryLikeTagCondition<B> -> JsonPrimitive(RegistryLikeTagCondition.PREFIX + src.tag.location())
is RegistryLikeIdentifierCondition<B> -> JsonPrimitive(src.identifier.toString())
else -> throw IllegalArgumentException("Unable to serialize $src")
}
}
}
@@ -98,14 +98,14 @@ object LivingEntityMoLangFunctions : AbstractMoLangFunctionHolder<LivingEntity>(
val targetEntity = params.get<MoValue>(0)
val maxDistance = params.getDoubleOrNull(1)?.toFloat() ?: 2.0F
val entity =
val target =
if (targetEntity is ObjectValue<*> && targetEntity.obj is Entity) {
targetEntity.obj as Entity
} else {
return@isLookingAt DoubleValue.ZERO
}
DoubleValue(entity.isLookingAt(entity, maxDistance))
DoubleValue(entity.isLookingAt(target, maxDistance))
}
functions["is_living_entity"] = { DoubleValue.ONE }
functions["is_flying"] = { DoubleValue(entity.isFallFlying) }
@@ -212,7 +212,7 @@ object PokemonMoLangFunctions : AbstractMoLangFunctionHolder<Pokemon>() {
// Lol. Lmao, even.
// TO-DO: Subscribe to [PokemonSpecies.observable].
functions["pre_evolution"] = { pokemon.species.preEvolution ?: DoubleValue.ZERO }
functions["nature"] = { StringValue(pokemon.nature.toString()) }
functions["nature"] = { StringValue(pokemon.nature.name.toString()) }
functions["is_wild"] = { DoubleValue(pokemon.entity?.let { it.ownerUUID == null } == true) }
functions["is_shiny"] = { DoubleValue(pokemon.shiny) }
functions["is_in_party"] = { DoubleValue(pokemon.storeCoordinates.get()?.store is PartyStore) }
@@ -10,9 +10,6 @@ package com.cobblemon.mod.common.api.pokedex
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.pokedex.def.PokedexDef
import com.cobblemon.mod.common.api.pokedex.entry.PokedexEntry
import com.cobblemon.mod.common.api.pokedex.entry.PokedexForm
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.data.PokedexDexSyncPacket
import com.cobblemon.mod.common.util.adapters.CodecBackedAdapter
@@ -64,38 +61,6 @@ object Dexes : JsonDataRegistry<PokedexDef> {
PokedexDexSyncPacket(dexEntryMap.values).sendToPlayer(player)
}
fun findDexEntryBySpeciesId(regionId: ResourceLocation, speciesId: ResourceLocation): PokedexEntry? {
return dexEntryMap[regionId]?.getEntries()?.firstOrNull { it -> it.speciesId == speciesId }
}
fun findDexEntryBySpeciesId(speciesId: ResourceLocation): PokedexEntry? {
return findDexEntryBySpeciesId(cobblemonResource("national"), speciesId)
}
fun findRootDexEntryBySpeciesId(speciesId: ResourceLocation, formName: String): Pair<PokedexEntry, PokedexForm> {
var rootSpecies = PokemonSpecies.getByIdentifier(speciesId)!!
val initialFormEquivalents = getFormEquivalents(formName)
var rootForm = rootSpecies.forms.firstOrNull { initialFormEquivalents.contains(it.name.lowercase()) }
?: rootSpecies.standardForm
var preEvolution = rootForm.preEvolution
while (preEvolution != null) {
rootSpecies = preEvolution.species
rootForm = preEvolution.resolveForm(rootForm.aspects.toSet())
preEvolution = rootForm.preEvolution
}
val entry = findDexEntryBySpeciesId(rootSpecies.resourceIdentifier)
?: throw RuntimeException("Root dex entry not found")
val formEquivalents = getFormEquivalents(rootForm.name)
val form =
entry.forms.firstOrNull { formEquivalents.contains(it.displayForm.lowercase()) } ?:
entry.forms.first { it.displayForm.equals("Normal", ignoreCase = true) }
return entry to form
}
fun getFormEquivalents(formName: String): List<String> {
val key = equivalentFormsLookup[formName.lowercase()]
return equivalentForms[key] ?: listOf(formName.lowercase())
@@ -8,13 +8,20 @@
package com.cobblemon.mod.common.api.pokedex.entry
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.data.DexEntrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.DexEvolutionDisplaySyncPacket
import com.cobblemon.mod.common.pokemon.FormData
import com.cobblemon.mod.common.util.adapters.ExpressionLikeAdapter
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
@@ -38,6 +45,8 @@ object DexEntries : JsonDataRegistry<PokedexEntry> {
val entries = mutableMapOf<ResourceLocation, PokedexEntry>()
val evolutionDisplays = mutableListOf<DexEvolutionDisplay>()
override fun reload(data: Map<ResourceLocation, PokedexEntry>) {
data.forEach { _, entry ->
entries[entry.id] = entry
@@ -50,6 +59,7 @@ object DexEntries : JsonDataRegistry<PokedexEntry> {
}
}
}
computeEvolutionDisplays()
observable.emit(this)
}
@@ -57,5 +67,58 @@ object DexEntries : JsonDataRegistry<PokedexEntry> {
override fun sync(player: ServerPlayer) {
DexEntrySyncPacket(entries.values).sendToPlayer(player)
DexEvolutionDisplaySyncPacket(evolutionDisplays).sendToPlayer(player)
}
}
fun computeEvolutionDisplays() {
evolutionDisplays.clear()
PokemonSpecies.species.forEach {
try {
evolutionDisplays.addAll(buildDisplays(it.standardForm) + it.forms.flatMap { form -> buildDisplays(form) })
} catch (e: Exception) {
Cobblemon.LOGGER.error("Failed to build dex evolution displays for ${it.resourceIdentifier}", e)
}
}
}
fun buildDisplays(form: FormData): List<DexEvolutionDisplay> {
val result = mutableListOf<DexEvolutionDisplay>()
(form.ownEvolutions ?: form.evolutions).forEach { evolution ->
val requirements = mutableListOf<DexEvolutionRequirementDisplay>()
getToolTipHeaderIcon(evolution)?.let { header ->
requirements.add(header)
}
evolution.requirements.mapNotNull { resolveRequirementData(it) }.forEach { requirement ->
requirements.add(requirement)
}
listOfNotNull(evolution.result, evolution.shedder).forEach resultLoop@{ resultProperties ->
val resultSpecies = resultProperties.species
?.let { PokemonSpecies.getByIdentifier(it.asIdentifierDefaultingNamespace()) }
?: return@resultLoop
// The result's own aspects win, otherwise carry the source form's aspects over
// like a real evolution does (Alola-Bias Pichu -> Alola-Bias Pikachu)
val resultForm = resultSpecies.getForm(resultProperties.aspects).takeIf { it != resultSpecies.standardForm }
?: resultSpecies.getForm(form.aspects.toSet() + resultProperties.aspects)
val toAspects = resultProperties.aspects + resultForm.aspects
val resultRequirements = if (resultProperties === evolution.shedder) {
requirements + CobblemonDexEvolutionRequirementDisplay(CobblemonItems.POKE_BALL, lang("ui.pokedex.evolution.shedder.tooltip"))
} else {
requirements
}
result.add(DexEvolutionDisplay(
fromSpecies = form.species.resourceIdentifier,
fromForm = form.name,
fromAspects = form.aspects.toSet(),
toSpecies = resultSpecies.resourceIdentifier,
toForm = resultForm.name,
toAspects = toAspects,
requirements = resultRequirements,
))
}
}
return result
}
}
@@ -0,0 +1,466 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokedex.entry
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.conditional.RegistryLikeIdentifierCondition
import com.cobblemon.mod.common.api.conditional.RegistryLikeTagCondition
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.api.pokemon.feature.IntSpeciesFeatureProvider
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
import com.cobblemon.mod.common.api.pokemon.requirement.Requirement
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.text.add
import com.cobblemon.mod.common.api.text.green
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolution
import com.cobblemon.mod.common.pokemon.evolution.variants.TradeEvolution
import com.cobblemon.mod.common.pokemon.requirements.*
import com.cobblemon.mod.common.util.adapters.RegistryAccessResolver
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.asTranslated
import com.cobblemon.mod.common.util.lang
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.core.registries.Registries
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.ExtraCodecs
import net.minecraft.world.item.Item
import net.minecraft.world.item.Items
import net.minecraft.world.level.biome.Biome
import net.minecraft.world.level.levelgen.structure.Structure
import kotlin.jvm.optionals.getOrNull
data class DexEvolutionDisplay(
val fromSpecies: ResourceLocation,
val fromForm: String,
val fromAspects: Set<String>,
val toSpecies: ResourceLocation,
val toForm: String,
val toAspects: Set<String>,
val requirements: List<DexEvolutionRequirementDisplay>,
)
interface DexEvolutionRequirementDisplay {
val icon: Item
val tooltip: MutableComponent
}
data class CobblemonDexEvolutionRequirementDisplay(
override val icon: Item,
override val tooltip: MutableComponent,
) : DexEvolutionRequirementDisplay
data class CobblemonBiomeDexEvolutionRequirementDisplay(
override val icon: Item,
override val tooltip: MutableComponent,
val condition: ExtraCodecs.TagOrElementLocation? = null,
val antiCondition: ExtraCodecs.TagOrElementLocation? = null,
) : DexEvolutionRequirementDisplay
fun getToolTipHeaderIcon(evolution: Evolution): DexEvolutionRequirementDisplay? {
when (evolution) {
is ItemInteractionEvolution -> return getToolTipHeaderIcon(evolution)
is TradeEvolution -> return getToolTipHeaderIcon(evolution)
}
return null
}
fun getToolTipHeaderIcon(itemInteractionEvolution: ItemInteractionEvolution): CobblemonDexEvolutionRequirementDisplay? {
val item = itemInteractionEvolution.requiredContext.items.orElse(null)?.firstOrNull()?.value() ?: return null
return CobblemonDexEvolutionRequirementDisplay(item, lang("ui.pokedex.evolution.item_interact.tooltip", item.description.plainCopy().green()))
}
fun getToolTipHeaderIcon(tradeEvolution: TradeEvolution): CobblemonDexEvolutionRequirementDisplay {
val species = tradeEvolution.requiredContext.species
val tooltip = if (species == null) {
lang("ui.pokedex.evolution.trade.tooltip.with_any_species")
} else {
PokemonSpecies.getByIdentifier(species.asIdentifierDefaultingNamespace())
?.let { lang("ui.pokedex.evolution.trade.tooltip.with_species", it.translatedName.green()) }
?: lang("ui.pokedex.evolution.trade.tooltip.with_any_species")
}
return CobblemonDexEvolutionRequirementDisplay(CobblemonItems.LINK_CABLE, tooltip)
}
fun resolveRequirementData(requirement: Requirement): DexEvolutionRequirementDisplay? {
val icon = requirement.pokedexIcon?.let {
RegistryAccessResolver.getAccess().registryOrThrow(Registries.ITEM).get(it)
}
val tooltip = requirement.pokedexTooltip?.let {
Component.translatable(it)
}
if (icon != null && tooltip != null) {
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
return buildRequirementData(requirement)
}
fun buildRequirementData(requirement: Requirement): DexEvolutionRequirementDisplay? {
when (requirement) {
is AdvancementRequirement -> return buildAdvancementRequirement(requirement)
is BattleCriticalHitsRequirement -> return buildBattleCriticalHitsRequirement(requirement)
is BiomeRequirement -> return buildBiomeRequirement(requirement)
is BlocksTraveledRequirement -> return buildBlocksTraveledRequirement(requirement)
is DamageTakenRequirement -> return buildDamageTakenRequirement(requirement)
is DefeatRequirement -> return buildDefeatRequirement(requirement)
is FriendshipRequirement -> return buildFriendshipRequirement(requirement)
is HeldItemRequirement -> return buildHeldItemRequirement(requirement)
is LevelRequirement -> return buildLevelRequirement(requirement)
is MoonPhaseRequirement -> return buildMoonPhaseRequirement(requirement)
is MoveSetRequirement -> return buildMoveSetRequirement(requirement)
is MoveTypeRequirement -> return buildMoveTypeRequirement(requirement)
is NegateRequirement -> return buildNegateRequirement(requirement)
is OwnerHoldsItemRequirement -> return buildOwnerHoldsItemRequirement(requirement)
is PartyMemberRequirement -> return buildPartyMemberRequirement(requirement)
is PokemonPropertiesRequirement -> return buildPokemonPropertiesRequirement(requirement)
is PropertyRangeRequirement -> return buildPropertyRangeRequirement(requirement)
is RecoilRequirement -> return buildRecoilRequirement(requirement)
is StatCompareRequirement -> return buildStatCompareRequirement(requirement)
is StatEqualRequirement -> return buildStatEqualRequirement(requirement)
is StructureRequirement -> return buildStructureRequirement(requirement)
is TimeRangeRequirement -> return buildTimeRangeRequirement(requirement)
is UseMoveRequirement -> return buildUseMoveRequirement(requirement)
is WeatherRequirement -> return buildWeatherRequirement(requirement)
}
return null
}
fun buildAdvancementRequirement(advancementRequirement: AdvancementRequirement): DexEvolutionRequirementDisplay {
val icon = Items.BOOK
val identifier = advancementRequirement.requiredAdvancement
val advancementLanguageKey = "${identifier.namespace}.${identifier.path.substringAfterLast('/')}"
val advancementComponent = Component.translatable("advancements.$advancementLanguageKey").green()
val tooltip = lang("ui.pokedex.requirement.advancement.tooltip", advancementComponent)
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildBattleCriticalHitsRequirement(battleCriticalHitsRequirement: BattleCriticalHitsRequirement): DexEvolutionRequirementDisplay {
val icon = CobblemonItems.MEDICINAL_LEEK
val criticalsComponent = Component.literal(battleCriticalHitsRequirement.amount.toString()).green()
val tooltip = lang("ui.pokedex.requirement.battle_critical_hits.tooltip", criticalsComponent)
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
private fun tagDisplayName(registry: String, id: ResourceLocation): MutableComponent {
return Component.translatableWithFallback("tag.$registry.${id.namespace}.${id.path.replace('/', '.')}", prettifyPath(id.path))
}
private fun prettifyPath(path: String): String {
return path.substringAfterLast('/').removePrefix("is_").split('_').joinToString(" ") { part -> part.replaceFirstChar(Char::titlecase) }
}
fun buildNegateRequirement(negateRequirement: NegateRequirement): DexEvolutionRequirementDisplay? {
val inner = negateRequirement.inner
when (inner) {
is BiomeRequirement -> buildBiomeRequirement(inner, negated = true)?.let { return it }
is StructureRequirement -> buildStructureRequirement(inner, negated = true)?.let { return it }
}
val innerDisplay = resolveRequirementData(inner) ?: return null
return CobblemonDexEvolutionRequirementDisplay(innerDisplay.icon, lang("ui.pokedex.requirement.negate.tooltip", innerDisplay.tooltip))
}
fun buildBiomeRequirement(biomeRequirement: BiomeRequirement, negated: Boolean = false): DexEvolutionRequirementDisplay? {
// Negating "in X" is "not in X"; negating both a condition and an anticondition isn't expressible as one line
if (negated && biomeRequirement.biomeCondition != null && biomeRequirement.biomeAnticondition != null) return null
val condition = if (negated) biomeRequirement.biomeAnticondition else biomeRequirement.biomeCondition
val antiCondition = if (negated) biomeRequirement.biomeCondition else biomeRequirement.biomeAnticondition
if (condition == null && antiCondition == null) {
return null
}
val icon = Items.COMPASS
val displayName: (RegistryLikeCondition<Biome>) -> MutableComponent = {
when (it) {
is RegistryLikeTagCondition -> tagDisplayName("worldgen.biome", it.tag.location())
is RegistryLikeIdentifierCondition -> Component.translatable("biome.${it.identifier.namespace}.${it.identifier.path}")
else -> Component.literal(it.toString())
}
}
val tooltip = if (condition != null && antiCondition != null) {
lang("ui.pokedex.requirement.biome.both.tooltip", displayName(condition).green(), displayName(antiCondition).green())
} else if (condition != null) {
lang("ui.pokedex.requirement.biome.tooltip", displayName(condition).green())
} else {
lang("ui.pokedex.requirement.biome.anticondition.tooltip", displayName(antiCondition!!).green())
}
var conditionConverted: ExtraCodecs.TagOrElementLocation? = null
if (condition != null) {
val conditionIdentifier = if (condition is RegistryLikeIdentifierCondition) condition.identifier else (condition as RegistryLikeTagCondition).tag.location
conditionConverted = ExtraCodecs.TagOrElementLocation(conditionIdentifier, condition is RegistryLikeTagCondition)
}
var antiConditionConverted: ExtraCodecs.TagOrElementLocation? = null
if (antiCondition != null) {
val conditionIdentifier = if (antiCondition is RegistryLikeIdentifierCondition) antiCondition.identifier else (antiCondition as RegistryLikeTagCondition).tag.location
antiConditionConverted = ExtraCodecs.TagOrElementLocation(conditionIdentifier, antiCondition is RegistryLikeTagCondition)
}
return CobblemonBiomeDexEvolutionRequirementDisplay(icon, tooltip, conditionConverted, antiConditionConverted)
}
fun buildBlocksTraveledRequirement(blocksTraveledRequirement: BlocksTraveledRequirement): DexEvolutionRequirementDisplay {
val icon = Items.LEATHER_BOOTS
val blocksComponent = Component.literal(blocksTraveledRequirement.amount.toString()).green()
val tooltip = lang("ui.pokedex.requirement.blocks_traveled.tooltip", blocksComponent)
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildDamageTakenRequirement(damageTakenRequirement: DamageTakenRequirement): DexEvolutionRequirementDisplay {
val icon = CobblemonItems.PUNCHING_GLOVE
val blocksComponent = Component.literal(damageTakenRequirement.amount.toString()).green()
val tooltip = lang("ui.pokedex.requirement.damage_taken.tooltip", blocksComponent)
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildDefeatRequirement(defeatRequirement: DefeatRequirement): DexEvolutionRequirementDisplay? {
val description = describePokemonProperties(defeatRequirement.target) ?: return null
val icon = CobblemonItems.PUNCHING_GLOVE
val defeatedAmount = defeatRequirement.amount
val descriptionComponent = Component.literal(defeatedAmount.toString()).green().add(" ").add(description)
val tooltip = lang("ui.pokedex.requirement.defeat.tooltip", descriptionComponent)
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildFriendshipRequirement(friendshipRequirement: FriendshipRequirement): DexEvolutionRequirementDisplay {
val icon = CobblemonItems.SOOTHE_BELL
val friendshipAmountComponent = Component.literal(friendshipRequirement.amount.toString()).green()
val tooltip = lang("ui.pokedex.requirement.friendship.tooltip", friendshipAmountComponent)
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildHeldItemRequirement(heldItemRequirement: HeldItemRequirement): DexEvolutionRequirementDisplay? {
val items = heldItemRequirement.itemCondition.items.getOrNull() ?: return null
val item = items.firstOrNull()?.value() ?: return null
val itemComponent = items.unwrapKey().map { tag ->
val id = tag.location()
val fallback = id.path.substringAfterLast('/').split('_').joinToString(" ") { part -> part.replaceFirstChar(Char::titlecase) }
Component.translatableWithFallback("tag.item.${id.namespace}.${id.path.replace('/', '.')}", fallback)
}.orElseGet { item.description.plainCopy() }
val tooltip = lang("ui.pokedex.requirement.held_item.tooltip", itemComponent.green())
return CobblemonDexEvolutionRequirementDisplay(item, tooltip)
}
fun buildLevelRequirement(levelRequirement: LevelRequirement): DexEvolutionRequirementDisplay {
val minLevel = levelRequirement.minLevel
val maxLevel = levelRequirement.maxLevel
val icon = CobblemonItems.RARE_CANDY
val tooltip =
if (minLevel == maxLevel || maxLevel == Int.MAX_VALUE)
lang("ui.pokedex.requirement.level.tooltip.min", Component.literal(minLevel.toString()).green())
else
lang("ui.pokedex.requirement.level.tooltip.between", Component.literal(minLevel.toString()).green(), Component.literal(maxLevel.toString()).green())
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildMoonPhaseRequirement(moonPhaseRequirement: MoonPhaseRequirement): DexEvolutionRequirementDisplay {
val icon = Items.CLOCK
val moonPhaseComponent = moonPhaseRequirement.moonPhase.displayName().green()
val tooltip = lang("ui.pokedex.requirement.moon_phase.tooltip", moonPhaseComponent)
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildMoveSetRequirement(moveSetRequirement: MoveSetRequirement): DexEvolutionRequirementDisplay {
val icon = CobblemonItems.TECHNICAL_MACHINE
val tooltip = lang("ui.pokedex.requirement.has_move.tooltip", moveSetRequirement.move.displayName.green())
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildMoveTypeRequirement(moveTypeRequirement: MoveTypeRequirement): DexEvolutionRequirementDisplay {
val type = ElementalTypes.get(moveTypeRequirement.type.name)
val icon = when (type) {
ElementalTypes.NORMAL -> CobblemonItems.NORMAL_GEM
ElementalTypes.FIRE -> CobblemonItems.FIRE_GEM
ElementalTypes.WATER -> CobblemonItems.WATER_GEM
ElementalTypes.GRASS -> CobblemonItems.GRASS_GEM
ElementalTypes.ELECTRIC -> CobblemonItems.ELECTRIC_GEM
ElementalTypes.ICE -> CobblemonItems.ICE_GEM
ElementalTypes.FIGHTING -> CobblemonItems.FIGHTING_GEM
ElementalTypes.POISON -> CobblemonItems.POISON_GEM
ElementalTypes.GROUND -> CobblemonItems.GROUND_GEM
ElementalTypes.FLYING -> CobblemonItems.FLYING_GEM
ElementalTypes.PSYCHIC -> CobblemonItems.PSYCHIC_GEM
ElementalTypes.BUG -> CobblemonItems.BUG_GEM
ElementalTypes.ROCK -> CobblemonItems.ROCK_GEM
ElementalTypes.GHOST -> CobblemonItems.GHOST_GEM
ElementalTypes.DRAGON -> CobblemonItems.DRAGON_GEM
ElementalTypes.DARK -> CobblemonItems.DARK_GEM
ElementalTypes.STEEL -> CobblemonItems.STEEL_GEM
ElementalTypes.FAIRY -> CobblemonItems.FAIRY_GEM
else -> CobblemonItems.NORMAL_GEM
}
val tooltip = lang("ui.pokedex.requirement.has_move_type.tooltip", moveTypeRequirement.type.displayName.green())
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildOwnerHoldsItemRequirement(ownerHoldsItemRequirement: OwnerHoldsItemRequirement): DexEvolutionRequirementDisplay? {
val item = ownerHoldsItemRequirement.itemCondition.items.orElse(null)?.firstOrNull()?.value() ?: return null
val tooltip = lang("ui.pokedex.requirement.owner_held_item.tooltip", item.description.plainCopy().green())
return CobblemonDexEvolutionRequirementDisplay(item, tooltip)
}
fun buildPartyMemberRequirement(partyMemberRequirement: PartyMemberRequirement): DexEvolutionRequirementDisplay? {
val description = describePokemonProperties(partyMemberRequirement.target) ?: return null
val icon = CobblemonItems.POKE_BALL
val tooltip = if (partyMemberRequirement.contains) {
lang("ui.pokedex.requirement.party_member.tooltip", description)
} else {
lang("ui.pokedex.requirement.party_member.anticondition.tooltip", description)
}
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildPokemonPropertiesRequirement(pokemonPropertiesRequirement: PokemonPropertiesRequirement): DexEvolutionRequirementDisplay? {
val description = describePokemonProperties(pokemonPropertiesRequirement.target) ?: return null
val icon = Items.NAME_TAG
val tooltip = lang("ui.pokedex.requirement.pokemon_properties.tooltip", description)
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildPropertyRangeRequirement(propertyRangeRequirement: PropertyRangeRequirement): DexEvolutionRequirementDisplay? {
val provider = SpeciesFeatures.getFeature(propertyRangeRequirement.feature) as? IntSpeciesFeatureProvider ?: return null
val displayName = provider.display?.name?.takeIf { it.isNotBlank() }?.asTranslated()?.green() ?: return null
val icon = provider.itemPoints.keys.firstOrNull()?.let { BuiltInRegistries.ITEM.get(it) } ?: Items.NAME_TAG
val range = propertyRangeRequirement.range
val tooltip = lang(
"ui.pokedex.requirement.property_range.tooltip",
displayName,
Component.literal(range.first.toString()).green(),
Component.literal(range.last.toString()).green()
)
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildRecoilRequirement(recoilRequirement: RecoilRequirement): DexEvolutionRequirementDisplay {
val icon = CobblemonItems.LIFE_ORB
val recoilComponent = Component.literal(recoilRequirement.amount.toString()).green()
val tooltip = lang("ui.pokedex.requirement.recoil.tooltip", recoilComponent)
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildStatCompareRequirement(statCompareRequirement: StatCompareRequirement): DexEvolutionRequirementDisplay {
val highStat = Stats.getStat(statCompareRequirement.highStat)
val lowStat = Stats.getStat(statCompareRequirement.lowStat)
val icon = when (highStat) {
Stats.HP, Stats.EVASION, Stats.ACCURACY -> CobblemonItems.HEALTH_MOCHI
Stats.ATTACK -> CobblemonItems.MUSCLE_MOCHI
Stats.DEFENCE -> CobblemonItems.RESIST_MOCHI
Stats.SPECIAL_ATTACK -> CobblemonItems.GENIUS_MOCHI
Stats.SPECIAL_DEFENCE -> CobblemonItems.CLEVER_MOCHI
Stats.SPEED -> CobblemonItems.SWIFT_MOCHI
}
val highStatComponent = highStat.displayName.plainCopy().green()
val lowStatComponent = lowStat.displayName.plainCopy().green()
val tooltip = lang("ui.pokedex.requirement.stat_compare.tooltip", highStatComponent, lowStatComponent)
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildStatEqualRequirement(statEqualRequirement: StatEqualRequirement): DexEvolutionRequirementDisplay {
val statOne = Stats.getStat(statEqualRequirement.statOne)
val statTwo = Stats.getStat(statEqualRequirement.statTwo)
val statOneComponent = statOne.displayName.plainCopy().green()
val statTwoComponent = statTwo.displayName.plainCopy().green()
val icon = CobblemonItems.FRESH_START_MOCHI
val tooltip = lang("ui.pokedex.requirement.stat_equal.tooltip", statOneComponent, statTwoComponent)
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildStructureRequirement(structureRequirement: StructureRequirement, negated: Boolean = false): DexEvolutionRequirementDisplay? {
if (negated && structureRequirement.structureCondition != null && structureRequirement.structureAnticondition != null) return null
val condition = if (negated) structureRequirement.structureAnticondition else structureRequirement.structureCondition
val anticondition = if (negated) structureRequirement.structureCondition else structureRequirement.structureAnticondition
if (condition == null && anticondition == null) {
return null
}
val icon = Items.FILLED_MAP
val displayName: (RegistryLikeCondition<Structure>) -> MutableComponent = {
when (it) {
is RegistryLikeTagCondition -> tagDisplayName("worldgen.structure", it.tag.location())
is RegistryLikeIdentifierCondition -> Component.literal(prettifyPath(it.identifier.path))
else -> Component.literal(it.toString())
}
}
val tooltip = if (condition != null && anticondition != null) {
lang("ui.pokedex.requirement.structure.both.tooltip", displayName(condition).green(), displayName(anticondition).green())
} else if (condition != null) {
lang("ui.pokedex.requirement.structure.tooltip", displayName(condition).green())
} else {
lang("ui.pokedex.requirement.structure.anticondition.tooltip", displayName(anticondition!!).green())
}
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildTimeRangeRequirement(timeRangeRequirement: TimeRangeRequirement): DexEvolutionRequirementDisplay? {
val icon = Items.CLOCK
val timeRangeComponent = timeRangeRequirement.range.displayName() ?: return null
val tooltip = lang("ui.pokedex.requirement.time_range.tooltip", timeRangeComponent.green())
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildUseMoveRequirement(useMoveRequirement: UseMoveRequirement): DexEvolutionRequirementDisplay {
val icon = CobblemonItems.CHOICE_BAND
val tooltip = lang("ui.pokedex.requirement.use_move.tooltip", useMoveRequirement.move.displayName.green())
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildWeatherRequirement(weatherRequirement: WeatherRequirement): DexEvolutionRequirementDisplay {
val icon: Item
val tooltip: MutableComponent
if (weatherRequirement.isThundering == true) {
icon = Items.LIGHTNING_ROD
tooltip = lang("ui.pokedex.requirement.weather.tooltip.thundering")
} else {
icon = Items.WATER_BUCKET
tooltip = lang("ui.pokedex.requirement.weather.tooltip.raining")
}
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.pokedex.widgets.evolutiontree
package com.cobblemon.mod.common.api.pokedex.entry
import com.cobblemon.mod.common.api.pokemon.Natures
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
@@ -28,7 +28,7 @@ import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
import net.minecraft.world.item.Items
internal fun describePokemonProperties(properties: PokemonProperties): MutableComponent? {
fun describePokemonProperties(properties: PokemonProperties): MutableComponent? {
if (hasUndescribedProperty(properties)) {
return null
}
@@ -26,7 +26,6 @@ import com.cobblemon.mod.common.api.moves.MoveTemplate
import com.cobblemon.mod.common.api.moves.MovesetBuilder
import com.cobblemon.mod.common.api.moves.adapters.MoveTemplateAdapter
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies.getByIdentifier
import com.cobblemon.mod.common.api.pokemon.effect.ShoulderEffect
import com.cobblemon.mod.common.api.pokemon.effect.adapter.ShoulderEffectAdapter
import com.cobblemon.mod.common.api.pokemon.egg.EggGroup
@@ -44,9 +43,6 @@ import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.api.spawning.TimeRange
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.api.types.adapters.ElementalTypeAdapter
import com.cobblemon.mod.common.net.messages.client.data.FormEvolutionOverride
import com.cobblemon.mod.common.net.messages.client.data.SpeciesEvolutionEntry
import com.cobblemon.mod.common.net.messages.client.data.SpeciesEvolutionSyncPacket
import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPacket
import com.cobblemon.mod.common.pokemon.FormData
import com.cobblemon.mod.common.pokemon.Species
@@ -246,18 +242,6 @@ object PokemonSpecies : JsonDataRegistry<Species> {
override fun sync(player: ServerPlayer) {
SpeciesRegistrySyncPacket(species.toList()).sendToPlayer(player)
// Sent after the species themselves so every species is already registered by the time this is decoded.
val evolutionEntries = species.map { singleSpecies ->
val formOverrides = singleSpecies.forms.mapNotNull { form ->
if (form.ownEvolutions == null && form.ownPreEvolution == null) {
null
} else {
FormEvolutionOverride(form.name, form.ownEvolutions, form.ownPreEvolution)
}
}
SpeciesEvolutionEntry(singleSpecies.resourceIdentifier, singleSpecies.evolutions, singleSpecies.preEvolution, formOverrides)
}
SpeciesEvolutionSyncPacket(evolutionEntries).sendToPlayer(player)
}
/**
@@ -25,8 +25,6 @@ interface PreEvolution {
val form: FormData
fun resolveForm(extraAspects: Set<String> = emptySet()): FormData = form
companion object {
fun of(species: Species, form: FormData = species.standardForm): PreEvolution = StandardPreEvolution(species, form)
@@ -29,6 +29,7 @@ import com.cobblemon.mod.common.net.messages.client.storage.pc.InitializePCPacke
import com.cobblemon.mod.common.net.messages.client.storage.pc.MoveClientPCPokemonPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.SetPCPokemonPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.UnlockPCBoxWallpaperPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePCPokemonBatchPacket
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.asTranslated
@@ -270,6 +271,34 @@ open class PCStore(
}
}
fun moveBatch(moves: List<Triple<UUID, PCPosition, PCPosition>>): Boolean {
if (moves.isEmpty() || moves.size > MovePCPokemonBatchPacket.MAX_MOVES) return false
if (moves.map { it.second }.toSet().size != moves.size ||
moves.map { it.third }.toSet().size != moves.size ||
moves.map { it.first }.toSet().size != moves.size
) return false
if (moves.any { (_, source, target) -> !isValidPosition(source) || !isValidPosition(target) }) return false
val sourcePositions = moves.mapTo(mutableSetOf()) { it.second }
val pokemonMoves = moves.map { (pokemonID, source, target) ->
val pokemon = get(source)?.takeIf { it.uuid == pokemonID } ?: return false
Triple(source, target, pokemon)
}
if (pokemonMoves.any { (_, target, _) -> get(target) != null && target !in sourcePositions }) return false
pokemonMoves.forEach { (source, _, _) -> setAtPosition(source, null) }
pokemonMoves.forEach { (_, target, pokemon) -> setAtPosition(target, pokemon) }
pokemonMoves.forEach { (_, _, pokemon) ->
sendPacketToObservers(RemoveClientPokemonPacket(this, pokemon.uuid))
}
pokemonMoves.forEach { (_, target, pokemon) ->
sendPacketToObservers(SetPCPokemonPacket(uuid, target) { pokemon })
}
pcChangeObservable.emit(Unit)
return true
}
override fun loadPositionFromNBT(nbt: CompoundTag): StoreCoordinates<PCPosition> {
return StoreCoordinates(this, PCPosition(nbt.getShort(DataKeys.STORE_BOX).toInt(), nbt.getByte(DataKeys.STORE_SLOT).toInt()))
}
@@ -120,7 +120,7 @@ object CobblemonItemTags {
@JvmField val DESTINY_KNOT = create("held/destiny_knot")
@JvmField val EVERSTONE = create("held/everstone")
@JvmField val EXPERIENCE_SHARE = create("held/experience_share")
@JvmField val IS_FRIENDSHIP_BOOSTER = create("is_friendship_booster")
@JvmField val IS_FRIENDSHIP_BOOSTER = create("held/is_friendship_booster")
@JvmField val ANY_HELD_ITEM = create("held/is_held_item")
@JvmField val BLACKLISTED_ITEMS_TO_HOLD = create("held/blacklisted_items_to_hold")
@JvmField val WHITELISTED_ITEMS_TO_HOLD = create("held/whitelisted_items_to_hold")
@@ -29,7 +29,8 @@ class BoxStorageSlot(
val grabbedSlot = parent.grabbedSlot
return if (grabbedSlot == null) {
true
val pokemon = getPokemon()
pokemon == null || !parent.isInMultiSelection(pokemon, position)
} else {
grabbedSlot.getPokemon() != getPokemon()
}
@@ -50,13 +50,15 @@ import net.minecraft.network.chat.Component
import net.minecraft.sounds.SoundEvent
import kotlin.isInitialized
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.FastColor
class PCGUI(
val pc: ClientPC,
val party: ClientParty,
val configuration: PCGUIConfiguration,
val openOnBox: Int = CobblemonClient.lastPcBoxViewed,
val unseenWallpapers: MutableSet<ResourceLocation> = mutableSetOf()
val unseenWallpapers: MutableSet<ResourceLocation> = mutableSetOf(),
val silent: Boolean = false
) : Screen(Component.translatable("cobblemon.ui.pc.title")), CobblemonRenderable {
companion object {
@@ -128,6 +130,55 @@ class PCGUI(
var selectPointerOffsetIncrement = false
var displayOptions = false
private var dragStartX: Int? = null
private var dragStartY: Int? = null
private var dragEndX: Int? = null
private var dragEndY: Int? = null
private var selectionDragActive = false
data class SelectionArea(val left: Int, val top: Int, val right: Int, val bottom: Int)
private data class BoxGridBounds(val left: Int, val top: Int, val right: Int, val bottom: Int) {
fun contains(x: Int, y: Int) = x in left..< right && y >= top && y < bottom
}
private fun getBoxGridBounds(): BoxGridBounds {
val left = storageWidget.x
val top = storageWidget.y
return BoxGridBounds(
left,
top,
left + StorageWidget.SCREEN_WIDTH,
top + StorageWidget.SCREEN_HEIGHT
)
}
fun getSelectionArea(): SelectionArea? {
if (!selectionDragActive) return null
val startX = dragStartX ?: return null
val startY = dragStartY ?: return null
val endX = dragEndX ?: return null
val endY = dragEndY ?: return null
val bounds = getBoxGridBounds()
if (!bounds.contains(startX, startY)) return null
val columns = 6
val rows = 5
val slotPitch = StorageSlot.SIZE + StorageWidget.BOX_SLOT_PADDING
val gridLeft = bounds.left + StorageWidget.BOX_SLOT_START_OFFSET_X
val gridTop = bounds.top + StorageWidget.BOX_SLOT_START_OFFSET_Y + if (displayOptions) 5 else 0
val startColumn = ((startX - gridLeft) / slotPitch).coerceIn(0, columns - 1)
val endColumn = (((endX.coerceIn(bounds.left, bounds.right - 1)) - gridLeft) / slotPitch).coerceIn(0, columns - 1)
val startRow = ((startY - gridTop) / slotPitch).coerceIn(0, rows - 1)
val endRow = (((endY.coerceIn(bounds.top, bounds.bottom - 1)) - gridTop) / slotPitch).coerceIn(0, rows - 1)
return SelectionArea(
gridLeft + minOf(startColumn, endColumn) * slotPitch, // left
gridTop + minOf(startRow, endRow) * slotPitch, // top
gridLeft + (maxOf(startColumn, endColumn) * slotPitch) + StorageSlot.SIZE, // right
gridTop + (maxOf(startRow, endRow) * slotPitch) + StorageSlot.SIZE // bottom
)
}
override fun renderBlurredBackground(delta: Float) {}
override fun renderMenuBackground(context: GuiGraphics) {}
@@ -249,7 +300,7 @@ class PCGUI(
configuration.showParty = isVisible
wallpaperWidget.visible = !isVisible
(it as IconButton).highlighted = !isVisible
if (!unseenWallpapers.isEmpty()) MarkPCBoxWallpapersSeenPacket(unseenWallpapers).sendToServer()
if (unseenWallpapers.isNotEmpty()) MarkPCBoxWallpapersSeenPacket(unseenWallpapers).sendToServer()
}.also {
it.visible = displayOptions
optionButtons.add(it)
@@ -314,6 +365,20 @@ class PCGUI(
super.init()
}
private fun renderSelectionBox(context: GuiGraphics, area: SelectionArea) {
context.pose().pushPose()
context.pose().translate(0.0, 0.0, 1500.0)
val borderColor = FastColor.ARGB32.color(255, 205, 212, 255)
val bgColor = FastColor.ARGB32.color(75, 133, 183, 230)
val thickness = 1
context.fill(area.left, area.top, area.right, area.bottom, bgColor)
context.fill(area.left, area.top, area.right, area.top + thickness, borderColor)
context.fill(area.left, area.bottom - thickness, area.right, area.bottom, borderColor)
context.fill(area.left, area.top, area.left + thickness, area.bottom, borderColor)
context.fill(area.right - thickness, area.top, area.right, area.bottom, borderColor)
context.pose().popPose()
}
override fun render(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
val matrices = context.pose()
renderBackground(context, mouseX, mouseY, delta)
@@ -684,12 +749,14 @@ class PCGUI(
scale = SCALE
)
if (!optionButtons.isEmpty() && displayOptions) {
if (optionButtons.isNotEmpty() && displayOptions) {
for (button in optionButtons) button.showAlt = hasShiftDown()
}
super.render(context, mouseX, mouseY, delta)
getSelectionArea()?.let { renderSelectionBox(context, it) }
// Item Tooltip
if (pokemon != null) {
val displayedItem = if (showCosmeticItem) pokemon.cosmeticItem else pokemon.heldItemNoCopy()
@@ -703,7 +770,7 @@ class PCGUI(
}
fun closeNormally(unlink: Boolean = true) {
playSound(CobblemonSounds.PC_OFF)
if (!silent) playSound(CobblemonSounds.PC_OFF)
Minecraft.getInstance().setScreen(null)
if (unlink) {
UnlinkPlayerFromPCPacket().sendToServer()
@@ -711,6 +778,20 @@ class PCGUI(
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
storageWidget.clearPendingStorageSlotClick()
selectionDragActive = false
val clickX = mouseX.toInt()
val clickY = mouseY.toInt()
if (storageWidget.multiSelection == null && button == 0 &&
getBoxGridBounds().contains(clickX, clickY) &&
storageWidget.pastureWidget == null) {
dragStartX = clickX
dragStartY = clickY
} else {
dragStartX = null
dragStartY = null
}
// Trigger child component function as they will need to check the entire screen click area
if (::boxNameWidget.isInitialized) boxNameWidget.mouseClicked(mouseX, mouseY, button)
return super.mouseClicked(mouseX, mouseY, button)
@@ -739,16 +820,50 @@ class PCGUI(
storageWidget.pastureWidget?.let { pasture ->
if (pasture.pastureScrollList.isHovered) pasture.pastureScrollList.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
}
if (button == 0 && storageWidget.pastureWidget == null) {
val startX = dragStartX
val startY = dragStartY
if (startX != null && startY != null) {
val offsetX = mouseX - startX
val offsetY = mouseY - startY
if (offsetX * offsetX + offsetY * offsetY >= 25.0) {
selectionDragActive = true
storageWidget.clearPendingStorageSlotClick()
dragEndX = mouseX.toInt()
dragEndY = mouseY.toInt()
}
}
}
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
}
override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean {
val handled = super.mouseReleased(mouseX, mouseY, button)
if (button == 0) {
if (selectionDragActive) {
dragEndX = mouseX.toInt()
dragEndY = mouseY.toInt()
storageWidget.updateSelection()
storageWidget.clearPendingStorageSlotClick()
} else {
storageWidget.completePendingStorageSlotClick(mouseX, mouseY)
}
}
dragStartX = null
dragStartY = null
dragEndX = null
dragEndY = null
selectionDragActive = false
return handled
}
override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
val boxNameFocused = this::boxNameWidget.isInitialized && boxNameWidget.isFocused
val filterFocused = this::filterWidget.isInitialized && filterWidget.isFocused
if (isInventoryKeyPressed(minecraft, keyCode, scanCode) && !boxNameFocused && !filterFocused) {
saveMarkings(isPreviewInParty ?: false)
playSound(CobblemonSounds.PC_OFF)
if (!silent) playSound(CobblemonSounds.PC_OFF)
UnlinkPlayerFromPCPacket().sendToServer()
Minecraft.getInstance().setScreen(null)
return true
@@ -759,7 +874,7 @@ class PCGUI(
if (!filterFocused && !boxNameFocused) {
when (keyCode) {
InputConstants.KEY_ESCAPE -> {
playSound(CobblemonSounds.PC_OFF)
if (!silent) playSound(CobblemonSounds.PC_OFF)
UnlinkPlayerFromPCPacket().sendToServer()
onClose()
return true
@@ -831,4 +946,4 @@ class PCGUI(
markingsWidget.setActivePokemon(null)
}
}
}
}
@@ -0,0 +1,37 @@
package com.cobblemon.mod.common.client.gui.pc
import com.cobblemon.mod.common.pokemon.Pokemon
data class PCMultiSelection(
private val grid: MutableList<MutableList<Pokemon?>> = mutableListOf(),
val sourceBox: Int = 0,
val sourceColumn: Int = 0,
val sourceRow: Int = 0
) {
fun getHorizontalSize(): Int = grid.firstOrNull()?.size ?: 0
fun getVerticalSize(): Int = grid.size
constructor(horizontalSize: Int, verticalSize: Int, sourceBox: Int, sourceColumn: Int, sourceRow: Int) : this(
MutableList(verticalSize) { MutableList<Pokemon?>(horizontalSize) { null } },
sourceBox,
sourceColumn,
sourceRow
)
fun getPokemon(x: Int, y: Int): Pokemon? {
return grid[y][x]
}
fun setPokemon(x: Int, y: Int, pokemon: Pokemon?) {
grid[y][x] = pokemon
}
fun pokemonEntries(): List<Triple<Int, Int, Pokemon>> {
return grid.flatMapIndexed { y, row ->
row.mapIndexedNotNull { x, pokemon -> pokemon?.let { Triple(x, y, it) } }
}
}
fun getPokemonCount(): Int = pokemonEntries().size
}
@@ -45,11 +45,13 @@ class ReleaseButton(
vOffset = if (isHovered(mouseX.toDouble(), mouseY.toDouble())) HEIGHT else 0,
textureHeight = HEIGHT * 2
)
val pokemonCount = parent.multiSelection?.getPokemonCount() ?: 0
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = lang("ui.pc.release").bold(),
text = if (pokemonCount > 1)
{ lang("ui.pc.release_all").bold() } else { lang("ui.pc.release").bold() },
x = x + (WIDTH / 2),
y = y + 3.5,
centered = true,
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.client.gui.pc
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.gui.ColourLibrary
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
@@ -54,9 +55,17 @@ open class StorageSlot(
}
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
val selectionArea = parent.pcGui.getSelectionArea()
if (selectionArea != null && parent.getSelectedPokemon(selectionArea).contains(this)) {
val highlightColor = 0x40FFFFFF
context.fill(x + 1, y, x + (SIZE - 1), y + SIZE, highlightColor)
context.fill(x, y + 1, x + 1, y + (SIZE - 1), highlightColor)
context.fill(x + SIZE - 1, y + 1, x + SIZE, y + (SIZE - 1), highlightColor)
}
if (shouldRender()) {
renderSlot(context, x, y, delta)
}
}
fun renderSlot(context: GuiGraphics, posX: Int, posY: Int, partialTicks: Float) {
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.client.gui.pc
import com.cobblemon.mod.common.CobblemonNetwork
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.storage.StorePosition
import com.cobblemon.mod.common.api.storage.party.PartyPosition
import com.cobblemon.mod.common.api.storage.pc.PCPosition
import com.cobblemon.mod.common.api.text.bold
@@ -39,8 +38,9 @@ import net.minecraft.client.gui.components.Button
import net.minecraft.client.gui.screens.Screen
import net.minecraft.client.resources.sounds.SimpleSoundInstance
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.sounds.SoundEvent
import java.util.UUID
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePCPokemonBatchPacket.PCPokemonMove
class StorageWidget(
pX: Int, pY: Int,
@@ -77,9 +77,13 @@ class StorageWidget(
var displayConfirmRelease = false
var screenLoaded = false
var selectedPosition: StorePosition? = null
var selectedPosition: PartyPosition? = null
var grabbedSlot: GrabbedStorageSlot? = null
var multiSelection: PCMultiSelection? = null
private val multiGrabbedSlots = mutableMapOf<UUID, GrabbedStorageSlot>()
private var pendingStorageSlotClick: Button? = null
var box = 0
set(value) {
field =
@@ -113,16 +117,22 @@ class StorageWidget(
subKey = "ui.generic.yes",
onPress = {
if (canDeleteSelected() && displayConfirmRelease) {
val position = selectedPosition ?: return@ReleaseConfirmButton
val pokemon = getSelectedPokemon() ?: return@ReleaseConfirmButton
val selection = multiSelection
if (selection != null) {
selection.pokemonEntries().forEach { (column, row, pokemon) ->
val position = PCPosition(
selection.sourceBox,
(selection.sourceRow + row) * 6 + selection.sourceColumn + column
)
CobblemonNetwork.sendToServer(ReleasePCPokemonPacket(pokemon.uuid, position))
}
} else {
val position = selectedPosition ?: return@ReleaseConfirmButton
val pokemon = getSelectedPokemon() ?: return@ReleaseConfirmButton
val packet = when (position) {
is PartyPosition -> ReleasePartyPokemonPacket(pokemon.uuid, position)
is PCPosition -> ReleasePCPokemonPacket(pokemon.uuid, position)
else -> return@ReleaseConfirmButton
CobblemonNetwork.sendToServer(ReleasePartyPokemonPacket(pokemon.uuid, position))
}
CobblemonNetwork.sendToServer(packet)
playSound(CobblemonSounds.PC_RELEASE)
resetSelected()
displayConfirmRelease = false
@@ -149,9 +159,8 @@ class StorageWidget(
}
fun canDeleteSelected(): Boolean {
return !(selectedPosition is PartyPosition && party.filterNotNull().size <= 1)
&& selectedPosition != null
&& grabbedSlot != null
multiSelection?.let { return it.pokemonEntries().isNotEmpty() }
return party.filterNotNull().size > 1 && selectedPosition != null && grabbedSlot != null
}
fun setupStorageSlots() {
@@ -170,7 +179,7 @@ class StorageWidget(
parent = this,
pc = pc,
position = PCPosition(box, index),
onPress = { this.onStorageSlotClicked(it) }
onPress = { pendingStorageSlotClick = it }
).also { widget ->
this.addWidget(widget)
this.boxSlots.add(widget)
@@ -200,7 +209,7 @@ class StorageWidget(
parent = this,
party = party,
position = PartyPosition(partyIndex),
onPress = { this.onStorageSlotClicked(it) }
onPress = { pendingStorageSlotClick = it }
).also { widget ->
this.addWidget(widget)
this.partySlots.add(widget)
@@ -210,13 +219,115 @@ class StorageWidget(
private fun getSelectedPokemon(): Pokemon? {
val selectedPosition = this.selectedPosition ?: return null
return when (selectedPosition) {
is PCPosition -> pc.get(selectedPosition)
is PartyPosition -> party.get(selectedPosition)
else -> null
return party.get(selectedPosition)
}
fun getSelectedPokemon(area: PCGUI.SelectionArea): List<BoxStorageSlot> {
return boxSlots.filter { slot ->
slot.x < area.right && slot.x + StorageSlot.SIZE > area.left &&
slot.y < area.bottom && slot.y + StorageSlot.SIZE > area.top &&
slot.getPokemon()?.let(pcGui.search::passes) == true
}
}
fun clearPendingStorageSlotClick() {
pendingStorageSlotClick = null
}
fun completePendingStorageSlotClick(mouseX: Double, mouseY: Double) {
val button = pendingStorageSlotClick ?: return
pendingStorageSlotClick = null
if (button.isMouseOver(mouseX, mouseY)) {
onStorageSlotClicked(button)
}
}
fun updateSelection() {
val area = pcGui.getSelectionArea()
if (area == null) {
multiSelection = null
return
}
selectedPosition = null
grabbedSlot = null
pcGui.setPreviewPokemon(null)
val selectedSlots = getSelectedPokemon(area)
if (selectedSlots.isEmpty()) {
multiSelection = null
return
}
val minColumn = selectedSlots.minOf { it.position.slot % 6 }
val maxColumn = selectedSlots.maxOf { it.position.slot % 6 }
val minRow = selectedSlots.minOf { it.position.slot / 6 }
val maxRow = selectedSlots.maxOf { it.position.slot / 6 }
val selection = PCMultiSelection(maxColumn - minColumn + 1, maxRow - minRow + 1, box, minColumn, minRow)
selectedSlots.forEach { slot ->
val pokemon = slot.getPokemon() ?: return@forEach
val slotIndex = slot.position.slot
val column = slotIndex % 6
val row = slotIndex / 6
if (column in minColumn..maxColumn && row in minRow..maxRow) {
selection.setPokemon(column - minColumn, row - minRow, pokemon)
}
}
playSound(CobblemonSounds.PC_GRAB)
multiSelection = selection
}
private fun moveMultiSelectionTo(destination: PCPosition): Boolean {
val selection = multiSelection ?: return false
val width = selection.getHorizontalSize()
val height = selection.getVerticalSize()
val destinationColumn = destination.slot % 6
val destinationRow = destination.slot / 6
if (destinationColumn + width > 6 || destinationRow + height > 5) return false
val moves = mutableListOf<PCPokemonMove>()
val selectedSources = mutableSetOf<PCPosition>()
for (row in 0 until height) {
for (column in 0 until width) {
val pokemon = selection.getPokemon(column, row) ?: continue
val source = PCPosition(selection.sourceBox, (selection.sourceRow + row) * 6 + selection.sourceColumn + column)
val target = PCPosition(destination.box, (destinationRow + row) * 6 + destinationColumn + column)
selectedSources.add(source)
moves.add(PCPokemonMove(pokemon.uuid, source, target))
}
}
if (moves.isEmpty()) return false
for (row in 0 until height) {
for (column in 0 until width) {
if (selection.getPokemon(column, row) == null) continue
val target = PCPosition(destination.box, (destinationRow + row) * 6 + destinationColumn + column)
val displacedPokemon = pc.get(target) ?: continue
if (target in selectedSources) continue
val displacedSource = PCPosition(
selection.sourceBox,
(selection.sourceRow + row) * 6 + selection.sourceColumn + column
)
if (pc.get(displacedSource) != null && displacedSource !in selectedSources) return false
moves.add(PCPokemonMove(displacedPokemon.uuid, target, displacedSource))
}
}
if (moves.map { it.newPosition }.toSet().size != moves.size) return false
MovePCPokemonBatchPacket(moves).sendToServer()
return true
}
fun isInMultiSelection(pokemon: Pokemon, position: PCPosition): Boolean {
val selection = multiSelection ?: return false
return selection.sourceBox == position.box && selection.pokemonEntries().any { it.third == pokemon }
}
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
val matrices = context.pose()
// Party Label
@@ -240,11 +351,14 @@ class StorageWidget(
shadow = true
)
val pokemonCount = multiSelection?.getPokemonCount() ?: 0
if (canDeleteSelected() && displayConfirmRelease) {
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = lang("ui.pc.release").bold(),
text = if (pokemonCount > 1)
{ lang("ui.pc.release_all").bold() } else { lang("ui.pc.release").bold() },
x = x + 223,
y = y + 119,
centered = true
@@ -308,6 +422,7 @@ class StorageWidget(
slot.render(context, mouseX, mouseY, delta)
val pokemon = slot.getPokemon()
if (grabbedSlot == null && slot.isHovered(mouseX, mouseY)
&& multiSelection == null
&& pokemon != null && pokemon != pcGui.previewPokemon
) {
pcGui.setPreviewPokemon(pokemon, pcGui.isPreviewInParty ?: false)
@@ -324,6 +439,7 @@ class StorageWidget(
slot.render(context, mouseX, mouseY, delta)
val pokemon = slot.getPokemon()
if (grabbedSlot == null && slot.isHovered(mouseX, mouseY)
&& multiSelection == null
&& pokemon != null && pokemon != pcGui.previewPokemon
) {
pcGui.setPreviewPokemon(pokemon, pcGui.isPreviewInParty ?: true)
@@ -335,10 +451,31 @@ class StorageWidget(
pastureWidget?.render(context, mouseX, mouseY, delta)
matrices.pushPose()
matrices.translate(0.0, 0.0, 1000.0)
grabbedSlot?.render(context, mouseX, mouseY, delta)
renderSelectedPokemon(context, mouseX, mouseY, delta)
matrices.popPose()
}
private fun renderSelectedPokemon(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
val selection = multiSelection
if (selection == null) {
grabbedSlot?.render(context, mouseX, mouseY, delta)
return
}
val slotOffset = StorageSlot.SIZE + BOX_SLOT_PADDING
selection.pokemonEntries().forEach { (column, row, pokemon) ->
val grabbed = multiGrabbedSlots.getOrPut(pokemon.uuid) {
GrabbedStorageSlot(0, 0, this, pokemon)
}
grabbed.render(
context,
mouseX + column * slotOffset,
mouseY + row * slotOffset,
delta
)
}
}
override fun mouseClicked(pMouseX: Double, pMouseY: Double, pButton: Int): Boolean {
if (displayConfirmRelease) {
if (releaseYesButton.isHovered(pMouseX, pMouseY)) releaseYesButton.mouseClicked(pMouseX, pMouseY, pButton)
@@ -371,23 +508,55 @@ class StorageWidget(
else -> return
}
val clickedPokemon = when (clickedPosition) {
is PCPosition -> pc.get(clickedPosition)
is PartyPosition -> party.get(clickedPosition)
else -> null
}
multiSelection?.let { selection ->
when (clickedPosition) {
is PCPosition -> {
if (clickedPosition.box == selection.sourceBox &&
clickedPosition.slot == selection.sourceRow * 6 + selection.sourceColumn
) {
resetSelected()
} else if (moveMultiSelectionTo(clickedPosition)) {
playSound(CobblemonSounds.PC_DROP)
resetSelected()
}
}
is PartyPosition -> {
val selectedPokemon = selection.pokemonEntries().singleOrNull() ?: return
val (column, row, pokemon) = selectedPokemon
val source = PCPosition(
selection.sourceBox,
(selection.sourceRow + row) * 6 + selection.sourceColumn + column
)
if (pc.get(source) !== pokemon) return
val packet = clickedPokemon?.let {
SwapPCPartyPokemonPacket(it.uuid, clickedPosition, pokemon.uuid, source)
} ?: MovePCPokemonToPartyPacket(pokemon.uuid, source, clickedPosition)
packet.sendToServer()
playSound(CobblemonSounds.PC_DROP)
resetSelected()
}
else -> Unit
}
return
}
// Reset release confirmation
displayConfirmRelease = false
// Clicking on itself, so unselect position
if (this.selectedPosition != null && this.selectedPosition == clickedPosition) {
if (selectedPosition == clickedPosition) {
if (grabbedSlot != null) playSound(CobblemonSounds.PC_DROP)
resetSelected()
return
}
// Check for selecting a Pokémon
val clickedPokemon = when(button) {
is BoxStorageSlot -> pc.get(clickedPosition as PCPosition)
is PartyStorageSlot -> party.get(clickedPosition as PartyPosition)
else -> null
}
val selectOverride = pcGui.configuration.selectOverride
if (selectOverride != null) {
selectOverride(pcGui, clickedPosition, clickedPokemon)
@@ -420,56 +589,44 @@ class StorageWidget(
}
}
this.selectedPosition = clickedPosition
this.pcGui.setPreviewPokemon(clickedPokemon, pcGui.isPreviewInParty ?: false)
grabbedSlot = GrabbedStorageSlot(
x = button.x,
y = button.y,
parent = this,
pokemon = clickedPokemon
)
playSound(CobblemonSounds.PC_GRAB)
if (clickedPosition is PCPosition) {
val column = clickedPosition.slot % 6
val row = clickedPosition.slot / 6
multiSelection = PCMultiSelection(1, 1, clickedPosition.box, column, row).also {
it.setPokemon(0, 0, clickedPokemon)
}
playSound(CobblemonSounds.PC_GRAB)
return
} else if (clickedPosition is PartyPosition) {
this.selectedPosition = clickedPosition
this.pcGui.setPreviewPokemon(clickedPokemon, pcGui.isPreviewInParty ?: false)
grabbedSlot = GrabbedStorageSlot(
x = button.x,
y = button.y,
parent = this,
pokemon = clickedPokemon
)
playSound(CobblemonSounds.PC_GRAB)
}
}
} else {
// Handle movement within the Box
val selectedPokemon = when(this.selectedPosition) {
is PCPosition -> pc.get(this.selectedPosition as PCPosition)
is PartyPosition -> party.get(this.selectedPosition as PartyPosition)
else -> null
} ?: return
// Box to Box
if (this.selectedPosition is PCPosition && clickedPosition is PCPosition) {
val packet = clickedPokemon?.let { SwapPCPokemonPacket(it.uuid, clickedPosition, selectedPokemon.uuid, this.selectedPosition as PCPosition) }
?: MovePCPokemonPacket(selectedPokemon.uuid, selectedPosition as PCPosition, clickedPosition)
packet.sendToServer()
playSound(CobblemonSounds.PC_DROP)
resetSelected()
}
// Box to Party
else if (this.selectedPosition is PCPosition && clickedPosition is PartyPosition) {
val packet = clickedPokemon?.let { SwapPCPartyPokemonPacket(clickedPokemon.uuid, clickedPosition, selectedPokemon.uuid, this.selectedPosition as PCPosition) }
?: MovePCPokemonToPartyPacket(selectedPokemon.uuid, this.selectedPosition as PCPosition, clickedPosition)
packet.sendToServer()
playSound(CobblemonSounds.PC_DROP)
resetSelected()
}
val sourcePosition = selectedPosition ?: return
val selectedPokemon = party.get(sourcePosition) ?: return
// Party to Box
else if (this.selectedPosition is PartyPosition && clickedPosition is PCPosition) {
if (clickedPosition is PCPosition) {
if (ServerSettings.preventCompletePartyDeposit && this.party.filterNotNull().size == 1 && clickedPokemon == null) {
return
}
val packet = clickedPokemon?.let { SwapPCPartyPokemonPacket(selectedPokemon.uuid, this.selectedPosition as PartyPosition, clickedPokemon.uuid, clickedPosition) }
?: MovePartyPokemonToPCPacket(selectedPokemon.uuid, this.selectedPosition as PartyPosition, clickedPosition)
val packet = clickedPokemon?.let { SwapPCPartyPokemonPacket(selectedPokemon.uuid, sourcePosition, clickedPokemon.uuid, clickedPosition) }
?: MovePartyPokemonToPCPacket(selectedPokemon.uuid, sourcePosition, clickedPosition)
packet.sendToServer()
playSound(CobblemonSounds.PC_DROP)
resetSelected()
}
// Party to Party
else if (this.selectedPosition is PartyPosition && clickedPosition is PartyPosition) {
val packet = clickedPokemon?.let { SwapPartyPokemonPacket(it.uuid, clickedPosition, selectedPokemon.uuid, this.selectedPosition as PartyPosition) }
?: MovePartyPokemonPacket(selectedPokemon.uuid, selectedPosition as PartyPosition, clickedPosition)
else if (clickedPosition is PartyPosition) {
val packet = clickedPokemon?.let { SwapPartyPokemonPacket(it.uuid, clickedPosition, selectedPokemon.uuid, sourcePosition) }
?: MovePartyPokemonPacket(selectedPokemon.uuid, sourcePosition, clickedPosition)
packet.sendToServer()
playSound(CobblemonSounds.PC_DROP)
resetSelected()
@@ -480,6 +637,8 @@ class StorageWidget(
fun resetSelected() {
selectedPosition = null
grabbedSlot = null
multiSelection = null
multiGrabbedSlots.clear()
pcGui.setPreviewPokemon(null)
}
}
@@ -38,7 +38,6 @@ import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.BASE_HEIG
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.BASE_WIDTH
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.HALF_OVERLAY_HEIGHT
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.HALF_OVERLAY_WIDTH
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.HALF_OVERLAY_WIDTH
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.HEADER_BAR_HEIGHT
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.ICONS_ARROW_PADDING_AROUND
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.ICONS_PADDING_AROUND
@@ -52,8 +51,7 @@ import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.TAB_MOVES
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.TAB_SIZE
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.TAB_STATS
import com.cobblemon.mod.common.client.gui.pokedex.widgets.*
import com.cobblemon.mod.common.client.gui.pokedex.widgets.evolutiontree.EvolutionTreeWidget
import com.cobblemon.mod.common.client.gui.pokedex.widgets.evolutiontree.debugEvolutionRequirementRendering
import com.cobblemon.mod.common.client.gui.pokedex.widgets.EvolutionTreeWidget
import com.cobblemon.mod.common.client.pokedex.PokedexType
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.net.messages.server.block.AdjustBlockEntityViewerCountPacket
@@ -194,6 +192,7 @@ class PokedexGUI private constructor(
public override fun init() {
super.init()
clearWidgets()
evolutionTreeExpanded = false
availableRegions = Dexes.dexEntryMap.keys.toList()
selectedRegionIndex = 0
@@ -828,10 +827,6 @@ class PokedexGUI private constructor(
}
}
if (tabInfoIndex == TAB_EVOLUTION_TREE && keyCode == InputConstants.KEY_SEMICOLON) {
debugEvolutionRequirementRendering()
}
return super.keyPressed(keyCode, scanCode, modifiers)
}
@@ -16,13 +16,17 @@ import com.cobblemon.mod.common.util.cobblemonResource
import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
import net.minecraft.network.chat.Style
import net.minecraft.resources.ResourceLocation
import java.util.Optional
private val tooltipEdge = cobblemonResource("textures/gui/pokedex/tooltip_edge.png")
private val tooltipBackground = cobblemonResource("textures/gui/pokedex/tooltip_background.png")
private const val TOOLTIP_SLICE_SIZE = 11
private const val TOOLTIP_MAX_TEXT_WIDTH = 160
fun renderTooltip(
context: GuiGraphics,
@@ -49,7 +53,12 @@ fun renderTooltip(
posY: Int,
delta: Float,
offsetY: Int = 0,
maxTextWidth: Int = TOOLTIP_MAX_TEXT_WIDTH,
) {
val text = text.flatMap { wrapTooltipLine(it, maxTextWidth) }
// Only a tooltip that fits on a single line is centered; wrapped text is left-aligned
val centered = text.size == 1
val poseStack = context.pose()
val textWidth = text.maxOf { Minecraft.getInstance().font.width(it.font(CobblemonResources.DEFAULT_LARGE)) }
val tooltipPadding = 6
@@ -78,8 +87,6 @@ fun renderTooltip(
blitSlicedVertical(poseStack, tooltipBackground, posX - (tooltipWidth / 2), tooltipTop, tooltipWidth, totalHeight)
blitSlicedVertical(poseStack, tooltipEdge, posX + (tooltipWidth / 2), tooltipTop, 1, totalHeight)
val centered = text.size == 1
text.forEachIndexed { index, it ->
drawScaledText(
context = context,
@@ -95,6 +102,20 @@ fun renderTooltip(
poseStack.popPose()
}
private fun wrapTooltipLine(line: MutableComponent, maxWidth: Int): List<MutableComponent> {
val styled = line.copy().font(CobblemonResources.DEFAULT_LARGE)
if (Minecraft.getInstance().font.width(styled) <= maxWidth) return listOf(line)
return Minecraft.getInstance().font.splitter.splitLines(styled, maxWidth, Style.EMPTY).map { wrapped ->
val component = Component.empty()
wrapped.visit({ style, string ->
component.append(Component.literal(string).withStyle(style))
Optional.empty<Unit>()
}, Style.EMPTY)
component
}
}
// Stretching the whole 11px image over an arbitrary height thickens the caps as more lines are added, so
// instead the caps are drawn 1:1 and only the flat fill row is stretched to cover the remaining height
private fun blitSlicedVertical(
@@ -6,20 +6,18 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.pokedex.widgets.evolutiontree
package com.cobblemon.mod.common.client.gui.pokedex.widgets
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.gui.*
import com.cobblemon.mod.common.api.pokedex.Dexes
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
import com.cobblemon.mod.common.api.pokedex.entry.PokedexEntry
import com.cobblemon.mod.common.api.pokedex.entry.PokedexForm
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokedex.entry.*
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.api.text.add
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.api.text.green
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUI
@@ -29,18 +27,22 @@ import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.POKEMON_D
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.SCROLL_SLOT_SIZE
import com.cobblemon.mod.common.client.gui.pokedex.ScaledButton
import com.cobblemon.mod.common.client.gui.pokedex.renderTooltip
import com.cobblemon.mod.common.client.gui.pokedex.widgets.PokemonSlotWidget
import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.render.renderScaledGuiItemIcon
import com.cobblemon.mod.common.pokemon.FormData
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.pokemon.RenderablePokemon
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.core.registries.Registries
import net.minecraft.locale.Language
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
import net.minecraft.resources.ResourceLocation
import net.minecraft.tags.TagKey
class EvolutionTreeWidget(
val parent: PokedexGUI,
@@ -92,6 +94,8 @@ class EvolutionTreeWidget(
private var hoveredTooltips: List<MutableComponent>? = null
private var viewedForm: FormData? = null
private val expandButton: ScaledButton = ScaledButton(
0f,
0f,
@@ -100,11 +104,14 @@ class EvolutionTreeWidget(
resource = expandResource,
clickAction = {
parent.evolutionTreeExpanded = !parent.evolutionTreeExpanded
expandButton.resource = if (parent.evolutionTreeExpanded) minimizeResource else expandResource
updatePositioning()
}
).apply { addWidget(this) }
init {
updatePositioning()
}
override fun renderWidget(
guiGraphics: GuiGraphics,
mouseX: Int,
@@ -146,9 +153,31 @@ class EvolutionTreeWidget(
if (pokedexEntry == null || pokedexForm == null) return
updatePositioning()
var rootSpecies = pokedexEntry
var rootForm = pokedexForm
var preEvolution = findPreEvolution(rootSpecies, rootForm)
// Stop at whatever the dex can't resolve, and at cycles (datapacks)
val visited = mutableSetOf(rootSpecies.speciesId to rootForm.displayForm)
while (preEvolution != null) {
val preEvolutionSpecies = DexEntries.entries.values.firstOrNull { it.speciesId == preEvolution.fromSpecies &&
it.forms.any {form -> form.displayForm.equals(preEvolution.fromForm, ignoreCase = true)}} ?: break
val preEvolutionForm = preEvolutionSpecies.forms.firstOrNull { it.displayForm.equals(preEvolution.fromForm, ignoreCase = true) } ?: break
if (!visited.add(preEvolutionSpecies.speciesId to preEvolutionForm.displayForm)) break
rootSpecies = preEvolutionSpecies
rootForm = preEvolutionForm
preEvolution = findPreEvolution(rootSpecies, rootForm)
}
val (rootPokedexEntry, rootPokedexForm) = Dexes.findRootDexEntryBySpeciesId(pokedexEntry.speciesId, pokedexForm.displayForm)
val rootEvolutionNode = buildEvolutionTree(rootPokedexEntry, rootPokedexForm, null)
val viewedFormEquivalents = Dexes.getFormEquivalents(pokedexForm.displayForm)
viewedForm = PokemonSpecies.getByIdentifier(pokedexEntry.speciesId)
?.forms?.firstOrNull { viewedFormEquivalents.contains(it.name.lowercase()) }
val formEquivalents = Dexes.getFormEquivalents(rootForm.displayForm)
val form =
rootSpecies.forms.firstOrNull { formEquivalents.contains(it.displayForm.lowercase()) } ?:
rootSpecies.forms.first { it.displayForm.equals("Normal", ignoreCase = true) }
val rootEvolutionNode = buildEvolutionTree(rootSpecies, form)
// val isSameEntry = previousRootPokedexEntry != null && pokedexEntry?.id == previousRootPokedexEntry?.id
// val isSameForm =
@@ -172,6 +201,13 @@ class EvolutionTreeWidget(
panningY = 0.0
}
/** The evolution leading to [form]; the standard form falls back to any form of the species (no evolution leads to regular Vivillon). */
private fun findPreEvolution(entry: PokedexEntry, form: PokedexForm): DexEvolutionDisplay? {
return DexEntries.evolutionDisplays.firstOrNull { it.toSpecies == entry.speciesId && form.displayForm.equals(it.toForm, ignoreCase = true) }
?: DexEntries.evolutionDisplays.takeIf { form.displayForm.equals("Normal", ignoreCase = true) }
?.firstOrNull { it.toSpecies == entry.speciesId }
}
fun updatePositioning() {
x = pX + X_MARGIN
y = pY + if (parent.evolutionTreeExpanded) Y_MARGIN_EXPANDED else Y_MARGIN_MINIMIZED
@@ -180,6 +216,7 @@ class EvolutionTreeWidget(
panningX = 0.0
panningY = 0.0
expandButton.resource = if (parent.evolutionTreeExpanded) minimizeResource else expandResource
expandButton.buttonX = x + EXPAND_BUTTON_X_MARGIN
expandButton.buttonY = y + EXPAND_BUTTON_Y_MARGIN
}
@@ -187,61 +224,62 @@ class EvolutionTreeWidget(
private fun buildEvolutionTree(
pokedexEntry: PokedexEntry,
pokedexForm: PokedexForm,
evolution: Evolution? = null,
resultProperties: PokemonProperties? = evolution?.result,
renderData: RenderEvolutionData? = evolution?.let { renderEvolutionData(it) }
aspects: Set<String> = emptySet(),
ancestors: Set<Pair<ResourceLocation, String>> = emptySet(),
): EvolutionNode {
val species = PokemonSpecies.getByIdentifier(pokedexEntry.speciesId)!!
// Datapack evolutions can loop back (A -> B -> A), so stop at anything already on this branch
val path = ancestors + (pokedexEntry.speciesId to pokedexForm.displayForm.lowercase())
val discoveryLevel = CobblemonClient.clientPokedexData.getHighestKnowledgeFor(pokedexEntry, pokedexForm, aspects.toMutableSet())
val formEquivalents = Dexes.getFormEquivalents(pokedexForm.displayForm)
val form = species.forms.firstOrNull { formEquivalents.contains(it.name.lowercase()) }
val parentSpecies = Dexes.findDexEntryBySpeciesId(species.resourceIdentifier)!!
val parentPokedexForm = parentSpecies.forms.first { formEquivalents.contains(it.displayForm.lowercase()) }
val evolutions = DexEntries.evolutionDisplays.filter { it.fromSpecies == pokedexEntry.speciesId && formEquivalents.contains(it.fromForm.lowercase()) }
val aspects = resultProperties?.aspects?.toMutableSet() ?: mutableSetOf()
form?.aspects?.let { aspects.addAll(it) }
val discoveryLevel = CobblemonClient.clientPokedexData.getHighestKnowledgeFor(pokedexEntry, pokedexForm, aspects)
val evolutions = form?.evolutions ?: species.evolutions
// Avoid evolution.result.create() here: it builds a full battle-ready Pokemon (initializeMovesetFromDefault
// etc.) which relies on server-only data like CobblemonMovesetBuilders and crashes on the client
val children = evolutions.flatMap { evolution ->
val baseRenderData = renderEvolutionData(evolution)
listOfNotNull(evolution.result, evolution.shedder).mapNotNull { resultProperties ->
val resultSpecies = resultProperties.species
?.let { PokemonSpecies.getByIdentifier(it.asIdentifierDefaultingNamespace()) }
?: return@mapNotNull null
val resultForm = resultSpecies.getForm(aspects + resultProperties.aspects)
val speciesId = resultSpecies.resourceIdentifier
val childPokedexEntry = Dexes.findDexEntryBySpeciesId(speciesId)
if (childPokedexEntry == null) {
Cobblemon.LOGGER.warn("Dex entry not found for species: $speciesId")
return@mapNotNull null
}
val formEquivalents = Dexes.getFormEquivalents(resultForm.name.lowercase())
val childPokedexForm = childPokedexEntry.forms.first { formEquivalents.contains(it.displayForm.lowercase()) }
val childRenderData = if (resultProperties === evolution.shedder) {
RenderEvolutionData(
icons = (baseRenderData?.icons ?: emptyList()) + CobblemonItems.POKE_BALL,
tooltips = (baseRenderData?.tooltips ?: emptyList()) + lang("ui.pokedex.evolution.shedder.tooltip"),
)
} else {
baseRenderData
}
return@mapNotNull buildEvolutionTree(childPokedexEntry, childPokedexForm, evolution, resultProperties, childRenderData)
val children = evolutions.mapNotNull {
val childPokedexEntry = DexEntries.entries.values.firstOrNull { entry -> entry.speciesId == it.toSpecies &&
entry.forms.any {form -> form.displayForm.equals(it.toForm, ignoreCase = true)}}
if (childPokedexEntry == null) {
Cobblemon.LOGGER.warn("Dex entry not found for species: ${it.toSpecies}")
return@mapNotNull null
}
val formEquivalents = Dexes.getFormEquivalents(it.toForm.lowercase())
val childPokedexForm = childPokedexEntry.forms.firstOrNull { formEquivalents.contains(it.displayForm.lowercase()) }
if (childPokedexForm == null) {
Cobblemon.LOGGER.warn("Dex form ${it.toForm} not found for species: ${it.toSpecies}")
return@mapNotNull null
}
if ((childPokedexEntry.speciesId to childPokedexForm.displayForm.lowercase()) in path) {
Cobblemon.LOGGER.warn("Evolution loop found at species: ${it.toSpecies} form: ${it.toForm}")
return@mapNotNull null
}
buildEvolutionTree(childPokedexEntry, childPokedexForm, it.toAspects, path)
}
return EvolutionNode(pokedexEntry, parentPokedexForm, aspects, discoveryLevel, children, renderData)
return EvolutionNode(
pokedexEntry = pokedexEntry,
pokedexForm = pokedexForm,
aspects = aspects,
pokedexEntryProgress = discoveryLevel,
children = mergeEquivalentChildren(children),
renderDataAlternatives = evolutions,
)
}
private fun mergeEquivalentChildren(children: List<EvolutionNode>): List<EvolutionNode> {
return children
.groupBy { Triple(it.pokedexEntry.speciesId, it.pokedexForm.displayForm.lowercase(), it.aspects) }
.values
.map { group ->
if (group.size == 1) return@map group.first()
val first = group.first()
EvolutionNode(
first.pokedexEntry,
first.pokedexForm,
first.aspects,
first.pokedexEntryProgress,
first.children,
group.flatMap { it.renderDataAlternatives }
)
}
}
private fun renderTreeDrawingNode(
@@ -258,15 +296,19 @@ class EvolutionTreeWidget(
val (childX, _) = getDrawingCoordinates(child)
val lineStartX = treeX + SCROLL_SLOT_SIZE
val lineEndX = childX
val lineY = treeY + SCROLL_SLOT_SIZE / 2
child.value.renderData?.let { renderData ->
val formEquivalents = Dexes.getFormEquivalents(child.value.pokedexForm.displayForm)
val alternatives = treeDrawingNode.value.renderDataAlternatives.filter {
it.toSpecies == child.value.pokedexEntry.speciesId && formEquivalents.contains(it.toForm.lowercase()) && it.toAspects == child.value.aspects
}
alternatives.takeIf { it.isNotEmpty() }?.let {
drawEvolutionInfo(
guiGraphics,
renderData,
it,
lineStartX,
lineEndX,
childX,
lineY,
mouseX,
mouseY,
@@ -298,16 +340,19 @@ class EvolutionTreeWidget(
treeDrawingNode.children.forEach { child ->
val (childX, childY) = getDrawingCoordinates(child)
val lineStartX = treeForkEnd
val lineEndX = childX
val lineY = childY + SCROLL_SLOT_SIZE / 2
child.value.renderData?.let { renderData ->
val formEquivalents = Dexes.getFormEquivalents(child.value.pokedexForm.displayForm)
val alternatives = treeDrawingNode.value.renderDataAlternatives.filter {
it.toSpecies == child.value.pokedexEntry.speciesId && formEquivalents.contains(it.toForm.lowercase()) && it.toAspects == child.value.aspects
}
alternatives.takeIf { it.isNotEmpty() }?.let {
drawEvolutionInfo(
guiGraphics,
renderData,
lineStartX,
lineEndX,
it,
treeForkEnd,
childX,
lineY,
mouseX,
mouseY,
@@ -346,7 +391,7 @@ class EvolutionTreeWidget(
if (formName == "normal") null
else lang("ui.pokedex.info.form.${species}-${formName.replace("-", "")}")
formComponent?.add(" ")?.add(speciesComponent) ?: speciesComponent
formComponent?.let { speciesComponent.copy().add(" (").add(it).add(")") } ?: speciesComponent
}
guiGraphics.disableScissor()
@@ -408,7 +453,7 @@ class EvolutionTreeWidget(
private fun drawEvolutionInfo(
guiGraphics: GuiGraphics,
renderData: RenderEvolutionData,
renderDataAlternatives: List<DexEvolutionDisplay>,
lineStartX: Int,
lineEndX: Int,
lineY: Int,
@@ -416,6 +461,26 @@ class EvolutionTreeWidget(
mouseY: Int,
delta: Float,
) {
val icons = renderDataAlternatives.flatMap { it.requirements.filter(::isDisplayable).map { req -> req.icon } }.distinct()
val playerBiome = Minecraft.getInstance().player?.let { it.level().getBiome(it.blockPosition()) }
val alternativeTooltips = renderDataAlternatives.map { alternative ->
alternative.requirements.filter(::isDisplayable).map { req ->
if (req is CobblemonBiomeDexEvolutionRequirementDisplay) {
val tooltip = req.tooltip.copy()
if (playerBiome != null) {
val conditionFits = req.condition?.let { if (it.tag) playerBiome.`is`(TagKey.create(Registries.BIOME, it.id)) else playerBiome.`is`(it.id) } ?: true
val antiConditionFits = req.antiCondition?.let { if (it.tag) playerBiome.`is`(TagKey.create(Registries.BIOME, it.id)) else playerBiome.`is`(it.id) } ?: false
val isMet = conditionFits && !antiConditionFits
tooltip.add(if (isMet) Component.literal(" ✔").green() else Component.literal(" ✘").red())
}
tooltip
} else {
req.tooltip
}
}
}
val tooltips = combineAlternativeTooltips(alternativeTooltips)
val lineXMiddle = (lineStartX + lineEndX) / 2
val iconX = (lineXMiddle + ICON_X_PADDING - ICON_SLOT_HALF_PADDING).toInt()
val iconY = (lineY + ICON_Y_PADDING - ICON_SLOT_HALF_PADDING).toInt()
@@ -431,7 +496,7 @@ class EvolutionTreeWidget(
return
}
if (renderData.icons.isEmpty()) {
if (icons.isEmpty()) {
guiGraphics.hLine(
lineStartX,
lineEndX,
@@ -462,8 +527,8 @@ class EvolutionTreeWidget(
height = iconSize,
)
val index = (System.currentTimeMillis() / MILLIS_BETWEEN_ICONS_CYCLE) % renderData.icons.size
val item = renderData.icons[index.toInt()]
val index = (System.currentTimeMillis() / MILLIS_BETWEEN_ICONS_CYCLE) % icons.size
val item = icons[index.toInt()]
renderScaledGuiItemIcon(
itemStack = item.defaultInstance,
@@ -478,17 +543,49 @@ class EvolutionTreeWidget(
val isIconHovered = isElementHovered(
mouseX,
mouseY,
iconX.toInt(),
iconX.toInt() + iconSize,
iconY.toInt(),
iconY.toInt() + iconSize,
iconX,
iconX + iconSize,
iconY,
iconY + iconSize,
)
if (renderData.tooltips.isNotEmpty() &&
if (tooltips.isNotEmpty() &&
(isLineHovered || isIconHovered) &&
this.isHovered(mouseX.toDouble(), mouseY.toDouble())
) {
hoveredTooltips = renderData.tooltips
hoveredTooltips = tooltips.map { it.copy() }
}
}
/** Hides biome requirements naming untranslated biomes, e.g. from mods that aren't installed (aether:skyroot_forest) */
private fun isDisplayable(requirement: DexEvolutionRequirementDisplay): Boolean {
if (requirement !is CobblemonBiomeDexEvolutionRequirementDisplay) return true
return listOfNotNull(requirement.condition, requirement.antiCondition).all {
it.tag || Language.getInstance().has("biome.${it.id.namespace}.${it.id.path}")
}
}
private fun combineAlternativeTooltips(alternatives: List<List<Component>>): List<Component> {
val groups = alternatives.filter { it.isNotEmpty() }
if (groups.size <= 1) return groups.firstOrNull() ?: emptyList()
val sharedStrings = groups.map { group -> group.map { it.string }.toSet() }.reduce { acc, strings -> acc intersect strings }
val shared = groups.first().filter { it.string in sharedStrings }.distinctBy { it.string }
val remainders = groups
.map { group -> group.filter { it.string !in sharedStrings } }
.distinctBy { group -> group.map { it.string } }
// An alternative needing nothing beyond the shared lines makes the others irrelevant
if (remainders.any { it.isEmpty() }) return shared
if (remainders.size == 1) return shared + remainders.first()
return if (remainders.size >= 3 && remainders.all { it.size == 1 }) {
shared + lang("ui.pokedex.evolution.one_of").bold() + remainders.map { it.first() }
} else {
// Show each alternative in full, otherwise it's unclear where the first one starts
groups.distinctBy { group -> group.map { it.string } }.flatMapIndexed { index, group ->
if (index == 0) group else listOf(lang("ui.pokedex.evolution.or").bold()) + group
}
}
}
@@ -566,6 +663,6 @@ class EvolutionTreeWidget(
val aspects: Set<String>,
val pokedexEntryProgress: PokedexEntryProgress,
override val children: List<EvolutionNode>,
val renderData: RenderEvolutionData?,
val renderDataAlternatives: List<DexEvolutionDisplay>,
) : TreeNode<EvolutionNode>
}
@@ -1,48 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.pokedex.widgets.evolutiontree
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import net.minecraft.client.Minecraft
import net.minecraft.network.chat.Component
fun debugEvolutionRequirementRendering() {
if (!Cobblemon.config.enableDebugKeys) {
return
}
val gaps = mutableListOf<Pair<String, String>>()
for (species in PokemonSpecies.implemented) {
val evolutions = species.evolutions + species.forms.flatMap { it.evolutions }
for (evolution in evolutions) {
if (renderEvolutionData(evolution) == null) {
gaps.add("Evolution: ${evolution::class.simpleName}" to species.showdownId())
}
for (requirement in evolution.requirements) {
if (resolveRequirementData(requirement) == null) {
gaps.add("Requirement: ${requirement::class.simpleName}" to species.showdownId())
}
}
}
}
val player = Minecraft.getInstance().player
if (gaps.isEmpty()) {
player?.sendSystemMessage(Component.literal("Evolution render debug: no gaps found."))
return
}
val byType = gaps.groupBy({ it.first }, { it.second })
println("Evolution render debug found ${gaps.size} gap(s) across ${byType.size} type(s):")
byType.forEach { (type, species) -> println(" $type: ${species.distinct().joinToString(", ")}") }
player?.sendSystemMessage(Component.literal("Evolution render debug: ${gaps.size} gap(s) across ${byType.size} type(s), see log for details."))
}
@@ -1,123 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.pokedex.widgets.evolutiontree
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.api.pokemon.requirement.Requirement
import com.cobblemon.mod.common.api.text.green
import com.cobblemon.mod.common.pokemon.evolution.variants.BlockClickEvolution
import com.cobblemon.mod.common.pokemon.evolution.variants.DummyEvolution
import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolution
import com.cobblemon.mod.common.pokemon.evolution.variants.LevelUpEvolution
import com.cobblemon.mod.common.pokemon.evolution.variants.TradeEvolution
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.itemRegistry
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.Minecraft
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
import net.minecraft.world.item.Item
data class RenderEvolutionData(
val icons: List<Item>,
val tooltips: List<MutableComponent>,
)
internal fun resolveRequirementData(requirement: Requirement): RenderRequirementData? {
val icon = requirement.pokedexIcon?.let {
Minecraft.getInstance()?.player?.level()?.itemRegistry?.get(it)
}
val tooltip = requirement.pokedexTooltip?.let {
Component.translatable(it)
}
if (icon != null && tooltip != null) {
return RenderRequirementData(icon, tooltip)
}
return renderRequirementData(requirement)
}
fun renderEvolutionData(evolution: Evolution): RenderEvolutionData? {
when (evolution) {
is BlockClickEvolution -> return renderBlockClickEvolution(evolution)
is DummyEvolution -> return renderDummyEvolution(evolution)
is ItemInteractionEvolution -> return renderItemInteractionEvolution(evolution)
is LevelUpEvolution -> return renderLevelUpEvolution(evolution)
is TradeEvolution -> return renderTradeEvolution(evolution)
}
return null
}
fun renderBlockClickEvolution(blockClickEvolution: BlockClickEvolution): RenderEvolutionData? {
return null
}
// TODO: Use dummyEvolution?
fun renderDummyEvolution(evolution: Evolution): RenderEvolutionData? {
return null
}
fun renderItemInteractionEvolution(itemInteractionEvolution: ItemInteractionEvolution): RenderEvolutionData? {
val item = itemInteractionEvolution.requiredContext.items.get().first().value()
val icons = mutableListOf(item)
val tooltips =
mutableListOf(lang("ui.pokedex.evolution.item_interact.tooltip", item.description.plainCopy().green()))
val requirementsDataList = itemInteractionEvolution.requirements.mapNotNull { requirement ->
resolveRequirementData(requirement)
}
requirementsDataList.forEach { requirement ->
icons.add(requirement.icon)
tooltips.add(requirement.tooltip)
}
return RenderEvolutionData(icons, tooltips)
}
fun renderLevelUpEvolution(levelUpEvolution: LevelUpEvolution): RenderEvolutionData? {
val requirementsDataList = levelUpEvolution.requirements.map { requirement ->
resolveRequirementData(requirement)
}
val icons = requirementsDataList.mapNotNull { requirementData -> requirementData?.icon }
val tooltips = requirementsDataList.mapNotNull { requirementData -> requirementData?.tooltip }
return RenderEvolutionData(icons, tooltips)
}
fun renderTradeEvolution(tradeEvolution: TradeEvolution): RenderEvolutionData? {
val icons = mutableListOf<Item>(CobblemonItems.LINK_CABLE)
val tooltips = mutableListOf<MutableComponent>()
val species = tradeEvolution.requiredContext.species
if (species == null) {
tooltips.add(lang("ui.pokedex.evolution.trade.tooltip.with_any_species"))
} else {
PokemonSpecies.getByIdentifier(species.asIdentifierDefaultingNamespace())?.let {
tooltips.add(lang("ui.pokedex.evolution.trade.tooltip.with_species", it.translatedName.green()))
}
}
val requirementsDataList = tradeEvolution.requirements.mapNotNull { requirement ->
resolveRequirementData(requirement)
}
requirementsDataList.forEach { requirement ->
icons.add(requirement.icon)
tooltips.add(requirement.tooltip)
}
return RenderEvolutionData(icons, tooltips)
}
@@ -1,379 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.pokedex.widgets.evolutiontree
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.conditional.RegistryLikeIdentifierCondition
import com.cobblemon.mod.common.api.conditional.RegistryLikeTagCondition
import com.cobblemon.mod.common.api.pokemon.feature.IntSpeciesFeatureProvider
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
import com.cobblemon.mod.common.api.pokemon.requirement.Requirement
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.text.add
import com.cobblemon.mod.common.api.text.green
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.pokemon.requirements.*
import com.cobblemon.mod.common.util.asTranslated
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.Minecraft
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
import net.minecraft.world.item.Item
import net.minecraft.world.item.Items
import net.minecraft.world.level.biome.Biome
import net.minecraft.world.level.levelgen.structure.Structure
data class RenderRequirementData(
val icon: Item,
val tooltip: MutableComponent,
)
fun renderRequirementData(requirement: Requirement): RenderRequirementData? {
when (requirement) {
is AdvancementRequirement -> return renderAdvancementRequirement(requirement)
is AnyRequirement -> return renderAnyRequirement(requirement)
is AreaRequirement -> return renderAreaRequirement(requirement)
is AttackDefenceRatioRequirement -> return renderAttackDefenceRatioRequirement(requirement)
is BattleCriticalHitsRequirement -> return renderBattleCriticalHitsRequirement(requirement)
is BiomeRequirement -> return renderBiomeRequirement(requirement)
is BlocksTraveledRequirement -> return renderBlocksTraveledRequirement(requirement)
is DamageTakenRequirement -> return renderDamageTakenRequirement(requirement)
is DefeatRequirement -> return renderDefeatRequirement(requirement)
is FriendshipRequirement -> return renderFriendshipRequirement(requirement)
is HeldItemRequirement -> return renderHeldItemRequirement(requirement)
is LevelRequirement -> return renderLevelRequirement(requirement)
is MoonPhaseRequirement -> return renderMoonPhaseRequirement(requirement)
is MoveSetRequirement -> return renderMoveSetRequirement(requirement)
is MoveTypeRequirement -> return renderMoveTypeRequirement(requirement)
is OwnerHoldsItemRequirement -> return renderOwnerHoldsItemRequirement(requirement)
is PartyMemberRequirement -> return renderPartyMemberRequirement(requirement)
is PokemonPropertiesRequirement -> return renderPokemonPropertiesRequirement(requirement)
is PropertyRangeRequirement -> return renderPropertyRangeRequirement(requirement)
is RecoilRequirement -> return renderRecoilRequirement(requirement)
is StatCompareRequirement -> return renderStatCompareRequirement(requirement)
is StatEqualRequirement -> return renderStatEqualRequirement(requirement)
is StructureRequirement -> return renderStructureRequirement(requirement)
is TimeRangeRequirement -> return renderTimeRangeRequirement(requirement)
is UseMoveRequirement -> return renderUseMoveRequirement(requirement)
is WeatherRequirement -> return renderWeatherRequirement(requirement)
is WorldRequirement -> return renderWorldRequirement(requirement)
}
return null
}
fun renderAdvancementRequirement(advancementRequirement: AdvancementRequirement): RenderRequirementData? {
val icon = Items.BOOK
val identifier = advancementRequirement.requiredAdvancement
val advancementLanguageKey = "${identifier.namespace}.${identifier.path.substringAfterLast('/')}"
val advancementComponent = Component.translatable("advancements.$advancementLanguageKey").green()
val tooltip = lang("ui.pokedex.requirement.advancement.tooltip", advancementComponent)
return RenderRequirementData(icon, tooltip)
}
fun renderAnyRequirement(anyRequirement: AnyRequirement): RenderRequirementData? {
return null
}
fun renderAreaRequirement(areaRequirement: AreaRequirement): RenderRequirementData? {
return null
}
fun renderAttackDefenceRatioRequirement(attackDefenceRatioRequirement: AttackDefenceRatioRequirement): RenderRequirementData? {
return null
}
fun renderBattleCriticalHitsRequirement(battleCriticalHitsRequirement: BattleCriticalHitsRequirement): RenderRequirementData? {
val icon = CobblemonItems.MEDICINAL_LEEK
val criticalsComponent = Component.literal(battleCriticalHitsRequirement.amount.toString()).green()
val tooltip = lang("ui.pokedex.requirement.battle_critical_hits.tooltip", criticalsComponent)
return RenderRequirementData(icon, tooltip)
}
fun renderBiomeRequirement(biomeRequirement: BiomeRequirement): RenderRequirementData? {
val condition = biomeRequirement.biomeCondition
val anticondition = biomeRequirement.biomeAnticondition
if (condition == null && anticondition == null) {
return null
}
val icon = Items.COMPASS
val displayName: (RegistryLikeCondition<Biome>) -> MutableComponent = {
when (it) {
is RegistryLikeTagCondition -> Component.literal(it.tag.location().path.substringAfterLast('/').split('_').joinToString(" ") { part -> part.replaceFirstChar(Char::titlecase) })
is RegistryLikeIdentifierCondition -> Component.translatable("biome.${it.identifier.namespace}.${it.identifier.path}")
else -> Component.literal(it.toString())
}
}
val tooltip = if (condition != null && anticondition != null) {
lang("ui.pokedex.requirement.biome.both.tooltip", displayName(condition).green(), displayName(anticondition).green())
} else if (condition != null) {
lang("ui.pokedex.requirement.biome.tooltip", displayName(condition).green())
} else {
lang("ui.pokedex.requirement.biome.anticondition.tooltip", displayName(anticondition!!).green())
}
val playerBiome = Minecraft.getInstance()?.player?.let { it.level().getBiome(it.blockPosition()) }
if (playerBiome != null) {
val isMet = (condition == null || condition.fits(playerBiome)) && (anticondition == null || !anticondition.fits(playerBiome))
tooltip.add(if (isMet) Component.literal(" ✔").green() else Component.literal(" ✘").red())
}
return RenderRequirementData(icon, tooltip)
}
fun renderBlocksTraveledRequirement(blocksTraveledRequirement: BlocksTraveledRequirement): RenderRequirementData? {
val icon = Items.LEATHER_BOOTS
val blocksComponent = Component.literal(blocksTraveledRequirement.amount.toString()).green()
val tooltip = lang("ui.pokedex.requirement.blocks_traveled.tooltip", blocksComponent)
return RenderRequirementData(icon, tooltip)
}
fun renderDamageTakenRequirement(damageTakenRequirement: DamageTakenRequirement): RenderRequirementData? {
val icon = CobblemonItems.PUNCHING_GLOVE
val blocksComponent = Component.literal(damageTakenRequirement.amount.toString()).green()
val tooltip = lang("ui.pokedex.requirement.damage_taken.tooltip", blocksComponent)
return RenderRequirementData(icon, tooltip)
}
fun renderDefeatRequirement(defeatRequirement: DefeatRequirement): RenderRequirementData? {
val description = describePokemonProperties(defeatRequirement.target) ?: return null
val icon = CobblemonItems.PUNCHING_GLOVE
val defeatedAmount = defeatRequirement.amount
val descriptionComponent = Component.literal(defeatedAmount.toString()).green().add(" ").add(description)
val tooltip = lang("ui.pokedex.requirement.defeat.tooltip", descriptionComponent)
return RenderRequirementData(icon, tooltip)
}
fun renderFriendshipRequirement(friendshipRequirement: FriendshipRequirement): RenderRequirementData? {
val icon = CobblemonItems.SOOTHE_BELL
val friendshipAmountComponent = Component.literal(friendshipRequirement.amount.toString()).green()
val tooltip = lang("ui.pokedex.requirement.friendship.tooltip", friendshipAmountComponent)
return RenderRequirementData(icon, tooltip)
}
fun renderHeldItemRequirement(heldItemRequirement: HeldItemRequirement): RenderRequirementData? {
val item = heldItemRequirement.itemCondition.items.get().first().value()
val tooltip = lang("ui.pokedex.requirement.held_item.tooltip", item.description.plainCopy().green())
return RenderRequirementData(item, tooltip)
}
fun renderLevelRequirement(levelRequirement: LevelRequirement): RenderRequirementData? {
val minLevel = levelRequirement.minLevel
val maxLevel = levelRequirement.maxLevel
val icon = CobblemonItems.RARE_CANDY
val tooltip =
if (minLevel == maxLevel || maxLevel == Int.MAX_VALUE)
lang("ui.pokedex.requirement.level.tooltip.min", Component.literal(minLevel.toString()).green())
else
lang("ui.pokedex.requirement.level.tooltip.between", Component.literal(minLevel.toString()).green(), Component.literal(maxLevel.toString()).green())
return RenderRequirementData(icon, tooltip)
}
fun renderMoonPhaseRequirement(moonPhaseRequirement: MoonPhaseRequirement): RenderRequirementData? {
val icon = Items.CLOCK
val moonPhaseComponent = moonPhaseRequirement.moonPhase.displayName().green()
val tooltip = lang("ui.pokedex.requirement.moon_phase.tooltip", moonPhaseComponent)
return RenderRequirementData(icon, tooltip)
}
fun renderMoveSetRequirement(moveSetRequirement: MoveSetRequirement): RenderRequirementData? {
val icon = CobblemonItems.TECHNICAL_MACHINE
val tooltip = lang("ui.pokedex.requirement.has_move.tooltip", moveSetRequirement.move.displayName.green())
return RenderRequirementData(icon, tooltip)
}
fun renderMoveTypeRequirement(moveTypeRequirement: MoveTypeRequirement): RenderRequirementData? {
val type = ElementalTypes.get(moveTypeRequirement.type.name)
val icon = when (type) {
ElementalTypes.NORMAL -> CobblemonItems.NORMAL_GEM
ElementalTypes.FIRE -> CobblemonItems.FIRE_GEM
ElementalTypes.WATER -> CobblemonItems.WATER_GEM
ElementalTypes.GRASS -> CobblemonItems.GRASS_GEM
ElementalTypes.ELECTRIC -> CobblemonItems.ELECTRIC_GEM
ElementalTypes.ICE -> CobblemonItems.ICE_GEM
ElementalTypes.FIGHTING -> CobblemonItems.FIGHTING_GEM
ElementalTypes.POISON -> CobblemonItems.POISON_GEM
ElementalTypes.GROUND -> CobblemonItems.GROUND_GEM
ElementalTypes.FLYING -> CobblemonItems.FLYING_GEM
ElementalTypes.PSYCHIC -> CobblemonItems.PSYCHIC_GEM
ElementalTypes.BUG -> CobblemonItems.BUG_GEM
ElementalTypes.ROCK -> CobblemonItems.ROCK_GEM
ElementalTypes.GHOST -> CobblemonItems.GHOST_GEM
ElementalTypes.DRAGON -> CobblemonItems.DRAGON_GEM
ElementalTypes.DARK -> CobblemonItems.DARK_GEM
ElementalTypes.STEEL -> CobblemonItems.STEEL_GEM
ElementalTypes.FAIRY -> CobblemonItems.FAIRY_GEM
else -> CobblemonItems.NORMAL_GEM
}
val tooltip = lang("ui.pokedex.requirement.has_move_type.tooltip", moveTypeRequirement.type.displayName.green())
return RenderRequirementData(icon, tooltip)
}
fun renderOwnerHoldsItemRequirement(ownerHoldsItemRequirement: OwnerHoldsItemRequirement): RenderRequirementData? {
val item = ownerHoldsItemRequirement.itemCondition.items.get().first().value()
val tooltip = lang("ui.pokedex.requirement.owner_held_item.tooltip", item.description.plainCopy().green())
return RenderRequirementData(item, tooltip)
}
fun renderPartyMemberRequirement(partyMemberRequirement: PartyMemberRequirement): RenderRequirementData? {
val description = describePokemonProperties(partyMemberRequirement.target) ?: return null
val icon = CobblemonItems.POKE_BALL
val tooltip = if (partyMemberRequirement.contains) {
lang("ui.pokedex.requirement.party_member.tooltip", description)
} else {
lang("ui.pokedex.requirement.party_member.anticondition.tooltip", description)
}
return RenderRequirementData(icon, tooltip)
}
fun renderPokemonPropertiesRequirement(pokemonPropertiesRequirement: PokemonPropertiesRequirement): RenderRequirementData? {
val description = describePokemonProperties(pokemonPropertiesRequirement.target) ?: return null
val icon = Items.NAME_TAG
val tooltip = lang("ui.pokedex.requirement.pokemon_properties.tooltip", description)
return RenderRequirementData(icon, tooltip)
}
fun renderPropertyRangeRequirement(propertyRangeRequirement: PropertyRangeRequirement): RenderRequirementData? {
val provider = SpeciesFeatures.getFeature(propertyRangeRequirement.feature) as? IntSpeciesFeatureProvider ?: return null
val displayName = provider.display?.name?.takeIf { it.isNotBlank() }?.asTranslated()?.green() ?: return null
val icon = provider.itemPoints.keys.firstOrNull()?.let { BuiltInRegistries.ITEM.get(it) } ?: Items.NAME_TAG
val range = propertyRangeRequirement.range
val tooltip = lang(
"ui.pokedex.requirement.property_range.tooltip",
displayName,
Component.literal(range.first.toString()).green(),
Component.literal(range.last.toString()).green()
)
return RenderRequirementData(icon, tooltip)
}
fun renderRecoilRequirement(recoilRequirement: RecoilRequirement): RenderRequirementData? {
val icon = CobblemonItems.LIFE_ORB
val recoilComponent = Component.literal(recoilRequirement.amount.toString()).green()
val tooltip = lang("ui.pokedex.requirement.recoil.tooltip", recoilComponent)
return RenderRequirementData(icon, tooltip)
}
fun renderStatCompareRequirement(statCompareRequirement: StatCompareRequirement): RenderRequirementData? {
val highStat = Stats.getStat(statCompareRequirement.highStat)
val lowStat = Stats.getStat(statCompareRequirement.lowStat)
val icon = when (highStat) {
Stats.HP, Stats.EVASION, Stats.ACCURACY -> CobblemonItems.HEALTH_MOCHI
Stats.ATTACK -> CobblemonItems.MUSCLE_MOCHI
Stats.DEFENCE -> CobblemonItems.RESIST_MOCHI
Stats.SPECIAL_ATTACK -> CobblemonItems.GENIUS_MOCHI
Stats.SPECIAL_DEFENCE -> CobblemonItems.CLEVER_MOCHI
Stats.SPEED -> CobblemonItems.SWIFT_MOCHI
}
val highStatComponent = highStat.displayName.plainCopy().green()
val lowStatComponent = lowStat.displayName.plainCopy().green()
val tooltip = lang("ui.pokedex.requirement.stat_compare.tooltip", highStatComponent, lowStatComponent)
return RenderRequirementData(icon, tooltip)
}
fun renderStatEqualRequirement(statEqualRequirement: StatEqualRequirement): RenderRequirementData? {
val statOne = Stats.getStat(statEqualRequirement.statOne)
val statTwo = Stats.getStat(statEqualRequirement.statTwo)
val statOneComponent = statOne.displayName.plainCopy().green()
val statTwoComponent = statTwo.displayName.plainCopy().green()
val icon = CobblemonItems.FRESH_START_MOCHI
val tooltip = lang("ui.pokedex.requirement.stat_equal.tooltip", statOneComponent, statTwoComponent)
return RenderRequirementData(icon, tooltip)
}
fun renderStructureRequirement(structureRequirement: StructureRequirement): RenderRequirementData? {
val condition = structureRequirement.structureCondition
val anticondition = structureRequirement.structureAnticondition
if (condition == null && anticondition == null) {
return null
}
val icon = Items.STRUCTURE_BLOCK
val displayName: (RegistryLikeCondition<Structure>) -> MutableComponent = {
val path = when (it) {
is RegistryLikeTagCondition -> it.tag.location().path
is RegistryLikeIdentifierCondition -> it.identifier.path
else -> it.toString()
}
Component.literal(path.substringAfterLast('/').split('_').joinToString(" ") { part -> part.replaceFirstChar(Char::titlecase) })
}
val tooltip = if (condition != null && anticondition != null) {
lang("ui.pokedex.requirement.structure.both.tooltip", displayName(condition).green(), displayName(anticondition).green())
} else if (condition != null) {
lang("ui.pokedex.requirement.structure.tooltip", displayName(condition).green())
} else {
lang("ui.pokedex.requirement.structure.anticondition.tooltip", displayName(anticondition!!).green())
}
return RenderRequirementData(icon, tooltip)
}
fun renderTimeRangeRequirement(timeRangeRequirement: TimeRangeRequirement): RenderRequirementData? {
val icon = Items.CLOCK
val timeRangeComponent = timeRangeRequirement.range.displayName() ?: return null
val tooltip = lang("ui.pokedex.requirement.time_range.tooltip", timeRangeComponent.green())
return RenderRequirementData(icon, tooltip)
}
fun renderUseMoveRequirement(useMoveRequirement: UseMoveRequirement): RenderRequirementData? {
val icon = CobblemonItems.CHOICE_BAND
val tooltip = lang("ui.pokedex.requirement.use_move.tooltip", useMoveRequirement.move.displayName.green())
return RenderRequirementData(icon, tooltip)
}
fun renderWeatherRequirement(weatherRequirement: WeatherRequirement): RenderRequirementData? {
val icon: Item
val tooltip: MutableComponent
if (weatherRequirement.isThundering == true) {
icon = Items.LIGHTNING_ROD
tooltip = lang("ui.pokedex.requirement.weather.tooltip.thundering")
} else {
icon = Items.WATER_BUCKET
tooltip = lang("ui.pokedex.requirement.weather.tooltip.raining")
}
return RenderRequirementData(icon, tooltip)
}
fun renderWorldRequirement(worldRequirement: WorldRequirement): RenderRequirementData? {
return null
}
@@ -24,8 +24,9 @@ object OpenPCHandler : ClientNetworkPacketHandler<OpenPCPacket> {
party = CobblemonClient.storage.party,
configuration = PCGUIConfiguration(),
openOnBox = packet.box ?: CobblemonClient.lastPcBoxViewed,
unseenWallpapers = packet.unseenWallpapers.toMutableSet()
unseenWallpapers = packet.unseenWallpapers.toMutableSet(),
silent = packet.silent
)
)
}
}
}
@@ -1,98 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen1
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.createTransformation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.CobblemonPose
import com.cobblemon.mod.common.client.render.models.blockbench.pose.ModelPartTransformation
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.PoseType.Companion.MOVING_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.STATIONARY_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class CaterpieModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
override val rootPart = root.registerChildWithAllChildren("caterpie")
override val head = getPart("head")
override var portraitScale = 2.1F
override var portraitTranslation = Vec3(-0.55, -1.55, 0.0)
override var profileScale = 0.8F
override var profileTranslation = Vec3(0.0, 0.55, 0.0)
lateinit var sleep: Pose
lateinit var standing: Pose
lateinit var walk: Pose
lateinit var shoulderLeft: CobblemonPose
lateinit var shoulderRight: CobblemonPose
val shoulderOffset = 3
override val cryAnimation = CryProvider { bedrockStateful("caterpie", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("caterpie", "blink")}
sleep = registerPose(
poseType = PoseType.SLEEP,
transformTicks = 10,
animations = arrayOf(bedrock("caterpie", "sleep"))
)
standing = registerPose(
poseName = "standing",
poseTypes = STATIONARY_POSES + UI_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("caterpie", "ground_idle")
)
)
walk = registerPose(
poseName = "walking",
poseTypes = MOVING_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("caterpie", "ground_walk")
)
)
shoulderLeft = registerPose(
poseType = PoseType.SHOULDER_LEFT,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("caterpie", "ground_idle")
),
transformedParts = arrayOf(
rootPart.createTransformation().addPosition(ModelPartTransformation.X_AXIS, shoulderOffset)
)
)
shoulderRight = registerPose(
poseType = PoseType.SHOULDER_RIGHT,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("caterpie", "ground_idle")
),
transformedParts = arrayOf(
rootPart.createTransformation().addPosition(ModelPartTransformation.X_AXIS, -shoulderOffset)
)
)
}
override fun getFaintAnimation(state: PosableState) = bedrockStateful("caterpie", "faint")
}
@@ -1,98 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen1
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.createTransformation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.ModelPartTransformation
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.PoseType.Companion.MOVING_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.STATIONARY_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class WeedleModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
override val rootPart = root.registerChildWithAllChildren("weedle")
override val head = getPart("head")
override var portraitScale = 2.2F
override var portraitTranslation = Vec3(-0.31, -1.83, 0.0)
override var profileScale = 0.8F
override var profileTranslation = Vec3(0.1, 0.6, 0.0)
lateinit var sleep: Pose
lateinit var standing: Pose
lateinit var walk: Pose
lateinit var shoulderLeft: Pose
lateinit var shoulderRight: Pose
val shoulderOffset = 3.8
val shoulderZOffset = -3
override val cryAnimation = CryProvider { bedrockStateful("weedle", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("weedle", "blink")}
sleep = registerPose(
poseType = PoseType.SLEEP,
animations = arrayOf(bedrock("weedle", "sleep"))
)
standing = registerPose(
poseName = "standing",
poseTypes = STATIONARY_POSES + UI_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("weedle", "ground_idle2")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = MOVING_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("weedle", "ground_walk")
)
)
shoulderLeft = registerPose(
poseType = PoseType.SHOULDER_LEFT,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("weedle", "ground_idle")
),
transformedParts = arrayOf(
rootPart.createTransformation().addPosition(ModelPartTransformation.X_AXIS, shoulderOffset),
rootPart.createTransformation().addPosition(ModelPartTransformation.Z_AXIS, shoulderZOffset)
)
)
shoulderRight = registerPose(
poseType = PoseType.SHOULDER_RIGHT,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("weedle", "ground_idle")
),
transformedParts = arrayOf(
rootPart.createTransformation().addPosition(ModelPartTransformation.X_AXIS, -shoulderOffset),
rootPart.createTransformation().addPosition(ModelPartTransformation.Z_AXIS, shoulderZOffset)
)
)
}
override fun getFaintAnimation(state: PosableState) = bedrockStateful("weedle", "faint")
}
@@ -1,59 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen5
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.CobblemonPose
import com.cobblemon.mod.common.entity.PoseType
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class JellicentModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
override val rootPart = root.registerChildWithAllChildren("jellicent")
override val head = getPart("head")
override var portraitScale = 1.13F
override var portraitTranslation = Vec3(-0.56, 1.64, 0.0)
override var profileScale = 0.51F
override var profileTranslation = Vec3(0.0, 1.05, 0.0)
lateinit var standing: CobblemonPose
lateinit var walk: CobblemonPose
override fun registerPoses() {
val blink = quirk { bedrockStateful("jellicent", "blink") }
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("jellicent", "render")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("jellicent", "render")
)
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PosableState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("jellicent", "faint") else null
}
@@ -1,57 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen7
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class CrabominableModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
override val rootPart = root.registerChildWithAllChildren("crabominable")
override val head = getPart("head")
override var portraitScale = 1.0F
override var portraitTranslation = Vec3(-0.55, 1.65, 0.0)
override var profileScale = 0.3F
override var profileTranslation = Vec3(0.0, 1.2, 0.0)
lateinit var standing: Pose
lateinit var walk: Pose
override fun registerPoses() {
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + UI_POSES,
animations = arrayOf(
singleBoneLook(),
bedrock("crabominable", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
animations = arrayOf(
singleBoneLook(),
bedrock("crabominable", "ground_idle"),
bedrock("crabominable", "ground_walk")
)
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PosableState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("crabominable", "faint") else null
}
@@ -220,10 +220,8 @@ object VaryingModelRepository {
inbuilt("squirtle", ::SquirtleModel)
inbuilt("wartortle", ::WartortleModel)
inbuilt("caterpie", ::CaterpieModel)
inbuilt("metapod", ::MetapodModel)
inbuilt("butterfree", ::ButterfreeModel)
inbuilt("weedle", ::WeedleModel)
inbuilt("beedrill", ::BeedrillModel)
inbuilt("raticate", ::RaticateModel)
inbuilt("raticate_alolan", ::RaticateAlolanModel)
@@ -446,7 +444,6 @@ object VaryingModelRepository {
inbuilt("bayleef", ::BayleefModel)
inbuilt("meganium", ::MeganiumModel)
inbuilt("crabrawler", ::CrabrawlerModel)
inbuilt("crabominable", ::CrabominableModel)
inbuilt("wimpod", ::WimpodModel)
inbuilt("golisopod", ::GolisopodModel)
inbuilt("ralts", ::RaltsModel)
@@ -502,7 +499,6 @@ object VaryingModelRepository {
inbuilt("woobat", ::WoobatModel)
inbuilt("swoobat", ::SwoobatModel)
inbuilt("frillish", ::FrillishModel)
inbuilt("jellicent", ::JellicentModel)
inbuilt("cubchoo", ::CubchooModel)
inbuilt("beartic", ::BearticModel)
inbuilt("dreepy", ::DreepyModel)
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.command
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.permission.CobblemonPermissions
import com.cobblemon.mod.common.api.storage.pc.link.PCLinkManager
import com.cobblemon.mod.common.api.storage.pc.link.PermissiblePcLink
@@ -54,8 +53,8 @@ object PcCommand {
throw SimpleCommandExceptionType(lang("command.pc.invalid-box", box, pc.boxes.size).red()).create()
}
PCLinkManager.addLink(PermissiblePcLink(pc, player, CobblemonPermissions.PC))
OpenPCPacket(pc, box = if (box != null) (box - 1) else null).sendToPlayer(player)
OpenPCPacket(pc, box = if (box != null) (box - 1) else null, silent = true).sendToPlayer(player)
return Command.SINGLE_SUCCESS
}
}
}
@@ -269,6 +269,7 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
attemptCatch(pokemonEntity)
},
ifCanceled = {
captureFuture.complete(false)
drop()
return
}
@@ -0,0 +1,107 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.net.messages.client.data
import com.cobblemon.mod.common.api.pokedex.entry.CobblemonBiomeDexEvolutionRequirementDisplay
import com.cobblemon.mod.common.api.pokedex.entry.CobblemonDexEvolutionRequirementDisplay
import com.cobblemon.mod.common.api.pokedex.entry.DexEntries
import com.cobblemon.mod.common.api.pokedex.entry.DexEvolutionDisplay
import com.cobblemon.mod.common.api.pokedex.entry.DexEvolutionRequirementDisplay
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.readIdentifier
import com.cobblemon.mod.common.util.readNullable
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.readText
import com.cobblemon.mod.common.util.writeIdentifier
import com.cobblemon.mod.common.util.writeNullable
import com.cobblemon.mod.common.util.writeString
import com.cobblemon.mod.common.util.writeText
import com.google.common.collect.Lists
import com.google.common.collect.Sets
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.util.ExtraCodecs
class DexEvolutionDisplaySyncPacket(entries: MutableList<DexEvolutionDisplay>)
: DataRegistrySyncPacket<DexEvolutionDisplay, DexEvolutionDisplaySyncPacket>(entries) {
override val id = ID
override fun encodeEntry(buffer: RegistryFriendlyByteBuf, entry: DexEvolutionDisplay) {
buffer.writeIdentifier(entry.fromSpecies)
buffer.writeString(entry.fromForm)
buffer.writeCollection(entry.fromAspects) { _, aspect -> buffer.writeString(aspect) }
buffer.writeIdentifier(entry.toSpecies)
buffer.writeString(entry.toForm)
buffer.writeCollection(entry.toAspects) { _, aspect -> buffer.writeString(aspect) }
buffer.writeCollection(entry.requirements) { _, req ->
when (req) {
is CobblemonBiomeDexEvolutionRequirementDisplay -> {
buffer.writeByte(1)
buffer.writeIdentifier(BuiltInRegistries.ITEM.getKey(req.icon))
buffer.writeText(req.tooltip)
buffer.writeNullable(req.condition) { b, cond ->
b.writeIdentifier(cond.id)
b.writeBoolean(cond.tag)
}
buffer.writeNullable(req.antiCondition) { b, cond ->
b.writeIdentifier(cond.id)
b.writeBoolean(cond.tag)
}
}
else -> {
buffer.writeByte(0)
buffer.writeIdentifier(BuiltInRegistries.ITEM.getKey(req.icon))
buffer.writeText(req.tooltip)
}
}
}
}
override fun decodeEntry(buffer: RegistryFriendlyByteBuf): DexEvolutionDisplay? {
val fromSpecies = buffer.readIdentifier()
val fromForm = buffer.readString()
val fromAspects = buffer.readCollection(Sets::newHashSetWithExpectedSize) { buffer.readString() }
val toSpecies = buffer.readIdentifier()
val toForm = buffer.readString()
val toAspects = buffer.readCollection(Sets::newHashSetWithExpectedSize) { buffer.readString() }
val requirements = buffer.readCollection(Lists::newArrayListWithExpectedSize) {
val type = buffer.readByte().toInt()
val icon = BuiltInRegistries.ITEM.get(buffer.readIdentifier())
val tooltip = buffer.readText().copy()
if (type == 1) {
val condition = buffer.readNullable { b ->
val id = b.readIdentifier()
val tag = b.readBoolean()
ExtraCodecs.TagOrElementLocation(id, tag)
}
val antiCondition = buffer.readNullable { b ->
val id = b.readIdentifier()
val tag = b.readBoolean()
ExtraCodecs.TagOrElementLocation(id, tag)
}
CobblemonBiomeDexEvolutionRequirementDisplay(icon, tooltip, condition, antiCondition)
} else {
CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
}.toList()
return DexEvolutionDisplay(fromSpecies, fromForm, fromAspects, toSpecies, toForm, toAspects, requirements)
}
override fun synchronizeDecoded(entries: Collection<DexEvolutionDisplay>) {
DexEntries.evolutionDisplays.clear()
DexEntries.evolutionDisplays.addAll(entries)
}
companion object {
val ID = cobblemonResource("dex_evolution_display_sync")
fun decode(buffer: RegistryFriendlyByteBuf): DexEvolutionDisplaySyncPacket =
DexEvolutionDisplaySyncPacket(mutableListOf()).apply { decodeBuffer(buffer) }
}
}
@@ -1,100 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.net.messages.client.data
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.api.pokemon.evolution.PreEvolution
import com.cobblemon.mod.common.pokemon.FormData
import com.cobblemon.mod.common.pokemon.Species
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.readIdentifier
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.writeIdentifier
import com.cobblemon.mod.common.util.writeString
import com.google.gson.reflect.TypeToken
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
/**
* Carries the evolutions/preEvolution of every [Species] (and any [FormData] overrides), sent right after
* [SpeciesRegistrySyncPacket] so that every species is already registered by the time this is decoded. This avoids
* the cross-species lookup ordering hazards that come with resolving evolutions during the species sync itself.
*/
class SpeciesEvolutionSyncPacket(entries: Collection<SpeciesEvolutionEntry>) : DataRegistrySyncPacket<SpeciesEvolutionEntry, SpeciesEvolutionSyncPacket>(entries) {
override val id = ID
override fun encodeEntry(buffer: RegistryFriendlyByteBuf, entry: SpeciesEvolutionEntry) {
try {
buffer.writeIdentifier(entry.speciesIdentifier)
buffer.writeString(PokemonSpecies.gson.toJson(entry.evolutions, EVOLUTION_SET_TYPE))
buffer.writeNullable(entry.preEvolution) { _, preEvolution -> buffer.writeString(PokemonSpecies.gson.toJson(preEvolution, PreEvolution::class.java)) }
buffer.writeCollection(entry.formOverrides) { _, override ->
buffer.writeString(override.formName)
buffer.writeNullable(override.evolutions) { _, evolutions -> buffer.writeString(PokemonSpecies.gson.toJson(evolutions, EVOLUTION_SET_TYPE)) }
buffer.writeNullable(override.preEvolution) { _, preEvolution -> buffer.writeString(PokemonSpecies.gson.toJson(preEvolution, PreEvolution::class.java)) }
}
} catch (e: Exception) {
Cobblemon.LOGGER.error("Caught exception encoding evolution data for species {}", entry.speciesIdentifier, e)
}
}
override fun decodeEntry(buffer: RegistryFriendlyByteBuf): SpeciesEvolutionEntry? {
val identifier = buffer.readIdentifier()
return try {
val evolutions: MutableSet<Evolution> = PokemonSpecies.gson.fromJson(buffer.readString(), EVOLUTION_SET_TYPE)
val preEvolution = buffer.readNullable { _ -> PokemonSpecies.gson.fromJson(buffer.readString(), PreEvolution::class.java) }
val formOverrides = buffer.readList { _ ->
val formName = buffer.readString()
val formEvolutions = buffer.readNullable { _ -> PokemonSpecies.gson.fromJson<MutableSet<Evolution>>(buffer.readString(), EVOLUTION_SET_TYPE) }
val formPreEvolution = buffer.readNullable { _ -> PokemonSpecies.gson.fromJson(buffer.readString(), PreEvolution::class.java) }
FormEvolutionOverride(formName, formEvolutions, formPreEvolution)
}
SpeciesEvolutionEntry(identifier, evolutions, preEvolution, formOverrides)
} catch (e: Exception) {
Cobblemon.LOGGER.error("Caught exception decoding evolution data for species {}", identifier, e)
null
}
}
override fun synchronizeDecoded(entries: Collection<SpeciesEvolutionEntry>) {
entries.forEach { entry ->
val species = PokemonSpecies.getByIdentifier(entry.speciesIdentifier)
if (species == null) {
Cobblemon.LOGGER.error("Received evolution data for unknown species {}", entry.speciesIdentifier)
return@forEach
}
species.applyEvolutionData(entry.evolutions, entry.preEvolution)
entry.formOverrides.forEach { override ->
species.getFormByName(override.formName).applyEvolutionData(override.evolutions, override.preEvolution)
}
}
}
companion object {
val ID = cobblemonResource("species_evolution_sync")
private val EVOLUTION_SET_TYPE = TypeToken.getParameterized(MutableSet::class.java, Evolution::class.java).type
fun decode(buffer: RegistryFriendlyByteBuf): SpeciesEvolutionSyncPacket = SpeciesEvolutionSyncPacket(emptyList()).apply { decodeBuffer(buffer) }
}
}
class SpeciesEvolutionEntry(
val speciesIdentifier: ResourceLocation,
val evolutions: MutableSet<Evolution>,
val preEvolution: PreEvolution?,
val formOverrides: List<FormEvolutionOverride>
)
class FormEvolutionOverride(
val formName: String,
val evolutions: MutableSet<Evolution>?,
val preEvolution: PreEvolution?
)
@@ -34,17 +34,19 @@ class OpenPCPacket : NetworkPacket<OpenPCPacket> {
val storeID: UUID
val box: Int?
val unseenWallpapers: Set<ResourceLocation>
val silent: Boolean
@JvmOverloads
@Deprecated("Use the constructor with the PCStore object instead, this will become private in a future title update", level = DeprecationLevel.WARNING)
constructor(storeID: UUID, box: Int? = null, unseenWallpapers: Set<ResourceLocation> = emptySet()) {
constructor(storeID: UUID, box: Int? = null, unseenWallpapers: Set<ResourceLocation> = emptySet(), silent: Boolean? = false) {
this.storeID = storeID
this.box = box
this.unseenWallpapers = unseenWallpapers
this.silent = silent ?: false
}
@JvmOverloads
constructor(pc: PCStore, box: Int? = null): this(pc.uuid, box, pc.unseenWallpapers)
constructor(pc: PCStore, box: Int? = null, silent: Boolean? = false): this(pc.uuid, box, pc.unseenWallpapers, silent ?: false)
override val id = ID
@@ -52,6 +54,7 @@ class OpenPCPacket : NetworkPacket<OpenPCPacket> {
buffer.writeUUID(storeID)
buffer.writeNullable(box) { buf, value -> buf.writeInt(value)}
buffer.writeCollection(unseenWallpapers) { _, it -> buffer.writeResourceLocation(it) }
buffer.writeBoolean(silent)
}
companion object {
@@ -59,7 +62,8 @@ class OpenPCPacket : NetworkPacket<OpenPCPacket> {
fun decode(buffer: RegistryFriendlyByteBuf) = OpenPCPacket(
buffer.readUUID(),
buffer.readNullable { it.readInt() },
buffer.readList { buffer.readResourceLocation() }.toSet()
buffer.readList { buffer.readResourceLocation() }.toSet(),
buffer.readBoolean()
)
}
}
}
@@ -0,0 +1,55 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://www.mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.net.messages.server.storage.pc
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.api.storage.pc.PCPosition
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.readPCPosition
import com.cobblemon.mod.common.util.writePCPosition
import java.util.UUID
import net.minecraft.network.RegistryFriendlyByteBuf
class MovePCPokemonBatchPacket(val moves: List<PCPokemonMove>) : NetworkPacket<MovePCPokemonBatchPacket> {
override val id = ID
data class PCPokemonMove(
val pokemonID: UUID,
val oldPosition: PCPosition,
val newPosition: PCPosition
)
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeVarInt(moves.size)
moves.forEach { move ->
buffer.writeUUID(move.pokemonID)
buffer.writePCPosition(move.oldPosition)
buffer.writePCPosition(move.newPosition)
}
}
companion object {
val ID = cobblemonResource("move_pc_pokemon_batch")
const val MAX_MOVES = 60
fun decode(buffer: RegistryFriendlyByteBuf): MovePCPokemonBatchPacket {
val size = buffer.readVarInt()
require(size in 1..MAX_MOVES) { "Invalid batch PC move size: $size" }
return MovePCPokemonBatchPacket(
List(size) {
PCPokemonMove(
pokemonID = buffer.readUUID(),
oldPosition = buffer.readPCPosition(),
newPosition = buffer.readPCPosition()
)
}
)
}
}
}
@@ -0,0 +1,23 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://www.mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.net.serverhandling.storage.pc
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
import com.cobblemon.mod.common.api.storage.pc.link.PCLinkManager
import com.cobblemon.mod.common.net.messages.client.storage.pc.ClosePCPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePCPokemonBatchPacket
import net.minecraft.server.MinecraftServer
import net.minecraft.server.level.ServerPlayer
object MovePCPokemonBatchHandler : ServerNetworkPacketHandler<MovePCPokemonBatchPacket> {
override fun handle(packet: MovePCPokemonBatchPacket, server: MinecraftServer, player: ServerPlayer) {
val pc = PCLinkManager.getPC(player) ?: return run { ClosePCPacket(null).sendToPlayer(player) }
pc.moveBatch(packet.moves.map { Triple(it.pokemonID, it.oldPosition, it.newPosition) })
}
}
@@ -76,7 +76,7 @@ class FormData(
@SerializedName("moves")
private var _moves: Learnset? = null,
@SerializedName("evolutions")
private var _evolutions: MutableSet<Evolution>? = null,
private val _evolutions: MutableSet<Evolution>? = null,
@SerializedName("abilities")
private var _abilities: AbilityPool? = null,
@SerializedName("drops")
@@ -84,7 +84,7 @@ class FormData(
@SerializedName("pokedex")
private var _pokedex: MutableList<String>? = null,
@SerializedName("preEvolution")
private var _preEvolution: PreEvolution? = null,
private val _preEvolution: PreEvolution? = null,
private var standingEyeHeight: Float? = null,
private var swimmingEyeHeight: Float? = null,
private var flyingEyeHeight: Float? = null,
@@ -269,22 +269,13 @@ class FormData(
this.species = species
this.behaviour.parent = species.behaviour
Cobblemon.statProvider.provide(this)
this._lightingData?.let { this._lightingData = it.copy(lightLevel = it.lightLevel.coerceIn(0, 15)) }
behaviour.herd.initialize()
return this
}
/**
* Applies decoded evolution data onto this form, received separately via [com.cobblemon.mod.common.net.messages.client.data.SpeciesEvolutionSyncPacket]
* after every species has already been registered.
*/
internal fun applyEvolutionData(evolutions: MutableSet<Evolution>?, preEvolution: PreEvolution?) {
this._evolutions = evolutions
this._preEvolution = preEvolution
// These properties are lazy, force resolution now that every species is guaranteed to be registered
// These properties are lazy, these need all species to be reloaded but SpeciesAdditions is what will eventually trigger this after all species have been loaded
this.preEvolution?.species
this.preEvolution?.form
this.evolutions.size
this._lightingData?.let { this._lightingData = it.copy(lightLevel = it.lightLevel.coerceIn(0, 15)) }
behaviour.herd.initialize()
return this
}
internal fun resolveEvolutionMoves() {
@@ -181,20 +181,11 @@ class Species : ShowdownIdentifiable {
this.forms.add(0, this.standardForm)
}
this.lightingData?.let { this.lightingData = it.copy(lightLevel = it.lightLevel.coerceIn(0, 15)) }
behaviour.herd.initialize()
}
/**
* Applies decoded evolution data onto this species, received separately via [com.cobblemon.mod.common.net.messages.client.data.SpeciesEvolutionSyncPacket]
* after every species has already been registered.
*/
internal fun applyEvolutionData(evolutions: MutableSet<Evolution>, preEvolution: PreEvolution?) {
// Mutate in place rather than reassign: standardForm may have already captured this.evolutions by reference.
this.evolutions.clear()
this.evolutions.addAll(evolutions)
this.preEvolution = preEvolution
// These properties are lazy, these need all species to be reloaded but SpeciesAdditions is what will eventually trigger this after all species have been loaded
this.preEvolution?.species
this.preEvolution?.form
this.evolutions.size
behaviour.herd.initialize()
}
// Ran after initialize due to us creating a Pokémon here which requires all the properties in #initialize to be present for both this and the results, this is the easiest way to quickly resolve species + form
@@ -25,20 +25,12 @@ internal class CobblemonLazyPreEvolution(private val rawData: String) : PreEvolu
this.properties.species?.asIdentifierDefaultingNamespace()?.let { PokemonSpecies.getByIdentifier(it) } ?: throw IllegalArgumentException("A PreEvolution needs a valid species")
}
private fun computeForm(extraAspects: Set<String>): FormData {
val properties = this.properties
return properties.form?.let { formId -> this.species.forms.firstOrNull { it.formOnlyShowdownId().equals(formId, true) } }
?: this.species.getForm(properties.aspects + this.rawData.split(" ").map { it.lowercase() }.toSet() + extraAspects)
private val lazyForm: FormData by lazy {
this.properties.form?.let { formId -> this.species.forms.firstOrNull { it.formOnlyShowdownId().equals(formId, true) } } ?: this.species.standardForm
}
private val lazyForm: FormData by lazy { computeForm(emptySet()) }
override val species: Species
get() = this.lazySpecies
override val form: FormData
get() = this.lazyForm
override fun resolveForm(extraAspects: Set<String>): FormData {
return if (extraAspects.isEmpty()) this.form else computeForm(extraAspects)
}
}
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.api.abilities.Abilities
import com.cobblemon.mod.common.api.data.DataRegistry
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokemon.Natures
import com.cobblemon.mod.common.api.pokemon.feature.AbstractChoiceSpeciesFeatureProvider
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
@@ -134,7 +135,7 @@ internal object PropertiesCompletionProvider : DataRegistry {
private fun addDefaults() {
this.inject(setOf("level", "lvl", "l"), setOf("1", "${Cobblemon.config.maxPokemonLevel}") )
this.inject(setOf("shiny", "s"), setOf("yes", "no"))
this.inject(setOf("gender"), Gender.values().map { it.name.lowercase() })
this.inject(setOf("gender"), Gender.entries.map { it.name.lowercase() })
this.inject(setOf("friendship"), setOf("0", Cobblemon.config.maxPokemonFriendship.toString()))
this.inject(setOf("fullness"), setOf("0", "255"))
this.inject(setOf("pokeball"), PokeBalls.all().map { it.name.simplify() })
@@ -164,7 +165,11 @@ internal object PropertiesCompletionProvider : DataRegistry {
private fun addCustom() {
CustomPokemonProperty.properties.forEach { property ->
// We won't tab complete properties that have no key attached to them as it would be fairly hard to determine which one to suggest
if (property.needsKey) {
if (property.needsKey && property !is AbstractChoiceSpeciesFeatureProvider<*>) {
this.inject(property.keys, property.examples())
}
// Except choice features that fill examples with all available choices - Gito
if (property is AbstractChoiceSpeciesFeatureProvider<*>) {
this.inject(property.keys, property.examples())
}
}
@@ -175,4 +180,4 @@ internal object PropertiesCompletionProvider : DataRegistry {
val suggestions: Collection<String>
)
}
}
@@ -12,12 +12,10 @@ import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import com.google.gson.JsonSerializationContext
import com.google.gson.JsonSerializer
import java.lang.reflect.Type
import net.minecraft.world.phys.AABB
object BoxAdapter : JsonDeserializer<AABB>, JsonSerializer<AABB> {
object BoxAdapter : JsonDeserializer<AABB> {
override fun deserialize(json: JsonElement, type: Type, ctx: JsonDeserializationContext): AABB {
json as JsonObject
return AABB(
@@ -29,15 +27,4 @@ object BoxAdapter : JsonDeserializer<AABB>, JsonSerializer<AABB> {
json.get("maxZ")?.asDouble ?: 9999999.0
)
}
override fun serialize(src: AABB, typeOfSrc: Type, ctx: JsonSerializationContext): JsonElement {
val json = JsonObject()
json.addProperty("minX", src.minX)
json.addProperty("minY", src.minY)
json.addProperty("minZ", src.minZ)
json.addProperty("maxX", src.maxX)
json.addProperty("maxY", src.maxY)
json.addProperty("maxZ", src.maxZ)
return json
}
}
@@ -13,8 +13,6 @@ import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import com.google.gson.JsonSerializationContext
import com.google.gson.JsonSerializer
import java.lang.reflect.Type
/**
@@ -24,7 +22,7 @@ import java.lang.reflect.Type
* @author Hiroku
* @since July 24th, 2022
*/
object DropEntryAdapter : JsonDeserializer<DropEntry>, JsonSerializer<DropEntry> {
object DropEntryAdapter : JsonDeserializer<DropEntry> {
override fun deserialize(json: JsonElement, type: Type, ctx: JsonDeserializationContext): DropEntry {
json as JsonObject
val entryClass = json.get("type")?.asString
@@ -32,12 +30,4 @@ object DropEntryAdapter : JsonDeserializer<DropEntry>, JsonSerializer<DropEntry>
?: DropEntry.defaultType
return ctx.deserialize(json, entryClass)
}
override fun serialize(src: DropEntry, typeOfSrc: Type, ctx: JsonSerializationContext): JsonElement {
val json = ctx.serialize(src, src::class.java).asJsonObject
val name = DropEntry.entryTypes.entries.find { it.value == src::class.java }?.key
?: throw IllegalArgumentException("Cannot resolve drop entry type for ${src::class.qualifiedName}")
json.addProperty("type", name)
return json
}
}
@@ -10,21 +10,13 @@ package com.cobblemon.mod.common.util.adapters
import com.bedrockk.molang.Expression
import com.bedrockk.molang.MoLang
import com.cobblemon.mod.common.util.getString
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonPrimitive
import com.google.gson.JsonSerializationContext
import com.google.gson.JsonSerializer
import java.lang.reflect.Type
object ExpressionAdapter : JsonDeserializer<Expression>, JsonSerializer<Expression> {
object ExpressionAdapter : JsonDeserializer<Expression> {
override fun deserialize(json: JsonElement, typeOfT: Type, ctx: JsonDeserializationContext): Expression {
return MoLang.createParser(json.asString).parseExpression()
}
override fun serialize(src: Expression, typeOfSrc: Type, ctx: JsonSerializationContext): JsonElement {
return JsonPrimitive(src.getString())
}
}
@@ -13,9 +13,6 @@ import com.cobblemon.mod.common.util.asExpressionLike
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonPrimitive
import com.google.gson.JsonSerializationContext
import com.google.gson.JsonSerializer
import java.lang.reflect.Type
/**
@@ -25,7 +22,7 @@ import java.lang.reflect.Type
* @author Hiroku
* @since October 22nd, 2023
*/
object ExpressionLikeAdapter : JsonDeserializer<ExpressionLike>, JsonSerializer<ExpressionLike> {
object ExpressionLikeAdapter : JsonDeserializer<ExpressionLike> {
override fun deserialize(json: JsonElement, type: Type, ctx: JsonDeserializationContext): ExpressionLike {
return if (json.isJsonPrimitive) {
json.asString.asExpressionLike()
@@ -35,8 +32,4 @@ object ExpressionLikeAdapter : JsonDeserializer<ExpressionLike>, JsonSerializer<
throw IllegalArgumentException("Invalid expression JSON: $json")
}
}
override fun serialize(src: ExpressionLike, typeOfSrc: Type, ctx: JsonSerializationContext): JsonElement {
return JsonPrimitive(src.getString())
}
}
@@ -11,23 +11,22 @@ package com.cobblemon.mod.common.util.adapters
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Environment
import com.cobblemon.mod.common.util.server
import com.google.gson.JsonElement
import com.mojang.serialization.DynamicOps
import com.mojang.serialization.JsonOps
import net.minecraft.client.Minecraft
import net.minecraft.core.RegistryAccess
import net.minecraft.resources.RegistryOps
import net.minecraft.core.registries.BuiltInRegistries
/**
* Utility for getting RegistryAccess with the correct client/server registry access.
* Used when as a fallback when manually encoding/decoding registry-backed values (e.g., items, predicates).
*/
object RegistryAccessResolver {
private val builtInAccess: RegistryAccess by lazy { RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY) }
fun getAccess(): RegistryAccess {
try {
Cobblemon.implementation
} catch (e: UninitializedPropertyAccessException) {
return RegistryAccess.EMPTY
return builtInAccess
}
if (Cobblemon.implementation.environment() == Environment.CLIENT) {
val minecraft = Minecraft.getInstance()
@@ -35,13 +34,12 @@ object RegistryAccessResolver {
return minecraft.level!!.registryAccess()
}
if (Cobblemon.implementation.environment() == Environment.SERVER) {
val server = server()
if (server != null)
return server.registryAccess()
}
return RegistryAccess.EMPTY
return builtInAccess
}
}
@@ -364,7 +364,7 @@
"bones": {
"body": {
"rotation": [55, 0, 0],
"position": [0, "-8+Math.cos((query.anim_time) * 90 * 2) * 0.2", -1]
"position": [0, "-3+Math.cos((query.anim_time) * 90 * 2) * 0.2", -1]
},
"torso2": {
"rotation": ["-12.5+Math.cos((query.anim_time-0.4) * 90 * 2) * 2", 0, 0]
@@ -426,16 +426,16 @@
"body": {
"rotation": [55, 0, 0],
"position": {
"0.0": [0, "-9+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", -1.5],
"0.25": [0, "-9+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", 0.5],
"0.5": [0, "-9+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", 3.5],
"0.6667": [0, "-9+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", 5.17],
"0.75": [0, "-9+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", 5.25],
"0.7917": [0, "-9+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", 3.92],
"1.0": [0, "-9+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", 1],
"1.2083": [0, "-9+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", -1.25],
"1.375": [0, "-9+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", -1.75],
"1.5": [0, "-9+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", -1.5]
"0.0": [0, "-3+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", -1.5],
"0.25": [0, "-3+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", 0.5],
"0.5": [0, "-3+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", 3.5],
"0.6667": [0, "-3+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", 5.17],
"0.75": [0, "-3+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", 5.25],
"0.7917": [0, "-3+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", 3.92],
"1.0": [0, "-3+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", 1],
"1.2083": [0, "-3+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", -1.25],
"1.375": [0, "-3+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", -1.75],
"1.5": [0, "-3+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", -1.5]
}
},
"torso2": {
@@ -313,7 +313,7 @@
"bones": {
"body": {
"rotation": [47.5, 0, 0],
"position": [0, "-10+Math.cos((query.anim_time) * 90 * 1) * 0.3", 0]
"position": [0, "-6+Math.cos((query.anim_time) * 90 * 1) * 0.3", 0]
},
"torso": {
"rotation": [35, 0, 0],
@@ -426,7 +426,7 @@
"bones": {
"body": {
"rotation": [47.5, 0, 0],
"position": [0, -10, "Math.cos((query.anim_time-0.5) * 90 * (2+2/3)) * 4"]
"position": [0, -6, "Math.cos((query.anim_time-0.5) * 90 * (2+2/3)) * 4"]
},
"torso": {
"rotation": [35, 0, 0],
@@ -1925,7 +1925,7 @@
},
"body": {
"rotation": ["68.5-Math.cos((query.anim_time)*360/1-20)*-3", 0, 0],
"position": [0, "-24+Math.cos((query.anim_time)*360/1-90)*-0.5", "-1+Math.cos((query.anim_time)*360/1-45)*4"]
"position": [0, "Math.cos((query.anim_time)*360/1-90)*-0.5", "-1+Math.cos((query.anim_time)*360/1-45)*4"]
},
"neck": {
"rotation": [-7.5, 0, 0]
@@ -96,13 +96,13 @@
"parent": "head",
"pivot": [0, 6, 0.25],
"cubes": [
{"origin": [-2.4, 4.4, -5.28], "size": [1, 1, 0], "inflate": 0.01, "pivot": [-2, 5, 0], "rotation": [0, 0, -45], "uv": [12, 19]},
{"origin": [-1.75, 5.25, 5.76], "size": [1, 1, 0], "pivot": [-2, 5, 0], "rotation": [0, 0, -45], "uv": [8, 10]},
{"origin": [1.25, 1.25, 5.76], "size": [1, 1, 0], "pivot": [-2, 5, 0], "rotation": [0, 0, -45], "uv": [9, 2]},
{"origin": [-4.51, 6, -2.75], "size": [0, 1, 1], "pivot": [-2, 6.5, -2.25], "rotation": [-45, 0, 0], "uv": [7, 1]},
{"origin": [4.51, 6.75, -2.75], "size": [0, 1, 1], "pivot": [-2, 6.5, -2.25], "rotation": [-45, 0, 0], "uv": [0, 5]},
{"origin": [4.51, 8, 0.5], "size": [0, 1, 1], "pivot": [-2, 6.5, -2.25], "rotation": [-45, 0, 0], "uv": [0, 1]},
{"origin": [-4.51, 6.5, 1.25], "size": [0, 1, 1], "pivot": [-2, 7, 1.75], "rotation": [-45, 0, 0], "uv": [0, 6]}
{"origin": [-2.5, 4.4, -5.28], "size": [1, 1, 0], "uv": [12, 19]},
{"origin": [-2.5, 5.5, 5.76], "size": [1, 1, 0], "uv": [8, 10]},
{"origin": [2.5, 4.5, 5.76], "size": [1, 1, 0], "uv": [9, 2]},
{"origin": [-4.51, 6, -2.75], "size": [0, 1, 1], "uv": [7, 1]},
{"origin": [4.51, 6.5, -2.25], "size": [0, 1, 1], "uv": [0, 5]},
{"origin": [4.51, 5, 0.75], "size": [0, 1, 1], "uv": [0, 1]},
{"origin": [-4.51, 6.5, 1.75], "size": [0, 1, 1], "uv": [0, 6]}
]
},
{
@@ -119,7 +119,6 @@
"name": "eye_right",
"parent": "eyes",
"pivot": [-1.5, 5.75, -5.25],
"rotation": [0, 0, 5],
"cubes": [
{"origin": [-2.5, 6, -5.26], "size": [1, 2, 0], "uv": [8, 19]}
]
@@ -152,7 +151,6 @@
"name": "eye_left",
"parent": "eyes",
"pivot": [1.5, 5.75, -5.25],
"rotation": [0, 0, -5],
"cubes": [
{"origin": [1.5, 6, -5.26], "size": [1, 2, 0], "uv": [8, 8]}
]
@@ -112,7 +112,7 @@
{
"name": "head",
"parent": "neck",
"pivot": [0, 9, -0.25],
"pivot": [0, 8.25, -0.25],
"cubes": [
{"origin": [-4, 7.5, -3.75], "size": [8, 6, 7], "uv": [0, 0]},
{"origin": [-1.25, 9.25, -4.75], "size": [2, 2, 1], "inflate": 0.01, "uv": [23, 1]},
@@ -151,67 +151,75 @@
{
"name": "eye_left",
"parent": "eyes",
"pivot": [2.75, 11, -3.76],
"rotation": [0, 0, -7.5],
"pivot": [2.75, 10.75, -3.76]
},
{
"name": "iris_left",
"parent": "eye_left",
"pivot": [2.75, 10.875, -3.765],
"cubes": [
{"origin": [2, 10, -3.76], "size": [1.5, 2, 0], "uv": [0, 0]}
{"origin": [2, 9.75, -3.76], "size": [1.5, 2, 0], "uv": [0, 0]}
]
},
{
"name": "pupil_left",
"parent": "eye_left",
"pivot": [2.75, 11.25, -3.77],
"parent": "iris_left",
"pivot": [2.75, 11, -3.77],
"cubes": [
{"origin": [2.25, 10.5, -3.77], "size": [1, 1, 0], "uv": [2, 0]}
{"origin": [2.25, 10.25, -3.77], "size": [1, 1, 0], "uv": [2, 0]}
]
},
{
"name": "locator_eye_left",
"parent": "pupil_left",
"pivot": [2.75, 11, -4],
"pivot": [2.75, 10.75, -4],
"locators": {
"eye2": [2.75, 11, -4]
"eye2": [2.75, 10.75, -4]
}
},
{
"name": "eyelid_left",
"parent": "eye_left",
"pivot": [2.75, 11, -3.17],
"pivot": [2.75, 10.75, -3.17],
"cubes": [
{"origin": [1.75, 10, -3.67], "size": [2, 2, 1], "inflate": 0.01, "uv": [1, 3]}
{"origin": [1.75, 9.75, -3.67], "size": [2, 2, 1], "inflate": 0.01, "uv": [1, 3]}
]
},
{
"name": "eye_right",
"parent": "eyes",
"pivot": [-2.75, 11, -3.76],
"rotation": [0, 0, 7.5],
"pivot": [-2.75, 10.75, -3.76]
},
{
"name": "iris_right",
"parent": "eye_right",
"pivot": [-2.75, 10.875, -3.765],
"cubes": [
{"origin": [-3.5, 10, -3.76], "size": [1.5, 2, 0], "uv": [0, 2], "mirror": true}
{"origin": [-3.5, 9.75, -3.76], "size": [1.5, 2, 0], "uv": [0, 2], "mirror": true}
]
},
{
"name": "pupil_right",
"parent": "eye_right",
"pivot": [-2.75, 11.25, -3.77],
"parent": "iris_right",
"pivot": [-2.75, 11, -3.77],
"cubes": [
{"origin": [-3.25, 10.5, -3.77], "size": [1, 1, 0], "uv": [2, 1], "mirror": true}
{"origin": [-3.25, 10.25, -3.77], "size": [1, 1, 0], "uv": [2, 1], "mirror": true}
]
},
{
"name": "locator_eye_right",
"parent": "pupil_right",
"pivot": [-2.75, 11, -4],
"pivot": [-2.75, 10.75, -4],
"locators": {
"eye1": [-2.75, 11, -4]
"eye1": [-2.75, 10.75, -4]
}
},
{
"name": "eyelid_right",
"parent": "eye_right",
"pivot": [-2.75, 11, -3.17],
"pivot": [-2.75, 10.75, -3.17],
"cubes": [
{"origin": [-3.75, 10, -3.67], "size": [2, 2, 1], "inflate": 0.01, "uv": [1, 15], "mirror": true}
{"origin": [-3.75, 9.75, -3.67], "size": [2, 2, 1], "inflate": 0.01, "uv": [1, 15], "mirror": true}
]
},
{
@@ -247,9 +247,7 @@
"pivot": [0, 8.5, 0],
"cubes": [
{"origin": [-2, 12, -2], "size": [4, 4, 4], "inflate": -0.25, "uv": [31, 2]},
{"origin": [2, 16, 2], "size": [-4, -4, -4], "uv": [17, 30]},
{"origin": [-1, 14.25, -1], "size": [2, 3, 2], "uv": [44, 0]},
{"origin": [1, 17.25, 1], "size": [-2, -1, -2], "inflate": -0.25, "uv": [21, 29]}
{"origin": [-1, 14.25, -1], "size": [2, 3, 2], "uv": [44, 0]}
]
},
{
@@ -267,9 +265,7 @@
"rotation": [0, 0, 67.5],
"cubes": [
{"origin": [-2, 12, -2], "size": [4, 4, 4], "inflate": -0.25, "uv": [67, 2]},
{"origin": [2, 16, 2], "size": [-4, -4, -4], "uv": [49, 30]},
{"origin": [-1, 14.25, -1], "size": [2, 3, 2], "uv": [80, 0]},
{"origin": [1, 17.25, 1], "size": [-2, -1, -2], "inflate": -0.25, "uv": [37, 29]}
{"origin": [-1, 14.25, -1], "size": [2, 3, 2], "uv": [80, 0]}
]
},
{
@@ -279,9 +275,7 @@
"rotation": [0, 0, -67.5],
"cubes": [
{"origin": [-2, 12, -2], "size": [4, 4, 4], "inflate": -0.25, "uv": [49, 2]},
{"origin": [2, 16, 2], "size": [-4, -4, -4], "uv": [33, 30]},
{"origin": [-1, 14.25, -1], "size": [2, 3, 2], "uv": [62, 0]},
{"origin": [1, 17.25, 1], "size": [-2, -1, -2], "inflate": -0.25, "uv": [69, 29]}
{"origin": [-1, 14.25, -1], "size": [2, 3, 2], "uv": [62, 0]}
]
},
{
@@ -299,9 +293,7 @@
"rotation": [0, 0, -137.5],
"cubes": [
{"origin": [-2, 12, -2], "size": [4, 4, 4], "inflate": -0.25, "uv": [85, 2]},
{"origin": [2, 16, 2], "size": [-4, -4, -4], "uv": [81, 30]},
{"origin": [-1, 14.25, -1], "size": [2, 3, 2], "uv": [98, 0]},
{"origin": [1, 17.25, 1], "size": [-2, -1, -2], "inflate": -0.25, "uv": [85, 29]}
{"origin": [-1, 14.25, -1], "size": [2, 3, 2], "uv": [98, 0]}
]
},
{
@@ -311,9 +303,7 @@
"rotation": [0, 0, 137.5],
"cubes": [
{"origin": [-2, 12, -2], "size": [4, 4, 4], "inflate": -0.25, "uv": [103, 2]},
{"origin": [2, 16, 2], "size": [-4, -4, -4], "uv": [65, 30]},
{"origin": [-1, 14.25, -1], "size": [2, 3, 2], "uv": [116, 0]},
{"origin": [1, 17.25, 1], "size": [-2, -1, -2], "inflate": -0.25, "uv": [53, 29]}
{"origin": [-1, 14.25, -1], "size": [2, 3, 2], "uv": [116, 0]}
]
},
{
@@ -0,0 +1,69 @@
{
"portraitScale": 2.1,
"portraitTranslation": [ -0.55, -1.55, 0 ],
"profileScale": 0.80,
"profileTranslation": [ 0, 0.55, 0 ],
"rootBone": "caterpie",
"animations": {
"cry": "q.bedrock_stateful('caterpie', 'cry')",
"faint": "q.bedrock_primary('caterpie', 'faint', q.curve('one'))"
},
"poses": {
"standing": {
"poseTypes": ["STAND", "NONE", "FLOAT", "PORTRAIT", "PROFILE"],
"animations": [
"q.look('head')",
"q.bedrock('caterpie', 'ground_idle')"
],
"quirks": ["q.bedrock_quirk('caterpie', 'blink')"]
},
"walking": {
"poseTypes": ["WALK"],
"animations": [
"q.look('head')",
"q.bedrock('caterpie', 'ground_walk')"
],
"quirks": ["q.bedrock_quirk('caterpie', 'blink')"]
},
"swimming": {
"poseTypes": ["WALK","SWIM"],
"isTouchingWater": true,
"animations": [
"q.pitch_tilt('body')",
"q.look('head')",
"q.bedrock('caterpie', 'ground_walk')"
],
"quirks": ["q.bedrock_quirk('caterpie', 'blink')"]
},
"sleep": {
"poseTypes": ["SLEEP"],
"namedAnimations": {
"cry": "q.bedrock_stateful('dummy', 'cry')"
},
"animations": ["q.bedrock('caterpie', 'sleep')"]
},
"shoulder_left": {
"poseTypes": ["SHOULDER_LEFT"],
"animations": ["q.look('head')", "q.bedrock('caterpie', 'ground_idle')"],
"quirks": ["q.bedrock_quirk('caterpie', 'blink')"],
"transformedParts": [
{
"part": "body",
"position": [3, 0, 0]
}
]
},
"shoulder_right": {
"poseTypes": ["SHOULDER_RIGHT"],
"animations": ["q.look('head')", "q.bedrock('caterpie', 'ground_idle')"],
"quirks": ["q.bedrock_quirk('caterpie', 'blink')"],
"transformedParts": [
{
"part": "body",
"position": [-3, 0, 0]
}
]
}
}
}
@@ -0,0 +1,69 @@
{
"portraitScale": 2.3,
"portraitTranslation": [-0.53, -1.9, 0],
"profileScale": 0.80,
"profileTranslation": [0, 0.51, 0],
"rootBone": "weedle",
"animations": {
"cry": "q.bedrock_stateful('weedle', 'cry')",
"faint": "q.bedrock_primary('weedle', 'faint', q.curve('one'))"
},
"poses": {
"standing": {
"poseTypes": ["STAND", "NONE", "FLOAT", "PORTRAIT", "PROFILE"],
"animations": [
"q.look('head')",
"q.bedrock('weedle', 'ground_idle2')"
],
"quirks": ["q.bedrock_quirk('weedle', 'blink')"]
},
"walking": {
"poseTypes": ["WALK"],
"animations": [
"q.look('head')",
"q.bedrock('weedle', 'ground_walk')"
],
"quirks": ["q.bedrock_quirk('weedle', 'blink')"]
},
"swimming": {
"poseTypes": ["WALK","SWIM"],
"isTouchingWater": true,
"animations": [
"q.pitch_tilt('body')",
"q.look('head')",
"q.bedrock('weedle', 'ground_walk')"
],
"quirks": ["q.bedrock_quirk('weedle', 'blink')"]
},
"sleep": {
"poseTypes": ["SLEEP"],
"namedAnimations": {
"cry": "q.bedrock_stateful('dummy', 'cry')"
},
"animations": ["q.bedrock('weedle', 'sleep')"]
},
"shoulder_left": {
"poseTypes": ["SHOULDER_LEFT"],
"animations": ["q.look('head')", "q.bedrock('weedle', 'ground_idle')"],
"quirks": ["q.bedrock_quirk('weedle', 'blink')"],
"transformedParts": [
{
"part": "body",
"position": [3.8, 0, -3]
}
]
},
"shoulder_right": {
"poseTypes": ["SHOULDER_RIGHT"],
"animations": ["q.look('head')", "q.bedrock('weedle', 'ground_idle')"],
"quirks": ["q.bedrock_quirk('weedle', 'blink')"],
"transformedParts": [
{
"part": "body",
"position": [-3.8, 0, -3]
}
]
}
}
}
@@ -7,8 +7,9 @@
"poses": {
"standing": {
"poseTypes": ["STAND", "NONE", "FLOAT", "PORTRAIT", "PROFILE"],
"condition": "q.has_entity ? !q.in_air : 1.0",
"animations": [
"q.look('head_ai')",
"q.look('head')",
"q.bedrock('mime_jr', 'ground_idle')"
],
"quirks": [
@@ -19,7 +20,7 @@
"poseTypes": ["WALK", "SWIM"],
"condition": "!q.is_sprinting",
"animations": [
"q.look('head_ai')",
"q.look('head')",
"q.bedrock('mime_jr', 'ground_walk')"
],
"quirks": ["q.bedrock_quirk('mime_jr', 'blink')"]
@@ -28,7 +29,7 @@
"poseTypes": ["WALK", "SWIM"],
"condition": "q.is_sprinting",
"animations": [
"q.look('head_ai')",
"q.look('head')",
"q.bedrock('mime_jr', 'ground_run')"
],
"quirks": ["q.bedrock_quirk('mime_jr', 'blink')"]
@@ -36,21 +37,33 @@
"shoulder_left": {
"poseTypes": ["SHOULDER_LEFT"],
"animations": [
"q.look('head_ai')",
"q.look('head')",
"q.bedrock('mime_jr', 'shoulder_left')"
],
"quirks": [
"q.bedrock_quirk('mime_jr', 'blink')"
],
"transformedParts": [
{
"part": "body",
"position": [0, -0.25, 0]
}
]
},
"shoulder_right": {
"poseTypes": ["SHOULDER_RIGHT"],
"animations": [
"q.look('head_ai')",
"q.look('head')",
"q.bedrock('mime_jr', 'shoulder_right')"
],
"quirks": [
"q.bedrock_quirk('mime_jr', 'blink')"
],
"transformedParts": [
{
"part": "body",
"position": [0, -0.25, 0]
}
]
}
}
@@ -0,0 +1,42 @@
{
"portraitScale": 1.13,
"portraitTranslation": [-0.56, 1.64, 0],
"profileScale": 0.51,
"profileTranslation": [0, 1.05, 0],
"rootBone": "jellicent",
"animations": {
"recoil": "q.bedrock_stateful('jellicent', 'recoil')"
},
"poses": {
"standing": {
"poseTypes": ["STAND", "NONE", "FLOAT", "PORTRAIT", "PROFILE"],
"animations": [
"q.look('head')",
"q.bedrock('jellicent', 'water_idle')"
],
"quirks": ["q.bedrock_quirk('jellicent', 'blink')"]
},
"walking": {
"poseTypes": ["WALK"],
"animations": [
"q.look('head')",
"q.bedrock('jellicent', 'water_swim')"
],
"quirks": ["q.bedrock_quirk('jellicent', 'blink')"]
},
"flying": {
"poseTypes": ["FLY", "SWIM"],
"animations": [
"q.pitch_tilt('body')",
"q.look('head')",
"q.bedrock('jellicent', 'water_swim')"
],
"quirks": ["q.bedrock_quirk('jellicent', 'blink')"]
},
"sleep": {
"poseTypes": ["SLEEP"],
"animations": ["q.bedrock('jellicent', 'sleep')"]
}
}
}
@@ -55,13 +55,7 @@
"q.look('head_ai')",
"q.bedrock('froakie', 'water_idle')"
],
"quirks": [],
"transformedParts": [
{
"part": "body",
"position": [0, -5, 0]
}
]
"quirks": []
},
"sleep": {
"poseTypes": ["SLEEP"],
@@ -10,8 +10,26 @@
"recoil": "q.bedrock_stateful('greninja', 'recoil')",
"physical": "q.bedrock_primary('greninja', 'physical', q.curve('symmetrical'))",
"lick": "q.bedrock_primary('greninja', 'physical_tongue', q.curve('symmetrical'))",
"gunkshot": "q.bedrock_primary('greninja', 'physical_tongue', q.curve('symmetrical'))",
"bounce": "q.bedrock_primary('greninja', 'physical_tongue', q.curve('symmetrical'))",
"thief": "q.bedrock_primary('greninja', 'physical_tongue', q.curve('symmetrical'))",
"pound": "q.bedrock_primary('greninja', 'physical_tongue', q.curve('symmetrical'))",
"punch": "q.bedrock_primary('greninja', 'physical_tongue', q.curve('symmetrical'))",
"kick": "q.bedrock_primary('greninja', 'physical_kick', q.curve('symmetrical'))",
"lowkick": "q.bedrock_primary('greninja', 'physical_kick', q.curve('symmetrical'))",
"lowsweep": "q.bedrock_primary('greninja', 'physical_kick', q.curve('symmetrical'))",
"acrobatics": "q.bedrock_primary('greninja', 'physical_kick', q.curve('symmetrical'))",
"takedown": "q.bedrock_primary('greninja', 'physical_kick', q.curve('symmetrical'))",
"gigaimpact": "q.bedrock_primary('greninja', 'physical_kick', q.curve('symmetrical'))",
"liquidation": "q.bedrock_primary('greninja', 'physical_kick', q.curve('symmetrical'))",
"cut": "q.bedrock_primary('greninja', 'physical_katana', q.curve('symmetrical'))",
"aerialace": "q.bedrock_primary('greninja', 'physical_katana', q.curve('symmetrical'))",
"nightslash": "q.bedrock_primary('greninja', 'physical_katana', q.curve('symmetrical'))",
"falseswipe": "q.bedrock_primary('greninja', 'physical_katana', q.curve('symmetrical'))",
"watershuriken_2hit": "q.bedrock_primary('greninja', 'watershuriken_2hit', q.curve('symmetrical'))",
"watershuriken_3hit": "q.bedrock_primary('greninja', 'watershuriken_3hit', q.curve('symmetrical'))",
"watershuriken_4hit": "q.bedrock_primary('greninja', 'watershuriken_4hit', q.curve('symmetrical'))",
"watershuriken_5hit": "q.bedrock_primary('greninja', 'watershuriken_5hit', q.curve('symmetrical'))",
"special": "q.bedrock_primary('greninja', 'special', q.curve('symmetrical'))",
"status": "q.bedrock_primary('greninja', 'status', q.curve('symmetrical'))"
},
@@ -21,7 +39,7 @@
"isBattle": true,
"isTouchingWater": false,
"animations": [
"q.look('head_ai')",
"q.look('head_ai', -1, 1, 0, 0, 45, -45)",
"q.look('tongue1', -1, 1, 70, -45, 0, 0)",
"q.bedrock('greninja', 'battle_idle')"
],
@@ -30,14 +48,22 @@
},
"quirks": ["q.bedrock_quirk('greninja', 'blink')","q.bedrock_quirk('greninja','battle_quirk', 20, 40, 1)"]
},
"profile": {
"poseTypes": ["PORTRAIT", "PROFILE"],
"animations": [
"q.look('head_ai')",
"q.look('tongue1', -1, 1, 70, -45, 0, 0)",
"q.bedrock('greninja', 'ground_idle')"
]
},
"standing": {
"poseTypes": ["STAND", "NONE", "PORTRAIT", "PROFILE"],
"poseTypes": ["STAND", "NONE"],
"isBattle": false,
"isTouchingWater": false,
"animations": [
"q.look('head_ai')",
"q.look('tongue1', -1, 1, 70, -45, 0, 0)",
"q.bedrock('greninja', 'ground_idle')"
"q.bedrock('greninja', math.random(0, 1) < 0.4 ? 'ground_idle_alt' : 'ground_idle')"
],
"quirks": [
"q.bedrock_quirk('greninja', q.array('quirk_look_left', 'quirk_look_right'), 8, 30, 1)"
@@ -75,7 +101,13 @@
"q.look('tongue1', -1, 1, 70, -45, 0, 0)",
"q.bedrock('greninja', 'water_swim')"
],
"quirks": []
"quirks": [],
"transformedParts": [
{
"part": "body",
"position": [0, 18, 0]
}
]
},
"surface-float": {
"poseTypes": ["STAND","FLOAT"],
@@ -97,6 +129,7 @@
"swimming": {
"poseTypes": ["WALK","SWIM"],
"isTouchingWater": true,
"isUnderWater": true,
"animations": [
"q.pitch_tilt('body')",
"q.look('head_ai')",
@@ -107,19 +140,26 @@
"transformedParts": [
{
"part": "body",
"position": [0, 24, 0]
"position": [0, 5, 0]
}
]
},
"float": {
"poseTypes": ["STAND","FLOAT"],
"isTouchingWater": true,
"isUnderWater": true,
"animations": [
"q.look('head_ai')",
"q.look('tongue1', -1, 1, 70, -45, 0, 0)",
"q.bedrock('greninja', 'water_idle')"
],
"quirks": []
"quirks": [],
"transformedParts": [
{
"part": "body",
"position": [0, 5, 0]
}
]
},
"sleep": {
"poseTypes": ["SLEEP"],
@@ -0,0 +1,28 @@
{
"portraitScale": 1.0,
"portraitTranslation": [-0.55, 1.65, 0],
"profileScale": 0.3,
"profileTranslation": [0, 1.2, 0],
"rootBone": "crabominable",
"animations": {
"cry": "q.bedrock_stateful('crabominable', 'cry')"
},
"poses": {
"standing": {
"poseTypes": ["STAND", "NONE", "FLOAT", "PORTRAIT", "PROFILE"],
"animations": [
"q.bedrock('crabominable', 'ground_idle')"
],
"quirks": ["q.bedrock_quirk('crabominable', 'blink')"]
},
"walking": {
"poseTypes": ["WALK", "SWIM"],
"animations": [
"q.bedrock('crabominable', 'ground_idle')",
"q.bedrock('crabominable', 'ground_walk')"
],
"quirks": ["q.bedrock_quirk('crabominable', 'blink')"]
}
}
}
@@ -1299,6 +1299,7 @@
"cobblemon.ui.pc.filter.tooltip": "Format: pikachu shiny held_item lvl=1...",
"cobblemon.ui.pc.options": "Options",
"cobblemon.ui.pc.release": "Release",
"cobblemon.ui.pc.release_all": "Release All",
"cobblemon.ui.sort.name": "Sort by name",
"cobblemon.ui.sort.level": "Sort by level",
"cobblemon.ui.sort.pokedex_number": "Sort by Pokédex number",
@@ -1477,22 +1478,28 @@
"cobblemon.ui.pokedex.evolution.trade.tooltip.with_species": "Evolves when traded with %1$s",
"cobblemon.ui.pokedex.evolution.trade.tooltip.with_any_species": "Evolves when traded",
"cobblemon.ui.pokedex.evolution.shedder.tooltip": "Also requires a spare Poké Ball and an empty party slot",
"cobblemon.ui.pokedex.evolution.or": "OR",
"cobblemon.ui.pokedex.evolution.one_of": "One of:",
"cobblemon.ui.pokedex.requirement.advancement.tooltip": "Player must have %1$s advancement",
"cobblemon.ui.pokedex.requirement.battle_critical_hits.tooltip": "Must hit %1$s critical-hits in battle",
"cobblemon.ui.pokedex.requirement.biome.anticondition.tooltip": "Must not be in the %1$s biome to evolve",
"cobblemon.ui.pokedex.requirement.biome.both.tooltip": "Must be in the %1$s biome and not in the %2$s biome to evolve",
"cobblemon.ui.pokedex.requirement.biome.tooltip": "Must be in the %1$s biome to evolve",
"cobblemon.ui.pokedex.requirement.biome.anticondition.tooltip": "Must not be in the %1$s biome",
"cobblemon.ui.pokedex.requirement.biome.both.tooltip": "Must be in the %1$s biome and not in the %2$s biome",
"cobblemon.ui.pokedex.requirement.biome.tooltip": "Must be in the %1$s biome",
"cobblemon.ui.pokedex.requirement.blocks_traveled.tooltip": "Must travel %1$s blocks",
"cobblemon.ui.pokedex.requirement.damage_taken.tooltip": "Must take %1$s damage in battle without fainting",
"cobblemon.ui.pokedex.requirement.defeat.tooltip": "Must defeat %1$s in battle to evolve",
"cobblemon.ui.pokedex.requirement.friendship.tooltip": "Requires at least %1$s friendship",
"cobblemon.ui.pokedex.requirement.has_move.tooltip": "Must know move %1$s",
"cobblemon.ui.pokedex.requirement.has_move_type.tooltip": "Must know a move of type %1$s",
"cobblemon.ui.pokedex.requirement.has_move_type.tooltip": "Must know a move of %1$s type",
"cobblemon.ui.pokedex.requirement.held_item.tooltip": "Must be holding %1$s",
"cobblemon.ui.pokedex.requirement.level.tooltip.min": "Evolves starting at level %1$s",
"cobblemon.ui.pokedex.requirement.level.tooltip.between": "Evolves between levels %1$s and %2$s",
"cobblemon.ui.pokedex.requirement.moon_phase.tooltip": "Must evolve in %1$s moon phase",
"cobblemon.ui.pokedex.requirement.nature.toxtricity_amped": "Must have a §aHardy§r, §aBrave§r, §aAdamant§r, §aNaughty§r, §aDocile§r, §aImpish§r, §aLax§r, §aHasty§r, §aJolly§r, §aNaive§r, §aRash§r, §aSassy§r, or §aQuirky§r nature",
"cobblemon.ui.pokedex.requirement.nature.toxtricity_low_key": "Must have a §aLonely§r, §aBold§r, §aRelaxed§r, §aTimid§r, §aSerious§r, §aModest§r, §aMild§r, §aQuiet§r, §aBashful§r, §aCalm§r, §aGentle§r, or §aCareful§r nature",
"cobblemon.ui.pokedex.requirement.negate.tooltip": "Not: %1$s",
"cobblemon.ui.pokedex.requirement.nickname.malamar": "Must be named §aDinnerbone§r or §aGrumm§r",
"cobblemon.ui.pokedex.requirement.party_member.anticondition.tooltip": "Party must not have a Pokémon with %1$s",
"cobblemon.ui.pokedex.requirement.party_member.tooltip": "Party must have a Pokémon with %1$s",
"cobblemon.ui.pokedex.requirement.pokemon_properties.tooltip": "Must be %1$s",
@@ -1507,9 +1514,9 @@
"cobblemon.ui.pokedex.requirement.recoil.tooltip": "Must suffer %1$s units of recoil in battle",
"cobblemon.ui.pokedex.requirement.stat_compare.tooltip": "Must have its %1$s higher than its %2$s",
"cobblemon.ui.pokedex.requirement.stat_equal.tooltip": "Must have its %1$s equals to its %2$s",
"cobblemon.ui.pokedex.requirement.structure.anticondition.tooltip": "Must not be in the %1$s structure to evolve",
"cobblemon.ui.pokedex.requirement.structure.both.tooltip": "Must be in the %1$s structure and not in the %2$s structure to evolve",
"cobblemon.ui.pokedex.requirement.structure.tooltip": "Must be in the %1$s structure to evolve",
"cobblemon.ui.pokedex.requirement.structure.anticondition.tooltip": "Must not be in the %1$s structure",
"cobblemon.ui.pokedex.requirement.structure.both.tooltip": "Must be in the %1$s structure and not in the %2$s structure",
"cobblemon.ui.pokedex.requirement.structure.tooltip": "Must be in the %1$s structure",
"cobblemon.ui.pokedex.requirement.time_range.tooltip": "Must evolve during the %1$s",
"cobblemon.ui.pokedex.requirement.use_move.tooltip": "Must use move %1$s in battle",
"cobblemon.ui.pokedex.requirement.weather.tooltip.raining": "Must be in rain to evolve",
@@ -3663,6 +3670,31 @@
"tag.item.cobblemon.tall_flowers": "Tall Flowers",
"tag.item.cobblemon.small_flowers": "Small Flowers",
"tag.worldgen.biome.cobblemon.evolution.regional.bergmite_hisuibiome": "Hisuian",
"tag.worldgen.biome.cobblemon.evolution.regional.bergmite_kalosbiome": "Kalosian",
"tag.worldgen.biome.cobblemon.evolution.regional.cubone_alolabiome": "Alolan",
"tag.worldgen.biome.cobblemon.evolution.regional.cubone_kantobiome": "Kantonian",
"tag.worldgen.biome.cobblemon.evolution.regional.dartrix_alolabiome": "Alolan",
"tag.worldgen.biome.cobblemon.evolution.regional.dartrix_hisuibiome": "Hisuian",
"tag.worldgen.biome.cobblemon.evolution.regional.dewott_hisuibiome": "Hisuian",
"tag.worldgen.biome.cobblemon.evolution.regional.dewott_unovabiome": "Unovan",
"tag.worldgen.biome.cobblemon.evolution.regional.exeggcute_alolabiome": "Alolan",
"tag.worldgen.biome.cobblemon.evolution.regional.exeggcute_kantobiome": "Kantonian",
"tag.worldgen.biome.cobblemon.evolution.regional.goomy_hisuibiome": "Hisuian",
"tag.worldgen.biome.cobblemon.evolution.regional.goomy_kalosbiome": "Kalosian",
"tag.worldgen.biome.cobblemon.evolution.regional.koffing_galarbiome": "Galarian",
"tag.worldgen.biome.cobblemon.evolution.regional.koffing_kantobiome": "Kantonian",
"tag.worldgen.biome.cobblemon.evolution.regional.mimejr_galarbiome": "Galarian",
"tag.worldgen.biome.cobblemon.evolution.regional.mimejr_sinnohbiome": "Sinnohan",
"tag.worldgen.biome.cobblemon.evolution.regional.petilil_hisuibiome": "Hisuian",
"tag.worldgen.biome.cobblemon.evolution.regional.petilil_unovabiome": "Unovan",
"tag.worldgen.biome.cobblemon.evolution.regional.pikachu_alolabiome": "Alolan",
"tag.worldgen.biome.cobblemon.evolution.regional.pikachu_kantobiome": "Kantonian",
"tag.worldgen.biome.cobblemon.evolution.regional.quilava_hisuibiome": "Hisuian",
"tag.worldgen.biome.cobblemon.evolution.regional.quilava_johtobiome": "Johtonian",
"tag.worldgen.biome.cobblemon.evolution.regional.rufflet_hisuibiome": "Hisuian",
"tag.worldgen.biome.cobblemon.evolution.regional.rufflet_unovabiome": "Unovan",
"cobblemon.entity.variable.challenge_cooldown.name": "Challenge Cooldown",
"cobblemon.entity.variable.challenge_cooldown.desc": "How long the NPC will be un-challenge-able from that player's last challenge.",
"cobblemon.entity.variable.player_win_command.name": "Player Win Command",
@@ -6885,12 +6917,14 @@
"cobblemon.ui.pokedex.info.form.latios-mega": "Mega Latios",
"cobblemon.species.latios-mega.desc": "It flies at speeds exceeding Mach 4. It is among the top tier of all Pokémon in terms of aerial combat capability.",
"cobblemon.species.kyogre.name": "Kyogre",
"cobblemon.ui.pokedex.info.form.kyogre": "",
"cobblemon.species.kyogre.desc": "Kyogre is said to be the personification of the sea itself. Legends tell of its many clashes against Groudon, as each sought to gain the power of nature.",
"cobblemon.ui.pokedex.info.form.kyogre-primal": "Primal Kyogre",
"cobblemon.ui.pokedex.info.form.kyogre-primal": "Primal Reversion",
"cobblemon.species.kyogre-primal.desc": "Through Primal Reversion and with nature's full power, it will take back its true form. It can summon storms that cause the sea levels to rise.",
"cobblemon.species.groudon.name": "Groudon",
"cobblemon.ui.pokedex.info.form.groudon": "",
"cobblemon.species.groudon.desc": "Groudon is said to be the personification of the land itself. Legends tell of its many clashes against Kyogre, as each sought to gain the power of nature.",
"cobblemon.ui.pokedex.info.form.groudon-primal": "Primal Groudon",
"cobblemon.ui.pokedex.info.form.groudon-primal": "Primal Reversion",
"cobblemon.species.groudon-primal.desc": "Through Primal Reversion and with nature's full power, it will take back its true form. It can cause magma to erupt and expand the landmass of the world.",
"cobblemon.species.rayquaza.name": "Rayquaza",
"cobblemon.ui.pokedex.info.form.rayquaza": "",
@@ -7699,7 +7733,6 @@
"cobblemon.species.spewpa.name": "Spewpa",
"cobblemon.species.spewpa.desc": "The beaks of bird Pokémon can't begin to scratch its stalwart body. To defend itself, it spews powder.",
"cobblemon.species.vivillon.name": "Vivillon",
"cobblemon.ui.pokedex.info.form.vivillon": "",
"cobblemon.species.vivillon.desc": "The patterns on this Pokémon's wings depend on the climate and topography of its habitat. It scatters colorful scales.",
"cobblemon.ui.pokedex.info.form.vivillon-icy snow": "Icy Snow Pattern",
"cobblemon.species.vivillon-icy-snow.desc": "The patterns on this Pokémon depend on the climate and topography of the land it was born in. This form is from frigid lands.",
@@ -8156,11 +8189,8 @@
"cobblemon.ui.pokedex.info.form.silvally-fairy": "Type: Fairy",
"cobblemon.species.silvally-fairy.desc": "Thanks to its awakened powers, it can change to the Fairy type to match the memory it has equipped.",
"cobblemon.species.minior.name": "Minior",
"cobblemon.ui.pokedex.info.form.minior": "Core",
"cobblemon.species.minior.desc": "This is its form when its shell has fallen off. The color of its core depends on the materials that made up the food it ate.",
"cobblemon.ui.pokedex.info.form.minior-meteor": "Meteor Form",
"cobblemon.species.minior-meteor.desc": "This Pokémon is born in the ozone layer. It falls to the ground once the outer shell enclosing its core grows heavy.",
"cobblemon.ui.pokedex.info.form.minior-red": "Red Core",
"cobblemon.species.minior-red.desc": "This is its form when its shell has fallen off. The color of its core depends on the materials that made up the food it ate.",
"cobblemon.ui.pokedex.info.form.minior-orange": "Orange Core",
"cobblemon.species.minior-orange.desc": "This is its form when its shell has fallen off. The color of its core depends on the materials that made up the food it ate.",
"cobblemon.ui.pokedex.info.form.minior-yellow": "Yellow Core",
@@ -8173,6 +8203,8 @@
"cobblemon.species.minior-indigo.desc": "This is its form when its shell has fallen off. The color of its core depends on the materials that made up the food it ate.",
"cobblemon.ui.pokedex.info.form.minior-violet": "Violet Core",
"cobblemon.species.minior-violet.desc": "This is its form when its shell has fallen off. The color of its core depends on the materials that made up the food it ate.",
"cobblemon.ui.pokedex.info.form.minior-meteor": "Meteor Form",
"cobblemon.species.minior-meteor.desc": "This Pokémon is born in the ozone layer. It falls to the ground once the outer shell enclosing its core grows heavy.",
"cobblemon.species.komala.name": "Komala",
"cobblemon.species.komala.desc": "It remains asleep from birth to death as a result of the sedative properties of the leaves that form its diet.",
"cobblemon.species.turtonator.name": "Turtonator",
@@ -5094,9 +5094,9 @@
"cobblemon.species.trevenant.name": "Desséliande",
"cobblemon.species.trevenant.desc": "Ses racines sont un véritable système nerveux qui lui permet de contrôler les arbres de la forêt. Il se montre très gentil envers les Pokémon vivant dans son écorce.",
"cobblemon.species.pumpkaboo.name": "Pitrouille",
"cobblemon.species.pumpkaboo.desc": "Il paraît qu'il accompagne les âmes des défunts jusque dans l'au-delà, pour qu'elles puissent goûter au repos éternel.",
"cobblemon.species.pumpkaboo.desc": "Il a été confirmé récemment que chaque variété se distingue non seulement par sa taille, mais également par celle des âmes qu'elle enlève.",
"cobblemon.species.gourgeist.name": "Banshitrouye",
"cobblemon.species.gourgeist.desc": "Il enserre ses proies dans ses bras, semblables à de longues mèches de cheveux. Il se délecte de leur souffrance en chantonnant gaiement.",
"cobblemon.species.gourgeist.desc": "Les nuits de nouvelle lune, ce Pokémon erre dans les rues en chantant d'une voix lugubre. Ceux qui l'entendent sont maudits.",
"cobblemon.species.bergmite.name": "Grelaçon",
"cobblemon.species.bergmite.desc": "Il gèle ses ennemis d'une bourrasque à -100 °C. Il vit en troupeau avec ses congénères, dans les neiges éternelles des hautes montagnes.",
"cobblemon.species.avalugg.name": "Séracrawl",
@@ -5220,7 +5220,7 @@
"cobblemon.species.typenull.name": "Type:0",
"cobblemon.species.typenull.desc": "On lui fait porter un masque qui limite sa puissance afin d'éviter qu'il n'en perde le contrôle et sème le chaos.",
"cobblemon.species.silvally.name": "Silvallié",
"cobblemon.species.silvally.desc": "Il a réussi à briser son masque grâce à la force mentale qu'il a acquise en établissant un lien puissant avec son Dresseur.",
"cobblemon.species.silvally.desc": "Un lien fort avec son Dresseur lui a permis de libérer tout son potentiel.",
"cobblemon.species.minior.name": "Météno",
"cobblemon.species.minior.desc": "Météno se présente sous cette forme quand il perd sa carapace. La couleur de son noyau varie en fonction des composants de sa nourriture.",
"cobblemon.species.komala.name": "Dodoala",
@@ -5452,9 +5452,9 @@
"cobblemon.species.zacian.name": "Zacian",
"cobblemon.species.zacian.desc": "Également surnommé « héros légendaire », ce Pokémon absorbe du métal et se change en arme pour se battre.",
"cobblemon.species.zamazenta.name": "Zamazenta",
"cobblemon.species.zamazenta.desc": "Ce Pokémon sauva autrefois Galar en joignant ses forces à celles du roi des Hommes. Il absorbe du métal quand il passe au combat.",
"cobblemon.species.zamazenta.desc": "Ce Pokémon, changé en statue, est resté pétrifié si longtemps qu'il a failli tomber dans l'oubli.",
"cobblemon.species.eternatus.name": "Éthernatos",
"cobblemon.species.eternatus.desc": "Il devient actif en absorbant l'énergie qui émerge du sol de Galar grâce au noyau situé sur sa poitrine.",
"cobblemon.species.eternatus.desc": "Il se trouvait dans une météorite qui s'est écrasée il y a 20 000 ans. Il semble y avoir un lien entre ce Pokémon et le phénomène Dynamax.",
"cobblemon.species.kubfu.name": "Wushours",
"cobblemon.species.kubfu.desc": "Il suit un entraînement rigoureux pour parfaire ses techniques. La forme de son évolution dépend des capacités qu'il a assimilées.",
"cobblemon.species.urshifu.name": "Shifours",
@@ -5697,7 +5697,7 @@
"item.cobblemon.strawberry_sweet.tooltip": "Évolue Crèmy en Charmilly avec une Fraise en Sucre",
"item.cobblemon.clover_sweet.tooltip": "Évolue Crèmy en Charmilly avec un Trèfle en Sucre",
"item.cobblemon.galarica_cuff.tooltip": "Évolue Ramoloss de Galar en Flagadoss de Galar",
"item.cobblemon.link_cable.tooltip": "Évolue les Pokémon nécessitant un échange",
"item.cobblemon.link_cable.tooltip": "Permet d'évoluer un Pokémon nécessitant un échange",
"item.cobblemon.reaper_cloth.tooltip": "Évolue Téraclope en Noctunoir lorsque tenu lors d'un échange",
"item.cobblemon.star_sweet.tooltip": "Évolue Crèmy en Charmilly avec une Étoile en Sucre",
"cobblemon.config.ui.experience_share_multiplier.tooltip": "Multiplicateur de la part d'Expérience totale accordée à un Pokémon détenant un Multi Exp.",
@@ -6090,7 +6090,7 @@
"cobblemon.species.arceus-water.desc": "Dans la mythologie, ce Pokémon existait déjà avant la formation de l'univers.",
"cobblemon.species.arceus-psychic.desc": "Dans la mythologie, ce Pokémon existait déjà avant la formation de l'univers.",
"cobblemon.species.vivillon-garden.desc": "Ses motifs varient selon la topographie et le climat de l'endroit où il naît. Cette forme est originaire de terres verdoyantes.",
"cobblemon.species.genesect-fire.desc": "En cours de recherche.",
"cobblemon.species.genesect-fire.desc": "Ce Pokémon prend cette forme quand on l'équipe d'un module chargé en énergie de type Feu. Il dispose de griffes d'acier tranchantes.",
"cobblemon.species.vivillon-tundra.desc": "Ses motifs varient selon la topographie et le climat de l'endroit où il naît. Cette forme est originaire de terres glaciales.",
"cobblemon.species.vivillon-monsoon.desc": "Ses motifs varient selon la topographie et le climat de l'endroit où il naît. Cette forme est originaire de terres où s'abattent des pluies diluviennes.",
"cobblemon.species.vivillon-modern.desc": "Ses motifs varient selon la topographie et le climat de l'endroit où il naît. Cette forme est originaire de terres très ensoleillées.",
@@ -6140,8 +6140,8 @@
"cobblemon.species.slowpoke-galar.desc": "Bien qu'il ait toujours l'air un peu absent, il arrive que son visage se crispe brusquement. Son alimentation serait à l'origine du phénomène.",
"cobblemon.species.slowbro-mega.desc": "Il est complètement enveloppé dans le Kokiyas, qui forme un cocon impénétrable, mais somme toute plutôt confortable.",
"cobblemon.species.slowbro-galar.desc": "Le stimulus de la morsure du Kokiyas a déclenché une réaction chimique des épices retenues dans le corps de Flagadoss, lui donnant le type Poison.",
"cobblemon.species.farfetchd-galar.desc": "Les Canarticho ont pris cette forme à force de manier les poireaux de Galar, qui sont plus longs et plus larges que ceux des autres régions.",
"cobblemon.species.grimer-alola.desc": "Ce glouton, capable de dévorer tout et n'importe quoi, a été introduit à Alola pour résoudre les problèmes de déchets.",
"cobblemon.species.farfetchd-galar.desc": "À force de soulever son poireau lourd et massif, il a musclé son corps. Il compte sur la puissance destructrice d'une seule frappe pour gagner.",
"cobblemon.species.grimer-alola.desc": "Les cristaux sur son corps sont des agrégats de toxines. S'ils se détachent, des poisons mortels commencent à se répandre.",
"cobblemon.species.muk-alola.desc": "Ses crocs et ses griffes sont des toxines cristallisées. Il gangrène tout ce qu'il touche, alors n'approchez pas trop près.",
"cobblemon.species.gengar-mega.desc": "Grâce à la Méga-Évolution, il a accès à une autre dimension. Son corps déborde de puissance occulte.",
"cobblemon.species.gengar-gmax.desc": "Son corps est rempli d'énergie négative. Sa bouche gigantesque est un portail qui débouche sur l'autre monde.",
@@ -6190,7 +6190,7 @@
"cobblemon.species.unown-w.desc": "Ce Pokémon a la forme d'un caractère d'écriture antique. Personne ne sait si ces écritures antiques sont apparues avant les Zarbi ou le contraire. Des études sont en cours, mais aucun résultat n'a été annoncé.",
"cobblemon.species.unown-y.desc": "Ce Pokémon a la forme d'un caractère d'écriture antique. Personne ne sait si ces écritures antiques sont apparues avant les Zarbi ou le contraire. Des études sont en cours, mais aucun résultat n'a été annoncé.",
"cobblemon.species.unown-z.desc": "Ce Pokémon a la forme d'un caractère d'écriture antique. Personne ne sait si ces écritures antiques sont apparues avant les Zarbi ou le contraire. Des études sont en cours, mais aucun résultat n'a été annoncé.",
"cobblemon.species.steelix-mega.desc": "En cours de recherche.",
"cobblemon.species.steelix-mega.desc": "Il contrôle par magnétisme les fragments qui se sont détachés de la surface de son corps et s'en sert pour parer les attaques.",
"cobblemon.species.qwilfish-hisui.desc": "Il vit dans l'eau près des côtes froides. Ses épines empoisonnées sont plus épaisses et plus courtes que celle des Qwilfish des autres régions.",
"cobblemon.species.scizor-mega.desc": "Ses pinces lui servent davantage à frapper qu'à couper. Elles sont si lourdes qu'il lui est difficile de tenir quand le combat s'éternise.",
"cobblemon.species.heracross-mega.desc": "Le flot d'énergie décuple ses muscles, mais lui donne de terribles courbatures après avoir retrouvé sa forme d'origine.",
@@ -6198,20 +6198,20 @@
"cobblemon.species.corsola-galar.desc": "Il s'agit d'un Corayon mort il y a longtemps à cause d'un changement brutal de l'environnement. Il vole l'essence vitale des gens avec ses branches.",
"cobblemon.species.houndoom-mega.desc": "Son corps tout entier devient fiévreux lors de la Méga-Évolution. Il réduit ses adversaires en cendres avec son souffle enflammé.",
"cobblemon.species.tyranitar-mega.desc": "Il est encore plus violent en méga-évoluant, à tel point que l'on se demande s'il entend toujours les ordres de son Dresseur.",
"cobblemon.species.sceptile-mega.desc": "En cours de recherche.",
"cobblemon.species.blaziken-mega.desc": "En cours de recherche.",
"cobblemon.species.swampert-mega.desc": "En cours de recherche.",
"cobblemon.species.sceptile-mega.desc": "Pour projeter le bout de sa queue, il fait exploser les graines situées sur son dos, qui sont chargées de l'énergie de la Méga-Évolution.",
"cobblemon.species.blaziken-mega.desc": "Grâce au sang bouillant qui coule dans ses veines, ses techniques sont deux fois plus précises. Il soumet ses ennemis en un clin d'œil.",
"cobblemon.species.swampert-mega.desc": "En utilisant le même principe qu'une pompe hydraulique, l'eau dans son corps renforce encore plus ses muscles déjà considérablement gonflés.",
"cobblemon.species.zigzagoon-galar.desc": "Il court partout sans s'arrêter. Quand il rencontre un autre Pokémon, il fait exprès de lui rentrer dedans pour le provoquer.",
"cobblemon.species.linoone-galar.desc": "Il provoque ses adversaires avec sa longue langue pour les mettre en colère avant de les plaquer au sol avec un puissant tacle.",
"cobblemon.species.gardevoir-mega.desc": "En cours de recherche.",
"cobblemon.species.gardevoir-mega.desc": "Lorsqu'il déploie les plaques rouges sur sa poitrine et libère son esprit, il est capable de déchaîner ses plus grands pouvoirs psychiques.",
"cobblemon.species.sableye-mega.desc": "Son joyau est devenu immense et très lourd. Il lui permet de se protéger contre toute attaque, mais ralentit beaucoup ses mouvements.",
"cobblemon.species.mawile-mega.desc": "Ses deux gueules bougent violemment, comme mues par leur propre volonté. Elles peuvent pulvériser un rocher d'un seul claquement.",
"cobblemon.species.aggron-mega.desc": "En cours de recherche.",
"cobblemon.species.medicham-mega.desc": "En cours de recherche.",
"cobblemon.species.aggron-mega.desc": "L'énergie de la Méga-Évolution a transformé le fer dans l'organisme de ce Pokémon en une armure d'acier qui couvre tout son corps.",
"cobblemon.species.medicham-mega.desc": "Grâce à la Méga-Évolution, sa force spirituelle n'a plus de limites. Il matérialise des bras maléfiques par la pensée pour terrasser ses adversaires.",
"cobblemon.species.manectric-mega.desc": "L'électricité accumulée dans son corps lui permet de se déplacer à la vitesse de l'éclair, mais elle le rend aussi colérique.",
"cobblemon.species.sharpedo-mega.desc": "Quand il charge, des pics acérés surgissent sur sa tête ; il s'en sert pour blesser gravement ses adversaires.",
"cobblemon.species.camerupt-mega.desc": "En cours de recherche.",
"cobblemon.species.altaria-mega.desc": "En cours de recherche.",
"cobblemon.species.camerupt-mega.desc": "Quand sa colère atteint un point critique, l'immense volcan sur son dos entre en violente éruption et projette de la lave.",
"cobblemon.species.altaria-mega.desc": "Ses douces plumes si fragiles en apparence sont tellement robustes qu'une poignée suffit pour hisser un camion-benne.",
"cobblemon.species.castform-rainy.desc": "Morphéo adopte cette apparence uniquement les jours de pluie. On a tenté de reproduire ce phénomène sous une douche, sans succès.",
"cobblemon.species.castform-sunny.desc": "Morphéo adopte cette apparence uniquement les jours ensoleillés. On a tenté de reproduire ce phénomène avec un radiateur, sans succès.",
"cobblemon.species.castform-snowy.desc": "Morphéo adopte cette apparence uniquement les jours enneigés. On a tenté de reproduire ce phénomène avec un congélateur, sans succès.",
@@ -6220,14 +6220,14 @@
"cobblemon.species.absol-mega.desc": "L'énergie de la Méga-Évolution a fait se hérisser tous les poils de son corps. Comme il ne s'agit pas d'ailes, il ne peut pas voler.",
"cobblemon.species.salamence-mega.desc": "Il se met en position de vol en rangeant ses pattes avant dans sa carapace, et fonce en rase-mottes au-dessus des terrains accidentés.",
"cobblemon.species.metagross-mega.desc": "Quand il voit qu'il ne peut pas gagner, il attrape son adversaire avec ses griffes et lance le compte à rebours avant explosion.",
"cobblemon.species.latias-mega.desc": "En cours de recherche.",
"cobblemon.species.latios-mega.desc": "En cours de recherche.",
"cobblemon.species.latias-mega.desc": "Ses prouesses en vol et sa puissance de frappe se sont renforcées. Il disperse ses plumes transparentes pour perturber ses adversaires.",
"cobblemon.species.latios-mega.desc": "Il vole à une vitesse dépassant Mach 4. Il compte parmi les Pokémon les plus doués en combat aérien.",
"cobblemon.species.kyogre-primal.desc": "Grâce à l'énergie de la nature, il peut accomplir sa Primo-Résurgence pour retrouver son apparence originelle. Ce pouvoir lui permet d'appeler de terribles déluges pour étendre les mers.",
"cobblemon.species.groudon-primal.desc": "Grâce à l'énergie de la nature, il peut accomplir sa Primo-Résurgence pour retrouver son apparence originelle. Ce pouvoir lui permet de créer du magma pour étendre les continents.",
"cobblemon.species.rayquaza-mega.desc": "Un Pokémon qui vit dans la couche d'ozone et se nourrit des météorites qu'il intercepte. Leur énergie s'accumule dans son corps, ce qui lui permet de méga-évoluer.",
"cobblemon.species.deoxys-attack.desc": "En cours de recherche.",
"cobblemon.species.deoxys-defense.desc": "En cours de recherche.",
"cobblemon.species.deoxys-speed.desc": "En cours de recherche.",
"cobblemon.species.deoxys-attack.desc": "Deoxys a choisi lui-même d'avoir cette apparence agressive. Il trompe ses ennemis en changeant de forme à volonté.",
"cobblemon.species.deoxys-defense.desc": "Une aurore apparaît quand il change de forme. Il absorbe les attaques en altérant sa structure cellulaire.",
"cobblemon.species.deoxys-speed.desc": "Il est le résultat de la mutation d'un virus extraterrestre exposé à un rayon laser. Sa structure corporelle lui confère une agilité et une vitesse supérieures.",
"cobblemon.species.burmy-sandy.desc": "Il pétrit du sable et de la boue avec les fils de soie qu'il génère pour se confectionner une cape. Mais le vent et la pluie pouvant abimer celle-ci, Cheniti se cache dans les crevasses et les grottes.",
"cobblemon.species.burmy-trash.desc": "Lorsqu'il ne trouve pas de matière première adéquate pour confectionner sa cape, il utilise des détritus. Sa cape est d'ailleurs bien plus douillette qu'elle n'y parait.",
"cobblemon.species.wormadam-sandy.desc": "Son épiderme de sable et de boue très solide peut aisément repousser les coups de bec des Étourmi.",
@@ -6238,8 +6238,8 @@
"cobblemon.species.lopunny-mega.desc": "Ce Pokémon au tempérament violent se sert de ses oreilles comme s'il s'agissait de fouets pour corriger ses adversaires.",
"cobblemon.species.garchomp-mega.desc": "La Méga-Évolution l'a rendu plus brutal. Il taille en pièces ses ennemis à l'aide de ses bras en forme de faux.",
"cobblemon.species.lucario-mega.desc": "Son instinct guerrier s'est réveillé après son exposition à une explosion d'énergie. Il n'a absolument aucune pitié pour ses ennemis.",
"cobblemon.species.abomasnow-mega.desc": "En cours de recherche.",
"cobblemon.species.gallade-mega.desc": "En cours de recherche.",
"cobblemon.species.abomasnow-mega.desc": "Les bourgeons sur son dos ont donné naissance à d'imposantes épines de glace qui déclenchent des blizzards à −30 °C.",
"cobblemon.species.gallade-mega.desc": "Il tire sa force du lien qui l'unit à son Dresseur ou à sa Dresseuse. Son corps s'est doté de plaques tranchantes et d'une cape protectrice.",
"cobblemon.species.rotom-heat.desc": "Sous cette forme, Motisma adore utiliser la chaleur pour faire des bêtises, comme brûler les vêtements favoris des gens.",
"cobblemon.species.rotom-wash.desc": "Sous cette forme, Motisma adore utiliser de l'eau pour faire des bêtises, comme complètement inonder les pièces à vivre.",
"cobblemon.species.rotom-frost.desc": "Sous cette forme, Motisma adore utiliser l'air froid pour faire des bêtises, comme geler le bain chaud d'une personne avant qu'elle n'y mette le pied.",
@@ -6263,7 +6263,7 @@
"cobblemon.species.arceus-dragon.desc": "Dans la mythologie, ce Pokémon existait déjà avant la formation de l'univers.",
"cobblemon.species.arceus-dark.desc": "Dans la mythologie, ce Pokémon existait déjà avant la formation de l'univers.",
"cobblemon.species.samurott-hisui.desc": "Cette fine lame au tempérament impitoyable est une évolution rare qui ne se produit qu'à Hisui. Ses attaques sont aussi ravageuses que les vagues déferlantes.",
"cobblemon.species.audino-mega.desc": "En cours de recherche.",
"cobblemon.species.audino-mega.desc": "Deux antennes ont poussé au cou de ce Pokémon. Il suffit de les toucher pour se retrouver plongé dans un profond sommeil.",
"cobblemon.species.lilligant-hisui.desc": "Ce Pokémon doit ses jambes musclées à son habitat dans les montagnes couvertes de neige épaisse. La fleur sur sa couronne exhale un parfum stimulant tout être alentour.",
"cobblemon.species.basculin-bluestriped.desc": "Ils forment des bancs entre spécimens de même couleur. Leurs plus grands ennemis sont les Bargantua rouges.",
"cobblemon.species.basculin-white.desc": "Ces Pokémon vivent en bancs dans les océans glacés et se partagent la nourriture. Quand le temps est venu, ils retournent à leur fleuve natal.",
@@ -6289,10 +6289,10 @@
"cobblemon.species.kyurem-white.desc": "Il prédit un futur où humains et Pokémon vivront dans un monde où la Réalité prévaut. Il fait tout son possible pour protéger cet avenir.",
"cobblemon.species.kyurem-black.desc": "On dit qu'il se bat pour défendre un monde Idéal dans lequel Pokémon et humains cohabiteront.",
"cobblemon.species.keldeo-resolute.desc": "Le pouvoir qui sommeillait au plus profond de lui s'est concentré dans sa corne, qui est devenue une lame capable de fendre n'importe quoi.",
"cobblemon.species.meloetta-pirouette.desc": "En cours de recherche.",
"cobblemon.species.genesect-water.desc": "En cours de recherche.",
"cobblemon.species.genesect-electric.desc": "En cours de recherche.",
"cobblemon.species.genesect-ice.desc": "En cours de recherche.",
"cobblemon.species.meloetta-pirouette.desc": "Meloetta a retrouvé un chant qui fut longtemps oublié. Ses pas de danse envoûtants peuvent prendre n'importe qui au piège.",
"cobblemon.species.genesect-water.desc": "Ce Pokémon prend cette forme quand on l'équipe d'un module chargé en énergie de type Eau. Il peut se transformer pour voler.",
"cobblemon.species.genesect-electric.desc": "Ce Pokémon prend cette forme quand on l'équipe d'un module chargé en énergie de type Électrik. Il peut tirer des boules d'énergie.",
"cobblemon.species.genesect-ice.desc": "Ce Pokémon prend cette forme quand on l'équipe d'un module chargé en énergie de type Glace. Il n'éprouve alors plus aucune compassion.",
"cobblemon.species.greninja-bond.desc": "En cours de recherche.",
"cobblemon.species.greninja-ash.desc": "En cours de recherche.",
"cobblemon.species.vivillon-polar.desc": "Ses motifs varient selon la topographie et le climat de l'endroit où il naît. Cette forme est originaire de terres enneigées.",
@@ -6331,10 +6331,10 @@
"cobblemon.species.furfrou-pharaoh.desc": "Il fut un temps où la noblesse organisait des concours pour déterminer quel Couafarel arborait la plus belle coupe.",
"cobblemon.species.meowstic-f.desc": "Les femelles sont un peu plus agressives et égoïstes que les mâles. Quand elles sont fâchées, elles se vengent avec leurs pouvoirs psychiques.",
"cobblemon.species.aegislash-blade.desc": "Sous cette forme, il excelle en attaque. Il utilise le poids et la puissance de l'acier pour détruire ses adversaires.",
"cobblemon.species.silvally-ghost.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-fire.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-electric.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-dragon.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-ghost.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Spectre grâce à la ROM dont il est équipé.",
"cobblemon.species.silvally-fire.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Feu grâce à la ROM dont il est équipé.",
"cobblemon.species.silvally-electric.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Électrik grâce à la ROM dont il est équipé.",
"cobblemon.species.silvally-dragon.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Dragon grâce à la ROM dont il est équipé.",
"cobblemon.species.minior-yellow.desc": "Météno se présente sous cette forme quand il perd sa carapace. La couleur de son noyau varie en fonction des composants de sa nourriture.",
"cobblemon.species.minior-meteor.desc": "Ce Pokémon vient de la couche d'ozone. Il s'écrase parfois au sol, lorsque la carapace qui enveloppe son noyau devient trop lourde.",
"cobblemon.species.minior-indigo.desc": "Météno se présente sous cette forme quand il perd sa carapace. La couleur de son noyau varie en fonction des composants de sa nourriture.",
@@ -6370,21 +6370,21 @@
"cobblemon.species.klawf.desc": "Il se poste la tête en bas sur une falaise, dans l'attente d'une proie, mais il ne peut pas rester longtemps ainsi, car le sang lui monte à la tête.",
"cobblemon.species.wiglett.desc": "Il détecte l'odeur de Délestin à plus de vingt mètres, ce qui lui donne le temps de s'enfouir dans le sable.",
"cobblemon.species.flamigo.desc": "On pense qu'il fait un nœud à la base de son cou afin d'empêcher l'énergie qu'il accumule dans son ventre de s'échapper par son bec.",
"cobblemon.species.sliggoo-hisui.desc": "Ce Pokémon est de nature plutôt mélancolique. Le fer contenu dans les eaux de Hisui a agi sur son mucus épidermique et a changé sa peau en carapace de métal.",
"cobblemon.species.sliggoo-hisui.desc": "Sa coquille est devenue métallique en raison du fer contenu dans l'eau qu'il boit. Il n'en sort jamais lorsque le soleil brille.",
"cobblemon.species.goodra-hisui.desc": "Il peut moduler la rigidité de sa carapace de métal à volonté. Il abhorre la solitude et enrage si un être qu'il affectionne profondément l'abandonne.",
"cobblemon.species.pumpkaboo-small.desc": "Les Pitrouille Taille Mini aiment emporter l'âme des enfants vers l'au-delà.",
"cobblemon.species.pumpkaboo-large.desc": "Les Pitrouille Taille Maxi aiment emporter l'âme des adultes vers l'au-delà.",
"cobblemon.species.pumpkaboo-super.desc": "Les Pitrouille Taille Ultra adorent les esprits humains de très grande taille.",
"cobblemon.species.pumpkaboo-small.desc": "Cette variété emmène vers l'au-delà les âmes des enfants, dont la petite taille correspond à celle de son propre corps.",
"cobblemon.species.pumpkaboo-large.desc": "Cette variété emmène vers l'au-delà les âmes des adultes, dont la grande taille correspond à celle de son propre corps.",
"cobblemon.species.pumpkaboo-super.desc": "Cette variété emmène vers l'au-delà les âmes gigantesques, dont la taille correspond à celle de son propre corps.",
"cobblemon.species.gourgeist-small.desc": "Les Banshitrouye Taille Mini revêtent la forme d'enfants pour tromper les adultes et les emmener vers l'au-delà.",
"cobblemon.species.gourgeist-large.desc": "Les Banshitrouye Taille Maxi revêtent la forme d'adultes et prennent la main des enfants pour les emmener vers l'au-delà.",
"cobblemon.species.gourgeist-super.desc": "Les Banshitrouye Taille Ultra emmèneront de force n'importe qui vers l'au-delà, peu importe leur âge.",
"cobblemon.species.gourgeist-super.desc": "Les Banshitrouye gigantesques sont capables d'emmener n'importe qui de force vers l'au-delà.",
"cobblemon.species.avalugg-hisui.desc": "L'armure de glace qui enveloppe sa mâchoire inférieure peut briser des rocs plus durs que l'acier. Il se meut vélocement en fendant la neige épaisse des chemins de montagne.",
"cobblemon.species.xerneas-active.desc": "D'après la légende, quand ses bois brillent de sept couleurs, cela signifie qu'il prodigue la vie éternelle.",
"cobblemon.species.zygarde-10.desc": "La forme que Zygarde prend lorsqu'il rassemble 10% de ses Cellules éparpillées un peu partout. Il excelle au combat au corps-à-corps.",
"cobblemon.species.zygarde-10c.desc": "La forme que Zygarde prend lorsqu'il rassemble 10% de ses Cellules éparpillées un peu partout. Il excelle au combat au corps-à-corps.",
"cobblemon.species.zygarde-complete.desc": "La forme que Zygarde prend lorsqu'il rassemble 100% de ses Cellules. Sa puissance dépasse même celle de Xerneas ou d'Yveltal.",
"cobblemon.species.zygarde-50c.desc": "La forme que Zygarde prend lorsqu'il rassemble 50% de ses Cellules. Sa mission est de surveiller l'écosystème.",
"cobblemon.species.diancie-mega.desc": "En cours de recherche.",
"cobblemon.species.diancie-mega.desc": "On l'appelle aussi « la princesse rose de l'élégance ». Le diamant resplendissant qui orne son front pèse pas moins de 2 000 carats.",
"cobblemon.species.hoopa-unbound.desc": "Il possède une puissance considérable sous son apparence véritable. Une légende raconte qu'il désirait si fort un trésor caché dans un château qu'il déroba l'édifice tout entier.",
"cobblemon.species.decidueye-hisui.desc": "Les plumes d'Archéduc le protègent de la froideur glaciale de Hisui grâce à l'air logé dans leur tige. Voici une preuve que l'environnement agit sur l'évolution des Pokémon.",
"cobblemon.species.oricorio-pompom.desc": "Un Plumeline ayant bu du Nectar Jaune. Quand il aperçoit quelqu'un qui a une baisse de tension, il danse pour lui remonter le moral.",
@@ -6394,19 +6394,19 @@
"cobblemon.species.lycanroc-midnight.desc": "Cet habitué à la vie en solitaire n'obéit qu'aux Dresseurs qui savent exploiter tout le potentiel de sa force.",
"cobblemon.species.lycanroc-dusk.desc": "Ce Lougaroc a subi une évolution spéciale en étant irradié par les rayons du soleil couchant. Son calme cache une combativité hors norme.",
"cobblemon.species.wishiwashi-school.desc": "On l'appelle « le démon des mers ». Quand trop de Froussardine tombent sous les coups de l'ennemi, le banc reprend une activité normale.",
"cobblemon.species.silvally-fighting.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-flying.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-poison.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-ground.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-rock.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-bug.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-steel.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-water.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-grass.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-psychic.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-ice.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-dark.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-fairy.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-fighting.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Combat grâce à la ROM dont il est équipé.",
"cobblemon.species.silvally-flying.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Vol grâce à la ROM dont il est équipé.",
"cobblemon.species.silvally-poison.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Poison grâce à la ROM dont il est équipé.",
"cobblemon.species.silvally-ground.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Sol grâce à la ROM dont il est équipé.",
"cobblemon.species.silvally-rock.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Roche grâce à la ROM dont il est équipé.",
"cobblemon.species.silvally-bug.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Insecte grâce à la ROM dont il est équipé.",
"cobblemon.species.silvally-steel.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Acier grâce à la ROM dont il est équipé.",
"cobblemon.species.silvally-water.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Eau grâce à la ROM dont il est équipé.",
"cobblemon.species.silvally-grass.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Plante grâce à la ROM dont il est équipé.",
"cobblemon.species.silvally-psychic.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Psy grâce à la ROM dont il est équipé.",
"cobblemon.species.silvally-ice.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Glace grâce à la ROM dont il est équipé.",
"cobblemon.species.silvally-dark.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Ténèbres grâce à la ROM dont il est équipé.",
"cobblemon.species.silvally-fairy.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Fée grâce à la ROM dont il est équipé.",
"cobblemon.species.minior-orange.desc": "Météno se présente sous cette forme quand il perd sa carapace. La couleur de son noyau varie en fonction des composants de sa nourriture.",
"cobblemon.species.minior-green.desc": "Météno se présente sous cette forme quand il perd sa carapace. La couleur de son noyau varie en fonction des composants de sa nourriture.",
"cobblemon.species.minior-blue.desc": "Météno se présente sous cette forme quand il perd sa carapace. La couleur de son noyau varie en fonction des composants de sa nourriture.",
@@ -6807,13 +6807,13 @@
"cobblemon.mark.ribbon_contest_super_master_cleverness": "Ruban Maître Intelligence",
"cobblemon.mark.ribbon_contest_super_master_cleverness.title": "%1$s le Génie",
"cobblemon.mark.ribbon_contest_super_master_coolness": "Ruban Maître Sang-froid",
"cobblemon.mark.ribbon_contest_super_master_coolness.title": "%1$s l'Ancienne Star",
"cobblemon.mark.ribbon_contest_super_master_coolness.title": "%1$s la Star",
"cobblemon.mark.ribbon_contest_super_master_coolness.desc": "Ruban commémorant une incroyable démonstration de Sang-froid durant un concours.",
"cobblemon.mark.ribbon_contest_super_master_cuteness": "Ruban Maître Grâce",
"cobblemon.mark.ribbon_contest_super_master_cuteness.title": "%1$s la Grâce Incarnée",
"cobblemon.mark.ribbon_contest_super_master_cuteness.desc": "Ruban commémorant une incroyable démonstration de Grâce durant un concours.",
"cobblemon.mark.ribbon_contest_super_master_toughness": "Ruban Maître Robustesse",
"cobblemon.mark.ribbon_contest_super_master_toughness.title": "%1$s l'Ancien Gros Dur",
"cobblemon.mark.ribbon_contest_super_master_toughness.title": "%1$s le Gros Dur",
"cobblemon.mark.ribbon_contest_super_master_toughness.desc": "Ruban commémorant une incroyable démonstration de Robustesse durant un concours.",
"cobblemon.mark.ribbon_contest_super_star": "Ruban Star des Concours",
"cobblemon.mark.ribbon_contest_super_star.title": "%1$s la Star de Légende",
@@ -6830,7 +6830,7 @@
"cobblemon.mark.ribbon_event_special.desc": "Ruban spécial pour un jour très particulier.",
"cobblemon.mark.ribbon_event_mystery_zone_sky": "Ruban Ciel",
"cobblemon.mark.ribbon_event_color_blue.desc": "Ruban commémoratif.",
"cobblemon.mark.mark_titan.desc": "L'insigne d'un ancien Pokémon Dominant.",
"cobblemon.mark.mark_titan.desc": "L'insigne d'un Pokémon Dominant.",
"cobblemon.mark.mark_personality_kindly.desc": "L'insigne d'un Pokémon bienveillant.",
"cobblemon.mark.mark_personality_slump.desc": "L'insigne d'un Pokémon blessé.",
"cobblemon.mark.ribbon_master_rank.desc": "Ruban commémorant la victoire contre un Dresseur de niveau Master Ball en combat classé.",
@@ -6947,8 +6947,8 @@
"cobblemon.mark.ribbon_training": "Ruban Perfectionnement",
"cobblemon.mark.ribbon_training.title": "%1$s le Surentraîné",
"cobblemon.mark.mark_alpha": "Insigne Baron",
"cobblemon.mark.mark_alpha.title": "%1$s l'Ancien Baron",
"cobblemon.mark.mark_alpha.desc": "L'insigne d'un ancien Pokémon Baron.",
"cobblemon.mark.mark_alpha.title": "%1$s le Baron",
"cobblemon.mark.mark_alpha.desc": "L'insigne d'un Pokémon Baron.",
"cobblemon.mark.mark_curry": "Insigne Curry",
"cobblemon.mark.mark_curry.title": "%1$s le Gastronome",
"cobblemon.mark.mark_curry.desc": "L'insigne d'un Pokémon gastronome.",
@@ -6982,7 +6982,7 @@
"cobblemon.mark.mark_revival.title": "%1$s le Ressuscité",
"cobblemon.mark.mark_revival.desc": "L'insigne d'un Pokémon ressuscité.",
"cobblemon.mark.mark_titan": "Insigne Dominant",
"cobblemon.mark.mark_titan.title": "%1$s l'Ancien Dominant",
"cobblemon.mark.mark_titan.title": "%1$s le Dominant",
"cobblemon.mark.mark_uncommon": "Insigne Sociabilité",
"cobblemon.mark.mark_uncommon.title": "%1$s le Sociable",
"cobblemon.mark.mark_uncommon.desc": "L'insigne d'un Pokémon sociable.",
@@ -7339,9 +7339,9 @@
"cobblemon.ui.pokedex.info.form.pinsir": " ",
"cobblemon.ui.pokedex.info.form.pinsir-mega": "Méga-Scarabrute",
"cobblemon.ui.pokedex.info.form.tauros": " ",
"cobblemon.ui.pokedex.info.form.tauros-paldeacombat": "Race Combative (Paldea)",
"cobblemon.ui.pokedex.info.form.tauros-paldeablaze": "Race Flamboyante (Paldea)",
"cobblemon.ui.pokedex.info.form.tauros-paldeaaqua": "Race Aquatique (Paldea)",
"cobblemon.ui.pokedex.info.form.tauros-paldeacombat": "Forme de Paldea (Race Combative)",
"cobblemon.ui.pokedex.info.form.tauros-paldeablaze": "Forme de Paldea (Race Flamboyante)",
"cobblemon.ui.pokedex.info.form.tauros-paldeaaqua": "Forme de Paldea (Race Aquatique)",
"cobblemon.ui.pokedex.info.form.gyarados": " ",
"cobblemon.ui.pokedex.info.form.gyarados-mega": "Méga-Léviator",
"cobblemon.ui.pokedex.info.form.lapras": " ",
@@ -7530,9 +7530,9 @@
"cobblemon.ui.pokedex.info.form.darumaka": " ",
"cobblemon.ui.pokedex.info.form.darumaka-galar": "Forme de Galar",
"cobblemon.ui.pokedex.info.form.darmanitan-zen": "Mode Transe",
"cobblemon.ui.pokedex.info.form.darmanitan-galar": "Forme de Galar",
"cobblemon.ui.pokedex.info.form.darmanitan-galar": "Forme de Galar du Mode Normal",
"cobblemon.ui.pokedex.info.form.darmanitan": " ",
"cobblemon.ui.pokedex.info.form.darmanitan-galarzen": "Mode Transe",
"cobblemon.ui.pokedex.info.form.darmanitan-galarzen": "Forme de Galar du Mode Transe",
"cobblemon.ui.pokedex.info.form.yamask": " ",
"cobblemon.ui.pokedex.info.form.yamask-galar": "Forme de Galar",
"cobblemon.ui.pokedex.info.form.garbodor": " ",
@@ -7566,10 +7566,10 @@
"cobblemon.ui.pokedex.info.form.keldeo-resolute": "Aspect Décidé",
"cobblemon.ui.pokedex.info.form.meloetta": "Forme Chant",
"cobblemon.ui.pokedex.info.form.meloetta-pirouette": "Forme Danse",
"cobblemon.ui.pokedex.info.form.genesect-water": "Forme de Genesect",
"cobblemon.ui.pokedex.info.form.genesect-electric": "Forme de Genesect",
"cobblemon.ui.pokedex.info.form.genesect-fire": "Forme de Genesect",
"cobblemon.ui.pokedex.info.form.genesect-ice": "Forme de Genesect",
"cobblemon.ui.pokedex.info.form.genesect-water": "Module Aqua",
"cobblemon.ui.pokedex.info.form.genesect-electric": "Module Choc",
"cobblemon.ui.pokedex.info.form.genesect-fire": "Module Pyro",
"cobblemon.ui.pokedex.info.form.genesect-ice": "Module Cryo",
"cobblemon.ui.pokedex.info.form.greninja": " ",
"cobblemon.ui.pokedex.info.form.greninja-bond": " ",
"cobblemon.ui.pokedex.info.form.greninja-ash": "Forme Sacha",
@@ -7613,14 +7613,14 @@
"cobblemon.ui.pokedex.info.form.sliggoo-hisui": "Forme de Hisui",
"cobblemon.ui.pokedex.info.form.goodra": " ",
"cobblemon.ui.pokedex.info.form.goodra-hisui": "Forme de Hisui",
"cobblemon.ui.pokedex.info.form.pumpkaboo": "Taille Normale",
"cobblemon.ui.pokedex.info.form.pumpkaboo-small": "Taille Mini",
"cobblemon.ui.pokedex.info.form.pumpkaboo-large": "Taille Maxi",
"cobblemon.ui.pokedex.info.form.pumpkaboo-super": "Taille Ultra",
"cobblemon.ui.pokedex.info.form.gourgeist": "Taille Normale",
"cobblemon.ui.pokedex.info.form.gourgeist-small": "Taille Mini",
"cobblemon.ui.pokedex.info.form.gourgeist-large": "Taille Maxi",
"cobblemon.ui.pokedex.info.form.gourgeist-super": "Taille Ultra",
"cobblemon.ui.pokedex.info.form.pumpkaboo": "Variété Moyenne",
"cobblemon.ui.pokedex.info.form.pumpkaboo-small": "Variété Petite",
"cobblemon.ui.pokedex.info.form.pumpkaboo-large": "Variété Grande",
"cobblemon.ui.pokedex.info.form.pumpkaboo-super": "Variété Giga",
"cobblemon.ui.pokedex.info.form.gourgeist": "Variété Moyenne",
"cobblemon.ui.pokedex.info.form.gourgeist-small": "Variété Petite",
"cobblemon.ui.pokedex.info.form.gourgeist-large": "Variété Grande",
"cobblemon.ui.pokedex.info.form.gourgeist-super": "Variété Giga",
"cobblemon.ui.pokedex.info.form.bergmite": " ",
"cobblemon.ui.pokedex.info.form.avalugg": " ",
"cobblemon.ui.pokedex.info.form.avalugg-hisui": "Forme de Hisui",
@@ -9198,5 +9198,10 @@
"stat.cobblemon.riding_liquid": "Distance parcourue par Pokémon dans un liquide",
"cobblemon.ui.ride_style.air.bird": "Oiseau",
"effect.cobblemon.cleanse_all": "Nettoyage des effets",
"effect.cobblemon.cleanse_negative": "Nettoyage des effets négatifs"
"effect.cobblemon.cleanse_negative": "Nettoyage des effets négatifs",
"item.cobblemon.sweet_heart": "Chococœur",
"item.cobblemon.sweet_heart.tooltip": "Une friandise au chocolat sucrée",
"item.cobblemon.technical_machine": "Capsule Technique",
"item.cobblemon.blank_tm": "CT Vierge",
"block.cobblemon.deepslate_crystal_core": "Cœur Cristal de profondoise"
}
@@ -3342,7 +3342,7 @@
"cobblemon.species.golbat.name": "ゴルバット",
"cobblemon.species.golbat.desc": "小さな 脚で 器用に 歩く。 寝ている 獲物に 忍びより キバを 突きたて 血を すするのだ。",
"cobblemon.species.oddish.name": "ナゾノクサ",
"cobblemon.species.oddish.desc": "昼間は 太陽を 避けるため 冷たい 地面に もぐっている。 月の光を 浴びて 育つ。",
"cobblemon.species.oddish.desc": "別名 §oアルキメンデス§r。 夜になると 2本の 根っこで 300メートルも 歩くという。",
"cobblemon.species.gloom.name": "クサイハナ",
"cobblemon.species.gloom.desc": "よだれのように 見える 甘い蜜。 とても ねばねば しており 触れると いつまでも まとわりつく。",
"cobblemon.species.vileplume.name": "ラフレシア",
@@ -3548,13 +3548,13 @@
"cobblemon.species.zapdos.name": "サンダー",
"cobblemon.species.zapdos.desc": "雷雲の 中に いると 言われる 伝説の ポケモン。 雷を 自在に 操る。",
"cobblemon.species.moltres.name": "ファイヤー",
"cobblemon.species.moltres.desc": "美しく 燃えあがる 翼で 山道を 照らし 遭難者を 助けたと 言い伝えられている。",
"cobblemon.species.moltres.desc": "伝説の とりポケモンの 1匹。 ファイヤーが 姿を 見せると 春が 訪れると 言われている。",
"cobblemon.species.dratini.name": "ミニリュウ",
"cobblemon.species.dratini.desc": "ながく 幻と 言われていた。 額にある 小さな 突起は 成長途中の ツノ。",
"cobblemon.species.dragonair.name": "ハクリュー",
"cobblemon.species.dragonair.desc": "きれいな 海や 湖に 棲む。 天候を 操る 力で 風に 乗り 空へと 昇る。",
"cobblemon.species.dragonite.name": "カイリュー",
"cobblemon.species.dragonite.desc": "海の 化身と 呼ばれる。 カイリュー型の 彫像を 舳先に 付ける 船も 多い。",
"cobblemon.species.dragonite.desc": "§o海の 化身§rと 呼ばれる。 カイリュー型の 彫像を 舳先に 付ける 船も 多い。",
"cobblemon.species.mewtwo.name": "ミュウツー",
"cobblemon.species.mewtwo.desc": "ミュウの 遺伝子を 組み替えて 生み出された。ポケモンで 一番 凶暴な 心を 持つという。",
"cobblemon.species.mew.name": "ミュウ",
@@ -3754,7 +3754,7 @@
"cobblemon.species.tyranitar.name": "バンギラス",
"cobblemon.species.tyranitar.desc": "地響きを 鳴らしながら 歩くと 大きな 山でさえ 崩れ まわりの 地形は 変わってしまう。",
"cobblemon.species.lugia.name": "ルギア",
"cobblemon.species.lugia.desc": "海の神様と 伝えられる ポケモン。 嵐の 夜 姿を 見たという 話が 伝えられる。",
"cobblemon.species.lugia.desc": "§o海の神様§rと 伝えられる ポケモン。 嵐の 夜 姿を 見たという 話が 伝えられる。",
"cobblemon.species.hooh.name": "ホウオウ",
"cobblemon.species.hooh.desc": "体は 七色に 輝き 飛んだあとは 虹が できると 神話に 残されている ポケモン。",
"cobblemon.species.celebi.name": "セレビィ",
@@ -3894,7 +3894,7 @@
"cobblemon.species.carvanha.name": "キバニア",
"cobblemon.species.carvanha.desc": "鋭い キバと たくましい 顎を もつ。 船乗りたちは キバニアの すみかには けっして 近づかない。",
"cobblemon.species.sharpedo.name": "サメハダー",
"cobblemon.species.sharpedo.desc": "海のギャングと 呼ばれ 生息 海域に 入りこんでしまった 船は もれなく 襲われてしまう。",
"cobblemon.species.sharpedo.desc": "§o海のギャング§rと 呼ばれ 生息 海域に 入りこんでしまった 船は もれなく 襲われてしまう。",
"cobblemon.species.wailmer.name": "ホエルコ",
"cobblemon.species.wailmer.desc": "海水を たくさん 飲みこむと 体が ボールの ように 弾む。 毎日 1トンの エサを 食べる。",
"cobblemon.species.wailord.name": "ホエルオー",
@@ -3916,7 +3916,7 @@
"cobblemon.species.vibrava.name": "ビブラーバ",
"cobblemon.species.vibrava.desc": "ハネを 成長 させるために 毎日 大量の 獲物を 消化液で 溶かして すする。",
"cobblemon.species.flygon.name": "フライゴン",
"cobblemon.species.flygon.desc": "はばたく 音が 女の人の 美しい 歌声に 似ているため 砂漠の精霊 と 呼ばれている。",
"cobblemon.species.flygon.desc": "はばたく 音が 女の人の 美しい 歌声に 似ているため §o砂漠の精霊§r と 呼ばれている。",
"cobblemon.species.cacnea.name": "サボネア",
"cobblemon.species.cacnea.desc": "雨の 少ない 厳しい 環境 ほど きれいで 香りの 強い 花を 咲かせる。 トゲの ついた 腕を 振り回して 戦う。",
"cobblemon.species.cacturne.name": "ノクタス",
@@ -3994,7 +3994,7 @@
"cobblemon.species.gorebyss.name": "サクラビス",
"cobblemon.species.gorebyss.desc": "獲物の 体液を 吸う。 肉は 海底に 沈み 他の ポケモンの エサに なるのだ。",
"cobblemon.species.relicanth.name": "ジーランス",
"cobblemon.species.relicanth.desc": "深海調査で 発見された。 1億年間 姿が 変わらず 生きている化石 と 呼ばれている。",
"cobblemon.species.relicanth.desc": "深海調査で 発見された。 1億年間 姿が 変わらず §o生きている化石§r と 呼ばれている。",
"cobblemon.species.luvdisc.name": "ラブカス",
"cobblemon.species.luvdisc.desc": "暖かい 海の サンゴ礁が 棲み処。 サニーゴの枝の 間で 眠るのが 特に お気に入り。",
"cobblemon.species.bagon.name": "タツベイ",
@@ -4020,9 +4020,9 @@
"cobblemon.species.latios.name": "ラティオス",
"cobblemon.species.latios.desc": "優しい 心の 持ち主にしか なつかない。 腕を 折り畳むと 空気抵抗が 減って ジェット機 よりも 速く 空を 飛べるぞ。",
"cobblemon.species.kyogre.name": "カイオーガ",
"cobblemon.species.kyogre.desc": "海の 化身 と 伝わる ポケモン。 自然のエネルギーを 求めて グラードンと 争いを 繰り返したという 伝説が ある。",
"cobblemon.species.kyogre.desc": "§o海の 化身§r と 伝わる ポケモン。 自然のエネルギーを 求めて グラードンと 争いを 繰り返したという 伝説が ある。",
"cobblemon.species.groudon.name": "グラードン",
"cobblemon.species.groudon.desc": "大地の 化身 と 伝わる ポケモン。自然のエネルギーを 求めて カイオーガと 争いを 繰り返したという 伝説が ある。",
"cobblemon.species.groudon.desc": "§o大地の 化身§r と 伝わる ポケモン。自然のエネルギーを 求めて カイオーガと 争いを 繰り返したという 伝説が ある。",
"cobblemon.species.rayquaza.name": "レックウザ",
"cobblemon.species.rayquaza.desc": "何億年も 生き続けていると いわれる。 グラードンと カイオーガの 争いを 治めたという 伝説が 残されている。",
"cobblemon.species.jirachi.name": "ジラーチ",
@@ -4160,7 +4160,7 @@
"cobblemon.species.skorupi.name": "スコルピ",
"cobblemon.species.skorupi.desc": "毒のある しっぽの ツメで 襲いかかる。 挟まれると 毒が 染みて 動けなくなる。",
"cobblemon.species.drapion.name": "ドラピオン",
"cobblemon.species.drapion.desc": "砂の悪魔と 呼ばれるほど 獰猛だが カバルドンには ケンカも 売らず おとなしい。",
"cobblemon.species.drapion.desc": "§o砂の悪魔§rと 呼ばれるほど 獰猛だが カバルドンには ケンカも 売らず おとなしい。",
"cobblemon.species.croagunk.name": "グレッグル",
"cobblemon.species.croagunk.desc": "毒を 薄めると 薬に なる。 薬品会社の マスコットに なって 人気者に なった。",
"cobblemon.species.toxicroak.name": "ドクロッグ",
@@ -4232,7 +4232,7 @@
"cobblemon.species.giratina.name": "ギラティナ",
"cobblemon.species.giratina.desc": "この世の 裏側にある 世界に 住んでいると 言われる ポケモン。 古代の 墓場に 現れる。",
"cobblemon.species.cresselia.name": "クレセリア",
"cobblemon.species.cresselia.desc": "クレセリアの 羽を 持って 寝ると 楽しい 夢が 見られると いう。 三日月の化身 と 呼ばれている。",
"cobblemon.species.cresselia.desc": "クレセリアの 羽を 持って 寝ると 楽しい 夢が 見られると いう。 §o三日月の化身§r と 呼ばれている。",
"cobblemon.species.phione.name": "フィオネ",
"cobblemon.species.phione.desc": "暖かい 海を 漂っている。 どんな 遠くに 流されても 生まれた 場所に かならず戻る。",
"cobblemon.species.manaphy.name": "マナフィ",
@@ -4316,7 +4316,7 @@
"cobblemon.species.drilbur.name": "モグリュー",
"cobblemon.species.drilbur.desc": "ツメで 地中を 掘り進むので 育てた 野菜が 傷むと 農業関係者は 嫌う。",
"cobblemon.species.excadrill.name": "ドリュウズ",
"cobblemon.species.excadrill.desc": "別名 ドリルキング。 地中を 掘り進む 速度は 時速150キロに 達する。",
"cobblemon.species.excadrill.desc": "別名 §oドリルキング§r。 地中を 掘り進む 速度は 時速150キロに 達する。",
"cobblemon.species.audino.name": "タブンネ",
"cobblemon.species.audino.desc": "心優しい ポケモン。 触角を 押し当て 相手の 気持ちや 体調を 理解する。",
"cobblemon.species.timburr.name": "ドッコラー",
@@ -4834,13 +4834,13 @@
"cobblemon.species.tapufini.name": "カプ・レヒレ",
"cobblemon.species.tapufini.desc": "水を 操る 遠い島の 守り神。 けがれを 払う 清らかな 水を 生みだすと 伝えられている。",
"cobblemon.species.cosmog.name": "コスモッグ",
"cobblemon.species.cosmog.desc": "大昔は 星の子 という 名で 呼ばれた。 別世界の ポケモンと いわれているが 詳しくは 不明。",
"cobblemon.species.cosmog.desc": "大昔は §o星の子§r という 名で 呼ばれた。 別世界の ポケモンと いわれているが 詳しくは 不明。",
"cobblemon.species.cosmoem.name": "コスモウム",
"cobblemon.species.cosmoem.desc": "死んだように まったく 動かないが 触れると ほのかに 温かい。 大昔は 星の繭と 呼ばれた。",
"cobblemon.species.cosmoem.desc": "死んだように まったく 動かないが 触れると ほのかに 温かい。 大昔は §o星の繭§rと 呼ばれた。",
"cobblemon.species.solgaleo.name": "ソルガレオ",
"cobblemon.species.solgaleo.desc": "太陽を 食らいし 獣 と かつて 呼ばれた。 無尽蔵の 光エネルギーを 放出する。",
"cobblemon.species.solgaleo.desc": "§o太陽を 食らいし 獣§r と かつて 呼ばれた。 無尽蔵の 光エネルギーを 放出する。",
"cobblemon.species.lunala.name": "ルナアーラ",
"cobblemon.species.lunala.desc": "月を 誘いし 獣 という 名前を もつ。 あらゆる 光を エネルギーに 変えて 生きている。",
"cobblemon.species.lunala.desc": "§o月を 誘いし 獣§r という 名前を もつ。 あらゆる 光を エネルギーに 変えて 生きている。",
"cobblemon.species.nihilego.name": "ウツロイド",
"cobblemon.species.nihilego.desc": "謎に 包まれた ウルトラビーストの 一種。 町ゆく 人が 寄生され 暴れだす 姿が 目撃された。",
"cobblemon.species.buzzwole.name": "マッシブーン",
@@ -4972,7 +4972,7 @@
"cobblemon.species.hattrem.name": "テブリム",
"cobblemon.species.hattrem.desc": "頭の房で 相手を 殴り 黙らせる。 プロボクサーさえ 一発 KOの 破壊力。",
"cobblemon.species.hatterene.name": "ブリムオン",
"cobblemon.species.hatterene.desc": "別名は 森の 魔女。 騒ぎたてると 触手のツメで 引き裂かれるかも しれないぞ。",
"cobblemon.species.hatterene.desc": "別名は §o森の 魔女§r。 騒ぎたてると 触手のツメで 引き裂かれるかも しれないぞ。",
"cobblemon.species.impidimp.name": "ベロバー",
"cobblemon.species.impidimp.desc": "民家に 忍びこみ 盗みを 働き さらに 悔しがる 人の マイナスエネルギーも いただくのだ。",
"cobblemon.species.morgrem.name": "ギモー",
@@ -5032,7 +5032,7 @@
"cobblemon.species.dragapult.name": "ドラパルト",
"cobblemon.species.dragapult.desc": "ツノに 入った ドラメシヤは マッハの スピードで 飛ばされるのを 心待ちに しているらしい。",
"cobblemon.species.zacian.name": "ザシアン",
"cobblemon.species.zacian.desc": "伝説の 英雄と 呼ばれる ポケモン。 金属を 取り込み 武具に 変化させ 戦う。",
"cobblemon.species.zacian.desc": "§o伝説の 英雄§rと 呼ばれる ポケモン。 金属を 取り込み 武具に 変化させ 戦う。",
"cobblemon.species.zamazenta.name": "ザマゼンタ",
"cobblemon.species.zamazenta.desc": "石像の ような 姿で 忘れ去られるほどの あいだ 眠りに ついていた ポケモン。",
"cobblemon.species.eternatus.name": "ムゲンダイナ",
@@ -5224,29 +5224,29 @@
"cobblemon.species.kingambit.name": "ドドゲザン",
"cobblemon.species.kingambit.desc": "ドドゲザンに 進化できるのは 大軍勢の 頂点に 立った 1匹の キリキザンだけなのだ。",
"cobblemon.species.greattusk.name": "イダイナキバ",
"cobblemon.species.greattusk.desc": "近年 目撃例が ある。 イダイナキバという 名は ある本に 記された 生物から 取られた。",
"cobblemon.species.greattusk.desc": "近年 目撃例が ある。 イダイナキバという 名は ある本に 記された 生物から 取られた。 ”パルデア未確認ファイル No.09 太古から 生き続ける 恐竜!? 謎の生物 イダイナキバ パルデア某所にある 乾燥地帯において 謎の生物の 目撃が 報告されている。 一見 ドンファンに 似た その生物は 巨大な牙と 強固な鱗を 備えており 好戦的な性質を 示したという。 現代に 生き残る 恐竜というのが 有力な説だが§r §oその正体は 不明。 イダイナキバ という名は かの奇書 スカーレットブックに 記された 謎の生物から 命名されたと いわれる。” §o-謎のオカルト雑誌より引用§r",
"cobblemon.species.screamtail.name": "サケブシッポ",
"cobblemon.species.screamtail.desc": "10億年前の プリンと オカルト雑誌が 紹介した 謎の ポケモンに 似ている。",
"cobblemon.species.screamtail.desc": "10億年前の プリンと オカルト雑誌が 紹介した 謎の ポケモンに 似ている。 ”パルデア未確認ファイル No.10 10億年前の プリン 発見!? 謎の生物 サケブシッポ パルデア某所の 森で 目撃された 謎の生物が サケブシッポ である。 プリンに 酷似した 愛くるしさを 持っているが 攻撃的な性質で 近づくものに 襲いかかるという。 原始的な姿と 凶暴性から 一説には 10億年前の プリンと 噂されている。 奇書 スカーレットブックに 記された 叫ぶ尻尾と 呼ばれる 謎の生物が 名前の由来 である。” §o-謎のオカルト雑誌より引用§r",
"cobblemon.species.brutebonnet.name": "アラブルタケ",
"cobblemon.species.brutebonnet.desc": "怪しい 雑誌が 恐竜と キノコの 合成と 紹介した ポケモンに 少し 似ているのだ。",
"cobblemon.species.brutebonnet.desc": "怪しい 雑誌が 恐竜と キノコの 合成と 紹介した ポケモンに 少し 似ているのだ。 ”パルデア未確認ファイル No.06 恐竜? モロバレル? 謎の生物 アラブルタケ 恐竜の特徴とモロバレルの特徴を あわせ持つと 噂される 奇妙な生物。 奇書と 名高い スカーレットブックで 紹介された 荒ぶる茸と 呼ばれる 謎の生物と おおむね 特徴が 一致。 そこから この未確認生物は アラブルタケと 呼ばれるように なった。 通常の モロバレルより はるかに 身体は 巨大だが 知性は低いようで 凶暴な ハードパンチャーらしい。” §o-謎のオカルト雑誌より引用§r",
"cobblemon.species.fluttermane.name": "ハバタクカミ",
"cobblemon.species.fluttermane.desc": "オカルト雑誌で 取り上げられた 翼竜の 幽霊と 特徴だけは 似ているのだ。",
"cobblemon.species.fluttermane.desc": "オカルト雑誌で 取り上げられた 翼竜の 幽霊と 特徴だけは 似ているのだ。 ”パルデア未確認ファイル No.05 翼竜のゴースト!? 謎の生物 ハバタクカミその名は スカーレットブックと 呼ばれる 奇書の中に 登場する 謎の生物から 名づけられた。 長い体毛を くねらせ 夜空を ゆらゆらと 飛びまわると いわれる。 目撃者からの 証言によると 一見 巨大なムウマの ようだが 気性が とても荒く 好戦的とのこと。 古代に 生息した 翼竜の魂から 生まれた ゴーストという 説もある。” §o-謎のオカルト雑誌より引用§r",
"cobblemon.species.slitherwing.name": "チヲハウハネ",
"cobblemon.species.slitherwing.desc": "古い 本で チヲハウハネと 紹介されている 生物に 似た点が ある 謎のポケモン。",
"cobblemon.species.slitherwing.desc": "古い 本で チヲハウハネと 紹介されている 生物に 似た点が ある 謎のポケモン。 ”パルデア未確認ファイル No.08 蘇った太古の ウルガモス!? 謎の生物 チヲハウハネ 目撃情報が少なく 極めて 謎の多い 未確認生物が チヲハウハネ である。 その名は 奇書として 知られる スカーレットブックに 記された 謎の生物に ちなんで 名づけられた。 かつて 本誌が§r §oとりあげた 原始時代の ウルガモスと 共通する特徴が あり 化石から 復元したとも 考えられる。 だが 過去に ウルガモスの化石が 発見された記録は 存在していない。§r” §o-謎のオカルト雑誌より引用§r",
"cobblemon.species.sandyshocks.name": "スナノケガワ",
"cobblemon.species.sandyshocks.desc": "オカルト雑誌の 記事にあった 1万年 生きた レアコイルの 姿に やや 似ている。",
"cobblemon.species.sandyshocks.desc": "オカルト雑誌の 記事にあった 1万年 生きた レアコイルの 姿に やや 似ている。 ”パルデア未確認ファイル No.07 寿命1万年の レアコイル!? 謎の生物 スナノケガワ 1万年生きた レアコイルの姿だと 主張する 科学者もいる 謎の生物。 パルデアの 乾燥地帯を 中心に 数件の 目撃報告が あるものの いまだ 捕獲されたという 記録はない。 攻撃的な性質で 目撃者が 襲われるケースも あるという。 世紀の奇書 スカーレットブックで 紹介された 砂の毛皮と 呼ばれる 謎の生物と 特徴が 一致している。” §o-謎のオカルト雑誌より引用§r",
"cobblemon.species.irontreads.name": "テツノワダチ",
"cobblemon.species.irontreads.desc": "オカルト雑誌が 紹介した 宇宙人が 送り込んできた 科学兵器に よく 似ている。",
"cobblemon.species.irontreads.desc": "オカルト雑誌が 紹介した 宇宙人が 送り込んできた 科学兵器に よく 似ている。 ”パルデア未確認ファイル No.09 宇宙人の科学兵器か!? 謎の物体 テツノワダチ パルデア某所にある 乾燥地帯において 謎の生物の 目撃が 報告されている。 一見 ドンファンに 似た その生物は 突如 球体に 変化すると 高速で 回転し 襲いかかってきたという。 宇宙人の 科学兵器では ないかという 噂もあるが 真相は 不明。 テツノワダチ という名は かの奇書 バイオレットブックに 記された 謎の物体から 命名されたと いわれる。” §o-謎のオカルト雑誌より引用§r",
"cobblemon.species.ironbundle.name": "テツノツツミ",
"cobblemon.species.ironbundle.desc": "オカルト雑誌の 記事になった 太古の 文明が 生み出した ロボットに 形が 似ている。",
"cobblemon.species.ironbundle.desc": "オカルト雑誌の 記事になった 太古の 文明が 生み出した ロボットに 形が 似ている。 ”パルデア未確認ファイル No.10 太古の文明が 生んだ ロボット!? 謎の物体 テツノツツミ 奇書 バイオレットブックに 書かれた 謎の物体が 名前の由来 である。 デリバードに 似た 形状をしており 球体の装置から 膨大な氷を 噴射し 雪原を 移動するという。 正体に 関しては 多くの説が あるが 超古代文明の産物 という説が 有力。 太古の文献に テツノツツミ らしき 機械を 設計し 使役していたという 記録が 残っているのだ。” §o-謎のオカルト雑誌より引用§r",
"cobblemon.species.ironhands.name": "テツノカイナ",
"cobblemon.species.ironhands.desc": "あるアスリートが 改造された 姿だと オカルト雑誌が スクープした サイボーグに 近い。",
"cobblemon.species.ironhands.desc": "あるアスリートが 改造された 姿だと オカルト雑誌が スクープした サイボーグに 近い。 ”パルデア未確認ファイル No.05 その正体は サイボーグ!? 謎の物体 テツノカイナ 世紀の奇書 バイオレットブックに 記された 鉄の腕と 呼ばれる 謎の物体から 命名された。 自在に動く 掌で 高速で 走っていた 大型車両を 投げ飛ばしたという。 テツノカイナの 正体として 有力なのは 瀕死の重傷を 負った アスリートが サイボーグ化した という説である。 ただ 何故 ハリテヤマを 模した姿に なったのかは 謎に つつまれているのだ。” §o-謎のオカルト雑誌より引用§r",
"cobblemon.species.ironjugulis.name": "テツノコウベ",
"cobblemon.species.ironjugulis.desc": "ロボットに 恋した サザンドラの 子どもと オカルト雑誌が 取り上げた ポケモンに 似ている。",
"cobblemon.species.ironjugulis.desc": "ロボットに 恋した サザンドラの 子どもと オカルト雑誌が 取り上げた ポケモンに 似ている。 ”パルデア未確認ファイル No.07 機械!? ポケモン!? 謎の物体 テツノコウベ パルデア某所にて 目撃された サザンドラに 似た 危険な 飛行物体。 高エネルギーを 放射し 建造物を 徹底的に 破壊すると 次の標的を 求めるように 飛び去っていったという。 サザンドラの凶暴性と 機械の冷酷さを あわせ持つため 機械と サザンドラの ハイブリッドなのではと 噂される。 奇書 バイオレットブックに 書かれた 謎の物体が 名前の由来 である。” §o-謎のオカルト雑誌より引用§r",
"cobblemon.species.ironmoth.name": "テツノドクガ",
"cobblemon.species.ironmoth.desc": "人類を 監視する UFOだと オカルト雑誌が 紹介した 未知の 物体に 似ている。",
"cobblemon.species.ironmoth.desc": "人類を 監視する UFOだと オカルト雑誌が 紹介した 未知の 物体に 似ている。 ”パルデア未確認ファイル No.08 人類を監視する UFO!? 謎の物体 テツノドクガ 世紀の奇書 バイオレットブックに 登場する 謎の物体から 命名された。 ウルガモスに 似た 飛行物体であり 上空から 地上の人々を 追尾し 時に 襲いかかってくることが あるという。 攻撃的な性質を 持つ 理由として テツノドクガの 正体は 宇宙人が あやつる UFOだという 説がある。 テツノドクガを 使い 人類を 監視し 侵略の機会を うかがっているかもしれない。” §o-謎のオカルト雑誌より引用§r",
"cobblemon.species.ironthorns.name": "テツノイバラ",
"cobblemon.species.ironthorns.desc": "10億年後の バンギラスと 怪しい 雑誌が 取り上げていた ポケモンに 似た 部分が ある。",
"cobblemon.species.ironthorns.desc": "10億年後の バンギラスと 怪しい 雑誌が 取り上げていた ポケモンに 似た 部分が ある。 ”パルデア未確認ファイル No.06 10億年後の バンギラス!? 謎の物体 テツノイバラ パルデア某所 とある洞くつで 目撃された 謎の物体が テツノイバラ である。 バンギラスに 酷似しているが 極めて 冷徹な性質を 持つとともに 無駄な争いを 好まないという。 金属質の装甲や 高い知性から 10億年後の バンギラスと 噂されている。 奇書 バイオレットブックに 記された 鉄の茨と 呼ばれる 謎の物体が 名前の由来に なっている。” §o-謎のオカルト雑誌より引用§r",
"cobblemon.species.frigibax.name": "セビエ",
"cobblemon.species.frigibax.desc": "背びれで 熱を 吸収して 氷エネルギーに 変換する。 高温ほど エネルギーが 溜まる。",
"cobblemon.species.arctibax.name": "セゴール",
@@ -5266,13 +5266,13 @@
"cobblemon.species.chiyu.name": "イーユイ",
"cobblemon.species.chiyu.desc": "多くの 争いの 火種となった 勾玉に 集まった 妬みが 炎を まとい ポケモンとなった。",
"cobblemon.species.roaringmoon.name": "トドロクツキ",
"cobblemon.species.roaringmoon.desc": "謎の 多い 探検記の中で トドロクツキという 名で 記された 生物の 可能性が ある。",
"cobblemon.species.roaringmoon.desc": "謎の 多い 探検記の中で トドロクツキという 名で 記された 生物の 可能性が ある。 ”パルデア未確認ファイル No.04 原始の ボーマンダ!? 謎の生物 トドロクツキ 奇書 スカーレットブックに 書かれた 謎の生物が 名前の由来に なっている。 その姿は ほかの地方で ボーマンダに 発生する とある現象の 結果に 酷似しているが 関連性は 不明。 羽毛を まき散らしながら 高速で 飛びまわり 獲物を 襲う。 その 凶暴な性質は ボーマンダ以上と 考えられており 遭遇することが あっても 絶対に 接触を 避けるべきである。” §o-謎のオカルト雑誌より引用§r",
"cobblemon.species.ironvaliant.name": "テツノブジン",
"cobblemon.species.ironvaliant.desc": "オカルト雑誌の 記事に あった マッドサイエンティストの 発明と 近い 特徴が いくつかある。",
"cobblemon.species.ironvaliant.desc": "オカルト雑誌の 記事に あった マッドサイエンティストの 発明と 近い 特徴が いくつかある。 ”パルデア未確認ファイル No.04 博士の 異常な発明!? 謎の物体 テツノブジン 奇書 バイオレットブックに 書かれた 謎の物体が 名前の由来に なっている。 最強の エスパーポケモンを 求める マッドサイエンティストに よって 造られた ロボットという 説がある。 数少ない 目撃者からの 証言によるとサーナイトと エルレイド どちらにも見えるような 奇妙な姿を しており対峙する者は 光りかがやく刃で 躊躇なく 切り捨てる 残酷さを 持っているという。” §o-謎のオカルト雑誌より引用§r",
"cobblemon.species.koraidon.name": "コライドン",
"cobblemon.species.koraidon.desc": "拳で 大地を 引き裂いたと 古い 探検記に 記された ツバサノオウの 正体らしい。",
"cobblemon.species.koraidon.desc": "拳で 大地を 引き裂いたと 古い 探検記に 記された §oツバサノオウ§rの 正体らしい。",
"cobblemon.species.miraidon.name": "ミライドン",
"cobblemon.species.miraidon.desc": "古い 書物に 名が ある テツノオロチらしい。 雷で 大地を 灰に 変えたという。",
"cobblemon.species.miraidon.desc": "古い 書物に 名が ある §oテツノオロチ§rらしい。 雷で 大地を 灰に 変えたという。",
"cobblemon.species.walkingwake.name": "ウネルミナモ",
"cobblemon.species.walkingwake.desc": "謎多き 凶暴な 生物。 古い 探検記に 記された 水の 怪物から 名付けられた。",
"cobblemon.species.ironleaves.name": "テツノイサハ",
@@ -5957,7 +5957,7 @@
"item.cobblemon.galarica_nuts": "ガラナツのナッツ",
"cobblemon.pokerod.apply": "右クリックでエサを付ける",
"item.cobblemon.npc_editor": "NPCのトレーナーカード",
"item.cobblemon.link_cable.tooltip": "なにかしらのつながりを感じさせる 不思議なエネルギーを秘めているヒモ。 ポケモン交換で進化するポケモンを進化させる。",
"item.cobblemon.link_cable.tooltip": "ポケモン交換で進化するポケモンを進化させられるヒモ。",
"item.cobblemon.dragon_scale.tooltip": "硬くて 丈夫な ウロコ。 シードラに持たせてポケモン交換すると キングドラに進化する。",
"item.cobblemon.kings_rock.tooltip_1": "持たせると 攻撃して ダメージを 与えたときに 相手を ひるませることが ある。",
"item.cobblemon.metal_coat.tooltip_1": "特殊な金属の 膜。 持たせると はがねタイプの 技の威力が 上がる。",
@@ -5968,11 +5968,11 @@
"item.cobblemon.deep_sea_scale.tooltip_2": "パールルに持たせてポケモン交換すると サクラビスに進化する。",
"item.cobblemon.deep_sea_tooth.tooltip_1": "パールルに 持たせると 特攻が あがる キバ。",
"item.cobblemon.deep_sea_tooth.tooltip_2": "パールルに持たせてポケモン交換すると ハンテールに進化する。",
"item.cobblemon.electirizer.tooltip": "とてつもない 電気の エネルギーが 蓄えられている 箱。 エレブーに持たせてポケモン交換すると エレキブルに進化する。",
"item.cobblemon.magmarizer.tooltip": "とてつもない マグマの エネルギーが 蓄えられている 箱。 ブーバーに持たせてポケモン交換すると ブーバーンに進化する。",
"item.cobblemon.electirizer.tooltip": "電気が蓄えられた箱。 エレブーに持たせてポケモン交換すると エレキブルに進化する。",
"item.cobblemon.magmarizer.tooltip": "マグマが蓄えられた箱。 ブーバーに持たせてポケモン交換すると ブーバーンに進化する。",
"item.cobblemon.oval_stone.tooltip": "ある特定の ポケモンを 進化させる 不思議な石。 たまのように まるい。",
"item.cobblemon.reaper_cloth.tooltip": "おそろしく 強い霊力が こめられている 布。 サマヨールに持たせてポケモン交換すると ヨノワールに進化する。",
"item.cobblemon.sachet.tooltip": "ちょっと きつめの 香りをだす 香料を 詰めた 袋。 シュシュプに持たせてポケモン交換すると フレフワンに進化する。",
"item.cobblemon.sachet.tooltip": "きつめの 香料を 詰めた 袋。 シュシュプに持たせてポケモン交換すると フレフワンに進化する。",
"item.cobblemon.strawberry_sweet.tooltip": "いちごの形をした アメ細工。 マホミルに使うと いちごのアメざいくを身に付けた マホイップになる。",
"item.cobblemon.berry_sweet.tooltip": "ベリーの形をした アメ細工。 マホミルに使うと ベリーのアメざいくを身に付けた マホイップになる。",
"item.cobblemon.clover_sweet.tooltip": "よつばの形をした アメ細工。 マホミルに使うと よつばのアメざいくを身に付けた マホイップになる。",
@@ -6052,7 +6052,7 @@
"item.cobblemon.grass_gem.tooltip_2": "使うとなくなる。",
"item.cobblemon.dragon_gem.tooltip_2": "使うとなくなる。",
"item.cobblemon.ghost_gem.tooltip_2": "使うとなくなる。",
"item.cobblemon.whipped_dream.tooltip": "ふんわり ふわふわに 泡立てた ほんのり 甘い クリーム。 ペロッパフに持たせてポケモン交換すると ペロリームに進化する。",
"item.cobblemon.whipped_dream.tooltip": "ふわふわに 泡立てた クリーム。 ペロッパフに持たせてポケモン交換すると ペロリームに進化する。",
"item.cobblemon.sun_stone.tooltip": "ある特定の ポケモンを 進化させる 不思議な石。 太陽のように 赤い。",
"item.cobblemon.prism_scale.tooltip": "にじ色に 輝く 不思議なウロコ。 ヒンバスに持たせてポケモン交換すると ミロカロスに進化する。",
"item.cobblemon.love_sweet.tooltip": "ハートの形をした アメ細工。 マホミルに使うと ハートのアメざいくを身に付けた マホイップになる。",
@@ -6115,7 +6115,7 @@
"item.cobblemon.poison_barb.tooltip": "どくのある ちいさな ハリ。 持たせると どくタイプの 技の威力が あがる。",
"item.cobblemon.punching_glove.tooltip": "こぶしを 保護する グローブ。 持たせると パンチ技の 威力が あがり 相手に 接触しない パンチに なる。",
"item.cobblemon.quick_claw.tooltip": "軽くて 鋭い ツメ。 持たせると 相手より 先に 行動できることが ある。",
"item.cobblemon.razor_fang.tooltip_1": "鋭く とがった キバ。持たせると ダメージを 与えたとき ひるませることが ある。",
"item.cobblemon.razor_fang.tooltip_1": "鋭く とがった キバ。 持たせると ダメージを 与えたとき ひるませることが ある。",
"item.cobblemon.razor_fang.tooltip_2": "グライガーに 持たせると、 夜にグライオンに進化する。",
"item.cobblemon.red_card.tooltip_1": "不思議な ちからのある カード。 持たせると ダメージを 与えてきた 相手を 控えに 退場させることが できる。",
"item.cobblemon.rocky_helmet.tooltip": "ポケモンに 持たせると 自分に さわった相手に ダメージを 与える。",
@@ -6254,7 +6254,7 @@
"cobblemon.trade.accept.receiver": "あなたは %1$s の交換リクエストを受け入れました!",
"item.cobblemon.safety_goggles.tooltip": "天候の ダメージだけでなく 粉を だす 技の 効果も 防げる ゴーグル。",
"item.cobblemon.shell_bell.tooltip": "持たせた ポケモンが 攻撃して 相手に ダメージを あたえたとき HPを 少し 回復できる。",
"item.cobblemon.shell_helmet.tooltip": "とあるポケモンが身に付けていた殻。 カブルモに使うと シュバルゴに進化する。",
"item.cobblemon.shell_helmet.tooltip": "とあるポケモンが 身に付けていた 殻。 カブルモに使うと シュバルゴに進化する。",
"cobblemon.trade.expired.sender": "%1$s との交換リクエストがタイムアウトしました。",
"item.cobblemon.ground_gem.tooltip_1": "じめんのタイプの ジュエル。 持たせると 1度だけ じめんの 技の威力が 強まる。",
"item.cobblemon.x_attack.tooltip": "戦闘中の ポケモンの 攻撃を 2段階 あげる 道具。 ひっこめると 元に 戻る。",
@@ -6495,7 +6495,7 @@
"cobblemon.ui.pokedex.info.form.glalie-mega": "メガオニゴーリ",
"cobblemon.species.glalie-mega.desc": "砕けた 口から 凄まじい 冷気を 吐くと あたり 一帯は ホワイトアウトに 陥ってしまう。",
"cobblemon.ui.pokedex.info.form.salamence-mega": "メガボーマンダ",
"cobblemon.species.salamence-mega.desc": "より 凶暴に なって 育ての トレーナーにも 襲いかかるほど。別名 血に 濡れた 三日月。",
"cobblemon.species.salamence-mega.desc": "より 凶暴に なって 育ての トレーナーにも 襲いかかるほど。別名 §o血に 濡れた 三日月§r。",
"cobblemon.ui.pokedex.info.form.metagross-mega": "メガメタグロス",
"cobblemon.species.metagross-mega.desc": "メガシンカで 脳を 刺激され 勝利の ためには 手段を 選ばない 冷酷な 性格に。",
"cobblemon.ui.pokedex.info.form.latias-mega": "メガラティアス",
@@ -6537,7 +6537,7 @@
"cobblemon.ui.pokedex.info.form.meloetta": "ボイスフォルム",
"cobblemon.ui.pokedex.info.form.meloetta-pirouette": "ステップフォルム",
"cobblemon.ui.pokedex.info.form.diancie-mega": "メガディアンシー",
"cobblemon.species.diancie-mega.desc": "額で 眩く 輝く ダイヤは なんと 2000カラット。 別名 ロイヤルピンクプリンセス。",
"cobblemon.species.diancie-mega.desc": "額で 眩く 輝く ダイヤは なんと 2000カラット。 別名 §oロイヤルピンクプリンセス§r。",
"cobblemon.ui.pokedex.info.form.color-cyan": "青緑色",
"cobblemon.ui.pokedex.info.form.vivillon-wings-forsaken": "みすてられたもよう",
"cobblemon.ui.pokedex.info.form.snake-pattern-speed": "すばやさのもよう",
@@ -7374,7 +7374,7 @@
"cobblemon.ui.pokedex.info.form.alakazam": " ",
"cobblemon.species.voltorb-hisui.desc": "モンスターボールと 空似せし 謎の ポケモン。気 昂るほど 腹に 蓄えし電流を 解き放ち 大笑す。",
"cobblemon.species.eevee-gmax.desc": "キョダイマックス パワーで さらに もふもふした 首まわりの 綿毛で 敵を 包みこみ とりこにする。",
"cobblemon.species.articuno-galar.desc": "凍りついたかのように 体の 自由を 奪う ビームを 撃ちだす フリーザーの 名を もつ ポケモン。",
"cobblemon.species.articuno-galar.desc": "凍りついたかのように 体の 自由を 奪う ビームを 撃ちだす §oフリーザー§rの 名を もつ ポケモン。",
"cobblemon.ui.pokedex.info.form.wooper-paldea": "パルデアのすがた",
"cobblemon.species.venusaur-gmax.desc": "2本の ふとい ツルを 振りまわし 戦う。10階建ての ビルを かるく なぎ倒すほど パワフルだ。",
"cobblemon.ui.pokedex.info.form.blastoise": " ",
@@ -7493,10 +7493,10 @@
"cobblemon.ui.pokedex.info.form.articuno-galar": "ガラルのすがた",
"cobblemon.ui.pokedex.info.form.zapdos": " ",
"cobblemon.ui.pokedex.info.form.zapdos-galar": "ガラルのすがた",
"cobblemon.species.zapdos-galar.desc": "羽毛が 擦れるとき バチバチと 電気のような 音が することから サンダーと 呼ばれてきた。",
"cobblemon.species.zapdos-galar.desc": "羽毛が 擦れるとき バチバチと 電気のような 音が することから §oサンダー§rと 呼ばれてきた。",
"cobblemon.ui.pokedex.info.form.moltres": " ",
"cobblemon.ui.pokedex.info.form.moltres-galar": "ガラルのすがた",
"cobblemon.species.moltres-galar.desc": "伝説の とりポケモンの 1匹。 ファイヤーが 姿を 見せると 春が 訪れると 言われている。",
"cobblemon.species.moltres-galar.desc": "邪悪な オーラを 炎のように 燃え上がらせる その 姿から §oファイヤー§rと 名づけられた。",
"cobblemon.ui.pokedex.info.form.aerodactyl": " ",
"cobblemon.ui.pokedex.info.form.cyndaquil": " ",
"cobblemon.ui.pokedex.info.form.mewtwo": " ",
@@ -7611,7 +7611,7 @@
"cobblemon.species.orbeetle-gmax.desc": "脳を 中心に 巨大化。 圧倒的 知能と 膨大な サイコエネルギーを もっている。",
"cobblemon.species.sandaconda-gmax.desc": "砂袋も 超巨大に。 体の まわりに 渦巻く 砂は 100万トンを 超える。",
"cobblemon.species.centiskorch-gmax.desc": "キョダイマックスの パワーによって 体温は 摂氏1000度を 超え 熱波で 相手を 焼き焦がす。",
"cobblemon.species.urshifu-gmax.desc": "怒りの化身 と 呼ばれている。 凄まじい 形相と 雄叫びで 世界の 邪気を 払うという。",
"cobblemon.species.urshifu-gmax.desc": "§o怒りの化身§r と 呼ばれている。 凄まじい 形相と 雄叫びで 世界の 邪気を 払うという。",
"cobblemon.cosmetic_item.give": "%1$sに%2$sを身に付けさせた!",
"cobblemon.cosmetic_item.take": "%2$sから %1$sを 受け取った!",
"cobblemon.cosmetic_item.already_wearing": "%1$sはもう %2$sを 身に付けている!",
@@ -7774,7 +7774,7 @@
"cobblemon.move.gmaxsweetness.desc": "キョダイマックスした タルップルが 繰りだす くさタイプの 攻撃。 味方の 状態異常を 回復する。",
"cobblemon.move.gmaxvoltcrash.desc": "キョダイマックスした ピカチュウが 繰りだす でんきタイプの 攻撃。 相手を まひ状態に する。",
"cobblemon.ui.pokedex.info.form.samurott-hisui": "ヒスイのすがた",
"cobblemon.species.dialga-origin.desc": "神と畏れし伝説 古の地に あり。 誕生により うつし世の 壁 消え去り 延々と 際限なき空 出現したり。",
"cobblemon.species.dialga-origin.desc": "光明 フォルムチェンジの 引き金。 創造神に 酷似する姿こそ 真なりと 思わざるを得ぬほどの 剛力 発揮す。",
"cobblemon.move.gmaxtartness.desc": "キョダイマックスした アップリューが 繰りだす くさタイプの 攻撃。 相手の 回避率を 下げる。",
"cobblemon.species.basculin-white.desc": "極寒の 海で 群れを 作り エサを 分け合って 暮らす。 時が 来ると 生まれた 川に 帰る。",
"cobblemon.species.landorus-therian.desc": "霊獣の姿にて 天翔けしとき 大地に 豊穣もたらす。 衆人 これを 敬う。 一刻あれば 古の地を 縦断す。",
@@ -8179,8 +8179,8 @@
"cobblemon.ui.pokedex.info.form.eiscue-noiceface": "ナイスフェイス",
"cobblemon.pokedex.variation.face_spot_1": "顔のブチ1",
"cobblemon.pokedex.variation.face_spot_2": "顔のブチ2",
"cobblemon.species.zacian-crowned.desc": "あらゆるものを 切り捨てるさまから 妖精王の剣 と 呼ばれ 敵味方に 恐れ崇められた。",
"cobblemon.species.zamazenta-crowned.desc": "いかなる 攻撃も 弾き返す 姿は 格闘王の盾 と 呼ばれ 恐れ 崇められた。",
"cobblemon.species.zacian-crowned.desc": "あらゆるものを 切り捨てるさまから §o妖精王の剣§r と 呼ばれ 敵味方に 恐れ崇められた。",
"cobblemon.species.zamazenta-crowned.desc": "いかなる 攻撃も 弾き返す 姿は §o格闘王の盾§r と 呼ばれ 恐れ 崇められた。",
"cobblemon.ui.pokedex.info.form.calyrex-ice": "はくばじょう",
"cobblemon.species.gimmighoul-roaming.desc": "古い コインを 背負い さまよう。 コインを 拾おうとした 人の 生気を 吸い取って 生きている。",
"cobblemon.species.archaludon.desc": "まわりから 静電気を 集める。 四つん這いの 体勢で 撃ち出す ビームは 絶大な 威力。",
@@ -9466,7 +9466,7 @@
"cobblemon.species.victreebel-mega.desc": "メガシンカにより 増量した 溶解液が 口内を 満たす。 気を抜くと 口から 溢れだす。",
"cobblemon.species.starmie-mega.desc": "動きが 人間らしくなってきた。 意思疎通を 図ろうとしているのか 人に 成り代わるつもりかは 謎。",
"cobblemon.species.dragonite-mega.desc": "メガシンカのエネルギーが 優しい 気持ちを 過剰に パワーアップ。 慈悲の 心で 敵を 葬る。",
"cobblemon.species.meganium-mega.desc": "4輪の 花から 放たれる ソーラービームは 威力絶大。 別名 メガソーラー砲。",
"cobblemon.species.meganium-mega.desc": "4輪の 花から 放たれる ソーラービームは 威力絶大。 別名 §oメガソーラー砲§r。",
"cobblemon.species.feraligatr-mega.desc": "フードのような ヒレと 両腕で 巨大な 顎を 作り 噛みつく 威力は 10倍 アップした。",
"cobblemon.species.skarmory-mega.desc": "飛行速度は 音速を 超える。 衝撃波を 巻き起こして 敵を 吹き飛ばし ツメで とどめを 刺す。",
"cobblemon.species.chimecho-mega.desc": "全身を 震わせて あらゆる 方向を 音波で 攻撃。 聴いていると 自分を 見失う。",
@@ -2920,7 +2920,7 @@
"cobblemon.move.meteorbeam": "메테오빔",
"cobblemon.move.meteorbeam.desc": "1턴째에 우주의 힘을 모아서 특수공격을 올리고 2턴째에 상대를 공격한다.",
"cobblemon.move.meteormash": "코멧펀치",
"cobblemon.move.meteormash.desc": "혜성과 같은 펀치를 날려서 상대를 공격한다. 20%의 확률로 자신의 공격이 2랭크 오른다.",
"cobblemon.move.meteormash.desc": "혜성과 같은 펀치를 날려서 상대를 공격한다. 20%의 확률로 자신의 공격이 1랭크 오른다.",
"cobblemon.move.metronome": "손가락흔들기",
"cobblemon.move.metronome.desc": "손가락을 흔들어 자신의 뇌를 자극해서 모든 기술 중 어느 하나를 날린다.",
"cobblemon.move.milkdrink": "우유마시기",
@@ -5699,7 +5699,7 @@
"item.cobblemon.blunder_policy.tooltip_2": "사용 시 소모된다",
"item.cobblemon.cell_battery.tooltip_1": "지니게 하고 전기 기술을 받으면 공격이 올라간다",
"item.cobblemon.cell_battery.tooltip_2": "사용 시 소모된다",
"item.cobblemon.charcoal_stick.tooltip": "물건을 태우는 연료. 지니게 하면 불꽃건을 태우는 연료. 지니게 하면 불꽃타입 기술의 위력이 올라간다",
"item.cobblemon.charcoal_stick.tooltip": "물건을 태우는 연료. 지니게 하면 불꽃타입 기술의 위력이 올라간다",
"item.cobblemon.choice_band.tooltip": "기술을 구애받는 머리띠. 지니게 하면 공격은 올라가지만 똑같은 기술밖에 쓰지 못한다",
"item.cobblemon.choice_scarf.tooltip": "기술을 구애받는 스카프. 지니게 하면 스피드는 올라가지만 같은 기술밖에 쓸 수 없다",
"item.cobblemon.cleanse_tag.tooltip": "효과가 없다",
@@ -9408,9 +9408,9 @@
"cobblemon.ui.pokedex.info.form.glimmora-mega": "Mega Glimmora",
"cobblemon.ui.pokedex.info.form.tatsugiri-mega": "Mega Tatsugiri Forma Curvada",
"cobblemon.species.tatsugiri-mega.desc": "O cérebro de Tatsugiri foi energizado pela Mega Evolução, tornando-o mais astuto. Pode criar e comandar cópias de si mesmo.",
"cobblemon.ui.pokedex.info.form.tatsugiri-droopymega": "Mega Tatsuguri Forma Molenga",
"cobblemon.ui.pokedex.info.form.tatsugiri-droopymega": "Mega Tatsuguri Forma Pendular",
"cobblemon.species.tatsugiri-droopymega.desc": "Solidifica a energia da Mega Evolução, construindo uma pilha excessiva para lançar como projéteis. Esses projéteis explodem ao primeiro contato.",
"cobblemon.ui.pokedex.info.form.tatsugiri-stretchymega": "Mega Tatsugiri Forma Elástica",
"cobblemon.ui.pokedex.info.form.tatsugiri-stretchymega": "Mega Tatsugiri Forma Estendida",
"cobblemon.species.tatsugiri-stretchymega.desc": "Usando a energia da Mega Evolução, cria um prato onde repousar, permitindo com que se mova com mobilidade total até mesmo pelo ar.",
"cobblemon.ui.pokedex.info.form.baxcalibur-mega": "Mega Baxcalibur",
"cobblemon.species.baxcalibur-mega.desc": "A lâmina dorsal de Baxcalibur ficou ainda maior graças à Mega Evolução. Esse Pokémon dispara feixes do cabo localizado em seu peito."
@@ -3500,15 +3500,15 @@
"cobblemon.species.machamp.name": "Мачамп",
"cobblemon.species.machamp.desc": "Мачамп известен как покемон, в совершенстве овладевший всеми видами боевых искусств. Если он схватит противника своими четырьмя руками, битву можно считать практически законченной. Несчастный противник будет отброшен далеко за горизонт.",
"cobblemon.species.bellsprout.name": "Беллспраут",
"cobblemon.species.bellsprout.desc": "Prefers hot and humid places. It ensnares tiny bugs with its vines and devours them.",
"cobblemon.species.bellsprout.desc": "Тонкое и гибкое тело Беллспраута позволяет ему изгибаться и раскачиваться, чтобы уклоняться от любой атаки, какой бы сильной она ни была. Этот покемон извергает из своего рта едкую жидкость, способную растворить даже железо.",
"cobblemon.species.weepinbell.name": "Випинбелл",
"cobblemon.species.weepinbell.desc": "When hungry, it swallows anything that moves. Its hapless prey is dissolved by strong acids.",
"cobblemon.species.weepinbell.desc": "На задней части тела Випинбелла есть большой крючок. Ночью этот покемон цепляется к ветке дерева и засыпает. Если он шевелится во сне, то, проснувшись, может обнаружить, что он оказался на земле.",
"cobblemon.species.victreebel.name": "Виктрибелл",
"cobblemon.species.victreebel.desc": "Lures prey with the sweet aroma of honey. Swallowed whole, the prey is dissolved in a day, bones and all.",
"cobblemon.species.victreebel.desc": "У Виктрибела из головы растёт длинная лиана. Он машет ею и взмахивает, имитируя животное, чтобы привлечь добычу. Когда ничего не подозревающая добыча подходит поближе, этот покемон проглатывает её целиком.",
"cobblemon.species.tentacool.name": "Тентакул",
"cobblemon.species.tentacool.desc": "They fire beams from the glassy, magenta orbs that resemble eyes atop their heads, and they drift in shallow seas. During low tide, they can sometimes be found on beaches, desiccated.",
"cobblemon.species.tentacool.desc": "Тентакул поглощает солнечный свет и преломляет его, используя воду, находящуюся внутри его тела, чтобы преобразовать его в лучевую энергию. Этот покемон стреляет лучами из небольшого круглого органа, расположенного над глазами.",
"cobblemon.species.tentacruel.name": "Тентакрул",
"cobblemon.species.tentacruel.desc": "It has 80 tentacles, each with a venomous tip. These tentacles are also extendible, lengthening when Tentacruel attempts to catch prey. Use caution.",
"cobblemon.species.tentacruel.desc": "У Тентакрула есть щупальца, которые он может свободно удлинять и укорачивать по собственному желанию. Он опутывает добычу своими щупальцами и ослабляет её, впрыскивая в неё сильный яд. Он может поймать до 80 особей одновременно.",
"cobblemon.species.geodude.name": "Геодуд",
"cobblemon.species.geodude.desc": "Makes its home in mountainous regions, using its arms to climb along harsh mountain roads. Can be troublesome—carelessly kicking one will cause it to fly into a rage and chase after you.",
"cobblemon.species.graveler.name": "Гравелер",
@@ -8392,7 +8392,7 @@
"item.cobblemon.sweet_heart": "Сладкое сердце",
"item.cobblemon.sweet_heart.tooltip": "Сладкое шоколадное лакомство",
"item.cobblemon.technical_machine": "Техническое устройство (ТУ)",
"item.cobblemon.blank_tm": "Пустое ТУ",
"item.cobblemon.blank_tm": "Пустая ТМ",
"block.cobblemon.deepslate_crystal_core": "Глубинный сланец с кристальным ядром",
"block.cobblemon.normal_gem_block": "Блок нормального самоцвета",
"block.cobblemon.fire_gem_block": "Блок огненного самоцвета",
@@ -8432,16 +8432,16 @@
"block.cobblemon.fairy_gem_cluster": "Друза волшебного самоцвета",
"cobblemon.item_class.technical_machine": "Техническое устройство",
"block.cobblemon.damaged_monitor": "Повреждённый отслеживатель данных",
"block.cobblemon.tm_machine": "ТУ станок",
"block.cobblemon.tm_machine": "ТМ станок",
"block.cobblemon.disc_shelf": "Полка для дисков",
"cobblemon.fishing_bait_effects.mark_chance.tooltip": "%1$s%% - +%3$s%% шанс метки",
"cobblemon.ui.sort.discovered": "Сортировать по статусу открытия",
"cobblemon.fishing_bait_effects.alpha_chance.tooltip": "%1$s%% - привлекает альфа покемонов",
"cobblemon.fishing_bait_effects.size.tooltip": "%1$s%% - меняет размер на %3$s%%",
"cobblemon.technical_machine_effect_header": "О техническом устройстве:",
"cobblemon.command.technicalmachine.invalid-tm": "Неправильное ТУ",
"cobblemon.command.technicalmachine.invalid-tm": "Неправильная ТМ",
"cobblemon.ui.generic.question_marks": "???",
"cobblemon.container.tm_machine": "ТУ станок",
"cobblemon.container.tm_machine": "ТМ станок",
"cobblemon.ui.edit.habitat": "Редактор среды",
"cobblemon.ui.edit.habitat.mimic_id": "Идентификатор копируемого блока",
"cobblemon.ui.edit.habitat.phase_order": "Фаза порядка: %s",
@@ -8567,20 +8567,20 @@
"advancements.cobblemon.construct_note_block_sequencer.description": "Создайте аудиосеквенсор из нотного блока и полки для дисков",
"advancements.cobblemon.download_porygon": "Технические неполадки",
"advancements.cobblemon.download_porygon.description": "Вы же не будете скачивать Поригона",
"cobblemon.ui.moves.learnset.tm.discovered": "Обнаружен",
"cobblemon.ui.moves.learnset.tm.discovered": "Обнаружена",
"cobblemon.ui.moves.level": "Уровень",
"cobblemon.ui.moves.egg": "Яйцо",
"cobblemon.ui.moves.tm": "ТУ",
"cobblemon.ui.tm_machine.can_learn": "Выучиваемый",
"cobblemon.ui.tm_machine.cannot_learn": "Не выучиваемый",
"cobblemon.ui.tm_machine.learned": "Выучен",
"cobblemon.ui.moves.tm": "ТМ",
"cobblemon.ui.tm_machine.can_learn": "Выучиваемая",
"cobblemon.ui.tm_machine.cannot_learn": "Не выучиваемая",
"cobblemon.ui.tm_machine.learned": "Выучена",
"cobblemon.tms.unknown_move": "Неизвестный приём",
"cobblemon.tms.already_known": "%1$s уже знает %2$s!",
"cobblemon.tms.teach_move": "%1$s выучивает %2$s!",
"cobblemon.tms.cannot_learn": "%1$s не может выучить %2$s!",
"cobblemon.tms.unlock_tm": "Вы разблокировали ТУ для %1$s!",
"cobblemon.tms.new_tms_learned": "Выучены новые ТУ!",
"cobblemon.tms.check_tmm": "Проверьте ТУ станок",
"cobblemon.tms.unlock_tm": "Вы разблокировали ТМ для %1$s!",
"cobblemon.tms.new_tms_learned": "Выучены новые ТМ!",
"cobblemon.tms.check_tmm": "Проверьте ТМ станок",
"cobblemon.ui.party.party_select_title": "Изменить выбор команды",
"cobblemon.ui.party.party_select_description": "Нажмите %1$s/%2$s или удерживайте %3$s и прокрутите.",
"cobblemon.ui.category": "Категория",
@@ -8592,7 +8592,7 @@
"cobblemon.ui.pokedex.filter.seen": "Просмотрен",
"cobblemon.ui.pokedex.filter.unregistered": "Незарегестрирован",
"cobblemon.ui.pokedex.filter.rideable": "Ездовой",
"cobblemon.ui.pokedex.filter.undiscovered_tm_move": "Необнаруженный ТУ приём",
"cobblemon.ui.pokedex.filter.undiscovered_tm_move": "Необнаруженный ТМ приём",
"cobblemon.habitat.abandoned_fortress.name": "Заброшенная крепость",
"cobblemon.habitat.abandoned_village_house.name": "Заброшенный деревенский дом",
"cobblemon.habitat.ancient_wellspring.name": "Древний источник",
@@ -8641,16 +8641,16 @@
"cobblemon.habitat.volcanic_plume.name": "Вулканический шлейф",
"cobblemon.habitat.warped_fungal_dwelling.name": "Искажённое грибное жилище",
"cobblemon.habitat.zen_garden.name": "Сад дзен",
"cobblemon.subtitle.tm.use": "Использовано техническое устройство",
"cobblemon.subtitle.block.tm_machine.burn_loop": "ТУ станок печатает ТУ",
"cobblemon.subtitle.block.tm_machine.start": "ТУ станок начинает печатать ТУ",
"cobblemon.subtitle.block.tm_machine.craft": "ТУ станок заканчивает печатать ТУ",
"cobblemon.subtitle.block.tm_machine.open": "Крышка ТУ станка открывается",
"cobblemon.subtitle.block.tm_machine.close": "Крышка ТУ станка закрывается",
"cobblemon.subtitle.block.tm_machine.tm_insert": "Пустое ТУ вставлено в ТУ станок",
"cobblemon.subtitle.block.tm_machine.tm_retrieve": "ТУ извлечено из ТУ станка",
"cobblemon.subtitle.block.tm_machine.item_insert": "Предмет вставлен в ТУ станок",
"cobblemon.subtitle.block.tm_machine.item_retrieve": "Предмет извлечён из ТУ станка",
"cobblemon.subtitle.tm.use": "Использована техническая машина",
"cobblemon.subtitle.block.tm_machine.burn_loop": "ТМ станок печатает ТМ",
"cobblemon.subtitle.block.tm_machine.start": "ТМ станок начинает печатать ТМ",
"cobblemon.subtitle.block.tm_machine.craft": "ТМ станок заканчивает печатать ТМ",
"cobblemon.subtitle.block.tm_machine.open": "Крышка ТМ станка открывается",
"cobblemon.subtitle.block.tm_machine.close": "Крышка ТМ станка закрывается",
"cobblemon.subtitle.block.tm_machine.tm_insert": "Пустая ТМ вставлена в ТМ станок",
"cobblemon.subtitle.block.tm_machine.tm_retrieve": "ТМ извлечена из ТМ станка",
"cobblemon.subtitle.block.tm_machine.item_insert": "Предмет вставлен в ТМ станок",
"cobblemon.subtitle.block.tm_machine.item_retrieve": "Предмет извлечён из ТМ станка",
"cobblemon.ui.exp.number": "+%1$s §o опыта",
"cobblemon.config.ui.award_experience_to_fainted_pokemon": "Выдавать опыт поверженным покемонам",
"cobblemon.config.ui.award_experience_to_fainted_pokemon.tooltip": "Поверженные покемоны получают опыт за противников, которых они помогли победить до того, как были повержены.",
@@ -8687,9 +8687,9 @@
"cobblemon.config.ui.minimum_riding_scale": "Минимальный модификатор размера покемона для езды",
"cobblemon.config.ui.minimum_riding_scale.tooltip": "Размер, при котором нельзя кататься на покемоне.",
"cobblemon.config.ui.unlock_all_move_dex_moves_by_default": "Разблокировать все приёмы",
"cobblemon.config.ui.unlock_all_move_dex_moves_by_default.tooltip": "Если эта опция включена, ТУ и приёмы, получаемые при повышении уровня, становятся доступными и можно их найти в ТУ машине и в разделе покедекса для приёмов.",
"cobblemon.config.ui.infinite_tm_uses": "Бесконечное использование ТУ",
"cobblemon.config.ui.infinite_tm_uses.tooltip": "ТУ не пропадают после использования.",
"cobblemon.config.ui.unlock_all_move_dex_moves_by_default.tooltip": "Если эта опция включена, ТМ и приёмы, получаемые при повышении уровня, становятся доступными и можно их найти в ТМ машине и в разделе с приёмами в покедексе.",
"cobblemon.config.ui.infinite_tm_uses": "Бесконечное использование ТМ",
"cobblemon.config.ui.infinite_tm_uses.tooltip": "ТМ не пропадают после использования.",
"cobblemon.battle.combine": "Два приёма стали одним! Это совместный приём!",
"cobblemon.battle.waiting": "%1$s ожидает приёма от %2$s...",
"cobblemon.battle.start.alphaboost": "Альфа %1$s наполняется дикой силой! Его сила возрастает!",
@@ -9013,5 +9013,13 @@
"cobblemon.species.growlithe-hisui.desc": "Они патрулируют свою территорию парами. Я полагаю, что наличие магматических пород в шерсти этого вида является результатом вулканической деятельности в его среде обитания.",
"cobblemon.species.arcanine-hisui.desc": "Набрасывается на своих врагов с окутанными ярким пламенем клыками. Несмотря на свои внушительные размеры, он ловко делает манёвры во всех стороны, увлекая противников в обманчиво весёлую погоню, словно танцуя вокруг них.",
"cobblemon.species.alakazam-mega.desc": "Потеряв свои мышцы, истинную мощь Алаказама была высвобождена. Используя свои психические способности он может предвидеть всё.",
"cobblemon.species.machamp-gmax.desc": "Гигантамакс энергия, текущая в его руках, придаёт его ударам силу, сравнимую с взрывом бомбы."
"cobblemon.species.machamp-gmax.desc": "Гигантамакс энергия, текущая в его руках, придаёт его ударам силу, сравнимую с взрывом бомбы.",
"cobblemon.ui.starter.random": "Случайный",
"advancements.cobblemon.obtain_tm": "Покажи, что ты можешь",
"cobblemon.ui.starter.random_description": "Случайный покемон из списка доступных вариантов. Невозможно узнать, кто им будет, пока не выбрана эта опция.",
"cobblemon.config.ui.world_spawning_blocklist": "Список запрещённых появлений на мир",
"cobblemon.config.ui.world_spawning_blocklist.tooltip": "Миры, в которых отключается принцип появления покемонов.",
"advancements.cobblemon.obtain_tm.description": "Найдите или напечатайте ТМ",
"cobblemon.species.victreebel-mega.desc": "В результате Мега эволюции объём кислоты у этого покемона увеличился, заполняя весь его рот. Если он не будет осторожен, кислота переполнится и выльется наружу.",
"cobblemon.ui.pokedex.info.form.victreebel-mega": "Мега Виктрибелл"
}
@@ -7347,5 +7347,6 @@
"cobblemon.mark.ribbon_day_downcast": "Стрічка смутку",
"cobblemon.mark.ribbon_day_downcast.desc": "Стрічка на згадку про почуття смутку, що додали життю барв.",
"cobblemon.mark.ribbon_day_downcast.title": "Приголомшений %1$s",
"cobblemon.mark.ribbon_day_relax": "Стрічка відпочинку"
"cobblemon.mark.ribbon_day_relax": "Стрічка відпочинку",
"cobblemon.mark.ribbon_day_relax.title": "%1$s відпочив"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

@@ -4,32 +4,54 @@
"displayAspects": [],
"conditionAspects": [],
"forms": [
{
"displayForm": "Normal",
"unlockForms": [
"Normal"
]
},
{
"displayForm": "Meteor",
"unlockForms": [
"Meteor"
]
}
],
"variations": [
},
{
"displayName": "cobblemon.pokedex.variation.color",
"icon": "cobblemon:textures/gui/pokedex/variation/color.png",
"aspects": [
"red-core",
"orange-core",
"yellow-core",
"green-core",
"blue-core",
"indigo-core",
"violet-core"
"displayForm": "Red",
"unlockForms": [
"Red"
]
},
{
"displayForm": "Orange",
"unlockForms": [
"Orange"
]
},
{
"displayForm": "Yellow",
"unlockForms": [
"Yellow"
]
},
{
"displayForm": "Green",
"unlockForms": [
"Green"
]
},
{
"displayForm": "Blue",
"unlockForms": [
"Blue"
]
},
{
"displayForm": "Indigo",
"unlockForms": [
"Indigo"
]
},
{
"displayForm": "Violet",
"unlockForms": [
"Violet"
]
}
]
],
"variations": []
}
@@ -4,12 +4,6 @@
"displayAspects": [],
"conditionAspects": [],
"forms": [
{
"displayForm": "Normal",
"unlockForms": [
"Normal"
]
},
{
"displayForm": "Icy Snow",
"unlockForms": [
@@ -0,0 +1,35 @@
{
"enabled": true,
"neededInstalledMods": [],
"neededUninstalledMods": [],
"spawns": [
{
"presets": [
"water"
],
"type": "empty",
"spawnablePositionType": "surface",
"bucket": "common",
"weight": 200.0,
"condition": {
"biomes": [
"#cobblemon:is_ocean"
]
},
"anticondition": {
"biomes": [
"#cobblemon:is_deep_ocean"
]
}
},
{
"presets": [
"lava"
],
"type": "empty",
"spawnablePositionType": "surface",
"bucket": "common",
"weight": 200.0
}
]
}
@@ -37,7 +37,9 @@
},
"anticondition": {
"biomes": [
"#cobblemon:is_spooky"
"#cobblemon:is_beach",
"#cobblemon:is_spooky",
"#cobblemon:is_tropical_island"
]
}
},
@@ -37,7 +37,9 @@
},
"anticondition": {
"biomes": [
"#cobblemon:is_spooky"
"#cobblemon:is_beach",
"#cobblemon:is_spooky",
"#cobblemon:is_tropical_island"
]
}
},
@@ -27,6 +27,12 @@
"#cobblemon:is_jungle",
"#cobblemon:is_savanna"
]
},
"anticondition": {
"biomes": [
"#cobblemon:is_beach",
"#cobblemon:is_tropical_island"
]
}
},
{
@@ -27,6 +27,12 @@
"#cobblemon:is_jungle",
"#cobblemon:is_savanna"
]
},
"anticondition": {
"biomes": [
"#cobblemon:is_beach",
"#cobblemon:is_tropical_island"
]
}
},
{
@@ -28,6 +28,11 @@
"#cobblemon:is_badlands",
"#cobblemon:is_desert"
]
},
"anticondition": {
"biomes": [
"#cobblemon:is_volcanic"
]
}
},
{
@@ -76,7 +81,8 @@
},
"condition": {
"biomes": [
"#cobblemon:nether/is_desert"
"#cobblemon:nether/is_desert",
"#cobblemon:nether/is_soul_fire"
]
}
}
@@ -28,6 +28,11 @@
"#cobblemon:is_badlands",
"#cobblemon:is_desert"
]
},
"anticondition": {
"biomes": [
"#cobblemon:is_volcanic"
]
}
},
{
@@ -76,7 +81,8 @@
},
"condition": {
"biomes": [
"#cobblemon:nether/is_desert"
"#cobblemon:nether/is_desert",
"#cobblemon:nether/is_soul_fire"
]
}
}
@@ -35,6 +35,12 @@
"biomes": [
"#cobblemon:is_overworld"
]
},
"anticondition": {
"biomes": [
"#cobblemon:is_magical",
"#cobblemon:is_spooky"
]
}
},
{
@@ -71,6 +77,12 @@
"structures": [
"#minecraft:village"
]
},
"anticondition": {
"biomes": [
"#cobblemon:is_magical",
"#cobblemon:is_spooky"
]
}
},
{
@@ -29,6 +29,7 @@
},
"anticondition": {
"biomes": [
"#cobblemon:is_taiga",
"#cobblemon:is_spooky"
]
}
@@ -29,6 +29,7 @@
},
"anticondition": {
"biomes": [
"#cobblemon:is_taiga",
"#cobblemon:is_spooky"
]
}
@@ -29,6 +29,7 @@
},
"anticondition": {
"biomes": [
"#cobblemon:is_taiga",
"#cobblemon:is_spooky"
]
}
@@ -30,7 +30,9 @@
},
"anticondition": {
"biomes": [
"#cobblemon:is_spooky"
"#cobblemon:is_beach",
"#cobblemon:is_spooky",
"#cobblemon:is_tropical_island"
]
}
},
@@ -38,7 +38,7 @@
}
},
{
"id": "rowlet-hisui-3",
"id": "rowlet-hisui-2",
"pokemon": "rowlet region_bias=hisui",
"presets": [
"natural",
@@ -32,7 +32,7 @@
}
},
{
"id": "dartrix-hisui-3",
"id": "dartrix-hisui-2",
"pokemon": "dartrix region_bias=hisui",
"presets": [
"natural",
@@ -32,7 +32,7 @@
}
},
{
"id": "decidueye-hisuian-3",
"id": "decidueye-hisuian-2",
"pokemon": "decidueye hisuian",
"presets": [
"natural",
@@ -217,27 +217,6 @@
{
"variant": "level",
"minLevel": 28
},
{
"variant": "negate",
"inner": {
"variant": "biome",
"biomeCondition": "#cobblemon:is_volcanic"
}
},
{
"variant": "negate",
"inner": {
"variant": "biome",
"biomeCondition": "#cobblemon:is_beach"
}
},
{
"variant": "negate",
"inner": {
"variant": "biome",
"biomeCondition": "#minecraft:is_nether"
}
}
]
},
@@ -261,20 +240,6 @@
{
"variant": "biome",
"biomeCondition": "#cobblemon:evolution/regional/cubone_alolabiome"
},
{
"variant": "negate",
"inner": {
"variant": "biome",
"biomeCondition": "#cobblemon:is_arid"
}
},
{
"variant": "negate",
"inner": {
"variant": "biome",
"biomeCondition": "#minecraft:is_nether"
}
}
]
},
@@ -342,21 +307,7 @@
"variant": "negate",
"inner": {
"variant": "biome",
"biomeCondition": "#cobblemon:is_volcanic"
}
},
{
"variant": "negate",
"inner": {
"variant": "biome",
"biomeCondition": "#cobblemon:is_beach"
}
},
{
"variant": "negate",
"inner": {
"variant": "biome",
"biomeCondition": "#minecraft:is_nether"
"biomeCondition": "#cobblemon:evolution/regional/cubone_alolabiome"
}
}
]
@@ -377,20 +328,6 @@
{
"variant": "time_range",
"range": "night"
},
{
"variant": "negate",
"inner": {
"variant": "biome",
"biomeCondition": "#cobblemon:is_arid"
}
},
{
"variant": "negate",
"inner": {
"variant": "biome",
"biomeCondition": "#minecraft:is_nether"
}
}
]
},
@@ -453,4 +390,4 @@
"willDefendOwner": true
}
}
}
}
@@ -165,17 +165,6 @@
"special:sing"
],
"evolutions": [
{
"id": "eevee_jolteon",
"variant": "item_interact",
"result": "jolteon",
"consumeHeldItem": false,
"learnableMoves": [
"thundershock"
],
"requirements": [],
"requiredContext": "cobblemon:thunder_stone"
},
{
"id": "eevee_vaporeon",
"variant": "item_interact",
@@ -187,6 +176,17 @@
"requirements": [],
"requiredContext": "cobblemon:water_stone"
},
{
"id": "eevee_jolteon",
"variant": "item_interact",
"result": "jolteon",
"consumeHeldItem": false,
"learnableMoves": [
"thundershock"
],
"requirements": [],
"requiredContext": "cobblemon:thunder_stone"
},
{
"id": "eevee_flareon",
"variant": "item_interact",
@@ -237,26 +237,6 @@
}
]
},
{
"id": "eevee_sylveon",
"variant": "level_up",
"result": "sylveon",
"consumeHeldItem": false,
"learnableMoves": [
"fairywind",
"disarmingvoice"
],
"requirements": [
{
"variant": "friendship",
"amount": 160
},
{
"variant": "has_move_type",
"type": "fairy"
}
]
},
{
"id": "eevee_leafeon",
"variant": "item_interact",
@@ -278,6 +258,26 @@
],
"requirements": [],
"requiredContext": "cobblemon:ice_stone"
},
{
"id": "eevee_sylveon",
"variant": "level_up",
"result": "sylveon",
"consumeHeldItem": false,
"learnableMoves": [
"fairywind",
"disarmingvoice"
],
"requirements": [
{
"variant": "friendship",
"amount": 160
},
{
"variant": "has_move_type",
"type": "fairy"
}
]
}
],
"forms": [
@@ -233,22 +233,6 @@
"learnableMoves": [
"stomp"
],
"requirements": [
{
"variant": "negate",
"inner": {
"variant": "biome",
"biomeCondition": "#cobblemon:is_beach"
}
},
{
"variant": "negate",
"inner": {
"variant": "biome",
"biomeCondition": "#cobblemon:is_tropical_island"
}
}
],
"requiredContext": "cobblemon:leaf_stone"
},
{
@@ -301,6 +285,13 @@
{
"variant": "biome",
"biomeCondition": "#cobblemon:evolution/regional/exeggcute_kantobiome"
},
{
"variant": "negate",
"inner": {
"variant": "biome",
"biomeCondition": "#cobblemon:evolution/regional/exeggcute_alolabiome"
}
}
],
"requiredContext": "cobblemon:leaf_stone"
@@ -313,25 +304,9 @@
"learnableMoves": [
"dragonhammer"
],
"requirements": [
{
"variant": "negate",
"inner": {
"variant": "biome",
"biomeCondition": "#cobblemon:is_jungle"
}
},
{
"variant": "negate",
"inner": {
"variant": "biome",
"biomeCondition": "#cobblemon:is_savanna"
}
}
],
"requiredContext": "cobblemon:leaf_stone"
}
]
}
]
}
}
@@ -217,20 +217,6 @@
{
"variant": "level",
"minLevel": 35
},
{
"variant": "negate",
"inner": {
"variant": "biome",
"biomeCondition": "#cobblemon:is_magical"
}
},
{
"variant": "negate",
"inner": {
"variant": "biome",
"biomeCondition": "#cobblemon:is_spooky"
}
}
]
},
@@ -290,6 +276,13 @@
{
"variant": "biome",
"biomeCondition": "#cobblemon:evolution/regional/koffing_kantobiome"
},
{
"variant": "negate",
"inner": {
"variant": "biome",
"biomeCondition": "#cobblemon:evolution/regional/koffing_galarbiome"
}
}
]
},
@@ -305,17 +298,10 @@
{
"variant": "level",
"minLevel": 35
},
{
"variant": "negate",
"inner": {
"variant": "biome",
"biomeCondition": "#cobblemon:nether/is_toxic"
}
}
]
}
]
}
]
}
}
@@ -297,36 +297,6 @@
"learnableMoves": [
"thunderpunch"
],
"requirements": [
{
"variant": "negate",
"inner": {
"variant": "biome",
"biomeCondition": "#cobblemon:is_beach"
}
},
{
"variant": "negate",
"inner": {
"variant": "biome",
"biomeCondition": "#cobblemon:is_tropical_island"
}
},
{
"variant": "negate",
"inner": {
"variant": "biome",
"biomeCondition": "aether:skyroot_forest"
}
},
{
"variant": "negate",
"inner": {
"variant": "biome",
"biomeCondition": "aether:skyroot_woodland"
}
}
],
"requiredContext": "cobblemon:thunder_stone"
},
{
@@ -341,13 +311,6 @@
{
"variant": "biome",
"biomeCondition": "#cobblemon:evolution/regional/pikachu_alolabiome"
},
{
"variant": "negate",
"inner": {
"variant": "biome",
"biomeCondition": "#cobblemon:is_forest"
}
}
],
"requiredContext": "cobblemon:thunder_stone"
@@ -424,37 +387,16 @@
"thunderpunch"
],
"requirements": [
{
"variant": "negate",
"inner": {
"variant": "biome",
"biomeCondition": "#cobblemon:is_beach"
}
},
{
"variant": "negate",
"inner": {
"variant": "biome",
"biomeCondition": "#cobblemon:is_tropical_island"
}
},
{
"variant": "negate",
"inner": {
"variant": "biome",
"biomeCondition": "aether:skyroot_forest"
}
},
{
"variant": "negate",
"inner": {
"variant": "biome",
"biomeCondition": "aether:skyroot_woodland"
}
},
{
"variant": "biome",
"biomeCondition": "#cobblemon:evolution/regional/pikachu_kantobiome"
},
{
"variant": "negate",
"inner": {
"variant": "biome",
"biomeCondition": "#cobblemon:evolution/regional/pikachu_alolabiome"
}
}
],
"requiredContext": "cobblemon:thunder_stone"
@@ -467,18 +409,9 @@
"learnableMoves": [
"psychic"
],
"requirements": [
{
"variant": "negate",
"inner": {
"variant": "biome",
"biomeCondition": "#cobblemon:is_forest"
}
}
],
"requiredContext": "cobblemon:thunder_stone"
}
]
}
]
}
}
@@ -44,8 +44,8 @@
"baseFriendship": 50,
"baseScale": 1,
"hitbox": {
"width": 0.6,
"height": 1,
"width": 0.65,
"height": 1.35,
"fixed": false
},
"behaviour": {
@@ -281,6 +281,13 @@
{
"variant": "biome",
"biomeCondition": "#cobblemon:evolution/regional/quilava_johtobiome"
},
{
"variant": "negate",
"inner": {
"variant": "biome",
"biomeCondition": "#cobblemon:evolution/regional/quilava_hisuibiome"
}
}
]
},
@@ -302,4 +309,4 @@
]
}
]
}
}
@@ -1,4 +1,5 @@
{
"implemented": true,
"nationalPokedexNumber": 460,
"name": "Abomasnow",
"primaryType": "grass",

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