Compare commits

...
Author SHA1 Message Date
MeAlam1 433a66124b Fucking JavaDocs 2026-09-27 18:05:15 +02:00
MeAlam1 4209ca3845 License 2026-09-27 17:44:25 +02:00
MeAlam1 6edd81c0f5 I think all weird Merge Conflicts are gone now 2026-09-27 17:19:57 +02:00
MeAlam1 7939b4ca71 Weird formatting 2026-09-27 17:14:21 +02:00
MeAlam1 0cc22b6675 Fixed some more weird MR Conflict inconsistancies 2026-09-27 17:05:08 +02:00
MeAlam1 0405eea167 Fixing Merge Conflicts 2026-09-27 16:56:06 +02:00
MeAlam1 17d07774d2 merge conflicts 2026-09-27 16:51:48 +02:00
MeAlam1 d71373c237 Merge branch 'main' into hello_world-but_worse 2026-09-27 16:41:23 +02:00
Polymeta a0e732dc6e Merge branch 'perf/build-speed' into 'main'
Speed up the Gradle build

See merge request cable-mc/cobblemon!2429
2026-09-24 14:35:25 +00:00
terrainwax ea20c2aa4c CI: don't upload dev-shadow jars 2026-09-24 10:43:07 +02:00
terrainwax da5272c473 Speed up the Gradle build
- Exclude assets/data (~250MB) from sources jars
- Limit licenser to .kt/.java so it doesn't scan ~29k resource files
- Enable the Gradle daemon and build cache
- The versioning plugin runs a full JGit status (~28s on this repo) in every project replace by ou own helper that use git local or CI env variable
2026-09-24 10:04:35 +02:00
Polymeta a785e574e0 Merge branch 'feature/data-registries' into 'main'
26.1.2 backport of dataregistry

See merge request cable-mc/cobblemon!2285
2026-09-22 21:08:14 +00:00
Polymeta 4b236b0268 Merge branch 'feature/remember-your-tm' into 'main'
load features a lot sooner to avoid form switching

Closes #2085

See merge request cable-mc/cobblemon!2406
2026-09-22 21:07:44 +00:00
Polymeta 1cd86ae5d6 small fixes
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-22 16:43:33 +01:00
Polymeta b7489a0b14 tidy up changelog
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-22 15:32:25 +01:00
Polymeta f7554425dc Merge branch 'tm-shelf-mipmap-correction' into 'main'
Fix mip map of TM shelf disc textures to be 16x16

Closes #2081

See merge request cable-mc/cobblemon!2398
2026-09-22 14:26:41 +00:00
Polymeta 858d2e908a I tidied up too well
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-21 22:12:18 +01:00
Polymeta b61e0b0c82 tidy up changelog
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-21 22:11:43 +01:00
Polymeta 4d9a6e71c9 use correct texture for upgrade and dubious disc
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-21 22:07:52 +01:00
Plastered_Crab a0010de504 Merge remote-tracking branch 'origin/tm-shelf-mipmap-correction' into tm-shelf-mipmap-correction
# Conflicts:
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/dubious_disc.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_11.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_13.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_5.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_blocks.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_cat.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_chirp.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_creator.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_creator_music_box.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_far.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_mall.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_mellohi.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_otherside.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_pigstep.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_precipice.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_relic.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_stal.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_strad.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_unknown.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_wait.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_ward.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/technical_machine.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/technical_machine_base.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/technical_machine_overlay.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/upgrade.png
2026-09-21 10:34:36 -07:00
Plastered_Crab cb73a30e23 changeloguwu 2026-09-21 10:18:50 -07:00
Polymeta 9a7536b068 you were not supposed to be there
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-21 14:42:23 +01:00
Polymeta bd973e702e per type texture for disc shelf tms
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-21 14:40:24 +01:00
Plastered_Crab ede1851d34 Fix mip map of TM shelf disc textures to be 16x16 2026-09-21 14:28:14 +01:00
Polymeta cdb8ee0d8e [skip-ci] tidy up changelog
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-21 12:07:55 +01:00
Polymeta e7d86a382c Merge branch 'dex_molang' into 'main'
Molang: Individual Dex molang

See merge request cable-mc/cobblemon!2273
2026-09-21 09:16:10 +00:00
Konoha D eb911df830 Molang: Individual Dex molang 2026-09-21 09:16:10 +00:00
Polymeta 1c30acc53c Merge branch 'implement_pokemon' into 'main'
implement_pokemon

See merge request cable-mc/cobblemon!2417
2026-09-21 09:14:23 +00:00
Polymeta 32b0699235 load features a lot sooner to avoid form switching
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-21 10:13:59 +01:00
Polymeta e9c6580230 fix merge conflicts
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-21 10:13:27 +01:00
Polymeta f893eb48fd fix some errors I forgot about
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-21 09:38:51 +01:00
Polymeta 232e2f18bf MR feedback
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-21 09:38:51 +01:00
Polymeta c266c99710 26.1.2 backport of dataregistry
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-21 09:38:49 +01:00
Polymeta 2a0e8f6d47 [skip-ci] tidy up changelog
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-21 09:32:44 +01:00
Polymeta e6bd8ec529 Merge branch 'neoforge-reload-fix' into 'main'
Neoforge resource fix

See merge request cable-mc/cobblemon!2333
2026-09-21 08:30:54 +00:00
Crater 911a95e873 Neoforge resource fix 2026-09-21 08:30:53 +00:00
Polymeta 447cfc2ae5 Merge branch 'useful-misc-functions' into 'main'
Smoother ride rotations.

See merge request cable-mc/cobblemon!2407
2026-09-21 08:28:50 +00:00
Polymeta 8dfeb0d260 Merge branch 'no-glass-bottles-in-those' into 'main'
Fixed Mochis and Feathers dropping glass bottles when they shouldn't

See merge request cable-mc/cobblemon!2425
2026-09-21 08:24:28 +00:00
Polymeta caa7b19833 Merge branch 'Snowy-Burrow-Spawns-Fix' into 'main'
Fix Snowy burrows spawns and some floating water in one difting iceberg

See merge request cable-mc/cobblemon!2423
2026-09-21 08:23:17 +00:00
Polymeta 64dd8ae193 fix license header
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-21 09:17:44 +01:00
Polymeta 156ee6f290 new negate requirement
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-21 08:23:04 +01:00
Gertimoshka fe433d1417 Fixed Mochis and Feathers dropping glass bottles when they shouldn't 2026-09-20 20:10:25 +02:00
Hiroku 0dc662d295 Merge branch 'starter-screen-fixes' into 'main'
Fixes minor but annoying starter screen fixes

See merge request cable-mc/cobblemon!2424
2026-09-20 09:33:15 +00:00
Gertimoshka 081e370bfd User friendlier changelog 2026-09-20 11:13:08 +02:00
Gertimoshka 3ef0567287 simplifications 2026-09-20 11:05:26 +02:00
Gertimoshka 3cad9e5481 Forgor the Changelog 2026-09-20 11:01:52 +02:00
Gertimoshka dc8b7908be Fixed impropper rendering culling of entries, and random starter not being part of height calculation of the category 2026-09-20 10:52:15 +02:00
Slippy 85085a1eca Decaying Crypt tombstones now will generate with TMs in the gidled chest 2026-09-19 22:40:20 -05:00
Slippy bff59dee6e Fix Snowy burrows spawns and some floating water in one difting iceberg 2026-09-19 22:01:17 -05:00
Hiroku bb5828531a Merge branch 'type-gem-fix' into 'main'
Fix typo and update changelog.

See merge request cable-mc/cobblemon!2422
2026-09-20 02:29:36 +00:00
Deltric cd08e98c28 Fix typo and update changelog. 2026-09-19 21:09:32 -05:00
Jerzykv 331f7ca425 Implemented Burmy, Wormadam and Mothim 2026-09-18 07:15:33 +02:00
RedGenesectNinja 71c9ac0ff0 Updated changelog 2026-09-17 15:15:20 -05:00
GenotypeXD 1404cc0ab5 Sun Lizards Implementation Tags 2026-09-17 15:11:31 -05:00
GenotypeXD 6ca908a91d Implemented Helioptile Line 2026-09-17 13:05:12 -05:00
Jerzykv 0948b8da9a Fixed Dragonair not using its idle correctly 2026-09-17 19:17:29 +02:00
Jerzykv 22e04a53b3 changelog 2026-09-17 18:39:10 +02:00
Jerzykv df3217b41b Fixed Bramblin always sleeping 2026-09-17 18:38:48 +02:00
Jerzykv 9ff15a1913 Flygon poser fix 2026-09-17 06:43:03 +02:00
RedGenesectNinja ca58801794 Implemented Tympole, Palpitoad and Seismitoad 2026-09-15 23:36:40 -05:00
Instrafe 38fa5e2628 Updated Changelog 2026-09-15 16:46:25 -04:00
Instrafe fae1ae7124 Implemented Absol's remodel 2026-09-15 16:45:59 -04:00
Jerzykv 3ec5f76c73 Implemented Deerling and Sawsbuck remodels 2026-09-15 14:15:37 +02:00
ewwww 5bb8aaba55 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-09-15 05:05:08 -04:00
ewwww 23ba41e981 wiglett + wugtrio underwater tweaks 2026-09-15 05:04:40 -04:00
Instrafe 29b049452b Updated Changelog 2026-09-14 19:24:03 -04:00
Instrafe 0b0f665804 Glimmet and Glimmora now have implemented remodels 2026-09-14 19:23:30 -04:00
Kenji_64 5d53fba2ce Fixed Rockruff line missing blinks 2026-09-14 20:47:40 +02:00
FrankTheFarmer 14cc851562 Tweaked Mimikyus poser. Hopefully fixes an issue nobody can replicate 😭 2026-09-14 09:46:55 +10:00
FrankTheFarmer 7223d1fd68 Merge branch 'main' into implement_pokemon 2026-09-14 09:21:43 +10:00
Instrafe 75f52e4cf4 Updated Changelog 2026-09-13 11:10:48 -04:00
Instrafe 55446dd4b1 Implemented remodels for Binacle and Barbaracle 2026-09-13 11:08:58 -04:00
ewwww 8b825d558f cacnea & cacturne remodels + animations 2026-09-13 06:28:57 -04:00
jackowes 9a7f97f11f changelog 2026-09-12 21:52:56 -05:00
jackowes 95b8d62e4a Fix issue with quat spring and replace orientation controller tick slerp with quat spring 2026-09-12 21:45:26 -05:00
Plastered_Crab 4354c3fb03 Fix mip map of TM shelf disc textures to be 16x16 2026-09-10 11:56:30 -07:00
Kenji_64 f25a676bd5 Changelog 2026-09-10 13:26:54 +02:00
Kenji_64 a9906762a0 Fixed head_ai and removed battle condition on Lycanroc (all forms) 2026-09-10 13:23:03 +02:00
Kenji_64 7b5b88eca2 Added head movement to Archaludon and added missing sleep poses to Duraludon line 2026-09-10 13:22:34 +02:00
Kenji_64 291682ba52 Added Diglett line faints (which were in the files, but not implemented) 2026-09-10 12:50:56 +02:00
Kenji_64 546d403b45 Fixed Kingambit waddling its legs around when moving 2026-09-10 12:50:22 +02:00
FrankTheFarmer c9f40e32a1 Changedog 2026-09-09 20:42:39 +10:00
FrankTheFarmer d642b8dd07 Candle dog's dad 🪦 🐶 2026-09-09 20:41:42 +10:00
FrankTheFarmer 549a11116e Candle dog. Dog with the candle. We put candle on him 🕯🐶 2026-09-09 20:39:45 +10:00
Polymeta b5468a4a4f Merge branch 'main' into implement_pokemon 2026-09-09 09:06:51 +01:00
ewwww c5d278ccb7 spheal. 2026-09-09 03:42:50 -04:00
ewwww bf5e15de45 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-09-09 03:40:21 -04:00
Jerzykv f4af417267 Fixed Alolan Raticate's cry 2026-09-09 03:40:14 -04:00
FrankTheFarmer 69c04acaeb ⚡ Vikavolt 2026-09-09 03:40:14 -04:00
FrankTheFarmer 83a6bb188f 🚎Charjabug💖 2026-09-09 03:40:14 -04:00
FrankTheFarmer 494a8b5e91 🐛Grubbin 2026-09-09 03:40:14 -04:00
FrankTheFarmer c8021f2f08 Im putting bugs into cobblemon changelog 2026-09-09 03:40:14 -04:00
Jerzykv 8cdf3af047 Ampharos now has eye locators 2026-09-09 03:40:14 -04:00
ewwww c90a24e1ab switcheroo part two 2026-09-09 03:40:14 -04:00
FrankTheFarmer 83e3cfc34e Placeholder idle animation for snover 2026-09-09 03:40:14 -04:00
FrankTheFarmer 133dd4e5b7 🏔 Added snover line 🌲 2026-09-09 03:40:13 -04:00
FrankTheFarmer a8cca14b86 Obamasnow 2026-09-09 03:40:13 -04:00
FrankTheFarmer 9b0f356fa4 Donald Snover 2026-09-09 03:40:13 -04:00
FrankTheFarmer f2b0a102b6 What if we started implementing pokemon rn rn 2026-09-09 03:40:13 -04:00
FrankTheFarmer a7d01b230a Merge branch 'main' into implement_pokemon 2026-09-09 16:04:20 +10:00
jackowes 2a7db4b9b8 bring in util functions 2026-09-08 20:15:54 -05:00
Jerzykv 77e5f2501d Fixed Alolan Raticate's cry 2026-09-08 19:56:35 +02:00
MeAlam1 fc2335f5d5 Fixed Biomes 2026-09-08 14:04:46 +02:00
FrankTheFarmer e6513717bd ⚡ Vikavolt 2026-09-08 21:34:26 +10:00
FrankTheFarmer 0e0c98f693 🚎Charjabug💖 2026-09-08 21:32:56 +10:00
FrankTheFarmer 528bfa4e22 🐛Grubbin 2026-09-08 21:32:09 +10:00
FrankTheFarmer 83c87779d7 Im putting bugs into cobblemon changelog 2026-09-08 21:30:42 +10:00
FrankTheFarmer dc33235144 Merge branch 'refs/heads/main' into implement_pokemon 2026-09-08 21:29:01 +10:00
MeAlam1 7d8329b311 Update from Main 2026-09-08 13:19:56 +02:00
MeAlam1 9b46757d14 Merge branch 'main' into hello_world-but_worse 2026-09-08 13:19:28 +02:00
FrankTheFarmer 935c658c5f Merge branch 'main' into implement_pokemon 2026-09-08 17:59:22 +10:00
Jerzykv 0426f4fbf9 Ampharos now has eye locators 2026-09-07 08:31:09 +02:00
MeAlam1 fe5970180d Fixed the merge confilicts, Biomes are still broken tho 2026-09-06 15:52:50 +02:00
MeAlam1 89b81d6882 Update from main (Merge Conflicts will be fixed in the upcoming commits) 2026-09-06 15:01:08 +02:00
ewwww 46e771a062 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-09-05 22:07:28 -04:00
ewwww 4c0095844b Merge remote-tracking branch 'origin/implement_pokemon_2' into implement_pokemon 2026-09-05 22:06:39 -04:00
ewwww 10a2ef27d6 switcheroo part two 2026-08-24 15:36:02 -04:00
FrankTheFarmer 3c7637515c Placeholder idle animation for snover 2026-08-10 13:03:00 +10:00
FrankTheFarmer 0a5abdb331 🏔 Added snover line 🌲 2026-08-09 11:15:45 +10:00
FrankTheFarmer 4ed7413dd2 Obamasnow 2026-08-09 11:13:17 +10:00
FrankTheFarmer 40b66c24fc Donald Snover 2026-08-09 11:12:56 +10:00
FrankTheFarmer 20a35de3d8 What if we started implementing pokemon rn rn 2026-08-08 14:58:20 +10:00
MeAlam1 2eb6042d06 Created a simple way to add Custom Biome Data Parsers 2026-04-16 00:04:06 +02:00
MeAlam1 5ec2fc7937 Revert Change
https://minecraft.wiki/w/World_generation
2026-04-15 23:46:09 +02:00
MeAlam1 f57093b835 DATA DRIVEN BIOMES WORK + COMPATIBILITY 2026-04-14 19:04:58 +02:00
MeAlam1 03bb782dc4 Big Boy cleanup 2026-04-14 18:14:11 +02:00
Aram d296fa81dc Cleanup 2026-04-13 15:09:20 +02:00
Aram 1621bada06 Fixed JSON Loading 2026-04-10 15:56:58 +02:00
Aram 873ccc8109 JSON Loading Works!!!! 2026-04-10 15:01:06 +02:00
Aram eb4257cc26 Biome Registry Order is broken 2026-04-10 14:28:09 +02:00
Aram dfcb988f1e Got some issues with JSON loading, but ill work on it when i got time 2026-04-09 16:59:10 +02:00
Aram 2ca2a77333 Cobblemon Biomes are now accessible in World Generation 2026-04-09 15:39:43 +02:00
MeAlam1 b468e4eb6c Forgor 2026-04-09 02:01:55 +02:00
MeAlam1 c39397413f Final Renaming of Methods 2026-04-09 01:54:31 +02:00
MeAlam1 b3fe03194a Cleanup of all the Code 2026-04-09 01:52:33 +02:00
MeAlam1 d2eafe7b41 Added all Mixin Docs 2026-04-09 01:09:41 +02:00
MeAlam1 7f78c15f6b Hello World be Worlding 2026-04-08 23:02:29 +02:00
MeAlam1 0cd1f8a2fd Commit 2026-04-08 16:10:51 +02:00
MeAlam1 d6c2da3d90 Update CobblemonBiomes.kt 2026-04-07 22:10:23 +02:00
MeAlam1 ba84883eae Evolved to Registry is already frozen ERROR 2026-04-06 23:18:06 +02:00
MeAlam1 fcfdfb270d Slowly Start working on Biome Registry 2026-03-26 14:13:46 +01:00
MeAlam1 96d1d9d552 Started implementing custom biomes 2026-03-26 00:36:33 +01:00
MeAlam1 0d1de548a0 Test Hello *World* 2026-03-25 23:06:41 +01:00
425 changed files with 43950 additions and 5236 deletions

No files matched your search

+3
View File
@@ -157,6 +157,9 @@ build:
paths:
- neoforge/build/libs
- fabric/build/libs
exclude:
- neoforge/build/libs/*-dev-shadow.jar
- fabric/build/libs/*-dev-shadow.jar
publish:
stage: deploy
+59 -6
View File
@@ -7,38 +7,91 @@
- Iron Ball, Metal Coat, Throat Spray, 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:
- 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.
### New rideable Pokémon
### Pokémon Added
#### Generation 4
- Snover
- Abomasnow
- Burmy
- Wormadam
- Mothim
#### Regional Forms
#### Generation 5
- Tympole
- Palpitoad
- Seismitoad
#### Generation 6
- Helioptile
- Heliolisk
#### Generation 7
- Grubbin
- Charjabug
- Vikavolt
#### Generation 9
- Greavard
- Houndstone
### Model updates for the following Pokémon
- Cacnea
- Cacturne
- Binacle
- Barbaracle
- Glimmet
- Glimmora
- Deerling
- Sawsbuck (All forms)
- Absol
### Animation updates for the following Pokémon
#### Added sounds to the following moves
- Cacnea
- Cacturne
### Changes
- 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
- Make ride rotations less jittery in multiplayer.
- Changes tm shelf discs to use textures instead of tinting and overlays to line up with item display
- Blank TM can now be inserted into the Disc Shelf
### Fixes
- Correctly grants "They're Minerals!" advancement when planting a type gem
- 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
- Various Structure fixes
- Fixed shiny pokemon under certain circumstances forgetting taught TMs
### Developer
- 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
### 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.
### Particles
-9
View File
@@ -1,9 +0,0 @@
- Some kind of particle effect for them? they're meant to have glowing eyes, so maybe some red particles on the eye locator
--- global posers? stacked posers?
--- red texture seems to be what some people wanted.
/- shoulder mounts part of it
- omni boost
- roar when player comes within reach, adjust levels
- Riding settings to control what the min scale is for a pokemon to be ridable.
+5 -2
View File
@@ -1,3 +1,6 @@
import utilities.gitBranch
import utilities.gitShortCommit
plugins {
id("cobblemon.root-conventions")
}
@@ -6,6 +9,6 @@ version = "${project.property("mod_version")}+${project.property("mc_version")}"
val isSnapshot = project.property("snapshot")?.equals("true") == true
if (isSnapshot) {
val fixedBranchName = versioning.info.branch.substringAfter("/")
version = "$version-${fixedBranchName}-${versioning.info.build}"
val fixedBranchName = gitBranch().substringAfter("/")
version = "$version-${fixedBranchName}-${gitShortCommit()}"
}
+15 -6
View File
@@ -1,9 +1,8 @@
import utilities.isSnapshot
import utilities.version
import java.time.OffsetDateTime
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
import utilities.gitBranch
import utilities.gitCommit
/*
*
@@ -72,6 +71,14 @@ tasks.withType<Test> {
}
}
// Git info lives in its own output dir (not processResources) so a new commit only rewrites this one file.
val writeGitInfo by tasks.registering(WriteProperties::class) {
destinationFile = layout.buildDirectory.file("generated/gitInfo/cobblemon-git.properties")
property("commit", gitCommit())
property("branch", gitBranch())
}
sourceSets.main { output.dir(mapOf("builtBy" to writeGitInfo), layout.buildDirectory.dir("generated/gitInfo")) }
tasks.processResources {
exclude("data/**/README.md")
}
@@ -98,11 +105,13 @@ sourceSets {
property("modid", "cobblemon")
property("version", project.version())
property("isSnapshot", if(rootProject.isSnapshot()) "true" else "false")
property("gitCommit", versioning.info.commit)
property("branch", versioning.info.branch)
System.getProperty("buildNumber")?.let { property("buildNumber", it) }
property("timestamp", OffsetDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ofPattern("MM/dd/yyyy hh:mm:ss")) + " UTC")
}
}
}
}
// The platform jars are built from :common:jar, so the remapped common jars are only needed by `publish`
tasks.assemble {
setDependsOn(dependsOn.filterNot { (it as? TaskProvider<*>)?.name in setOf("remapJar", "remapSourcesJar") })
}
@@ -0,0 +1,18 @@
/*
* 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.duck.biome;
import net.minecraft.core.Holder;
import net.minecraft.world.level.biome.Biome;
import java.util.List;
public interface BiomeSourceDuck {
void addPossibleBiomes(List<Holder<Biome>> biomes);
}
@@ -8,14 +8,21 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.duck.MinecraftServerDuck;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.core.LayeredRegistryAccess;
import net.minecraft.core.RegistryAccess;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.RegistryLayer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level;
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 java.util.Map;
import java.util.Optional;
@@ -23,18 +30,41 @@ import java.util.Optional;
@Mixin(MinecraftServer.class)
public class MinecraftServerMixin implements MinecraftServerDuck {
@Shadow
@Final
private Map<ResourceKey<Level>, ServerLevel> levels;
@Shadow
@Final
private Map<ResourceKey<Level>, ServerLevel> levels;
@Override
public Optional<ResourceKey<Level>> cobblemon$getLevelKey(final Level level) {
return this.levels.entrySet()
.stream()
// Reference equality is intended here, the level we're handed is the same one as in the
// map so we don't need to check value semantics.
.filter(entry -> entry.getValue() == level)
.map(Map.Entry::getKey)
.findFirst();
}
@Override
public Optional<ResourceKey<Level>> cobblemon$getLevelKey(final Level level) {
return this.levels.entrySet()
.stream()
// Reference equality is intended here, the level we're handed is the same one as in the
// map so we don't need to check value semantics.
.filter(entry -> entry.getValue() == level)
.map(Map.Entry::getKey)
.findFirst();
}
/**
* Wraps the earliest {@code compositeAccess()} call in {@link MinecraftServer}'s constructor
* so Cobblemon can capture the server registry access as soon as it exists.
*
* <p>This sets {@link Cobblemon#registryAccess} before downstream initialization
* depends on biome lookups, while still delegating to the original call unchanged.</p>
*
* @author MeAlam
* @since April 9th 2026
*/
@WrapOperation(method = "<init>", at = @At(
value = "INVOKE",
target = "Lnet/minecraft/core/LayeredRegistryAccess;compositeAccess()Lnet/minecraft/core/RegistryAccess$Frozen;",
ordinal = 0
))
private RegistryAccess.Frozen cobblemon$captureRegistryAccess
(LayeredRegistryAccess<RegistryLayer> instance, Operation<RegistryAccess.Frozen> original) {
Cobblemon.registryAccess = instance.compositeAccess();
return original.call(instance);
}
}
@@ -0,0 +1,51 @@
/*
* 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.mixin.biome;
import net.minecraft.data.worldgen.BootstrapContext;
import net.minecraft.data.worldgen.biome.BiomeData;
import net.minecraft.world.level.biome.Biome;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
* TODO (MeAlam):<br>
* This mixin is currently disabled, as it does not appear to be required at this time.
* It is retained for potential future use if biome registration issues arise.
*
* <p>
* When enabled, this mixin registers biomes into the bootstrap context
* of Minecraft's {@code BiomeData} class.
* </p>
*
* <p>
* Example usage:
* <pre>{@code
* context.register(MY_BIOME_KEY, createMyBiome());
*
* // e.g.
* context.register(
* CobblemonBiomes.TEST_BIOME,
* CobblemonBiomes.createTestBiome()
* );
* }</pre>
*
* @author MeAlam
* @since April 9th 2026
*/
@Mixin(BiomeData.class)
public class BiomeDataMixin {
@Inject(method = "bootstrap", at = @At("TAIL"))
private static void cobblemon$registerBiomes(BootstrapContext<Biome> context, CallbackInfo ci) {
}
}
@@ -0,0 +1,68 @@
/*
* 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.mixin.biome;
import com.cobblemon.mod.common.duck.biome.BiomeSourceDuck;
import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet;
import net.minecraft.core.Holder;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeResolver;
import net.minecraft.world.level.biome.BiomeSource;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.Shadow;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* TODO (MeAlam):<br>
* This mixin is currently disabled, as it does not appear to be required at this time.
* It is retained for potential future use if biome injection becomes necessary.
*
* <p>
* When enabled, this mixin extends the {@code BiomeSource} by modifying the
* {@code possibleBiomes} supplier to include additional biomes at runtime.
* It merges existing biomes with new ones while ensuring uniqueness.
* </p>
*
* <p>
* Example usage:
* <pre>{@code
* ((BiomeSourceDuck) biomeSource).addPossibleBiomes(List.of(
* biomeHolderA,
* biomeHolderB
* ));
*
* // Existing and new biomes are merged with duplicates removed.
* }</pre>
*
* @author MeAlam
* @since April 9th 2026
*/
@Mixin(BiomeSource.class)
public abstract class BiomeSourceMixin implements BiomeResolver, BiomeSourceDuck {
@Mutable
@Final
@Shadow
private Supplier<Set<Holder<Biome>>> possibleBiomes;
@Override
public void addPossibleBiomes(List<Holder<Biome>> biomes) {
List<Holder<Biome>> combined = new ArrayList<>();
combined.addAll(this.possibleBiomes.get());
combined.addAll(biomes);
this.possibleBiomes = () -> new ObjectLinkedOpenHashSet<>(combined.stream().distinct().collect(Collectors.toList()));
}
}
@@ -0,0 +1,123 @@
/*
* 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.mixin.biome;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.util.biome.RegistryExtensions;
import com.cobblemon.mod.common.world.biome.CobblemonBiome;
import com.cobblemon.mod.common.world.biome.CobblemonBiomeLoader;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.mojang.datafixers.util.Either;
import com.mojang.datafixers.util.Pair;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderGetter;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.biome.*;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
/**
* Ensures compatibility with Biolith by applying this mixin before its biome modifications.
*
* <p>
* Biolith uses mixins (see
* <a href="https://github.com/TerraformersMC/Biolith/blob/1.21.1/common/src/main/java/com/terraformersmc/biolith/impl/mixin/MixinMultiNoiseBiomeSource.java">
* MixinMultiNoiseBiomeSource
* </a>)
* with a priority of {@code 900}, allowing it to run before Terrablender.
* </p>
*
* <p>
* Because Biolith includes functionality to disable vanilla biomes, this mixin must
* execute before Biolith’s transformations to ensure compatibility and prevent
* unintended biome removal or conflicts.
* </p>
*
* @author MeAlam
* @since April 9th 2026
*/
@Mixin(value = MultiNoiseBiomeSource.class, priority = 850)
public abstract class MultiNoiseBiomeSourceMixin extends BiomeSource {
@Shadow
protected abstract Climate.ParameterList<Holder<Biome>> parameters();
@Unique
private Climate.ParameterList<Holder.Reference<Biome>> cobblemon$parameters;
@WrapOperation(
method = "parameters",
at = @At(value = "INVOKE", target = "Lcom/mojang/datafixers/util/Either;map(Ljava/util/function/Function;Ljava/util/function/Function;)Ljava/lang/Object;"),
remap = false
)
private Object cobblemon$injectBiomes(
Either<Climate.ParameterList<RegistryAccess.RegistryEntry<Biome>>,
RegistryAccess.RegistryEntry<MultiNoiseBiomeSourceParameterList>> instance,
Function<Climate.ParameterList<RegistryAccess.RegistryEntry<Biome>>,
Climate.ParameterList<RegistryAccess.RegistryEntry<Biome>>> leftMap,
Function<RegistryAccess.RegistryEntry<MultiNoiseBiomeSourceParameterList>,
Climate.ParameterList<RegistryAccess.RegistryEntry<Biome>>> rightMap,
Operation<Object> original) {
synchronized (this) {
if (cobblemon$parameters == null) {
/* NOTE:
* Minecraft also does this exact cast on the return of this operation, so we know it's safe.
*/
@SuppressWarnings("unchecked")
Climate.ParameterList<Holder.Reference<Biome>> originalEntries =
(Climate.ParameterList<Holder.Reference<Biome>>) original.call(instance, leftMap, rightMap);
Map<ResourceLocation, CobblemonBiome> loadedBiomes = CobblemonBiomeLoader.getLoadedBiomes();
List<Pair<Climate.ParameterPoint, Holder.Reference<Biome>>> parameterList =
new ArrayList<>(originalEntries.values().size() + loadedBiomes.size());
parameterList.addAll(originalEntries.values());
HolderGetter<Biome> biomeEntryGetter = RegistryExtensions.getRegistryLookup(Registries.BIOME).orElseThrow();
int appended = 0;
int skipped = 0;
for (Map.Entry<ResourceLocation, CobblemonBiome> loadedEntry : loadedBiomes.entrySet()) {
ResourceKey<Biome> biomeKey = ResourceKey.create(Registries.BIOME, loadedEntry.getKey());
Optional<Holder.Reference<Biome>> biomeHolder = biomeEntryGetter.get(biomeKey);
if (biomeHolder.isEmpty()) {
skipped++;
Cobblemon.LOGGER.warn("MEALAM Skipping cobblemon biome climate entry for missing biome {}", loadedEntry.getKey());
continue;
}
parameterList.add(new Pair<>(loadedEntry.getValue().getParameters(), biomeHolder.get()));
appended++;
}
Cobblemon.LOGGER.info(
"MEALAM Biome source climate entries: base={}, appended={}, skipped={}.",
originalEntries.values().size(),
appended,
skipped
);
cobblemon$parameters = new Climate.ParameterList<>(parameterList);
}
return cobblemon$parameters;
}
}
}
@@ -0,0 +1,67 @@
/*
* 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.mixin.biome;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.CobblemonBiomes;
import com.cobblemon.mod.common.world.biome.CobblemonBiomeLoader;
import com.mojang.serialization.Decoder;
import net.minecraft.core.WritableRegistry;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.RegistryDataLoader;
import net.minecraft.resources.RegistryOps;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.world.level.biome.Biome;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Map;
/**
* Registers Cobblemon biomes during datapack registry loading.
*
* <p>Injected into {@link RegistryDataLoader} at the tail of
* {@code loadContentsFromManager}.</p>
*
* <p>At this point, vanilla biome JSON has already been decoded, but registries are not frozen
* yet, making this the safest point to add biome entries.</p>
*
* <p>Only applies when the currently loaded registry is {@link Registries#BIOME}.</p>
*
* @author MeAlam
* @since April 9th 2026
*/
@Mixin(RegistryDataLoader.class)
public class RegistryDataLoaderMixin {
@Inject(method = "loadContentsFromManager", at = @At("TAIL"))
private static <E> void cobblemon$registerBiomes(
ResourceManager resourceManager,
RegistryOps.RegistryInfoLookup registryInfoLookup,
WritableRegistry<E> registry,
Decoder<E> codec,
Map<ResourceKey<?>, Exception> loadingErrors,
CallbackInfo ci
) {
if (!registry.key().equals(Registries.BIOME)) {
return;
}
@SuppressWarnings("unchecked")
WritableRegistry<Biome> biomeRegistry = (WritableRegistry<Biome>) registry;
CobblemonBiomes.register(
CobblemonBiomeLoader.load(resourceManager, registryInfoLookup)
);
CobblemonBiomes.INSTANCE.setRegistry(biomeRegistry);
Cobblemon.implementation.registerBiomes();
}
}
@@ -8,8 +8,12 @@ object CobblemonBuildDetails {
const val VERSION = "{{ version }}"
const val SNAPSHOT = {{ isSnapshot }}
const val GIT_COMMIT = "{{ gitCommit | default("Not Specified") }}"
const val BRANCH = "{{ branch | default("Not Specified") }}"
// Read at runtime rather than templated in, so a new commit doesn't invalidate compiled code.
private val git = java.util.Properties().apply {
CobblemonBuildDetails::class.java.getResourceAsStream("/cobblemon-git.properties")?.use(::load)
}
@JvmField val GIT_COMMIT: String = git.getProperty("commit", "Not Specified")
@JvmField val BRANCH: String = git.getProperty("branch", "Not Specified")
fun smallCommitHash(): String {
return this.GIT_COMMIT.substring(0, 7)
@@ -137,6 +137,7 @@ import kotlin.reflect.jvm.isAccessible
import kotlin.reflect.jvm.javaField
import net.minecraft.client.Minecraft
import net.minecraft.commands.synchronization.SingletonArgumentInfo
import net.minecraft.core.RegistryAccess
import net.minecraft.resources.ResourceKey
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
@@ -182,7 +183,10 @@ object Cobblemon {
var seasonResolver: SeasonResolver = TagSeasonResolver
var wallpapers = mutableMapOf<UUID, Set<ResourceLocation>>()
val serverPlayerStructs = mutableMapOf<UUID, ObjectValue<Player>>()
@JvmField
var registryAccess: RegistryAccess.Frozen = RegistryAccess.EMPTY
val serverPlayerStructs = mutableMapOf<UUID, ObjectValue<Player>>()
val statistics: CobblemonStats = CobblemonStats
@@ -0,0 +1,37 @@
/*
* 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
import com.cobblemon.mod.common.platform.PlatformRegistry
import com.cobblemon.mod.common.world.biome.CobblemonBiome
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.core.Registry
import net.minecraft.core.registries.Registries
import net.minecraft.resources.ResourceKey
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.level.biome.Biome
object CobblemonBiomes : PlatformRegistry<Registry<Biome>, ResourceKey<Registry<Biome>>, Biome>() {
override var registry: Registry<Biome>? = null
override val resourceKey: ResourceKey<Registry<Biome>> = Registries.BIOME
@JvmField
val TEST_BIOME: ResourceKey<Biome> = registerKey("test_biome")
@JvmStatic
fun register(data: Map<ResourceLocation, CobblemonBiome>) {
data.forEach { (identifier, cobblemonBiome) ->
create(identifier, cobblemonBiome.biome)
}
}
private fun registerKey(key: String): ResourceKey<Biome> {
return ResourceKey.create(Registries.BIOME, cobblemonResource(key))
}
}
@@ -20,7 +20,7 @@ import net.minecraft.world.level.block.entity.BlockEntityType
object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, ResourceKey<Registry<BlockEntityType<*>>>, BlockEntityType<*>>() {
override val registry: Registry<BlockEntityType<*>> = BuiltInRegistries.BLOCK_ENTITY_TYPE
override var registry: Registry<BlockEntityType<*>>? = BuiltInRegistries.BLOCK_ENTITY_TYPE
override val resourceKey: ResourceKey<Registry<BlockEntityType<*>>> = Registries.BLOCK_ENTITY_TYPE
@JvmField
@@ -59,7 +59,7 @@ import net.minecraft.world.level.material.PushReaction
@Suppress("SameParameterValue", "HasPlatformType", "MemberVisibilityCanBePrivate", "unused")
object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<Block>>, Block>() {
override val registry: Registry<Block> = BuiltInRegistries.BLOCK
override var registry: Registry<Block>? = BuiltInRegistries.BLOCK
override val resourceKey: ResourceKey<Registry<Block>> = Registries.BLOCK
val APRICORN_BLOCK_SET_TYPE = BlockSetType("apricorn")
@@ -30,7 +30,7 @@ import net.minecraft.world.entity.ai.attributes.AttributeSupplier
object CobblemonEntities : PlatformRegistry<Registry<EntityType<*>>, ResourceKey<Registry<EntityType<*>>>, EntityType<*>>() {
override val registry: Registry<EntityType<*>> = BuiltInRegistries.ENTITY_TYPE
override var registry: Registry<EntityType<*>>? = BuiltInRegistries.ENTITY_TYPE
override val resourceKey: ResourceKey<Registry<EntityType<*>>> = Registries.ENTITY_TYPE
@JvmField
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.api.net.NetworkPacket
import com.mojang.brigadier.arguments.ArgumentType
import kotlin.reflect.KClass
import net.minecraft.commands.synchronization.ArgumentTypeInfo
import net.minecraft.core.HolderLookup
import net.minecraft.resources.ResourceKey
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.MinecraftServer
@@ -122,6 +123,8 @@ interface CobblemonImplementation {
* TODO
*/
fun registerMobEffects()
fun registerBiomes()
/**
@@ -168,12 +171,16 @@ interface CobblemonImplementation {
* TODO
*
* @param identifier
* @param reloader
* @param reloaderFactory
* @param type
* @param dependencies
*/
fun registerResourceReloader(identifier: ResourceLocation, reloader: PreparableReloadListener, type: PackType, dependencies: Collection<ResourceLocation>)
fun registerResourceReloader(
identifier: ResourceLocation,
type: PackType,
dependencies: Collection<ResourceLocation>,
reloaderFactory: (HolderLookup.Provider) -> PreparableReloadListener
)
/**
* TODO
*
@@ -114,7 +114,7 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:held_item_effect"), HELD_ITEM_EFFECT)
}
override val registry = BuiltInRegistries.DATA_COMPONENT_TYPE
override var registry = BuiltInRegistries.DATA_COMPONENT_TYPE
override val resourceKey = Registries.DATA_COMPONENT_TYPE
}
@@ -68,7 +68,7 @@ import net.minecraft.world.level.block.Block
@Suppress("unused", "SameParameterValue")
object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<Item>>, Item>() {
override val registry: Registry<Item> = BuiltInRegistries.ITEM
override var registry: Registry<Item>? = BuiltInRegistries.ITEM
override val resourceKey: ResourceKey<Registry<Item>> = Registries.ITEM
@JvmField
@@ -23,8 +23,7 @@ object CobblemonMenuType : PlatformRegistry<Registry<MenuType<*>>, ResourceKey<R
val COOKING_POT = MenuType.register("cooking_pot_menu", ::CookingPotMenu)
val TM_MACHINE = MenuType.register("tm_machine_menu", ::TMMachineMenu)
override val registry: Registry<MenuType<*>>
get() = BuiltInRegistries.MENU
override var registry: Registry<MenuType<*>>? = BuiltInRegistries.MENU
override val resourceKey: ResourceKey<Registry<MenuType<*>>>
get() = Registries.MENU
}
@@ -19,7 +19,7 @@ import net.minecraft.resources.ResourceKey
import net.minecraft.world.effect.MobEffect
object CobblemonMobEffects : PlatformRegistry<Registry<MobEffect>, ResourceKey<Registry<MobEffect>>, MobEffect>() {
override val registry: Registry<MobEffect> = BuiltInRegistries.MOB_EFFECT
override var registry: Registry<MobEffect>? = BuiltInRegistries.MOB_EFFECT
override val resourceKey: ResourceKey<Registry<MobEffect>> = Registries.MOB_EFFECT
@JvmField
@@ -20,7 +20,7 @@ import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.state.BlockState
object CobblemonPoiTypes: PlatformRegistry<Registry<PoiType>, ResourceKey<Registry<PoiType>>, PoiType>() {
override val registry: Registry<PoiType> = BuiltInRegistries.POINT_OF_INTEREST_TYPE
override var registry: Registry<PoiType>? = BuiltInRegistries.POINT_OF_INTEREST_TYPE
override val resourceKey: ResourceKey<Registry<PoiType>> = Registries.POINT_OF_INTEREST_TYPE
@JvmField
@@ -27,8 +27,7 @@ object CobblemonRecipeSerializers : PlatformRegistry<Registry<RecipeSerializer<*
val BREWING_STAND: RecipeSerializer<*> = register("cobblemon:brewing_stand", BrewingStandRecipe.Serializer())
override val registry: Registry<RecipeSerializer<*>>
get() = BuiltInRegistries.RECIPE_SERIALIZER
override var registry: Registry<RecipeSerializer<*>>? = BuiltInRegistries.RECIPE_SERIALIZER
override val resourceKey: ResourceKey<Registry<RecipeSerializer<*>>>
get() = Registries.RECIPE_SERIALIZER
@@ -27,8 +27,7 @@ object CobblemonRecipeTypes : PlatformRegistry<Registry<RecipeType<*>>, Resource
val BREWING_STAND: RecipeType<BrewingStandRecipe> = register("cobblemon:brewing_stand")
override val registry: Registry<RecipeType<*>>
get() = BuiltInRegistries.RECIPE_TYPE
override var registry: Registry<RecipeType<*>>? = BuiltInRegistries.RECIPE_TYPE
override val resourceKey: ResourceKey<Registry<RecipeType<*>>>
get() = Registries.RECIPE_TYPE
@@ -20,7 +20,7 @@ import net.minecraft.world.level.block.SoundType
object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Registry<SoundEvent>>, SoundEvent>() {
override val registry: Registry<SoundEvent> = BuiltInRegistries.SOUND_EVENT
override var registry: Registry<SoundEvent>? = BuiltInRegistries.SOUND_EVENT
override val resourceKey: ResourceKey<Registry<SoundEvent>> = Registries.SOUND_EVENT
@JvmField
@@ -20,7 +20,7 @@ import net.minecraft.world.entity.ai.village.poi.PoiType
import net.minecraft.world.entity.npc.VillagerProfession
object CobblemonVillagerProfessions: PlatformRegistry<Registry<VillagerProfession>, ResourceKey<Registry<VillagerProfession>>, VillagerProfession>() {
override val registry: Registry<VillagerProfession> = BuiltInRegistries.VILLAGER_PROFESSION
override var registry: Registry<VillagerProfession>? = BuiltInRegistries.VILLAGER_PROFESSION
override val resourceKey: ResourceKey<Registry<VillagerProfession>> = Registries.VILLAGER_PROFESSION
@JvmField
@@ -95,6 +95,6 @@ object CobblemonCriteria : PlatformRegistry<Registry<CriterionTrigger<*>>, Resou
@JvmField
val LEARN_TM_COUNT = this.create("tms_learned", SimpleCriterionTrigger(CountableCriterion.CODEC))
override val registry = BuiltInRegistries.TRIGGER_TYPES
override var registry = BuiltInRegistries.TRIGGER_TYPES
override val resourceKey = Registries.TRIGGER_TYPE
}
@@ -21,7 +21,7 @@ object CobblemonEntitySubPredicates : PlatformRegistry<Registry<MapCodec<out Ent
@JvmStatic
val POKE_POBBER = this.create("poke_bobber", FishingBobberPredicate.CODEC)
override val registry: Registry<MapCodec<out EntitySubPredicate>> = BuiltInRegistries.ENTITY_SUB_PREDICATE_TYPE
override var registry: Registry<MapCodec<out EntitySubPredicate>>? = BuiltInRegistries.ENTITY_SUB_PREDICATE_TYPE
override val resourceKey: ResourceKey<Registry<MapCodec<out EntitySubPredicate>>> = Registries.ENTITY_SUB_PREDICATE_TYPE
@@ -44,8 +44,15 @@ interface JsonDataRegistry<T> : DataRegistry {
*/
val resourcePath: String
override fun reload(manager: ResourceManager) {
val data = hashMapOf<ResourceLocation, T>()
/**
* Traverses JSON resources for this registry and maps them to their resolved identifiers.
* Returning null from [parse] skips the resource.
*/
fun <R> loadJsonData(
manager: ResourceManager,
parse: (reader: BufferedReader, sourceIdentifier: ResourceLocation, resolvedIdentifier: ResourceLocation) -> R?
): Map<ResourceLocation, R> {
val data = hashMapOf<ResourceLocation, R>()
manager.listResources(resourcePath) { path -> path.endsWith(JSON_EXTENSION) }.forEach { (identifier, resource) ->
if (identifier.namespace == "pixelmon") {
return@forEach
@@ -54,11 +61,21 @@ interface JsonDataRegistry<T> : DataRegistry {
resource.open().use { stream ->
stream.bufferedReader().use { reader ->
val resolvedIdentifier = ResourceLocation.fromNamespaceAndPath(identifier.namespace, File(identifier.path).nameWithoutExtension)
data[resolvedIdentifier] = parse(reader, resolvedIdentifier)
parse(reader, identifier, resolvedIdentifier)?.let { parsed ->
data[resolvedIdentifier] = parsed
}
}
}
}
return data
}
override fun reload(manager: ResourceManager) {
val data = loadJsonData(manager) { reader, _, resolvedIdentifier ->
parse(reader, resolvedIdentifier)
}
reload(data)
}
@@ -0,0 +1,93 @@
/*
* 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.data
import com.cobblemon.mod.common.util.endsWith
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import net.minecraft.core.HolderLookup
import java.io.BufferedReader
import java.io.File
import java.util.concurrent.ExecutionException
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.packs.PackType
import net.minecraft.server.packs.resources.ResourceManager
/**
* A [DataRegistry] that consumes JSON files.
* Every deserialized instance is attached to an [Identifier].
* For example a file under data/mymod/[resourcePath]/entry.json would be backed by the identifier modid:entry.
*
* @param T The type of the data consumed by this registry.
*
* @author Licious
* @since August 5th, 2022
*/
interface ServerJsonDataRegistry<T> : DataRegistry {
/**
* The [TypeToken] of type [T].
*/
val typeToken: TypeToken<T>
/**
* The folder location for the data this registry will consume.
*/
val resourcePath: String
override val type: PackType
get() = PackType.SERVER_DATA
override fun reload(manager: ResourceManager) {
//NO-OP, handled by reload(manager, registry)
}
fun reload(manager: ResourceManager, registry: HolderLookup.Provider) {
val gson = setupParser(registry)
val data = hashMapOf<ResourceLocation, T>()
manager.listResources(resourcePath) { path -> path.endsWith(JSON_EXTENSION) }.forEach { (identifier, resource) ->
if (identifier.namespace == "pixelmon") {
return@forEach
}
resource.open().use { stream ->
stream.bufferedReader().use { reader ->
val resolvedIdentifier = ResourceLocation.fromNamespaceAndPath(identifier.namespace, File(identifier.path).nameWithoutExtension)
data[resolvedIdentifier] = parse(reader, resolvedIdentifier, gson)
}
}
}
reload(data, registry)
}
fun parse(reader: BufferedReader, identifier: ResourceLocation, gson: Gson): T {
return try {
gson.fromJson(reader, typeToken.type)
} catch (exception: Exception) {
throw ExecutionException("Error loading JSON for data: $identifier", exception)
}
}
/**
* Allows the creation of [Gson] instances which have adapters that are registry-aware, e.g. [com.cobblemon.mod.common.pokemon.evolution.adapters.LegacyItemConditionWrapperAdapter]
*/
fun setupParser(registry: HolderLookup.Provider): Gson
/**
* Reloads this registry from the deserialized data.
*
* @param data A map of the data associating an instance to the respective identifier from the [ResourceManager].
*/
fun reload(data: Map<ResourceLocation, T>, registry: HolderLookup.Provider)
companion object {
const val JSON_EXTENSION = ".json"
}
}
@@ -40,7 +40,7 @@ object Fossils: JsonDataRegistry<Fossil> {
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, ItemLikeConditionAdapter)
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter)
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter())
.create()
override val typeToken: TypeToken<Fossil> = TypeToken.get(Fossil::class.java)
@@ -10,7 +10,7 @@ package com.cobblemon.mod.common.api.interaction
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.data.ServerJsonDataRegistry
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.requirement.Requirement
@@ -26,6 +26,7 @@ import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.advancements.critereon.ItemPredicate
import net.minecraft.core.HolderLookup
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
@@ -36,28 +37,31 @@ import net.minecraft.world.level.block.Block
import net.minecraft.world.level.levelgen.structure.Structure
import net.minecraft.world.level.material.Fluid
object PokemonInteractions : JsonDataRegistry<PokemonInteractionSet> {
object PokemonInteractions : ServerJsonDataRegistry<PokemonInteractionSet> {
override val id = cobblemonResource("pokemon_interactions")
override val type = PackType.SERVER_DATA
override fun setupParser(registry: HolderLookup.Provider): Gson {
return GsonBuilder()
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter(registry))
.registerTypeAdapter(Requirement::class.java, CobblemonRequirementAdapter)
.registerTypeAdapter(InteractionEffect::class.java, InteractionEffectAdapter)
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, BiomeLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Block::class.java).type, BlockLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, ItemLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Structure::class.java).type, StructureLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Fluid::class.java).type, FluidLikeConditionAdapter)
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
.registerTypeAdapterFactory(LowerCaseEnumTypeAdapterFactory())
.setPrettyPrinting()
.create()
}
override val observable = SimpleObservable<PokemonInteractions>()
override val typeToken: TypeToken<PokemonInteractionSet> = TypeToken.get(PokemonInteractionSet::class.java)
override val resourcePath = "pokemon_interactions"
override val gson: Gson = GsonBuilder()
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter)
.registerTypeAdapter(Requirement::class.java, CobblemonRequirementAdapter)
.registerTypeAdapter(InteractionEffect::class.java, InteractionEffectAdapter)
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, BiomeLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Block::class.java).type, BlockLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, ItemLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Structure::class.java).type, StructureLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Fluid::class.java).type, FluidLikeConditionAdapter)
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
.registerTypeAdapterFactory(LowerCaseEnumTypeAdapterFactory())
.setPrettyPrinting()
.create()
val interactions = mutableListOf<PokemonInteractionSet>()
@@ -65,7 +69,7 @@ object PokemonInteractions : JsonDataRegistry<PokemonInteractionSet> {
PokemonInteractionsSyncPacket(interactions).sendToPlayer(player)
}
override fun reload(data: Map<ResourceLocation, PokemonInteractionSet>) {
override fun reload(data: Map<ResourceLocation, PokemonInteractionSet>, registry: HolderLookup.Provider) {
interactions.clear()
val split = data.entries
.partition {
@@ -87,4 +91,4 @@ object PokemonInteractions : JsonDataRegistry<PokemonInteractionSet> {
.flatMap { it.interactions }
.firstOrNull(interactionCheck)
}
}
}
@@ -16,6 +16,7 @@ import com.cobblemon.mod.common.api.molang.ObjectValue
import com.cobblemon.mod.common.api.pokedex.AbstractPokedexManager
import com.cobblemon.mod.common.api.pokedex.CaughtCount
import com.cobblemon.mod.common.api.pokedex.CaughtPercent
import com.cobblemon.mod.common.api.pokedex.Dexes
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
import com.cobblemon.mod.common.api.pokedex.PokedexManager
import com.cobblemon.mod.common.api.pokedex.SeenCount
@@ -74,6 +75,27 @@ object PokedexMoLangFunctions : AbstractMoLangFunctionHolder<AbstractPokedexMana
functions["caught_percent"] = { DoubleValue(pokedex.getGlobalCalculatedValue(CaughtPercent)) }
functions["seen_percent"] = { DoubleValue(pokedex.getGlobalCalculatedValue(SeenPercent)) }
functions["dex_caught_count"] = { params ->
val dexId = params.getString(0).asIdentifierDefaultingNamespace()
if (Dexes.dexEntryMap.containsKey(dexId)) DoubleValue(pokedex.getDexCalculatedValue(dexId, CaughtCount))
else DoubleValue.ZERO
}
functions["dex_seen_count"] = { params ->
val dexId = params.getString(0).asIdentifierDefaultingNamespace()
if (Dexes.dexEntryMap.containsKey(dexId)) DoubleValue(pokedex.getDexCalculatedValue(dexId, SeenCount))
else DoubleValue.ZERO
}
functions["dex_caught_percent"] = { params ->
val dexId = params.getString(0).asIdentifierDefaultingNamespace()
if (Dexes.dexEntryMap.containsKey(dexId)) DoubleValue(pokedex.getDexCalculatedValue(dexId, CaughtPercent).toDouble())
else DoubleValue.ZERO
}
functions["dex_seen_percent"] = { params ->
val dexId = params.getString(0).asIdentifierDefaultingNamespace()
if (Dexes.dexEntryMap.containsKey(dexId)) DoubleValue(pokedex.getDexCalculatedValue(dexId, SeenPercent).toDouble())
else DoubleValue.ZERO
}
when (pokedex) {
is PokedexManager -> {
functions["player_id"] = { StringValue(pokedex.uuid.toString()) }
@@ -8,8 +8,10 @@
package com.cobblemon.mod.common.api.orientation
import com.cobblemon.mod.common.util.math.QuatSpring
import com.cobblemon.mod.common.util.math.geometry.toDegrees
import com.cobblemon.mod.common.util.math.geometry.toRadians
import com.cobblemon.mod.common.util.math.simpleSpringDamperQuat
import net.minecraft.client.Minecraft
import net.minecraft.util.Mth
import net.minecraft.world.entity.LivingEntity
@@ -26,6 +28,8 @@ open class OrientationController(val entity: LivingEntity) {
val FORWARDS: Vector3f = Vector3f(0.0f, 0.0f, -1.0f)
val UP: Vector3f = Vector3f(0.0f, 1.0f, 0.0f)
val LEFT: Vector3f = Vector3f(-1.0f, 0.0f, 0.0f)
private const val TICK_DT = 1f / 20f // Minecraft server tick rate
private const val RENDER_SMOOTH_HALFLIFE = 0.1f // seconds to close ~half the gap
}
open var active: Boolean = false
@@ -33,6 +37,8 @@ open class OrientationController(val entity: LivingEntity) {
var orientation: Matrix3f? = null
private set
private val renderSpring = QuatSpring()
private var renderOrientationO: Matrix3f? = null
private var renderOrientation: Matrix3f? = null
@@ -54,6 +60,8 @@ open class OrientationController(val entity: LivingEntity) {
orientation = null
renderOrientationO = null
renderOrientation = null
renderSpring.quat.identity()
renderSpring.angVel.set(0f, 0f, 0f)
active = false
}
@@ -77,18 +85,26 @@ open class OrientationController(val entity: LivingEntity) {
return oldQuat
}
/** Every tick move the current render orientation towards the last known true orientation **/
fun tick() {
if (!active || orientation == null) return
renderOrientationO = renderOrientation
val current = orientation
val renderMatrix = this.renderOrientation ?: current
val renderQuat = Quaternionf().setFromUnnormalized(renderMatrix)
val targetQuat = Quaternionf().setFromUnnormalized(current)
val dampingFactor = 0.66f // Smooth interpolation to reduce jitter of orientations recieved from the server.
renderQuat.slerp(targetQuat, dampingFactor)
val current = orientation!!
val targetQuat = Quaternionf().setFromUnnormalized(current)
if (renderOrientation == null) {
// First tick: snap the spring to the target instead of easing in from identity
renderSpring.quat.set(targetQuat)
renderSpring.angVel.set(0f, 0f, 0f)
}
// Use a "quat spring" to smoothly move the render orientation towards the goal orientation.
// It takes into account the orientations angular velocities and should be much more resilient
// to sharp changes in goal orientation or late/missed packets.
simpleSpringDamperQuat(renderSpring, targetQuat, 0.15f, TICK_DT)
val newRenderOrientation = Matrix3f()
renderQuat.get(newRenderOrientation)
renderSpring.quat.get(newRenderOrientation)
renderOrientation = newRenderOrientation
}
@@ -128,7 +128,7 @@ object PokemonSpecies : JsonDataRegistry<Species> {
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Fluid::class.java).type, FluidLikeConditionAdapter)
.registerTypeAdapter(EggGroup::class.java, EggGroupAdapter)
.registerTypeAdapter(MobEffect::class.java, RegistryElementAdapter<MobEffect>(BuiltInRegistries::MOB_EFFECT))
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter)
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter())
.registerTypeAdapter(RidingBehaviourSettings::class.java, RidingBehaviourSettingsAdapter)
.registerTypeAdapter(RideSoundSettingsList::class.java, RideSoundSettingsListAdapter)
.registerTypeAdapter(ObtainableItemCondition::class.java, ObtainableItemConditionAdapter)
@@ -0,0 +1,62 @@
/*
* 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.riding.util
import net.minecraft.core.BlockPos
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.phys.AABB
import kotlin.math.floor
/**
* Walks every block position inside [searchBox] and turns each solid block into one
* full-cube [FieldPrimitive].
*
* @author Jackson
* @since July 27th, 2026
*/
fun collectPrimitivesFromBlocks(
level: BlockGetter,
searchBox: AABB,
shape: PrimitiveShape,
includeFluids: Boolean = false
): List<FieldPrimitive> {
val primitives = mutableListOf<FieldPrimitive>()
val minX = floor(searchBox.minX).toInt()
val maxX = floor(searchBox.maxX).toInt()
val minY = floor(searchBox.minY).toInt()
val maxY = floor(searchBox.maxY).toInt()
val minZ = floor(searchBox.minZ).toInt()
val maxZ = floor(searchBox.maxZ).toInt()
val pos = BlockPos.MutableBlockPos()
for (x in minX..maxX) {
for (y in minY..maxY) {
for (z in minZ..maxZ) {
pos.set(x, y, z)
val state = level.getBlockState(pos)
val isSolid = !state.getCollisionShape(level, pos).isEmpty
val isFluid = includeFluids && !state.fluidState.isEmpty
if (!isSolid && !isFluid) continue
primitives.add(
FieldPrimitive(
shape = shape,
centerX = x + 0.5f, centerY = y + 0.5f, centerZ = z + 0.5f,
halfWidth = 0.5f, halfHeight = 0.5f, halfDepth = 0.5f
)
)
}
}
}
return primitives
}
@@ -0,0 +1,155 @@
/*
* 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.riding.util
import net.minecraft.world.phys.Vec3
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import kotlin.math.sqrt
/**
* Helpful Functions when working with signed distance fields/functions.
* https://iquilezles.org/articles/distfunctions/
*
* @author Jackson
* @since July 27th, 2026
*/
/** Which distance formula a primitive uses. */
enum class PrimitiveShape {
BOX,
ROUNDED_BOX,
ELLIPSOID
}
/** Tunable constants for the field. */
data class DistanceFieldTuning(
val smoothingRange: Float = 0.5f,
val roundingRadius: Float = 0.08f
)
/** A single shape in the field - a center position, half-extents, and which formula to use. */
data class FieldPrimitive(
val shape: PrimitiveShape,
val centerX: Float, val centerY: Float, val centerZ: Float,
val halfWidth: Float, val halfHeight: Float, val halfDepth: Float
)
/** Fast approximate 1/sqrt(x) - the Quake III trick, cheaper than a real sqrt + divide. */
fun fastInverseSqrt(value: Float): Float {
var x = value
val halfX = 0.5f * x
var bits = java.lang.Float.floatToIntBits(x)
bits = 0x5f3759df - (bits shr 1)
x = java.lang.Float.intBitsToFloat(bits)
x *= (1.5f - halfX * x * x)
return x
}
/** Exact distance from a point to an axis-aligned box. Inputs are relative to the box's own center. */
fun boxDistance(relX: Float, relY: Float, relZ: Float, halfWidth: Float, halfHeight: Float, halfDepth: Float): Float {
val qx = abs(relX) - halfWidth
val qy = abs(relY) - halfHeight
val qz = abs(relZ) - halfDepth
val ox = max(qx, 0f)
val oy = max(qy, 0f)
val oz = max(qz, 0f)
val outside = sqrt(ox * ox + oy * oy + oz * oz)
val inside = min(max(qx, max(qy, qz)), 0f)
return outside + inside
}
/** Same as boxDistance but with rounded corners/edges https://iquilezles.org/articles/distfunctions/ */
fun roundedBoxDistance(relX: Float, relY: Float, relZ: Float, halfWidth: Float, halfHeight: Float, halfDepth: Float, roundingRadius: Float): Float =
boxDistance(relX, relY, relZ, halfWidth - roundingRadius, halfHeight - roundingRadius, halfDepth - roundingRadius) - roundingRadius
/** Approximate (lower-bound) distance to an ellipsoid https://iquilezles.org/articles/ellipsoids/. */
fun ellipsoidDistance(relX: Float, relY: Float, relZ: Float, halfWidth: Float, halfHeight: Float, halfDepth: Float): Float {
val px = relX / halfWidth
val py = relY / halfHeight
val pz = relZ / halfDepth
val inverseMagnitude = fastInverseSqrt(px * px + py * py + pz * pz)
val trueDistance = sqrt(relX * relX + relY * relY + relZ * relZ)
return trueDistance * (1.0f - inverseMagnitude)
}
/** Distance from a point to one primitive, dispatched by its shape. */
fun distanceToPrimitive(primitive: FieldPrimitive, sampleX: Float, sampleY: Float, sampleZ: Float, tuning: DistanceFieldTuning): Float {
val relX = sampleX - primitive.centerX
val relY = sampleY - primitive.centerY
val relZ = sampleZ - primitive.centerZ
return when (primitive.shape) {
PrimitiveShape.BOX -> boxDistance(relX, relY, relZ, primitive.halfWidth, primitive.halfHeight, primitive.halfDepth)
PrimitiveShape.ROUNDED_BOX -> roundedBoxDistance(relX, relY, relZ, primitive.halfWidth, primitive.halfHeight, primitive.halfDepth, tuning.roundingRadius)
PrimitiveShape.ELLIPSOID -> ellipsoidDistance(relX, relY, relZ, primitive.halfWidth, primitive.halfHeight, primitive.halfDepth)
}
}
///** Normalized quadratic smooth-min https://iquilezles.org/articles/smin/ */
//fun smoothMinimum(a: Float, b: Float, smoothingRange: Float): Float {
// if (smoothingRange <= 0.001f) return min(a, b)
// val k = smoothingRange * 4.0f
// val h = (0.5f + 0.5f * (b - a) / k).coerceIn(0f, 1f)
// return b + (a - b) * h - k * h * (1f - h)
//}
//
///** Folds every primitive's distance together into one field value at a point. */
//fun sampleField(primitives: List<FieldPrimitive>, sampleX: Float, sampleY: Float, sampleZ: Float, tuning: DistanceFieldTuning): Float {
// var result = Float.POSITIVE_INFINITY
// for (primitive in primitives) {
// val distance = distanceToPrimitive(primitive, sampleX, sampleY, sampleZ, tuning)
// result = if (result == Float.POSITIVE_INFINITY) distance else smoothMinimum(result, distance, tuning.smoothingRange)
// }
// return result
//}
/** Normalized quadratic smooth-min https://iquilezles.org/articles/smin/ */
fun smoothMinimum(a: Float, b: Float, smoothingRange: Float): Float {
if (smoothingRange <= 0.001f) return min(a, b)
val k = smoothingRange * 4.0f
val h = (0.5f + 0.5f * (b - a) / k).coerceIn(0f, 1f)
return b + (a - b) * h - k * h * (1f - h)
}
/** Folds nearby primitive distances together into one field value at a point. */
fun sampleField(primitives: List<FieldPrimitive>, sampleX: Float, sampleY: Float, sampleZ: Float, tuning: DistanceFieldTuning): Float {
if (primitives.isEmpty()) return Float.POSITIVE_INFINITY
val distances = FloatArray(primitives.size) { distanceToPrimitive(primitives[it], sampleX, sampleY, sampleZ, tuning) }
val minDistance = distances.min()
if (tuning.smoothingRange <= 0.001f) return minDistance
// Anything more than a few blend-widths past the closest primitive is cut - its
// contribution would be negligible in a correct blend anyway, but left in, it still
// drags the chained result down the more of them there are.
val cutoff = tuning.smoothingRange * 3f
var result = Float.POSITIVE_INFINITY
for (distance in distances) {
if (distance - minDistance > cutoff) continue
result = if (result == Float.POSITIVE_INFINITY) distance else smoothMinimum(result, distance, tuning.smoothingRange)
}
return result
}
/** Finite-difference gradient of the field at a point - points away from the surface, magnitude ~1 for a clean field. */
fun estimateGradient(primitives: List<FieldPrimitive>, point: Vec3, tuning: DistanceFieldTuning, epsilon: Float = 0.05f): Vec3 {
val px = point.x.toFloat()
val py = point.y.toFloat()
val pz = point.z.toFloat()
val dx = sampleField(primitives, px + epsilon, py, pz, tuning) - sampleField(primitives, px - epsilon, py, pz, tuning)
val dy = sampleField(primitives, px, py + epsilon, pz, tuning) - sampleField(primitives, px, py - epsilon, pz, tuning)
val dz = sampleField(primitives, px, py, pz + epsilon, tuning) - sampleField(primitives, px, py, pz - epsilon, tuning)
val gradient = Vec3(dx.toDouble(), dy.toDouble(), dz.toDouble())
return if (gradient.lengthSqr() > 1e-8) gradient.normalize() else Vec3(0.0, 1.0, 0.0)
}
@@ -0,0 +1,105 @@
/*
* 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.riding.util
import net.minecraft.core.particles.DustParticleOptions
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.Entity
import net.minecraft.world.phys.AABB
import net.minecraft.world.phys.Vec3
import org.joml.Vector3f
/** One shell to draw - a target field value, a color, a particle size. */
data class DistanceFieldLayer(
val isoValue: Float,
val color: Vector3f,
val particleSize: Float = 1.0f
)
/**
* Marches a grid of vertical columns through the field around [center] and spawns a
* particle everywhere it crosses one of [layers]' target values. Server-side only.
*/
fun visualizeDistanceField(
referenceEntity: Entity,
center: Vec3,
layers: List<DistanceFieldLayer>,
shape: PrimitiveShape = PrimitiveShape.BOX,
horizontalRadius: Double = 4.0,
verticalRange: Double = 4.0,
sampleSpacing: Double = 0.5,
marchStep: Double = 0.1,
tuning: DistanceFieldTuning = DistanceFieldTuning()
) {
val level = referenceEntity.level()
if (level !is ServerLevel) return
val searchBox = AABB(
center.x - horizontalRadius, center.y - verticalRange, center.z - horizontalRadius,
center.x + horizontalRadius, center.y, center.z + horizontalRadius
)
val primitives = collectPrimitivesFromBlocks(level, searchBox, shape, includeFluids = true)
if (primitives.isEmpty()) return
var x = -horizontalRadius
while (x <= horizontalRadius) {
var z = -horizontalRadius
while (z <= horizontalRadius) {
marchColumn(level, primitives, center, x, z, verticalRange, marchStep, layers, tuning)
z += sampleSpacing
}
x += sampleSpacing
}
}
private fun marchColumn(
level: ServerLevel,
primitives: List<FieldPrimitive>,
center: Vec3,
offsetX: Double,
offsetZ: Double,
verticalRange: Double,
marchStep: Double,
layers: List<DistanceFieldLayer>,
tuning: DistanceFieldTuning
) {
val previousValue = FloatArray(layers.size) { Float.NaN }
var y = verticalRange
while (y >= -verticalRange) {
val worldX = (center.x + offsetX).toFloat()
val worldY = (center.y + y).toFloat()
val worldZ = (center.z + offsetZ).toFloat()
val fieldValue = sampleField(primitives, worldX, worldY, worldZ, tuning)
for (layerIndex in layers.indices) {
val layer = layers[layerIndex]
val previous = previousValue[layerIndex]
if (!previous.isNaN() && crossedTarget(previous, fieldValue, layer.isoValue)) {
val t = (layer.isoValue - previous) / (fieldValue - previous)
val crossingY = (y + marchStep) - marchStep * t
spawnLayerParticle(level, center.add(offsetX, crossingY, offsetZ), layer)
}
previousValue[layerIndex] = fieldValue
}
y -= marchStep
}
}
private fun crossedTarget(previous: Float, current: Float, target: Float): Boolean =
(previous - target) * (current - target) < 0f
private fun spawnLayerParticle(level: ServerLevel, pos: Vec3, layer: DistanceFieldLayer) {
val particle = DustParticleOptions(layer.color, layer.particleSize)
level.sendParticles(particle, pos.x, pos.y, pos.z, 1, 0.0, 0.0, 0.0, 0.0)
}
@@ -128,6 +128,7 @@ class DiscShelfBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobbl
fun isValidItem(stack: ItemStack): Boolean {
return stack.item is TechnicalMachineItem
|| stack.item == CobblemonItems.BLANK_TM
|| stack.item == CobblemonItems.UPGRADE
|| stack.item == CobblemonItems.DUBIOUS_DISC
|| stack.has(DataComponents.JUKEBOX_PLAYABLE)
@@ -31,6 +31,7 @@ import com.cobblemon.mod.common.client.sound.instances.CancellableSoundInstance
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.item.PokeBallItem
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.activestate.SentOutState
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.giveOrDropItemStack
import com.cobblemon.mod.common.util.lang
@@ -235,6 +236,7 @@ class FossilMultiblockStructure (
entity.setPos(fixedPosition.center.subtract(0.0, 0.5, 0.0))
// TODO: Find a correct way to set the new entity's Yaw rotation. (Face away from the machine)
if (world.addFreshEntity(entity)) {
pokemon.state = SentOutState(entity)
CobblemonEvents.FOSSIL_REVIVED.post(FossilRevivedEvent(pokemon, null))
return true
} else {
@@ -71,7 +71,7 @@ open class VariableObjectSelectionList(x: Int, y: Int, width: Int, height: Int):
var stackHeight = -scrollAmount.toInt()
guiGraphics.enableScissor(x, y, x + width, y + height)
children.forEachIndexed { index, entry ->
if (stackHeight in 1..<height || stackHeight + entry.height in 1..<height) {
if (stackHeight < height && stackHeight + entry.height > 0) {
entry.render(guiGraphics, index, x, y + stackHeight, mouseX, mouseY, partialTick)
}
stackHeight += entry.height
@@ -8,12 +8,10 @@
package com.cobblemon.mod.common.client.gui.startselection.widgets
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.gui.PokemonGUIAnimationStyle
import com.cobblemon.mod.common.client.gui.VariableObjectSelectionList
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
import com.cobblemon.mod.common.client.gui.startselection.StarterSelectionScreen
@@ -29,12 +27,11 @@ import org.joml.Quaternionf
import org.joml.Vector3f
class CategoryList(
private val paneWidth: Int,
private val paneHeight: Int,
paneWidth: Int,
paneHeight: Int,
private val categories: List<RenderableStarterCategory>,
val listX: Int,
val listY: Int,
private val minecraft: Minecraft = Minecraft.getInstance(),
private val starterSelectionScreen: StarterSelectionScreen
) : VariableObjectSelectionList(
listX,
@@ -70,7 +67,8 @@ class CategoryList(
}
private fun createEntries() = categories.map { category ->
val rows = (category.pokemon.size - 1) / 3 + 1
val pokemonCount = category.pokemon.size - if (category.randomStarter) 0 else 1
val rows = pokemonCount / 3 + 1
val height = CONTAINER_BUTTON_SIZE * rows + BUTTON_SEPERATION * (rows + 1) + HEADER_HEIGHT + 3
Category(category, height)
}
@@ -8,9 +8,9 @@
package com.cobblemon.mod.common.client.render.block
import com.cobblemon.mod.common.CobblemonItemComponents
import com.cobblemon.mod.common.block.entity.DiscShelfBlockEntity
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.client.render.color.TechnicalMachineItemColorProvider
import com.cobblemon.mod.common.util.cobblemonResource
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.blaze3d.vertex.VertexConsumer
@@ -37,15 +37,15 @@ class DiscShelfBlockEntityRenderer(ctx: BlockEntityRendererProvider.Context) :
private val slotHeightPixels = 2
private val textureSize = 16f
private val slotUMax = slotWidthPixels / textureSize
private val slotVMax = slotHeightPixels / textureSize
private val leftMargin = 1f
private val rightMargin = 1f
private val topMargin = 1f
private val interRowSpacing = 0f
private val tmOverlayDepthOffset = -0.0002f
private val slotBrightnessMultiplier = 0.65f
private val tmBaseTexture = cobblemonResource("textures/block/disc_shelf/technical_machine_base.png")
private val tmOverlayTexture = cobblemonResource("textures/block/disc_shelf/technical_machine_overlay.png")
private val musicDiscUnknownTexture = cobblemonResource("textures/block/disc_shelf/music_disc_unknown.png")
override fun render(
@@ -98,35 +98,19 @@ class DiscShelfBlockEntityRenderer(ctx: BlockEntityRendererProvider.Context) :
poseStack.translate(posX, posY, -0.001f)
if (item.item == CobblemonItems.TECHNICAL_MACHINE) {
val baseColor = TechnicalMachineItemColorProvider.getColor(item, 0)
val overlayColor = TechnicalMachineItemColorProvider.getColor(item, 1)
val elementId = item.components.get(CobblemonItemComponents.TM_MOVE)!!.move.elementalType.showdownId
val texture = cobblemonResource("textures/block/disc_shelf/${elementId}_tm.png")
renderSlotQuad(
poseStack,
buffer,
tmBaseTexture,
baseColor,
texture,
0xFFFFFF,
packedLight,
slotWidth,
slotHeight,
tint = true,
useTextRenderType = true
)
poseStack.pushPose()
poseStack.translate(0.0, 0.0, tmOverlayDepthOffset.toDouble())
renderSlotQuad(
poseStack,
buffer,
tmOverlayTexture,
overlayColor,
packedLight,
slotWidth,
slotHeight,
tint = true,
useTextRenderType = true
)
poseStack.popPose()
} else {
val tex = getItemTexture(item)
renderSlotQuad(
@@ -176,13 +160,15 @@ class DiscShelfBlockEntityRenderer(ctx: BlockEntityRendererProvider.Context) :
val g = (gBase * slotBrightnessMultiplier).coerceIn(0f, 1f)
val b = (bBase * slotBrightnessMultiplier).coerceIn(0f, 1f)
consumer.addVertex(matrix, 0f, 0f, 0f).setColor(r, g, b, a).setUv(0f, 1f)
// The quad faces -Z, so a viewer looking at it sees +X on their left.
// U runs from slotUMax at x = 0 to 0 at x = width so the art is not mirrored.
consumer.addVertex(matrix, 0f, 0f, 0f).setColor(r, g, b, a).setUv(slotUMax, slotVMax)
.setUv1(0, 0).setUv2(light and 0xFFFF, light shr 16).setNormal(0f, 0f, -1f)
consumer.addVertex(matrix, 0f, height, 0f).setColor(r, g, b, a).setUv(0f, 0f)
consumer.addVertex(matrix, 0f, height, 0f).setColor(r, g, b, a).setUv(slotUMax, 0f)
.setUv1(0, 0).setUv2(light and 0xFFFF, light shr 16).setNormal(0f, 0f, -1f)
consumer.addVertex(matrix, width, height, 0f).setColor(r, g, b, a).setUv(1f, 0f)
consumer.addVertex(matrix, width, height, 0f).setColor(r, g, b, a).setUv(0f, 0f)
.setUv1(0, 0).setUv2(light and 0xFFFF, light shr 16).setNormal(0f, 0f, -1f)
consumer.addVertex(matrix, width, 0f, 0f).setColor(r, g, b, a).setUv(1f, 1f)
consumer.addVertex(matrix, width, 0f, 0f).setColor(r, g, b, a).setUv(0f, slotVMax)
.setUv1(0, 0).setUv2(light and 0xFFFF, light shr 16).setNormal(0f, 0f, -1f)
}
@@ -1,27 +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.color
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.item.components.TMMoveComponent.Companion.getTMMove
import net.minecraft.client.color.item.ItemColor
import net.minecraft.util.FastColor
import net.minecraft.world.item.ItemStack
object TechnicalMachineItemColorProvider : ItemColor {
override fun getColor(itemStack: ItemStack, layer: Int): Int {
val moveType = getTMMove(itemStack)?.elementalType ?: ElementalTypes.NORMAL
val color = when (layer) {
0 -> moveType.primaryColor
1 -> moveType.secondaryColor
else -> 0xFFFFFF
}
return FastColor.ARGB32.opaque(color)
}
}
@@ -1,63 +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.animation.QuadrupedWalkAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.QuadrupedFrame
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 net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class DeerlingModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame, QuadrupedFrame {
override val rootPart = root.registerChildWithAllChildren("deerling")
override val head = getPart("head")
override var portraitScale = 3.5F
override var portraitTranslation = Vec3(-0.55, -1.1, 0.0)
override var profileScale = 1.0F
override var profileTranslation = Vec3(0.0, 0.25, 0.0)
override val foreLeftLeg= getPart("leg_front_left")
override val foreRightLeg = getPart("leg_front_right")
override val hindLeftLeg = getPart("leg_back_left")
override val hindRightLeg = getPart("leg_back_right")
lateinit var standing: Pose
lateinit var walk: Pose
override fun registerPoses() {
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
animations = arrayOf(
singleBoneLook(),
// bedrock("deerling", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
animations = arrayOf(
singleBoneLook(),
QuadrupedWalkAnimation(this, periodMultiplier = 0.8F, amplitudeMultiplier = 0.8F)
// bedrock("deerling", "ground_walk")
)
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PosableState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("deerling", "faint") else null
}
@@ -1,63 +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.animation.QuadrupedWalkAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.QuadrupedFrame
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 net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class SawsbuckModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame, QuadrupedFrame {
override val rootPart = root.registerChildWithAllChildren("sawsbuck")
override val head = getPart("head")
override var portraitScale = 2.0F
override var portraitTranslation = Vec3(-0.5, 1.1, 0.0)
override var profileScale = 0.65F
override var profileTranslation = Vec3(0.0, 0.75, 0.0)
override val foreLeftLeg= getPart("leg_front_left")
override val foreRightLeg = getPart("leg_front_right")
override val hindLeftLeg = getPart("leg_back_left")
override val hindRightLeg = getPart("leg_back_right")
lateinit var standing: Pose
lateinit var walk: Pose
override fun registerPoses() {
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
animations = arrayOf(
singleBoneLook(),
// bedrock("sawsbuck", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
animations = arrayOf(
singleBoneLook(),
QuadrupedWalkAnimation(this, periodMultiplier = 0.9F, amplitudeMultiplier = 0.9F)
// bedrock("sawsbuck", "ground_walk")
)
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PosableState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("sawsbuck", "faint") else null
}
@@ -1,90 +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.gen9
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.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 net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class GlimmetModel (root: ModelPart) : PokemonPosableModel(root) {
override val rootPart = root.registerChildWithAllChildren("glimmet")
override var portraitScale = 2.0F
override var portraitTranslation = Vec3(-0.5, 1.3, 0.0)
override var profileScale = 0.7F
override var profileTranslation = Vec3(0.0, 0.8, 0.0)
lateinit var standing: Pose
lateinit var walk: Pose
lateinit var sleep: Pose
lateinit var shoulderLeft: Pose
lateinit var shoulderRight: Pose
val shoulderOffset = 8
override val cryAnimation = CryProvider { bedrockStateful("glimmet", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("glimmet", "blink") }
sleep = registerPose(
poseType = PoseType.SLEEP,
animations = arrayOf(bedrock("glimmet", "sleep"))
)
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("glimmet", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("glimmet", "ground_walk")
)
)
shoulderLeft = registerPose(
poseType = PoseType.SHOULDER_LEFT,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("glimmet", "ground_idle")
),
transformedParts = arrayOf(
rootPart.createTransformation().addPosition(ModelPartTransformation.X_AXIS, shoulderOffset)
)
)
shoulderRight = registerPose(
poseType = PoseType.SHOULDER_RIGHT,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("glimmet", "ground_idle")
),
transformedParts = arrayOf(
rootPart.createTransformation().addPosition(ModelPartTransformation.X_AXIS, -shoulderOffset)
)
)
}
override fun getFaintAnimation(state: PosableState) = if (state.isPosedIn(standing, walk)) bedrockStateful("glimmet", "faint") else null
}
@@ -1,79 +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.gen9
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
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.Pose
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.util.isBattling
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class GlimmoraModel (root: ModelPart) : PokemonPosableModel(root) {
override val rootPart = root.registerChildWithAllChildren("glimmora")
override var portraitScale = 2.0F
override var portraitTranslation = Vec3(-0.6, -0.1, 0.0)
override var profileScale = 0.7F
override var profileTranslation = Vec3(0.0, 0.8, 0.0)
lateinit var standing: Pose
lateinit var walk: Pose
lateinit var sleep: Pose
lateinit var battleidle: Pose
override val cryAnimation = CryProvider { bedrockStateful("glimmora", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("glimmora", "blink") }
sleep = registerPose(
poseType = PoseType.SLEEP,
animations = arrayOf(bedrock("glimmora", "sleep"))
)
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
transformTicks = 10,
condition = { !it.isBattling },
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("glimmora", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("glimmora", "ground_walk")
)
)
battleidle = registerPose(
poseName = "battle_idle",
poseTypes = PoseType.STATIONARY_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
condition = { it.isBattling },
animations = arrayOf(
bedrock("glimmora", "battle_idle")
)
)
}
override fun getFaintAnimation(state: PosableState) = if (state.isPosedIn(standing, walk, sleep)) bedrockStateful("glimmora", "faint") else
if (state.isPosedIn(battleidle)) bedrockStateful("glimmora", "faint2")
else null
}
@@ -304,8 +304,6 @@ object VaryingModelRepository {
inbuilt("sizzlipede", ::SizzlipedeModel)
inbuilt("kleavor", ::KleavorModel)
inbuilt("pyukumuku", ::PyukumukuModel)
inbuilt("deerling", ::DeerlingModel)
inbuilt("sawsbuck", ::SawsbuckModel)
inbuilt("natu", ::NatuModel)
inbuilt("xatu", ::XatuModel)
inbuilt("milcery", :: MilceryModel)
@@ -399,8 +397,6 @@ object VaryingModelRepository {
inbuilt("sirfetchd", ::SirfetchdModel)
inbuilt("duskull", ::DuskullModel)
inbuilt("dusclops", ::DusclopsModel)
inbuilt("glimmet", ::GlimmetModel)
inbuilt("glimmora", ::GlimmoraModel)
inbuilt("bonsly", ::BonslyModel)
inbuilt("sudowoodo", ::SudowoodoModel)
inbuilt("cetoddle", ::CetoddleModel)
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.command
import com.cobblemon.mod.common.api.permission.CobblemonPermissions
import com.cobblemon.mod.common.api.pokemon.PokemonPropertyExtractor
import com.cobblemon.mod.common.command.argument.PokemonPropertiesArgumentType
import com.cobblemon.mod.common.util.party
import com.cobblemon.mod.common.util.permission
@@ -45,7 +44,7 @@ object TestPartySlotCommand {
val player = context.player(PLAYER)
val slot = IntegerArgumentType.getInteger(context, SLOT)
val properties = PokemonPropertiesArgumentType.getPokemonProperties(context, PROPERTIES)
return if (player.party().get(slot - 1)?.createPokemonProperties(PokemonPropertyExtractor.ALL)
?.let { properties.isSubSetOf(it) } == true) Command.SINGLE_SUCCESS else NO_SUCCESS
val target = player.party().get(slot - 1) ?: return NO_SUCCESS
return if (properties.matches(target)) Command.SINGLE_SUCCESS else NO_SUCCESS
}
}
@@ -15,6 +15,7 @@ import com.cobblemon.mod.common.api.berry.Berries
import com.cobblemon.mod.common.api.cooking.Seasonings
import com.cobblemon.mod.common.api.data.DataProvider
import com.cobblemon.mod.common.api.data.DataRegistry
import com.cobblemon.mod.common.api.data.ServerJsonDataRegistry
import com.cobblemon.mod.common.api.dialogue.Dialogues
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.fishing.PokeRods
@@ -51,12 +52,16 @@ import com.cobblemon.mod.common.pokemon.properties.PropertiesCompletionProvider
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.ifClient
import com.cobblemon.mod.common.util.server
import net.minecraft.core.HolderLookup
import com.cobblemon.mod.common.world.biome.CobblemonBiomeLoader
import java.util.UUID
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.server.packs.resources.PreparableReloadListener
import net.minecraft.server.packs.resources.ResourceManager
import net.minecraft.server.packs.resources.ResourceManagerReloadListener
import org.jetbrains.annotations.ApiStatus
object CobblemonDataProvider : DataProvider {
@@ -67,6 +72,10 @@ object CobblemonDataProvider : DataProvider {
private val scheduledActions = mutableMapOf<UUID, MutableList<() -> Unit>>()
// client is public only so we can use it cross-module. maybe a more elegant solution exists, but this works
@ApiStatus.Internal
val clientResourceReloader: PreparableReloadListener = ClientResourceReloader()
fun registerDefaults() {
this.register(CobblemonScripts, reloadable = true)
this.register(SpeciesFeatures, reloadable = false)
@@ -83,6 +92,7 @@ object CobblemonDataProvider : DataProvider {
this.register(PokeBalls, reloadable = false)
this.register(PropertiesCompletionProvider, reloadable = false)
this.register(CobblemonPartyPools, reloadable = false)
this.register(CobblemonBiomeLoader, reloadable = true)
this.register(CobblemonPartyCompositions, reloadable = false)
this.register(SpawnDetailPresets, reloadable = true)
this.register(CobblemonSpawnRules, reloadable = true)
@@ -119,9 +129,9 @@ object CobblemonDataProvider : DataProvider {
}
ifClient {
Cobblemon.implementation.registerResourceReloader(cobblemonResource("client_resources"), SimpleResourceReloader(PackType.CLIENT_RESOURCES), PackType.CLIENT_RESOURCES, emptyList())
Cobblemon.implementation.registerResourceReloader(cobblemonResource("client_resources"), PackType.CLIENT_RESOURCES, emptyList()) { clientResourceReloader }
}
Cobblemon.implementation.registerResourceReloader(cobblemonResource("data_resources"), SimpleResourceReloader(PackType.SERVER_DATA), PackType.SERVER_DATA, emptyList())
Cobblemon.implementation.registerResourceReloader(cobblemonResource("data_resources"), PackType.SERVER_DATA, emptyList()) { ServerResourceReloader(it) }
}
override fun <T : DataRegistry> register(registry: T, reloadable: Boolean): T {
@@ -160,12 +170,25 @@ object CobblemonDataProvider : DataProvider {
}
}
private class SimpleResourceReloader(private val type: PackType) : ResourceManagerReloadListener {
private class ClientResourceReloader : ResourceManagerReloadListener {
override fun onResourceManagerReload(manager: ResourceManager) {
registries.filter { it.type == PackType.CLIENT_RESOURCES }.forEach { it.reload(manager) }
}
}
private class ServerResourceReloader(private val registryAccess: HolderLookup.Provider) : ResourceManagerReloadListener {
override fun onResourceManagerReload(manager: ResourceManager) {
// Check for a server running, this is due to the create a world screen triggering datapack reloads, these are fine to happen as many times as needed as players may be in the process of adding their datapacks.
val reloadAllowed = server()?.isReady != true
registries.filter { it.type == this.type && (reloadAllowed || it in reloadableRegistries) }
.forEach { it.reload(manager) }
registries
.filter { it.type == PackType.SERVER_DATA && (reloadAllowed || it in reloadableRegistries) }
.forEach {
if (it is ServerJsonDataRegistry<*>) {
it.reload(manager, registryAccess)
} else {
it.reload(manager)
}
}
}
}
}
@@ -31,8 +31,6 @@ interface EVIncreaseItem: PokemonSelectingItem {
override fun canUseOnPokemon(stack: ItemStack, pokemon: Pokemon) = pokemon.evs.getOrDefault(stat) < EVs.MAX_STAT_VALUE &&
super.canUseOnPokemon(stack, pokemon)
val sound: SoundEvent
override fun applyToPokemon(
player: ServerPlayer,
stack: ItemStack,
@@ -40,14 +38,12 @@ interface EVIncreaseItem: PokemonSelectingItem {
): InteractionResultHolder<ItemStack> {
val evsGained = pokemon.evs.add(stat, evIncreaseAmount, ItemEvSource(player, stack, pokemon))
return if (evsGained > 0) {
pokemon.entity?.playSound(sound, 1F, 1F)
stack.consume(1, player)
if (!player.hasInfiniteMaterials()) {
player.giveOrDropItemStack(ItemStack(Items.GLASS_BOTTLE))
}
onSuccessfulApply(player, stack, pokemon)
InteractionResultHolder.success(stack)
} else {
InteractionResultHolder.fail(stack)
}
}
fun onSuccessfulApply(player: ServerPlayer, stack: ItemStack, pokemon: Pokemon)
}
@@ -11,8 +11,8 @@ package com.cobblemon.mod.common.item.interactive
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.item.CobblemonItem
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.server.level.ServerPlayer
import net.minecraft.sounds.SoundEvent
import net.minecraft.world.InteractionHand
import net.minecraft.world.InteractionResultHolder
import net.minecraft.world.entity.player.Player
@@ -21,7 +21,6 @@ import net.minecraft.world.level.Level
class FeatherItem(override val stat: Stat): CobblemonItem(Properties()), EVIncreaseItem {
override val evIncreaseAmount: Int = 1
override val sound: SoundEvent = CobblemonSounds.MEDICINE_FEATHER_USE
override fun use(world: Level, user: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> {
if (user is ServerPlayer) {
@@ -29,4 +28,9 @@ class FeatherItem(override val stat: Stat): CobblemonItem(Properties()), EVIncre
}
return InteractionResultHolder.success(user.getItemInHand(hand))
}
override fun onSuccessfulApply(player: ServerPlayer, stack: ItemStack, pokemon: Pokemon) {
pokemon.entity?.playSound(CobblemonSounds.MEDICINE_FEATHER_USE)
stack.consume(1, player)
}
}
@@ -13,7 +13,6 @@ import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.item.CobblemonItem
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.server.level.ServerPlayer
import net.minecraft.sounds.SoundEvent
import net.minecraft.world.InteractionHand
import net.minecraft.world.InteractionResultHolder
import net.minecraft.world.entity.player.Player
@@ -22,7 +21,6 @@ import net.minecraft.world.level.Level
class MochiItem(override val stat: Stat): CobblemonItem(Properties()), EVIncreaseItem {
override val evIncreaseAmount: Int = 4
override val sound: SoundEvent = CobblemonSounds.MOCHI_USE
override fun applyToPokemon(player: ServerPlayer, stack: ItemStack, pokemon: Pokemon): InteractionResultHolder<ItemStack> {
if (!canUseOnPokemon(stack, pokemon)) {
@@ -40,4 +38,9 @@ class MochiItem(override val stat: Stat): CobblemonItem(Properties()), EVIncreas
}
return InteractionResultHolder.success(user.getItemInHand(hand))
}
override fun onSuccessfulApply(player: ServerPlayer, stack: ItemStack, pokemon: Pokemon) {
pokemon.entity?.playSound(CobblemonSounds.MOCHI_USE)
stack.consume(1, player)
}
}
@@ -10,19 +10,20 @@ package com.cobblemon.mod.common.item.interactive
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.giveOrDropItemStack
import net.minecraft.server.level.ServerPlayer
import net.minecraft.sounds.SoundEvent
import net.minecraft.world.InteractionHand
import net.minecraft.world.InteractionResultHolder
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemNameBlockItem
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block
class VitaminItem(override val stat: Stat, block: Block): ItemNameBlockItem(block, Properties()), EVIncreaseItem {
override val evIncreaseAmount: Int = 10
override val sound: SoundEvent = CobblemonSounds.MEDICINE_PILLS_USE
override fun use(world: Level, user: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> {
if (user is ServerPlayer) {
@@ -30,4 +31,12 @@ class VitaminItem(override val stat: Stat, block: Block): ItemNameBlockItem(bloc
}
return InteractionResultHolder.success(user.getItemInHand(hand))
}
override fun onSuccessfulApply(player: ServerPlayer, stack: ItemStack, pokemon: Pokemon) {
pokemon.entity?.playSound(CobblemonSounds.MEDICINE_PILLS_USE)
stack.consume(1, player)
if (!player.hasInfiniteMaterials()) {
player.giveOrDropItemStack(Items.GLASS_BOTTLE.defaultInstance)
}
}
}
@@ -17,7 +17,7 @@ import net.minecraft.resources.ResourceKey
object CobblemonParticles : PlatformRegistry<Registry<ParticleType<*>>, ResourceKey<Registry<ParticleType<*>>>, ParticleType<*>>() {
override val registry: Registry<ParticleType<*>> = BuiltInRegistries.PARTICLE_TYPE
override var registry: Registry<ParticleType<*>>? = BuiltInRegistries.PARTICLE_TYPE
override val resourceKey: ResourceKey<Registry<ParticleType<*>>> = Registries.PARTICLE_TYPE
val SNOWSTORM_PARTICLE_TYPE = this.create("snowstorm", SnowstormParticleType())
@@ -26,66 +26,58 @@ import net.minecraft.resources.ResourceLocation
* @author Licious
* @since February 11th, 2023
*/
abstract class PlatformRegistry<R : Registry<T>, K : ResourceKey<R>, T> {
abstract class PlatformRegistry<R : Registry<T>, K : ResourceKey<R>, T>(
open var registry: R? = null,
open val resourceKey: K? = null
) {
protected val queue = hashMapOf<ResourceLocation, T>()
/**
* The vanilla [Registry].
*/
abstract val registry: R
/**
* Creates a new entry in this registry.
*
* @param E The type of the entry.
* @param name The name of the entry, this will be an [ResourceLocation.path].
* @param entry The entry being added.
* @return The entry created.
*/
open fun <E : T> create(name: String, entry: E): E {
val resourceLocation = cobblemonResource(name)
return this.create(resourceLocation, entry)
}
/**
* The vanilla [ResourceKey].
*/
abstract val resourceKey: K
/**
* Creates a new entry in this registry. Introduced for sidemod usage
*
* @param E The type of the entry.
* @param resourceLocation The name of the entry, this will be an [ResourceLocation.path].
* @param entry The entry being added.
* @return The entry created.
*/
open fun <E : T> create(resourceLocation: ResourceLocation, entry: E): E {
this.queue[resourceLocation] = entry
if (entry is BagItemLike) {
BagItems.bagItems.add(
priority = Priority.NORMAL,
value = entry
)
}
return entry
}
protected val queue = hashMapOf<ResourceLocation, T>()
/**
* Handles the registration of this registry into the platform specific one.
*
* @param consumer The consumer that will handle the logic for registering every entry in this registry into the platform specific one.
*/
open fun register(consumer: (ResourceLocation, T) -> Unit) {
this.queue.forEach(consumer)
}
/**
* Creates a new entry in this registry.
*
* @param E The type of the entry.
* @param name The name of the entry, this will be an [ResourceLocation.path].
* @param entry The entry being added.
* @return The entry created.
*/
open fun <E : T> create(name: String, entry: E): E {
val resourceLocation = cobblemonResource(name)
return this.create(resourceLocation, entry)
}
/**
* Creates a new entry in this registry. Introduced for sidemod usage
*
* @param E The type of the entry.
* @param name The name of the entry, this will be an [ResourceLocation.path].
* @param entry The entry being added.
* @return The entry created.
*/
open fun <E : T> create(resourceLocation: ResourceLocation, entry: E): E {
this.queue[resourceLocation] = entry
if (entry is BagItemLike) {
BagItems.bagItems.add(
priority = Priority.NORMAL,
value = entry
)
}
return entry
}
/**
* Handles the registration of this registry into the platform specific one.
*
* @param consumer The consumer that will handle the logic for registering every entry in this registry into the platform specific one.
*/
open fun register(consumer: (ResourceLocation, T) -> Unit) {
this.queue.forEach(consumer)
}
/**
* Returns a collection of every entry in this registry.
*
* @return The entries of this registry.
*/
open fun all(): Collection<T> = this.queue.values.toList()
/**
* Returns a collection of every entry in this registry.
*
* @return The entries of this registry.
*/
open fun all(): Collection<T> = this.queue.values.toList()
}
@@ -68,6 +68,7 @@ object CobblemonRequirementAdapter : RequirementAdapter {
this.registerType(AdvancementRequirement.ADAPTER_VARIANT, AdvancementRequirement::class)
this.registerType(OwnerHoldsItemRequirement.ADAPTER_VARIANT, OwnerHoldsItemRequirement::class)
this.registerType(ChanceRequirement.ADAPTER_VARIANT, ChanceRequirement::class)
this.registerType(NegateRequirement.ADAPTER_VARIANT, NegateRequirement::class)
}
val gson: Gson by lazy {
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.pokemon.evolution.adapters
import com.cobblemon.mod.common.util.adapters.CodecBackedAdapter
import com.cobblemon.mod.common.util.adapters.RegistryAccessResolver
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
@@ -19,6 +20,7 @@ import net.minecraft.advancements.critereon.ItemCustomDataPredicate
import net.minecraft.advancements.critereon.ItemPredicate
import net.minecraft.advancements.critereon.ItemSubPredicates
import net.minecraft.advancements.critereon.NbtPredicate
import net.minecraft.core.HolderLookup
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.core.registries.Registries
import net.minecraft.resources.ResourceLocation
@@ -26,13 +28,16 @@ import net.minecraft.tags.TagKey
import java.lang.reflect.Type
// This is an ugly adapter because breaking changes are scary and people would ;(
object LegacyItemConditionWrapperAdapter : JsonDeserializer<ItemPredicate>, JsonSerializer<ItemPredicate> {
class LegacyItemConditionWrapperAdapter(val registryAccess: HolderLookup.Provider = RegistryAccessResolver.getAccess())
: JsonDeserializer<ItemPredicate>, JsonSerializer<ItemPredicate> {
private const val TAG_PREFIX = "#"
private const val LEGACY_ITEM = "item"
private const val LEGACY_NBT = "nbt"
companion object {
private const val TAG_PREFIX = "#"
private const val LEGACY_ITEM = "item"
private const val LEGACY_NBT = "nbt"
}
private val codecAdapter = CodecBackedAdapter(ItemPredicate.CODEC)
private val codecAdapter = CodecBackedAdapter(ItemPredicate.CODEC, registryAccess)
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): ItemPredicate {
if (json.isJsonPrimitive) {
@@ -64,7 +69,9 @@ object LegacyItemConditionWrapperAdapter : JsonDeserializer<ItemPredicate>, Json
{ id ->
if (isTag) {
val tag = TagKey.create(Registries.ITEM, id)
builder.of(tag)
registryAccess.lookup(Registries.ITEM)
.map { builder.of(tag) }
.orElseGet { builder }
} else {
BuiltInRegistries.ITEM.getOptional(id)
.map { builder.of(it) }
@@ -75,4 +82,4 @@ object LegacyItemConditionWrapperAdapter : JsonDeserializer<ItemPredicate>, Json
)
}
}
}
@@ -0,0 +1,21 @@
/*
* 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.pokemon.requirements
import com.cobblemon.mod.common.api.pokemon.requirement.Requirement
import com.cobblemon.mod.common.pokemon.Pokemon
class NegateRequirement(val inner: Requirement) : Requirement {
override fun check(pokemon: Pokemon) = !inner.check(pokemon)
companion object {
const val ADAPTER_VARIANT = "negate"
}
}
@@ -52,6 +52,10 @@ fun Collection<JsonElement>.toJsonArray(): JsonArray {
return array
}
fun JsonElement.asJsonObjectOrNull(): JsonObject? =
if (isJsonObject) asJsonObject else null
fun JsonObject.isEmpty() = size() <= 0
fun JsonObject.isNotEmpty() = size() > 0
@@ -10,16 +10,19 @@ package com.cobblemon.mod.common.util.adapters
import com.google.gson.*
import com.mojang.serialization.Codec
import com.mojang.serialization.JsonOps
import net.minecraft.core.HolderLookup
import net.minecraft.resources.RegistryOps
import java.lang.reflect.Type
class CodecBackedAdapter<T>(val codec: Codec<T>) : JsonDeserializer<T>, JsonSerializer<T> {
class CodecBackedAdapter<T>(val codec: Codec<T>, val registryAccess: HolderLookup.Provider = RegistryAccessResolver.getAccess()) : JsonDeserializer<T>, JsonSerializer<T> {
override fun deserialize(
jElement: JsonElement,
typeOfT: Type,
context: JsonDeserializationContext
): T {
val ops = RegistryOpsProvider.getOpsWithDefaultFallback()
val ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess)
val result = ops.withDecoder(codec).apply(jElement)
return result.result().orElseThrow {
IllegalStateException("Failed to deserialize $jElement: ${result.error().orElse(null)}")
@@ -31,7 +34,7 @@ class CodecBackedAdapter<T>(val codec: Codec<T>) : JsonDeserializer<T>, JsonSeri
typeOfSrc: Type?,
context: JsonSerializationContext?
): JsonElement {
val ops = RegistryOpsProvider.getOpsWithDefaultFallback()
val ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess)
val result = ops.withEncoder(codec).apply(src)
return result.result().orElseThrow {
IllegalStateException("Failed to serialize $src: ${result.error().orElse(null)}")
@@ -15,27 +15,33 @@ 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
/**
* Utility for obtaining RegistryOps with the correct client/server registry access.
* Used when manually encoding/decoding registry-backed values (e.g., items, predicates).
* 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 RegistryOpsProvider {
fun getOpsWithDefaultFallback(): DynamicOps<JsonElement> {
object RegistryAccessResolver {
fun getAccess(): RegistryAccess {
try {
Cobblemon.implementation
} catch (e: UninitializedPropertyAccessException) {
return RegistryAccess.EMPTY
}
if (Cobblemon.implementation.environment() == Environment.CLIENT) {
val minecraft = Minecraft.getInstance()
if (minecraft != null && minecraft.level != null)
return RegistryOps.create(JsonOps.INSTANCE, minecraft.level!!.registryAccess())
return minecraft.level!!.registryAccess()
}
if (Cobblemon.implementation.environment() == Environment.SERVER) {
val server = server()
if (server != null)
return RegistryOps.create(JsonOps.INSTANCE, server.registryAccess())
return server.registryAccess()
}
return JsonOps.INSTANCE
return RegistryAccess.EMPTY
}
}
@@ -0,0 +1,35 @@
/*
* 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.util.biome
import com.cobblemon.mod.common.Cobblemon
import net.minecraft.core.HolderLookup
import net.minecraft.core.Registry
import net.minecraft.core.RegistryAccess
import net.minecraft.resources.ResourceKey
import java.util.*
object RegistryExtensions {
@JvmStatic
fun <T> getRegistryLookup(
registryKey: ResourceKey<out Registry<T>>
): Optional<HolderLookup.RegistryLookup<T>> {
return Cobblemon.registryAccess.getRegistryLookup(registryKey)
}
}
fun <T> RegistryAccess?.getRegistryLookup(
registryKey: ResourceKey<out Registry<T>>
): Optional<HolderLookup.RegistryLookup<T>> {
if (this == null || this == RegistryAccess.EMPTY) {
return Optional.empty()
}
return this.lookup(registryKey)
}
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.api.abilities.Abilities
import com.cobblemon.mod.common.api.abilities.Ability
import com.cobblemon.mod.common.api.moves.BenchedMoves
import com.cobblemon.mod.common.api.moves.MoveSet
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
import com.cobblemon.mod.common.pokemon.EVs
import com.cobblemon.mod.common.pokemon.FormData
import com.cobblemon.mod.common.pokemon.Gender
@@ -21,20 +22,22 @@ import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.Species
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.codec.CodecUtils
import com.cobblemon.mod.common.util.codec.internal.ClientPokemonP1.Companion.FEATURES
import com.cobblemon.mod.common.util.codec.internal.ClientPokemonP1.Companion.FEATURE_ID
import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import java.util.Optional
import java.util.UUID
import net.minecraft.core.UUIDUtil
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.ComponentSerialization
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.StringTag
import net.minecraft.nbt.Tag
internal class PokemonP1(
val uuid: UUID,
val species: Species,
val form: FormData,
val nickname: Optional<Component>,
val features: List<CompoundTag>,
val level: Int,
val experience: Int,
val friendship: Int,
@@ -55,7 +58,22 @@ internal class PokemonP1(
other.ability = this.ability
other.species = this.species
other.form = this.form
this.nickname.ifPresent { other.nickname = it.copy() }
this.features.forEach { featureNbt ->
val featureId = featureNbt.getString(FEATURE_ID)
if (featureId.isEmpty()) {
return@forEach
}
val speciesFeatureProviders = SpeciesFeatures.getFeaturesFor(other.species)
val feature = speciesFeatureProviders.firstNotNullOfOrNull { provider -> provider(featureNbt) } ?: return@forEach
if (
featureNbt.contains("keys", Tag.TAG_STRING.toInt()) &&
!featureNbt.getList("keys", Tag.TAG_STRING.toInt()).contains(StringTag.valueOf(featureId))
) {
return@forEach
}
other.features.removeIf { it.name == feature.name }
other.features.add(feature)
}
other.level = this.level
other.experience = this.experience
other.setFriendship(this.friendship)
@@ -91,7 +109,7 @@ internal class PokemonP1(
UUIDUtil.LENIENT_CODEC.fieldOf(DataKeys.POKEMON_UUID).forGetter(PokemonP1::uuid),
Species.BY_IDENTIFIER_CODEC.fieldOf(DataKeys.POKEMON_SPECIES_IDENTIFIER).forGetter(PokemonP1::species),
Codec.STRING.fieldOf(DataKeys.POKEMON_FORM_ID).forGetter { pokemon -> pokemon.form.formOnlyShowdownId() },
ComponentSerialization.CODEC.optionalFieldOf(DataKeys.POKEMON_NICKNAME).forGetter(PokemonP1::nickname),
Codec.list(CompoundTag.CODEC).optionalFieldOf(FEATURES, emptyList()).forGetter(PokemonP1::features),
CodecUtils.dynamicIntRange(1) { Cobblemon.config.maxPokemonLevel }.fieldOf(DataKeys.POKEMON_LEVEL).forGetter(PokemonP1::level),
Codec.intRange(0, Int.MAX_VALUE).fieldOf(DataKeys.POKEMON_EXPERIENCE).forGetter(PokemonP1::experience),
CodecUtils.dynamicIntRange(0) { Cobblemon.config.maxPokemonFriendship }.fieldOf(DataKeys.POKEMON_FRIENDSHIP).forGetter(PokemonP1::friendship),
@@ -104,9 +122,9 @@ internal class PokemonP1(
Codec.FLOAT.fieldOf(DataKeys.POKEMON_SCALE_MODIFIER).forGetter(PokemonP1::scaleModifier),
Codec.BOOL.fieldOf(DataKeys.POKEMON_SHINY).forGetter(PokemonP1::shiny),
Ability.CODEC.fieldOf(DataKeys.POKEMON_ABILITY).forGetter(PokemonP1::ability)
).apply(instance) { uuid, species, formId, nickname, level, experience, friendship, currentHealth, gender, ivs, evs, moveSet, benchedMoves, scaleModifier, shiny, ability ->
).apply(instance) { uuid, species, formId, features, level, experience, friendship, currentHealth, gender, ivs, evs, moveSet, benchedMoves, scaleModifier, shiny, ability ->
val form = species.forms.firstOrNull { it.formOnlyShowdownId() == formId } ?: species.standardForm
PokemonP1(uuid, species, form, nickname, level, experience, friendship, currentHealth, gender, ivs, evs, moveSet, benchedMoves, scaleModifier, shiny, ability)
PokemonP1(uuid, species, form, features, level, experience, friendship, currentHealth, gender, ivs, evs, moveSet, benchedMoves, scaleModifier, shiny, ability)
}
}
@@ -114,7 +132,11 @@ internal class PokemonP1(
pokemon.uuid,
pokemon.species,
pokemon.form,
Optional.ofNullable(pokemon.nickname),
pokemon.features.map { feature ->
val nbt = CompoundTag()
nbt.putString(FEATURE_ID, feature.name)
feature.saveToNBT(nbt)
},
pokemon.level,
pokemon.experience,
pokemon.friendship,
@@ -14,15 +14,12 @@ import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.pokemon.OriginalTrainerType
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.codec.internal.ClientPokemonP1.Companion.FEATURES
import com.cobblemon.mod.common.util.codec.internal.ClientPokemonP1.Companion.FEATURE_ID
import com.cobblemon.mod.common.util.codec.optionalFieldOfWithDefault
import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.StringTag
import net.minecraft.nbt.Tag
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.ComponentSerialization
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.item.ItemStack
import java.util.*
@@ -31,7 +28,7 @@ internal data class PokemonP3(
val originalTrainerType: OriginalTrainerType,
val originalTrainer: Optional<String>,
val forcedAspects: Set<String>,
val features: List<CompoundTag>,
val nickname: Optional<Component>,
val heldItemVisible: Optional<Boolean>,
val canDropHeldItem: Optional<Boolean>,
val cosmeticItem: ItemStack,
@@ -51,22 +48,7 @@ internal data class PokemonP3(
this.originalTrainer.ifPresent { other.originalTrainer = it }
other.refreshOriginalTrainer()
other.forcedAspects = this.forcedAspects
this.features.forEach { featureNbt ->
val featureId = featureNbt.getString(FEATURE_ID)
if (featureId.isEmpty()) {
return@forEach
}
val speciesFeatureProviders = SpeciesFeatures.getFeaturesFor(other.species)
val feature = speciesFeatureProviders.firstNotNullOfOrNull { provider -> provider(featureNbt) } ?: return@forEach
if (
featureNbt.contains("keys", Tag.TAG_STRING.toInt()) &&
!featureNbt.getList("keys", Tag.TAG_STRING.toInt()).contains(StringTag.valueOf(featureId))
) {
return@forEach
}
other.features.removeIf { it.name == feature.name }
other.features.add(feature)
}
this.nickname.ifPresent { other.nickname = it.copy() }
this.heldItemVisible.ifPresent { other.heldItemVisible = it }
this.canDropHeldItem.ifPresent { other.canDropHeldItem = it }
this.cosmeticItem.let { other.cosmeticItem = it }
@@ -92,7 +74,7 @@ internal data class PokemonP3(
OriginalTrainerType.CODEC.optionalFieldOfWithDefault(DataKeys.POKEMON_ORIGINAL_TRAINER_TYPE, OriginalTrainerType.NONE).forGetter(PokemonP3::originalTrainerType),
Codec.STRING.optionalFieldOf(DataKeys.POKEMON_ORIGINAL_TRAINER).forGetter(PokemonP3::originalTrainer),
Codec.list(Codec.STRING).optionalFieldOf(DataKeys.POKEMON_FORCED_ASPECTS, emptyList()).forGetter { it.forcedAspects.toMutableList() },
Codec.list(CompoundTag.CODEC).optionalFieldOf(FEATURES, emptyList()).forGetter(PokemonP3::features),
ComponentSerialization.CODEC.optionalFieldOf(DataKeys.POKEMON_NICKNAME).forGetter(PokemonP3::nickname),
Codec.BOOL.optionalFieldOf(DataKeys.HELD_ITEM_VISIBLE).forGetter(PokemonP3::heldItemVisible),
Codec.BOOL.optionalFieldOf(DataKeys.HELD_ITEM_AI_DROPPABLE).forGetter(PokemonP3::canDropHeldItem),
ItemStack.CODEC.optionalFieldOf(DataKeys.POKEMON_COSMETIC_ITEM).forGetter { Optional.ofNullable(it.cosmeticItem.takeIf { !it.isEmpty }) },
@@ -109,7 +91,7 @@ internal data class PokemonP3(
originalTrainerType,
originalTrainer,
forcedAspects,
features,
nickname,
heldItemVisible,
canDropHeldItem,
cosmeticItem,
@@ -125,7 +107,7 @@ internal data class PokemonP3(
originalTrainerType,
originalTrainer,
forcedAspects.toSet(),
features,
nickname,
heldItemVisible,
canDropHeldItem,
cosmeticItem.orElse(ItemStack.EMPTY),
@@ -146,11 +128,7 @@ internal data class PokemonP3(
pokemon.originalTrainerType,
Optional.ofNullable(pokemon.originalTrainer),
pokemon.forcedAspects,
pokemon.features.map { feature ->
val nbt = CompoundTag()
nbt.putString(FEATURE_ID, feature.name)
feature.saveToNBT(nbt)
},
Optional.ofNullable(pokemon.nickname),
Optional.ofNullable(pokemon.heldItemVisible),
Optional.ofNullable(pokemon.canDropHeldItem),
pokemon.cosmeticItem,
@@ -53,4 +53,60 @@ fun Quaternionf.toEulerXYZDegrees(): Vector3f {
vector.z = vector.z.toDegrees()
return vector
}
/* // is not allocation free so just inline ".get(AxisAngle4f)" like in the function below this
fun Quaternionf.toScaledAngleAxis(angAxisScaled: Vector3f) {
val axisAng4 = AxisAngle4f()
this.get(axisAng4)
angAxisScaled.x = axisAng4.x
angAxisScaled.y = axisAng4.y
angAxisScaled.z = axisAng4.z
angAxisScaled.mul(axisAng4.angle)
} */
fun Quaternionf.toScaledAngleAxis(dest: Vector3f) {
var x = this.x
var y = this.y
var z = this.z
var w = this.w
if (w > 1.0f) {
val invNorm = 1.0f / kotlin.math.sqrt(x * x + y * y + z * z + w * w)
x *= invNorm
y *= invNorm
z *= invNorm
w *= invNorm
}
val angle = 2.0f * kotlin.math.acos(w)
val s = kotlin.math.sqrt(1.0f - w * w)
if (s < 0.001f) {
dest.x = x * angle
dest.y = y * angle
dest.z = z * angle
} else {
val invS = angle / s
dest.x = x * invS
dest.y = y * invS
dest.z = z * invS
}
}
fun Quaternionf.setFromScaledAngleAxis(angAxisScaled: Vector3f) {
val len = angAxisScaled.length()
if (len > 1e-5f) {
this.rotationAxis(
len,
angAxisScaled.x / len,
angAxisScaled.y / len,
angAxisScaled.z / len
)
} else {
this.identity()
}
}
@@ -0,0 +1,163 @@
/*
* 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.util.math
import net.minecraft.util.Mth
import kotlin.math.max
import kotlin.math.sqrt
import kotlin.math.abs
import org.joml.Quaternionf
import org.joml.Vector3f
/**
* Much of the code here is directly taken or inspired off this great article:
* https://theorangeduck.com/page/spring-roll-call#critical
*
* Very much worth the read.
*
* Util funcitons for using springs to smoothly/naturally approach one value towards a target
*/
/**
* Need to pass in and mutate both a value and a velocity to a spring
*/
class FloatSpring(
var value: Float = 0f,
var velocity: Float = 0f
)
class QuatSpring(
var quat: Quaternionf = Quaternionf(),
var angVel: Vector3f = Vector3f()
)
/**
* Provides a fast negative exponent approximation for e^-x
*/
fun negExp(x: Float): Float {
return 1.0f / (1.0f + x + 0.48f*x*x + 0.235f*x*x*x)
}
fun halflifeToDamping(halflife: Float, eps: Float = 1e-5f): Float {
return (4.0f * 0.69314718056f) / (halflife + eps)
}
fun simpleSpringDamper(
spring: FloatSpring,
goalValue: Float,
halflife: Float,
dt: Float,
isAngle: Boolean = false
) {
val y = halflifeToDamping(halflife) / 2.0f
val j0 = if (isAngle) Mth.wrapDegrees(spring.value - goalValue) else spring.value - goalValue
val j1 = spring.velocity + j0*y;
val eydt = negExp(y*dt);
spring.value = eydt*(j0 + j1*dt) + goalValue;
spring.velocity = eydt*(spring.velocity - j1*y*dt);
}
fun simpleSpringDamperQuat(
spring: QuatSpring,
goalQuat: Quaternionf,
halflife: Float,
dt: Float
) {
val y = halflifeToDamping(halflife) / 2.0f
// Quaternions have double cover q and -q represent the same rotation.
// so an accidentally rotation the long way could be calculated.
// account for that specifically
val goal = if (spring.quat.dot(goalQuat) < 0f) {
Quaternionf(-goalQuat.x, -goalQuat.y, -goalQuat.z, -goalQuat.w)
} else {
goalQuat
}
// Gather the rotation from the goal to the current
val goalToCurrQuat = Quaternionf(spring.quat).mul(Quaternionf(goal).conjugate())
val j0 = Vector3f()
goalToCurrQuat.toScaledAngleAxis(j0)
val j1: Vector3f = Vector3f(spring.angVel).add(Vector3f(j0).mul(y))
val eydt: Float = negExp(y * dt)
val scaledAxis = Vector3f(j0).add(Vector3f(j1).mul(dt)).mul(eydt)
spring.quat.setFromScaledAngleAxis(scaledAxis)
spring.quat.mul(goal)
spring.angVel.sub(j1.mul(y * dt))
spring.angVel.mul(eydt)
}
/**
* Below is essentially the same code as found here: https://github.com/orangeduck/Spring-It-On/blob/main/inertialeasing.c
* https://theorangeduck.com/page/inertial-easing <- the blogpost
*/
// Class to hold the "time" along the curve/function and scale of the function currently
// Held in this class so both can be mutated and returned in one function call
class InertialVals(
var t: Float = 0f,
var s: Float = 0f,
var offset: Float = 0f,
)
// Includes the negative portion
fun smoothstep(t: Float, s: Float): Float {
return s * (if(t > 1.0f) 1.0f else 3*t*t - 2*t*t*t)
}
// Derivative of the above smoothstep
fun smoothstepDt(t: Float, s: Float): Float {
return s * (if(t > 1.0f) 0.0f else 6*t - 6*t*t)
}
// Solve for smoothstep parameters `t` and `s` given new target `x` and velocity `v`
fun smoothstepSolve( //TODO change these names to make more sense
vals: InertialVals,
x: Float, // Target Value x
v: Float, // Goal velocity
overshoot: Float = 0.05f,
eps: Float = 1e-8f
) {
// If velocity is low enough then start from time zero
if (abs(v) < 1e-8f) {
vals.t = 0f
vals.s = x
return
}
// If x is negative then invert signs and re-Solve
if (x < 0f) {
smoothstepSolve(vals, -x, -v, overshoot, eps)
vals.s *= -1f
return
}
// Find the possible times that solve for the given `x` and `v`
// Quadratic formula!
val q: Float = sqrt(max((6*x - v)*(6*x - v) + 8*v*v, eps))
val t0: Float = (v - 6*x + q) / (4*v)
val t1: Float = (v - 6*x - q) / (4*v)
// Overshoot if the alternative time is between -0.5f and -(0.5f + overshoot)
vals.t = if(-0.5f > t1 && t1 > -(0.5f + overshoot)) t1 else t0;
// Find the un-scaled velocity at the fitted time
val vt: Float = smoothstepDt(vals.t, 1f);
// Find the scaling factor so that the velocity at the fitted time matches
vals.s = if(abs(vt) < eps) 0f else v / vt;
}
@@ -0,0 +1,19 @@
/*
* 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.world.biome
import com.google.gson.JsonElement
import net.minecraft.world.level.biome.Biome
import net.minecraft.world.level.biome.Climate
data class CobblemonBiome(
val biome: Biome,
val parameters: Climate.ParameterPoint,
val customData: Map<String, JsonElement> = emptyMap()
)
@@ -0,0 +1,75 @@
/*
* 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.world.biome
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.world.biome.parse.CobblemonBiomeJsonParser
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.JsonElement
import com.google.gson.reflect.TypeToken
import com.mojang.serialization.JsonOps
import net.minecraft.resources.RegistryOps
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.server.packs.resources.ResourceManager
import java.io.Reader
object CobblemonBiomeLoader : JsonDataRegistry<CobblemonBiome> {
override val gson: Gson = GsonBuilder().setPrettyPrinting().create()
override val typeToken: TypeToken<CobblemonBiome> = TypeToken.get(CobblemonBiome::class.java)
override val resourcePath = "biomes"
override val id = cobblemonResource(resourcePath)
override val type = PackType.SERVER_DATA
override val observable = SimpleObservable<CobblemonBiomeLoader>()
private var loadedBiomes: Map<ResourceLocation, CobblemonBiome> = emptyMap()
@JvmStatic
fun load(
manager: ResourceManager,
registryInfoLookup: RegistryOps.RegistryInfoLookup
): Map<ResourceLocation, CobblemonBiome> {
val ops = RegistryOps.create(JsonOps.INSTANCE, registryInfoLookup)
val result = loadJsonData(manager) { reader, _, _ ->
parseBiome(reader, ops)
}
loadedBiomes = result.toMap()
observable.emit(this)
Cobblemon.LOGGER.info("MEALAM Loaded {} cobblemon biomes.", result.size)
return result
}
private fun parseBiome(
reader: Reader,
ops: RegistryOps<JsonElement>
): CobblemonBiome? = CobblemonBiomeJsonParser.parse(reader, ops)
override fun reload(manager: ResourceManager) = observable.emit(this)
override fun reload(data: Map<ResourceLocation, CobblemonBiome>) = Unit
override fun sync(player: ServerPlayer) = Unit
@JvmStatic
fun getLoadedBiomes(): Map<ResourceLocation, CobblemonBiome> = loadedBiomes
@JvmStatic
fun getLoadedBiome(location: ResourceLocation): CobblemonBiome =
loadedBiomes[location]
?: error("MEALAM Unknown location: $location")
}
@@ -0,0 +1,43 @@
/*
* 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.world.biome.parse
import com.cobblemon.mod.common.Cobblemon
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import com.mojang.serialization.JsonOps
import net.minecraft.resources.RegistryOps
import net.minecraft.world.level.biome.Climate
object ClimateParametersSectionParser : CobblemonBiomeSectionParser {
override val id = "parameters"
override fun parse(
root: JsonObject,
ops: RegistryOps<JsonElement>,
builder: CobblemonBiomeParseBuilder
): Boolean {
val params = root.getAsJsonObject("parameters")
if (params == null) {
Cobblemon.LOGGER.error("MEALAM Missing 'parameters' section in biome JSON")
return false
}
val result = Climate.ParameterPoint.CODEC.parse(JsonOps.INSTANCE, params)
val parameterPoint = result.result().orElse(null)
if (parameterPoint == null) {
val errorMessage = result.error().map { it.message() }.orElse("unknown error")
Cobblemon.LOGGER.error("MEALAM Failed to parse climate parameters: {}", errorMessage)
return false
}
builder.parameters = parameterPoint
return true
}
}
@@ -0,0 +1,45 @@
/*
* 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.world.biome.parse
import com.cobblemon.mod.common.util.asJsonObjectOrNull
import com.cobblemon.mod.common.world.biome.CobblemonBiome
import com.google.gson.JsonElement
import com.google.gson.JsonParser
import net.minecraft.resources.RegistryOps
import java.io.Reader
object CobblemonBiomeJsonParser {
private val sectionParsers = mutableListOf(
VanillaBiomeSectionParser,
ClimateParametersSectionParser,
CustomDataSectionParser(setOf("parameters"))
)
fun parse(reader: Reader, ops: RegistryOps<JsonElement>): CobblemonBiome? {
val root = JsonParser.parseReader(reader).asJsonObjectOrNull() ?: return null
val builder = CobblemonBiomeParseBuilder()
for (parser in getParsers()) {
if (!parser.parse(root, ops, builder) && parser.required) {
return null
}
}
return builder.buildOrNull()
}
fun register(parser: CobblemonBiomeSectionParser) {
sectionParsers.add(parser)
}
private fun getParsers(): List<CobblemonBiomeSectionParser> = sectionParsers.toList()
}
@@ -0,0 +1,27 @@
/*
* 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.world.biome.parse
import com.cobblemon.mod.common.world.biome.CobblemonBiome
import com.google.gson.JsonElement
import net.minecraft.world.level.biome.Biome
import net.minecraft.world.level.biome.Climate
class CobblemonBiomeParseBuilder {
var biome: Biome? = null
var parameters: Climate.ParameterPoint? = null
val customData: MutableMap<String, JsonElement> = linkedMapOf()
fun buildOrNull(): CobblemonBiome? {
val parsedBiome = biome ?: return null
val parsedParameters = parameters ?: return null
return CobblemonBiome(parsedBiome, parsedParameters, customData.toMap())
}
}
@@ -0,0 +1,26 @@
/*
* 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.world.biome.parse
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import net.minecraft.resources.RegistryOps
interface CobblemonBiomeSectionParser {
val id: String
val required: Boolean
get() = true
fun parse(
root: JsonObject,
ops: RegistryOps<JsonElement>,
builder: CobblemonBiomeParseBuilder
): Boolean
}
@@ -0,0 +1,34 @@
/*
* 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.world.biome.parse
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import net.minecraft.resources.RegistryOps
class CustomDataSectionParser(
private val reservedKeys: Set<String>
) : CobblemonBiomeSectionParser {
override val id = "custom_data"
override val required = false
override fun parse(
root: JsonObject,
ops: RegistryOps<JsonElement>,
builder: CobblemonBiomeParseBuilder
): Boolean {
for ((key, value) in root.entrySet()) {
if (key !in reservedKeys) {
builder.customData[key] = value.deepCopy()
}
}
return true
}
}
@@ -0,0 +1,36 @@
/*
* 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.world.biome.parse
import com.cobblemon.mod.common.Cobblemon
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import net.minecraft.resources.RegistryOps
import net.minecraft.world.level.biome.Biome
object VanillaBiomeSectionParser : CobblemonBiomeSectionParser {
override val id = "vanilla_biome"
override fun parse(
root: JsonObject,
ops: RegistryOps<JsonElement>,
builder: CobblemonBiomeParseBuilder
): Boolean {
val result = Biome.DIRECT_CODEC.parse(ops, root)
val biome = result.result().orElse(null)
if (biome == null) {
val errorMessage = result.error().map { it.message() }.orElse("unknown error")
Cobblemon.LOGGER.error("MEALAM Failed to parse vanilla biome: {}", errorMessage)
return false
}
builder.biome = biome
return true
}
}
@@ -18,7 +18,7 @@ import net.minecraft.world.level.levelgen.feature.Feature
object CobblemonFeatures : PlatformRegistry<Registry<Feature<*>>, ResourceKey<Registry<Feature<*>>>, Feature<*>>() {
override val registry: Registry<Feature<*>> = BuiltInRegistries.FEATURE
override var registry: Registry<Feature<*>>? = BuiltInRegistries.FEATURE
override val resourceKey: ResourceKey<Registry<Feature<*>>> = Registries.FEATURE
@JvmField
@@ -43,7 +43,7 @@ class TypeGemFeature : Feature<BlockStateConfiguration>(BlockStateConfiguration.
val coreState = TYPE_GEM_CORE.defaultBlockState()
// Place the gem first then make sure it placed before placing the core.
worldGenLevel.setBlock(origin, coreState, UPDATE_ALL)
worldGenLevel.setBlock(chosenPos, gemState, UPDATE_ALL)
if (!worldGenLevel.getBlockState(chosenPos).`is`(gemBlock)) {
return false
}
@@ -1,192 +1,286 @@
{
"format_version": "1.8.0",
"animations": {
"animation.absol.render": {
"loop": true,
"bones": {
"hips": {
"rotation": [-2.5, 0, 0]
},
"chest": {
"rotation": [12.5, 0, 0]
},
"tail": {
"rotation": [-8.01788, 32.61138, -0.66603]
},
"neck": {
"rotation": [15.34479, -13.10699, 9.18372]
},
"head": {
"rotation": [-25.32307, -13.66763, -4.61603]
},
"hair": {
"rotation": [0, 0, 45]
},
"hair2": {
"rotation": [1.95155, 7.24305, 27.62353]
},
"hair3": {
"rotation": [8.61775, 34.05444, 17.64371]
},
"neck_fur_right2": {
"rotation": [0, 0, -30]
},
"neck_fur_left2": {
"rotation": [0, 0, 40]
},
"neck_fur_back2": {
"rotation": [-45, 0, 0]
},
"chest_fur": {
"rotation": [-42.5, 0, 0]
},
"chest_fur_right": {
"rotation": [0, 35, 0]
},
"chest_fur_left": {
"rotation": [0, -25, 0]
},
"chest_fur_1": {
"rotation": [27.5, 0, 0]
},
"chest_fur_2": {
"rotation": [7.5, 0, 0]
},
"leg_back_left": {
"rotation": [21.9659, -13.44762, -0.11731]
},
"leg_back_left2": {
"rotation": [-10, 0, 0]
},
"leg_back_left3": {
"rotation": [5, 0, 0]
},
"foot__back_left": {
"rotation": [-14.82005, -12.39135, -1.6575]
},
"leg_front_left": {
"rotation": [-25.47479, -17.22575, 3.13388],
"position": [0, -0.25, 0]
},
"leg_front_left2": {
"rotation": [14.61364, 12.22219, 2.52829]
},
"foot_front_left": {
"rotation": [12.15846, -18.26883, -13.36473]
},
"eyes_open": {
"position": [0, 0, 0]
},
"eyes_happy": {
"position": [0, 0, 0]
},
"eyes_closed": {
"position": [0, 0, 0]
},
"eyes_halfclosed": {
"position": [0, 0, 0]
},
"leg_back_right": {
"rotation": [0, -22.5, 0],
"position": [-0.11888, 0, -1.67283]
},
"foot_back_right": {
"rotation": [0, 15, 0]
},
"waist": {
"rotation": [0, -20, 0]
},
"leg_front_right": {
"rotation": [12.5, 0, 0]
},
"leg_front_right2": {
"rotation": [0, 0, -10]
},
"foot_front_right": {
"rotation": [-12.07362, 14.63639, 13.31914]
}
}
},
"animation.absol.ground_idle": {
"loop": true,
"bones": {
"neck": {
"rotation": [20, 0, 0]
"rotation": [30, 0, 0]
},
"head": {
"rotation": [-17.5, 0, 0]
},
"hair": {
"rotation": [0, 0, 47]
"rotation": [0, 0, -2.5]
},
"hair2": {
"rotation": [0, 8.5, 38]
"rotation": [2.5, 10, 0]
},
"hair3": {
"rotation": [9.69368, 25.60466, 13.24197]
"rotation": [1.9544, 7.5589, 12.4168]
},
"neck_fur_right2": {
"rotation": [0, 0, -30]
"neck_fluff_left": {
"rotation": [0, 0, 67.5]
},
"neck_fur_left2": {
"rotation": [0, 0, 40]
},
"neck_fur_back2": {
"neck_fluff_back": {
"rotation": [-45, 0, 0]
},
"chest_fur": {
"rotation": [-30, 0, 0]
"chest_fluff": {
"rotation": [-12.5, 0, 0]
},
"chest_fur_right": {
"rotation": [0, 35, 0]
"chest_fluff_right": {
"rotation": [0, 37.5, 0]
},
"chest_fur_left": {
"rotation": [0, -25, 0]
"chest_fluff_left": {
"rotation": [0, -37.5, 0]
},
"chest_fur_1": {
"rotation": [27.5, 0, 0]
"chest_fluff2": {
"rotation": [-7.5, 0, 0]
},
"chest_fur_2": {
"chest_fluff3": {
"rotation": [7.5, 0, 0]
},
"tail": {
"rotation": [-22.5, 0, 0]
}
}
},
"animation.absol.cry": {
"animation_length": 2,
"bones": {
"mouth": {
"position": {
"0.0": [0, 0, 0.17],
"0.25": {"pre": [0, 0, 0.17], "post": [0, 0, -1]},
"1.7917": {"pre": [0, 0, -1], "post": [0, 0, 0.17]}
}
}
},
"sound_effects": {
"0.04": {
"effect": "pokemon.absol.cry"
"rotation": [10, 0, 0]
},
"neck_fluff_right": {
"rotation": [0, 0, -67.5]
},
"torso": {
"rotation": [2.5, 0, 0]
},
"torso2": {
"rotation": [-5, 0, 0]
},
"head_angle": {
"rotation": [-5, 0, 0]
},
"leg_front_left": {
"rotation": [-4.9083, 1.8509, 2.416]
},
"leg_back_left": {
"rotation": [0, -10, 0]
},
"leg_back_left2": {
"rotation": [5, 0, 0]
},
"leg_back_left3": {
"rotation": [10, 0, 0]
},
"foot_back_left": {
"rotation": [-13.9904, -4.9952, -0.2186]
},
"foot_front_left": {
"rotation": [4.991, -1.4949, -3.2033]
},
"neck_fluff_back2": {
"rotation": [-50, 0, 0]
},
"leg_front_right": {
"rotation": [2.585, -1.6431, -2.3501]
},
"leg_front_right2": {
"rotation": [2.5, 0, 0]
},
"foot_front_right": {
"rotation": [-4.9201, 6.845, 3.7466]
},
"leg_back_right": {
"rotation": [0, 10, 0]
},
"leg_back_right2": {
"rotation": [5, 0, 0]
},
"leg_back_right3": {
"rotation": [10, 0, 0]
},
"foot_back_right": {
"rotation": [-13.9904, 4.9952, 0.2186]
}
}
},
"animation.absol.blink": {
"animation_length": 0.1667,
"bones": {
"eyes_open": {
"eye_left": {
"position": {
"0.0": [0, 0, 1],
"0.125": {"pre": [0, 0, 1], "post": [0, 0, 1]},
"0.1667": {"pre": [0, 0, 1], "post": [0, 0, 0]}
"0.0": [-0.1, 0, 0.1],
"0.1667": {"pre": [-0.1, 0, 0.1], "post": [0, 0, 0]}
}
},
"eyes_closed": {
"eye_right": {
"position": {
"0.0": [0, 0, -1],
"0.125": {"pre": [0, 0, -1], "post": [0, 0, -1]},
"0.1667": {"pre": [0, 0, -1], "post": [0, 0, 0]}
"0.0": [0.1, 0, 0.1],
"0.1667": {"pre": [0.1, 0, 0.1], "post": [0, 0, 0]}
}
},
"eyelid_right": {
"position": {
"0.0": [-0.1, 0, -0.1],
"0.1667": {"pre": [-0.1, 0, -0.1], "post": [0, 0, 0]}
}
},
"eyelid_left": {
"position": {
"0.0": [0.1, 0, -0.1],
"0.1667": {"pre": [0.1, 0, -0.1], "post": [0, 0, 0]}
}
},
"eyes_ditto": {
"position": {
"0.0": [0, 0, 0.1],
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"eyes_blink_ditto": {
"position": {
"0.0": [0, 0, -0.1],
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
}
}
},
"animation.absol.ditto_face": {
"loop": true,
"bones": {
"face": {
"position": [0, 0, 0.1],
"scale": 0
},
"face_ditto": {
"position": [0, 0, -0.1]
}
}
},
"animation.absol.pose": {
"loop": true,
"bones": {
"neck": {
"rotation": [30.3812, -8.6492, -5.0384]
},
"head": {
"rotation": [-17.5, 0, 0]
},
"hair": {
"rotation": [0, 0, -2.5]
},
"hair2": {
"rotation": [2.5, 10, 0]
},
"hair3": {
"rotation": [1.9544, 7.5589, 12.4168]
},
"neck_fluff_left": {
"rotation": [0, 0, 67.5]
},
"neck_fluff_back": {
"rotation": [-45, 0, 0]
},
"chest_fluff": {
"rotation": [-17.5, 0, 0]
},
"chest_fluff_right": {
"rotation": [0, 37.5, 0]
},
"chest_fluff_left": {
"rotation": [0, -37.5, 0]
},
"chest_fluff2": {
"rotation": [-5, 0, 0]
},
"chest_fluff3": {
"rotation": [7.5, 0, 0]
},
"tail": {
"rotation": [-2.2793, -9.656, -2.6129]
},
"horn": {
"rotation": [0, -10, 0]
},
"neck_fluff_right": {
"rotation": [0, 0, -52.5]
},
"body": {
"rotation": [0, 65, 0],
"position": [2, -0.55, 0]
},
"torso": {
"rotation": [9.9864, 0.6469, -2.4149]
},
"torso2": {
"rotation": [-5, 0, 0]
},
"head_angle": {
"rotation": [-5.7686, -29.8743, 2.8806]
},
"leg_front_left": {
"rotation": [16.5711, -9.4592, -13.6836],
"position": [0.75, -2, 0]
},
"leg_front_left2": {
"rotation": [-45, 0, 0]
},
"leg_back_left": {
"rotation": [14.2119, -9.413, -10.1281]
},
"leg_back_left2": {
"rotation": [-2.5, 0, 0]
},
"leg_back_left3": {
"rotation": [17.5, 0, 0]
},
"foot_back_left": {
"rotation": [-27.3943, 4.3233, 8.0916]
},
"iris_left": {
"position": [1, 0, 0]
},
"foot_front_left": {
"rotation": [41.9868, -4.2611, 0.0665],
"position": [0, 0, -0.75]
},
"claw_middle_front_left": {
"rotation": [7.5, 0, 0]
},
"claw_outer_front_left": {
"rotation": [7.5, 0, 0]
},
"claw_inner_front_left": {
"rotation": [7.5, 0, 0]
},
"neck_fluff_back2": {
"rotation": [-50, 0, 0]
},
"leg_front_right": {
"rotation": [-37.5352, -0.064, 9.7291],
"position": [-0.25, -1.75, 0]
},
"leg_front_right2": {
"rotation": [3.5, 0, 0]
},
"foot_front_right": {
"rotation": [34.0275, 7.3656, -7.2548],
"position": [0, 0.3, 0]
},
"leg_back_right": {
"rotation": [-18.2881, 9.413, 10.1281],
"position": [-0.25, 0, 0]
},
"leg_back_right2": {
"rotation": [16.5248, 0.124, 0.1823]
},
"leg_back_right3": {
"rotation": [6, 0, 0]
},
"foot_back_right": {
"rotation": [-2.4124, 5.5284, -9.8095]
}
}
},
"animation.absol.cry": {
"sound_effects": {
"0.04": {
"effect": "pokemon.absol.cry"
}
}
}
@@ -0,0 +1,208 @@
{
"format_version": "1.8.0",
"animations": {
"animation.burmy_plant.ground_idle": {
"loop": true,
"bones": {
"body": {
"rotation": [2.5201, 0.3141, 0.0322],
"position": [0, 6, 0]
},
"body_rotation": {
"rotation": [2.5, 0, 0]
},
"spring2": {
"rotation": [-2.5, 0, 0]
},
"segment1": {
"position": [0, 0.5, 0]
},
"leg_left1": {
"rotation": [0, 15, 0]
},
"leg_right1": {
"rotation": [0, -15, 0]
},
"segment2": {
"position": [0, 0.5, 0]
},
"segment3": {
"position": [0, 0.5, 0]
},
"leg_left2": {
"rotation": [0, 17.5, 0]
},
"leg_left3": {
"rotation": [0, 25, 0]
},
"leg_right2": {
"rotation": [0, -17.5, 0]
},
"leg_right3": {
"rotation": [0, -25, 0]
}
}
},
"animation.burmy_plant.ground_idle_naked": {
"loop": true,
"bones": {
"body_rotation": {
"rotation": [7.5, 0, 0],
"position": [0, 0, -3]
},
"segment1": {
"rotation": [75, 0, 0]
},
"leg_left1": {
"rotation": [0.00002, 0.00005, 0.00002]
},
"cloak_plant": {
"scale": 0
},
"body": {
"rotation": [2.5, 0, 0],
"position": [0, -7.25, 0]
},
"leg_right1": {
"rotation": [0.00002, -0.00005, -0.00002]
},
"segment2": {
"rotation": [5, 0, 0]
},
"segment3": {
"rotation": [2.5781, -0.5734, 0.2485]
},
"leg_left2": {
"rotation": [2.53742, -4.92385, -0.87038]
},
"leg_left3": {
"rotation": [7.53742, -4.92385, -0.87038]
},
"leg_right2": {
"rotation": [2.53742, 4.92385, 0.87038]
},
"leg_right3": {
"rotation": [7.53742, 4.92385, 0.87038]
},
"face": {
"position": [0, 0, 1]
},
"cloaked_face": {
"scale": 0
},
"face_ditto": {
"position": [0, 0, 1]
}
}
},
"animation.burmy_plant.spring": {
"loop": true,
"bones": {
"body": {
"rotation": [2.5201, 0.3141, 0.0322],
"position": [0, 6, 0]
},
"body_rotation": {
"rotation": [2.5, 0, 0],
"position": [0, -3, 0]
},
"spring2": {
"rotation": [35.9366, -29.087, 32.6804]
},
"segment1": {
"position": [0, 0.5, 0]
},
"leg_left1": {
"rotation": [0, 15, 0]
},
"spring": {
"rotation": [-26.941, -5.7358, -11.1249]
},
"spring3": {
"rotation": [-24.3803, 6.6782, -46.9093]
},
"spring4": {
"rotation": [0, 0, -35]
},
"leg_right1": {
"rotation": [0, -15, 0]
},
"segment2": {
"position": [0, 0.5, 0]
},
"segment3": {
"position": [0, 0.5, 0]
},
"leg_left2": {
"rotation": [0, 17.5, 0]
},
"leg_left3": {
"rotation": [0, 25, 0]
},
"leg_right2": {
"rotation": [0, -17.5, 0]
},
"leg_right3": {
"rotation": [0, -25, 0]
}
}
},
"animation.burmy_plant.ditto_face": {
"loop": true,
"bones": {
"face": {
"position": [0, 0, 0.1],
"scale": 0
},
"face_ditto": {
"position": [0, 0, -0.1]
}
}
},
"animation.burmy_plant.blink": {
"animation_length": 0.1667,
"bones": {
"pupil_left": {
"position": {
"0.0": [0, 0, 0.1],
"0.125": {"pre": [0, 0, 0.1], "post": [0, 0, 0.1]},
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"pupil_closed_left": {
"position": {
"0.0": [0, 0, -0.1],
"0.125": {"pre": [0, 0, -0.1], "post": [0, 0, -0.1]},
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
},
"pupil_right": {
"position": {
"0.0": [0, 0, 0.1],
"0.125": {"pre": [0, 0, 0.1], "post": [0, 0, 0.1]},
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"pupil_closed_right": {
"position": {
"0.0": [0, 0, -0.1],
"0.125": {"pre": [0, 0, -0.1], "post": [0, 0, -0.1]},
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
},
"eyes_ditto": {
"position": {
"0.0": [0, 0, 0.1],
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"eyes_blink_ditto": {
"position": {
"0.0": [0, 0, -0.1],
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
}
}
}
}
}
@@ -0,0 +1,208 @@
{
"format_version": "1.8.0",
"animations": {
"animation.burmy_sandy.ground_idle": {
"loop": true,
"bones": {
"body": {
"rotation": [2.5201, 0.3141, 0.0322],
"position": [0, 6, 0]
},
"body_rotation": {
"rotation": [2.5, 0, 0]
},
"spring2": {
"rotation": [-2.5, 0, 0]
},
"segment1": {
"position": [0, 0.5, 0]
},
"leg_left1": {
"rotation": [0, 15, 0]
},
"leg_right1": {
"rotation": [0, -15, 0]
},
"segment2": {
"position": [0, 0.5, 0]
},
"segment3": {
"position": [0, 0.5, 0]
},
"leg_left2": {
"rotation": [0, 17.5, 0]
},
"leg_left3": {
"rotation": [0, 25, 0]
},
"leg_right2": {
"rotation": [0, -17.5, 0]
},
"leg_right3": {
"rotation": [0, -25, 0]
}
}
},
"animation.burmy_sandy.ground_idle_naked": {
"loop": true,
"bones": {
"body_rotation": {
"rotation": [7.5, 0, 0],
"position": [0, 0, -3]
},
"segment1": {
"rotation": [75, 0, 0]
},
"leg_left1": {
"rotation": [0.00002, 0.00005, 0.00002]
},
"cloak_plant": {
"scale": 0
},
"body": {
"rotation": [2.5, 0, 0],
"position": [0, -7.25, 0]
},
"leg_right1": {
"rotation": [0.00002, -0.00005, -0.00002]
},
"segment2": {
"rotation": [5, 0, 0]
},
"segment3": {
"rotation": [2.5781, -0.5734, 0.2485]
},
"leg_left2": {
"rotation": [2.53742, -4.92385, -0.87038]
},
"leg_left3": {
"rotation": [7.53742, -4.92385, -0.87038]
},
"leg_right2": {
"rotation": [2.53742, 4.92385, 0.87038]
},
"leg_right3": {
"rotation": [7.53742, 4.92385, 0.87038]
},
"face": {
"position": [0, 0, 1]
},
"cloaked_face": {
"scale": 0
},
"face_ditto": {
"position": [0, 0, 1]
}
}
},
"animation.burmy_sandy.spring": {
"loop": true,
"bones": {
"body": {
"rotation": [2.5201, 0.3141, 0.0322],
"position": [0, 6, 0]
},
"body_rotation": {
"rotation": [2.5, 0, 0],
"position": [0, -3, 0]
},
"spring2": {
"rotation": [35.9366, -29.087, 32.6804]
},
"segment1": {
"position": [0, 0.5, 0]
},
"leg_left1": {
"rotation": [0, 15, 0]
},
"spring": {
"rotation": [-26.941, -5.7358, -11.1249]
},
"spring3": {
"rotation": [-24.3803, 6.6782, -46.9093]
},
"spring4": {
"rotation": [0, 0, -35]
},
"leg_right1": {
"rotation": [0, -15, 0]
},
"segment2": {
"position": [0, 0.5, 0]
},
"segment3": {
"position": [0, 0.5, 0]
},
"leg_left2": {
"rotation": [0, 17.5, 0]
},
"leg_left3": {
"rotation": [0, 25, 0]
},
"leg_right2": {
"rotation": [0, -17.5, 0]
},
"leg_right3": {
"rotation": [0, -25, 0]
}
}
},
"animation.burmy_sandy.ditto_face": {
"loop": true,
"bones": {
"face": {
"position": [0, 0, 0.1],
"scale": 0
},
"face_ditto": {
"position": [0, 0, -0.1]
}
}
},
"animation.burmy_sandy.blink": {
"animation_length": 0.1667,
"bones": {
"pupil_left": {
"position": {
"0.0": [0, 0, 0.1],
"0.125": [0, 0, 0.1],
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"pupil_closed_left": {
"position": {
"0.0": [0, 0, -0.1],
"0.125": [0, 0, -0.1],
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
},
"pupil_right": {
"position": {
"0.0": [0, 0, 0.1],
"0.125": [0, 0, 0.1],
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"pupil_closed_right": {
"position": {
"0.0": [0, 0, -0.1],
"0.125": [0, 0, -0.1],
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
},
"eyes_ditto": {
"position": {
"0.0": [0, 0, 0.1],
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"eyes_blink_ditto": {
"position": {
"0.0": [0, 0, -0.1],
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
}
}
}
}
}
@@ -0,0 +1,208 @@
{
"format_version": "1.8.0",
"animations": {
"animation.burmy_trash.ground_idle": {
"loop": true,
"bones": {
"body": {
"rotation": [2.5201, 0.3141, 0.0322],
"position": [0, 6, 0]
},
"body_rotation": {
"rotation": [2.5, 0, 0]
},
"spring2": {
"rotation": [-2.5, 0, 0]
},
"segment1": {
"position": [0, 0.5, 0]
},
"leg_left1": {
"rotation": [0, 15, 0]
},
"leg_right1": {
"rotation": [0, -15, 0]
},
"segment2": {
"position": [0, 0.5, 0]
},
"segment3": {
"position": [0, 0.5, 0]
},
"leg_left2": {
"rotation": [0, 17.5, 0]
},
"leg_left3": {
"rotation": [0, 25, 0]
},
"leg_right2": {
"rotation": [0, -17.5, 0]
},
"leg_right3": {
"rotation": [0, -25, 0]
}
}
},
"animation.burmy_trash.ground_idle_naked": {
"loop": true,
"bones": {
"body_rotation": {
"rotation": [7.5, 0, 0],
"position": [0, 0, -3]
},
"segment1": {
"rotation": [75, 0, 0]
},
"leg_left1": {
"rotation": [0.00002, 0.00005, 0.00002]
},
"cloak_plant": {
"scale": 0
},
"body": {
"rotation": [2.5, 0, 0],
"position": [0, -7.25, 0]
},
"leg_right1": {
"rotation": [0.00002, -0.00005, -0.00002]
},
"segment2": {
"rotation": [5, 0, 0]
},
"segment3": {
"rotation": [2.5781, -0.5734, 0.2485]
},
"leg_left2": {
"rotation": [2.53742, -4.92385, -0.87038]
},
"leg_left3": {
"rotation": [7.53742, -4.92385, -0.87038]
},
"leg_right2": {
"rotation": [2.53742, 4.92385, 0.87038]
},
"leg_right3": {
"rotation": [7.53742, 4.92385, 0.87038]
},
"face": {
"position": [0, 0, 1]
},
"cloaked_face": {
"scale": 0
},
"face_ditto": {
"position": [0, 0, 1]
}
}
},
"animation.burmy_trash.spring": {
"loop": true,
"bones": {
"body": {
"rotation": [2.5201, 0.3141, 0.0322],
"position": [0, 6, 0]
},
"body_rotation": {
"rotation": [2.5, 0, 0],
"position": [0, -3, 0]
},
"spring2": {
"rotation": [35.9366, -29.087, 32.6804]
},
"segment1": {
"position": [0, 0.5, 0]
},
"leg_left1": {
"rotation": [0, 15, 0]
},
"spring": {
"rotation": [-26.941, -5.7358, -11.1249]
},
"spring3": {
"rotation": [-24.3803, 6.6782, -46.9093]
},
"spring4": {
"rotation": [0, 0, -35]
},
"leg_right1": {
"rotation": [0, -15, 0]
},
"segment2": {
"position": [0, 0.5, 0]
},
"segment3": {
"position": [0, 0.5, 0]
},
"leg_left2": {
"rotation": [0, 17.5, 0]
},
"leg_left3": {
"rotation": [0, 25, 0]
},
"leg_right2": {
"rotation": [0, -17.5, 0]
},
"leg_right3": {
"rotation": [0, -25, 0]
}
}
},
"animation.burmy_trash.ditto_face": {
"loop": true,
"bones": {
"face": {
"position": [0, 0, 0.1],
"scale": 0
},
"face_ditto": {
"position": [0, 0, -0.1]
}
}
},
"animation.burmy_trash.blink": {
"animation_length": 0.1667,
"bones": {
"pupil_left": {
"position": {
"0.0": [0, 0, 0.1],
"0.125": [0, 0, 0.1],
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"pupil_closed_left": {
"position": {
"0.0": [0, 0, -0.1],
"0.125": [0, 0, -0.1],
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
},
"pupil_right": {
"position": {
"0.0": [0, 0, 0.1],
"0.125": [0, 0, 0.1],
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"pupil_closed_right": {
"position": {
"0.0": [0, 0, -0.1],
"0.125": [0, 0, -0.1],
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
},
"eyes_ditto": {
"position": {
"0.0": [0, 0, 0.1],
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"eyes_blink_ditto": {
"position": {
"0.0": [0, 0, -0.1],
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
}
}
}
}
}
@@ -0,0 +1,107 @@
{
"format_version": "1.8.0",
"animations": {
"animation.wormadam_plant.ground_idle": {
"loop": true,
"bones": {
"body": {
"rotation": [5, 0, 0],
"position": [0, 6, 0]
},
"body_rotation": {
"rotation": [0, 0, 0]
},
"antenna": {
"rotation": [-2.5, 0, 0]
},
"ear_left": {
"rotation": [0, -12.5, 0]
},
"ear_left2": {
"rotation": [0, 0, 20]
},
"ear_left3": {
"rotation": [0, 0, 22.5]
},
"ear_back_left": {
"rotation": [-7.5, 0, 25]
},
"ear_left4": {
"rotation": [0, 0, 17.5]
},
"ear_right": {
"rotation": [0, 12.5, 0]
},
"ear_right2": {
"rotation": [0, 0, -20]
},
"ear_right3": {
"rotation": [0, 0, -22.5]
},
"ear_right4": {
"rotation": [0, 0, -17.5]
},
"ear_back_right": {
"rotation": [-7.5, 0, -25]
}
}
},
"animation.wormadam_plant.blink": {
"animation_length": 0.1667,
"bones": {
"pupil_left": {
"position": {
"0.0": [0, 0, 0.1],
"0.125": [0, 0, 0.1],
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"pupil_closed_left": {
"position": {
"0.0": [0, 0, -0.1],
"0.125": [0, 0, -0.1],
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
},
"pupil_right": {
"position": {
"0.0": [0, 0, 0.1],
"0.125": [0, 0, 0.1],
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"pupil_closed_right": {
"position": {
"0.0": [0, 0, -0.1],
"0.125": [0, 0, -0.1],
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
},
"eyes_ditto": {
"position": {
"0.0": [0, 0, 0.1],
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"eyes_blink_ditto": {
"position": {
"0.0": [0, 0, -0.1],
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
}
}
},
"animation.wormadam_plant.ditto_face": {
"loop": true,
"bones": {
"face": {
"position": [0, 0, 0.1],
"scale": 0
},
"face_ditto": {
"position": [0, 0, -0.1]
}
}
}
}
}
@@ -0,0 +1,107 @@
{
"format_version": "1.8.0",
"animations": {
"animation.wormadam_sandy.ground_idle": {
"loop": true,
"bones": {
"body": {
"rotation": [5, 0, 0],
"position": [0, 6, 0]
},
"body_rotation": {
"rotation": [0, 0, 0]
},
"antenna": {
"rotation": [-2.5, 0, 0]
},
"ear_left": {
"rotation": [0, -12.5, 0]
},
"ear_left2": {
"rotation": [0, 0, 20]
},
"ear_left3": {
"rotation": [0, 0, 22.5]
},
"ear_back_left": {
"rotation": [-7.5, 0, 25]
},
"ear_left4": {
"rotation": [0, 0, 17.5]
},
"ear_right": {
"rotation": [0, 12.5, 0]
},
"ear_right2": {
"rotation": [0, 0, -20]
},
"ear_right3": {
"rotation": [0, 0, -22.5]
},
"ear_right4": {
"rotation": [0, 0, -17.5]
},
"ear_back_right": {
"rotation": [-7.5, 0, -25]
}
}
},
"animation.wormadam_sandy.blink": {
"animation_length": 0.1667,
"bones": {
"pupil_left": {
"position": {
"0.0": [0, 0, 0.1],
"0.125": [0, 0, 0.1],
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"pupil_closed_left": {
"position": {
"0.0": [0, 0, -0.1],
"0.125": [0, 0, -0.1],
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
},
"pupil_right": {
"position": {
"0.0": [0, 0, 0.1],
"0.125": [0, 0, 0.1],
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"pupil_closed_right": {
"position": {
"0.0": [0, 0, -0.1],
"0.125": [0, 0, -0.1],
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
},
"eyes_ditto": {
"position": {
"0.0": [0, 0, 0.1],
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"eyes_blink_ditto": {
"position": {
"0.0": [0, 0, -0.1],
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
}
}
},
"animation.wormadam_sandy.ditto_face": {
"loop": true,
"bones": {
"face": {
"position": [0, 0, 0.1],
"scale": 0
},
"face_ditto": {
"position": [0, 0, -0.1]
}
}
}
}
}
@@ -0,0 +1,107 @@
{
"format_version": "1.8.0",
"animations": {
"animation.wormadam_trash.ground_idle": {
"loop": true,
"bones": {
"body": {
"rotation": [5, 0, 0],
"position": [0, 6, 0]
},
"body_rotation": {
"rotation": [0, 0, 0]
},
"antenna": {
"rotation": [-2.5, 0, 0]
},
"ear_left": {
"rotation": [0, -12.5, 0]
},
"ear_left2": {
"rotation": [0, 0, 20]
},
"ear_left3": {
"rotation": [0, 0, 22.5]
},
"ear_back_left": {
"rotation": [-7.5, 0, 25]
},
"ear_left4": {
"rotation": [0, 0, 17.5]
},
"ear_right": {
"rotation": [0, 12.5, 0]
},
"ear_right2": {
"rotation": [0, 0, -20]
},
"ear_right3": {
"rotation": [0, 0, -22.5]
},
"ear_right4": {
"rotation": [0, 0, -17.5]
},
"ear_back_right": {
"rotation": [-7.5, 0, -25]
}
}
},
"animation.wormadam_trash.blink": {
"animation_length": 0.1667,
"bones": {
"pupil_left": {
"position": {
"0.0": [0, 0, 0.1],
"0.125": [0, 0, 0.1],
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"pupil_closed_left": {
"position": {
"0.0": [0, 0, -0.1],
"0.125": [0, 0, -0.1],
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
},
"pupil_right": {
"position": {
"0.0": [0, 0, 0.1],
"0.125": [0, 0, 0.1],
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"pupil_closed_right": {
"position": {
"0.0": [0, 0, -0.1],
"0.125": [0, 0, -0.1],
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
},
"eyes_ditto": {
"position": {
"0.0": [0, 0, 0.1],
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"eyes_blink_ditto": {
"position": {
"0.0": [0, 0, -0.1],
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
}
}
},
"animation.wormadam_trash.ditto_face": {
"loop": true,
"bones": {
"face": {
"position": [0, 0, 0.1],
"scale": 0
},
"face_ditto": {
"position": [0, 0, -0.1]
}
}
}
}
}
@@ -0,0 +1,100 @@
{
"format_version": "1.8.0",
"animations": {
"animation.mothim.ground_idle": {
"loop": true,
"bones": {
"torso": {
"rotation": [5, 0, 0]
},
"wing_upper_left": {
"rotation": [1.3185, -9.9136, -7.6143]
},
"wing_lower_left": {
"rotation": [-2.5, -12.5, 0]
},
"head": {
"rotation": [-5, 0, 0]
},
"antenna_left": {
"rotation": [-22.4229, 1.9113, 4.6211]
},
"antenna_left2": {
"rotation": [-7.5, 0, 0]
},
"tail": {
"rotation": [-12.5, 0, 0]
},
"antenna_right": {
"rotation": [-22.4229, -1.9113, -4.6211]
},
"antenna_right2": {
"rotation": [-7.5, 0, 0]
},
"arm_left": {
"rotation": [-0.9096, 19.9802, 44.8398]
},
"arm_right": {
"rotation": [-0.9096, -19.9802, -44.8398]
},
"leg_left": {
"rotation": [-9.041, -41.7073, 13.4501]
},
"leg_right": {
"rotation": [-9.041, 41.7073, -13.4501]
},
"wing_upper_right": {
"rotation": [1.3185, 9.9136, 7.6143]
},
"wing_lower_right": {
"rotation": [-2.5, 12.5, 0]
}
}
},
"animation.mothim.blink": {
"animation_length": 0.1667,
"bones": {
"eye_open_left": {
"position": {
"0.0": [0, 0, 0.1],
"0.125": {"pre": [0, 0, 0.1], "post": [0, 0, 0.1]},
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"eye_open_right": {
"position": {
"0.0": [0, 0, 0.1],
"0.125": {"pre": [0, 0, 0.1], "post": [0, 0, 0.1]},
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"eye_closed_left": {
"position": {
"0.0": [0, 0, -0.1],
"0.125": {"pre": [0, 0, -0.1], "post": [0, 0, -0.1]},
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, -0.1]}
}
},
"eye_closed_right": {
"position": {
"0.0": [0, 0, -0.1],
"0.125": {"pre": [0, 0, -0.1], "post": [0, 0, -0.1]},
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, -0.1]}
}
},
"eyes_ditto": {
"position": {
"0.0": [0, 0, 0.1],
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"eyes_blink_ditto": {
"position": {
"0.0": [0, 0, -0.1],
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
}
}
}
}
}
@@ -0,0 +1,104 @@
{
"format_version": "1.8.0",
"animations": {
"animation.snover.ground_idle": {
"loop": true,
"bones": {
"tail": {
"rotation": [-7.44134, 0.07948, 0.53084]
},
"arm_right": {
"rotation": [0, 0, "-32.5-5*Math.sin((query.anim_time-0.5) * 90 * 1)"]
},
"arm_right2": {
"rotation": [0, "-12.5+5*Math.sin((query.anim_time-0.75) * 90 * 1)", 0]
},
"torso": {
"rotation": ["-2.5+2*Math.sin((query.anim_time-0) * 90 * 1)", 0, "-2*Math.sin((query.anim_time-0.8) * 90 * 0.5)"],
"position": [0, "0.1*Math.sin((query.anim_time-0.75) * 90 * 1)", 0]
},
"torso2": {
"rotation": ["5+1*Math.sin((query.anim_time-0.25) * 90 * 1)", 0, 0]
},
"arm_left": {
"rotation": [0, 0, "32.5+5*Math.sin((query.anim_time-0.5) * 90 * 1)"]
},
"arm_left2": {
"rotation": [0, "12.5-5*Math.sin((query.anim_time-0.75) * 90 * 1)", 0]
}
}
},
"animation.snover.blink": {
"animation_length": 0.1667,
"bones": {
"pupil_right": {
"position": {
"0.0": [0, 0, 0.1],
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"pupil_left": {
"position": {
"0.0": [0, 0, 0.1],
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"pupil_closed_right": {
"position": {
"0.0": [0, 0, -0.1],
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
},
"pupil_closed_left": {
"position": {
"0.0": [0, 0, -0.1],
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
},
"eyes_ditto": {
"position": {
"0.0": [0, 0, 0.1],
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"eyes_blink_ditto": {
"position": {
"0.0": [0, 0, -0.1],
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
}
}
},
"animation.snover.ditto_face": {
"loop": true,
"bones": {
"eyes": {
"scale": 0
},
"face_ditto": {
"position": [0, 0, -0.1]
}
}
},
"animation.snover.berries_female": {
"loop": true,
"bones": {
"berry_front": {
"position": [0, 0, -4]
},
"berry_front_right": {
"position": [-4, 0, -4]
},
"berry_back_right": {
"position": [-4, 0, 4]
},
"berry_front_left": {
"position": [4, 0, -4]
},
"berry_back_left": {
"position": [4, 0, 4]
}
}
}
}
}
@@ -0,0 +1,110 @@
{
"format_version": "1.8.0",
"animations": {
"animation.abomasnow.ground_idle": {
"loop": true,
"bones": {
"torso2": {
"rotation": [2.5, 0, 0]
},
"head": {
"rotation": [-2.5, 0, 0]
},
"arm_left": {
"rotation": [0, 0, 35]
},
"arm_left2": {
"rotation": [4.75346, 32.18826, 8.87221]
},
"finger_front_left": {
"rotation": [-3.96713, -14.47751, 15.50409]
},
"finger_middle_left": {
"rotation": [0, 0, 15]
},
"finger_back_left": {
"rotation": [3.96713, 14.47751, 15.50409]
},
"foot_left": {
"rotation": [0, -15, 0]
},
"tail": {
"rotation": [-15, 0, 0]
},
"tail2": {
"rotation": [5, 0, 0]
},
"tail3": {
"rotation": [5, 0, 0]
},
"arm_right": {
"rotation": [0, 0, -35]
},
"arm_right2": {
"rotation": [4.75346, -32.18826, -8.87221]
},
"finger_front_right": {
"rotation": [-3.96713, 14.47751, -15.50409]
},
"finger_middle_right": {
"rotation": [0, 0, -15]
},
"finger_back_right": {
"rotation": [3.96713, -14.47751, -15.50409]
}
}
},
"animation.abomasnow.blink": {
"animation_length": 0.1667,
"bones": {
"eye_left": {
"position": {
"0.0": [0, 0, 0.1],
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"eye_right": {
"position": {
"0.0": [0, 0, 0.1],
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"eyelid_right": {
"position": {
"0.0": [0, 0, -0.1],
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
},
"eyelid_left": {
"position": {
"0.0": [0, 0, -0.1],
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
},
"eyes_ditto": {
"position": {
"0.0": [0, 0, 0.1],
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"eyes_blink_ditto": {
"position": {
"0.0": [0, 0, -0.1],
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
}
}
},
"animation.abomasnow.ditto_face": {
"loop": true,
"bones": {
"eyes": {
"scale": 0
},
"face_ditto": {
"position": [0, 0, -0.1]
}
}
}
}
}
@@ -0,0 +1,279 @@
{
"format_version": "1.8.0",
"animations": {
"animation.tympole.ground_idle": {
"loop": true,
"bones": {
"torso": {
"rotation": [-2.5, 0, 0]
},
"tail": {
"rotation": [-37.5, 0, 0]
},
"tail2": {
"rotation": [35, 0, 0]
},
"tail3": {
"rotation": [10, 0, 0]
}
}
},
"animation.tympole.sleep": {
"loop": true,
"bones": {
"body": {
"rotation": [-95, 0, 0],
"position": [0, 0, 2]
},
"tail": {
"rotation": [-85, 0, 0]
},
"tail2": {
"rotation": [-5, 0, 0]
},
"tail3": {
"rotation": [-12.5, 0, 0]
},
"eye_left": {
"position": [0, 0, 0.1]
},
"eye_right": {
"position": [0, 0, 0.1]
},
"eyelid_right": {
"position": [0, 0, -0.1]
},
"eyelid_left": {
"position": [0, 0, -0.1]
}
}
},
"animation.tympole.blink": {
"animation_length": 0.1667,
"bones": {
"eye_left": {
"position": {
"0.0": [0, 0, 0.1],
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"eyelid_left": {
"position": {
"0.0": [0, 0, -0.1],
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
},
"eye_right": {
"position": {
"0.0": [0, 0, 0.1],
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"eyelid_right": {
"position": {
"0.0": [0, 0, -0.1],
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
},
"eyes_ditto": {
"position": {
"0.0": [0, 0, 0.1],
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"eyes_blink_ditto": {
"position": {
"0.0": [0, 0, -0.1],
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
}
}
},
"animation.tympole.ditto_face": {
"loop": true,
"bones": {
"face": {
"scale": 0
},
"face_ditto": {
"position": [0, 0, -0.1]
}
}
},
"animation.tympole.pose": {
"loop": true,
"bones": {
"body": {
"rotation": [-8.5958, -5.68422, 21.80633],
"position": [0, 8, 0]
},
"tail": {
"rotation": [-67.5, -22.5, 0],
"position": [0, -2, 0]
},
"tail2": {
"rotation": [22.5, 0, 0]
},
"tail3": {
"rotation": [22.5, 0, 0]
},
"mouth": {
"position": [0, 0, 0.1]
},
"mouth_open": {
"position": [0, 0, 0]
},
"mouth_open_small": {
"position": [0, 0, -0.1]
}
}
},
"animation.tympole.happy": {
"loop": true,
"bones": {
"eye_left": {
"position": [0, 0, 0.1]
},
"eye_right": {
"position": [0, 0, 0.1]
},
"mouth": {
"position": [0, 0, 0.1]
},
"mouth_open": {
"position": [0, 0, -0.1]
},
"eye_left_happy": {
"position": [0, 0, -0.1]
},
"eye_right_happy": {
"position": [0, 0, -0.1]
}
}
},
"animation.tympole.shock": {
"loop": true,
"bones": {
"mouth": {
"position": [0, 0, 0.1]
},
"mouth_open_tall": {
"position": [0, 0, -0.1]
},
"eyebrow_left": {
"position": [0, 0.5, 0]
},
"eyebrow_right": {
"position": [0, 0.5, 0]
}
}
},
"animation.tympole.angry": {
"loop": true,
"bones": {
"eyebrow_left": {
"rotation": [0, 0, -22.5],
"position": [-0.25, -0.25, -0.04]
},
"eyebrow_right": {
"rotation": [0, 0, 22.5],
"position": [0.25, -0.25, -0.04]
},
"mouth": {
"position": [0, 0, 0.1]
},
"eyelid_left_emote": {
"position": [0, 0, -0.1]
},
"eyelid_right_emote": {
"position": [0, 0, -0.1]
},
"mouth_angry": {
"position": [0, 0, -0.1]
}
}
},
"animation.tympole.sad": {
"loop": true,
"bones": {
"eyebrow_left": {
"rotation": [0, 0, 10],
"position": [0.15, 0, -0.04]
},
"eyebrow_right": {
"rotation": [0, 0, -10],
"position": [-0.15, 0, -0.04]
},
"mouth": {
"position": [0, 0, 0.1]
},
"eyelid_left_emote": {
"position": [0.9, 0, -0.1]
},
"eyelid_right_emote": {
"position": [-0.9, 0, -0.1]
},
"mouth_sad": {
"position": [0, 0, -0.1]
}
}
},
"animation.tympole.hurt": {
"loop": true,
"bones": {
"eyebrow_left": {
"position": [0, -0.25, 0]
},
"eyebrow_right": {
"position": [0, -0.25, 0]
},
"eye_left": {
"position": [0, 0, 0.1]
},
"eye_right": {
"position": [0, 0, 0.1]
},
"mouth": {
"position": [0, 0, 0.1]
},
"mouth_open_frown": {
"position": [0, 0, -0.1]
},
"eye_left_hurt": {
"position": [0, 0, -0.1]
},
"eye_right_hurt": {
"position": [0, 0, -0.1]
}
}
},
"animation.tympole.unamused": {
"loop": true,
"bones": {
"mouth": {
"position": [0, 0, 0.1]
},
"mouth_angry": {
"position": [0, 0, -0.1]
},
"eyelid_left_unamused": {
"position": [0, 0, -0.1]
},
"eyelid_right_unamused": {
"position": [0, 0, -0.1]
}
}
},
"animation.tympole.scale": {
"loop": true,
"bones": {
"tympole": {
"scale": 0.65
},
"ear_left_inside": {
"position": [0, 0, 0]
}
}
}
}
}
@@ -0,0 +1,223 @@
{
"format_version": "1.8.0",
"animations": {
"animation.palpitoad.ground_idle": {
"loop": true,
"bones": {
"palpitoad": {
"scale": 1
},
"torso": {
"rotation": [-2.5, 0, 0]
},
"tail": {
"rotation": [-32.5, 0, 0]
},
"nose": {
"rotation": [-10, 0, 0]
},
"leg_left": {
"rotation": [0, 15, 0]
},
"leg_right": {
"rotation": [0, -15, 0]
},
"tail2": {
"rotation": [20, 0, 0]
},
"tail3": {
"rotation": [12.5, 0, 0]
},
"tongue": {
"rotation": [260, 0, 0]
},
"tongue_2": {
"rotation": [180, 0, 0]
},
"tongue_3": {
"rotation": [180, 0, 0]
},
"tongue_4": {
"rotation": [180, 0, 0]
},
"tongue_5": {
"rotation": [180, 0, 0]
},
"tongue_6": {
"rotation": [180, 0, 0]
},
"tongue_7": {
"rotation": [180, 0, 0]
},
"tongue_8": {
"rotation": [180, 0, 0]
},
"tongue_9": {
"rotation": [180, 0, 0]
},
"tongue_10": {
"rotation": [180, 0, 0]
},
"tongue_11": {
"rotation": [180, 0, 0]
},
"tongue_12": {
"rotation": [180, 0, 0]
},
"head": {
"rotation": [2.5, 0, 0]
}
}
},
"animation.palpitoad.blink": {
"animation_length": 0.1667,
"bones": {
"eye_left": {
"position": {
"0.0": [0, 0, 0.1],
"0.125": [0, 0, 0.1],
"0.1667": [0, 0, 0]
}
},
"shine_left": {
"position": {
"0.0": [0, 0, 0.1],
"0.125": [0, 0, 0.1],
"0.1667": [0, 0, 0]
}
},
"eye_right": {
"position": {
"0.0": [0, 0, 0.1],
"0.125": [0, 0, 0.1],
"0.1667": [0, 0, 0]
}
},
"shine_right": {
"position": {
"0.0": [0, 0, 0.1],
"0.125": [0, 0, 0.1],
"0.1667": [0, 0, 0]
}
},
"eyelid_right": {
"position": {
"0.0": [0, 0, -0.1],
"0.125": [0, 0, -0.1],
"0.1667": [0, 0, 0]
}
},
"eyelid_left": {
"position": {
"0.0": [0, 0, -0.1],
"0.125": [0, 0, -0.1],
"0.1667": [0, 0, 0]
}
}
}
},
"animation.palpitoad.ditto_face": {
"loop": true,
"bones": {
"mouth_open_big_bottom": {
"scale": 0
},
"face": {
"scale": 0
},
"face_ditto": {
"position": [0, 0, -0.1]
}
}
},
"animation.palpitoad.pose": {
"loop": true,
"bones": {
"body": {
"position": [0, 1, 0]
},
"leg_left": {
"rotation": [20, 25, 0]
},
"toes_left": {
"rotation": [-20, 0, 0]
},
"leg_right": {
"rotation": [20, -25, 0]
},
"toes_right": {
"rotation": [-20, 0, 0]
},
"torso": {
"rotation": [-5, 0, 0]
},
"head_angle": {
"rotation": [5, 10, 0]
},
"mouth": {
"position": [0, 0, 0.1]
},
"nose": {
"rotation": [-10, 0, 0]
},
"tail": {
"rotation": [27.5, -30, 0]
},
"tail2": {
"rotation": [27.5, 0, 0]
},
"tongue": {
"rotation": [-10, -32.5, 0]
},
"tongue_2": {
"rotation": [-42.79487, -18.88952, 16.68541]
},
"tongue_3": {
"rotation": [-38.85707, -12.27659, 12.56853]
},
"tongue_4": {
"rotation": [-25, 0, 0]
},
"tongue_5": {
"rotation": [-10.82756, 27.88369, 21.32202]
},
"tongue_6": {
"rotation": [-10, 42.5, 0]
},
"tongue_7": {
"rotation": [-30.86748, 12.95254, -7.63074]
},
"mouth_open": {
"position": [0, 0, -0.1]
},
"tongue_8": {
"rotation": [-15, 0, 0]
},
"tongue_9": {
"rotation": [-22.5, 0, 0]
},
"tongue_12": {
"rotation": [40, 0, 0]
},
"tongue_11": {
"rotation": [37.5, 0, 0]
},
"tongue_10": {
"rotation": [-15, 0, 0]
},
"shine_left": {
"position": [-0.25, 0, 0]
},
"shine_right": {
"position": [-0.25, 0, 0]
},
"pupil_left": {
"position": [-0.25, 0, 0]
},
"pupil_right": {
"position": [-0.25, 0, 0]
}
}
}
}
}
@@ -0,0 +1,341 @@
{
"format_version": "1.8.0",
"animations": {
"animation.seismitoad.ground_idle": {
"loop": true,
"bones": {
"body": {
"position": [0, -3.2, 0]
},
"torso": {
"rotation": [2.5, 0, 0]
},
"shoulder_left": {
"rotation": [34.3569, -7.1313, 32.7929]
},
"arm_left": {
"rotation": [0, 27.5, 5]
},
"elbow_left": {
"rotation": [-15, 22.5, 0]
},
"hand_left": {
"rotation": [0, 0, 22.5]
},
"leg_left": {
"rotation": [-45, -20, -10],
"position": [-1, 0, 0]
},
"knee_left": {
"rotation": [60, -10, 10]
},
"foot_left": {
"rotation": [-12, 0, -5]
},
"wart_knee_left": {
"rotation": [22.5, 0, 7.5]
},
"neck_rotation": {
"rotation": [2.5, 0, 0]
},
"head": {
"rotation": [-90, 0, 0]
},
"finger_middle_left2": {
"rotation": [0, 0, 15]
},
"finger_front_left": {
"rotation": [0, 0, 7.5]
},
"finger_front_left2": {
"rotation": [0, 0, 15]
},
"finger_back_left": {
"rotation": [0, 0, 7.5]
},
"finger_back_left2": {
"rotation": [0, 0, 15]
},
"shoulder_right": {
"rotation": [34.3569, 7.1313, -32.7929]
},
"arm_right": {
"rotation": [0, -27.5, -5]
},
"elbow_right": {
"rotation": [-15, -22.5, 0]
},
"hand_right": {
"rotation": [0, 0, -22.5]
},
"finger_middle_right2": {
"rotation": [0, 0, -15]
},
"finger_front_right": {
"rotation": [0, 0, -7.5]
},
"finger_front_right2": {
"rotation": [0, 0, -15]
},
"finger_back_right": {
"rotation": [0, 0, -7.5]
},
"finger_back_right2": {
"rotation": [0, 0, -15]
},
"toe_back_left2": {
"rotation": [-10, 0, 0]
},
"toe_out_left2": {
"rotation": [7.5, 0, 0]
},
"toe_in_left2": {
"rotation": [7.5, 0, 0]
},
"leg_right": {
"rotation": [-45, 20, 10],
"position": [1, 0, 0]
},
"knee_right": {
"rotation": [60, 10, -10]
},
"foot_right": {
"rotation": [-12, 0, 5]
},
"toe_out_right2": {
"rotation": [7.5, 0, 0]
},
"toe_in_right2": {
"rotation": [7.5, 0, 0]
},
"toe_back_right2": {
"rotation": [-10, 0, 0]
},
"wart_knee_right": {
"rotation": [22.5, 0, -7.5]
},
"neck": {
"rotation": [-45, 0, 0]
},
"tongue": {
"position": [0, -0.25, 3]
},
"tongue2": {
"position": [0, 0, 5]
},
"tongue3": {
"position": [0, 0, 5]
},
"tongue4": {
"position": [0, 0, 5]
},
"tongue5": {
"position": [0, 0, 5]
},
"tongue_tip": {
"position": [0, -1, 3]
},
"head_angle": {
"rotation": [90, 0, 0]
},
"neck_angle": {
"rotation": [45, 0, 0]
}
}
},
"animation.seismitoad.blink": {
"animation_length": 0.2917,
"bones": {
"eye_left": {
"position": {
"0.0": [0, 0, 0],
"0.0833": {"pre": [0, 0, 0], "post": [0, 0, 0.1]},
"0.2917": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"eye_right": {
"position": {
"0.0": [0, 0, 0.1],
"0.2083": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"eyelid_left": {
"position": {
"0.0": [0, 0, 0],
"0.0833": {"pre": [0, 0, 0], "post": [0, 0, -0.1]},
"0.2917": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
},
"eyelid_right": {
"position": {
"0.0": [0, 0, -0.1],
"0.2083": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
},
"eyes_ditto": {
"position": {
"0.0": [0, 0, 0.1],
"0.2083": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"eyes_blink_ditto": {
"position": {
"0.0": [0, 0, -0.1],
"0.2083": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
}
}
},
"animation.seismitoad.ditto_face": {
"loop": true,
"bones": {
"face": {
"scale": 0
},
"face_ditto": {
"position": [0, 0, -0.1]
},
"tongue": {
"scale": 0
}
}
},
"animation.seismitoad.pose": {
"loop": true,
"bones": {
"body": {
"rotation": [0, -30, 0],
"position": [0, -2.25, 0]
},
"torso": {
"rotation": [17.5, 0, 0]
},
"jaw": {
"rotation": [42.5, 0, 0]
},
"shoulder_left": {
"rotation": [-5, -7.5, 25]
},
"arm_left": {
"rotation": [0, 5, 0]
},
"elbow_left": {
"rotation": [-12.5, 50, 0]
},
"finger_middle_left": {
"rotation": [0, 0, 7.5]
},
"leg_left": {
"rotation": [-40.6299, -29.1905, -7.6552]
},
"knee_left": {
"rotation": [37.5, 0, 0]
},
"foot_left": {
"rotation": [6.4717, -0.6518, 4.9574]
},
"head": {
"rotation": [-22.0182, -4.7511, -24.0751]
},
"finger_middle_left2": {
"rotation": [0, 0, 20]
},
"finger_front_left2": {
"rotation": [0, 0, 15]
},
"finger_back_left": {
"rotation": [0, 0, 27.5]
},
"finger_back_left2": {
"rotation": [0, 0, 40]
},
"shoulder_right": {
"rotation": [0, 0, -22.5]
},
"arm_right": {
"rotation": [0, -12.5, 0]
},
"elbow_right": {
"rotation": [173.5473, -44.2356, -288.0659]
},
"hand_right": {
"rotation": [0, 0, -15]
},
"finger_middle_right": {
"rotation": [0, 0, -15]
},
"finger_middle_right2": {
"rotation": [0, 0, -5]
},
"finger_front_right": {
"rotation": [20.5765, 31.8072, -73.3068],
"position": [0, 0, -0.5]
},
"finger_front_right2": {
"rotation": [0, 0, -50]
},
"finger_back_right": {
"rotation": [-19.2568, -34.3655, -80.4799],
"position": [0, 0, 0.5]
},
"finger_back_right2": {
"rotation": [0, 0, -50]
},
"toe_back_left2": {
"rotation": [-10, 0, 0]
},
"toe_out_left2": {
"rotation": [7.5, 0, 0]
},
"toe_in_left2": {
"rotation": [7.5, 0, 0]
},
"leg_right": {
"rotation": [-38.922, 39.0907, 8.6174]
},
"knee_right": {
"rotation": [37.5, 0, 0]
},
"foot_right": {
"rotation": [7.4593, 0.7817, -5.949]
},
"toe_out_right2": {
"rotation": [7.5, 0, 0]
},
"toe_in_right2": {
"rotation": [7.5, 0, 0]
},
"toe_back_right2": {
"rotation": [-10, 0, 0]
},
"pupil_right": {
"position": [-0.25, 0, 0]
},
"pupil_left": {
"position": [-0.25, 0, 0]
},
"neck": {
"rotation": [0, -4, 0]
},
"tongue": {
"rotation": [-25, 0, 0]
},
"tongue2": {
"rotation": [-10, 25, 0]
},
"tongue3": {
"rotation": [-5, 32.5, 0]
},
"tongue4": {
"rotation": [-5, 45, 0]
},
"tongue5": {
"rotation": [-22.99, 11.5348, -4.8492]
},
"tongue_tip": {
"rotation": [-25.3376, 9.0548, -4.2618]
}
}
}
}
}
@@ -0,0 +1,105 @@
{
"format_version": "1.8.0",
"animations": {
"animation.deerling.ground_idle": {
"loop": true,
"bones": {
"body": {
"position": [0, -0.1, 0]
},
"torso": {
"rotation": [5, 0, 0]
},
"torso2": {
"rotation": [-5, 0, 0]
},
"neck": {
"rotation": [7.5, 0, 0]
},
"ear_left2": {
"rotation": [0, 0, 7.5]
},
"ear_right2": {
"rotation": [0, 0, -7.5]
},
"tail": {
"rotation": [5, 0, 0]
},
"leg_front_left": {
"rotation": [2.5, 0, 0]
},
"foot_front_left": {
"rotation": [-2.5, 0, 0]
},
"leg_back_left": {
"rotation": [2.5, 0, -2.5]
},
"leg_back_left2": {
"rotation": [-2.5, 0, 0]
},
"foot_back_left": {
"rotation": [0, 0, 2.5]
},
"leg_front_right": {
"rotation": [2.5, 0, 0]
},
"foot_front_right": {
"rotation": [-2.5, 0, 0]
},
"leg_back_right": {
"rotation": [2.5, 0, 2.5]
},
"leg_back_right2": {
"rotation": [-2.5, 0, 0]
},
"foot_back_right": {
"rotation": [0, 0, -2.5]
},
"head": {
"rotation": [-7.5, 0, 0]
}
}
},
"animation.deerling.blink": {
"animation_length": 0.1667,
"bones": {
"eyelid_left": {
"position": {
"0.0": [0.1, 0, -0.1],
"0.1667": {"pre": [0.1, 0, -0.1], "post": [0, 0, 0]}
}
},
"eye_left": {
"position": {
"0.0": [-0.1, 0, 0.1],
"0.1667": {"pre": [-0.1, 0, 0.1], "post": [0, 0, 0]}
}
},
"eye_right": {
"position": {
"0.0": [0.1, 0, 0.1],
"0.1667": {"pre": [0.1, 0, 0.1], "post": [0, 0, 0]}
}
},
"eyelid_right": {
"position": {
"0.0": [-0.1, 0, -0.1],
"0.1667": {"pre": [-0.1, 0, -0.1], "post": [0, 0, 0]}
}
},
"eyes_ditto": {
"position": {
"0.0": [0, 0, 0.1],
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
}
},
"eyes_blink_ditto": {
"position": {
"0.0": [0, 0, -0.1],
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
}
}
}
}
}
}
@@ -0,0 +1,129 @@
{
"format_version": "1.8.0",
"animations": {
"animation.sawsbuck_autumn.ground_idle": {
"loop": true,
"bones": {
"body": {
"position": [0, -0.4, 0]
},
"head": {
"rotation": [-3, 0, 0]
},
"leg_front_left": {
"rotation": [0, -3, -2]
},
"leg_front_left2": {
"rotation": [0, 0, 0]
},
"leg_front_left3": {
"rotation": [2.5, 0, 0]
},
"foot_front_left": {
"rotation": [-0.5, 0, 2]
},
"leg_back_left": {
"rotation": [-5, -3, -2]
},
"leg_back_left2": {
"rotation": [3, 0, 0]
},
"leg_back_left3": {
"rotation": [-5, 0, 0]
},
"foot_back_left": {
"rotation": [7, -1, 2]
},
"leg_front_right": {
"rotation": [0, 3, 2]
},
"leg_front_right2": {
"rotation": [0, 0, 0]
},
"leg_front_right3": {
"rotation": [2.5, 0, 0]
},
"foot_front_right": {
"rotation": [-0.5, 0, -2]
},
"leg_back_right": {
"rotation": [-5, 3, 2]
},
"leg_back_right2": {
"rotation": [3, 0, 0]
},
"leg_back_right3": {
"rotation": [-5, 0, 0]
},
"foot_back_right": {
"rotation": [7, 1, -2]
},
"ear_left": {
"rotation": [0, 0, -10]
},
"ear_right": {
"rotation": [0, 0, 10]
},
"tail1": {
"rotation": [-3, 0, 0]
},
"tail2": {
"rotation": [-5, 0, 0]
},
"torso": {
"rotation": [5, 0, 0]
},
"torso2": {
"rotation": [-5, 0, 0]
},
"neck": {
"rotation": [-5, 0, 0]
},
"ftorso2": {
"rotation": [-2.5, 0, 0]
}
}
},
"animation.sawsbuck_autumn.blink": {
"animation_length": 0.1667,
"bones": {
"eyelid_left": {
"position": {
"0.0": [0.1, -0.1, 0],
"0.1667": {"pre": [0.1, -0.1, 0], "post": [0, 0, 0]}
}
},
"eyelid_right": {
"position": {
"0.0": [-0.1, -0.1, 0],
"0.1667": {"pre": [-0.1, -0.1, 0], "post": [0, 0, 0]}
}
},
"eye_left": {
"position": {
"0.0": [-0.1, 0.1, 0],
"0.1667": {"pre": [-0.1, 0.1, 0], "post": [0, 0, 0]}
}
},
"eyebrow_left": {
"position": {
"0.0": [-0.1, 0.1, 0],
"0.1667": {"pre": [-0.1, 0.1, 0], "post": [0, 0, 0]}
}
},
"eye_right": {
"position": {
"0.0": [0.1, 0.1, 0],
"0.1667": {"pre": [0.1, 0.1, 0], "post": [0, 0, 0]}
}
},
"eyebrow_right": {
"position": {
"0.0": [0.1, 0.1, 0],
"0.1667": {"pre": [0.1, 0.1, 0], "post": [0, 0, 0]}
}
}
}
}
}
}
@@ -0,0 +1,129 @@
{
"format_version": "1.8.0",
"animations": {
"animation.sawsbuck_spring.ground_idle": {
"loop": true,
"bones": {
"body": {
"position": [0, -0.4, 0]
},
"head": {
"rotation": [-3, 0, 0]
},
"leg_front_left": {
"rotation": [0, -3, -2]
},
"leg_front_left2": {
"rotation": [0, 0, 0]
},
"leg_front_left3": {
"rotation": [2.5, 0, 0]
},
"foot_front_left": {
"rotation": [-0.5, 0, 2]
},
"leg_back_left": {
"rotation": [-5, -3, -2]
},
"leg_back_left2": {
"rotation": [3, 0, 0]
},
"leg_back_left3": {
"rotation": [-5, 0, 0]
},
"foot_back_left": {
"rotation": [7, -1, 2]
},
"leg_front_right": {
"rotation": [0, 3, 2]
},
"leg_front_right2": {
"rotation": [0, 0, 0]
},
"leg_front_right3": {
"rotation": [2.5, 0, 0]
},
"foot_front_right": {
"rotation": [-0.5, 0, -2]
},
"leg_back_right": {
"rotation": [-5, 3, 2]
},
"leg_back_right2": {
"rotation": [3, 0, 0]
},
"leg_back_right3": {
"rotation": [-5, 0, 0]
},
"foot_back_right": {
"rotation": [7, 1, -2]
},
"ear_left": {
"rotation": [0, 0, -10]
},
"ear_right": {
"rotation": [0, 0, 10]
},
"tail1": {
"rotation": [-3, 0, 0]
},
"tail2": {
"rotation": [-5, 0, 0]
},
"torso": {
"rotation": [5, 0, 0]
},
"torso2": {
"rotation": [-5, 0, 0]
},
"neck": {
"rotation": [-5, 0, 0]
},
"ftorso2": {
"rotation": [-2.5, 0, 0]
}
}
},
"animation.sawsbuck_spring.blink": {
"animation_length": 0.1667,
"bones": {
"eyelid_left": {
"position": {
"0.0": [0.1, -0.1, 0],
"0.1667": {"pre": [0.1, -0.1, 0], "post": [0, 0, 0]}
}
},
"eyelid_right": {
"position": {
"0.0": [-0.1, -0.1, 0],
"0.1667": {"pre": [-0.1, -0.1, 0], "post": [0, 0, 0]}
}
},
"eye_left": {
"position": {
"0.0": [-0.1, 0.1, 0],
"0.1667": {"pre": [-0.1, 0.1, 0], "post": [0, 0, 0]}
}
},
"eyebrow_left": {
"position": {
"0.0": [-0.1, 0.1, 0],
"0.1667": {"pre": [-0.1, 0.1, 0], "post": [0, 0, 0]}
}
},
"eye_right": {
"position": {
"0.0": [0.1, 0.1, 0],
"0.1667": {"pre": [0.1, 0.1, 0], "post": [0, 0, 0]}
}
},
"eyebrow_right": {
"position": {
"0.0": [0.1, 0.1, 0],
"0.1667": {"pre": [0.1, 0.1, 0], "post": [0, 0, 0]}
}
}
}
}
}
}
@@ -0,0 +1,141 @@
{
"format_version": "1.8.0",
"animations": {
"animation.sawsbuck_summer.ground_idle": {
"loop": true,
"bones": {
"body": {
"position": [0, -0.4, 0]
},
"head": {
"rotation": [-3, 0, 0]
},
"leg_front_left": {
"rotation": [0, -3, -2]
},
"leg_front_left2": {
"rotation": [0, 0, 0]
},
"leg_front_left3": {
"rotation": [2.5, 0, 0]
},
"foot_front_left": {
"rotation": [-0.5, 0, 2]
},
"leg_back_left": {
"rotation": [-5, -3, -2]
},
"leg_back_left2": {
"rotation": [3, 0, 0]
},
"leg_back_left3": {
"rotation": [-5, 0, 0]
},
"foot_back_left": {
"rotation": [7, -1, 2]
},
"leg_front_right": {
"rotation": [0, 3, 2]
},
"leg_front_right2": {
"rotation": [0, 0, 0]
},
"leg_front_right3": {
"rotation": [2.5, 0, 0]
},
"foot_front_right": {
"rotation": [-0.5, 0, -2]
},
"leg_back_right": {
"rotation": [-5, 3, 2]
},
"leg_back_right2": {
"rotation": [3, 0, 0]
},
"leg_back_right3": {
"rotation": [-5, 0, 0]
},
"foot_back_right": {
"rotation": [7, 1, -2]
},
"ear_left": {
"rotation": [0, 0, -10]
},
"ear_right": {
"rotation": [0, 0, 10]
},
"tail1": {
"rotation": [-3, 0, 0]
},
"tail2": {
"rotation": [-5, 0, 0]
},
"torso": {
"rotation": [5, 0, 0]
},
"torso2": {
"rotation": [-5, 0, 0]
},
"neck": {
"rotation": [-5, 0, 0]
},
"ftorso2": {
"rotation": [-2.5, 0, 0]
},
"bush_left3_summer": {
"rotation": [0, 0, -5]
},
"bush_left4_summer": {
"rotation": [0, 0, -7.5]
},
"bush_right3_summer": {
"rotation": [0, 0, 5]
},
"bush_right4_summer": {
"rotation": [0, 0, 7.5]
}
}
},
"animation.sawsbuck_summer.blink": {
"animation_length": 0.1667,
"bones": {
"eyelid_left": {
"position": {
"0.0": [0.1, -0.1, 0],
"0.1667": {"pre": [0.1, -0.1, 0], "post": [0, 0, 0]}
}
},
"eyelid_right": {
"position": {
"0.0": [-0.1, -0.1, 0],
"0.1667": {"pre": [-0.1, -0.1, 0], "post": [0, 0, 0]}
}
},
"eye_left": {
"position": {
"0.0": [-0.1, 0.1, 0],
"0.1667": {"pre": [-0.1, 0.1, 0], "post": [0, 0, 0]}
}
},
"eyebrow_left": {
"position": {
"0.0": [-0.1, 0.1, 0],
"0.1667": {"pre": [-0.1, 0.1, 0], "post": [0, 0, 0]}
}
},
"eye_right": {
"position": {
"0.0": [0.1, 0.1, 0],
"0.1667": {"pre": [0.1, 0.1, 0], "post": [0, 0, 0]}
}
},
"eyebrow_right": {
"position": {
"0.0": [0.1, 0.1, 0],
"0.1667": {"pre": [0.1, 0.1, 0], "post": [0, 0, 0]}
}
}
}
}
}
}
@@ -0,0 +1,141 @@
{
"format_version": "1.8.0",
"animations": {
"animation.sawsbuck_winter.ground_idle": {
"loop": true,
"bones": {
"body": {
"position": [0, -0.4, 0]
},
"head": {
"rotation": [-3, 0, 0]
},
"leg_front_left": {
"rotation": [0, -3, -2]
},
"leg_front_left2": {
"rotation": [0, 0, 0]
},
"leg_front_left3": {
"rotation": [2.5, 0, 0]
},
"foot_front_left": {
"rotation": [-0.5, 0, 2]
},
"leg_back_left": {
"rotation": [-5, -3, -2]
},
"leg_back_left2": {
"rotation": [3, 0, 0]
},
"leg_back_left3": {
"rotation": [-5, 0, 0]
},
"foot_back_left": {
"rotation": [7, -1, 2]
},
"leg_front_right": {
"rotation": [0, 3, 2]
},
"leg_front_right2": {
"rotation": [0, 0, 0]
},
"leg_front_right3": {
"rotation": [2.5, 0, 0]
},
"foot_front_right": {
"rotation": [-0.5, 0, -2]
},
"leg_back_right": {
"rotation": [-5, 3, 2]
},
"leg_back_right2": {
"rotation": [3, 0, 0]
},
"leg_back_right3": {
"rotation": [-5, 0, 0]
},
"foot_back_right": {
"rotation": [7, 1, -2]
},
"ear_left": {
"rotation": [0, 0, -10]
},
"ear_right": {
"rotation": [0, 0, 10]
},
"tail1": {
"rotation": [-3, 0, 0]
},
"tail2": {
"rotation": [-5, 0, 0]
},
"torso": {
"rotation": [5, 0, 0]
},
"torso2": {
"rotation": [-5, 0, 0]
},
"neck": {
"rotation": [-5, 0, 0]
},
"ftorso2": {
"rotation": [-2.5, 0, 0]
},
"bush_left3_summer": {
"rotation": [0, 0, -5]
},
"bush_left4_summer": {
"rotation": [0, 0, -7.5]
},
"bush_right3_summer": {
"rotation": [0, 0, 5]
},
"bush_right4_summer": {
"rotation": [0, 0, 7.5]
}
}
},
"animation.sawsbuck_winter.blink": {
"animation_length": 0.1667,
"bones": {
"eyelid_left": {
"position": {
"0.0": [0.1, -0.1, 0],
"0.1667": {"pre": [0.1, -0.1, 0], "post": [0, 0, 0]}
}
},
"eyelid_right": {
"position": {
"0.0": [-0.1, -0.1, 0],
"0.1667": {"pre": [-0.1, -0.1, 0], "post": [0, 0, 0]}
}
},
"eye_left": {
"position": {
"0.0": [-0.1, 0.1, 0],
"0.1667": {"pre": [-0.1, 0.1, 0], "post": [0, 0, 0]}
}
},
"eyebrow_left": {
"position": {
"0.0": [-0.1, 0.1, 0],
"0.1667": {"pre": [-0.1, 0.1, 0], "post": [0, 0, 0]}
}
},
"eye_right": {
"position": {
"0.0": [0.1, 0.1, 0],
"0.1667": {"pre": [0.1, 0.1, 0], "post": [0, 0, 0]}
}
},
"eyebrow_right": {
"position": {
"0.0": [0.1, 0.1, 0],
"0.1667": {"pre": [0.1, 0.1, 0], "post": [0, 0, 0]}
}
}
}
}
}
}
Loaded 100 of 425 files, more files were not shown because too many files have changed in this diff. Show more