Compare commits

..
Author SHA1 Message Date
Plastered_Crab 35b2b33eb4 mega keystone? more like iron helmet 2026-09-14 23:03:03 -07:00
Plastered_Crab 9900fbf3d6 Get back to parity with main 2026-09-14 15:38:52 -07:00
Plastered_Crab 1e8d012371 Add in all non ZA and MD mega stones 2026-09-14 14:20:49 -07:00
Plastered_Crab 82ef84d82b Merge branch 'main' into mega-space-program
# Conflicts:
#	common/src/main/java/com/cobblemon/mod/common/mixin/ClientPacketListenerMixin.java
#	common/src/main/kotlin/com/cobblemon/mod/common/api/molang/function/EvolutionMoLangFunctions.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/molang/function/PokemonMoLangFunctions.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/tms/TechnicalMachines.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/block/entity/PokeSnackBlockEntity.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/block/tmmachine/TMMachineMenu.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/gui/summary/Summary.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/pokemon/gen1/RattataAlolanModel.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/pokemon/gen1/RattataModel.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/net/messages/client/pokemon/update/evolution/ClientboundSeatAssignmentPacket.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/requirements/StatCompareRequirement.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/requirements/StatEqualRequirement.kt
#	common/src/main/resources/data/cobblemon/species/generation5/panpour.json
#	common/src/main/resources/data/cobblemon/species/generation8/duraludon.json
2026-09-12 16:15:46 -07:00
Plastered_Crab c0154cd82d Add Mega Shards and Mega Stones as well as simple "mega bond" increasing system 2026-09-12 16:02:03 -07:00
Plastered_Crab 1c9c833a49 Add Mega Shards and Mega Stones as well as simple "mega bond" increasing system 2026-09-12 15:52:50 -07:00
Hiroku 0bc5bb5caa Snap off and changelog updates 2026-09-13 08:03:56 +10:00
Hiroku 3330941a2c Merge remote-tracking branch 'origin/lang-updates' 2026-09-13 07:58:31 +10:00
Plastered_Crab 901e3e09e1 Add Mega Shards and Mega Stones as well as simple "mega bond" increasing system 2026-09-12 14:44:44 -07:00
Polymeta 79ea082d7e Merge branch '1.8.1-final-engi-fixes' into 'main'
1.8.1 final engi fixes

See merge request cable-mc/cobblemon!2404
2026-09-12 20:09:26 +00:00
Kenji_64 3d78929ff0 Entire Applin line has placeholder anims now! + other Applin related minor fixes 2026-09-12 21:36:12 +02:00
Kenji_64 5d94b03f7a Fixed Lucario's aura particles not emitting 2026-09-12 21:32:48 +02:00
Kenji_64 587305eb52 Added Fickle Beam's double damage text to en_us 2026-09-12 21:28:52 +02:00
Polymeta e363ce5e0e Merge branch 'Log-Spam-Fix' into 'main'
Fixed console spam with carved_ice_spikes. Fixed air pockets in deep sea spires

See merge request cable-mc/cobblemon!2403
2026-09-12 17:14:50 +00:00
Slippy e49b704200 Fixed console spam with carved_ice_spikes. Fixed air pockets in deep sea spires 2026-09-12 11:39:48 -05:00
Polymeta ec8b35860c Merge branch '1.8.1-implementation-fixes' into 'main'
fixed sableye's shoulder mount animations (thanks @kiteman.exe from discord)

See merge request cable-mc/cobblemon!2402
2026-09-12 06:40:07 +00:00
ewwww 74c49bf2da fixed sableye's shoulder mount animations (thanks @kiteman.exe from discord) 2026-09-11 22:39:37 -04:00
Gustavo PuerariandWeblate c03e7d9ed3 Translated using Weblate (Portuguese (Brazil))
Currently translated at 96.8% (9175 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/pt_BR/
2026-09-10 18:30:26 +00:00
Polymeta b54f2442c9 Merge branch '1.8.1-implementation-fixes' into 'main'
1.8.1 implementation fixes (again)

See merge request cable-mc/cobblemon!2393
2026-09-10 12:07:05 +00:00
Jerzykv 62c0545ea7 Moved recent implement_pokemon fixes to this branch 2026-09-10 13:49:26 +02:00
Polymeta f821abd7e5 fix helditem notation in spawn file 2026-09-10 11:04:04 +00:00
Gustavo PuerariandWeblate 2652e53e0c Translated using Weblate (Portuguese (Brazil))
Currently translated at 96.0% (9097 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/pt_BR/
2026-09-10 10:03:46 +00:00
Polymeta 08ac7d478d Merge branch 'shoulder-fix' into 'main'
Fixed Pokémon on shoulders not animating if they are visibly holding a held item.

Closes #1859

See merge request cable-mc/cobblemon!2392
2026-09-10 09:37:54 +00:00
Polymeta 4f51016ca9 Merge branch '1.8.1-implementation-fixes' into 'main'
1.8.1-implementation-fixes

See merge request cable-mc/cobblemon!2390
2026-09-10 09:34:16 +00:00
Paul ef8b4fd3cd Fixed Pokémon on shoulders not animating if they are visibly holding a held item.
Closes #1859
2026-09-10 19:20:36 +10:00
FrankTheFarmer f55972c190 Mantyke, Mantine, Quaxly recoil expression changelog 2026-09-10 19:15:14 +10:00
FrankTheFarmer 2dbf304ebe Merge branch 'main' into 1.8.1-implementation-fixes 2026-09-10 19:08:55 +10:00
FrankTheFarmer 1c5feab9c9 Removed recoil animation from Mantyke, Mantine, and Quaxly 2026-09-10 19:08:35 +10:00
Paul 1c720793c4 Rescraped spawns 2026-09-10 18:56:17 +10:00
FrankTheFarmer 6a170f8ce8 Merge branch 'main' into 1.8.1-implementation-fixes 2026-09-10 18:09:00 +10:00
Polymeta b4aba94f89 bump neoforge.mods.toml loaderVersion to be in line with what cobblemon is built against
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-10 08:22:19 +01:00
Polymeta 9832730f5c Merge branch 'Ancient-Wellspring-&-Chorus-Briar-Fixes' into 'main'
Ancient Wellsprings now how the correct habitat spawn pool. Chorus Briars no...

See merge request cable-mc/cobblemon!2391
2026-09-10 06:53:35 +00:00
Slippy ffdaec3e15 Ancient Wellsprings now how the correct habitat spawn pool. Chorus Briars no longer have air pockets. 2026-09-10 01:50:38 -05:00
ewwww d481f887a8 i just wanted to make it tinier c: 2026-09-10 02:47:35 -04:00
ewwww df3aec6222 updated region bias pack icon 2026-09-10 02:47:23 -04:00
Gustavo PuerariandWeblate b423fb1c46 Translated using Weblate (Portuguese (Brazil))
Currently translated at 95.4% (9040 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/pt_BR/
2026-09-10 00:39:17 +00:00
Gustavo PuerariandWeblate 8ae26a268a Translated using Weblate (Portuguese (Brazil))
Currently translated at 95.1% (9016 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/pt_BR/
2026-09-10 00:11:34 +00:00
Gustavo PuerariandWeblate 39b522abcc Translated using Weblate (Portuguese (Brazil))
Currently translated at 95.0% (9005 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/pt_BR/
2026-09-10 00:05:18 +00:00
Gustavo PuerariandWeblate 9bcfcef4fd Translated using Weblate (Portuguese (Brazil))
Currently translated at 94.8% (8983 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/pt_BR/
2026-09-09 23:29:29 +00:00
Gustavo PuerariandWeblate 60ac2d8058 Translated using Weblate (Portuguese (Brazil))
Currently translated at 94.4% (8947 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/pt_BR/
2026-09-09 23:06:46 +00:00
Gustavo PuerariandWeblate 9366d28141 Translated using Weblate (Portuguese (Brazil))
Currently translated at 94.4% (8946 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/pt_BR/
2026-09-09 22:24:00 +00:00
阿鱼andWeblate 75b48196b7 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (9472 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-09-09 14:13:55 +00:00
FrankTheFarmer 5180e2b501 Changelog for blastoise mount fix 2026-09-09 22:42:08 +10:00
FrankTheFarmer bb407b1d65 Removed unintentional second seat for Blastoise mount 2026-09-09 22:40:02 +10:00
FrankTheFarmer 8b308e5595 RIP Hisiuan Melon 2026-09-09 22:16:18 +10:00
FrankTheFarmer 148526e5e8 Added a blank watermelon png to prevent unintentional applications of melon cosmetic for hisuian electrode 2026-09-09 22:13:45 +10:00
FrankTheFarmer f319efcf12 Removed Hisuian Electrode's ability to turn into a watermelon. witermlon 2026-09-09 22:13:02 +10:00
Polymeta 5055e3926c Merge branch '1.8.1-engi-fixes' into 'main'
ampharos + alolan raticate fixes

See merge request cable-mc/cobblemon!2387
2026-09-09 08:05:11 +00:00
ewwww 3d92de18b8 ampharos + alolan raticate fixes 2026-09-09 03:47:57 -04:00
Polymeta ab9851fd12 fixed size altering effect and ensure pokemon dont get too small to live
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-09 08:05:55 +01:00
Polymeta a9df6bc709 Merge branch 'pokeball-catchrate-fix' into 'main'
Fixed Beast, Moon, Love, Timer and Luxury Ball behaviour not matching their tooltips

Closes #2062, #2063, #2064, #2065, and #2066

See merge request cable-mc/cobblemon!2384
2026-09-09 06:33:15 +00:00
Polymeta 3681813976 Merge branch 'misc-bug-fixes' into 'main'
Fix spawn file typos and type gem cluster bug.

See merge request cable-mc/cobblemon!2386
2026-09-08 14:38:12 +00:00
Polymeta 333d234dbe [skip-ci] push new tag documentation for 1.8
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-08 10:21:46 +01:00
Deltric ee42825dc3 Fix spawn file typos and type gem cluster bug. 2026-09-07 15:31:10 -05:00
Paul 0d9a4f9392 Some changelog [skip-ci] 2026-09-07 20:04:41 +10:00
Kiritiop 5e90c05fdd Fixed Beast, Moon, Love, Timer and Luxury Ball behaviour not matching their tooltips
Closes #2062
Closes #2063
Closes #2064
Closes #2065
Closes #2066
2026-09-07 05:42:05 +00:00
Hiroku 9a0648d956 Merge branch 'issue-2061' into 'main'
Fixed the TM Machine rejecting moves unlocked by unlockAllMoveDexMovesByDefault

Closes #2061

See merge request cable-mc/cobblemon!2383
2026-09-07 01:43:53 +00:00
Paul 91187ce952 Fixed NPCs getting their own TM progress file. Closes #2059 2026-09-07 11:42:33 +10:00
Hiroku 686bc62996 Merge branch 'refactor/molang-fn-code-smell' into 'main'
style: MoLang functions autumn codestyle cleanup

See merge request cable-mc/cobblemon!2368
2026-09-07 01:22:48 +00:00
Hiroku 5925247ace Merge branch 'wing_flap_fix' into 'main'
Removed incorrect sound keyframes from Rufflet, Braviary and Hisuian Braviary

See merge request cable-mc/cobblemon!2381
2026-09-07 01:05:11 +00:00
Kiritiop c6a4bec3f3 Fixed the TM Machine rejecting moves unlocked by unlockAllMoveDexMovesByDefault
The server-side guard in SetActiveTMPacketHandler ignored the config, so it rejected every move the config had revealed.

Closes #2061
2026-09-07 01:01:17 +00:00
Hiroku 7e582090f8 Merge branch 'issue-1774v2' into 'main'
Fixed stat_compare and stat_equal evolution requirements crashing on special stats (Version 2)

Closes #1774

See merge request cable-mc/cobblemon!2373
2026-09-07 00:25:47 +00:00
Kiritiop 55eef6ea69 Fixed stat_compare and stat_equal evolution requirements crashing on special stats
Stats.getStat did not recognise special_attack or special_defence and fell through to ACCURACY, which has no IV entry, so the requirement threw a NullPointerException from the server tick loop. Added the identifier names to getStat and made both requirements fail instead of crash for any non-permanent stat.

Closes #1774
2026-09-07 00:18:45 +00:00
Watuneni iteiteandWeblate f65cb9538c Translated using Weblate (Japanese)
Currently translated at 100.0% (9472 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-09-06 14:57:47 +00:00
Sterrezo 8dbce8fefe Removed incorrect sound keyframes from Rufflet, Braviary and Hisuian Braviary 2026-09-06 10:00:31 +02:00
Paul 4eb39566fa Resnap your shot 2026-09-06 11:52:33 +10:00
Paul b33536e872 Not snapshot 2026-09-06 11:24:44 +10:00
Paul 5957ddae58 Merge remote-tracking branch 'origin/main' 2026-09-06 10:54:43 +10:00
Paul fcb798161e A bunch more changelog cleanup [skip-ci] 2026-09-06 10:54:30 +10:00
Polymeta 357ac35802 Merge branch 'implement_pokemon' into 'main'
implement_pokemon

See merge request cable-mc/cobblemon!2380
2026-09-05 23:27:50 +00:00
Paul 25c6fb6d31 Merge remote-tracking branch 'origin/lang-updates' 2026-09-06 09:18:08 +10:00
Polymeta 7fd9b678f1 Merge branch 'implement_pokemon_local' into 'implement_pokemon'
Quick-fix a few learnset gaps

See merge request cable-mc/cobblemon!2371
2026-09-05 23:12:55 +00:00
Konoha DandPolymeta 169e06a26f Quick-fix a few learnset gaps 2026-09-05 23:12:55 +00:00
Polymeta ecfc71bfd1 Merge branch 'implement_pokemon' into 'main'
implement_pokemon

See merge request cable-mc/cobblemon!2379
2026-09-05 23:01:10 +00:00
Polymeta f2919aa75d Merge branch 'tm-machine-loop-sound-fix' into 'main'
Fix TM Machine sound not looping properly when UI is open

See merge request cable-mc/cobblemon!2378
2026-09-05 23:00:56 +00:00
Polymeta 336b076eee Merge branch 'tm-drop-fix' into 'main'
Fixed neoforge tms being dropped in GUI when applied to pokemon through TMM

See merge request cable-mc/cobblemon!2377
2026-09-05 23:00:15 +00:00
ewwww 7a970b4bc1 taillow z-fighting fix + boltund neck centering 2026-09-05 18:20:29 -04:00
plastered_crab 1947450d77 Fix TM Machine sound not looping properly when UI is open 2026-09-05 15:14:46 -07:00
Gertimoshka de94721703 Fixed neoforge tms being dropped in GUI when applied to pokemon through TMM 2026-09-05 23:58:56 +02:00
Polymeta c6e217dda7 Merge branch 'implement_pokemon' into 'main'
implement_pokemon

See merge request cable-mc/cobblemon!2376
2026-09-05 21:24:33 +00:00
ewwww 49f4a254d1 these got fixed and reverted 🥀 2026-09-05 17:06:46 -04:00
Polymeta b0886449bb Merge branch 'move-sync-fix' into 'main'
Fix learnset not syncing TMs or Egg moves.

Closes #2058

See merge request cable-mc/cobblemon!2375
2026-09-05 21:00:54 +00:00
Deltric aca61300d6 Fix learnset not syncing TMs or Egg moves. 2026-09-05 15:42:45 -05:00
Polymeta 5667ddf6a2 Merge branch 'implement_pokemon' into 'main'
implement_pokemon

See merge request cable-mc/cobblemon!2374
2026-09-05 20:10:27 +00:00
ewwww 702ce3b8f8 added various cries 2026-09-05 15:51:30 -04:00
Polymeta 096136b48e bucket tweaks 2026-09-05 19:21:55 +00:00
Polymeta 25bcd1dec2 alphas have two tms for sure 2026-09-05 19:18:24 +00:00
ewwww a9432fc1de Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-09-05 14:59:11 -04:00
Polymeta 7ec7152ec3 Merge branch 'display-case-cull-fix' into 'main'
Fixed incorrect culling of display case

See merge request cable-mc/cobblemon!2370
2026-09-05 16:33:11 +00:00
Polymeta 0ac7f664f1 Merge branch 'rocky-tidepool-water-fix' into 'main'
fixing some blockstate frozen water

See merge request cable-mc/cobblemon!2372
2026-09-05 14:52:37 +00:00
Slippy 4550cf31ed fixing some blockstate frozen water 2026-09-05 09:34:12 -05:00
Gertimoshka 4d783b2d15 Fixed incorrect culling of display case 2026-09-05 15:27:30 +02:00
Gitoido d034469ebb style: additional cleanups 2026-09-05 16:00:00 +03:00
Gitoido 4f8a44c95b style: missed one return 2026-09-05 12:52:22 +00:00
Gitoido 52b17ca39e style: homogeneous code style in molang function classes 2026-09-05 12:52:22 +00:00
Polymeta c0fc28d61d Merge branch 'move-sound-implementation' into 'main'
Quick sound implementation

See merge request cable-mc/cobblemon!2367
2026-09-05 12:41:14 +00:00
Polymeta 3701ae7dac Merge branch 'implement_pokemon' into 'main'
Implement pokemon typo fix

Closes #2049 and #2050

See merge request cable-mc/cobblemon!2369
2026-09-05 12:39:41 +00:00
ewwww b4cc95b3bf added bonk to duraludon's battle_cry 2026-09-05 08:24:32 -04:00
Jerzykv eb83d11952 Bergmite typo fix 2026-09-05 14:02:57 +02:00
geumuandWeblate dc2ffa7899 Translated using Weblate (Korean)
Currently translated at 99.6% (9436 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ko/
2026-09-05 09:41:24 +00:00
Paul a80689609c Merge remote-tracking branch 'origin/main' into implement_pokemon 2026-09-05 19:28:24 +10:00
Paul 42c2eab418 A bunch of changelog cleanup [skip-ci] 2026-09-05 19:26:19 +10:00
Sterrezo 7c1c0dd3c8 Merge branch 'main' into move-sound-implementation 2026-09-05 10:58:07 +02:00
Sterrezo 320f5f7090 Added sounds to Blizzard and Horn Drill 2026-09-05 10:57:55 +02:00
Paul beed7a322c Merge remote-tracking branch 'origin/main' 2026-09-05 16:47:31 +10:00
Paul b58e9c6de5 Updated best-spawner-config version so it auto re-exports on new version 2026-09-05 16:47:16 +10:00
Dmytro BunnuaandWeblate 3e7ae9750c Translated using Weblate (Ukrainian)
Currently translated at 75.9% (7193 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/uk/
2026-09-05 05:04:38 +00:00
Hiroku 85f80c530e Merge branch 'berry-patch-persistent-leaves-fix' into 'main'
fixing the false persistent leaves

See merge request cable-mc/cobblemon!2366
2026-09-05 00:05:08 +00:00
Slippy 1ab5f13704 fixing the false persistent leaves 2026-09-04 18:40:03 -05:00
Paul 782f0b8a5a Merge remote-tracking branch 'origin/main' 2026-09-05 07:24:59 +10:00
Paul 9dbd27b23c Fixed alphas not being stronger than you if your highest level party member is level 41-50, rescaled alpha level boost 2026-09-05 07:24:44 +10:00
Hiroku 2faf669ace Merge branch 'implement_pokemon' into 'main'
Many movelist changes and pokemon bug fixes

Closes #2049 and #2050

See merge request cable-mc/cobblemon!2365
2026-09-04 20:44:52 +00:00
Frank The FarmerandHiroku 1c38433dda Many movelist changes and pokemon bug fixes 2026-09-04 20:44:52 +00:00
Paul d9d3583cb1 Wrong operator chief (closes #2057) 2026-09-05 05:35:33 +10:00
Jerzykv dd7edee941 Changelog 2026-09-04 20:11:59 +02:00
Jerzykv 616d16e11f Fixed #2050 2026-09-04 20:07:18 +02:00
Jerzykv 77a648cebd Fixed "shellos tree-birch" and "composer" typos, fixing #2049 2026-09-04 20:06:57 +02:00
Paul adaa852b59 Data scrape for deerling, sawsbuck, psyduck 2026-09-04 23:30:37 +10:00
Paul c17c48caec Merge remote-tracking branch 'origin/main' into implement_pokemon 2026-09-04 23:28:10 +10:00
Paul f6a833a5fd Fixed drops script not pointing to the right sheet, allow flower species feature to be used without a key, scraped spawns 2026-09-04 23:24:10 +10:00
Polymeta ab3d26dee6 Merge branch 'showdown-zip' into 'main'
Update showdown zip

See merge request cable-mc/cobblemon!2364
2026-09-04 10:34:25 +00:00
gruxor 37e526614f Update showdown zip 2026-09-04 06:29:26 -04:00
FrankTheFarmer 0f3c6d07c8 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-09-04 19:39:25 +10:00
FrankTheFarmer 3be4ae8fa6 Temporarily restoring Crobat's ability to walk 2026-09-04 19:39:18 +10:00
RedGenesectNinja d9b027dd66 I missed a few 2026-09-04 04:17:44 -05:00
Polymeta 612b75d3e5 Merge branch 'main' into implement_pokemon 2026-09-04 09:39:15 +01:00
Sterrezo bbfe6bd143 Revert "Added Cry SFX for Surskit, Duskull, Dusclops, Applin, Flapple, Duraludon (w/ bonk), Dipplin, Archaludon, & Hydrapple. Added Move SFX for Play Rough."
This reverts commit 562418e324.
2026-09-04 10:05:21 +02:00
FrankTheFarmer 219fcc369f Restored Dhelmise's seat locator again 2026-09-04 16:09:08 +10:00
RedGenesectNinja e28eb0e3da Removed Power Shift from TM learnsets due to it not having a TM 2026-09-04 00:37:41 -05:00
RedGenesectNinja b3160795fe Moved Captivate and Natural Gift from TM to Legacy learnsets 2026-09-04 00:27:18 -05:00
Polymeta aa68ef640d Merge branch 'tm-fix' into 'main'
Fixed TM serializing dummy move components and not being usable when first retrieved from a loot chest

See merge request cable-mc/cobblemon!2363
2026-09-04 05:25:29 +00:00
SplashOfAzure 562418e324 Added Cry SFX for Surskit, Duskull, Dusclops, Applin, Flapple, Duraludon (w/ bonk), Dipplin, Archaludon, & Hydrapple. Added Move SFX for Play Rough. 2026-09-03 21:37:41 -07:00
Hiroku 47a6d450cf Merge branch 'mp-ride-fix' into 'main'
Fixed crash when passenger packet arrives before seat assignment packet

See merge request cable-mc/cobblemon!2362
2026-09-04 04:25:35 +00:00
CraterandHiroku 14e4abca27 Fixed crash when passenger packet arrives before seat assignment packet 2026-09-04 04:25:35 +00:00
Hiroku 108716eb84 Merge branch 'temp-showdown-update' into 'main'
Update showdown version

See merge request cable-mc/cobblemon!2334
2026-09-04 04:08:34 +00:00
Paul 8c6ef80cf5 Fixed TM serializing dummy move components and not being usable when first retrieved from a loot chest 2026-09-04 14:07:52 +10:00
Hiroku 2d61cb5979 Merge branch 'alpha-eye-fixes' into 'main'
Alpha particles no longer render on pokemon without eyes and Alpha eye trails...

See merge request cable-mc/cobblemon!2361
2026-09-04 03:32:06 +00:00
FrankTheFarmer 5b8dc82ef2 Changelott 2026-09-04 12:53:18 +10:00
FrankTheFarmer 1d3bc137ad Fixed z-fighting on Hisui Bias Dewott 2026-09-04 12:50:25 +10:00
FrankTheFarmer 35e6caf6f2 Converted Hisui Bias Dewott to json because somebody forgot (it was me) 2026-09-04 12:50:11 +10:00
FrankTheFarmer cd827eccbf Converted Oshawott to json poser 2026-09-04 12:40:55 +10:00
JPAKx4 3e89e2b9ad Alpha particles no longer render on pokemon without eyes and Alpha eye trails no longer render on pokemon mid capture. 2026-09-03 21:12:09 -05:00
FrankTheFarmer f0c0bb2521 hitboxes and stuff changelog 2026-09-04 11:01:23 +10:00
FrankTheFarmer 82c2ce22c1 Torkoal tweaks. He will no longer be faster than the hares 🚫🐢💨 2026-09-04 11:00:42 +10:00
FrankTheFarmer d14a269b29 Restored Lanturn's missing ground walk and sleep animations 2026-09-04 10:59:40 +10:00
FrankTheFarmer a726408482 Corrected the scales, hitbox sizes, and eye heights of many pokemon 2026-09-04 10:59:27 +10:00
Dmytro BunnuaandWeblate 7e8b972c70 Translated using Weblate (Ukrainian)
Currently translated at 75.2% (7130 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/uk/
2026-09-03 21:35:23 +00:00
SplashOfAzure 016db10de7 Added Sound for Light Screen 2026-09-03 13:41:46 -07:00
RedGenesectNinja 15e76629cb Leafeon, Turtwig, Grotle and Torterra could learn Leafage through TM when they shouldn't 2026-09-03 15:39:26 -05:00
RedGenesectNinja 10c0c188b3 Grimer and Muk could learn Smog through TM when they shouldn't 2026-09-03 15:38:40 -05:00
RedGenesectNinja fd9d3a3028 Adjusted Cherubi and Cherrim's scale 2026-09-03 15:36:52 -05:00
Sterrezo 61d01d3731 Added sounds for Hydro Pump, updated Kinesis 2026-09-03 22:14:07 +02:00
Polymeta ba5529e401 Merge branch 'adorn-compat-update' into 'main'
Update Adorn minimum compatibility version to 6.3.1.

See merge request cable-mc/cobblemon!2351
2026-09-03 16:09:14 +00:00
Jackson Bowes e0a4a72f9c Merge branch 'stamina-doubling-the-squeakuel' into 'main'
return stamina mins to previous and raise stamina regen where it matters

See merge request cable-mc/cobblemon!2360
2026-09-03 11:07:29 -05:00
Paul 59c922dee0 Merge remote-tracking branch 'origin/main' 2026-09-03 20:52:37 +10:00
Paul e5dda3e975 In herds, levelRangeOffset applies last in the process instead of near the start, fixes Unowns 2026-09-03 20:52:29 +10:00
jackowes 7d75993562 return stamina mins to previous and raise stamina regen where it matters 2026-09-02 20:28:49 -05:00
Jackson Bowes 54c64d3dfe Merge branch 'double-stams' into 'main'
double all ride stams

See merge request cable-mc/cobblemon!2359
2026-09-02 18:09:46 -05:00
jackowes ddf1396abb double all ride stams 2026-09-02 17:36:39 -05:00
Paul e3b2220813 Only wild alphas hide their items 2026-09-03 08:29:32 +10:00
Hiroku f982a94ebb Merge branch 'some-fixes' into 'main'
A few bug fixes

See merge request cable-mc/cobblemon!2358
2026-09-02 22:28:53 +00:00
阿鱼andWeblate 1c97760188 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (9472 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-09-02 22:17:31 +00:00
Dmytro BunnuaandWeblate c6d33e6cec Translated using Weblate (Ukrainian)
Currently translated at 73.5% (6971 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/uk/
2026-09-02 22:17:30 +00:00
Gustavo PuerariandWeblate 8e6e1cc3f2 Translated using Weblate (Portuguese (Brazil))
Currently translated at 94.4% (8946 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/pt_BR/
2026-09-02 22:17:29 +00:00
Watuneni iteiteandWeblate ea9654af61 Translated using Weblate (Japanese)
Currently translated at 100.0% (9472 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-09-02 22:17:28 +00:00
TeggyandWeblate b8ccaf9f89 Translated using Weblate (Spanish)
Currently translated at 100.0% (9472 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-09-02 22:17:27 +00:00
Jackson Bowes dac42f1971 Merge branch 'issue-1738' into 'main'
Fixed bird mounts flickering between their fly and glide animations

Closes #1738

See merge request cable-mc/cobblemon!2346
2026-09-02 14:37:25 -05:00
Jerzykv 09152c7e97 begone, bracket! 2026-09-02 20:26:38 +02:00
Jerzykv f9cddd02a9 Very minor changes to Cinderace's battle intro 2026-09-02 20:26:08 +02:00
Jerzykv 4920244767 Dracovish can no longer swim 2026-09-02 20:07:42 +02:00
Jerzykv 9b20a55b69 Added focus blast to Clawitzer at level 2026-09-02 20:00:55 +02:00
Jerzykv 9f1be5feaa Added body press to Probopass at level 2026-09-02 20:00:38 +02:00
Jerzykv 8ea8968a04 More alpha eye stuff 2026-09-02 19:57:48 +02:00
Jerzykv efd4f3c623 Adjusted Miltank's alpha eyes 2026-09-02 19:09:19 +02:00
Jerzykv 4a06c57a1b Fixed Alolan Sandslash's cry animation, added it to the poser 2026-09-02 19:04:32 +02:00
Watuneni iteiteandWeblate 2ab3d396d2 Translated using Weblate (Japanese)
Currently translated at 100.0% (9468 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-09-02 11:14:53 +00:00
Paul 297fa6f9b0 Changelog 2026-09-02 21:13:18 +10:00
Paul 455b6bde43 Merge remote-tracking branch 'origin/lang-updates' 2026-09-02 21:12:20 +10:00
Hiroku b3f32404cb Merge branch 'random-starter' into 'main'
Random Starters

See merge request cable-mc/cobblemon!2354
2026-09-02 11:10:13 +00:00
Paul 8b4326a82d Logic cleanup 2026-09-02 21:05:45 +10:00
Paul fb45d110bc Merge remote-tracking branch 'origin/random-starter' into random-starter 2026-09-02 20:58:07 +10:00
Paul a51ccb1a86 Merge remote-tracking branch 'origin/main' into random-starter
# Conflicts:
#	common/src/main/resources/data/cobblemon/callbacks/pokemon_entity_spawn/apply_marks.molang
2026-09-02 20:57:59 +10:00
Hiroku e3cfb7199d Merge branch 'main' into 'random-starter'
# Conflicts:
#   common/src/main/resources/data/cobblemon/callbacks/pokemon_entity_spawn/apply_marks.molang
2026-09-02 10:54:05 +00:00
Paul 3bb61dd649 Alphas don't show their held items 2026-09-02 20:51:42 +10:00
Paul b55d4d576d Merge remote-tracking branch 'origin/main' into some-fixes 2026-09-02 20:31:10 +10:00
Paul 99d818d11e Fixed syncing of battle victory count not running, leading to R to battle prompt staying after first victory 2026-09-02 20:28:51 +10:00
Paul 48b546fad4 Fixed (hackily) gender selection for strictly gender-formed pokemon 2026-09-02 20:13:38 +10:00
Paul 2ee04e3b1c Ensure healthy pokemon appear earlier in the party list when converting a party to a battle team 2026-09-02 19:18:02 +10:00
Hiroku fbef8ff0dc Merge branch 'fix/battle-faint-enhancements' into 'main'
🐛 fix: battle faint enhancement

See merge request cable-mc/cobblemon!2323
2026-09-01 12:02:22 +00:00
Polymeta c25a8ca8b5 Merge branch 'Added-TM-Loot-and-Bug-Fixes' into 'main'
Added tm loot and bug fixes

See merge request cable-mc/cobblemon!2357
2026-09-01 09:22:47 +00:00
Jerzykv 248cc4c5bc Added more missing moves 2026-09-01 11:02:56 +02:00
Jerzykv 1bbebfd3ae Removed Volt Tackle as a TM for Pikachu line, Alolan Raichu move edits 2026-09-01 10:59:53 +02:00
Jerzykv 5b5a0afc45 The correct Samurott now has its moves 2026-09-01 10:34:56 +02:00
Slippy d07729abfc adjusted deep sea spire location 2026-08-31 21:56:25 -05:00
Slippy 945c34ccd6 woops. small issue 2026-08-31 12:23:15 -05:00
Jerzykv 01d2350993 Removed non-existing cry from Sandygast and Palossand 2026-08-31 13:10:20 +02:00
Jerzykv 93b7c421cb Fixed Alolan Sandshrew's cry 2026-08-31 13:09:45 +02:00
Jerzykv 192542a362 Added missing moves to 17 Pokemon 2026-08-31 12:29:04 +02:00
Hiroku 8af45c01d8 Merge branch 'they-are-jumbo-sized-alphas' into 'main'
Fixed Alphas and other pokemon getting improper size marks

See merge request cable-mc/cobblemon!2349
2026-08-31 10:21:45 +00:00
Jerzykv 96d508d548 Added Hisui bias Rufflet's model 2026-08-31 11:40:45 +02:00
Hiroku a51db63d86 Merge branch 'level-spawning' into 'main'
Add ability to blocklist worlds from spawning Pokemon

See merge request cable-mc/cobblemon!2356
2026-08-31 09:27:48 +00:00
Daniel Naylor f906079a25 Update changelog, add config en-US language strings 2026-08-31 10:24:48 +01:00
Paul efd77e1337 Scraped more spawns 2026-08-31 19:04:43 +10:00
Paul acc4f341b6 Fixed dudunsparce and maushold always having the same form instead of having randomization 2026-08-31 19:03:56 +10:00
Jerzykv 567372ef51 Bergmite can now evolve into Hisuian Avalugg, and added bias form 2026-08-31 11:02:59 +02:00
Paul 53b712f8a2 Added isPokeSnack condition to spawning 2026-08-31 19:02:31 +10:00
Gertimoshka d787aabe4e Remove Dead Import 2026-08-31 10:37:35 +03:00
Slippy 327c8b47d1 made submerged spike blend in more 2026-08-30 13:02:41 -05:00
Slippy 448b02cef9 fixed a bug with rocky outcrops 2026-08-30 12:47:16 -05:00
Slippy 1c669d3eae fixed bugs with wildfire scar habitats 2026-08-30 12:45:41 -05:00
Slippy 14d6b55989 After playtesting, added difficult to get TMs to gimmi towers 2026-08-30 12:19:44 -05:00
Daniel Naylor c3d023a56b Remove Jetbrains annotation 2026-08-30 15:32:45 +01:00
JPAKx4 8382e9d904 Merge remote-tracking branch 'origin/adorn-compat-update' into adorn-compat-update 2026-08-30 08:41:08 -05:00
JPAKx4 2cf907bec1 Update Adorn minimum compatibility version to 6.3.1. 2026-08-30 08:40:49 -05:00
Daniel Naylor d167d4c2f2 Add ability to blocklist worlds from spawning Pokemon
To achieve this, we cache the ResourceLocation of a world on the ServerLevel, and check that as an additional condition in ServerPlayerMixin.cobblemon. The configuration accepts a new config item level_spawning_blocklist to allow server owners to specify this. This allows server owners that don't want Cobblemon to spawn in certain worlds to turn it off here, rather than use other plugins that delete entities, saving valuable ticks
2026-08-30 12:37:45 +01:00
Paul 61181f3f1c Clamping probably goes the other way around for herds 2026-08-30 12:03:53 +10:00
Kenji_64 156d193fc8 Fixed Sliggoo (+ Hisuian) evolving in the Nether and End, where it can't rain 2026-08-29 15:03:18 +02:00
Kenji_64 b5b22e2c23 Fixed some jarring z-fighting in Togedemaru's model 2026-08-29 14:57:34 +02:00
Kenji_64 e47275236a Fixed a shiny error with Dracovish 2026-08-29 14:44:37 +02:00
Kenji_64 eb96be2eff Fixed Garganacl's missing cry sound 2026-08-29 14:42:48 +02:00
Kenji_64 f620f84222 Fixed Combee's pollinated state overwriting the shiny and female textures 2026-08-29 14:41:08 +02:00
Watuneni iteiteandWeblate 2c79a66298 Translated using Weblate (Japanese)
Currently translated at 100.0% (9468 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-08-28 15:03:32 +00:00
Jerzykv 27ce289de0 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-08-28 10:24:29 +02:00
Jerzykv bc4076f734 Fixed a typo in Darumaka's poser 2026-08-28 10:24:05 +02:00
Hiroku b2b5a8f715 Merge branch 'whoopsies' into 'main'
Fix parents

See merge request cable-mc/cobblemon!2355
2026-08-28 04:57:52 +00:00
dhi_holo 46764b9fab Fix parents
whoopsies
2026-08-28 09:41:11 +10:00
Kenji_64 d212cc0ad1 Skiploom drops the correct flower now 2026-08-28 00:27:00 +02:00
Gertimoshka 600e07fe08 Simpler fix 2026-08-27 21:17:00 +03:00
Hiroku 4a3f3b0d3f Merge branch '1.8-tag-updates' into 'main'
Edits to a few tags.

See merge request cable-mc/cobblemon!2353
2026-08-27 16:18:58 +00:00
Paul bc423515fb Merge remote-tracking branch 'origin/main' 2026-08-28 01:10:36 +10:00
Paul 13e8e39d43 Rescraped spawns 2026-08-28 01:10:25 +10:00
Paul 8dd6446b6a Fixed uncommon herds not spawning more than a leader most of the time, improved(?) herd members spawning in walls 2026-08-28 01:07:54 +10:00
Hiroku 8fc9b46ce7 Merge branch 'implement_pokemon' into 'main'
e

See merge request cable-mc/cobblemon!2352
2026-08-27 10:52:13 +00:00
Hiroku 832d315501 The possessive pronoun is without the apostrophe
(Otherwise it is a contraction)
2026-08-27 10:34:29 +00:00
Hiroku d4da62adb5 Merge branch 'More-Habitat-Bug-Fixes' into 'main'
Increased the rarity of fossil structures again. Adjusted so type gems should...

See merge request cable-mc/cobblemon!2344
2026-08-27 10:31:33 +00:00
Hiroku f7f6e9d2ee Merge branch 'patch-2' into 'main'
Fixed a typo causing the AI to not recognize surging strikes as a multi hit move.

See merge request cable-mc/cobblemon!2342
2026-08-27 10:30:20 +00:00
Veraxiel 8a375d5e81 Edits to a few tags. 2026-08-27 05:37:32 -04:00
geumuandWeblate 0bb860c9ab Translated using Weblate (Korean)
Currently translated at 99.6% (9432 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ko/
2026-08-27 02:03:22 +00:00
Gertimoshka 7616008cd3 Random Option now exist for Starter Selection, for those who cannot choose 2026-08-25 15:04:07 +03:00
Paul cf36db419d Oopsy on level clamping 2026-08-25 20:17:50 +10:00
Paul f634d7fae0 missed a file 2026-08-25 19:49:55 +10:00
Paul dddb3b3d48 Koffing and Sudowoodo herd fixes 2026-08-25 19:49:47 +10:00
Paul 2468f8a8e6 Fixed spawn scraper not processing Leader = "only" properly from the sheet, leading to impossible spawns 2026-08-25 19:45:27 +10:00
Paul c6ce359736 Fixed herdable pokemon levelRange field not being used to clamp the resulting level possibilities 2026-08-25 19:40:48 +10:00
Gertimoshka e99ba8cd1d Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon 2026-08-25 11:28:43 +03:00
ewwww 3f35ca5bd2 switcheroo 2026-08-24 15:26:31 -04:00
Paul e053d2864e Merge remote-tracking branch 'origin/main' 2026-08-24 23:15:09 +10:00
Paul e098426a6d Fixed issue in behaviour scraper, fixed see sky condition on herds not being interpreted, rescraped herds and species data 2026-08-24 23:14:58 +10:00
Hiroku 069f483552 Merge branch 'compost-this' into 'main'
Composting Fixes

Closes #2008 and #2043

See merge request cable-mc/cobblemon!2348
2026-08-24 11:18:40 +00:00
ewwww 9a8c7dc8bf oops forgot this too 2026-08-24 04:17:21 -04:00
Paul 82ba0334a7 open keyword not needed 2026-08-24 18:14:44 +10:00
Paul bfed3ad52f Fixed habitat natural-mode injecting spawns into all buckets 2026-08-24 18:13:52 +10:00
ewwww 149a59ffbf oops forgot this 2026-08-24 04:01:21 -04:00
Paul a7dd139c14 More script rescraping but bombing existing spawns first, correctly purging removed spawns 2026-08-24 18:00:42 +10:00
ewwww 3866e888de Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-08-24 03:55:30 -04:00
ewwww 5089ea5027 added regigigas & regidrago eye locators 2026-08-24 03:55:05 -04:00
Paul 840762c00d Some scraping of spawns 2026-08-24 17:53:29 +10:00
ewwww b38c44321e various seat locator naming fixes 2026-08-24 03:41:34 -04:00
ewwww 1ba7ed425e i'm not sure either 2026-08-24 03:15:00 -04:00
ewwww 9645a1dd17 locators for regieleki 2026-08-24 00:06:09 -04:00
ewwww 232d871bef fixed ho-oh's missing alpha texture 2026-08-24 00:05:57 -04:00
ewwww ce0b5b77bd compostable eggant berry 2026-08-24 00:05:45 -04:00
JPAKx4 a4f2472431 Update Adorn minimum compatibility version to 6.3.1. 2026-08-23 13:06:38 -05:00
dhi_holo aaae400f0a Merge branch 'data-fixings' into compost-this 2026-08-23 13:05:12 +10:00
dhi_holo ba42daa83e Merge branch 'main' into compost-this 2026-08-23 13:05:00 +10:00
geumuandWeblate 85577259a6 Translated using Weblate (Korean)
Currently translated at 99.6% (9432 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ko/
2026-08-22 20:02:37 +00:00
geumuandWeblate 5271e09d8d Translated using Weblate (Korean)
Currently translated at 99.6% (9432 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ko/
2026-08-22 19:38:43 +00:00
Polymeta c6cb98cabd Delete 0460_abomasnow_alpha.json 2026-08-22 14:23:00 +00:00
Polymeta 95f56fc1f8 Delete 0738_vikavolt_alpha.json 2026-08-22 14:12:58 +00:00
Polymeta a328c54dda Edit 0629_vullaby_herd.json 2026-08-22 13:58:54 +00:00
Gertimoshka 12db01b7b2 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon 2026-08-22 13:02:20 +03:00
Paul 93515777b8 oop put that back 2026-08-22 17:40:15 +10:00
Paul 16e327bd27 Rescraped behaviours, scraped spawns 2026-08-22 17:40:00 +10:00
Hiroku 0edb9ad115 Merge branch 'pasture-block-performance-optimization' into 'main'
Index Pokémon stores by UUID for O(1) lookup

See merge request cable-mc/cobblemon!2345
2026-08-22 02:18:14 +00:00
Hiroku 8189e5d819 Merge branch 'Habitat-Adjustments' into 'main'
bunch of fixes

See merge request cable-mc/cobblemon!2350
2026-08-22 00:39:34 +00:00
Slippy 72234e6260 turned the rocky outcrops into a biome feature because I can not get it to generate correctly as a structure 2026-08-21 13:21:12 -05:00
Mosekon 48e341aca6 Document UUID store index in changelog
Note the party/PC UUID lookup optimization under 1.8.0 Changes.
2026-08-21 10:16:21 -07:00
Slippy f2aed673ee Some new Biome tags. Adjusted biome tags of some structures 2026-08-21 12:02:58 -05:00
Slippy 51d214d45e Fixed Sus gravel not generating loot 2026-08-21 11:47:34 -05:00
Nico MerenqandWeblate a4619e761b Translated using Weblate (Russian)
Currently translated at 85.5% (8099 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-08-20 15:52:21 +00:00
Slippy fa67e21a76 Gimmighoul chests in most ruins are now a 10% chance to generate rather than a 40% or 50% chance to generate so you are more likely to get the TM. This does not include gimmighoul towers. 2026-08-20 10:47:56 -05:00
Gertimoshka 62cccd204a Fixed Alphas and Other pokemon getting improper size marks 2026-08-20 14:12:15 +03:00
Gertimoshka ec9c1a0db3 Fixed Alphas having size marks improperly applied 2026-08-20 14:10:43 +03:00
FrankTheFarmer d2bbf12598 Removed shiny aspect line in magikarp jump pattern resolver which broke some addons 2026-08-20 14:02:47 +10:00
dhi_holo e58ffb9a00 Compostables updated 2026-08-20 12:51:18 +10:00
dhi_holo 0281dd2e94 Eggant
Closes #2043
2026-08-20 11:07:04 +10:00
Nico MerenqandWeblate 4060472a23 Translated using Weblate (Russian)
Currently translated at 85.4% (8087 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-08-20 00:30:16 +00:00
Nico MerenqandWeblate 58ec5f38ed Translated using Weblate (Russian)
Currently translated at 85.0% (8055 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-08-19 21:00:15 +00:00
Nico MerenqandWeblate 38938b99ac Translated using Weblate (Russian)
Currently translated at 85.0% (8054 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-08-19 20:34:20 +00:00
Nico MerenqandWeblate ba204b1473 Translated using Weblate (Russian)
Currently translated at 85.0% (8051 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-08-19 19:41:52 +00:00
Nico MerenqandWeblate fd2de073d9 Translated using Weblate (Russian)
Currently translated at 84.9% (8040 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-08-19 18:28:23 +00:00
Nico MerenqandWeblate ecec45fb7f Translated using Weblate (Russian)
Currently translated at 84.7% (8022 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-08-19 18:05:22 +00:00
Slippy 9889307981 reduce type gem rarity. They were suddenly increased when fixing a world gen issue 2026-08-19 12:00:09 -05:00
Jerzykv 9a18dcadd4 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-08-18 20:50:39 +02:00
Jerzykv 7e6864764f Fixed Servine t-posing in battle 2026-08-18 20:50:19 +02:00
Kiritiop 8468d81286 Fixed bird mounts flickering between their fly and glide animations
activeInput was only set inside the speed cap check, so a mount reaching its top speed while the player still held W was indistinguishable from the player letting go, and the glide flag flipped every tick.

Both the glide and FLY pose thresholds were also hardcoded to 0.2, which is exactly the top speed of a minimum SPEED stat mount, so slow mounts sat on the boundary instead of crossing it.

activeInput now comes from player input alone, the glide test has a 0.19/0.13 hysteresis band using state.gliding as its own memory, and the FLY pose threshold moved to 0.1 to match JetBehaviour.

Closes #1738
2026-08-18 14:23:24 -04:00
plastered_crab e6636bb6af no changelog needed I guess 2026-08-18 11:19:32 -07:00
plastered_crab 78706548ab mixin to prevent our structures from leaving behind artifacted terrain when removing their placement via our processor. Now does a check before generation entirely so it never gets placed down in the first place. 2026-08-18 11:04:18 -07:00
阿鱼andWeblate a5184be524 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (9468 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-08-17 22:28:33 +00:00
Mosekon da6455fd33 Index Pokémon stores by UUID for O(1) lookup
get(uuid) scanned the whole store and allocated a full PC list. Pasture
block entities call that per tether per tick, which dominated MSPT with
hundreds of pastured Pokémon.

Keep an in-memory UUID map on set/remove, and rebuild after load and PC
resize. Public initialize() stays overridable for a micro version bump.

Measured: 240 pastures / 480 assigned, 73 ms to 16 ms MSPT.
2026-08-17 14:38:19 -07:00
阿鱼andWeblate 7e544346dd Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (9468 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-08-17 15:18:38 +00:00
阿鱼andWeblate 4812a33169 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 99.6% (9439 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-08-17 13:00:09 +00:00
阿鱼andWeblate 0fbd003cba Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 99.4% (9417 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-08-17 11:02:53 +00:00
FrankTheFarmer 074073a933 Changelog moment 2026-08-17 19:58:13 +10:00
FrankTheFarmer f82712e6b4 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-08-17 19:51:48 +10:00
FrankTheFarmer 5745ff547c Removed line in Gyarados-jump resolver that seems to be breaking gummy related gyarados addons 2026-08-17 19:51:29 +10:00
FrankTheFarmer 8f5617c986 Fixed eevee using it's shoulder animation when in battle due to battle conditions in its standing pose 2026-08-17 19:48:24 +10:00
RedGenesectNinja 02f69779a1 Adjusted Mightyena's hitbox 2026-08-16 18:57:02 -05:00
Jerzykv a2bfc55e96 Lanturn poser fix again 2026-08-16 22:01:52 +02:00
Paul 1f9c632883 Fixed scraper for behaviours skipping no-form records, rescraped 2026-08-15 14:38:56 +10:00
Jerzykv 7fecdb1126 Changelog 2026-08-14 18:31:34 +02:00
Jerzykv f88ec39ec7 Fixed Tentacool's surfacewater animation 2026-08-14 15:20:05 +02:00
Jerzykv f8b4b2fc2a Part 2 of restoring and adding cries 2026-08-13 08:33:19 +02:00
Slippy 271aef9821 Increased the rarity of fossil structures again. Adjusted so type gems should only generate below y level 0. There were some instances of them generating up high. Adjusted some other things 2026-08-13 00:55:33 -05:00
FrankTheFarmer ce9970ca1a Added unown's missing cry 2026-08-13 15:11:30 +10:00
Jerzykv bf47299b2f Multiple cries restored + poser conversions and fixes 2026-08-12 19:09:26 +02:00
Jerzykv e56b399c59 Darumaka no longer has empty eyes while sleeping 2026-08-12 18:38:02 +02:00
Nico MerenqandWeblate 39098134e8 Translated using Weblate (Russian)
Currently translated at 84.5% (8004 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-08-12 08:53:44 +00:00
redmageman 0f505ea3f8 Added the fix to the changelog 2026-08-11 20:13:10 -05:00
Slippy 478859d0ed more fixes 2026-08-11 16:38:48 -05:00
Slippy cf95f62185 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into More-Habitat-Bug-Fixes 2026-08-11 15:39:51 -05:00
Nico MerenqandWeblate 6ba2fc5b65 Translated using Weblate (Russian)
Currently translated at 84.5% (8001 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-08-11 18:34:14 +00:00
Nico MerenqandWeblate c25724967a Translated using Weblate (Russian)
Currently translated at 84.3% (7983 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-08-11 10:30:50 +00:00
Slippy 067e9d42e7 Flowerbed clearing habitat block fix 2026-08-11 00:13:07 -05:00
Nico MerenqandWeblate 0063c8c8af Translated using Weblate (Russian)
Currently translated at 84.3% (7983 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-08-11 04:53:41 +00:00
Kenji_64 ebdffb44e8 Properly added Snorlax' sleep_intro back 2026-08-10 23:20:57 +02:00
Kenji_64 6cc609fdfc Fixed Metang using the "wrong" animations as a perma-flyer 2026-08-10 23:11:39 +02:00
Kenji_64 864a4f5c09 Fixed Floragato's alt cry having no sound 2026-08-10 23:03:08 +02:00
Kenji_64 d2f365504b Dragapult poser fixes + physical fix 2026-08-10 22:48:38 +02:00
Sterrezo 0319ac9a87 Added sounds to Sucker Punch 2026-08-10 22:09:17 +02:00
Sterrezo a5aea7a75f Added sounds to Leaf Storm 2026-08-10 21:30:08 +02:00
Nico MerenqandWeblate 48f4790a36 Translated using Weblate (Russian)
Currently translated at 84.2% (7981 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-08-10 17:51:58 +00:00
FrankTheFarmer 024ec427da Dewott in da changelog 2026-08-10 14:31:36 +10:00
FrankTheFarmer 935ea3ad53 Tweaked Dewott's placeholder animations a bit to prevent clipping on the skirt 2026-08-10 14:28:01 +10:00
FrankTheFarmer f270e479e6 Converted Dewott's poser to json 2026-08-10 14:27:41 +10:00
FrankTheFarmer ce5d3e75a0 Fixed some z-fighting on Dewott's hands 2026-08-10 14:27:24 +10:00
Nico MerenqandWeblate f7a5bcbd4d Translated using Weblate (Russian)
Currently translated at 84.2% (7973 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-08-10 00:26:03 +00:00
Nico MerenqandWeblate 75b7ca4786 Translated using Weblate (Russian)
Currently translated at 84.2% (7973 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-08-09 21:09:56 +00:00
Nico MerenqandWeblate dfee92bc1c Translated using Weblate (Russian)
Currently translated at 84.2% (7973 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-08-09 15:48:37 +00:00
Watuneni iteiteandWeblate 0fd4bd443e Translated using Weblate (Japanese)
Currently translated at 100.0% (9468 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-08-09 15:48:36 +00:00
Nico MerenqandWeblate e26eceeb2a Translated using Weblate (Russian)
Currently translated at 84.1% (7967 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-08-09 13:28:23 +00:00
redmageman 51be1364d3 Merge branch cobblemon:main into patch-2 2026-08-09 01:35:04 -05:00
redmageman 27706a766e Fixed a typo causing the AI to not recognize surging strikes as a multi hit move. 2026-08-09 01:32:54 -05:00
Nico MerenqandWeblate 79a4fbcb48 Translated using Weblate (Russian)
Currently translated at 84.1% (7966 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-08-08 23:34:41 +00:00
Nico MerenqandWeblate 53fdce17f8 Translated using Weblate (Russian)
Currently translated at 84.1% (7966 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-08-08 21:52:48 +00:00
Nico MerenqandWeblate b03e29c5a9 Translated using Weblate (Russian)
Currently translated at 84.1% (7966 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-08-08 20:26:10 +00:00
Polymeta 058c06f56b Merge branch 'implement_pokemon' into 'main'
some ride stats and other stuff

See merge request cable-mc/cobblemon!2341
2026-08-08 19:14:42 +00:00
Nico MerenqandWeblate 3bdf89570a Translated using Weblate (Russian)
Currently translated at 84.1% (7966 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-08-08 15:19:54 +00:00
Carmendar 28bee4189a Merge branch 'main' into implement_pokemon 2026-08-07 23:30:54 -05:00
Carmendar 3f0e21b6b3 some minor ride stat adjustments 2026-08-07 23:10:53 -05:00
Nico MerenqandWeblate 0c15a05986 Translated using Weblate (Russian)
Currently translated at 83.7% (7932 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-08-07 15:58:13 +00:00
Jerzykv 9ca2d47562 Fixed Metagross idle loop yet again 2026-08-06 18:13:30 +02:00
Nico MerenqandWeblate bd69670cd2 Translated using Weblate (Russian)
Currently translated at 83.7% (7930 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-08-06 15:57:41 +00:00
Nico MerenqandWeblate a89f07856a Translated using Weblate (Russian)
Currently translated at 83.3% (7889 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-08-06 03:47:34 +00:00
Jakotens 94f0914aa8 Poser fixes to Pidgey, Pidgeotto, Pidgeot, Aerodactyl, and Snivy. Animation and walk speed fixes to Pidgey, Pidgeotto, and Pidgeot. Added a new neck_ai bone to Pidgeotto. 2026-08-05 13:09:05 -06:00
Jakotens 60101876b5 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-08-05 11:29:44 -06:00
Nico MerenqandWeblate 8d0fc13e11 Translated using Weblate (Russian)
Currently translated at 82.1% (7780 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-08-05 15:43:57 +00:00
RedGenesectNinja b8a4dfbcb4 Adjusted Bidoof's scale again 2026-08-05 03:45:24 -05:00
RedGenesectNinja fbc5959c67 Adjusted Bidoof's scale 2026-08-05 03:14:10 -05:00
Nico MerenqandWeblate 9fc77832e6 Translated using Weblate (Russian)
Currently translated at 81.8% (7747 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-08-04 14:06:46 +00:00
Kenji_64 49f20fe1a4 Several cry/poser related fixes 2026-08-04 15:17:35 +02:00
Jerzykv 3c4288d7fc More alpha eyes 2026-08-04 14:55:54 +02:00
Jerzykv e03bb51a74 Substitute animation fix 2026-08-04 14:55:43 +02:00
Jerzykv 8545de321f Lanturn poser fixes 2026-08-04 14:55:31 +02:00
Jerzykv 5e353e9a6d Chinchou json poser conversion 2026-08-04 14:55:21 +02:00
Jerzykv c1fd257f63 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-08-04 13:56:38 +02:00
Jerzykv ea0ff3bfb9 Komala can no longer walk 2026-08-04 13:56:30 +02:00
阿鱼andWeblate fbd928eeaf Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 98.8% (9358 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-08-04 09:27:21 +00:00
Nico MerenqandWeblate 8d0beca2f6 Translated using Weblate (Russian)
Currently translated at 81.4% (7711 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-08-04 09:27:20 +00:00
Plastered_Crab 94c159b42b Meteors land where they should and test spawning minior / dropping items 2026-08-03 20:17:00 -07:00
dhi_holo 9350960457 Update remedy.json
Resolves problem of if a furnace still has data of having this recipe stored within it
2026-08-04 09:34:53 +10:00
dhi_holo 1b3daa2740 Update en_us.json
Simplified Obtain TM description
2026-08-04 09:31:37 +10:00
Nico MerenqandWeblate 784f06b9f0 Translated using Weblate (Russian)
Currently translated at 80.8% (7658 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-08-03 11:56:56 +00:00
dhi_holo 6cfe3fd39d Merge branch 'main' into data-fixings 2026-08-03 19:13:53 +10:00
Hiroku d4b6e36aab Merge branch 'gamemasters' into 'main'
Interaction Fix

See merge request cable-mc/cobblemon!2339
2026-08-03 08:49:25 +00:00
dhi_holo 338cb819af Craft to Obtain
Update Craft TM Advancement to be Obtain TM Advancement
2026-08-03 17:41:04 +10:00
dhi_holo 1a75d4b2e8 Budding Tag
Added the item form of the budding blocks tag (block form already existed)
2026-08-03 15:50:03 +10:00
dhi_holo 5c4c7b2995 Fixes
Fixed #2008 to the best of my understanding
Corrected compatibility numbers to match updated loot tables
2026-08-03 14:58:17 +10:00
Nico MerenqandWeblate 39e3ca9da8 Translated using Weblate (Russian)
Currently translated at 80.8% (7658 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-08-02 23:36:20 +00:00
dhi_holo 0fb721702d Interaction Fix
Solves interaction problem reported https://discord.com/channels/725110639554592879/1481053968716009493/1533385403170820148

Habitat Block acts like a block without interactions if right clicked by a player in survival, or without operator permissions of level 2 or higher (same as command blocks, known as Gamemaster)
With those permissions, it works as usual

Also removed Habitat Block from the Cobblemon Blocks tab, leaving it solely in the Operator Items Tab, like Command Blocks.
2026-08-02 21:08:13 +10:00
Polymeta 0e5053b787 Merge branch 'config-screen-scrolling-labels' into 'main'
Labels on the config screen now scroll instead of overflowing

See merge request cable-mc/cobblemon!2338
2026-08-01 22:09:46 +00:00
lionandPolymeta f3867e0b1d Labels on the config screen now scroll instead of overflowing 2026-08-01 22:09:46 +00:00
Nico MerenqandWeblate e40164f8cf Translated using Weblate (Russian)
Currently translated at 80.5% (7625 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-08-01 16:41:28 +00:00
arvid6andWeblate c087cab84a Translated using Weblate (Spanish)
Currently translated at 100.0% (9468 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-08-01 10:27:01 +00:00
Hiroku a4fcd1ae7b Merge branch 'transparent-occlusion' into 'main'
Fix translucent draw order

See merge request cable-mc/cobblemon!2337
2026-08-01 01:56:58 +00:00
JordanandHiroku ed9aff2448 Fix translucent draw order 2026-08-01 01:56:57 +00:00
Nick 8497a2a083 Merge branch 'trade-dupe-fix' into 'main'
Trade dupe fix

See merge request cable-mc/cobblemon!2336
2026-07-31 15:22:15 -07:00
Deltric b389a95b5e Trade dupe fix 2026-07-31 17:03:33 -05:00
Polymeta c310027ce1 Merge branch 'Habitat-Bug-Fixes' into 'main'
Fixin some things

See merge request cable-mc/cobblemon!2335
2026-07-31 20:34:24 +00:00
Slippy 5ddb01df36 More Bug Fixes 2026-07-31 14:25:33 -05:00
Nico MerenqandWeblate bbdc732bd4 Translated using Weblate (Russian)
Currently translated at 80.1% (7587 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-31 18:54:52 +00:00
Slippy 5a6f49aad5 found one more thing 2026-07-31 02:54:16 -05:00
Slippy 793efcfa3d Fixin some things 2026-07-31 02:49:24 -05:00
TeggyandWeblate e9bdd6be11 Translated using Weblate (Spanish)
Currently translated at 99.3% (9404 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-07-30 11:47:06 +00:00
Watuneni iteiteandWeblate 2330aa9512 Translated using Weblate (Japanese)
Currently translated at 100.0% (9468 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-07-30 08:51:59 +00:00
gruxor 76ec04055a Update showdown version 2026-07-29 20:19:58 -04:00
Nico MerenqandWeblate 7472d3fd0f Translated using Weblate (Russian)
Currently translated at 79.9% (7573 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-29 21:14:23 +00:00
Cobblemonovich MaxandWeblate 73c842391e Translated using Weblate (Russian)
Currently translated at 79.9% (7573 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-29 21:14:21 +00:00
Cobblemonovich MaxandWeblate ce28b96fc8 Translated using Weblate (Russian)
Currently translated at 79.9% (7573 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-29 21:14:21 +00:00
Cobblemonovich MaxandWeblate f0b2f71f17 Translated using Weblate (Russian)
Currently translated at 79.9% (7573 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-29 21:14:20 +00:00
Cobblemonovich MaxandWeblate e5187f0d0a Translated using Weblate (Russian)
Currently translated at 79.9% (7573 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-29 21:14:20 +00:00
Cobblemonovich MaxandWeblate 4610870a1d Translated using Weblate (Russian)
Currently translated at 79.9% (7573 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-29 21:14:19 +00:00
Cobblemonovich MaxandWeblate 5658c12a7d Translated using Weblate (Russian)
Currently translated at 79.9% (7573 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-29 21:14:19 +00:00
Cobblemonovich MaxandWeblate c2e6723c3a Translated using Weblate (Russian)
Currently translated at 79.9% (7573 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-29 21:14:18 +00:00
Cobblemonovich MaxandWeblate 2a31ebf292 Translated using Weblate (Russian)
Currently translated at 79.9% (7573 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-29 21:14:17 +00:00
Cobblemonovich MaxandWeblate 77f76edfcf Translated using Weblate (Russian)
Currently translated at 79.9% (7573 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-29 21:14:17 +00:00
Cobblemonovich MaxandWeblate 9684f39c65 Translated using Weblate (Russian)
Currently translated at 79.9% (7573 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-29 21:14:16 +00:00
Cobblemonovich MaxandWeblate 011995390b Translated using Weblate (Russian)
Currently translated at 79.9% (7573 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-29 21:14:16 +00:00
Cobblemonovich MaxandWeblate a2500b7b2d Translated using Weblate (Russian)
Currently translated at 79.9% (7573 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-29 21:14:15 +00:00
Cobblemonovich MaxandWeblate 811674adb9 Translated using Weblate (Russian)
Currently translated at 79.9% (7573 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-29 21:14:14 +00:00
Cobblemonovich MaxandWeblate 6ca671c877 Translated using Weblate (Russian)
Currently translated at 79.9% (7573 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-29 21:14:14 +00:00
Cobblemonovich MaxandWeblate 080ba93d57 Translated using Weblate (Russian)
Currently translated at 79.9% (7573 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-29 21:14:13 +00:00
Cobblemonovich MaxandWeblate 96d3e39211 Translated using Weblate (Russian)
Currently translated at 79.9% (7573 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-29 21:14:13 +00:00
Cobblemonovich MaxandWeblate 79c02d386b Translated using Weblate (Russian)
Currently translated at 79.9% (7573 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-29 21:14:12 +00:00
Cobblemonovich MaxandWeblate 38c0c13121 Translated using Weblate (Russian)
Currently translated at 79.9% (7573 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-29 21:14:11 +00:00
Polymeta d671c5a8b8 [skip-ci] tidy up changelog
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-07-29 11:59:10 +01:00
Polymeta 69b31dd9b9 fix up null checks to prevent crashes during ticks in BerryBlock
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-07-29 11:45:50 +01:00
ewwww f785c5405f a variety of tiny tweaks 2026-07-29 02:20:57 -04:00
Gustavo PuerariandWeblate 157e211f06 Translated using Weblate (Portuguese (Brazil))
Currently translated at 94.4% (8942 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/pt_BR/
2026-07-28 21:13:01 +00:00
Polymeta fc94bbac4c Merge branch 'dandelion-sinistertea-fix' into 'main'
Dandelion Sinister Tea Fix

See merge request cable-mc/cobblemon!2324
2026-07-28 15:53:25 +00:00
CeejayandPolymeta f7d315fa33 Dandelion Sinister Tea Fix 2026-07-28 15:53:25 +00:00
arvid6andWeblate e4654cf24d Translated using Weblate (Spanish)
Currently translated at 98.9% (9365 of 9468 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-07-28 14:27:43 +00:00
Paul 7c30fd81f3 Scraped behaviours for updated AI behaviour 2026-07-28 20:10:29 +10:00
Paul 7f1704d573 Fixed habitat spelling beheeyem wrong 2026-07-28 19:49:22 +10:00
Paul 0c2ec3ca81 Scraped habitat spawns, deduplicated fixes in changelog, update wildlife -> wildfire in habitat pool 2026-07-28 19:43:09 +10:00
Paul 33d3e156cc Merge remote-tracking branch 'origin/lang-updates' 2026-07-28 19:06:49 +10:00
arvid6andWeblate 415642a8ba Translated using Weblate (Spanish)
Currently translated at 99.6% (9432 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-07-27 18:45:01 +00:00
arvid6andWeblate dd12081cd1 Translated using Weblate (Spanish)
Currently translated at 99.5% (9425 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-07-27 11:20:52 +00:00
arvid6andWeblate b7de84acfd Translated using Weblate (Spanish)
Currently translated at 99.1% (9384 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-07-27 11:06:54 +00:00
ewwww 2dfca3ff89 a variety of tiny tweaks 2026-07-27 05:17:44 -04:00
Hiroku 8efda8c47b Merge branch 'main' into 'main'
Fix load time deadlock between ElementalType and ElementalTypes

Closes #2034

See merge request cable-mc/cobblemon!2332
2026-07-26 02:24:21 +00:00
Naitronbomb 6baf96f83b Add changelog entry for load time deadlock fix 2026-07-25 11:13:18 -07:00
Naitronbomb 009a9c3f15 Fix load time deadlock between ElementalType and ElementalTypes 2026-07-25 09:29:47 -07:00
Polymeta fc0a6c0c8b Merge branch 'gem-unbalance' into 'main'
Type Gem Loot Table Tweaks

See merge request cable-mc/cobblemon!2322
2026-07-25 16:14:44 +00:00
Hiroku 041e080cd4 Merge branch 'fix-starter-toast-dismissal' into 'main'
Fix starter toast not being dismissed when capturing a pokemon

See merge request cable-mc/cobblemon!2331
2026-07-25 03:13:11 +00:00
lionandHiroku 65bbadcfa8 Fix starter toast not being dismissed when capturing a pokemon 2026-07-25 03:13:11 +00:00
阿鱼andWeblate a70d5e200a Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 99.5% (9420 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-07-25 00:24:55 +00:00
阿鱼andWeblate 4d0ee751a3 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 97.2% (9206 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-07-24 22:42:33 +00:00
Hiroku d87438e53d I'll try changing the repo URL, that's a good trick 2026-07-24 16:57:53 +10:00
Hiroku 3efef07afa Merge remote-tracking branch 'origin/main' 2026-07-24 16:27:11 +10:00
Hiroku 9ff227fbef Fix pipelines maybe with findutils installs? 2026-07-24 16:27:01 +10:00
Hiroku 55ef5fd537 Merge branch 'fix-forms-riding-camera-offset' into 'main'
Fix riding camera offset defined on Pokemon forms not being applied

See merge request cable-mc/cobblemon!2326
2026-07-24 06:23:29 +00:00
lionandHiroku b3cdfb579c Fix riding camera offset defined on Pokemon forms not being applied 2026-07-24 06:23:28 +00:00
Hiroku 29588f1fde Merge branch 'implement_pokemon' into 'main'
the last of 1.8 (merge once ride stats are in)

See merge request cable-mc/cobblemon!2329
2026-07-24 06:19:25 +00:00
Polymeta 24c10d1a4f Merge branch 'riding-scrape-1.8' into 'implement_pokemon'
Riding scrape

See merge request cable-mc/cobblemon!2330
2026-07-24 06:10:15 +00:00
Carmendar ac0d9d4d74 Seat fixes 2026-07-24 00:53:00 -05:00
Nico MerenqandWeblate d33974b19e Translated using Weblate (Russian)
Currently translated at 78.8% (7467 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-24 05:48:29 +00:00
Carmendar 1dcdfe309e Correcting Goodra ride stats 2026-07-24 00:03:33 -05:00
Landon Wills 6ce1990145 Fix issues with scraper 2026-07-24 00:54:33 -04:00
Landon Wills 1203721889 Remove pidgeotto scrape 2026-07-24 00:54:01 -04:00
Landon Wills 333672f993 Push pidgeot riding scrape 2026-07-24 00:53:33 -04:00
Carmendar f0f9a9596f Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-07-23 23:52:58 -05:00
Carmendar 80b8aa2b8c Hisuian Goodra anim updates 2026-07-23 23:52:41 -05:00
Landon Wills b919e447d8 Add scraped files 2026-07-24 00:36:22 -04:00
Hiroku 3075b7fd73 Improved spawn scraper script: recognize more time range names, support 'storm' as a weather condition 2026-07-24 14:31:13 +10:00
Hiroku 3c80a61011 Started on implementing falling stars, they hit the ground weirdly 2026-07-24 14:25:13 +10:00
ewwww 412e3fcb8a minor poser tweaks 2026-07-24 00:23:14 -04:00
ewwww 32253fd462 starmie shiny texture fix 2026-07-23 23:17:42 -04:00
arvid6andWeblate 4de018a209 Translated using Weblate (Spanish)
Currently translated at 98.2% (9297 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-07-23 15:30:25 +00:00
ewwww 2204a4b700 substitute remodel + animations 2026-07-23 02:43:37 -04:00
arvid6andWeblate 22a27cbbd7 Translated using Weblate (Spanish)
Currently translated at 97.5% (9238 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-07-21 18:03:56 +00:00
arvid6andWeblate e202e7bf2f Translated using Weblate (Spanish)
Currently translated at 97.3% (9217 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-07-21 17:03:35 +00:00
ewwww 03104b3a3d re-deleting these 2026-07-20 04:09:26 -04:00
Polymeta 280e1ae820 Merge branch 'implement_these_dumbfuck_birds' into 'implement_pokemon'
Implement these dumbfuck birds

See merge request cable-mc/cobblemon!2328
2026-07-20 08:02:19 +00:00
Carmendar ee5653bfba Merge branch 'implement_pokemon' into implement_these_dumbfuck_birds
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/repository/VaryingModelRepository.kt
2026-07-20 02:49:35 -05:00
Carmendar a9c3c14367 Finishing touches to Pidgeot 2026-07-20 02:44:46 -05:00
Paul 9cdc979067 Fixed /giveallpokemon not respecting the min and max options 2026-07-20 13:13:27 +10:00
Carmendar 4b45fdde3f Pidgeotto fixed, and work done to Pidgeot. 2026-07-19 14:55:31 -05:00
Nico MerenqandWeblate 5bf7cc6aef Translated using Weblate (Russian)
Currently translated at 78.7% (7454 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-19 12:01:56 +00:00
Polymeta 7cf1f5a4e5 Merge branch 'habitats-crash-fix' into 'main'
Fixed some crashes that happened due to invalid processor referencing and structure

See merge request cable-mc/cobblemon!2327
2026-07-18 22:12:10 +00:00
Faintful 0fe2b0f60c Fixed some crashes that happened due to invalid processor referencing and structure 2026-07-18 22:22:56 +02:00
阿鱼andWeblate 667376226b Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 96.4% (9134 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-07-18 14:24:13 +00:00
Nico MerenqandWeblate 5506e076f6 Translated using Weblate (Russian)
Currently translated at 78.5% (7437 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-18 14:24:11 +00:00
Hiroku f8799efd9d Merge branch 'Habitat-Blocks-pt2' into 'main'
Habitat blocks pt2

See merge request cable-mc/cobblemon!2325
2026-07-18 01:47:16 +00:00
Slippy bb642fbf29 ALL HABITATS HAVE HABITAT BLOCKS!!! 2026-07-17 16:23:59 -05:00
Nico MerenqandWeblate e19aa02283 Translated using Weblate (Russian)
Currently translated at 78.5% (7433 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-17 17:47:39 +00:00
Slippy e65010e289 Six more habitats with habitat blocks 2026-07-16 01:05:07 -05:00
Nico MerenqandWeblate 4b4a715868 Translated using Weblate (Russian)
Currently translated at 78.4% (7429 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-15 20:06:53 +00:00
Slippy 26ec5717f1 11 more habitats with habitat blocks 2026-07-15 11:55:41 -05:00
Nico MerenqandWeblate 446d4771d8 Translated using Weblate (Russian)
Currently translated at 78.4% (7429 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-15 13:44:03 +00:00
Nico MerenqandWeblate 9d817b3f77 Translated using Weblate (Russian)
Currently translated at 78.4% (7427 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-15 03:10:05 +00:00
Zack the GhastandWeblate 21213c4fce Translated using Weblate (Portuguese (Portugal))
Currently translated at 66.3% (6281 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2026-07-14 07:36:57 +00:00
João Piologo e6c54575ee 🐛 fix: battle faint enhancement
Makes the right-top opponent's pokeball only go dark after the fainting animation, preventing the user to know beforehand if it was a KO.
2026-07-13 20:30:09 -03:00
Watuneni iteiteandWeblate cebf105dd5 Translated using Weblate (Japanese)
Currently translated at 100.0% (9467 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-07-13 07:43:46 +00:00
Jakotens 123d3291c6 Fixed Gogoat's battle_idle animation being absent from its poser which caused certain battle animations to break. 2026-07-12 16:21:57 -06:00
dhi_holo 935a39c578 Type Gems now place Stage 1 (Medium) rather than Stage 0 (Small) 2026-07-12 17:56:24 +10:00
dhi_holo d53e67b55c New Loot Tables 2026-07-12 17:31:50 +10:00
Zack the GhastandWeblate ec0766ddcd Translated using Weblate (Portuguese (Portugal))
Currently translated at 65.9% (6244 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2026-07-11 15:56:17 +00:00
Nico MerenqandWeblate 917577f7a8 Translated using Weblate (Russian)
Currently translated at 78.3% (7418 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-11 03:06:15 +00:00
Nico MerenqandWeblate 01de2f71af Translated using Weblate (Russian)
Currently translated at 78.2% (7412 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-10 14:42:24 +00:00
Nico MerenqandWeblate 0779251581 Translated using Weblate (Russian)
Currently translated at 76.5% (7244 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-09 23:17:58 +00:00
Nico MerenqandWeblate 14d4c6c2e4 Translated using Weblate (Russian)
Currently translated at 76.2% (7222 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-09 21:50:35 +00:00
Nico MerenqandWeblate de82d36da3 Translated using Weblate (Russian)
Currently translated at 75.2% (7124 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-09 17:04:40 +00:00
Nico MerenqandWeblate ae2704e218 Translated using Weblate (Russian)
Currently translated at 75.0% (7104 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-09 13:30:48 +00:00
Nico MerenqandWeblate a235c1286c Translated using Weblate (Russian)
Currently translated at 74.6% (7063 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-07-09 13:20:24 +00:00
Polymeta ce584bd403 Merge branch 'tm-unlock-utilities' into 'main'
Move Data Monitor Learn to Helper, Add Utility and MoLang Functions for Unlocking TMs + Impossible Obtain Method

See merge request cable-mc/cobblemon!2321
2026-07-09 08:09:45 +00:00
Konoha DandPolymeta 1c25861e04 Move Data Monitor Learn to Helper, Add Utility and MoLang Functions for Unlocking TMs + Impossible Obtain Method 2026-07-09 08:09:45 +00:00
Watuneni iteiteandWeblate 006cc21ebb Translated using Weblate (Japanese)
Currently translated at 100.0% (9467 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-07-08 03:33:00 +00:00
Watuneni iteiteandWeblate 5ab9072f94 Translated using Weblate (Japanese)
Currently translated at 99.6% (9431 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-07-07 09:36:33 +00:00
Watuneni iteiteandWeblate f7023b9737 Translated using Weblate (Japanese)
Currently translated at 99.4% (9417 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-07-06 07:04:09 +00:00
Paul 987a7d0ccc Fixed entity attributes not updating when a pokemon levels up 2026-07-04 11:56:47 +10:00
ewwww d49c0a0ec6 changelog 2026-07-03 17:26:41 -04:00
ewwww d4a910bf26 added corsola's physical attack + updated textures 2026-07-03 17:23:13 -04:00
Kenji_64 25b75231ad Implemented Cottonee and Whimsicott animations! 2026-07-03 17:22:13 +02:00
RedGenesectNinja 0db1f372e3 Adjusted swimming heights for both forms of Grimer and Muk 2026-07-03 03:58:57 -05:00
RedGenesectNinja 1ddffc1c0d Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-07-03 01:19:34 -05:00
RedGenesectNinja 5fe04aad83 Implemented Grimer animations 2026-07-02 20:26:49 -05:00
Polymeta 03b74d4b72 Merge branch 'implement_pokemon' into 'main'
various asset updates

See merge request cable-mc/cobblemon!2319
2026-07-02 06:16:52 +00:00
ewwww a5f150af9a poser tweaks 2026-07-02 00:56:11 -04:00
ewwww b3b5e47cc9 tinyprogress 2026-07-02 00:06:01 -04:00
Watuneni iteiteandWeblate 1d0aa06dc4 Translated using Weblate (Japanese)
Currently translated at 99.1% (9389 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-07-01 11:24:54 +00:00
ewwww 1e5c0f5137 added chansey + blissey animations 2026-07-01 00:26:07 -04:00
ewwww 7bafd53f58 minor dugtrio tweaks 2026-06-30 07:36:16 -04:00
ewwww bdbdf238f5 dugtrio physical attack + diglett tweaks 2026-06-30 07:06:24 -04:00
ewwww 6cd71050df added golett remodel 2026-06-30 04:11:09 -04:00
Polymeta 2047f4f461 Merge branch 'my-stupid-tmm-bug' into 'main'
Fix for TMM putting burn screen tab when no tm is selected

See merge request cable-mc/cobblemon!2320
2026-06-30 08:08:37 +00:00
Gertimoshka 0aaed4bb84 Fixed issue where it would still put you on burn screen even with no tm selected 2026-06-30 06:03:56 +03:00
ewwww 3779b4c0b3 updated assets 2026-06-29 17:42:21 -04:00
ewwww 74aaa16a0d physical attack animation for graveler + eye locators for alolan graveler 2026-06-29 07:01:18 -04:00
ewwww 332ef2fddd diglett physical attack 2026-06-29 06:30:21 -04:00
ewwww f29c5c5f6d new animations for sandshrew 2026-06-29 05:24:17 -04:00
ewwww c46375aafb new animations for nidoran male, female, and nidorina 2026-06-29 05:23:54 -04:00
Watuneni iteiteandWeblate a5e16ccf65 Translated using Weblate (Japanese)
Currently translated at 98.7% (9349 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-06-29 08:24:38 +00:00
ewwww 4cc83560e8 kakuna animations 2026-06-29 01:53:20 -04:00
ewwww a7f4ef8449 mountable duraludon and archaludon (stats not final) 2026-06-29 01:25:42 -04:00
ewwww edaafa2263 work in progress i'm sleep deprived 2026-06-28 08:28:18 -04:00
ewwww 0162bdae5f added new animations for poliwhirl 2026-06-27 18:54:44 -04:00
阿鱼andWeblate da82eb8d61 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 94.9% (8993 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-06-27 11:50:29 +00:00
ewwww 34fc434883 implemented new battle animations for happiny 2026-06-27 05:51:30 -04:00
ewwww 74949a02de yungoos poser tweak 2026-06-27 05:51:04 -04:00
ewwww 1617c1b087 added zubat physical animation 2026-06-27 04:57:03 -04:00
ewwww 4b1c180538 added poliwrath walk animation 2026-06-27 04:56:50 -04:00
ewwww cba6f51706 implemented new yungoos & gumshoos animations 2026-06-27 03:46:00 -04:00
ewwww 9b9fbedaf7 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-06-26 19:27:45 -04:00
ewwww 8c3acd9a0e 'angery' typo 2026-06-26 18:23:48 -04:00
CaptainLennie 4ab52cac0e Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-06-26 19:28:24 +02:00
CaptainLennie 68805a5dd1 Fixed tentacool using ground animations while swimming on the surface (and updated to JSON poser) 2026-06-26 19:28:10 +02:00
Jerzykv 5a0b4e8f99 Alolan Vulpix portrait tweak 2026-06-26 14:03:11 +02:00
Jerzykv 3e9d3a307f Implemented animations for Rellor and Rabsca 2026-06-26 13:59:14 +02:00
ewwww 7c4d3ecb71 tweaked aerodactyl's walkspeed 2026-06-26 07:30:01 -04:00
ewwww e91fd5596a implemented new vulpix animations 2026-06-26 07:29:29 -04:00
ewwww f9d218adf8 forgot something 2026-06-25 05:46:04 -04:00
ewwww 05d70b28e7 monitor screen updates 2026-06-25 05:41:30 -04:00
ewwww 6755462eb0 added new animations for noctowl 2026-06-25 05:39:58 -04:00
ewwww 7ef3facac8 added new animations for drowzee and hypno 2026-06-25 04:07:39 -04:00
Watuneni iteiteandWeblate 2dc93f3336 Translated using Weblate (Japanese)
Currently translated at 96.7% (9158 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-06-24 10:16:38 +00:00
ewwww b7c16324bd misc texture fixes 2026-06-24 04:05:06 -04:00
阿鱼andWeblate e8864418d4 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 94.9% (8992 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-06-23 22:49:29 +00:00
Polymeta 7ccd0bbe11 Merge branch 'multiplayer_mount_sync' into 'main'
Multiplayer mount sync

Closes #2023

See merge request cable-mc/cobblemon!2315
2026-06-22 12:23:42 +00:00
ewwww e1cc9c7aba added new animations for Togetic 2026-06-22 02:45:55 -04:00
ewwww 79e821a438 added new animations for Staryu and Starmie 2026-06-22 01:08:45 -04:00
Polymeta d289880cc8 Merge branch 'tmm-fixes-n-changes' into 'main'
TMM Fixes and Changes

See merge request cable-mc/cobblemon!2316
2026-06-20 19:43:21 +00:00
ewwww 19dd57feed form name corrections 2026-06-20 13:16:12 -04:00
Paul 6609c7d038 Added held_item_visible pokemonproperty 2026-06-20 22:29:14 +10:00
Paul 88afe05c16 Fixed NPE's when block entities aren't attached to berry blocks at time of ticking, closes #2020 2026-06-20 18:01:52 +10:00
ewwww e739b57a08 grammar. 2026-06-20 01:22:04 -04:00
Gertimoshka 99aa25e1bf I forgor 2026-06-19 17:50:52 +03:00
Gertimoshka 76faa50035 Fixes, tmm multiplayer syncing and cleanup 2026-06-19 17:42:50 +03:00
aeroeng15 8ce989cb6b Changelog 2026-06-18 19:03:45 -07:00
aeroeng15 02030950aa Force passenger updates 2026-06-18 18:53:36 -07:00
阿鱼andWeblate 82b418d54d Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 94.9% (8993 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-06-18 21:12:39 +00:00
Polymeta 6148b58fdc Merge branch 'qa-fixes-84' into 'main'
Qa fixes 84

See merge request cable-mc/cobblemon!2314
2026-06-17 11:17:33 +00:00
ewwww 2044762b72 various poser alterations that accidentally got reverted 2026-06-17 05:33:34 -04:00
ewwww 77117e83c7 implemented (non-riding) animations & corrected scales for Duraludon & Archaludon 2026-06-17 05:33:21 -04:00
Carmendar 55f4e6e738 Goodra ride anim adjustments 2026-06-17 00:57:18 -05:00
Faintful 1e4936aeac Add changelog 2026-06-16 20:40:08 +02:00
Faintful 15ea72e777 Fixed Smoked Tail Curry returning a wooden bowl when consumed 2026-06-16 20:38:56 +02:00
Polymeta 3c3d4e4552 [skip-ci] changelogging 2026-06-16 17:43:50 +00:00
Polymeta 796e416578 Merge branch 'fix-auto-third-person-mod-compat' into 'main'
Add check for remember ride camera config when dismounting

Closes #2012

See merge request cable-mc/cobblemon!2308
2026-06-16 17:37:08 +00:00
lionandPolymeta 7608dfde94 Add check for remember ride camera config when dismounting 2026-06-16 17:37:07 +00:00
Polymeta b2b871617a Merge branch 'qa-fixes-85' into 'main'
Qa fixes 85

See merge request cable-mc/cobblemon!2313
2026-06-16 17:19:16 +00:00
Faintful bc4c8d13b7 Fix Panpour consuming a held item when evolving with a Water Stone 2026-06-16 17:07:36 +02:00
Faintful 0f9c51f7dd Fixed full heal stackable block not dropping full heals 2026-06-16 16:05:06 +02:00
Polymeta 2c2623e0f9 Merge branch 'summary-and-pokedex-profile-translations' into 'main'
Add profile translation and scale properties specific to the Pokédex and summary

See merge request cable-mc/cobblemon!2309
2026-06-16 13:44:41 +00:00
lionandPolymeta 8ec85e7d2f Add profile translation and scale properties specific to the Pokédex and summary 2026-06-16 13:44:41 +00:00
Paul d5a08314eb Merge remote-tracking branch 'origin/lang-updates' 2026-06-16 20:36:49 +10:00
Paul 062c32be2a Correct the parsing of phases in habitat GUI (they aren't moon phases) 2026-06-16 20:33:54 +10:00
阿鱼andWeblate 09590b49cd Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 94.9% (8988 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-06-16 10:14:47 +00:00
Polymeta 420cbd2c99 Merge branch 'fix-pvn-fainted-party-battle' into 'main'
Fix PvN battle start with fainted parties

See merge request cable-mc/cobblemon!2311
2026-06-16 09:45:36 +00:00
Jongho Hong 0380013f37 Add changelog entry 2026-06-16 18:41:43 +09:00
阿鱼andWeblate 6c1fcc103a Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 94.7% (8973 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-06-16 09:14:38 +00:00
阿鱼andWeblate 59b01c3988 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 94.7% (8971 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-06-16 08:58:39 +00:00
阿鱼andWeblate 678fa2d0ce Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 94.6% (8961 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-06-16 08:28:14 +00:00
Jongho Hong 94abd79923 Fix PvN battle start with fainted parties 2026-06-16 17:22:50 +09:00
阿鱼andWeblate 0a1931aaae Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 94.6% (8958 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-06-16 08:21:26 +00:00
阿鱼andWeblate 85d76dcc29 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 94.5% (8952 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-06-16 08:07:26 +00:00
阿鱼andWeblate bd0a9ef654 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 94.4% (8944 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-06-16 07:27:07 +00:00
阿鱼andWeblate 7a702e276c Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 94.2% (8926 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-06-16 05:41:55 +00:00
阿鱼andWeblate 3f9a2825cb Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 94.2% (8921 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-06-16 05:30:18 +00:00
阿鱼andWeblate d1462bc27b Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 93.9% (8892 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-06-16 05:05:07 +00:00
阿鱼andWeblate 211b14dbd0 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 93.5% (8852 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-06-16 04:59:55 +00:00
阿鱼andWeblate 47e83003c3 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 93.4% (8850 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-06-16 04:59:34 +00:00
阿鱼andWeblate 59134c4212 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 93.4% (8849 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-06-16 04:32:46 +00:00
阿鱼andWeblate 44b434562c Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 93.4% (8847 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-06-15 20:32:05 +00:00
Faintful 7af9b0854a Fixed Status Cure items not returning a bottle after use 2026-06-15 17:55:22 +02:00
Hiroku 344b0750f9 Merge branch 'implement_pokemon' into 'main'
more QA fixes

See merge request cable-mc/cobblemon!2310
2026-06-15 04:26:30 +00:00
ewwww 10d9d97c11 restored broken or missing seat locators for Ariados, Bastiodon, Dhelmise, Dondozo, Drifblim, Girafarig, and Klinklang 2026-06-15 00:06:56 -04:00
Paul 94bcfba076 Phase count should be derived, not set 2026-06-14 22:10:46 +10:00
RedGenesectNinja 69a75ffcd4 Fixed broken Alola bias Exeggcute textures 2026-06-13 13:25:57 -05:00
阿鱼andWeblate cc1317dd06 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 93.4% (8845 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-06-13 07:46:03 +00:00
Polymeta e15ac73b1b Merge branch 'fix-enchanted-fossil-items' into 'main'
Fix crash when using enchanted items in the ressurection machine

Closes #1973

See merge request cable-mc/cobblemon!2307
2026-06-11 21:48:56 +00:00
lionandPolymeta 6c6576c117 Fix crash when using enchanted items in the ressurection machine 2026-06-11 21:48:55 +00:00
Polymeta ebdfa7edc0 Merge branch 'fix-pokemon-properties-nickname-parsing-to-json' into 'main'
Add pokémon nickname when serializing and deserializing to json on pokémon properties

Closes #1981

See merge request cable-mc/cobblemon!2306
2026-06-11 13:48:21 +00:00
lionandPolymeta 297e8eae77 Add pokémon nickname when serializing and deserializing to json on pokémon properties 2026-06-11 13:48:21 +00:00
Polymeta ea8179018d Merge branch 'modded-discs-compat' into 'main'
Add modded discs compatibility with disc shelves

See merge request cable-mc/cobblemon!2305
2026-06-11 08:20:18 +00:00
lionandPolymeta 14fccfae1d Add modded discs compatibility with disc shelves 2026-06-11 08:20:18 +00:00
Polymeta 26386638ad Merge branch 'PathingThresholds' into 'main'
Fixed wider pokemon being completely unable to hop up ledges

See merge request cable-mc/cobblemon!2304
2026-06-10 08:21:01 +00:00
JazzMcNade 5d1d99b64a Rolled back jump height change 2026-06-09 23:58:44 -05:00
RedGenesectNinja a3c5a6c377 Implemented Yamask fixes 2026-06-09 21:58:33 -05:00
Polymeta eb2a5837eb Merge branch 'oops-unsynced-tms' into 'main'
Sync TM Data

See merge request cable-mc/cobblemon!2303
2026-06-09 18:35:59 +00:00
Gertimoshka 93298d9a87 Sync TM Data 2026-06-09 20:48:35 +03:00
Watuneni iteiteandWeblate abb5346305 Translated using Weblate (Japanese)
Currently translated at 96.5% (9139 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-06-09 15:19:33 +00:00
Watuneni iteiteandWeblate 9466f66721 Translated using Weblate (Japanese)
Currently translated at 96.4% (9129 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-06-09 14:13:54 +00:00
Watuneni iteiteandWeblate 716ea55bc7 Translated using Weblate (Japanese)
Currently translated at 96.2% (9115 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-06-09 14:01:57 +00:00
Polymeta 4d1f46c37c remove unused config values
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-06-08 14:55:41 +01:00
ManriqueandWeblate 15f0ad1821 Translated using Weblate (Spanish)
Currently translated at 97.2% (9211 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-06-08 08:42:34 +00:00
Polymeta a2101b5759 whoops
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-06-08 09:29:49 +01:00
Polymeta 187a190ec6 fix size categories not working properly on servers with non-default scales
Closes #2022

Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-06-08 09:16:57 +01:00
JazzMcNade 0d433ff394 Fixed wider pokemon being completely unable to hop up ledges
Fixed pokemon not being able to hop up snow
2026-06-07 23:26:31 -05:00
Watuneni iteiteandWeblate a16975c750 Translated using Weblate (Japanese)
Currently translated at 96.1% (9105 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-06-07 15:19:10 +00:00
Watuneni iteiteandWeblate c0e6d87c40 Translated using Weblate (Japanese)
Currently translated at 96.1% (9105 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-06-07 12:39:38 +00:00
Watuneni iteiteandWeblate 91078b917e Translated using Weblate (Japanese)
Currently translated at 96.0% (9090 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-06-07 12:02:11 +00:00
ewwww b7e27fa52a Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-06-05 21:10:08 -04:00
ewwww dc87ffd2a8 luvdisc faint fix 2026-06-05 21:09:49 -04:00
Polymeta d873399d05 Merge branch 'implement_pokemon' into 'main'
for merging when the last of the QA fixes are finished

See merge request cable-mc/cobblemon!2302
2026-06-05 08:28:27 +00:00
Jerzykv a7aab51e0d These didn't get in for some reason 2026-06-05 08:24:37 +02:00
ewwww e8e2057558 popplio t-pose fix 2026-06-04 23:51:06 -04:00
Polymeta 56b5dba911 Merge branch 'stoobid-aram' into 'main'
Stoobid Aram, Actually Fixed the Processor this time

See merge request cable-mc/cobblemon!2301
2026-06-04 22:51:01 +00:00
Aram f0a66458e5 Stoobid Aram, Actually Fixed the Processor this time 2026-06-05 00:31:55 +02:00
ewwww dba6a38f9e Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-06-04 13:07:42 -04:00
ewwww 342c0041e9 UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUGH 2026-06-04 13:07:27 -04:00
阿鱼andWeblate cbeda361d4 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 93.3% (8835 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-06-04 15:12:59 +00:00
Hiroku a811b3347c Merge branch 'implement_pokemon' into 'main'
Implement pokemon

See merge request cable-mc/cobblemon!2300
2026-06-04 10:53:32 +00:00
Polymeta 80e593f5c6 Merge branch 'fix-/locate-lie' into 'main'
Fixed the HeightRangeProcessor

See merge request cable-mc/cobblemon!2297
2026-06-04 10:43:12 +00:00
ewwww ff617348fe implemented arbok remodel 2026-06-04 06:00:31 -04:00
ewwww 1adecbf5f6 fixed incorrect "Head" naming for gallade 2026-06-04 04:37:32 -04:00
ewwww 29dbb13a0e cramorant poser tweaks 2026-06-04 04:11:06 -04:00
ewwww 67e03ecebd ditto poser tweaks 2026-06-04 04:10:57 -04:00
ewwww 215f48bdc7 musharna placeholder sleep 2026-06-04 04:03:05 -04:00
ewwww d4beab1bfb jynx poser tweaks 2026-06-04 04:02:56 -04:00
ewwww f2c21ec902 smoochum remodel 2026-06-04 04:02:46 -04:00
ewwww c9f3912677 updated omanyte and omastar's posers 2026-06-03 22:39:36 -04:00
ewwww c21b7d1a7d marked cherubi line as implemented + tweaked changelog 2026-06-03 19:59:45 -04:00
ewwww 26068694ad grafaiai animations 2026-06-03 07:15:38 -04:00
Polymeta c86d8f0696 Merge branch '1.8-move-effects-funny' into 'main'
1.8-move-effects-funny

See merge request cable-mc/cobblemon!2299
2026-06-03 09:55:40 +00:00
ewwww 16468059e4 oopsies forgot a few 2026-06-03 03:57:22 -04:00
ewwww 54dbe4769e changelog 2026-06-03 03:51:33 -04:00
321RetroGamer 82019c3722 1.8 MOVE STUFF (real)
NEW MOVES:
- Blizzard
- Disarming Voice
- Dragon Breath
- Dragon Rage
- Drain Punch
- Fairy Wind
- Fire Fang
- Gust
- Horn Drill (effect by Gesteeyy)
- Hydro Pump
- Ice Fang
- Leafage
- Leaf Storm
- Light Screen
- Night Shade
- Play Rough
- Reflect
- Rock Slide
- Sonic Boom
- Sucker Punch
- Supersonic
- Thunder Fang

ANIMATION .JSONS:
- Play Rough

TWEAKS:
- Confusion
- Eruption
- Flamethrower
- Hypnosis
- Poison Fang
- Protect
- Psychic
- Sing
- Spore
- String Shot
- Tail Whip
- Thunder Wave
- Water Pulse

FIXES:
- Dragon Claw (Action Effect Tweaks)
- Fire Punch (Action Effect Tweaks)
- Flame Burst (Delta Fixes)
- Flame Charge (Action Effect Tweaks)
- Frost Breath (Glow Fixes)
- Ice Punch (Action Effect Tweaks, Glow Fixes)
- Icy Wind (Action Effect Tweaks, Glow Fixes)
- Mud Sport (Glow Fixes)
- Quick Attack (Glow Fixes)
- Rage Powder (Action Effect Tweaks, Fixed Missing Texture Directory)
- Razor Leaf (Glow Fixes)
- Sand Attack (Action Effect Tweaks)
- Scratch (Glow Fixes)
- Shadow Claw (Action Effect Tweaks)
- Tackle (Glow Fixes)
- Thunder (Fixed Missing Texture Directory)
- Thunder Punch (Action Effect Tweaks)
- Water Gun (Glow Fixes)
- Water Sport (Glow Fixes)

MISCELLANEOUS:
- Shellder Line Anim/Poser Edits (Protect Signature Showcase)
2026-06-03 01:40:41 -05:00
阿鱼andWeblate 079a2fb50d Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 93.3% (8835 of 9467 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-06-03 06:11:14 +00:00
ewwww 78f0951345 (proper) Ditto remodel 2026-06-03 00:27:04 -04:00
Paul 96aaab29e2 Changelog for lang 2026-06-03 11:55:47 +10:00
Paul b1def44953 Merge remote-tracking branch 'origin/implement_pokemon' 2026-06-03 11:55:30 +10:00
Paul e2e38add1d Merge remote-tracking branch 'origin/lang-updates' 2026-06-03 11:54:38 +10:00
ewwww 1db0b0709a animation tweaks 2026-06-02 05:41:51 -04:00
ewwww c3ce3db387 implemented Turtonator remodel 2026-06-02 05:41:38 -04:00
ewwww b5e6fd3816 e 2026-06-01 23:20:38 -04:00
MeAlam1 5b5dcddb25 Fixed the HeightRangeProcessor 2026-06-01 20:16:27 +02:00
ewwww 0158524bff oops. 2026-06-01 07:11:35 -04:00
ewwww 2611e311a7 luvdisc poser conversion + new animations 2026-06-01 06:47:42 -04:00
ewwww 17061691a1 corrected Feraligatr profile scale 2026-06-01 05:16:36 -04:00
ewwww 9c6d7ddfef implemented Tyranitar remodel 2026-06-01 05:16:23 -04:00
Polymeta e8119198c1 Merge branch 'type_alpha_drops' into 'main'
Alphas also drop items based on the pokemon types

See merge request cable-mc/cobblemon!2295
2026-05-31 08:58:53 +00:00
Hiroku 55c0e3e8a0 Merge branch 'IceTypeSnowImmunity' into 'main'
Added powdered snow immunity to ice types

Closes #1809

See merge request cable-mc/cobblemon!2293
2026-05-31 02:07:20 +00:00
PaulFreck 70443bc7b0 added optional tag for powdered snow immunity 2026-05-30 08:26:41 -05:00
Hiroku acb166b5b9 Merge branch 'implement_pokemon' into 'main'
Implement merge to main

See merge request cable-mc/cobblemon!2296
2026-05-30 06:17:46 +00:00
Carmendar 462c361998 No more eepy Seedot line in party/PC 2026-05-30 00:51:58 -05:00
Carmendar 76c90fb1ae Merge branch 'main' into implement_pokemon 2026-05-30 00:49:53 -05:00
Carmendar 27e11dee87 Gen3 alpha eyes 2026-05-30 00:40:24 -05:00
Paul d7c8b8dd62 Alphas also drop items based on the pokemon types 2026-05-30 14:15:00 +10:00
RedGenesectNinja eb12ccf6d0 Updated changelog 2026-05-29 18:52:22 -05:00
mystixmew e7b58edcd1 fix: resolve Linux compatibility issue in Getch class
msvcrt platform detection was broken - the try/except ImportError in
__init__ never triggered since the import attempt already happened at
module level. Replaced with a _has_msvcrt boolean flag set at import
time to correctly route Linux to _getch_unix. Also fixed _getch_unix
to return ch.encode() so key comparisons behave consistently across
platforms.
2026-05-28 17:43:18 -04:00
Polymeta ca5c2ba381 Merge branch 'shiny-volume-tweaks' into 'main'
Update sounds.json and changelog documentation

See merge request cable-mc/cobblemon!2294
2026-05-28 14:15:12 +00:00
SterrezoandPolymeta 8aa538d8e7 Update sounds.json and changelog documentation 2026-05-28 14:15:11 +00:00
Polymeta 1bd33a0635 Merge branch 'main' into implement_pokemon
# Conflicts:
#	common/src/main/resources/assets/cobblemon/lang/en_us.json
2026-05-28 15:13:23 +01:00
Polymeta ff9ca4ee09 Merge branch 'implementation-vibe-check' into 'main'
Added a script to automatically find implementation issues

See merge request cable-mc/cobblemon!2290
2026-05-28 14:10:57 +00:00
ewwww 1def8fcd1a oops 2026-05-28 06:14:50 -04:00
ewwww d53603ec49 futureproofing 2026-05-28 05:50:28 -04:00
Sterrezo 5362995fbb Added checks for wing flap sound keyframes 2026-05-27 14:23:26 +02:00
PaulFreck e2d0b12f94 small change to how freezing works 2026-05-26 20:01:58 -05:00
PaulFreck b32c2cb81c added powdered snow immunity to ice types 2026-05-26 19:48:29 -05:00
Sterrezo 98a7240191 Lenient spellcheck 2026-05-27 02:08:11 +02:00
Sterrezo 84adc0e1cb Minor oopsie 2026-05-27 01:55:55 +02:00
Sterrezo 3ab2dfa0b3 Anti-optimizations 2026-05-27 01:47:28 +02:00
Polymeta d67669a278 Merge branch 'smol-structure-crash-fix' into 'main'
Structure crash fix

See merge request cable-mc/cobblemon!2292
2026-05-26 22:12:37 +00:00
Sterrezo 2a85dee373 Fixed structure file names 2026-05-26 22:23:15 +02:00
Sterrezo 86e8238230 Grammar 2026-05-26 20:16:31 +02:00
Sterrezo bdc7b5a504 Small correction 2026-05-26 20:10:07 +02:00
Sterrezo f3c165d282 Added geo and detailed locator parity checks 2026-05-26 20:04:04 +02:00
Sterrezo 9caf8376bc Made this thing easier to use 2026-05-26 17:53:17 +02:00
Polymeta 8661f7a01e Merge branch 'z-fighting-b-gone' into 'main'
Pokedex Z-Fighting Begone

See merge request cable-mc/cobblemon!2289
2026-05-26 15:17:55 +00:00
Polymeta e933a03e92 Merge branch 'Habitat-Blocks-pt2' into 'main'
drifting icebergs, volcanic plumes, sketal falls, strange fungal dwellings,...

See merge request cable-mc/cobblemon!2291
2026-05-26 15:12:31 +00:00
Watuneni iteiteandWeblate 982fc054a9 Translated using Weblate (Japanese)
Currently translated at 98.5% (9183 of 9320 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-05-26 07:06:18 +00:00
Watuneni iteiteandWeblate cb73bfb31b Translated using Weblate (Japanese)
Currently translated at 98.3% (9170 of 9320 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-05-26 06:47:54 +00:00
Slippy 5b84c2c8a0 drifting icebergs, volcanic plumes, sketal falls, strange fungal dwellings, and lush cenotes have habitat blocks. Adjusted the rates of the nether habitats once again. 2026-05-25 12:28:04 -05:00
Sterrezo 9f998d92e6 Added a script to automatically find issues 2026-05-25 17:55:36 +02:00
Test b835bfe596 dex geo, deepslate texture 2026-05-25 03:10:46 -04:00
dhi_holo 757e3d48cb Update pokedex_red_model.json 2026-05-25 05:41:25 +10:00
dhi_holo 2fbf77dd65 Update pokedex_red_model.json 2026-05-25 05:22:41 +10:00
dhi_holo bd451bf951 Pokedex Z-Fighting Begone 2026-05-25 04:20:44 +10:00
geumuandWeblate d88a9dda92 Translated using Weblate (Korean)
Currently translated at 100.0% (9320 of 9320 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ko/
2026-05-24 14:05:54 +00:00
Watuneni iteiteandWeblate 86c2282cf8 Translated using Weblate (Japanese)
Currently translated at 97.8% (9120 of 9320 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-05-24 09:11:09 +00:00
Watuneni iteiteandWeblate 6ac8523cf5 Translated using Weblate (Japanese)
Currently translated at 97.2% (9060 of 9320 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-05-23 17:05:17 +00:00
ManriqueandWeblate acb0aa854e Translated using Weblate (Spanish)
Currently translated at 99.8% (9310 of 9320 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-05-23 17:05:16 +00:00
ewwww fdafb1e64a updated en_us 2026-05-22 21:35:48 -04:00
RedGenesectNinja bd525c8740 Implemented the rest of the Gen 4 alpha eyes 2026-05-21 04:30:28 -05:00
ewwww 209dee1d25 . 2026-05-21 03:48:37 -04:00
ewwww 1eedc4862a Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-05-21 00:17:40 -04:00
ewwww b1fbf9ca02 PYRO BALLING 2026-05-21 00:17:28 -04:00
ewwww 5fda445274 cinderace tweak 2026-05-20 23:08:09 -04:00
Jerzykv ce0265e954 Goodra animation tweaks 2026-05-20 22:38:56 +02:00
ManriqueandWeblate da1654d9aa Translated using Weblate (Spanish)
Currently translated at 99.7% (9293 of 9320 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-05-20 16:10:05 +00:00
ManriqueandWeblate 27132031cd Translated using Weblate (Spanish)
Currently translated at 99.6% (9292 of 9320 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-05-20 16:09:24 +00:00
GenotypeXD cdc4a571b6 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-05-20 06:26:07 -05:00
GenotypeXD e8bc138e5d Gen 6 Alphas 2026-05-20 06:25:27 -05:00
ManriqueandWeblate 038d239f95 Translated using Weblate (Spanish)
Currently translated at 98.7% (9208 of 9320 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-05-20 11:20:21 +00:00
Watuneni iteiteandWeblate 9b2a1dc065 Translated using Weblate (Japanese)
Currently translated at 96.9% (9035 of 9320 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-05-20 10:26:50 +00:00
ManriqueandWeblate 3c222a3349 Translated using Weblate (Spanish)
Currently translated at 97.1% (9052 of 9320 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-05-20 10:26:49 +00:00
RedGenesectNinja a1035608c9 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-05-20 04:06:02 -05:00
RedGenesectNinja 2345565e38 Implemented more Gen 4 alpha eyes and a remodel for Chatot 2026-05-20 04:05:16 -05:00
ewwww 620002c165 chopped off gastly's tongue & poked zubat's eyes out 2026-05-20 01:45:14 -04:00
Sterrezo d89224a684 FIxed incorrectly named sound events for Galarian Ponyta and Rapidash cries 2026-05-19 14:15:05 +02:00
ewwww 856eb2a1bd pyro balling 2026-05-18 06:33:20 -04:00
TeggyandWeblate 136d7c08b8 Translated using Weblate (Spanish)
Currently translated at 94.2% (8786 of 9320 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-05-17 16:28:35 +00:00
Jerzykv ddc03766bb Implemented alpha eyes/locators of Galar and Hisui 2026-05-17 17:08:25 +02:00
ewwww 81dccf9c07 various fixes 2026-05-17 08:05:04 -04:00
Watuneni iteiteandWeblate ea93b8c1d8 Translated using Weblate (Japanese)
Currently translated at 96.8% (9024 of 9320 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-05-17 09:41:51 +00:00
Watuneni iteiteandWeblate 1abe39db3c Translated using Weblate (Japanese)
Currently translated at 96.6% (9012 of 9320 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-05-17 02:13:26 +00:00
TeggyandWeblate 4e7cdb6266 Translated using Weblate (Spanish)
Currently translated at 94.2% (8786 of 9320 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-05-17 02:13:25 +00:00
Carmendar 79b13acdc6 Gen7 alpha eyes 2026-05-16 20:43:04 -05:00
ewwww 0ffd6fad90 gen 2 locators wip, pushing for now 2026-05-16 01:47:17 -04:00
geumuandWeblate 3c9661dfd3 Translated using Weblate (Korean)
Currently translated at 100.0% (9320 of 9320 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ko/
2026-05-16 04:58:00 +00:00
Watuneni iteiteandWeblate 520f522f3c Translated using Weblate (Japanese)
Currently translated at 96.6% (9012 of 9320 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-05-16 04:58:00 +00:00
TeggyandWeblate 113ae1f749 Translated using Weblate (Spanish)
Currently translated at 94.2% (8785 of 9320 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-05-16 04:57:58 +00:00
ewwww 61bf5ffab1 feraligatr poser update 2026-05-15 23:40:09 -04:00
Gustavo PuerariandWeblate 87da3e83a0 Translated using Weblate (Portuguese (Brazil))
Currently translated at 97.5% (9090 of 9320 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/pt_BR/
2026-05-15 14:36:53 +00:00
geumuandWeblate c325d0215a Translated using Weblate (Korean)
Currently translated at 99.6% (9290 of 9320 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ko/
2026-05-15 14:36:50 +00:00
TeggyandWeblate 8afb4e6d16 Translated using Weblate (Spanish)
Currently translated at 93.7% (8742 of 9320 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-05-15 14:36:47 +00:00
ManriqueandWeblate 0ffff6f4af Translated using Weblate (Spanish)
Currently translated at 93.7% (8736 of 9320 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-05-15 13:06:40 +00:00
5018 changed files with 355054 additions and 51077 deletions
+1
View File
@@ -24,6 +24,7 @@ eclipse
run
runClient
runServer
utilityscripts/implementation/*.txt
# Files from Forge MDK
forge*changelog.txt
+4
View File
@@ -16,6 +16,7 @@ check_assets_cache:
stage: preprocessing
script:
- echo "Checking if cached hash matches current hash"
- yum install -y findutils
# Calculate the current hash
- find common/src/main/resources/assets common/src/main/resources/data -type f -exec sha256sum {} + | sort -k 2 | sha256sum > current_hash.txt
@@ -148,6 +149,7 @@ build:
dependencies:
- replace_resources
script:
- yum install -y findutils
- export GRADLE_OPTS="-Xmx4G -Xms4G"
- chmod +x ./gradlew
- ./gradlew build
@@ -163,6 +165,7 @@ publish:
rules:
- if: '$CI_PROJECT_PATH == "cable-mc/cobblemon" && $CI_COMMIT_BRANCH == "main"'
script:
- yum install -y findutils
- export GRADLE_OPTS="-Xmx4G -Xms4G"
- chmod +x ./gradlew
- ./gradlew publish
@@ -176,6 +179,7 @@ deploy_artifacts:
dependencies:
- build
script:
- yum install -y findutils
- echo "Collecting artifacts..."
- find neoforge/build/libs/ -type f -name "*.jar" | grep -vE "dev-shadow|sources|javadoc" | xargs -I {} mv {} ./
- find fabric/build/libs/ -type f -name "*.jar" | grep -vE "dev-shadow|sources|javadoc" | xargs -I {} mv {} ./
+32 -376
View File
@@ -1,385 +1,41 @@
# Changelog
## [1.8.0 (MONTH Xth, 2026)](#1-8-0)
### Additions
- Added config options to award experience to fainted Pokémon for opponents they helped defeat (`awardExperienceToFaintedPokemon`) and to still award experience for defeated enemies even after battle loss/forfeit (`awardExperienceOnBattleLoss`)
- Party Overlay Exp Gained Animation will now display the amount of EXP the Pokémon gained
- Added a toast to notify players of how they can scroll through their party. Did you know you can hold R and scroll instead of using arrow keys??!
- Added a new layer property that makes a layer scroll in a direction. "scrolling": {"speedU": 0.1, "speedV": 0.1}
- Added missing crops to Botany Pots integration
- Added a way to obtain Porygon by inserting the Upgrade or Dubious Disc into a data monitor
- Added a new `cobblemon:structure`, which is a feature without any restrictions allowing the Builders to place certain structures in much more specific manner.
- Added Configs for Infinite Use TMs and Unlocking all TMs
- Added Type Gem blocks
- Added Type Gem Clusters
- Added TMs
- Added TM Machine
- Added Deepslate Core Block
- Added /givetm Command
- Added advancement triggers for learning TMs: `cobblemon:has_learn_specific_tm` (per-TM), `cobblemon:has_learn_all_tm` (all TMs learned), and `cobblemon:tms_learned` (count-based), backed by a new `totalTMLearnedCount` on player advancement data.
- Added `cobblemon:catch_alpha_pokemon` advancement trigger and `totalAlphaCaptureCount` tracking for capturing Alpha Pokémon.
- Added Move Dex to Pokédex
- Added the Habitat Block, a spawner block for servers and adventure maps that controls spawning in an area.
- Added Disk Rack for storing disks and being an audio sequencer for Noteblocks
- Added intrinsic scaling to all mons (+-5%). Some rendering cases, such as shoulder mounting, revert to 1.0 scaling to preserve animation accuracy.
- Added scaling to "baby" Pokémon (smaller (-20%) at level 1 → normally sized at level 10)
- Added conditional seats for riding. Seats can be gated by molang such as "is_alpha".
- Added Alpha Pokémon
- Added alpha scaling (alphas are larger than their normal counterparts)
- Alphas will always lead herds and will spawn in herds
- Alphas spawn with two random TM moves from their learnset
- Alpha Pokémon will always defend themselves from players that attack them
- Added alpha level matching and stat boost (based on level)
- Added pitch-down and reverb to alpha cries
- Added alpha Pokémon bait effect
- Added alpha eye rendering including eye overlay, bloom, eye trail, and particles.
- Added cobweb block slowness immunity for spider Pokémon
- Added 49 new habitat structures. 34 as typical Minecraft structures and 15 as structures that get placed as biome features to allow for more natural placement in generation. Structures placed as biome features will not be able to be located with the `/locate structure` command.
- Vanilla village house template pools have been adjusted to account for the new abandoned village structure habitat. Weights have changed within the JSON file for every piece to ensure too many habitats don't generate in a single village.
- Added 10 new ruin structures.
- rooted_arch_ruins & crumbling_arch_ruins have been transferred over to biome features, allowing for better placement in generation.
- All ruin structures that did not generate with a gimmighoul/gilded chest before will now generate with one.
- All ruin structures gilded chest loot tables have been updated to include a guaranteed TM not learned naturally by any Pokémon through their level-up moveset. Each ruin gets its own specific TM, so you can now go hunting ruins to complete your move dex!
- All fossil structures have been transferred over to biome features, allowing for better placement in generation.
- The prehistoric_underwater_fissure & prehistoric_submerged_impact fossil structures now use the biome tag `#cobblemon:is_temperate_ocean`.
- A new Biome tag called `#cobblemon:is_stony_beach` has been added.
- Gimmighoul towers now generate at ground level rather than sinking one block into the ground.
- 1 New shipwreck cove structure called the "magma_shipwreck_cove" has been added.
- Shipwreck coves now generate slightly more frequently.
- Added equipment loot tables for shipwreck cove enemies.
- Maps from abandoned fishing boats leading to shipwreck coves now will skip explored chunks, allowing the player to find more shipwreck coves easier.
- The lush shipwreck cove's phantom mob spawner has been replaced with breezes.
- The lush shipwreck cove structure itself has been slightly cleaned up to be less noisy with the blocks it is built out of.
- All shipwreck coves' spawners have been adjusted to give compatible mobs enchanted iron or diamond armour and weapons while in its ominous state. The armour given to the mobs will also use the automaton trim. We upped the difficulty because it was too easy now that your Pokémon can fight for you.
- One TM of the elemental hyper beams has been added as guaranteed loot to the main treasure chest in every shipwreck cove. (Magma cove gets blast burn, lush cove gets frenzy plant, and submerged cove gets hydro cannon)
- Added 7 new advancements
- Pressing the inventory key now closes the Pokémon interact wheel GUI
- Pokémon cries now play when clicking Pokémon in the summary screen.
- Pressing the Summary key while either the starter selection or summary screen are open now closes those screens
### New rideable Pokémon
- Drampa
- Archeops
- Skarmory
- Crobat
- Goodra
- Hisuian Goodra
- Milotic
### Pokémon Added
- Wurmple
- Silcoon
- Beautifly
- Cascoon
- Dustox
- Skitty
- Delcatty
- Seviper
- Zangoose
- Cherubi
- Cherrim
- Stunky
- Skuntank
- Audino
- Pawniard
- Bisharp
- Kingambit
- Pancham (+ Cosmetic with Black Glasses)
- Pangoro (+ Cosmetic with Black Glasses)
- Rockruff
- Lycanroc
- Rolycoly
- Carkol
- Coalossal
- Snom
- Frosmoth
- Duraludon
- Archaludon
- Applin
- Flapple
- Appletun
- Dipplin
- Hydrapple
- Nymble
- Lokix
#### Regional Forms
- Sandshrew (Alolan)
- Sandslash (Alolan)
- Avalugg (Hisuian)
### Model updates for the following Pokémon
- Goomy
- Sliggoo
- Goodra
- Porygon
- Porygon2
- Porygon-Z
- Zubat
- Golbat
- Crobat
### Model updates for the following Pokémon
- Sandshrew
- Sandslash
- Nidoking
- Zubat
- Golbat
- Crobat
- Porygon
- Porygon2
- Porygon-Z
- Skarmory
- Jynx
- Popplio
- Brionne
- Primarina
- Fletchling
- Fletchinder
- Talonflame
- Sableye
- Mawile
- Goomy
- Sliggoo
- Goodra
- Nacli
- Naclstack
- Garganacl
#### Model changes to the regional bias resource pack:
- Added Mime Jr. (Galar Bias)
- Added Bergmite (Hisui Bias)
- Changed models for every existing regional bias
## [1.8.1 (September 13th, 2026)](#1-8-1)
### Animation updates for the following Pokémon
- Skarmory
- Skitty
- Flygon
- Shellos
- Riolu
- Lucario
- Purrloin
- Liepard
- Joltik
- Galvantula
- Nidoking
- Fletchling
- Fletchinder
- Talonflame
- Archeops
- Drampa
- Sewaddle
- Swadloon
- Leavanny
- Goomy
- Sliggoo
- Goodra
- Shedinja
- Skwovet
- Greedent
#### Added sounds to the following moves
- Absorb
- Aurora Beam
- Double Team
- Draco Meteor
- Fire Punch
- Fire Spin
- Flame Charge
- Flame Wheel
- Giga Drain
- Ice Punch
- Leech Life
- Magical Leaf
- Mega Drain
- Minimize
- Poison Gas
- Rock Throw
- Scary Face
- Seed bomb
- Smokescreen
- String Shot
- Tail Whip
### Changes
- Stat increase and decrease particles have been changed, with the addition of alpha boost particles.
- Dragonite's mailbag cosmetic can now also be obtained with a chest in the cosmetic slot
- Flapple, Appletun, Dipplin, Hydrapple (placeholder animations only).
### Fixes
- Adjusted Croagunk's walkspeed
- Fixed Blitzle's base scale being `1` rather than the intended `0.8`
- Fixed Magnezone's emissive layer for the shiny having the wrong name, thus displaying wrongly
- Fixed Litleo not blinking
- Fixed Garchomp T-posing when sleeping while out of battle.
- Sharp Beak can now be crafted
- Metal Alloy can now be crafted
- Ability Capsules can now be brewed
- Using an Ability Capsule will now return an Empty Bottle
- The following items can now be obtained by brushing the following Pokémon:
- Pink Wool - Wigglytuff
- White Wool - Swablu, Altaria
- Shed Shell - Ekans, Arbok, Dratini, Dragonair, Dunsparce, Treecko, Grovyle, Sceptile, Seviper, Scraggy, Scrafty, Helioptile, Heliolisk, Salandit, Salazzle, Sobble, Drizzile, Inteleon, Silicoblra, Sandaconda, Toxel, Toxtricity, Orthworm, Dudunsparce
- Phantom Membrane - Dreepy, Drakloak, Dragapult
- The following items can now be obtained by evolving the following Pokémon into any of their evolutions:
- Turtle Scute - Squirtle, Wartortle, Turtwig, Grotle, Tirtouga, Chewtle
- Shed Shell - Metapod, Kakuna, Ekans, Paras, Venonat, Krabby, Scyther, Kabuto, Dratini, Dragonair, Ledyba, Spinarak, Yanma, Pineco, Dunsparce, GLigar, Pupitar, Treecko, Grovyle, Silcoon, Cascoon, Surskit, Nincada, Trapinch, Vibrava, Corphish, Anorith, Bagon, Shelgon, Kricketot, Burmy, Combee, Skorupi, Swadloon, Whirlipede, Dwebble, Scraggy, Karrablast, Joltik, Larvesta, Spewpa, Clauncher, Helioptile, Charjabug, Crabrawler, Cutiefly, Dewpider, Salandit, Wimpod, Poipole, Sobble, Drizzile, Dottler, Silicobra, Toxel, Sizzlipede, Snom, Tarountula, Nymble, Rellor
- Saccharine Leaves now drop Tart, Sweet, or Syrupy Apples, depending on their honey coating stage, instead of vanilla Apples
- Fixed the height of the Tumblestone Bud items when held in hand, placed on head, viewed in a GUI, and placed in an Item Frame
- Refactored all advancements, removing several deemed unnecessary. Due to this refactor, all previously obtained advancements will be reset besides the root (for picking a starter Pokémon). We are sorry for the inconvience, but it makes the tab cleaner and simplifies future advancements.
- Restructured Botany Pots integration folder structure
- Decrease the default pastured mon wander radius to 32.
- Factor in the weight of Pokémon when calculating pushing forces between hitboxes.
- Remade the Starter Selection Screen with new assets
- Refactored berry trees to be less impactful on TPS (more performant)
- Changed the bait effects of the following items: (Vanilla) Sweet & Glow Berries, Golden & Enchanted Golden Apples (Cobblemon) Custap, Eggant, Hopo & Micle Berries, Sweet Hearts
- Wild Pokémon now reliably have at least one offensive move if they can have offensive moves at that level.
- Data Monitor can accept Disks (TMs, Music Disks, Upgrade, Dubious Disk, etc.) to change what's being displayed on the screen
- Rider positioning is now client-driven (Remove poseOffsets and offset per seat/pose) in favour of sending real-time passenger position.
- Cleaned up seat data. Removed all now unused fields from seats in all species files. Also made locator seat names explicit in the species files (however, this doesn't break the legacy format)
- Marks can now provide aspects.
- Added new shiny and Alpha chance configs
- Apricorns now make a new sound when harvested
- Lowered the volume of harvesting berries
- Added alternate versions of the basic PC Wallpapers based on the original design of Cobblemon's PC, before public release
- Improved compatibility with various mods
- Farmer's Delight's cutting board can now be used to strip Apricorn and Saccharine's logs, or salvage their planks from various blocks. It can also separate their boats from chests
- Farmer's Delight's cutting board can be used to get two white dye from Pep-Up Flowers and two magenta dye from Medicinal Leeks
- Farmer's Delight's cutting board can separate Tumblestone Blocks back into Tumblestones
- Farmer's Delight's cutting board can salvage Type Gems from their Gem Blocks
- Farmer's Delight's Onions, Tomatoes, and Cabbages can be used to cook Open-Faced Sandwiches. Nature's Spirit's Olives also can be used
- Farmer's Delight's cutting board can be used on Hearty Grains to obtain rice and straw, similarly to Farmer's Delight's Rice Panicles
- Farmer's Delight's Apple Pies and Apple Ciders can now be made using Applin Apples, alongside the Vanilla counterpart
- Amendments allows for cooking Leek and Potato Stew in a Cauldron
- Any mods that utilise Moonlight's Soft Fluid system can store Leek and Potato Stew, Medicinal Brew, Antidote, Awakening, Burn Heal, Ice Heal, Paralyze Heal, Full Heal, Potion, Super Potion, Hyper Potion, Max Potion, Full Restore, Ability Capsules, Moomoo Milk, and Berry Juice in liquid form (such as Suplementaries' Jar or Goblet, or Cauldrons while using Amendments)
- Supplementaries' Hourglass can now use Bright Powder, Quick Powder, Metal Powder, Heal Powder, Silver Powder, and Remedy
- Berry Trees are now compatible with the Planter properly
- Saccharine Way Signs (added by Supplementaries automagically) now use a premade texture to prevent odd visual issues
- Supplementaries' Jar can now store various Cobblemon cookies, candies, and similar items
- Some of Supplementaries' items have been added to the `#cobblemon:held/container_held_items` item tag, including sacks, safes, and lunch baskets
- Experience Nuggets can be used in place of Sculk for making the EXP Candies if using Create
- Rage Candy Bar has a Create Mixing recipe
- All Cobblemon ores can now be crushed in Create's Crushing Wheels
- All Gem and Tumblestone Clusters can be crushed for automatic, low return, gems
- Hearty Grains can be milled like Farmer's Delight Rice Panicles if both Farmer's Delight and Create are installed
- Big Roots and Revival Herbs can be milled to their respective powders
- Pep-Up Flowers, Medicinal Leeks, and all 70 berries can be milled in Create's Millstone for additional dye drops
- Auspicious Armor and Upgrades can be Haunted using Create's crafting systems
- Applin Apples can be used to make Honeyed Apples in Create
- Golden Apples can now be crafted using the Applin Apples
- Prismarine Crystals and Amethyst Shards can now be used to craft Pokedexes
- Updated Pokedex Screens item tag to support tagged variants of Redstone and Glowstone Dust
- Sounds for interacting with various Cobblemon blocks now play from the center of the block, rather than the corner
- The `/pc` command no longer plays PC activation sounds
- Lowered the volume of Blitzle and Zebstrika's cry
- Fixed Raichu (male) and Alolan Raichu (both genders) not displaying the correct Partner Cap when Pewter Crunchies are in the cosmetic slot.
- Regional Foods will now prioritize being eaten instead of used for healing Pokémon, shift clicking will use it as healing
### Fixes
- Fixed Croagunk's walkspeed
- Fixed Blitzle's base scale being `1` rather than the intended `0.8`
- Fixed Magnezone's emissive layer for the shiny having the wrong name, thus displaying wrongly
- Fixed Litleo not blinking
- Fixed Pokédex layout for genderless variations
- Fixed odd stacking behaviour with the poke_bait item.
- Fixed a compatibility issue with World Game Rules which caused some log spam in specific cases.
- Fixed improper validation of Pokémon when performing a trade.
- Fixed an issue where particle like beam can disappear when you don't see the beginning of the beam, or you look away
- Fixed Bedrock particle materials (alpha/opaque/blend/add) sharing one render batch, which caused incorrect culling on mixed batches; each material now batches and draws with its own blend and cull state, and the alpha material renders as a true cutout
- Fixed an occasional crash on retrieving revived fossils
- Fixed brushing not working on Neoforge
- Fixed braking not working for submarines (whoops).
- Fixed strafing not having the same friction as forward movement (funny whoops).
- Fixed submarine's surface-water animations not playing when supposed to.
- Fixed submarine camera freaking out when looking up or down on the surface.
- Fixed pastured mons despawning and running away.
- Fixed Apricorn trees crashing when other mods bonemeal it
- Fixed the issue that caused Hover Behaviour rides without a ground style to be rendered useless upon touching the ground on a server.
- Removed the "inAir" penalty for block destruction speed when on a hover mount.
- Fixed horizontal and vertical collisions with hover mounts so they no longer accumulate or retain momentum
- Fixed NeoForge brewing stand unexpectedly crashing users when shift clicking items within its menu
- Fixed NPCs switching Pokémon very often during battles
- Fixed Campfire pot causing crashes under certain circumstances
- Fixed Type Gems not being consumed upon use
- Fixed Ancient Pokéball's to their respective modifiers
- Fixed a Server warning when recalling Pokémon
- Fixed sliding particles incorrectly handling block collision
- Fixed Full Heal not curing status conditions in battle
- Fixed a rare issue with battles locking up when you are forced to switch, with the switch menu appearing instantly.
- Fixed the combined effects of Pledge moves having broken Showdown message handling and localisation
- Fixed rare Pokémon being far too likely to spawn multiple times in a single spawn cycle. The dev told me that the exact bug is too complicated to explain.
- Resolved the niche issue where Structures would spawn in an incorrect location, creating very funky world gen.
- Fixed Galarian Ponyta missing its cry.
- Fixed various log warnings caused by redundant files and sound events.
- Traded Pokémon will now retain the original friendship level when traded back to its OT
- Clusters now display correctly on maps
- Fixed removing mulch from berry trees with a shovel not working.
- Fixed collecting honey from Saccharine Leaf blocks deleting Honey Bottles when the inventory was full
- Fixed Stripped Saccharine Wood recipe being missing
- Fixed CancelableSoundInstance causing lag by attempting to create sounds every tick when out of range of hearing
- Fixed Stat Reducing Aprijuice not being possible to apply on a max out Pokémon, and the possibility of going into negatives
- Fixed Exploit where you could take out the bait or drop your Pokérod with bait to avoid consuming bait, now the bobber will just disappear if you do so
- Fixed Apricorn and Saccharine boats incorrectly using chest boat models
- Fixed wild and trainer battle AI refusing to select moves with the `adjacentAlly` target type, even when it's their only option
- Fixed Guilded chests being more accessible than intended
- Fixed fished up Pokémon not being added as "seen" to the Pokedex
- Fixed surprised Gimmighoul encounters not being added as "seen" to the Pokedex
### Developer
- Added `PokeSnackSpawnerFactory` which allows influence registration on PokeSnack encounters
- Added `FishingSpawnerFactory` which allows influence registration on fishing encounters
- Added `BattleFleeAttemptEvent` fired from `PokemonBattle.checkFleeAttempt`, allowing developers to intercept and control flee attempts
- Tweaked the spawning selector interface to take a bucket function rather than a bucket directly.
- Added a new `cobblemon:height_range` processor type to allow developers to restrict structure-spawning ranges.
- Separated all the functions of MoLangFunctions.kt into their own files.
- Spawn buckets and their base weights are now provided by the spawner, with different base weights for world, fishing, snack, and habitat spawn buckets.
- Removed "SpawnBucket" class in favour of just storing maps of bucket names to weights.
### Molang & Datapacks
- Added `moveset_builders` datapack folder for different styles of movesets.
- Added `party_pools` datapack folder for defining groups of Pokémon for complex NPC party definitions.
- Added `party_compositions` datapack folder for arranging parties on NPCs that rely on party pools.
- Added `composed_pool` NPC party provider type which combines a party pool with a party composition to create more complex NPC parties.
- Added basic chatter NPC Behaviour
- Added `q.item.is_enchanted()`
- Added `q.item.has_enchantment(minecraft:sharpness, 3)`
- Added `has_chosen_starter` & `get_starter_uuid` as available Molang functions
- Added `marks`, `has_mark`, & `remove_marks` as available Molang functions for Pokémon
- Added Data Component support for species drops, eg `"components": {"minecraft:custom_model_data": 123}`
- Added the new `sortOrder` field to mark entries, (defaults to 0 if not specified) which controls the order in which marks are rendered on the HUD, with higher numbers being rendered on top of lower numbers
- Added optional `order` field to starter categories. It will allow explicitly sorting categories in the starter selection screen
- SpeciesAdditions append vs override now matches wiki documentation
- Added `weighted_choice` as a new feature type. It requires setting weights for each choice and allows developers to simplify the process of assigning random aspect choice based on marble chance
- Added `q.world.spawn_loot_table_items(loot_table_id, x, y, z)` which spawns the items from the loot table at the position. (Use type: "minecraft:chest" for the loot table.)
- Added `q.pokemon.entity` to access the entity of Pokémon.
- Flamethrower and Seismic Toss had their action effect JSON cleaned up
- Ride sounds can now be set as exclusive to passengers
- Ride sounds no longer play when submerged unless specified with a new setting
- NPC Trainers can now call a `command` animation when issuing a command in battle.
- Added a q.failed function to move action effects.
- Herd spawned Pokémon have their levels controlled differently. The `levelRange` property now acts as a clamp instead of a filter, with a new `herdLevelRange` property to do the filter behaviour.
### Particles
- Fixed a potential world generation stall caused by Type Gem clusters.
- Fixed Pokémon on shoulders not animating if they are visibly holding a held item.
- Fixed NPCs getting their own TM progress file.
- Fixed odd NeoForge crash due to a dependency being outdated. It will now correctly flag the dependency for being outdated.
- Fixed the TM Machine refusing move selections that the `unlockAllMoveDexMovesByDefault` config had made visible, leaving the move slot blank and the craft button unresponsive.
- Fixed the Beast Ball not applying its 0.1× penalty to Pokémon that are not Ultra Beasts.
- Fixed the Moon Ball reading the world's total elapsed time instead of the time of day, so its bonus applied during the day instead.
- Fixed the Love Ball never awarding its 2.5× opposite gender bonus, and matching species by display name instead of identifier.
- Fixed the Timer Ball being weaker than a regular Poké Ball on early turns and only reaching 4× on turn 14 instead of turn 10.
- Fixed the Luxury Ball only doubling Friendship gains of 1, leaving level up and berry gains unaffected.
- Fixed Rufflet, Braviary, and Hisuian Braviary flooding logs with errors about missing sound events.
- Fixed Pokémon property typos in Pangoro and Dedenne herd spawn files.
- Fixed unintentional watermelon cosmetic for Hisuian Electrode.
- Fixed Blastoise having an unintentional passenger seat.
- Fixed form typo in Rockruff-Dusk spawn, preventing it from being able to spawn.
- Fixed the `stat_compare` and `stat_equal` evolution requirements crashing the game when given `special_attack` or `special_defence`, and made them treat any non-permanent stat as a failed requirement instead of crashing.
- Fixed Rowap and Jacoba berries not correctly altering Pokémon sizes when used as bait.
- Fixed Ampharos' eyes not emitting particles.
- Fixed Alolan Raticate's cry not playing.
- Fixed several incorrect light-based spawning conditions for ghosts and bugs.
- Removed unused, placeholder habitat pools.
- Fixed Mantyke, Mantine, and Quaxly getting stuck in a recoil 'expression' during battles due to having an expression assigned as a recoil animation.
- Fixed Lucario's aura particles not emitting.
- Fixed Lycanroc's head movement.
- Added head movement to Archaludon.
- Fixed Duraludon line not having their sleep animations.
- Fixed Diglett line not having their faint animations.
- Fixed Kingambit moving its legs while walking.
### Localization
- Updated translations for:
- Japanese
- German
- Ecuadorian Spanish
- Brazilian Portuguese
- Simplified Chinese
- Traditional Chinese
- Korean
- Japanese
+529
View File
@@ -0,0 +1,529 @@
# Changelog
## [1.8.0 - Make Your Move Update (September 6th, 2026)](#1-8-0)
#### I think the red eyes mean they're friendly!
### Additions
- Added Alpha Pokémon
- Alpha Pokémon are bigger, stronger, and more aggressive than regular Pokémon.
- They are also scarier.
- They always know a TM move and will adapt to be stronger than your party.
- Alphas will always lead herds and spawn in herds.
- Added alpha Pokémon attraction bait effect to Hopo Berries.
- Added intrinsic scaling to all Pokémon (anywhere from 5% smaller to 5% bigger than normal). Some cases, such as shoulder mounting, will not scale so that the shoulder animations don't look silly.
- Added scaling to "baby" Pokémon (10% smaller at level 1 → normally sized at level 10).
- Added TMs.
- Added TM Machine. TMs are made from elemental type gems, and their recipes are unlocked by having Pokémon that know the move.
- Added config options for making TMs infinite-use and instant unlocking of all TM recipes.
- Added Type Gem blocks as well as crystal clusters and deepslate core blocks that are used for growing elemental type gems.
- Added a new Starter Selection menu that looks way better.
- Added a "Move Dex" to the Pokédex to help identify what moves a Pokémon can learn and what Pokémon can get a move.
- Added the Habitat Block, a spawner block for servers and adventure maps that controls spawning in an area.
- Added 49 new habitat structures. In these habitats you will find rarer Pokémon and sometimes different Pokémon will appear on different days.
- Added a toast to notify players of how they can scroll through their party. Did you know you can hold R and scroll instead of using arrow keys? It's true!!!
- Added a way to obtain Porygon by inserting an Upgrade or Dubious Disc into a data monitor.
- Added Disk Rack for storing disks and being an audio sequencer for Noteblocks. What does that have to do with Pokémon? How about you mind your own business!
- Added missing crops to Botany Pots integration.
- Added conditional seats for riding. Seats can be gated by Molang such as "is_alpha".
- Added 10 new ruin structures.
- Added a new shipwreck cove structure called the Magma Shipwreck Cove.
- Added 7 new advancements.
- Pokémon cries now play when clicking Pokémon in the summary screen.
- Held item visibility can now be controlled by the `held_item_visible` command argument.
- Added config option to disable Pokémon spawning on a per-world basis.
- Added config options to award experience to fainted Pokémon for opponents they helped defeat (`awardExperienceToFaintedPokemon`) and to still award experience for defeated enemies even after battle loss/forfeit (`awardExperienceOnBattleLoss`).
- Added `/givetm` command.
- Added advancement triggers for learning TMs: `cobblemon:has_learn_specific_tm` (per-TM), `cobblemon:has_learn_all_tm` (all TMs learned), and `cobblemon:tms_learned` (count-based), backed by a new `totalTMLearnedCount` on player advancement data.
- Added `cobblemon:catch_alpha_pokemon` advancement trigger and `totalAlphaCaptureCount` stat for captured Alpha Pokémon.
### New rideable Pokémon
- Crobat
- Skarmory
- Milotic
- Archeops
- Goodra
- Goodra (Hisuian Form)
- Drampa
- Duraludon
- Archaludon
### Pokémon Added
#### Generation 3
- Wurmple
- Silcoon
- Beautifly
- Cascoon
- Dustox
- Skitty
- Delcatty
- Seviper
- Zangoose
#### Generation 4
- Cherubi
- Cherrim
- Stunky
- Skuntank
#### Generation 5
- Audino
- Pawniard
- Bisharp
#### Generation 6
- Pancham (+ Cosmetic with Black Glasses)
- Pangoro (+ Cosmetic with Black Glasses)
#### Generation 7
- Rockruff
- Lycanroc
#### Generation 8
- Rolycoly
- Carkol
- Coalossal
- Runerigus
- Snom
- Frosmoth
- Indeedee
- Duraludon
- Applin
- Flapple
- Appletun
#### Generation 9
- Nymble
- Lokix
- Kingambit
- Dipplin
- Archaludon
- Hydrapple
#### Regional Forms
- Sandshrew (Alolan Form)
- Sandslash (Alolan Form)
- Yamask (Galarian Form)
- Avalugg (Hisuian Form)
### Model updates for the following Pokémon
- Sandshrew
- Sandslash
- Nidoking
- Zubat
- Golbat
- Crobat
- Ditto
- Porygon
- Porygon2
- Porygon-Z
- Skarmory
- Smoochum
- Jynx
- Popplio
- Brionne
- Primarina
- Fletchling
- Fletchinder
- Talonflame
- Sableye
- Mawile
- Goomy
- Sliggoo
- Goodra
- Nacli
- Naclstack
- Garganacl
- Bidoof
- Cranidos
- Chatot
- Yamask
- Cofagrigus
- Tyranitar
- Turtonator
- Golett
- Corsola
- Arbok (New Patterns: Angry, Cartoon, Clown, Comedic, Crow, Grin, Ink, Modern, Rain, Scribble, Silence, Sloth, Suspicious, Will)
- ... and Substitute
#### Model changes to the regional bias resource pack:
- Added Mime Jr. (Galar Bias)
- Added Bergmite (Hisui Bias)
- Changed models for every existing regional bias
### Animation updates for the following Pokémon
- Kakuna
- Grimer
- Nidoran♀ (+ New shoulder mount)
- Nidorina
- Nidoran♂ (+ New shoulder mount)
- Sandshrew
- Diglett
- Dugtrio
- Graveler
- Happiny
- Chansey
- Blissey
- Poliwhirl
- Poliwrath
- Zubat
- Vulpix
- Drowzee
- Hypno
- Noctowl
- Skarmory
- Skitty
- Flygon
- Shellos
- Riolu
- Lucario
- Purrloin
- Liepard
- Joltik
- Galvantula
- Nidoking
- Fletchling
- Fletchinder
- Talonflame
- Archeops
- Drampa
- Sewaddle
- Swadloon
- Leavanny
- Goomy
- Sliggoo
- Goodra
- Shedinja
- Skwovet
- Greedent
- Bidoof
- Cranidos
- Chatot
- Yamask
- Cofagrigus
- Luvdisc
- Staryu
- Starmie
- Togetic
- Rellor
- Rabsca
- Yungoos
- Gumshoos
- Cottonee
- Whimsicott
- Corsola
#### Added sounds to the following moves
- Absorb
- Aurora Beam
- Double Team
- Draco Meteor
- Fire Punch
- Fire Spin
- Flame Charge
- Flame Wheel
- Giga Drain
- Ice Punch
- Leech Life
- Magical Leaf
- Mega Drain
- Minimize
- Poison Gas
- Rock Throw
- Scary Face
- Seed bomb
- Smokescreen
- String Shot
- Tail Whip
### Changes
- Removed the penalty for block destruction speed when on a hover mount, making them better for digging.
- Golden Apples can now be crafted using the Applin Apples.
- Prismarine Crystals and Amethyst Shards can now be used to craft Pokedexes.
- Updated Pokédex Screens item tag to support tagged variants of Redstone and Glowstone Dust.
- Sharp Beak can now be crafted using bones and Flying Gems.
- Metal Alloy can now be crafted using iron and Thunder Stones.
- Ability Capsules can now be brewed from Medicinal Brew and Dragon Breath.
- Using an Ability Capsule will now return an Empty Bottle.
- Decreased the default pastured wander radius to 32 blocks.
- Added alternate versions of the basic PC Wallpapers based on the original design of Cobblemon's PC, before public release.
- Rooted Arch Ruins, Crumbling Arch Ruins, and all fossil structures have been improved so that they generate more naturally in terrain.
- Gimmighoul towers now generate at ground-level rather than sinking one block into the ground.
- All ruin structures that did not generate with a gimmighoul/gilded chest before will now generate with one.
- All ruin structures gilded chest loot tables have been updated to include a guaranteed TM not learned naturally by any Pokémon through their level-up moveset. Each ruin gets its own specific TM, so you can now go hunting ruins to complete your move dex!
- The Prehistoric Underwater Fissure and Prehistoric Submerged Impact fossil structures now use the biome tag `#cobblemon:is_temperate_ocean`.
- The lush shipwreck cove's phantom mob spawner has been replaced with breezes.
- Shipwreck coves now generate slightly more frequently.
- Added equipment loot tables for shipwreck cove enemies.
- Maps from abandoned fishing boats leading to shipwreck coves now will skip explored chunks, allowing the player to find more shipwreck coves more easily.
- All shipwreck coves' spawners have been adjusted to give compatible mobs enchanted iron or diamond armour and weapons while in its ominous state. The armour given to the mobs will also use the automaton trim. We upped the difficulty because it was too easy now that your Pokémon can fight for you.
- One TM of the elemental hyper beams has been added as guaranteed loot to the main treasure chest in every shipwreck cove. (Magma Cove gets Blast Burn, Lush Cove gets Frenzy Plant, and Submerged Cove gets Hydro Cannon.)
- The following items can now be obtained by brushing the following Pokémon:
- Pink Wool - Wigglytuff
- White Wool - Swablu, Altaria
- Shed Shell - Ekans, Arbok, Dratini, Dragonair, Dunsparce, Treecko, Grovyle, Sceptile, Seviper, Scraggy, Scrafty, Helioptile, Heliolisk, Salandit, Salazzle, Sobble, Drizzile, Inteleon, Silicoblra, Sandaconda, Toxel, Toxtricity, Orthworm, Dudunsparce
- Phantom Membrane - Dreepy, Drakloak, Dragapult
- The following items can now be obtained by evolving the following Pokémon into any of their evolutions:
- Turtle Scute - Squirtle, Wartortle, Turtwig, Grotle, Tirtouga, Chewtle
- Shed Shell - Metapod, Kakuna, Ekans, Paras, Venonat, Krabby, Scyther, Kabuto, Dratini, Dragonair, Ledyba, Spinarak, Yanma, Pineco, Dunsparce, GLigar, Pupitar, Treecko, Grovyle, Silcoon, Cascoon, Surskit, Nincada, Trapinch, Vibrava, Corphish, Anorith, Bagon, Shelgon, Kricketot, Burmy, Combee, Skorupi, Swadloon, Whirlipede, Dwebble, Scraggy, Karrablast, Joltik, Larvesta, Spewpa, Clauncher, Helioptile, Charjabug, Crabrawler, Cutiefly, Dewpider, Salandit, Wimpod, Poipole, Sobble, Drizzile, Dottler, Silicobra, Toxel, Sizzlipede, Snom, Tarountula, Nymble, Rellor
- Saccharine Leaves now drop Tart, Sweet, or Syrupy Apples, depending on their honey coating stage, instead of vanilla Apples.
- Fixed the height of the Tumblestone Bud items when held in hand, placed on head, viewed in a GUI, and placed in an Item Frame.
- Refactored all advancements, removing several deemed unnecessary. Due to this cleanup, all previously obtained advancements will be reset besides the root (for picking a starter Pokémon). This sucks, and we're sorry. We had to do this eventually so better now than later.
- Forces between colliding hitboxes now factors in the weight of Pokémon.
- Improved berry tree performance on singleplayer and servers.
- Updated stat increase and decrease particle effects with the addition of alpha boost particles.
- Improved the performance of pasture blocks on singleplayer and servers.
- Changed the bait effects of the following items: (Vanilla) Sweet & Glow Berries, Golden & Enchanted Golden Apples (Cobblemon) Custap, Eggant, Hopo & Micle Berries, Sweet Hearts.
- Wild Pokémon now reliably have at least one offensive move if they can have offensive moves at that level. No more free lunches with Pokémon that can't even hurt you.
- Data Monitor can accept Disks (TMs, Music Disks, Upgrade, Dubious Disk, etc.) to change what's being displayed on the screen.
- The party overlay's experience-gained animation now displays the amount of EXP the Pokémon gained.
- Pressing the inventory key now closes the Pokémon interact wheel GUI.
- Updated the sound for harvesting Apricorns.
- Lowered the volume of harvesting berries.
- Improved compatibility with various mods
- Farmer's Delight's cutting board can now be used to strip Apricorn and Saccharine's logs, or salvage their planks from various blocks. It can also separate their boats from chests.
- Farmer's Delight's cutting board can be used to get two white dye from Pep-Up Flowers and two magenta dye from Medicinal Leeks.
- Farmer's Delight's cutting board can separate Tumblestone Blocks back into Tumblestones.
- Farmer's Delight's cutting board can salvage Type Gems from their Gem Blocks.
- Farmer's Delight's Onions, Tomatoes, and Cabbages can be used to cook Open-Faced Sandwiches. Nature's Spirit's Olives also can be used.
- Farmer's Delight's cutting board can be used on Hearty Grains to obtain rice and straw, similarly to Farmer's Delight's Rice Panicles.
- Farmer's Delight's Apple Pies and Apple Ciders can now be made using Applin Apples, alongside the Vanilla counterpart.
- Amendments allows for cooking Leek and Potato Stew in a Cauldron.
- Any mods that utilise Moonlight's Soft Fluid system can store Leek and Potato Stew, Medicinal Brew, Antidote, Awakening, Burn Heal, Ice Heal, Paralyze Heal, Full Heal, Potion, Super Potion, Hyper Potion, Max Potion, Full Restore, Ability Capsules, Moomoo Milk, and Berry Juice in liquid form (such as Suplementaries' Jar or Goblet, or Cauldrons while using Amendments).
- Supplementaries' Hourglass can now use Bright Powder, Quick Powder, Metal Powder, Heal Powder, Silver Powder, and Remedy.
- Berry Trees are now compatible with the Planter properly
- Saccharine Way Signs (added by Supplementaries automagically) now use a premade texture to prevent odd visual issues.
- Supplementaries' Jar can now store various Cobblemon cookies, candies, and similar items.
- Some of Supplementaries' items have been added to the `#cobblemon:held/container_held_items` item tag, including sacks, safes, and lunch baskets.
- Experience Nuggets can be used in place of Sculk for making the EXP Candies if using Create.
- Rage Candy Bar has a Create Mixing recipe.
- All Cobblemon ores can now be crushed in Create's Crushing Wheels. All Gem and Tumblestone Clusters can be crushed for automatic, low return, gems.
- Hearty Grains can be milled like Farmer's Delight Rice Panicles if both Farmer's Delight and Create are installed.
- Big Roots and Revival Herbs can be milled to their respective powders.
- Pep-Up Flowers, Medicinal Leeks, and all 70 berries can be milled in Create's Millstone for additional dye drops.
- Auspicious Armour and Upgrades can be Haunted using Create's crafting systems.
- Applin Apples can be used to make Honeyed Apples in Create.
- Sounds for interacting with various Cobblemon blocks now play from the centre of the block, rather than the corner. You probably didn't notice.
- Dragonite's mailbag cosmetic can now also be obtained with a chest in the cosmetic slot.
- Adjusted the walk speed for Croagunk and Aerodactyl.
- The `/pc` command no longer plays PC activation sounds
- Pressing the Summary key while either the starter selection or summary screen are open now closes those screens.
- Ice type Pokémon are now immune to Packed Snow.
- Added cobweb block slowness immunity for spider Pokémon
### Fixes
- Fixed rare Pokémon being far too likely to spawn multiple times in a single spawn cycle. The dev told me that the exact bug is too complicated to explain.
- Fixed Omanyte and Omastar incorrectly using water animations on land.
- Fixed Pokédex layout for genderless variations.
- Fixed odd stacking behaviour with Poké Baits.
- Fixed a compatibility issue with World Game Rules which caused some log spam in specific cases.
- Fixed improper validation of Pokémon when performing a trade.
- Fixed an issue where particles like beams can disappear when you don't see the beginning of the beam, or you look away.
- Fixed Bedrock particle materials sometimes being incorrectly invisible from some sides.
- Fixed an occasional crash on retrieving revived fossils.
- Fixed brushing not working on Neoforge. Again.
- Fixed braking not working for submarine-style mounts (whoops).
- Fixed strafing not having the same friction as forward movement (funny whoops).
- Fixed submarine-style mounts' surface-water animations not playing when supposed to.
- Fixed submarine-style mounts' camera freaking out when looking up or down on the surface.
- Fixed pastured Pokémon despawning and running away.
- Fixed Apricorn trees crashing when other mods bonemeal them.
- Fixed the issue that caused hover-style mounts without a ground style to be rendered useless upon touching the ground on a server.
- Fixed horizontal and vertical collisions with hover-style mounts so they no longer accumulate or retain momentum.
- Fixed NeoForge brewing stand unexpectedly crashing users when shift clicking items within its menu. Well, I suppose all crashes are unexpected. You know what I mean.
- Fixed NPCs switching Pokémon very often during battles.
- Fixed Campfire Pot causing crashes under certain circumstances.
- Fixed Type Gems not being consumed upon use in battles.
- Fixed several Ancient Pokéball's not using the correct capture multiplier.
- Fixed a server warning that could occur when recalling Pokémon.
- Fixed sliding particles incorrectly handling block collision.
- Fixed Full Heal not curing status conditions in battle. Literally it's one job.
- Fixed a rare issue with battles locking up when you are forced to switch, with the switch menu appearing instantly.
- Fixed the combined effects of Pledge moves having broken Showdown message handling and localisation.
- Fixed a very niche issue where structures would spawn in an incorrect location, creating very funky world gen.
- Fixed Galarian Ponyta missing its cry.
- Fixed various log warnings caused by redundant files and sound events.
- Traded Pokémon will now retain the original friendship level when traded back to its OT.
- Tumblestone Clusters now display correctly on maps.
- Fixed removing mulch from berry trees with a shovel not working. That was meant to be a thing.
- Fixed collecting honey from Saccharine Leaf blocks deleting Honey Bottles when the inventory was full.
- Fixed Stripped Saccharine Wood recipe being... not there.
- Fixed some performance issues with a kind of sound event causing lag by attempting to create sounds every tick when out of range of hearing.
- Fixed stat-reducing Aprijuice not being possible to apply on a maxed out Pokémon. Also fixed the possibility of going into negatives.
- Fixed exploit where you could take out the bait or drop your Pokérod with bait to avoid consuming bait, now the bobber will just disappear if you do so.
- Fixed Apricorn and Saccharine boats incorrectly using chest boat models.
- Fixed wild and trainer battle AI refusing to select moves with the `adjacentAlly` target type, even when it's their only option.
- Fixed gilded chests being more accessible than intended.
- Fixed fished up Pokémon not being added as "seen" to the Pokédex.
- Fixed surprised Gimmighoul encounters not being added as "seen" to the Pokédex.
- Fixed a crash involving berry trees that have been world edited.
- Fixed visual desync in multiplayer for players mounting Pokémon outside other players' loaded chunks.
- Fixed Pokémon entities leveled by rare candy not getting stronger as Minecraft entities unless recalled.
- Fixed a freeze during game load that occurred when multiple mods tried to load Cobblemon API classes at the same time.
- Lowered the volume of Blitzle and Zebstrika's cry (shut up!).
- Regional Foods will now prioritise being eaten instead of used for healing Pokémon - shift clicking will use it as healing.
- Resolved z-fighting (texture flickering) on Pokédex Models.
- Restored Ninjask's ability to do barrel rolls.
- Fixed Raichu (male) and Alolan Raichu (both genders) not displaying the correct Partner Cap when Pewter Crunchies are in the cosmetic slot.
- Fixed a crash caused by adding enchanted items to the Resurrection Machine.
- Fixed a crash that could occur when starting a PvN battle with only fainted Pokémon.
- Fixed the Remember Riding Camera config not being respected when dismounting and resolved the incompatibility with the Auto Third Person mod.
- Fixed Full Heal block not dropping Full Heals. Lmao.
- Fixed status cure items not returning an empty bottle when used.
- Fixed Panpour consuming its held item when evolving with a Water Stone.
- Fixed Smoked Tail Curry returning a Wooden Bowl when consumed despite it not being included in the recipe. Free bowls!
- Fixed `/giveallpokemon` not respecting the min and max arguments.
- Fixed the choose starter toast not disappearing when capturing a Pokémon instead of opening the starter selection screen.
- Fixed an issue where certain Sinister Tea seasonings were only applying as instant effects and would not apply over time.
- Long config option labels now scroll instead of overflowing in the configuration menu.
- Fixed some z-fighting and clipping on Dewott and Hisui-Bias Dewott.
- Eggant Berries can now be composted properly on NeoForge.
- Composting chances are now the same on NeoForge as on Fabric.
- Fixed problem where furnaces which had smelted Revival Herbs into Heal Powder in older versions would glitch out on trying to get the experience out of the block.
- Fixed the Battle AI not recognizing Surging Strikes as a multistrike move.
- Fixed Tentacool not using its swim animation when on water surface.
- Fixed Eevee using its left shoulder pose when in battle.
- Fixed Magikarp and Gyarados Jump Pattern resourcepacks replacing shiny textures of addons targeting Magikarp or Gyarados.
- Fixed Combee's pollinated texture overwriting the shiny and female textures.
- Fixed a colour mistake with Dracovish's shiny.
- Fixed Togedemaru's back spikes z-fighting.
- Fixed Sliggoo (+ Hisuian) evolving in the Nether and End where it can't actually rain.
- Corrected the scales, hitbox sizes, and eye heights for Ariados, Cetoddle, Durant, Galvantula, Klink, Lanturn, Lumineon, Mantine, Mantyke, Mudbray, Numel, Staravia, and Torkoal.
- Fixed Torkoal's walk speed not matching its animation. He will no longer move faster than Bunnelby and Buneary. This tortoise would've won.
- Fixed Lanturn T-posing due to missing ground walk and sleep animations.
- Fixed Oshawott and Dewott using their sleep animation in unintended scenarios.
- Fixed Dudunsparce and Maushold always having the same form instead of having the 1 in 100 chance of being different.
- Fixed bird mounts rapidly switching between their flying and gliding animations when flying at low speed.
- Fixed some battles being startable with fainted Pokémon in the initial sendout.
- Fixed random gender selection for forms with strict gender divides (such as Meowstic and Oinkologne) never choosing female (that's sexist).
- Fixed the "Press R to battle" prompt not disappearing after you finish your first battle, at least until you relogged.
- Fixed the composter block being listed as "composer" in the cosmetic items species feature
- Fixed Castform's forms being tagged as introduced in generation 4 instead of 3.
- Fixed Blitzle's base scale being `1` rather than the intended `0.8`.
- Fixed Magnezone's emissive layer for the shiny having the wrong name, thus displaying wrongly.
- Fixed Litleo not blinking.
### Developer
- Added `PokeSnackSpawnerFactory` which allows influence registration on PokeSnack encounters.
- Added `FishingSpawnerFactory` which allows influence registration on fishing encounters.
- Added `BattleFleeAttemptEvent` fired from `PokemonBattle.checkFleeAttempt`, allowing developers to intercept and control flee attempts.
- Tweaked the spawning selector interface to take a bucket function rather than a bucket directly.
- Added a new `cobblemon:height_range` processor type to allow developers to restrict structure-spawning ranges.
- Separated all the functions of MoLangFunctions.kt into their own files.
- Spawn buckets and their base weights are now provided by the spawner, with different base weights for world, fishing, snack, and habitat spawn buckets.
- Removed "SpawnBucket" class in favour of just storing maps of bucket names to weights.
- Fixed nicknames not being applied to Pokémon spawned from `spawn_pool_world` files.
- Added support for `profilePokedexScale`, `profilePokedexTranslation`, `profileSummaryScale`, and `profileSummaryTranslation` in poser files for finer control over profile displays.
- Fixed riding camera offsets defined on Pokémon forms not being applied.
- Marks can now provide aspects.
### Molang & Datapacks
- Added `moveset_builders` datapack folder for different styles of movesets.
- Added `party_pools` datapack folder for defining groups of Pokémon for complex NPC party definitions.
- Added `party_compositions` datapack folder for arranging parties on NPCs that rely on party pools.
- Added `composed_pool` NPC party provider type which combines a party pool with a party composition to create more complex NPC parties.
- Added basic chatter NPC Behaviour.
- Added `q.item.is_enchanted()`.
- Added `q.item.has_enchantment(minecraft:sharpness, 3)`.
- Added `has_chosen_starter` and `get_starter_uuid` as available Molang functions.
- Added `has_tm_move_unlocked`, `unlock_tm_move`, and `lock_tm_move` as available Molang functions for Players.
- Added `marks`, `has_mark`, and `remove_marks` as available Molang functions for Pokémon.
- Added Data Component support for species drops, eg `"components": {"minecraft:custom_model_data": 123}`
- Added the new `sortOrder` field to mark entries, (defaults to 0 if not specified) which controls the order in which marks are rendered on the HUD, with higher numbers being rendered on top of lower numbers.
- Added optional `order` field to starter categories. It will allow explicitly sorting categories in the starter selection screen.
- SpeciesAdditions append vs override now matches the wiki documentation.
- Added `weighted_choice` as a new feature type. It requires setting weights for each choice and allows developers to simplify the process of assigning random aspect choice based on marble chance.
- Added `q.world.spawn_loot_table_items(loot_table_id, x, y, z)` which spawns the items from the loot table at the position. Use type: `minecraft:chest` for the loot table.
- Added `q.pokemon.entity` to access the entity of Pokémon.
- Flamethrower and Seismic Toss had their action effect JSON cleaned up.
- Ride sounds can now be set as exclusive to passengers.
- Ride sounds no longer play when submerged unless specified with a new setting.
- NPC Trainers can now call a `command` animation when issuing a command in battle.
- Added a q.failed function to move action effects.
- Herd spawned Pokémon have their levels controlled differently. The `levelRange` property now acts as a pre-offset clamp instead of a filter, with a new `herdLevelRange` property to do the filter behaviour.
- `q.sound` now accepts three parameters (sound event, volume, and pitch) instead of just the sound event and pitch.
- Added a new layer property that makes a layer scroll in a direction. "scrolling": {"speedU": 0.1, "speedV": 0.1}
- Cleaned up seat data. Removed all now unused fields from seats in all species files. Also made locator seat names explicit in the species files (however, this doesn't break the legacy format)
### Particles
#### New Move Animations
- Blizzard
- Disarming Voice
- Draco Meteor
- Dragon Breath
- Dragon Rage
- Drain Punch
- Fairy Wind
- Fire Fang
- Gust
- Horn Drill
- Hydro Pump
- Ice Fang
- Leafage
- Leaf Storm
- Light Screen
- Night Shade
- Play Rough
- Pyro Ball
- Reflect
- Rock Slide
- Sonic Boom
- Sucker Punch
- Supersonic
- Thunder Fang
#### Tweaks and Fixes (Moves)
- Confusion
- Dragon Claw
- Eruption
- Fire Punch
- Flame Burst
- Flame Charge
- Flamethrower
- Frost Breath
- Hypnosis
- Ice Punch
- Icy Wind
- Mud Sport
- Poison Fang
- Protect
- Psychic
- Quick Attack
- Rage Powder
- Razor Leaf
- Sand Attack
- Scratch
- Shadow Claw
- Sing
- Spore
- String Shot
- Tackle
- Tail Whip
- Thunder
- Thunder Punch
- Thunder Wave
- Water Gun
- Water Pulse
- Water Sport
#### Miscellaneous Particles
- Added Wild Might particles for Alpha Pokémon.
- Updated stat increase and decrease particles.
### Localization
- Updated translations for:
- Japanese
- German
- Spanish
- Ecuadorian Spanish
- Portuguese
- Brazilian Portuguese
- Simplified Chinese
- Traditional Chinese
- Korean
- Russian
@@ -0,0 +1,24 @@
/*
* 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;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.Level;
import java.util.Optional;
/**
* Duck that facilitates getting a level key for a given level.
*
* @author dualspiral
* @since August 30th, 2026
*/
public interface MinecraftServerDuck {
Optional<ResourceKey<Level>> cobblemon$getLevelKey(Level level);
}
@@ -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.duck;
import net.minecraft.resources.ResourceLocation;
/**
* Duck that facilitates getting a level key that we store on the level.
*
* @author dualspiral
* @since August 30th, 2026
*/
public interface ServerLevelDuck {
ResourceLocation cobblemon$getLevelResourceLocation();
}
@@ -1,53 +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.mixin;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.network.protocol.game.ClientboundSetPassengersPacket;
import net.minecraft.world.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.ArrayList;
import java.util.Objects;
@Mixin(ClientPacketListener.class)
public class ClientPacketListenerMixin {
@Shadow private ClientLevel level;
@Inject(method = "handleSetEntityPassengersPacket", at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/world/entity/Entity;hasIndirectPassenger(Lnet/minecraft/world/entity/Entity;)Z", shift = At.Shift.BEFORE), cancellable = true)
private void cobblemon$handlePokemonPassengers(ClientboundSetPassengersPacket packet, CallbackInfo ci) {
Entity entity = this.level.getEntity(packet.getVehicle());
if (entity instanceof PokemonEntity pokemon) {
var passengers = new ArrayList<Entity>();
for (int passenger : packet.getPassengers()) {
passengers.add(this.level.getEntity(passenger));
}
pokemon.getOccupiedSeats().values().stream()
.filter(x -> !passengers.contains(x))
.filter(Objects::nonNull)
.forEach(Entity::stopRiding);
// Mount passengers not yet in the map
var currentPassengers = new ArrayList<>(pokemon.getOccupiedSeats().values());
passengers.stream()
.filter(x -> !currentPassengers.contains(x))
.filter(Objects::nonNull)
.forEach(x -> x.startRiding(pokemon, true));
ci.cancel();
}
}
}
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.CobblemonNetwork;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.api.orientation.OrientationController;
import com.cobblemon.mod.common.api.pokemon.effect.ShoulderEffectRegistry;
@@ -18,7 +17,6 @@ import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.SubmarineBehav
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate;
import com.cobblemon.mod.common.client.render.MatrixWrapper;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.net.messages.server.orientation.ServerboundUpdateOrientationPacket;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.server.level.ServerPlayer;
@@ -0,0 +1,40 @@
/*
* 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;
import com.cobblemon.mod.common.duck.MinecraftServerDuck;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.MinecraftServer;
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 java.util.Map;
import java.util.Optional;
@Mixin(MinecraftServer.class)
public class MinecraftServerMixin implements MinecraftServerDuck {
@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();
}
}
@@ -40,6 +40,7 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.List;
import java.util.LinkedHashMap;
import java.util.Set;
import java.util.function.Consumer;
@@ -114,15 +115,13 @@ public abstract class ServerEntityMixin {
if (currentPassengers.equals(cobblemon$lastSeatPassengers)) return;
cobblemon$lastSeatPassengers = List.copyOf(currentPassengers);
var assignments = new LinkedHashMap<String, Integer>();
pokemon.getOccupiedSeats().forEach((seat, passenger) -> {
if (seat.getLocator() == null) return;
if (passenger == null) return;
cobblemon$broadcast(new ClientboundSeatAssignmentPacket(
passenger.getId(),
pokemon.getId(),
seat.getLocator()
));
assignments.put(seat.getLocator(), passenger.getId());
});
cobblemon$broadcast(new ClientboundSeatAssignmentPacket(pokemon.getId(), assignments));
}
private void cobblemon$broadcast(NetworkPacket<?> packet) {
@@ -8,22 +8,69 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.duck.MinecraftServerDuck;
import com.cobblemon.mod.common.duck.ServerLevelDuck;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.core.RegistryAccess;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.storage.WritableLevelData;
import net.minecraft.world.phys.AABB;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
@Mixin(ServerLevel.class)
public abstract class ServerLevelMixin {
public abstract class ServerLevelMixin extends Level implements ServerLevelDuck {
// Ignored by Mixin, fulfils Java contract
protected ServerLevelMixin(
WritableLevelData levelData,
ResourceKey<Level> dimension,
RegistryAccess registryAccess,
Holder<DimensionType> dimensionTypeRegistration,
Supplier<ProfilerFiller> profiler,
boolean isClientSide,
boolean isDebug,
long biomeZoomSeed,
int maxChainedNeighborUpdates
) {
super(levelData, dimension, registryAccess, dimensionTypeRegistration, profiler, isClientSide, isDebug, biomeZoomSeed, maxChainedNeighborUpdates);
}
@Shadow
public abstract MinecraftServer shadow$getServer();
/**
* Cached to speed up level world key checks every tick.
*/
private ResourceLocation cobblemon$levelResourceLocation;
@Override
public ResourceLocation cobblemon$getLevelResourceLocation() {
if (this.cobblemon$levelResourceLocation == null) {
this.cobblemon$levelResourceLocation = ((MinecraftServerDuck) this.shadow$getServer())
.cobblemon$getLevelKey(this)
.map(ResourceKey::location)
.orElse(null);
}
return this.cobblemon$levelResourceLocation;
}
@Inject(method = "findLightningRod", at = @At(value = "RETURN"), cancellable = true)
private void cobblemon$findLightningRod(BlockPos blockPos, CallbackInfoReturnable<Optional<BlockPos>> ci) {
@@ -15,15 +15,18 @@ import com.cobblemon.mod.common.api.riding.RidingStyle;
import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawner;
import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawnerFactory;
import com.cobblemon.mod.common.api.stats.CobblemonStats;
import com.cobblemon.mod.common.duck.ServerLevelDuck;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
@@ -33,9 +36,13 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ServerPlayer.class)
public abstract class ServerPlayerMixin implements PlayerSpawnerAccessor {
@Shadow
public abstract ServerLevel serverLevel();
@Shadow
@Final
public MinecraftServer server;
@Unique
public PlayerSpawner cobblemon$spawner;
@@ -55,12 +62,9 @@ public abstract class ServerPlayerMixin implements PlayerSpawnerAccessor {
@Inject(method = "tick", at = @At("TAIL"))
private void cobblemon$tickSpawner(CallbackInfo ci) {
if (!Cobblemon.config.getEnableSpawning()) {
return;
} else if (!serverLevel().getGameRules().getBoolean(CobblemonGameRules.DO_POKEMON_SPAWNING)) {
return;
if (cobblemon$canSpawn()) {
getPlayerSpawner().tick();
}
getPlayerSpawner().tick();
}
@Inject(method = "rideTick", at = @At("HEAD"))
@@ -121,4 +125,21 @@ public abstract class ServerPlayerMixin implements PlayerSpawnerAccessor {
return entity;
}
/**
* Gets whether we should spawn Cobblemon entities here.
*
* @return true if we can spawn cobblemon in the current level,
* false otherwise
*/
@SuppressWarnings("resource")
@Unique
private boolean cobblemon$canSpawn() {
return Cobblemon.config.getEnableSpawning() &&
this.serverLevel().getGameRules().getBoolean(CobblemonGameRules.DO_POKEMON_SPAWNING) &&
// intentionally last as this is a more expensive check, even with all the caching
!Cobblemon.config.getWorldSpawningBlocklist().contains(
((ServerLevelDuck) this.serverLevel()).cobblemon$getLevelResourceLocation()
);
}
}
@@ -0,0 +1,103 @@
/*
* 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;
import com.cobblemon.mod.common.mixin.accessor.SinglePoolElementAccessor;
import com.cobblemon.mod.common.world.structureprocessors.HeightRangeStructureProcessor;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.core.Holder;
import net.minecraft.core.RegistryAccess;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.LevelHeightAccessor;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.levelgen.RandomState;
import net.minecraft.world.level.levelgen.structure.PoolElementStructurePiece;
import net.minecraft.world.level.levelgen.structure.Structure;
import net.minecraft.world.level.levelgen.structure.StructurePiece;
import net.minecraft.world.level.levelgen.structure.StructureStart;
import net.minecraft.world.level.levelgen.structure.pools.SinglePoolElement;
import net.minecraft.world.level.levelgen.structure.pools.StructurePoolElement;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessor;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorList;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.Optional;
import java.util.function.Predicate;
@Mixin(Structure.class)
public abstract class StructureMixin {
/**
* Discards a structure start when any of its pieces carries a {@link HeightRangeStructureProcessor}
* whose range the piece does not fit in.
* Crab Note: We have to make this has to happen here rather than in the processor itself. Terrain adaptation and {@code /locate}
* both read the start, so by the time processors run the terrain has already been generated and the
* structure is already on the map which previously were left behdind when we were trying to make some structures not generate via processor. Now
* Returning {@link StructureStart#INVALID_START} keeps the start from ever existing, which makes sure that weird generation isn't left behind.
*/
@Inject(
method = "generate",
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/levelgen/structure/StructureStart;isValid()Z"),
cancellable = true
)
private void cobblemon$rejectStartsOutsideHeightRange(
RegistryAccess registryAccess, ChunkGenerator chunkGenerator, BiomeSource biomeSource,
RandomState randomState, StructureTemplateManager structureTemplateManager, long seed,
ChunkPos chunkPos, int references, LevelHeightAccessor heightAccessor,
Predicate<Holder<Biome>> validBiome,
CallbackInfoReturnable<StructureStart> cir,
@Local StructureStart structureStart
) {
for (StructurePiece piece : structureStart.getPieces()) {
if (!(piece instanceof PoolElementStructurePiece poolPiece)) {
continue;
}
Optional<HeightRangeStructureProcessor> heightRangeProcessor = cobblemon$getHeightRangeProcessor(poolPiece.getElement());
if (heightRangeProcessor.isEmpty()) {
continue;
}
boolean valid = heightRangeProcessor.get().isValidPlacement(
chunkGenerator,
heightAccessor,
randomState,
piece.getBoundingBox()
);
if (!valid) {
cir.setReturnValue(StructureStart.INVALID_START);
return;
}
}
}
@Unique
private static Optional<HeightRangeStructureProcessor> cobblemon$getHeightRangeProcessor(StructurePoolElement element) {
if (!(element instanceof SinglePoolElement)) {
return Optional.empty();
}
StructureProcessorList processorList = ((SinglePoolElementAccessor) element).getProcessors().value();
for (StructureProcessor processor : processorList.list()) {
if (processor instanceof HeightRangeStructureProcessor heightRangeStructureProcessor) {
return Optional.of(heightRangeStructureProcessor);
}
}
return Optional.empty();
}
}
@@ -0,0 +1,22 @@
/*
* 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.accessor;
import net.minecraft.core.Holder;
import net.minecraft.world.level.levelgen.structure.pools.SinglePoolElement;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorList;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(SinglePoolElement.class)
public interface SinglePoolElementAccessor {
@Accessor("processors")
Holder<StructureProcessorList> getProcessors();
}
@@ -20,7 +20,7 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
//This is a yucky hack but better than what we were doing before
// There appears to be a data driven way to do this now, however I am not sure how it works since there is no wiki or anything so yolo
@Mixin(BlockVariantSets.class)
public abstract class AdornRegisterInvoker {
@Final
@@ -108,6 +108,7 @@ import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.pokemon.properties.AspectPropertyType
import com.cobblemon.mod.common.pokemon.properties.BattleCloneProperty
import com.cobblemon.mod.common.pokemon.properties.FreezeFrameProperty
import com.cobblemon.mod.common.pokemon.properties.HeldItemVisibleProperty
import com.cobblemon.mod.common.pokemon.properties.HiddenAbilityPropertyType
import com.cobblemon.mod.common.pokemon.properties.LabelProperty
import com.cobblemon.mod.common.pokemon.properties.NoAIProperty
@@ -304,7 +305,9 @@ object Cobblemon {
this.config.maxPokemonLevel,
this.config.maxPokemonFriendship,
this.config.maxDynamaxLevel,
this.config.unlockAllMoveDexMovesByDefault
this.config.unlockAllMoveDexMovesByDefault,
this.config.pokemonIntrinsicSizeMin,
this.config.pokemonIntrinsicSizeMax
).sendToPlayer(player)
}
@@ -345,6 +348,7 @@ object Cobblemon {
CustomPokemonProperty.register(UnaspectPropertyType)
CustomPokemonProperty.register(FreezeFrameProperty)
CustomPokemonProperty.register(NoAIProperty)
CustomPokemonProperty.register(HeldItemVisibleProperty)
CobblemonEvents.POKEMON_PROPERTY_INITIALISED.emit(Unit)
@@ -41,6 +41,8 @@ object CobblemonCommands {
HeldItemCommand.register(dispatcher, registry)
PcCommand.register(dispatcher)
SpawnPokemonFromPool.register(dispatcher)
SpawnMeteorCommand.register(dispatcher)
SetMegaBondCommand.register(dispatcher)
PokeboxCommand.register(dispatcher)
PokedexCommand.register(dispatcher)
TestStoreCommand.register(dispatcher)
@@ -16,6 +16,7 @@ import com.cobblemon.mod.common.entity.generic.GenericBedrockEntity
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.projectile.FallingStarEntity
import com.cobblemon.mod.common.platform.PlatformRegistry
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.core.Registry
@@ -95,6 +96,15 @@ object CobblemonEntities : PlatformRegistry<Registry<EntityType<*>>, ResourceKey
EntityType.Builder.of({ _, world -> NPCEntity(world) }, MobCategory.CREATURE).build("$NPC_KEY")
)
@JvmField
val FALLING_STAR_KEY = cobblemonResource("falling_star")
@JvmField
val FALLING_STAR: EntityType<FallingStarEntity> = create(
FALLING_STAR_KEY.path,
EntityType.Builder.of(::FallingStarEntity, MobCategory.MISC).sized(0.5F, 0.5F).clientTrackingRange(8)
.build(FALLING_STAR_KEY.toString())
)
fun registerAttributes(consumer: (EntityType<out LivingEntity>, AttributeSupplier.Builder) -> Unit) {
consumer(POKEMON, PokemonEntity.createAttributes())
consumer(NPC, NPCEntity.createAttributes())
@@ -33,6 +33,12 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
.networkSynchronized(HeldItemEffectComponent.PACKET_CODEC)
.build())
@JvmField
val MEGA_BOND: DataComponentType<MegaBondComponent> = create("mega_bond", DataComponentType.builder<MegaBondComponent>()
.persistent(MegaBondComponent.CODEC)
.networkSynchronized(MegaBondComponent.PACKET_CODEC)
.build())
@JvmField
val BAIT: DataComponentType<RodBaitComponent> = create("bait", DataComponentType.builder<RodBaitComponent>()
.persistent(RodBaitComponent.CODEC)
@@ -405,7 +405,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val BIG_MALASADA = regionalFoodItem("big_malasada", 64, 7, 0.3f, false)
@JvmField
val SMOKED_TAIL_CURRY = regionalFoodItem("smoked_tail_curry", 64, 10, 0.6f, false, ItemStack(Items.BOWL, 1))
val SMOKED_TAIL_CURRY = regionalFoodItem("smoked_tail_curry", 64, 10, 0.6f, false)
@JvmField
val JUBILIFE_MUFFIN = regionalFoodItem("jubilife_muffin", 64, 7, 0.3f, false)
@JvmField
@@ -914,6 +914,10 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
val THUNDER_STONE = noSettingsItem("thunder_stone")
@JvmField
val WATER_STONE = noSettingsItem("water_stone")
@JvmField
val MEGA_SHARD = noSettingsItem("mega_shard")
@JvmField
val DORMANT_MEGA_STONE = heldItem("dormant_mega_stone")
val wearables = mutableListOf<WearableItem>()
//Wearable items (these items should have a corresponding 3D model)
@@ -932,14 +936,34 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val ABILITY_SHIELD = heldItem("ability_shield")
@JvmField
val ABOMASITE = heldItem("abomasite")
@JvmField
val ABSOLITE = heldItem("absolite")
@JvmField
val ABSORB_BULB = heldItem("absorb_bulb")
@JvmField
val AERODACTYLITE = heldItem("aerodactylite")
@JvmField
val AGGRONITE = heldItem("aggronite")
@JvmField
val AIR_BALLOON = heldItem("air_balloon")
@JvmField
val ALAKAZITE = heldItem("alakazite")
@JvmField
val ALTARIANITE = heldItem("altarianite")
@JvmField
val AMPHAROSITE = heldItem("ampharosite")
@JvmField
val ASSAULT_VEST = heldItem("assault_vest")
@JvmField
val BIG_ROOT = compostableBlockItem("big_root", CobblemonBlocks.BIG_ROOT, 0.30f)
@JvmField
val AUDINITE = heldItem("audinite")
@JvmField
val BANETTITE = heldItem("banettite")
@JvmField
val BEEDRILLITE = heldItem("beedrillite")
@JvmField
val BINDING_BAND = heldItem("binding_band")
@JvmField
val BLACK_BELT = heldItem("black_belt")
@@ -948,10 +972,20 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val BLUNDER_POLICY = itemNameBlockItem("blunder_policy", CobblemonBlocks.BLUNDER_POLICY)
@JvmField
val BLASTOISINITE = heldItem("blastoisinite")
@JvmField
val BLAZIKENITE = heldItem("blazikenite")
@JvmField
val CAMERUPTITE = heldItem("cameruptite")
@JvmField
val CELL_BATTERY = heldItem("cell_battery")
@JvmField
val CHARCOAL = heldItem("charcoal_stick", remappedName = "charcoal")
@JvmField
val CHARIZARDITE_X = heldItem("charizardite_x")
@JvmField
val CHARIZARDITE_Y = heldItem("charizardite_y")
@JvmField
val CHOICE_SCARF = heldItem("choice_scarf")
@JvmField
val CLEANSE_TAG = itemNameBlockItem("cleanse_tag", CobblemonBlocks.CLEANSE_TAG)
@@ -962,6 +996,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val DESTINY_KNOT = heldItem("destiny_knot")
@JvmField
val DIANCITE = heldItem("diancite")
@JvmField
val DRAGON_FANG = heldItem("dragon_fang")
@JvmField
val EJECT_BUTTON = heldItem("eject_button")
@@ -984,20 +1020,42 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val FOCUS_SASH = heldItem("focus_sash")
@JvmField
val GALLADITE = heldItem("galladite")
@JvmField
val GARCHOMPITE = heldItem("garchompite")
@JvmField
val GARDEVOIRITE = heldItem("gardevoirite")
@JvmField
val GENGARITE = heldItem("gengarite")
@JvmField
val GLALITITE = heldItem("glalitite")
@JvmField
val GRASSY_SEED = heldItem("grassy_seed")
@JvmField
val GRIP_CLAW = heldItem("grip_claw")
@JvmField
val GYARADOSITE = heldItem("gyaradosite")
@JvmField
val HARD_STONE = heldItem("hard_stone")
@JvmField
val HEAVY_DUTY_BOOTS = heldItem("heavy_duty_boots")
@JvmField
val HERACRONITE = heldItem("heracronite")
@JvmField
val HOUNDOOMINITE = heldItem("houndoominite")
@JvmField
val IRON_BALL = heldItem("iron_ball")
@JvmField
val KANGASKHANITE = heldItem("kangaskhanite")
@JvmField
val LAGGING_TAIL = heldItem("lagging_tail")
@JvmField
val LEFTOVERS = compostableHeldItem("leftovers", null, 0.50f)
@JvmField
val LATIASITE = heldItem("latiasite")
@JvmField
val LATIOSITE = heldItem("latiosite")
@JvmField
val LIFE_ORB = heldItem("life_orb")
@JvmField
val LIGHT_BALL = heldItem("light_ball")
@@ -1006,20 +1064,40 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val LOADED_DICE = heldItem("loaded_dice")
@JvmField
val LOPUNNITE = heldItem("lopunnite")
@JvmField
val LUCARIONITE = heldItem("lucarionite")
@JvmField
val LUCKY_EGG = heldItem("lucky_egg")
@JvmField
val LUMINOUS_MOSS = heldItem("luminous_moss")
@JvmField
val MAGNET = heldItem("magnet")
@JvmField
val MANECTITE = heldItem("manectite")
@JvmField
val MAWILITE = heldItem("mawilite")
@JvmField
val MEDICHAMITE = heldItem("medichamite")
@JvmField
val METAGROSSITE = heldItem("metagrossite")
@JvmField
val METRONOME = heldItem("metronome")
@JvmField
val MEWTWONITE_X = heldItem("mewtwonite_x")
@JvmField
val MEWTWONITE_Y = heldItem("mewtwonite_y")
@JvmField
val MIRACLE_SEED = heldItem("miracle_seed")
@JvmField
val MYSTIC_WATER = heldItem("mystic_water")
@JvmField
val NEVER_MELT_ICE = heldItem("never_melt_ice")
@JvmField
val PIDGEOTITE = heldItem("pidgeotite")
@JvmField
val PINSIRITE = heldItem("pinsirite")
@JvmField
val POISON_BARB = heldItem("poison_barb")
@JvmField
val POWER_ANKLET = heldItem("power_anklet")
@@ -1048,10 +1126,20 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val ROOM_SERVICE = heldItem("room_service")
@JvmField
val SABLENITE = heldItem("sablenite")
@JvmField
val SALAMENCITE = heldItem("salamencite")
@JvmField
val SCEPTILITE = heldItem("sceptilite")
@JvmField
val SCIZORITE = heldItem("scizorite")
@JvmField
val SCOPE_LENS = heldItem("scope_lens")
@JvmField
val SHARP_BEAK = heldItem("sharp_beak")
@JvmField
val SHARPEDONITE = heldItem("sharpedonite")
@JvmField
val SHED_SHELL = heldItem("shed_shell")
@JvmField
val SHELL_BELL = heldItem("shell_bell")
@@ -1060,6 +1148,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val SILVER_POWDER = heldItem("silver_powder")
@JvmField
val SLOWBRONITE = heldItem("slowbronite")
@JvmField
val SOFT_SAND = heldItem("soft_sand")
@JvmField
val SPELL_TAG = itemNameBlockItem("spell_tag", CobblemonBlocks.SPELL_TAG)
@@ -1068,8 +1158,12 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val SOOTHE_BELL = heldItem("soothe_bell")
@JvmField
val STEELIXITE = heldItem("steelixite")
@JvmField
val STICKY_BARB = heldItem("sticky_barb")
@JvmField
val SWAMPERTITE = heldItem("swampertite")
@JvmField
val TERRAIN_EXTENDER = heldItem("terrain_extender")
@JvmField
val THROAT_SPRAY = heldItem("throat_spray")
@@ -1078,10 +1172,14 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val TWISTED_SPOON = heldItem("twisted_spoon")
@JvmField
val TYRANITARITE = heldItem("tyranitarite")
@JvmField
val UTILITY_UMBRELLA = heldItem("utility_umbrella")
@JvmField
val WEAKNESS_POLICY = itemNameBlockItem("weakness_policy", CobblemonBlocks.WEAKNESS_POLICY)
@JvmField
val VENUSAURITE = heldItem("venusaurite")
@JvmField
val WIDE_LENS = heldItem("wide_lens")
@JvmField
val ZOOM_LENS = heldItem("zoom_lens")
@@ -1756,4 +1854,4 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
private fun foodItem(nutrition: Int, saturationModifier: Float): Item {
return CobblemonItem(Item.Properties().food(FoodProperties.Builder().nutrition(nutrition).saturationModifier(saturationModifier).build()))
}
}
}
@@ -35,6 +35,7 @@ import com.cobblemon.mod.common.client.net.gui.PokedexUIPacketHandler
import com.cobblemon.mod.common.client.net.gui.SetActiveTMPacketHandler
import com.cobblemon.mod.common.client.net.gui.SetTMMachineContainerDataPacketHandler
import com.cobblemon.mod.common.client.net.gui.SummaryUIPacketHandler
import com.cobblemon.mod.common.client.net.gui.SynchronizeActiveTMHandler
import com.cobblemon.mod.common.client.net.habitat.OpenHabitatBlockEditorHandler
import com.cobblemon.mod.common.client.net.npc.CloseNPCEditorHandler
import com.cobblemon.mod.common.client.net.npc.OpenNPCEditorHandler
@@ -113,6 +114,7 @@ import com.cobblemon.mod.common.net.messages.client.settings.OpenCobblemonConfig
import com.cobblemon.mod.common.net.messages.client.settings.ServerSettingsPacket
import com.cobblemon.mod.common.net.messages.client.sound.UnvalidatedPlaySoundS2CPacket
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnGenericBedrockPacket
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnFallingStarPacket
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnNPCPacket
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnPokeballPacket
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnPokemonPacket
@@ -134,6 +136,7 @@ import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.ChangeP
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.RequestPCBoxWallpapersPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.SetPCBoxWallpapersPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.UnlockPCBoxWallpaperPacket
import com.cobblemon.mod.common.net.messages.client.tms.TechnicalMachineRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.toast.ToastPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeAcceptanceChangedPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeCancelledPacket
@@ -149,6 +152,7 @@ import com.cobblemon.mod.common.net.messages.client.ui.PokedexUIPacket
import com.cobblemon.mod.common.net.messages.client.ui.SetActiveTMPacket
import com.cobblemon.mod.common.net.messages.client.ui.SetTMMachineContainerDataPacket
import com.cobblemon.mod.common.net.messages.client.ui.SummaryUIPacket
import com.cobblemon.mod.common.net.messages.client.ui.SynchronizeActiveTMPacket
import com.cobblemon.mod.common.net.messages.server.BattleChallengePacket
import com.cobblemon.mod.common.net.messages.server.BattleChallengeResponsePacket
import com.cobblemon.mod.common.net.messages.server.BenchMovePacket
@@ -381,6 +385,7 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(PlayerInteractOptionsPacket.ID, PlayerInteractOptionsPacket::decode, PlayerInteractOptionsHandler))
list.add(PacketRegisterInfo(PokedexUIPacket.ID, PokedexUIPacket::decode, PokedexUIPacketHandler))
list.add(PacketRegisterInfo(ExpGainedDataPacket.ID, ExpGainedDataPacket::decode, ExpGainedDataPacketHandler))
list.add(PacketRegisterInfo(SynchronizeActiveTMPacket.ID, SynchronizeActiveTMPacket::decode, SynchronizeActiveTMHandler))
// Starter packets
list.add(PacketRegisterInfo(OpenStarterUIPacket.ID, OpenStarterUIPacket::decode, StarterUIPacketHandler))
@@ -446,6 +451,7 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(BehaviourSyncPacket.ID, BehaviourSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(MarkRegistrySyncPacket.ID, MarkRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(RideSettingsSyncPacket.ID, RideSettingsSyncPacket::decode, RideSettingsSyncHandler))
list.add(PacketRegisterInfo(TechnicalMachineRegistrySyncPacket.ID, TechnicalMachineRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
// Effects
list.add(PacketRegisterInfo(SpawnSnowstormParticlePacket.ID, SpawnSnowstormParticlePacket::decode, SpawnSnowstormParticleHandler))
@@ -459,6 +465,7 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(SpawnPokemonPacket.ID, SpawnPokemonPacket::decode, SpawnExtraDataEntityHandler()))
list.add(PacketRegisterInfo(SpawnPokeballPacket.ID, SpawnPokeballPacket::decode, SpawnExtraDataEntityHandler()))
list.add(PacketRegisterInfo(SpawnGenericBedrockPacket.ID, SpawnGenericBedrockPacket::decode, SpawnExtraDataEntityHandler()))
list.add(PacketRegisterInfo(SpawnFallingStarPacket.ID, SpawnFallingStarPacket::decode, SpawnExtraDataEntityHandler()))
list.add(PacketRegisterInfo(SpawnNPCPacket.ID, SpawnNPCPacket::decode, SpawnExtraDataEntityHandler()))
list.add(PacketRegisterInfo(ToastPacket.ID, ToastPacket::decode, ToastPacketHandler))
@@ -133,7 +133,7 @@ open class PokemonBattle(
set(value) { field = value.coerceAtMost(Int.MAX_VALUE) } // go outside
/** The current duration of the battle in seconds. */
val time: Int get() = ticks % 20
val time: Int get() = ticks / 20
var dispatchResult = GO
val dispatches = ConcurrentLinkedDeque<BattleDispatch>()
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.api.cooking.effects
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.effect.MobEffect
import net.minecraft.world.effect.MobEffectCategory
import net.minecraft.world.entity.Entity
@@ -18,5 +19,13 @@ class CleanseAllEffect : MobEffect(MobEffectCategory.BENEFICIAL, 0xFFFFFF) {
override fun applyInstantenousEffect(source: Entity?, indirect: Entity?, target: LivingEntity, amplifier: Int, proximity: Double) {
target.removeAllEffects()
}
override fun shouldApplyEffectTickThisTick(duration: Int, amplifier: Int) = false
override fun applyEffectTick(livingEntity: LivingEntity, amplifier: Int): Boolean {
if (livingEntity is ServerPlayer){
livingEntity.removeAllEffects()
}
return true
}
override fun shouldApplyEffectTickThisTick(duration: Int, amplifier: Int) = true
}
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.api.cooking.effects
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.effect.MobEffect
import net.minecraft.world.effect.MobEffectCategory
import net.minecraft.world.entity.Entity
@@ -23,5 +24,18 @@ class CleanseNegativeEffect : MobEffect(MobEffectCategory.BENEFICIAL, 0xFFFFFF)
}
}
}
override fun shouldApplyEffectTickThisTick(duration: Int, amplifier: Int) = false
override fun applyEffectTick(livingEntity: LivingEntity, amplifier: Int): Boolean {
if (livingEntity is ServerPlayer){
val snapshot = livingEntity.activeEffects.toList()
for (ei in snapshot) {
if (ei.effect.value().category == MobEffectCategory.HARMFUL) {
livingEntity.removeEffect(ei.effect)
}
}
}
return true
}
override fun shouldApplyEffectTickThisTick(duration: Int, amplifier: Int) = true
}
@@ -21,7 +21,8 @@ import com.cobblemon.mod.common.pokemon.Pokemon
*/
data class FriendshipUpdatedEvent(
val pokemon: Pokemon,
val newFriendshipInitial: Int
val newFriendshipInitial: Int,
val earned: Boolean = false
) {
var newFriendship: Int = newFriendshipInitial
set(value) {
@@ -69,7 +69,7 @@ class HabitatPool {
pool.spawns = spawns
return pool
} else {
return HabitatPools.habitatPoolsById[poolId] ?: HabitatPool.default()
return HabitatPools.habitatPoolsById[poolId] ?: default()
}
}
}
@@ -94,6 +94,14 @@ class HabitatPool {
return spawns.mapNotNull { it.createSpawnDetail(habitatBlockEntity) }
}
fun getPhaseCount(): Int {
return spawns
.flatMap { it.phases?.ranges?.flatten() ?: emptySet() }
.toSet()
.count()
.coerceAtLeast(1)
}
fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(id)
buffer.writeUtf(name)
@@ -65,7 +65,7 @@ class HabitatSpawn {
/** The maximum light level that the Pokémon can spawn in. */
var maxLight: Int? = null
open fun writeToNBT(nbt: CompoundTag) {
fun writeToNBT(nbt: CompoundTag) {
nbt.putString(DataKeys.HABITAT_POOL_SPAWN_SPECIES, species.resourceIdentifier.toString())
nbt.putString(DataKeys.HABITAT_POOL_SPAWN_POSITION_TYPE, spawnablePositionType)
nbt.putFloat(DataKeys.HABITAT_POOL_SPAWN_WEIGHT, weight)
@@ -78,7 +78,7 @@ class HabitatSpawn {
nbt.putString(DataKeys.HABITAT_POOL_SPAWN_BUCKET, bucket)
}
open fun readFromNBT(nbt: CompoundTag) {
fun readFromNBT(nbt: CompoundTag) {
species = PokemonSpecies.getByIdentifier(nbt.getString(DataKeys.HABITAT_POOL_SPAWN_SPECIES).asIdentifierDefaultingNamespace())!!
spawnablePositionType = nbt.getString(DataKeys.HABITAT_POOL_SPAWN_POSITION_TYPE)
weight = nbt.getFloat(DataKeys.HABITAT_POOL_SPAWN_WEIGHT)
@@ -91,7 +91,7 @@ class HabitatSpawn {
bucket = if (nbt.contains(DataKeys.HABITAT_POOL_SPAWN_BUCKET)) nbt.getString(DataKeys.HABITAT_POOL_SPAWN_BUCKET) else "common"
}
open fun encode(buffer: RegistryFriendlyByteBuf) {
fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(species.resourceIdentifier)
buffer.writeUtf(spawnablePositionType)
buffer.writeFloat(weight)
@@ -104,7 +104,7 @@ class HabitatSpawn {
buffer.writeUtf(bucket)
}
open fun decode(buffer: RegistryFriendlyByteBuf) {
fun decode(buffer: RegistryFriendlyByteBuf) {
species = PokemonSpecies.getByIdentifier(buffer.readResourceLocation())!!
spawnablePositionType = buffer.readUtf()
weight = buffer.readFloat()
@@ -118,7 +118,7 @@ class HabitatSpawn {
}
/** Creates the [PokemonSpawnDetail] that will be used in the spawner. This is done per habitat block. */
open fun createSpawnDetail(habitatBlockEntity: HabitatBlockEntity): PokemonSpawnDetail? {
fun createSpawnDetail(habitatBlockEntity: HabitatBlockEntity): PokemonSpawnDetail? {
val spawnDetail = PokemonSpawnDetail()
// If the habitat block specifies a level range, it restricts how and if this spawn exists
@@ -17,7 +17,6 @@ import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
class HabitatSettingsDTO() {
var numberOfPhases = 1
var phaseOrder = HabitatPhaseOrder.SIMPLE
lateinit var levelRange: IntRange
var modifiers: String = ""
@@ -28,7 +27,6 @@ class HabitatSettingsDTO() {
var naturalSettings: NaturalHabitatSettingsDTO? = null
constructor(habitatBlockEntity: HabitatBlockEntity) : this() {
this.numberOfPhases = habitatBlockEntity.numberOfPhases
this.phaseOrder = habitatBlockEntity.phaseOrder
this.levelRange = habitatBlockEntity.levelRange
this.modifiers = habitatBlockEntity.modifiers.asString()
@@ -44,7 +42,6 @@ class HabitatSettingsDTO() {
}
fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeInt(numberOfPhases)
buffer.writeEnum(phaseOrder)
buffer.writeInt(levelRange.first)
buffer.writeInt(levelRange.last)
@@ -60,7 +57,6 @@ class HabitatSettingsDTO() {
}
fun decode(buffer: RegistryFriendlyByteBuf) {
this.numberOfPhases = buffer.readInt()
this.phaseOrder = buffer.readEnum(HabitatPhaseOrder::class.java)
val levelRangeStart = buffer.readInt()
val levelRangeEnd = buffer.readInt()
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.api.item
import com.cobblemon.mod.common.api.data.DataRegistry
import com.cobblemon.mod.common.api.data.ShowdownIdentifiable
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.battles.runner.ShowdownService
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
@@ -29,9 +30,11 @@ object HeldItems : DataRegistry {
// val heldItems = PrioritizedList<BagItemLike>()
internal val heldItemsScripts = mutableMapOf<String, String>() // showdown item ID to JavaScript
val showdownItems = hashSetOf<String>()
val megaStones = hashMapOf<String, String>()
override fun reload(manager: ResourceManager) {
heldItemsScripts.clear()
megaStones.clear()
ShowdownService.service.resetRegistryData("heldItem")
manager.listResources("held_items") { it.path.endsWith(".js") }.forEach { (identifier, resource) ->
resource.open().use { stream ->
@@ -48,6 +51,9 @@ object HeldItems : DataRegistry {
for (i in 0 until itemsJson.size()) {
val jsItem = itemsJson[i].asJsonObject
showdownItems += jsItem.get("id").asString
if (jsItem.has("megaEvolves")) {
megaStones[jsItem.get("id").asString] = ShowdownIdentifiable.REGEX.replace(jsItem.get("megaEvolves").asString.lowercase(), "")
}
}
CobblemonHeldItemManager.load()
@@ -69,25 +69,31 @@ import net.minecraft.world.phys.Vec3
* @since October 2nd, 2023
*/
object MoLangFunctions {
fun ItemStack.asMoLangValue(registryAccess: RegistryAccess) = ObjectValue(this)
.addStandardFunctions()
.addFunctions(ItemStackMoLangFunctions.attach(Pair(this, registryAccess)))
fun ItemStack.asMoLangValue(registryAccess: RegistryAccess) =
ObjectValue(this)
.addStandardFunctions()
.addFunctions(ItemStackMoLangFunctions.attach(Pair(this, registryAccess)))
fun Holder<Biome>.asBiomeMoLangValue() = asMoLangValue(Registries.BIOME)
.addFunctions(BiomeMoLangFunctions.attach(this))
fun Holder<Biome>.asBiomeMoLangValue() =
asMoLangValue(Registries.BIOME)
.addFunctions(BiomeMoLangFunctions.attach(this))
fun Holder<Level>.asWorldMoLangValue() = asMoLangValue(Registries.DIMENSION)
.addFunctions(WorldMoLangFunctions.attach(this))
fun Holder<Level>.asWorldMoLangValue() =
asMoLangValue(Registries.DIMENSION)
.addFunctions(WorldMoLangFunctions.attach(this))
fun Holder<Block>.asBlockMoLangValue() = asMoLangValue(Registries.BLOCK)
.addFunctions(BlockMoLangFunctions.attach(this))
fun Holder<Block>.asBlockMoLangValue() =
asMoLangValue(Registries.BLOCK)
.addFunctions(BlockMoLangFunctions.attach(this))
fun MinecraftServer.asMoLangValue() = ObjectValue(this)
.addStandardFunctions()
.addFunctions(ServerMoLangFunctions.attach(this))
fun MinecraftServer.asMoLangValue() =
ObjectValue(this)
.addStandardFunctions()
.addFunctions(ServerMoLangFunctions.attach(this))
fun Holder<DimensionType>.asDimensionTypeMoLangValue() = asMoLangValue(Registries.DIMENSION_TYPE)
.addFunctions(DimensionTypeMoLangFunctions.attach(this))
fun Holder<DimensionType>.asDimensionTypeMoLangValue() =
asMoLangValue(Registries.DIMENSION_TYPE)
.addFunctions(DimensionTypeMoLangFunctions.attach(this))
fun Player.asMoLangValue(): ObjectValue<Player> {
if (this is ServerPlayer && uuid in Cobblemon.serverPlayerStructs) {
@@ -99,10 +105,11 @@ object MoLangFunctions {
}
}
val value = ObjectValue(
obj = this,
stringify = { it.effectiveName().string }
)
val value =
ObjectValue(
obj = this,
stringify = { it.effectiveName().string },
)
value.addFunctions(EntityMoLangFunctions.attach(this))
value.addFunctions(LivingEntityMoLangFunctions.attach(this))
@@ -123,57 +130,63 @@ object MoLangFunctions {
}
fun PartyStore.asMoLangValue(): ObjectValue<PartyStore> {
val value = ObjectValue(
obj = this,
stringify = { it.toString() }
)
val value =
ObjectValue(
obj = this,
stringify = { it.toString() },
)
value.addFunctions(PokemonStoreMoLangFunctions.attach(this))
value.addFunctions(PartyMoLangFunctions.attach(this))
return value
}
fun DropEntry.asMoLangValue(): ObjectValue<DropEntry> {
val value = ObjectValue(
obj = this,
stringify = { it.toString() }
)
val value =
ObjectValue(
obj = this,
stringify = { it.toString() },
)
value.addFunctions(DropEntryMoLangFunctions.attach(this))
return value
}
fun PokemonProperties.asMoLangValue(): ObjectValue<PokemonProperties> {
val value = ObjectValue(
obj = this,
stringify = { it.asString() }
)
val value =
ObjectValue(
obj = this,
stringify = { it.asString() },
)
value.addFunctions(PokemonPropertiesMoLangFunctions.attach(this))
return value
}
fun Evolution.asMoLangValue(): ObjectValue<Evolution> {
val value = ObjectValue(
obj = this,
stringify = { it.toString() }
)
val value =
ObjectValue(
obj = this,
stringify = { it.toString() },
)
value.addFunctions(EvolutionMoLangFunctions.attach(this))
return value
}
fun PCStore.asMoLangValue(): ObjectValue<PCStore> {
val value = ObjectValue(
obj = this,
stringify = { it.toString() }
)
val value =
ObjectValue(
obj = this,
stringify = { it.toString() },
)
value.addFunctions(PokemonStoreMoLangFunctions.attach(this))
value.addFunctions(PCMoLangFunctions.attach(this))
return value
}
fun NPCEntity.asMoLangValue(): ObjectValue<NPCEntity> {
val value = ObjectValue(
obj = this,
stringify = { it.name.string }
)
val value =
ObjectValue(
obj = this,
stringify = { it.name.string },
)
value.addFunctions(EntityMoLangFunctions.attach(this))
value.addFunctions(LivingEntityMoLangFunctions.attach(this))
value.addFunctions(NPCMoLangFunctions.attach(this))
@@ -181,10 +194,11 @@ object MoLangFunctions {
}
fun PokemonEntity.asMoLangValue(): ObjectValue<PokemonEntity> {
val value = ObjectValue(
obj = this,
stringify = { it.pokemon.uuid.toString() }
)
val value =
ObjectValue(
obj = this,
stringify = { it.pokemon.uuid.toString() },
)
value.addFunctions(EntityMoLangFunctions.attach(this))
value.addFunctions(LivingEntityMoLangFunctions.attach(this))
@@ -195,36 +209,40 @@ object MoLangFunctions {
}
fun PokemonBattle.asMoLangValue(): ObjectValue<PokemonBattle> {
val value = ObjectValue(
obj = this,
stringify = { it.battleId.toString() }
)
val value =
ObjectValue(
obj = this,
stringify = { it.battleId.toString() },
)
value.addFunctions(BattleMoLangFunctions.attach(this))
return value
}
fun BattleActor.asMoLangValue(): ObjectValue<BattleActor> {
val value = ObjectValue(
obj = this,
stringify = { it.toString() }
)
val value =
ObjectValue(
obj = this,
stringify = { it.toString() },
)
value.addFunctions(BattleActorMoLangFunctions.attach(this))
return value
}
fun SpawnablePosition.asMoLangValue(): ObjectValue<SpawnablePosition> {
val value = ObjectValue(
obj = this,
stringify = { it.toString() }
)
val value =
ObjectValue(
obj = this,
stringify = { it.toString() },
)
value.addFunctions(SpawnablePositionMoLangFunctions.attach(this))
return value
}
fun Vec3.asMoLangValue(): ObjectValue<Vec3> {
val value = ObjectValue(
obj = this
)
val value =
ObjectValue(
obj = this,
)
value.addFunction("x") { this.x }
value.addFunction("y") { this.y }
value.addFunction("z") { this.z }
@@ -235,45 +253,66 @@ object MoLangFunctions {
/**
* Different functions exist depending on the entity type, this tries to make the struct that's most specific to the type.
*/
fun Entity.asMostSpecificMoLangValue(): ObjectValue<out Entity> {
return when (this) {
is Player -> asMoLangValue()
is PokemonEntity -> struct
is NPCEntity -> struct
is ItemEntity -> ObjectValue(this).also { item ->
item.addStandardFunctions()
item.addFunctions(EntityMoLangFunctions.attach(this))
item.addFunctions(ItemStackMoLangFunctions.attach(Pair(this.item, this.registryAccess())))
fun Entity.asMostSpecificMoLangValue(): ObjectValue<out Entity> =
when (this) {
is Player -> {
asMoLangValue()
}
else -> ObjectValue(this).also { objectValue ->
objectValue.addStandardFunctions()
objectValue.addFunctions(EntityMoLangFunctions.attach(this))
if (this is LivingEntity) {
objectValue.addFunctions(LivingEntityMoLangFunctions.attach(this))
is PokemonEntity -> {
struct
}
is NPCEntity -> {
struct
}
is ItemEntity -> {
ObjectValue(this).also { item ->
item.addStandardFunctions()
item.addFunctions(EntityMoLangFunctions.attach(this))
item.addFunctions(ItemStackMoLangFunctions.attach(Pair(this.item, this.registryAccess())))
}
}
else -> {
ObjectValue(this).also { objectValue ->
objectValue.addStandardFunctions()
objectValue.addFunctions(EntityMoLangFunctions.attach(this))
if (this is LivingEntity) {
objectValue.addFunctions(LivingEntityMoLangFunctions.attach(this))
}
}
}
}
}
fun <T> Holder<T>.asMoLangValue(key: ResourceKey<Registry<T>>): ObjectValue<Holder<T>> {
val value = ObjectValue(
obj = this,
stringify = { it.unwrapKey().get().location().toString() }
)
value.functions["is_in"] = Function put@{ params: MoParams ->
val tag = TagKey.create(key, ResourceLocation.parse(params.getString(0).replace("#", "")))
return@put DoubleValue(if (value.obj.`is`(tag)) 1.0 else 0.0)
}
value.functions["is_of"] = Function put@{ params: MoParams ->
val identifier = ResourceLocation.parse(params.getString(0))
return@put DoubleValue(if (value.obj.`is`(identifier)) 1.0 else 0.0)
}
val value =
ObjectValue(
obj = this,
stringify = {
it
.unwrapKey()
.get()
.location()
.toString()
},
)
value.functions["is_in"] =
Function put@{ params: MoParams ->
val tag = TagKey.create(key, ResourceLocation.parse(params.getString(0).replace("#", "")))
return@put DoubleValue(if (value.obj.`is`(tag)) 1.0 else 0.0)
}
value.functions["is_of"] =
Function put@{ params: MoParams ->
val identifier = ResourceLocation.parse(params.getString(0))
return@put DoubleValue(if (value.obj.`is`(identifier)) 1.0 else 0.0)
}
return value
}
fun QueryStruct.addStandardFunctions(): QueryStruct {
addFunctions(GeneralMoLangFunctions.holder)
addFunctions(GeneralMoLangFunctions.functions)
return this
}
@@ -340,23 +379,17 @@ object MoLangFunctions {
return this
}
fun moLangFunctionMap(
vararg functions: Pair<String, (MoParams) -> MoValue>
): Map<String, (MoParams) -> MoValue> {
return functions.toMap()
}
fun moLangFunctionMap(vararg functions: Pair<String, (MoParams) -> MoValue>): Map<String, (MoParams) -> MoValue> = functions.toMap()
fun queryStructOf(
vararg functions: Pair<String, (MoParams) -> MoValue>
): QueryStruct {
return QueryStruct(
fun queryStructOf(vararg functions: Pair<String, (MoParams) -> MoValue>): QueryStruct =
QueryStruct(
hashMapOf<String, Function<MoParams, Any>>(
*functions.map { (name, func) ->
name to Function<MoParams, Any> { params -> func(params) }
}.toTypedArray()
)
*functions
.map { (name, func) ->
name to Function<MoParams, Any> { params -> func(params) }
}.toTypedArray(),
),
)
}
fun MoLangRuntime.setup(): MoLangRuntime {
environment.query.addStandardFunctions()
@@ -365,8 +398,14 @@ object MoLangFunctions {
fun writeMoValueToNBT(value: MoValue): Tag? {
return when (value) {
is DoubleValue -> DoubleTag.valueOf(value.value)
is StringValue -> StringTag.valueOf(value.value)
is DoubleValue -> {
DoubleTag.valueOf(value.value)
}
is StringValue -> {
StringTag.valueOf(value.value)
}
is ArrayStruct -> {
val list = value.map.values
val nbtList = ListTag()
@@ -383,14 +422,22 @@ object MoLangFunctions {
nbt
}
else -> null
else -> {
null
}
}
}
fun readMoValueFromNBT(nbt: Tag): MoValue {
return when (nbt) {
is DoubleTag -> DoubleValue(nbt.asDouble)
is StringTag -> StringValue(nbt.asString)
fun readMoValueFromNBT(nbt: Tag): MoValue =
when (nbt) {
is DoubleTag -> {
DoubleValue(nbt.asDouble)
}
is StringTag -> {
StringValue(nbt.asString)
}
is ListTag -> {
val array = ArrayStruct(hashMapOf())
var index = 0
@@ -411,11 +458,12 @@ object MoLangFunctions {
variable
}
else -> VariableStruct()
else -> {
VariableStruct()
}
}
}
}
// TODO: this one is unused. Should we move it to [MoLangFunctions] object?
fun Either<ResourceLocation, ExpressionLike>.runScript(runtime: MoLangRuntime) =
map({ CobblemonScripts.run(it, runtime) }, { it.resolve(runtime) })
map({ CobblemonScripts.run(it, runtime) }, { it.resolve(runtime) })
@@ -17,30 +17,29 @@ import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
import com.cobblemon.mod.common.entity.npc.NPCBattleActor
object BattleActorMoLangFunctions : AbstractMoLangFunctionHolder<BattleActor>() {
override fun BattleActor.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
val actor = this
val map = mutableMapOf<String, (MoParams) -> Any>(
"is_npc" to { DoubleValue(actor.type == ActorType.NPC) },
"is_player" to { DoubleValue(actor.type == ActorType.PLAYER) },
"is_wild" to { DoubleValue(actor.type == ActorType.WILD) }
)
val functions = hashMapOf<String, (MoParams) -> Any>()
functions["is_npc"] = { DoubleValue(actor.type == ActorType.NPC) }
functions["is_player"] = { DoubleValue(actor.type == ActorType.PLAYER) }
functions["is_wild"] = { DoubleValue(actor.type == ActorType.WILD) }
when (actor) {
is NPCBattleActor -> {
map["npc"] = { actor.entity.struct }
functions["npc"] = { actor.entity.struct }
}
is PlayerBattleActor -> {
map["player"] = { actor.entity?.asMoLangValue() ?: DoubleValue.ZERO }
functions["player"] = { actor.entity?.asMoLangValue() ?: DoubleValue.ZERO }
}
is PokemonBattleActor -> {
map["pokemon"] = { actor.entity?.asMoLangValue() ?: DoubleValue.ZERO }
functions["pokemon"] = { actor.entity?.asMoLangValue() ?: DoubleValue.ZERO }
}
}
return map
return functions
}
}
@@ -16,17 +16,19 @@ import com.cobblemon.mod.common.util.getStringOrNull
object BattleMessageMoLangFunctions : AbstractMoLangFunctionHolder<BattleMessage>() {
override fun BattleMessage.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
val battleMessage = this
return mutableMapOf(
"has_argument_at" to { params ->
val index = params.getInt(0)
val value = params.getStringOrNull(1) ?: ""
DoubleValue(value == battleMessage.argumentAt(index))
},
"has_argument" to { params ->
val argumentName = params.getString(0)
val value = params.getStringOrNull(1) ?: ""
DoubleValue(value == battleMessage.optionalArgument(argumentName))
}
)
val functions = hashMapOf<String, (MoParams) -> Any>()
functions["has_argument_at"] = { params ->
val index = params.getInt(0)
val value = params.getStringOrNull(1) ?: ""
DoubleValue(value == battleMessage.argumentAt(index))
}
functions["has_argument"] = { params ->
val argumentName = params.getString(0)
val value = params.getStringOrNull(1) ?: ""
DoubleValue(value == battleMessage.optionalArgument(argumentName))
}
return functions
}
}
@@ -18,20 +18,22 @@ import java.util.UUID
object BattleMoLangFunctions : AbstractMoLangFunctionHolder<PokemonBattle>() {
override fun PokemonBattle.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
val battle = this
return mutableMapOf(
"environment" to { battle.runtime.environment },
"battle_id" to { StringValue(battle.battleId.toString()) },
"battle_type" to { StringValue(battle.format.toString()) },
"is_pvn" to { DoubleValue(battle.isPvN) },
"is_pvp" to { DoubleValue(battle.isPvP) },
"is_pvw" to { DoubleValue(battle.isPvW) },
"stop" to { battle.stop() },
"actors" to { battle.actors.toList().asArrayValue { it.struct } },
"get_actor" to getActor@{ params ->
val uuid = UUID.fromString(params.getString(0))
val actor = battle.actors.find { it.uuid == uuid } ?: return@getActor DoubleValue.ZERO
actor.struct
}
)
val functions = hashMapOf<String, (MoParams) -> Any>()
functions["environment"] = { battle.runtime.environment }
functions["battle_id"] = { StringValue(battle.battleId.toString()) }
functions["battle_type"] = { StringValue(battle.format.toString()) }
functions["is_pvn"] = { DoubleValue(battle.isPvN) }
functions["is_pvp"] = { DoubleValue(battle.isPvP) }
functions["is_pvw"] = { DoubleValue(battle.isPvW) }
functions["stop"] = { battle.stop() }
functions["actors"] = { battle.actors.toList().asArrayValue { it.struct } }
functions["get_actor"] = getActor@{ params ->
val uuid = UUID.fromString(params.getString(0))
val actor = battle.actors.find { it.uuid == uuid } ?: return@getActor DoubleValue.ZERO
actor.struct
}
return functions
}
}
@@ -12,8 +12,11 @@ import com.bedrockk.molang.runtime.MoParams
import net.minecraft.core.Holder
import net.minecraft.world.level.biome.Biome
object BiomeMoLangFunctions: AbstractMoLangFunctionHolder<Holder<Biome>>() {
object BiomeMoLangFunctions : AbstractMoLangFunctionHolder<Holder<Biome>>() {
override fun Holder<Biome>.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
return mutableMapOf()
val biome = this
val functions = hashMapOf<String, (MoParams) -> Any>()
return functions
}
}
@@ -12,8 +12,11 @@ import com.bedrockk.molang.runtime.MoParams
import net.minecraft.core.Holder
import net.minecraft.world.level.block.Block
object BlockMoLangFunctions: AbstractMoLangFunctionHolder<Holder<Block>>() {
object BlockMoLangFunctions : AbstractMoLangFunctionHolder<Holder<Block>>() {
override fun Holder<Block>.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
return mutableMapOf()
val block = this
val functions = hashMapOf<String, (MoParams) -> Any>()
return functions
}
}
@@ -14,6 +14,9 @@ import net.minecraft.world.level.dimension.DimensionType
object DimensionTypeMoLangFunctions : AbstractMoLangFunctionHolder<Holder<DimensionType>>() {
override fun Holder<DimensionType>.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
return mutableMapOf()
val dimension = this
val functions = hashMapOf<String, (MoParams) -> Any>()
return functions
}
}
@@ -18,15 +18,16 @@ import com.cobblemon.mod.common.util.getOrNull
object DropEntryMoLangFunctions : AbstractMoLangFunctionHolder<DropEntry>() {
override fun DropEntry.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
val drop = this
val map = mutableMapOf<String, (MoParams) -> Any>()
val functions = hashMapOf<String, (MoParams) -> Any>()
map["percentage"] = { DoubleValue(drop.percentage.toDouble()) }
map["quantity"] = { DoubleValue(drop.quantity.toDouble()) }
map["max_selectable_times"] = { DoubleValue(drop.maxSelectableTimes.toDouble()) }
map["can_drop"] = { params ->
functions["percentage"] = { DoubleValue(drop.percentage.toDouble()) }
functions["quantity"] = { DoubleValue(drop.quantity.toDouble()) }
functions["max_selectable_times"] = { DoubleValue(drop.maxSelectableTimes.toDouble()) }
functions["can_drop"] = { params ->
val pokemon = params.getOrNull<ObjectValue<Pokemon>>(0)?.obj
DoubleValue(if (drop.canDrop(pokemon)) 1.0 else 0.0)
}
return map
return functions
}
}
@@ -56,258 +56,300 @@ import kotlin.math.sqrt
object EntityMoLangFunctions : AbstractMoLangFunctionHolder<Entity>() {
override fun Entity.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
val thisEntity = this
val map = mutableMapOf<String, (MoParams) -> Any>(
"uuid" to { StringValue(thisEntity.uuid.toString()) },
"name" to { _ -> StringValue(thisEntity.effectiveName().string) },
"yaw" to { _ -> DoubleValue(thisEntity.yRot.toDouble()) },
"pitch" to { _ -> DoubleValue(thisEntity.xRot.toDouble()) },
"x" to { _ -> DoubleValue(thisEntity.x) },
"y" to { _ -> DoubleValue(thisEntity.y) },
"z" to { _ -> DoubleValue(thisEntity.z) },
"velocity_x" to { _ -> DoubleValue(thisEntity.deltaMovement.x) },
"velocity_y" to { _ -> DoubleValue(thisEntity.deltaMovement.y) },
"velocity_z" to { _ -> DoubleValue(thisEntity.deltaMovement.z) },
"width" to { DoubleValue(thisEntity.boundingBox.xsize) },
"height" to { DoubleValue(thisEntity.boundingBox.ysize) },
"this_size" to { DoubleValue(thisEntity.boundingBox.run { if (xsize > ysize) xsize else ysize }) },
"this_width" to { DoubleValue(thisEntity.boundingBox.xsize) },
"this_height" to { DoubleValue(thisEntity.boundingBox.ysize) },
"id_modulo" to { params -> DoubleValue(thisEntity.uuid.hashCode() % params.getDouble(0)) },
"horizontal_velocity" to { _ -> DoubleValue(thisEntity.deltaMovement.horizontalDistance()) },
"vertical_velocity" to { DoubleValue(thisEntity.deltaMovement.y) },
"is_on_ground" to { _ -> DoubleValue(thisEntity.onGround()) },
"world" to { _ -> thisEntity.level().worldRegistry.wrapAsHolder(thisEntity.level()).asWorldMoLangValue() },
"biome" to { _ -> thisEntity.level().getBiome(thisEntity.blockPosition()).asBiomeMoLangValue() },
"is_passenger" to { DoubleValue(thisEntity.isPassenger) },
"discard" to { thisEntity.discard() },
"is_sneaking" to { _ -> DoubleValue(thisEntity.isShiftKeyDown) },
"is_sprinting" to { _ -> DoubleValue(thisEntity.isSprinting) },
"is_in_water" to { _ -> DoubleValue(thisEntity.isUnderWater) },
"is_in_rain" to { _ -> DoubleValue(thisEntity.isInWaterOrRain && !thisEntity.isInWater) },
"is_touching_water_or_rain" to { _ -> DoubleValue(thisEntity.isInWaterRainOrBubble) },
"is_touching_water" to { _ -> DoubleValue(thisEntity.isInWater) },
"is_underwater" to { DoubleValue(thisEntity.getIsSubmerged()) },
"is_in_lava" to { _ -> DoubleValue(thisEntity.isInLava) },
"is_on_fire" to { _ -> DoubleValue(thisEntity.isOnFire) },
"is_invisible" to { _ -> DoubleValue(thisEntity.isInvisible) },
"is_riding" to { _ -> DoubleValue(thisEntity.isPassenger) },
"set_name" to { params ->
val name = params.getString(0)
thisEntity.customName = name.text()
DoubleValue.ONE
},
"damage" to { params ->
val amount = params.getDouble(0)
val source = DamageSource(
thisEntity.level().registryAccess().registryOrThrow(Registries.DAMAGE_TYPE)
.getHolder(DamageTypes.GENERIC).get()
val entity = this
val functions = hashMapOf<String, (MoParams) -> Any>()
functions["uuid"] = { StringValue(entity.uuid.toString()) }
functions["name"] = { StringValue(entity.effectiveName().string) }
functions["yaw"] = { DoubleValue(entity.yRot.toDouble()) }
functions["pitch"] = { DoubleValue(entity.xRot.toDouble()) }
functions["x"] = { DoubleValue(entity.x) }
functions["y"] = { DoubleValue(entity.y) }
functions["z"] = { DoubleValue(entity.z) }
functions["velocity_x"] = { DoubleValue(entity.deltaMovement.x) }
functions["velocity_y"] = { DoubleValue(entity.deltaMovement.y) }
functions["velocity_z"] = { DoubleValue(entity.deltaMovement.z) }
functions["width"] = { DoubleValue(entity.boundingBox.xsize) }
functions["height"] = { DoubleValue(entity.boundingBox.ysize) }
functions["this_size"] = { DoubleValue(entity.boundingBox.run { if (xsize > ysize) xsize else ysize }) }
functions["this_width"] = { DoubleValue(entity.boundingBox.xsize) }
functions["this_height"] = { DoubleValue(entity.boundingBox.ysize) }
functions["id_modulo"] = { params -> DoubleValue(entity.uuid.hashCode() % params.getDouble(0)) }
functions["horizontal_velocity"] = { DoubleValue(entity.deltaMovement.horizontalDistance()) }
functions["vertical_velocity"] = { DoubleValue(entity.deltaMovement.y) }
functions["is_on_ground"] = { DoubleValue(entity.onGround()) }
functions["world"] = {
entity
.level()
.worldRegistry
.wrapAsHolder(entity.level())
.asWorldMoLangValue()
}
functions["biome"] = { entity.level().getBiome(entity.blockPosition()).asBiomeMoLangValue() }
functions["is_passenger"] = { DoubleValue(entity.isPassenger) }
functions["discard"] = { entity.discard() }
functions["is_sneaking"] = { DoubleValue(entity.isShiftKeyDown) }
functions["is_sprinting"] = { DoubleValue(entity.isSprinting) }
functions["is_in_water"] = { DoubleValue(entity.isUnderWater) }
functions["is_in_rain"] = { DoubleValue(entity.isInWaterOrRain && !entity.isInWater) }
functions["is_touching_water_or_rain"] = { DoubleValue(entity.isInWaterRainOrBubble) }
functions["is_touching_water"] = { DoubleValue(entity.isInWater) }
functions["is_underwater"] = { DoubleValue(entity.getIsSubmerged()) }
functions["is_in_lava"] = { DoubleValue(entity.isInLava) }
functions["is_on_fire"] = { DoubleValue(entity.isOnFire) }
functions["is_invisible"] = { DoubleValue(entity.isInvisible) }
functions["is_riding"] = { DoubleValue(entity.isPassenger) }
functions["set_name"] = { params ->
val name = params.getString(0)
entity.customName = name.text()
DoubleValue.ONE
}
functions["damage"] = { params ->
val amount = params.getDouble(0)
val source =
DamageSource(
entity
.level()
.registryAccess()
.registryOrThrow(Registries.DAMAGE_TYPE)
.getHolder(DamageTypes.GENERIC)
.get(),
)
thisEntity.hurt(source, amount.toFloat())
},
"distance_to_owner" to distanceToOwner@{
if (thisEntity !is TamableAnimal) {
return@distanceToOwner DoubleValue.ZERO
}
val owner = thisEntity.owner ?: return@distanceToOwner DoubleValue.ZERO
DoubleValue(thisEntity.distanceTo(owner))
},
"owner" to owner@{
if (thisEntity !is TamableAnimal) return@owner DoubleValue.ZERO
val owner = thisEntity.owner
owner?.asMostSpecificMoLangValue() ?: DoubleValue.ZERO
},
"delta_movement" to {
listOf(thisEntity.deltaMovement.x, thisEntity.deltaMovement.y, thisEntity.deltaMovement.z)
.asArrayValue(::DoubleValue)
},
"tags" to {
val tags = thisEntity.tags
val array = ArrayStruct(hashMapOf())
tags.forEachIndexed { index, tag -> array.setDirectly("$index", StringValue(tag)) }
array
},
"add_tag" to { params ->
val tag = params.getString(0)
thisEntity.addTag(tag)
DoubleValue.ONE
},
"remove_tag" to { params ->
val tag = params.getString(0)
thisEntity.removeTag(tag)
DoubleValue.ONE
},
"has_tag" to { params ->
val tag = params.getString(0)
DoubleValue(thisEntity.tags.contains(tag))
},
"distance_to_pos" to { params ->
val x = params.getDouble(0)
val y = params.getDouble(1)
val z = params.getDouble(2)
DoubleValue(sqrt(thisEntity.distanceToSqr(Vec3(x, y, z))))
},
"type" to { _ ->
thisEntity.registryAccess().registry(Registries.ENTITY_TYPE).get().getKey(thisEntity.type)?.toString()?.let {
entity.hurt(source, amount.toFloat())
}
functions["distance_to_owner"] = distanceToOwner@{
if (entity !is TamableAnimal) {
return@distanceToOwner DoubleValue.ZERO
}
val owner = entity.owner ?: return@distanceToOwner DoubleValue.ZERO
DoubleValue(entity.distanceTo(owner))
}
functions["owner"] = owner@{
if (entity !is TamableAnimal) return@owner DoubleValue.ZERO
val owner = entity.owner
owner?.asMostSpecificMoLangValue() ?: DoubleValue.ZERO
}
functions["delta_movement"] = {
listOf(entity.deltaMovement.x, entity.deltaMovement.y, entity.deltaMovement.z)
.asArrayValue(::DoubleValue)
}
functions["tags"] = {
val tags = entity.tags
val array = ArrayStruct(hashMapOf())
tags.forEachIndexed { index, tag -> array.setDirectly("$index", StringValue(tag)) }
array
}
functions["add_tag"] = { params ->
val tag = params.getString(0)
entity.addTag(tag)
DoubleValue.ONE
}
functions["remove_tag"] = { params ->
val tag = params.getString(0)
entity.removeTag(tag)
DoubleValue.ONE
}
functions["has_tag"] = { params ->
val tag = params.getString(0)
DoubleValue(entity.tags.contains(tag))
}
functions["distance_to_pos"] = { params ->
val x = params.getDouble(0)
val y = params.getDouble(1)
val z = params.getDouble(2)
DoubleValue(sqrt(entity.distanceToSqr(Vec3(x, y, z))))
}
functions["type"] = {
entity
.registryAccess()
.registry(Registries.ENTITY_TYPE)
.get()
.getKey(entity.type)
?.toString()
?.let {
StringValue(it)
} ?: DoubleValue.ZERO
},
"find_nearby_block" to findNearbyBlock@{ params ->
val input = params.getString(0)
val isTag = input.contains("#")
val type = input.replace("#", "").asIdentifierDefaultingNamespace(namespace = "minecraft")
val range = params.getDoubleOrNull(1) ?: 10
val blockPos = thisEntity.level().getBlockStatesWithPos(
AABB.ofSize(
thisEntity.position(),
range.toDouble(),
range.toDouble(),
range.toDouble()
)
)
.filter { blockPosPair ->
}
functions["find_nearby_block"] = findNearbyBlock@{ params ->
val input = params.getString(0)
val isTag = input.contains("#")
val type = input.replace("#", "").asIdentifierDefaultingNamespace(namespace = "minecraft")
val range = params.getDoubleOrNull(1) ?: 10
val blockPos =
entity
.level()
.getBlockStatesWithPos(
AABB.ofSize(
entity.position(),
range.toDouble(),
range.toDouble(),
range.toDouble(),
),
).filter { blockPosPair ->
blockPosPair.first.blockHolder.let {
if (isTag) it.`is`(
TagKey.create(
Registries.BLOCK,
type
if (isTag) {
it.`is`(
TagKey.create(
Registries.BLOCK,
type,
),
)
) else it.`is`(type)
} else {
it.`is`(type)
}
}
}
.minByOrNull { it.second.distSqr(thisEntity.blockPosition()) }
}.minByOrNull { it.second.distSqr(entity.blockPosition()) }
?.second
if (blockPos != null) {
return@findNearbyBlock ArrayStruct(
mapOf(
"0" to DoubleValue(blockPos.x),
"1" to DoubleValue(blockPos.y),
"2" to DoubleValue(blockPos.z)
)
)
}
return@findNearbyBlock DoubleValue.ZERO
},
"get_nearby_entities" to { params ->
val distance = params.getDouble(0)
val entities =
thisEntity.level().getEntities(thisEntity, AABB.ofSize(thisEntity.position(), distance, distance, distance))
if (blockPos != null) {
return@findNearbyBlock ArrayStruct(
mapOf(
"0" to DoubleValue(blockPos.x),
"1" to DoubleValue(blockPos.y),
"2" to DoubleValue(blockPos.z),
),
)
}
return@findNearbyBlock DoubleValue.ZERO
}
functions["get_nearby_entities"] = { params ->
val distance = params.getDouble(0)
val entities =
entity
.level()
.getEntities(entity, AABB.ofSize(entity.position(), distance, distance, distance))
entities
.filterIsInstance<Entity>()
.map { it.asMostSpecificMoLangValue() }
.asArrayValue()
},
"is_standing_on_blocks" to { params ->
val depth = params.getDouble(0).toInt()
val blockStrings: MutableSet<String> = mutableSetOf()
for (blockIndex in 1..<params.params.size) {
blockStrings.add(params.getString(blockIndex))
}
entities
.filterIsInstance<Entity>()
.map { it.asMostSpecificMoLangValue() }
.asArrayValue()
}
functions["is_standing_on_blocks"] = { params ->
val depth = params.getDouble(0).toInt()
val blockStrings: MutableSet<String> = mutableSetOf()
for (blockIndex in 1..<params.params.size) {
blockStrings.add(params.getString(blockIndex))
}
if (thisEntity.isStandingOn(blockStrings, depth)) DoubleValue.ONE else DoubleValue.ZERO
},
if (entity.isStandingOn(blockStrings, depth)) DoubleValue.ONE else DoubleValue.ZERO
}
// q.entity.spawn_bedrock_particles(effect, locator, [player])
// sends to everyone nearby or just to the player if they're set.
// Locator is necessary even if unused on non-posables.
"spawn_bedrock_particles" to { params ->
val particle = params.getString(0).asResource()
val locator = params.getString(1)
val player = params.getOrNull<MoValue>(2)?.let {
// q.entity.spawn_bedrock_particles(effect, locator, [player])
// sends to everyone nearby or just to the player if they're set.
// Locator is necessary even if unused on non-posables.
functions["spawn_bedrock_particles"] = { params ->
val particle = params.getString(0).asResource()
val locator = params.getString(1)
val player =
params.getOrNull<MoValue>(2)?.let {
when (it) {
is StringValue -> thisEntity.level().getPlayerByUUID(UUID.fromString(it.value))
is StringValue -> entity.level().getPlayerByUUID(UUID.fromString(it.value))
is ObjectValue<*> -> it.obj
else -> null
}
} as? ServerPlayer
val packet = SpawnSnowstormEntityParticlePacket(particle, thisEntity.id, listOf(locator))
if (player == null) {
packet.sendToPlayersAround(thisEntity.x, thisEntity.y, thisEntity.z, 64.0, thisEntity.level().dimension())
} else {
packet.sendToPlayer(player)
}
},
"make_intangible" to { params ->
val intangible = params.getBooleanOrNull(0) ?: true
thisEntity.noPhysics = intangible
DoubleValue.ONE
val packet = SpawnSnowstormEntityParticlePacket(particle, entity.id, listOf(locator))
if (player == null) {
packet.sendToPlayersAround(
entity.x,
entity.y,
entity.z,
64.0,
entity.level().dimension(),
)
} else {
packet.sendToPlayer(player)
}
)
}
functions["make_intangible"] = { params ->
val intangible = params.getBooleanOrNull(0) ?: true
entity.noPhysics = intangible
DoubleValue.ONE
}
if (thisEntity is PosableEntity) {
map["play_animation"] = playAnimation@{ params ->
val animation = params.getString(0)
val packet = PlayPosableAnimationPacket(thisEntity.id, setOf(animation), emptyList())
val target = params.getStringOrNull(1)
if (target != null) {
val targetPlayer = if (target.asUUID != null) {
thisEntity.level().getPlayerByUUID(target.asUUID!!) as ServerPlayer
} else if (thisEntity.level() is ServerLevel) {
thisEntity.level().server!!.playerList.getPlayerByName(target)
when (entity) {
is PosableEntity -> {
functions["play_animation"] = playAnimation@{ params ->
val animation = params.getString(0)
val packet = PlayPosableAnimationPacket(entity.id, setOf(animation), emptyList())
val target = params.getStringOrNull(1)
if (target != null) {
val targetPlayer =
if (target.asUUID != null) {
entity.level().getPlayerByUUID(target.asUUID!!) as ServerPlayer
} else if (entity.level() is ServerLevel) {
entity
.level()
.server!!
.playerList
.getPlayerByName(target)
} else {
null
}
if (targetPlayer != null) {
packet.sendToPlayer(targetPlayer)
return@playAnimation DoubleValue.ONE
} else {
return@playAnimation DoubleValue.ZERO
}
} else {
null
}
if (targetPlayer != null) {
packet.sendToPlayer(targetPlayer)
packet.sendToPlayersAround(
entity.x,
entity.y,
entity.z,
64.0,
entity.level().dimension(),
)
return@playAnimation DoubleValue.ONE
} else {
return@playAnimation DoubleValue.ZERO
}
} else {
packet.sendToPlayersAround(thisEntity.x, thisEntity.y, thisEntity.z, 64.0, thisEntity.level().dimension())
return@playAnimation DoubleValue.ONE
}
functions["face"] = { params ->
ObjectValue(ReferenceDialogueFaceProvider(entity.id, params.getBooleanOrNull(0) != false))
}
}
map["face"] = { params ->
ObjectValue(ReferenceDialogueFaceProvider(thisEntity.id, params.getBooleanOrNull(0) != false))
is Schedulable -> {
functions["run_molang_after"] = { params ->
val expression = params.getString(0).asExpressionLike()
val delayInSeconds = params.getDouble(1).toFloat()
val runtime = MoLangRuntime()
runtime.environment.cloneFrom(params.environment)
entity.after(delayInSeconds) {
runtime.resolve(expression)
}
}
}
}
if (thisEntity is Schedulable) {
map["run_molang_after"] = { params ->
val expression = params.getString(0).asExpressionLike()
val delayInSeconds = params.getDouble(1).toFloat()
val runtime = MoLangRuntime()
runtime.environment.cloneFrom(params.environment)
thisEntity.after(delayInSeconds) {
runtime.resolve(expression)
is MoLangScriptingEntity -> {
functions["add_callback"] = { params ->
val type = params.getString(0).asIdentifierDefaultingNamespace()
val callback = params.getString(1).asIdentifierDefaultingNamespace()
val allowDuplicates = params.getBooleanOrNull(2) ?: false
entity.callbacks.addCallback(type, callback, allowDuplicates)
}
functions["remove_callback"] = { params ->
val type = params.getString(0).asIdentifierDefaultingNamespace()
val callback = params.getString(1).asIdentifierDefaultingNamespace()
entity.callbacks.removeCallback(type, callback)
}
functions["has_callback"] = { params ->
val type = params.getString(0).asIdentifierDefaultingNamespace()
val callback = params.getString(1).asIdentifierDefaultingNamespace()
DoubleValue(callback in (entity.callbacks[type] ?: emptySet()))
}
functions["clear_callbacks"] = { params ->
val type = params.getStringOrNull(0)?.asIdentifierDefaultingNamespace()
if (type != null) {
entity.callbacks[type]?.clear()
} else {
entity.callbacks.clear()
}
DoubleValue.ONE
}
}
}
if (thisEntity is MoLangScriptingEntity) {
map["add_callback"] = { params ->
val type = params.getString(0).asIdentifierDefaultingNamespace()
val callback = params.getString(1).asIdentifierDefaultingNamespace()
val allowDuplicates = params.getBooleanOrNull(2) ?: false
thisEntity.callbacks.addCallback(type, callback, allowDuplicates)
}
map["remove_callback"] = { params ->
val type = params.getString(0).asIdentifierDefaultingNamespace()
val callback = params.getString(1).asIdentifierDefaultingNamespace()
thisEntity.callbacks.removeCallback(type, callback)
}
map["has_callback"] = { params ->
val type = params.getString(0).asIdentifierDefaultingNamespace()
val callback = params.getString(1).asIdentifierDefaultingNamespace()
DoubleValue(callback in (thisEntity.callbacks[type] ?: emptySet()))
}
map["clear_callbacks"] = { params ->
val type = params.getStringOrNull(0)?.asIdentifierDefaultingNamespace()
if (type != null) {
thisEntity.callbacks[type]?.clear()
} else {
thisEntity.callbacks.clear()
}
DoubleValue.ONE
}
}
return map
return functions
}
}
@@ -20,28 +20,28 @@ import com.cobblemon.mod.common.pokemon.transformation.triggers.TradeTrigger
object EvolutionMoLangFunctions : AbstractMoLangFunctionHolder<Transformation>() {
override fun Transformation.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
val evolution = this
val map = hashMapOf<String, (MoParams) -> Any>()
val functions = hashMapOf<String, (MoParams) -> Any>()
map["is_optional"] = { DoubleValue(evolution.optional) }
map["reevaluate"] = { DoubleValue(evolution.reevaluate) }
functions["reevaluate"] = { DoubleValue(evolution.reevaluate) }
functions["is_optional"] = { DoubleValue(evolution.optional) }
if (evolution is Evolution) {
map["result"] = { evolution.result.asMoLangValue() }
functions["result"] = { evolution.result.asMoLangValue() }
when (evolution.trigger) {
is LevelUpTrigger -> {
map["is_level_up"] = { DoubleValue.ONE }
functions["is_level_up"] = { DoubleValue.ONE }
}
is TradeTrigger -> {
map["is_trade"] = { DoubleValue.ONE }
functions["is_trade"] = { DoubleValue.ONE }
}
is ItemInteractionTrigger -> {
map["is_item"] = { DoubleValue.ONE }
functions["is_item"] = { DoubleValue.ONE }
}
}
}
return map
return functions
}
}
@@ -42,101 +42,99 @@ import net.minecraft.world.item.ItemStack
import java.text.ParseException
object GeneralMoLangFunctions {
val holder: HashMap<String, (MoParams) -> Any> = hashMapOf(
"print" to { params ->
val functions: MutableMap<String, (MoParams) -> Any> = mutableMapOf()
init {
functions["print"] = { params ->
val message = params.get<MoValue>(0).asString()
LOGGER.info(message)
},
"delete_variable" to deleteVar@{ params ->
}
functions["delete_variable"] = { params ->
val struct = params.get<VariableStruct>(0)
val variable = params.getString(1)
struct.map.remove(variable)
return@deleteVar DoubleValue.ONE
},
"delete_variables" to { params ->
val struct = params.get<VariableStruct>(0)
struct.map.clear()
DoubleValue.ONE
},
"get_variable" to { params ->
}
functions["get_variable"] = { params ->
val struct = params.get<VariableStruct>(0)
val variable = params.getString(1)
struct.map[variable] ?: DoubleValue.ZERO
},
"set_variable" to { params ->
}
functions["set_variable"] = { params ->
val struct = params.get<VariableStruct>(0)
val variable = params.getString(1)
val value = params.get<MoValue>(2)
struct.map[variable] = value
value
},
"set_query" to { params ->
}
functions["set_query"] = { params ->
val variable = params.getString(0)
val value = params.get<MoValue>(1)
params.environment.query.addFunction(variable) { value }
value
},
"replace" to { params ->
}
functions["replace"] = { params ->
val text = params.getString(0)
val search = params.getString(1)
val replace = params.getString(2)
StringValue(text.replace(search, replace))
},
"is_included" to { params ->
}
functions["is_included"] = { params ->
val text = params.getString(0)
val search = params.getString(1)
DoubleValue(text.contains(search))
},
"to_lower" to { params ->
}
functions["to_lower"] = { params ->
StringValue(params.getString(0).lowercase())
},
"to_upper" to { params ->
}
functions["to_upper"] = { params ->
StringValue(params.getString(0).uppercase())
},
"string_length" to { params ->
}
functions["string_length"] = { params ->
DoubleValue(params.getString(0).length)
},
"split_string" to { params ->
}
functions["split_string"] = { params ->
val text = params.getString(0)
val delimiter = params.getString(1)
val parts = text.split(delimiter).map { StringValue(it) }
val struct = ArrayStruct(hashMapOf())
parts.forEachIndexed { index, moValue -> struct.setDirectly("$index", moValue) }
struct
},
"is_blank" to { params ->
}
functions["is_blank"] = { params ->
val arg = params.get<MoValue>(0)
DoubleValue((arg is StringValue && (arg.value.isBlank() || arg.value.toDoubleOrNull() == 0.0)) || (arg is DoubleValue && arg.value == 0.0))
},
"run_command" to { params ->
DoubleValue(
(arg is StringValue && (arg.value.isBlank() || arg.value.toDoubleOrNull() == 0.0)) ||
(arg is DoubleValue && arg.value == 0.0),
)
}
functions["run_command"] = { params ->
val command = params.getString(0)
try {
server()!!.commands.performPrefixedCommand(server()!!.createCommandSourceStack(), command)
} catch (_: NullPointerException) {
DoubleValue.ZERO
}
},
"is_int" to { params -> DoubleValue(params.get<MoValue>(0).asString().isInt()) },
"is_number" to { params ->
}
functions["is_int"] = { params -> DoubleValue(params.get<MoValue>(0).asString().isInt()) }
functions["is_number"] = { params ->
DoubleValue(
params.get<MoValue>(0).asString().toDoubleOrNull() != null
params.get<MoValue>(0).asString().toDoubleOrNull() != null,
)
},
"to_number" to { params ->
}
functions["to_number"] = { params ->
DoubleValue(
params.get<MoValue>(0).asString().toDoubleOrNull() ?: 0.0
params.get<MoValue>(0).asString().toDoubleOrNull() ?: 0.0,
)
},
"to_int" to { params ->
}
functions["to_int"] = { params ->
DoubleValue(
params.get<MoValue>(0).asString().toIntOrNull() ?: 0
params.get<MoValue>(0).asString().toIntOrNull() ?: 0,
)
},
"to_string" to { params -> StringValue(params.get<MoValue>(0).asString()) },
"do_effect_walks" to { _ ->
DoubleValue(Cobblemon.config.walkingInBattleAnimations)
},
"random" to { params ->
}
functions["to_string"] = { params -> StringValue(params.get<MoValue>(0).asString()) }
functions["do_effect_walks"] = { DoubleValue(Cobblemon.config.walkingInBattleAnimations) }
functions["random"] = { params ->
val options = mutableListOf<MoValue>()
var index = 0
while (params.contains(index)) {
@@ -144,35 +142,35 @@ object GeneralMoLangFunctions {
index++
}
options.random() // Can throw an exception if they specified no args. They'd be idiots though.
},
"curve" to { params ->
}
functions["curve"] = { params ->
val curveName = params.getString(0)
val curve =
WaveFunctions.functions[curveName] ?: throw IllegalArgumentException("Unknown curve: $curveName")
ObjectValue(curve)
},
"array" to { params ->
}
functions["array"] = { params ->
val values = params.params
val array = ArrayStruct(hashMapOf())
values.forEachIndexed { index, moValue -> array.setDirectly("$index", moValue) }
array
},
"length" to { params ->
}
functions["length"] = { params ->
val array = (params.getOrNull<MoValue>(0) as? VariableStruct)
if (array == null) {
DoubleValue.ZERO
} else {
DoubleValue(array.map.size.toDouble())
}
},
"append" to { params ->
}
functions["append"] = { params ->
val array = params.get<ArrayStruct>(0)
val value = params.get<MoValue>(1)
val nextIndex = array.map.size
array.setDirectly("$nextIndex", value)
array
},
"insert" to { params ->
}
functions["insert"] = { params ->
val array = params.get<ArrayStruct>(0)
val index = params.getInt(1)
val value = params.get<MoValue>(2)
@@ -187,36 +185,40 @@ object GeneralMoLangFunctions {
}
array.map[index.toString()] = value
array
},
"delete" to { params ->
}
functions["delete"] = { params ->
val array = params.get<ArrayStruct>(0)
val index = params.getInt(1)
if (index in 0 until array.map.size) {
array.map.remove(index.toString())
// Re-index the array to keep keys sequential in numerical order
val newMap = hashMapOf<String, MoValue>()
array.map.keys.mapNotNull { it.toIntOrNull() }
array.map.keys
.mapNotNull { it.toIntOrNull() }
.sorted()
.forEachIndexed { i, k -> newMap[i.toString()] = array.map[k.toString()]!! }
array.map.clear()
array.map.putAll(newMap)
}
array
},
"run_script" to { params ->
}
functions["run_script"] = { params ->
val runtime = MoLangRuntime()
runtime.environment.query = params.environment.query
runtime.environment.variable = params.environment.variable
val args = params.params.subList(1, params.params.size)
runtime.environment.context = ContextStruct(
(params.environment.context?.map
?: emptyMap()) + args.mapIndexed { index, value -> "arg_${index + 1}" to value }.toMap()
)
runtime.environment.context =
ContextStruct(
(
params.environment.context?.map
?: emptyMap()
) + args.mapIndexed { index, value -> "arg_${index + 1}" to value }.toMap(),
)
val script = params.getString(0).asIdentifierDefaultingNamespace()
// store the args in the
CobblemonScripts.run(script, runtime) ?: DoubleValue.ZERO
},
"run_molang" to { params ->
}
functions["run_molang"] = { params ->
val runtime = MoLangRuntime()
runtime.environment.query = params.environment.query
runtime.environment.variable = params.environment.variable
@@ -224,17 +226,16 @@ object GeneralMoLangFunctions {
val expression = params.getString(0).asExpressionLike()
val delayInSeconds = params.getDoubleOrNull(1)?.toFloat() ?: 0.0f
fun evaluate(): MoValue {
return try {
fun evaluate(): MoValue =
try {
runtime.resolve(expression)
} catch (ex: Exception) {
LOGGER.warn(
"Failed to evaluate MoLang expression${if (delayInSeconds > 0.0f) " (delayed)" else ""}: $expression",
ex
ex,
)
DoubleValue.ZERO
}
}
if (delayInSeconds > 0.0f) {
val tracker =
@@ -246,55 +247,55 @@ object GeneralMoLangFunctions {
} else {
evaluate()
}
},
"system_time_millis" to { _ ->
DoubleValue(System.currentTimeMillis())
},
}
functions["system_time_millis"] = { DoubleValue(System.currentTimeMillis()) }
// the rest of the world use dd/MM/yyyy grow up america (this comment was generated by copilot)
"date_local_time" to { _ ->
functions["date_local_time"] = {
val time = System.currentTimeMillis()
val date = java.util.Date(time)
val formatted = java.text.SimpleDateFormat("dd/MM/yyyy").format(date)
StringValue(formatted)
},
"date_of" to { params ->
}
functions["date_of"] = { params ->
val time = params.getDouble(0).toLong()
val date = java.util.Date(time)
val formatted = java.text.SimpleDateFormat("dd/MM/yyyy").format(date)
StringValue(formatted)
},
"date_is_after" to { params ->
}
functions["date_is_after"] = { params ->
val dateA = params.getString(0)
val dateB = params.getString(1)
val format = java.text.SimpleDateFormat("dd/MM/yyyy")
val a = try {
format.parse(dateA)
} catch (_: ParseException) {
throw RuntimeException("Cannot parse date ${dateA}, expected it to be in dd/MM/yyyy format, other provided")
}
val b = try {
format.parse(dateB)
} catch (_: ParseException) {
throw RuntimeException("Cannot parse date ${dateB}, expected it to be in dd/MM/yyyy format, other provided")
}
val a =
try {
format.parse(dateA)
} catch (_: ParseException) {
throw RuntimeException("Cannot parse date $dateA, expected it to be in dd/MM/yyyy format, other provided")
}
val b =
try {
format.parse(dateB)
} catch (_: ParseException) {
throw RuntimeException("Cannot parse date $dateB, expected it to be in dd/MM/yyyy format, other provided")
}
DoubleValue(a.after(b))
},
"get_move_from_id" to { params ->
}
functions["get_move_from_id"] = { params ->
val moveId = params.getString(0)
val moveTemplate = Moves.getByName(moveId)
moveTemplate?.struct ?: DoubleValue.ZERO
},
"create_simple_party_provider" to { _ ->
}
functions["create_simple_party_provider"] = {
val partyProvider = SimplePartyProvider()
partyProvider.struct
},
"create_pickup_item" to { params ->
}
functions["create_pickup_item"] = { params ->
val item = params.getOrNull<MoValue>(0)?.asString()?.let(ObtainableItemCondition::parseFromString)
val pickupPriority = params.getIntOrNull(1) ?: 0
val pickupItem = ObtainableItem(item = item, pickupPriority = pickupPriority)
pickupItem.struct
},
"create_itemstack" to { params ->
}
functions["create_itemstack"] = { params ->
val itemId = params.getString(0).asIdentifierDefaultingNamespace()
try {
val item = BuiltInRegistries.ITEM.get(itemId)
@@ -305,7 +306,7 @@ object GeneralMoLangFunctions {
LOGGER.error("Cannot create item stack from identifier: $itemId, item with that ID does not exist. Possible typo?")
DoubleValue.ZERO
}
},
"file" to { MoLangLoadedFilesCache.struct }
)
}
functions["file"] = { MoLangLoadedFilesCache.struct }
}
}
@@ -26,43 +26,44 @@ object ItemStackMoLangFunctions : AbstractMoLangFunctionHolder<Pair<ItemStack, R
val registryAccess = this.second
val itemRegistry = registryAccess.registryOrThrow(Registries.ITEM)
val holder = itemRegistry.wrapAsHolder(stack.item)
val functions = mutableMapOf<String, (MoParams) -> Any>()
val map = hashMapOf<String, (MoParams) -> Any>()
map["item"] = { _ -> holder.asMoLangValue(Registries.ITEM) }
map["count"] = { _ -> DoubleValue(stack.count.toDouble()) }
map["damage_value"] = { _ -> DoubleValue(stack.damageValue) }
map["max_damage"] = { _ -> DoubleValue(stack.maxDamage) }
map["is_empty"] = { _ -> DoubleValue(stack.isEmpty) }
map["shrink"] = { params -> stack.shrink(params.getInt(0)) }
map["grow"] = { params -> stack.grow(params.getInt(0)) }
map["is_of"] = { params ->
functions["item"] = { holder.asMoLangValue(Registries.ITEM) }
functions["count"] = { DoubleValue(stack.count.toDouble()) }
functions["damage_value"] = { DoubleValue(stack.damageValue) }
functions["max_damage"] = { DoubleValue(stack.maxDamage) }
functions["is_empty"] = { DoubleValue(stack.isEmpty) }
functions["shrink"] = { params -> stack.shrink(params.getInt(0)) }
functions["grow"] = { params -> stack.grow(params.getInt(0)) }
functions["is_of"] = { params ->
DoubleValue(
holder.`is`(
params.getString(0).asIdentifierDefaultingNamespace()
)
params.getString(0).asIdentifierDefaultingNamespace(),
),
)
}
map["is_in"] = { params ->
functions["is_in"] = { params ->
DoubleValue(
holder.`is`(
TagKey.create(
Registries.ITEM,
params.getString(0).replace("#", "").asIdentifierDefaultingNamespace()
)
)
params.getString(0).replace("#", "").asIdentifierDefaultingNamespace(),
),
),
)
}
map["is_food"] = { _ -> DoubleValue(stack.has(DataComponents.FOOD)) }
map["is_enchanted"] = { _ -> DoubleValue(stack.hasFoil()) }
map["has_enchantment"] = put@{ params ->
functions["is_food"] = { DoubleValue(stack.has(DataComponents.FOOD)) }
functions["is_enchanted"] = { DoubleValue(stack.hasFoil()) }
functions["has_enchantment"] = hasEnchantment@{ params ->
val enchantmentId = params.getString(0).asIdentifierDefaultingNamespace()
val minLevel = params.getIntOrNull(1) ?: 1
val enchantmentRegistry = registryAccess.registryOrThrow(Registries.ENCHANTMENT)
val holder =
enchantmentRegistry.getHolder(enchantmentId).orElse(null) ?: return@put DoubleValue.ZERO
enchantmentRegistry.getHolder(enchantmentId).orElse(null) ?: return@hasEnchantment DoubleValue.ZERO
val level = EnchantmentHelper.getItemEnchantmentLevel(holder, stack)
return@put DoubleValue(level >= minLevel)
DoubleValue(level >= minLevel)
}
return map
return functions
}
}
@@ -53,26 +53,27 @@ import java.util.*
object LivingEntityMoLangFunctions : AbstractMoLangFunctionHolder<LivingEntity>() {
override fun LivingEntity.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
val entity = this
val map = mutableMapOf<String, (MoParams) -> Any>()
map["is_player"] = { _ -> DoubleValue(entity is Player) }
map["is_npc"] = { _ -> DoubleValue(entity is NPCEntity) }
map["is_mob"] = { _ -> DoubleValue(entity is Mob) }
map["is_pokemon"] = { _ -> DoubleValue(entity is PokemonEntity) }
map["is_animal"] = { _ -> DoubleValue(entity is Animal) }
map["is_tamable"] = { _ -> DoubleValue(entity is TamableAnimal) }
map["is_tamed"] = { _ -> DoubleValue(entity is TamableAnimal && entity.isTame) }
map["is_hostile"] = { _ -> DoubleValue(entity is Monster) }
map["is_baby"] = { _ -> DoubleValue(entity.isBaby) }
map["is_adult"] = { _ -> DoubleValue(!entity.isBaby) }
map["remove_effect"] = put@{ params ->
val functions = hashMapOf<String, (MoParams) -> Any>()
functions["is_player"] = { DoubleValue(entity is Player) }
functions["is_npc"] = { DoubleValue(entity is NPCEntity) }
functions["is_mob"] = { DoubleValue(entity is Mob) }
functions["is_pokemon"] = { DoubleValue(entity is PokemonEntity) }
functions["is_animal"] = { DoubleValue(entity is Animal) }
functions["is_tamable"] = { DoubleValue(entity is TamableAnimal) }
functions["is_tamed"] = { DoubleValue(entity is TamableAnimal && entity.isTame) }
functions["is_hostile"] = { DoubleValue(entity is Monster) }
functions["is_baby"] = { DoubleValue(entity.isBaby) }
functions["is_adult"] = { DoubleValue(!entity.isBaby) }
functions["remove_effect"] = removeEffect@{ params ->
val effectId = params.getString(0).asIdentifierDefaultingNamespace()
val effectHolder = BuiltInRegistries.MOB_EFFECT.getHolder(effectId).orElse(null)
if (effectHolder != null) {
entity.removeEffect(effectHolder)
return@put DoubleValue.ONE
return@removeEffect DoubleValue.ONE
}
}
map["add_effect"] = put@{ params ->
functions["add_effect"] = addEffect@{ params ->
val effectId = params.getString(0).asIdentifierDefaultingNamespace()
val duration = params.getInt(1)
val amplifier = params.getIntOrNull(2) ?: 0
@@ -81,36 +82,37 @@ object LivingEntityMoLangFunctions : AbstractMoLangFunctionHolder<LivingEntity>(
val effectHolder = BuiltInRegistries.MOB_EFFECT.getHolder(effectId).orElse(null)
if (effectHolder != null) {
entity.addEffect(MobEffectInstance(effectHolder, duration, amplifier, ambient, visible))
return@put DoubleValue.ONE
return@addEffect DoubleValue.ONE
}
}
map["has_effect"] = put@{ params ->
functions["has_effect"] = { params ->
val effectId = params.getString(0).asIdentifierDefaultingNamespace()
val effectHolder = BuiltInRegistries.MOB_EFFECT.getHolder(effectId).orElse(null)
return@put DoubleValue(if (effectHolder != null) entity.hasEffect(effectHolder) else false)
DoubleValue(effectHolder != null && entity.hasEffect(effectHolder))
}
map["heal"] = { params ->
functions["heal"] = { params ->
val amount = params.getDouble(0)
entity.heal(amount.toFloat())
}
map["is_looking_at"] = put@{ params ->
functions["is_looking_at"] = isLookingAt@{ params ->
val targetEntity = params.get<MoValue>(0)
val maxDistance = params.getDoubleOrNull(1)?.toFloat() ?: 2.0F
val entity = if (targetEntity is ObjectValue<*> && targetEntity.obj is Entity) {
targetEntity.obj as Entity
} else {
return@put DoubleValue.ZERO
}
val entity =
if (targetEntity is ObjectValue<*> && targetEntity.obj is Entity) {
targetEntity.obj as Entity
} else {
return@isLookingAt DoubleValue.ZERO
}
return@put DoubleValue(entity.isLookingAt(entity, maxDistance))
DoubleValue(entity.isLookingAt(entity, maxDistance))
}
map["is_living_entity"] = { DoubleValue.ONE }
map["is_flying"] = { _ -> DoubleValue(entity.isFallFlying) }
map["is_sleeping"] = { _ -> DoubleValue(entity.isSleeping) }
map["health"] = { _ -> DoubleValue(entity.health) }
map["max_health"] = { _ -> DoubleValue(entity.maxHealth) }
map["look_at_position"] = { params ->
functions["is_living_entity"] = { DoubleValue.ONE }
functions["is_flying"] = { DoubleValue(entity.isFallFlying) }
functions["is_sleeping"] = { DoubleValue(entity.isSleeping) }
functions["health"] = { DoubleValue(entity.health) }
functions["max_health"] = { DoubleValue(entity.maxHealth) }
functions["look_at_position"] = { params ->
val x = params.getDouble(0)
val y = params.getDouble(1)
val z = params.getDouble(2)
@@ -120,159 +122,174 @@ object LivingEntityMoLangFunctions : AbstractMoLangFunctionHolder<LivingEntity>(
brain.setMemoryWithExpiry(
MemoryModuleType.LOOK_TARGET,
CobblemonBlockPosTracker(Vec3(x, y, z), flags.toSet()),
duration.toLong()
duration.toLong(),
)
}
map["get_wander_control_memory"] = put@{
val value = entity.brain.getMemorySafely(CobblemonMemories.WANDER_CONTROL).orElse(null)
?: CobblemonWanderControl()
functions["get_wander_control_memory"] = {
val value =
entity.brain.getMemorySafely(CobblemonMemories.WANDER_CONTROL).orElse(null)
?: CobblemonWanderControl()
if (
!entity.brain.checkMemory(CobblemonMemories.WANDER_CONTROL, MemoryStatus.VALUE_PRESENT)
&& entity.brain.checkMemory(CobblemonMemories.WANDER_CONTROL, MemoryStatus.REGISTERED)
!entity.brain.checkMemory(CobblemonMemories.WANDER_CONTROL, MemoryStatus.VALUE_PRESENT) &&
entity.brain.checkMemory(CobblemonMemories.WANDER_CONTROL, MemoryStatus.REGISTERED)
) {
entity.brain.setMemory(CobblemonMemories.WANDER_CONTROL, value)
}
return@put value.struct
value.struct
}
map["get_position_memory"] = put@{ params ->
functions["get_position_memory"] = getPosMemory@{ params ->
val id = params.getString(0).asIdentifierDefaultingNamespace()
val memoryType = BuiltInRegistries.MEMORY_MODULE_TYPE.get(id)
if (entity.brain.checkMemory(memoryType, MemoryStatus.VALUE_PRESENT)) {
return@put when (val memory = entity.brain.getMemory(memoryType).get()) {
is Vec3i -> VariableStruct(
mapOf(
"x" to DoubleValue(memory.x),
"y" to DoubleValue(memory.y),
"z" to DoubleValue(memory.z)
return@getPosMemory when (val memory = entity.brain.getMemory(memoryType).get()) {
is Vec3i -> {
VariableStruct(
mapOf(
"x" to DoubleValue(memory.x),
"y" to DoubleValue(memory.y),
"z" to DoubleValue(memory.z),
),
)
)
}
is Position -> VariableStruct(
mapOf(
"x" to DoubleValue(memory.x()),
"y" to DoubleValue(memory.y()),
"z" to DoubleValue(memory.z())
is Position -> {
VariableStruct(
mapOf(
"x" to DoubleValue(memory.x()),
"y" to DoubleValue(memory.y()),
"z" to DoubleValue(memory.z()),
),
)
)
}
else -> DoubleValue.ZERO
else -> {
DoubleValue.ZERO
}
}
} else {
return@put DoubleValue.ZERO
}
DoubleValue.ZERO
}
map["set_uuid_memory"] = put@{ params ->
val memory = params.getString(0).asIdentifierDefaultingNamespace()
.let(BuiltInRegistries.MEMORY_MODULE_TYPE::get) as MemoryModuleType<UUID>
val uuid = params.getString(1).asUUID ?: return@put DoubleValue.ZERO
functions["set_uuid_memory"] = setUuidMemory@{ params ->
val memory =
params
.getString(0)
.asIdentifierDefaultingNamespace()
.let(BuiltInRegistries.MEMORY_MODULE_TYPE::get) as MemoryModuleType<UUID>
val uuid = params.getString(1).asUUID ?: return@setUuidMemory DoubleValue.ZERO
val expiry = params.getIntOrNull(2) ?: -1
if (expiry != -1) {
entity.brain.setMemoryWithExpiry(memory, uuid, expiry.toLong())
} else {
entity.brain.setMemory(memory, uuid)
}
return@put DoubleValue.ONE
DoubleValue.ONE
}
map["erase_memory"] = put@{ params ->
val memories = params.params.map { it.asString().asIdentifierDefaultingNamespace() }
.map(BuiltInRegistries.MEMORY_MODULE_TYPE::get)
functions["erase_memory"] = { params ->
val memories =
params.params
.map { it.asString().asIdentifierDefaultingNamespace() }
.map(BuiltInRegistries.MEMORY_MODULE_TYPE::get)
memories.forEach { entity.brain.eraseMemory(it) }
return@put DoubleValue.ONE
DoubleValue.ONE
}
map["has_memory_value"] = put@{ params ->
functions["has_memory_value"] = hasMemoryValue@{ params ->
for (param in params.params) {
if (!entity.hasMemoryFromString(param.asString())) {
return@put DoubleValue.ZERO
return@hasMemoryValue DoubleValue.ZERO
}
}
return@put DoubleValue.ONE
DoubleValue.ONE
}
map["lacks_memory_value"] = put@{ params ->
functions["lacks_memory_value"] = lacksMemoryValue@{ params ->
for (param in params.params) {
if (entity.hasMemoryFromString(param.asString())) {
return@put DoubleValue.ZERO
return@lacksMemoryValue DoubleValue.ZERO
}
}
return@put DoubleValue.ONE
DoubleValue.ONE
}
map["is_doing_activity"] = put@{ params ->
functions["is_doing_activity"] = isDoingActivity@{ params ->
for (param in params.params) {
if (entity.isDoingActivityFromString(param.asString())) {
return@put DoubleValue.ONE
return@isDoingActivity DoubleValue.ONE
}
}
return@put DoubleValue.ZERO
DoubleValue.ZERO
}
map["can_see"] = put@{ params ->
functions["can_see"] = canSee@{ params ->
val target = params.get<MoValue>(0)
val range = params.getDoubleOrNull(1) ?: 32.0
if (target is ObjectValue<*>) {
val targetEntity = target.obj as? LivingEntity ?: return@put DoubleValue.ZERO
val targetEntity = target.obj as? LivingEntity ?: return@canSee DoubleValue.ZERO
val vector = targetEntity.eyePosition.subtract(entity.eyePosition).normalize()
val trace = entity.traceEntityCollision(
maxDistance = range.toFloat(),
stepDistance = 0.1F,
direction = vector,
entityClass = targetEntity.javaClass,
collideBlock = ClipContext.Fluid.NONE
)
return@put DoubleValue(targetEntity in (trace?.entities ?: emptyList()))
} else {
return@put DoubleValue.ZERO
val trace =
entity.traceEntityCollision(
maxDistance = range.toFloat(),
stepDistance = 0.1F,
direction = vector,
entityClass = targetEntity.javaClass,
collideBlock = ClipContext.Fluid.NONE,
)
return@canSee DoubleValue(targetEntity in (trace?.entities ?: emptyList()))
}
DoubleValue.ZERO
}
when (entity) {
is PathfinderMob -> {
functions["walk_to"] = { params ->
val x = params.getDouble(0)
val y = params.getDouble(1)
val z = params.getDouble(2)
val speedMultiplier = params.getDoubleOrNull(3) ?: 0.35
if (entity.brain.checkMemory(MemoryModuleType.WALK_TARGET, MemoryStatus.REGISTERED)) {
entity.brain.setMemory(
MemoryModuleType.WALK_TARGET,
WalkTarget(Vec3(x, y, z), speedMultiplier.toFloat(), 1),
)
entity.brain.setMemory(
MemoryModuleType.LOOK_TARGET,
BlockPosTracker(Vec3(x, y + entity.eyeHeight, z)),
)
} else {
entity.navigation.moveTo(x, y, z, speedMultiplier)
entity.lookControl.setLookAt(Vec3(x, y + entity.eyeHeight, z))
}
}
functions["has_walk_target"] = {
DoubleValue(entity.brain.getMemorySafely(MemoryModuleType.WALK_TARGET).isPresent || entity.isPathFinding)
}
functions["set_can_float"] = { params ->
val canFloat = params.getBooleanOrNull(0) != false
entity.navigation.setCanFloat(canFloat)
DoubleValue.ONE
}
functions["get_pathfinding_malus"] = getPfMalus@{ params ->
val type = PathType.entries.find { it.name == params.getString(0).uppercase() }
if (type != null) {
return@getPfMalus DoubleValue(entity.getPathfindingMalus(type))
}
Cobblemon.LOGGER.error("Cannot get pathfinding type, unknown value: ${params.getString(0)}")
DoubleValue.ZERO
}
functions["set_pathfinding_malus"] = setPfMalus@{ params ->
val type = PathType.entries.find { it.name == params.getString(0).uppercase() }
val malus = params.getDouble(1).toFloat()
if (type != null) {
entity.setPathfindingMalus(type, malus)
return@setPfMalus DoubleValue.ONE
}
Cobblemon.LOGGER.error("Cannot set pathfinding type, unknown value: ${params.getString(0)}")
DoubleValue.ZERO
}
}
}
if (entity is PathfinderMob) {
map["walk_to"] = { params ->
val x = params.getDouble(0)
val y = params.getDouble(1)
val z = params.getDouble(2)
val speedMultiplier = params.getDoubleOrNull(3) ?: 0.35
if (entity.brain.checkMemory(MemoryModuleType.WALK_TARGET, MemoryStatus.REGISTERED)) {
entity.brain.setMemory(
MemoryModuleType.WALK_TARGET,
WalkTarget(Vec3(x, y, z), speedMultiplier.toFloat(), 1)
)
entity.brain.setMemory(
MemoryModuleType.LOOK_TARGET,
BlockPosTracker(Vec3(x, y + entity.eyeHeight, z))
)
} else {
entity.navigation.moveTo(x, y, z, speedMultiplier)
entity.lookControl.setLookAt(Vec3(x, y + entity.eyeHeight, z))
}
}
map["has_walk_target"] = { _ ->
DoubleValue(entity.brain.getMemorySafely(MemoryModuleType.WALK_TARGET).isPresent || entity.isPathFinding)
}
map["set_can_float"] = { params ->
val canFloat = params.getBooleanOrNull(0) != false
entity.navigation.setCanFloat(canFloat)
DoubleValue.ONE
}
map["get_pathfinding_malus"] = put@{ params ->
val type = PathType.entries.find { it.name == params.getString(0).uppercase() }
if (type != null) {
return@put DoubleValue(entity.getPathfindingMalus(type))
}
Cobblemon.LOGGER.error("Unknown pathfinding type: ${params.getString(0)}")
return@put DoubleValue.ZERO
}
map["set_pathfinding_malus"] = put@{ params ->
val type = PathType.entries.find { it.name == params.getString(0).uppercase() }
val malus = params.getDouble(1).toFloat()
if (type != null) {
entity.setPathfindingMalus(type, malus)
return@put DoubleValue.ONE
}
Cobblemon.LOGGER.error("Unknown pathfinding type: ${params.getString(0)}")
return@put DoubleValue.ZERO
}
}
return map
return functions
}
}
@@ -50,18 +50,23 @@ import kotlin.collections.any
object NPCMoLangFunctions : AbstractMoLangFunctionHolder<NPCEntity>() {
override fun NPCEntity.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
val npc = this
val map = hashMapOf<String, (MoParams) -> Any>()
map["class"] = { StringValue(npc.npc.id.toString()) }
map["name"] = { StringValue(npc.name.string) }
map["level"] = { DoubleValue(npc.level) }
map["has_aspect"] = { params -> DoubleValue(npc.aspects.contains(params.getString(0))) }
map["in_battle"] = { DoubleValue(npc.isInBattle()) }
map["battles"] = {
ArrayStruct(npc.battleIds.mapNotNull { BattleRegistry.getBattle(it)?.struct }
.mapIndexed { index, value -> "$index" to value }.toMap())
val functions = hashMapOf<String, (MoParams) -> Any>()
functions["class"] = { StringValue(npc.npc.id.toString()) }
functions["name"] = { StringValue(npc.name.string) }
functions["level"] = { DoubleValue(npc.level) }
functions["has_aspect"] = { params -> DoubleValue(npc.aspects.contains(params.getString(0))) }
functions["in_battle"] = { DoubleValue(npc.isInBattle()) }
functions["battles"] = {
ArrayStruct(
npc.battleIds
.mapNotNull { BattleRegistry.getBattle(it)?.struct }
.mapIndexed { index, value -> "$index" to value }
.toMap(),
)
}
map["stop_battles"] = { _ -> npc.battleIds.forEach { BattleRegistry.getBattle(it)?.stop() } }
map["run_script_on_client"] = { params ->
functions["stop_battles"] = { npc.battleIds.forEach { BattleRegistry.getBattle(it)?.stop() } }
functions["run_script_on_client"] = { params ->
val world = npc.level()
if (world is ServerLevel) {
val script = params.getString(0)
@@ -70,141 +75,142 @@ object NPCMoLangFunctions : AbstractMoLangFunctionHolder<NPCEntity>() {
}
Unit
}
map["run_script"] = { params ->
functions["run_script"] = { params ->
val script = params.getString(0).asIdentifierDefaultingNamespace()
val runtime = MoLangRuntime()
runtime.environment.cloneFrom(params.environment)
CobblemonScripts.run(script, runtime) ?: DoubleValue(0)
}
map["set_movable"] = put@{ params ->
functions["set_movable"] = { params ->
val movable = params.getBooleanOrNull(0) != false
npc.isMovable = movable
return@put DoubleValue.ONE
DoubleValue.ONE
}
map["set_invulnerable"] = put@{ params ->
functions["set_invulnerable"] = { params ->
val invulnerable = params.getBooleanOrNull(0) != false
npc.isInvulnerable = invulnerable
return@put DoubleValue.ONE
DoubleValue.ONE
}
map["set_leashable"] = put@{ params ->
functions["set_leashable"] = { params ->
val leashable = params.getBooleanOrNull(0) != false
npc.isLeashable = leashable
return@put DoubleValue.ONE
DoubleValue.ONE
}
map["set_allow_projectile_hits"] = put@{ params ->
functions["set_allow_projectile_hits"] = { params ->
val allowProjectileHits = params.getBooleanOrNull(0) != false
npc.allowProjectileHits = allowProjectileHits
return@put DoubleValue.ONE
DoubleValue.ONE
}
map["set_name_tag_visible"] = put@{ params ->
functions["set_name_tag_visible"] = { params ->
val nameTagVisible = params.getBooleanOrNull(0) != false
npc.hideNameTag = !nameTagVisible
return@put DoubleValue.ONE
DoubleValue.ONE
}
map["unset_interaction"] = put@{
functions["unset_interaction"] = {
npc.interaction = null
return@put DoubleValue.ONE
DoubleValue.ONE
}
map["set_dialogue_interaction"] = put@{ params ->
functions["set_dialogue_interaction"] = { params ->
val dialogue = params.getString(0).asIdentifierDefaultingNamespace()
npc.interaction = DialogueNPCInteractionConfiguration().also {
it.dialogue = dialogue
}
return@put DoubleValue.ONE
npc.interaction =
DialogueNPCInteractionConfiguration().also {
it.dialogue = dialogue
}
DoubleValue.ONE
}
map["set_script_interaction"] = put@{ params ->
functions["set_script_interaction"] = { params ->
val script = params.getString(0).asIdentifierDefaultingNamespace()
npc.interaction = ScriptNPCInteractionConfiguration().also {
it.script = script
}
return@put DoubleValue.ONE
npc.interaction =
ScriptNPCInteractionConfiguration().also {
it.script = script
}
DoubleValue.ONE
}
map["set_player_texture"] = put@{ params ->
functions["set_player_texture"] = setPlayerTexture@{ params ->
val username = params.getString(0)
// Re-applying it would be unnecessarily laggy.
if (username == npc.data.map["player_texture_username"]?.asString()) {
return@put DoubleValue.ZERO
return@setPlayerTexture DoubleValue.ZERO
}
npc.loadTextureFromGameProfileName(username)
return@put DoubleValue.ONE
DoubleValue.ONE
}
map["unset_player_texture"] = put@{
functions["unset_player_texture"] = {
npc.unloadTexture()
return@put DoubleValue.ONE
DoubleValue.ONE
}
map["set_resource_identifier"] = put@{ params ->
functions["set_resource_identifier"] = { params ->
val identifier = params.getStringOrNull(0)?.asIdentifierDefaultingNamespace()
npc.forcedResourceIdentifier = identifier
return@put DoubleValue.ONE
DoubleValue.ONE
}
map["unset_resource_identifier"] = put@{
functions["unset_resource_identifier"] = {
npc.forcedResourceIdentifier = null
return@put DoubleValue.ONE
DoubleValue.ONE
}
map["set_class"] = put@{ params ->
functions["set_class"] = setClass@{ params ->
val identifier = params.getString(0).asIdentifierDefaultingNamespace()
val npcClass = NPCClasses.getByIdentifier(identifier)
if (npcClass != null) {
npc.npc = npcClass
return@put DoubleValue.ONE
} else {
Cobblemon.LOGGER.error("Unknown NPC class: $identifier")
return@put DoubleValue.ZERO
return@setClass DoubleValue.ONE
}
Cobblemon.LOGGER.error("Unknown NPC class: $identifier")
DoubleValue.ZERO
}
map["set_render_scale"] = put@{ params ->
functions["set_render_scale"] = { params ->
val scale = params.getDouble(0)
npc.renderScale = scale.toFloat()
return@put DoubleValue.ONE
DoubleValue.ONE
}
map["render_scale"] = { _ -> DoubleValue(npc.renderScale) }
map["set_hitbox_scale"] = put@{ params ->
functions["render_scale"] = { DoubleValue(npc.renderScale) }
functions["set_hitbox_scale"] = { params ->
val scale = params.getDouble(0).toFloat()
npc.hitboxScale = scale
npc.refreshDimensions()
return@put DoubleValue.ONE
DoubleValue.ONE
}
map["hitbox_scale"] = { _ -> DoubleValue(npc.hitboxScale) }
map["set_hitbox"] = put@{ params ->
functions["hitbox_scale"] = { DoubleValue(npc.hitboxScale) }
functions["set_hitbox"] = setHitbox@{ params ->
if (params.params.isEmpty()) {
npc.hitbox = null
return@put DoubleValue.ONE
return@setHitbox DoubleValue.ONE
}
val width = params.getDouble(0).toFloat()
val height = params.getDouble(1).toFloat()
val eyeHeight = params.getDoubleOrNull(2)?.toFloat() ?: (height * 0.85F)
npc.hitbox = EntityDimensions.scalable(width, height).withEyeHeight(eyeHeight)
return@put DoubleValue.ONE
DoubleValue.ONE
}
map["unset_hitbox"] = put@{
functions["unset_hitbox"] = {
npc.hitbox = null
return@put DoubleValue.ONE
DoubleValue.ONE
}
map["aspects"] = put@{
functions["aspects"] = {
val aspects = npc.aspects
return@put aspects.asArrayValue { StringValue(it) }
aspects.asArrayValue { StringValue(it) }
}
map["add_aspect"] = put@{ params ->
functions["add_aspect"] = { params ->
val aspects = params.params.map { it.asString() }
npc.appliedAspects.addAll(aspects)
npc.updateAspects()
return@put DoubleValue.ONE
DoubleValue.ONE
}
map["remove_aspect"] = put@{ params ->
functions["remove_aspect"] = { params ->
val aspects = params.params.map { it.asString() }
npc.appliedAspects.removeAll(aspects.toSet())
npc.updateAspects()
return@put DoubleValue.ONE
DoubleValue.ONE
}
map["party"] = put@{
val party = npc.party ?: return@put DoubleValue.ZERO
return@put party.asMoLangValue()
functions["party"] = party@{
val party = npc.party ?: return@party DoubleValue.ZERO
party.asMoLangValue()
}
map["create_npc_party"] = put@{
functions["create_npc_party"] = {
val party = NPCPartyStore(npc)
return@put party.asMoLangValue()
party.asMoLangValue()
}
map["set_npc_party"] = { params ->
functions["set_npc_party"] = { params ->
val party = params.get<MoValue>(0)
if (party is ObjectValue<*>) {
npc.party = party.obj as NPCPartyStore
@@ -213,18 +219,19 @@ object NPCMoLangFunctions : AbstractMoLangFunctionHolder<NPCEntity>() {
}
DoubleValue.ONE
}
map["run_action_effect"] = put@{ params ->
functions["run_action_effect"] = runActionEffect@{ params ->
val runtime = MoLangRuntime().setup()
runtime.environment.cloneFrom(params.environment)
runtime.withNPCValue(value = npc)
val actionEffect = ActionEffects.actionEffects[params.getString(0).asIdentifierDefaultingNamespace()]
if (actionEffect != null) {
val context = ActionEffectContext(
actionEffect = actionEffect,
providers = mutableListOf(NPCProvider(npc)),
runtime = runtime,
level = npc.level()
)
val context =
ActionEffectContext(
actionEffect = actionEffect,
providers = mutableListOf(NPCProvider(npc)),
runtime = runtime,
level = npc.level(),
)
npc.actionEffect = context
npc.brain.setMemory(CobblemonMemories.ACTIVE_ACTION_EFFECT, context)
npc.brain.setActiveActivityIfPossible(CobblemonActivities.ACTION_EFFECT)
@@ -236,37 +243,40 @@ object NPCMoLangFunctions : AbstractMoLangFunctionHolder<NPCEntity>() {
}
}
return@put DoubleValue.ONE
return@runActionEffect DoubleValue.ONE
}
return@put DoubleValue.ZERO
DoubleValue.ZERO
}
map["put_pokemon_in_healer"] = put@{ params ->
val healer = npc.level()
.getBlockEntity(params.get<ArrayStruct>(0).asBlockPos(), CobblemonBlockEntities.HEALING_MACHINE)
.orElse(null) ?: return@put DoubleValue.ZERO
val party = npc.party ?: return@put DoubleValue.ZERO
functions["put_pokemon_in_healer"] = putPokeInHealer@{ params ->
val healer =
npc
.level()
.getBlockEntity(params.get<ArrayStruct>(0).asBlockPos(), CobblemonBlockEntities.HEALING_MACHINE)
.orElse(null) ?: return@putPokeInHealer DoubleValue.ZERO
val party = npc.party ?: return@putPokeInHealer DoubleValue.ZERO
if (healer.canHeal(party)) {
healer.activate(npc.uuid, party)
return@put DoubleValue.ONE
} else {
return@put DoubleValue.ZERO
return@putPokeInHealer DoubleValue.ONE
}
DoubleValue.ZERO
}
map["look_at_position"] = { params ->
functions["look_at_position"] = { params ->
val pos = Vec3(params.getDouble(0), params.getDouble(1), params.getDouble(2))
npc.lookAt(EntityAnchorArgument.Anchor.EYES, pos)
}
map["environment"] = { _ -> npc.runtime.environment }
map["party"] = { npc.party?.struct ?: DoubleValue.ZERO }
map["has_party"] = { DoubleValue(npc.party != null) }
map["is_npc"] = { DoubleValue.ONE }
map["can_battle"] =
{ DoubleValue(npc.party?.any { it.currentHealth > 0 } == true || npc.npc.party?.isStatic == false) }
map["set_battle_theme"] = put@{ params ->
functions["environment"] = { npc.runtime.environment }
functions["party"] = { npc.party?.struct ?: DoubleValue.ZERO }
functions["has_party"] = { DoubleValue(npc.party != null) }
functions["is_npc"] = { DoubleValue.ONE }
functions["can_battle"] = {
DoubleValue(npc.party?.any { it.currentHealth > 0 } == true || npc.npc.party?.isStatic == false)
}
functions["set_battle_theme"] = { params ->
val soundId = params.getString(0).asResource()
npc.npc.battleTheme = soundId
return@put DoubleValue.ONE
DoubleValue.ONE
}
return map
return functions
}
}
@@ -23,40 +23,40 @@ import com.cobblemon.mod.common.util.getBooleanOrNull
object PCMoLangFunctions : AbstractMoLangFunctionHolder<PCStore>() {
override fun PCStore.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
val pc = this
val map = mutableMapOf<String, (MoParams) -> Any>()
val functions = hashMapOf<String, (MoParams) -> Any>()
map["get_pokemon"] = getPokemon@{ params ->
functions["get_pokemon"] = getPokemon@{ params ->
val box = params.getInt(0)
val slot = params.getInt(1)
val pokemon = pc[PCPosition(box, slot)] ?: return@getPokemon DoubleValue.ZERO
return@getPokemon pokemon.struct
pokemon.struct
}
map["set_pokemon"] = { params ->
functions["set_pokemon"] = { params ->
val box = params.getInt(0)
val slot = params.getInt(1)
val pokemon = params.get<ObjectValue<Pokemon>>(2).obj
pc[PCPosition(box, slot)] = pokemon
DoubleValue.ONE
}
map["resize"] = { params ->
functions["resize"] = { params ->
val newSize = params.getInt(0)
val lockNewSize = params.getBooleanOrNull(1) == true
pc.resize(newSize, lockNewSize)
DoubleValue.ONE
}
map["get_box_count"] = { DoubleValue(pc.boxes.size.toDouble()) }
map["has_unlocked_wallpaper"] = { params ->
functions["get_box_count"] = { DoubleValue(pc.boxes.size.toDouble()) }
functions["has_unlocked_wallpaper"] = { params ->
val wallpaper = params.getString(0).asIdentifierDefaultingNamespace()
DoubleValue(pc.unlockedWallpapers.contains(wallpaper))
}
map["get_unlocked_wallpapers"] = { pc.unlockedWallpapers.asArrayValue { StringValue(it.toString()) } }
map["unlock_wallpaper"] = unlockWallpaper@{
functions["get_unlocked_wallpapers"] = { pc.unlockedWallpapers.asArrayValue { StringValue(it.toString()) } }
functions["unlock_wallpaper"] = unlockWallpaper@{
val wallpaper = it.getString(0).asIdentifierDefaultingNamespace()
val playSound = it.getBooleanOrNull(1) != false
CobblemonUnlockableWallpapers.unlockableWallpapers[wallpaper] ?: return@unlockWallpaper DoubleValue.ZERO
return@unlockWallpaper DoubleValue(pc.unlockWallpaper(wallpaper, playSound))
DoubleValue(pc.unlockWallpaper(wallpaper, playSound))
}
return map
return functions
}
}
@@ -14,21 +14,23 @@ import com.cobblemon.mod.common.api.molang.ObjectValue
import com.cobblemon.mod.common.api.storage.party.PartyStore
import com.cobblemon.mod.common.pokemon.Pokemon
object PartyMoLangFunctions: AbstractMoLangFunctionHolder<PartyStore>() {
object PartyMoLangFunctions : AbstractMoLangFunctionHolder<PartyStore>() {
override fun PartyStore.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
val party = this
val map = hashMapOf<String, (MoParams) -> Any>()
map["get_pokemon"] = getPokemon@{ params ->
val functions = hashMapOf<String, (MoParams) -> Any>()
functions["get_pokemon"] = getPokemon@{ params ->
val index = params.getInt(0)
val pokemon = party.get(index) ?: return@getPokemon DoubleValue.ZERO
pokemon.struct
}
map["set_pokemon"] = { params ->
functions["set_pokemon"] = { params ->
val index = params.getInt(0)
val pokemon = params.get<ObjectValue<Pokemon>>(1).obj
party.set(index, pokemon)
DoubleValue.ONE
}
return map
return functions
}
}
@@ -20,7 +20,9 @@ import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.api.dialogue.PlayerDialogueFaceProvider
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.ObjectValue
import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.api.tms.TechnicalMachines
import com.cobblemon.mod.common.battles.BattleBuilder
import com.cobblemon.mod.common.battles.BattleFormat
import com.cobblemon.mod.common.entity.npc.NPCEntity
@@ -44,343 +46,395 @@ import com.cobblemon.mod.common.util.party
import com.cobblemon.mod.common.util.pc
import com.cobblemon.mod.common.util.pokedex
import com.cobblemon.mod.common.util.server
import com.cobblemon.mod.common.util.tmList
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.core.registries.Registries
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.sounds.SoundSource
import net.minecraft.stats.Stats
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.GameType
import kotlin.collections.iterator
object PlayerMoLangFunctions : AbstractMoLangFunctionHolder<Player>() {
override fun Player.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
val player = this
val map = mutableMapOf<String, (MoParams) -> Any>()
map["username"] = { _ -> StringValue(player.gameProfile.name) }
map["uuid"] = { _ -> StringValue(player.gameProfile.id.toString()) }
map["main_held_item"] = { _ -> player.mainHandItem.asMoLangValue(player.registryAccess()) }
map["off_held_item"] = { _ -> player.offhandItem.asMoLangValue(player.registryAccess()) }
map["inventory"] = put@{ _ ->
val functions = hashMapOf<String, (MoParams) -> Any>()
functions["username"] = { StringValue(player.gameProfile.name) }
functions["uuid"] = { StringValue(player.gameProfile.id.toString()) }
functions["main_held_item"] = { player.mainHandItem.asMoLangValue(player.registryAccess()) }
functions["off_held_item"] = { player.offhandItem.asMoLangValue(player.registryAccess()) }
functions["inventory"] = {
val inventory = player.inventory
val items = ArrayStruct(hashMapOf())
for (i in 0 until inventory.containerSize) {
items.setDirectly("$i", inventory.getItem(i).asMoLangValue(player.registryAccess()))
}
return@put items
items
}
map["has_inventory_space"] = put@{ _ ->
functions["has_inventory_space"] = {
val inventory = player.inventory
return@put if (inventory.getFreeSlot() != -1) DoubleValue.ONE else DoubleValue.ZERO
if (inventory.freeSlot != -1) DoubleValue.ONE else DoubleValue.ZERO
}
map["set_inventory_slot"] = put@{ params ->
functions["set_inventory_slot"] = setInvSlot@{ params ->
val slot = params.getInt(0)
val inventory = player.inventory
if (slot !in 0 until inventory.containerSize) return@put DoubleValue.ZERO
if (slot !in 0 until inventory.containerSize) return@setInvSlot DoubleValue.ZERO
val value = params.getOrNull<MoValue>(1) ?: return@put DoubleValue.ZERO
val value = params.getOrNull<MoValue>(1) ?: return@setInvSlot DoubleValue.ZERO
val stack: ItemStack? = when (value) {
is ObjectValue<*> -> value.obj as? ItemStack
is StringValue -> {
val id = ResourceLocation.parse(value.value)
val item = player.registryAccess().registryOrThrow(Registries.ITEM).get(id)
?: return@put DoubleValue.ZERO
ItemStack(item)
val stack: ItemStack? =
when (value) {
is ObjectValue<*> -> {
value.obj as? ItemStack
}
is StringValue -> {
val id = ResourceLocation.parse(value.value)
val item =
player.registryAccess().registryOrThrow(Registries.ITEM).get(id)
?: return@setInvSlot DoubleValue.ZERO
ItemStack(item)
}
else -> {
null
}
}
else -> null
}
if (stack == null) return@put DoubleValue.ZERO
if (stack == null) return@setInvSlot DoubleValue.ZERO
inventory.setItem(slot, stack)
return@put DoubleValue.ONE
DoubleValue.ONE
}
map["give_item"] = put@{ params ->
functions["give_item"] = giveItem@{ params ->
val inventory = player.inventory
val freeSlot = inventory.getFreeSlot()
val value = params.getOrNull<MoValue>(0) ?: return@put DoubleValue.ZERO
val freeSlot = inventory.freeSlot
val value = params.getOrNull<MoValue>(0) ?: return@giveItem DoubleValue.ZERO
val dropIfFull = params.getBooleanOrNull(1) ?: false
val stack: ItemStack? = when (value) {
is ObjectValue<*> -> value.obj as? ItemStack
is StringValue -> {
val id = ResourceLocation.parse(value.value)
val item = player.registryAccess().registryOrThrow(Registries.ITEM).get(id)
?: return@put DoubleValue.ZERO
ItemStack(item)
val stack: ItemStack? =
when (value) {
is ObjectValue<*> -> {
value.obj as? ItemStack
}
is StringValue -> {
val id = ResourceLocation.parse(value.value)
val item =
player.registryAccess().registryOrThrow(Registries.ITEM).get(id)
?: return@giveItem DoubleValue.ZERO
ItemStack(item)
}
else -> {
null
}
}
else -> null
}
if (stack == null) return@put DoubleValue.ZERO
if (stack == null) return@giveItem DoubleValue.ZERO
if (freeSlot == -1) {
if (dropIfFull) {
player.drop(stack, false)
return@put DoubleValue.ONE
return@giveItem DoubleValue.ONE
} else {
return@put DoubleValue.ZERO
return@giveItem DoubleValue.ZERO
}
}
inventory.setItem(freeSlot, stack)
return@put DoubleValue.ONE
DoubleValue.ONE
}
map["face"] = { params ->
functions["face"] = { params ->
ObjectValue(
PlayerDialogueFaceProvider(
player.uuid,
params.getBooleanOrNull(0) != false
)
params.getBooleanOrNull(0) != false,
),
)
}
map["swing_hand"] = { _ -> player.swing(player.usedItemHand) }
map["food_level"] = { _ -> DoubleValue(player.foodData.foodLevel) }
map["saturation_level"] = { _ -> DoubleValue(player.foodData.saturationLevel) }
map["tell"] = { params ->
functions["swing_hand"] = { player.swing(player.usedItemHand) }
functions["food_level"] = { DoubleValue(player.foodData.foodLevel) }
functions["saturation_level"] = { DoubleValue(player.foodData.saturationLevel) }
functions["tell"] = { params ->
val message = params.getString(0).text()
val overlay = params.getBooleanOrNull(1) == true
player.displayClientMessage(message, overlay)
}
map["teleport"] = { params ->
functions["teleport"] = { params ->
val x = params.getDouble(0)
val y = params.getDouble(1)
val z = params.getDouble(2)
val playParticleOptionss = params.getBooleanOrNull(3) ?: false
player.randomTeleport(x, y, z, playParticleOptionss)
val playParticleOptions = params.getBooleanOrNull(3) ?: false
player.randomTeleport(x, y, z, playParticleOptions)
}
map["heal"] = { params ->
functions["heal"] = { params ->
val amount = params.getDoubleOrNull(0) ?: player.maxHealth
player.heal(amount.toFloat())
}
map["environment"] = {
functions["environment"] = {
val environment = MoLangEnvironment()
environment.query = player.asMoLangValue()
environment
}
map["is_player"] = { DoubleValue.ONE }
map["riding_pokemon"] = put@{
functions["is_player"] = { DoubleValue.ONE }
functions["riding_pokemon"] = ridingPokemon@{
val vehicle = player.vehicle
if (vehicle is PokemonEntity) {
return@put vehicle.struct
} else {
return@put DoubleValue.ZERO
return@ridingPokemon vehicle.struct
}
DoubleValue.ZERO
}
if (player is ServerPlayer) {
map["seen_credits"] = { _ ->
DoubleValue(player.seenCredits)
}
map["is_in_dialogue"] = { _ ->
DoubleValue(player.isInDialogue)
}
map["active_dialogue"] = put@{ _ ->
if (player.isInDialogue) {
return@put player.activeDialogue?.toMoLangStruct() ?: DoubleValue.ZERO
} else {
when (player) {
is ServerPlayer -> {
functions["seen_credits"] = { DoubleValue(player.seenCredits) }
functions["is_in_dialogue"] = { DoubleValue(player.isInDialogue) }
functions["active_dialogue"] = activeDialogue@{
if (player.isInDialogue) {
return@activeDialogue player.activeDialogue?.toMoLangStruct() ?: DoubleValue.ZERO
}
DoubleValue.ZERO
}
}
map["is_spectator"] = { DoubleValue(player.isSpectator) }
map["is_creative"] = { DoubleValue(player.isCreative) }
map["is_survival"] = { DoubleValue(player.gameMode.isSurvival) }
map["is_adventure"] = { DoubleValue(player.gameMode.gameModeForPlayer == GameType.ADVENTURE) }
map["run_command"] = { params ->
val command = params.getString(0)
player.server.commands.performPrefixedCommand(player.createCommandSourceStack(), command)
}
map["set_battle_theme"] = put@{ params ->
val soundId = params.getString(0).asResource()
Cobblemon.playerDataManager.getGenericData(player).battleTheme = soundId
return@put DoubleValue.ONE
}
map["battle_music"] = put@{ params ->
val soundId = params.getString(0).asResource()
val volume = params.getDoubleOrNull(1)?.toFloat() ?: 1.0f
val pitch = params.getDoubleOrNull(2)?.toFloat() ?: 1.0f
val restart = params.getBooleanOrNull(3) ?: true
functions["is_spectator"] = { DoubleValue(player.isSpectator) }
functions["is_creative"] = { DoubleValue(player.isCreative) }
functions["is_survival"] = { DoubleValue(player.gameMode.isSurvival) }
functions["is_adventure"] = { DoubleValue(player.gameMode.gameModeForPlayer == GameType.ADVENTURE) }
functions["run_command"] = { params ->
val command = params.getString(0)
player.server.commands.performPrefixedCommand(player.createCommandSourceStack(), command)
}
functions["set_battle_theme"] = { params ->
val soundId = params.getString(0).asResource()
Cobblemon.playerDataManager.getGenericData(player).battleTheme = soundId
DoubleValue.ONE
}
functions["battle_music"] = battleMusic@{ params ->
val soundId = params.getString(0).asResource()
val volume = params.getDoubleOrNull(1)?.toFloat() ?: 1.0f
val pitch = params.getDoubleOrNull(2)?.toFloat() ?: 1.0f
val restart = params.getBooleanOrNull(3) ?: true
if (soundId != null) {
val packet = BattleMusicPacket(soundId, volume, pitch, restart)
if (soundId != null) {
val packet = BattleMusicPacket(soundId, volume, pitch, restart)
packet.sendToPlayer(player)
return@battleMusic DoubleValue.ONE
}
DoubleValue.ZERO
}
functions["stop_battle_music"] = {
val packet = BattleMusicPacket(null)
packet.sendToPlayer(player)
return@put DoubleValue.ONE
} else {
return@put DoubleValue.ZERO
DoubleValue.ONE
}
}
map["stop_battle_music"] = put@{ _ ->
val packet = BattleMusicPacket(null)
packet.sendToPlayer(player)
return@put DoubleValue.ONE
}
map["play_sound_on_server"] = { params ->
val sound = params.getString(0).asResource()
val soundSource = SoundSource.valueOf(params.getString(1).uppercase())
val volume = params.getDoubleOrNull(2)?.toFloat() ?: 1.0f
val pitch = params.getDoubleOrNull(3)?.toFloat() ?: 1.0f
functions["play_sound_on_server"] = { params ->
val sound = params.getString(0).asResource()
val soundSource = SoundSource.valueOf(params.getString(1).uppercase())
val volume = params.getDoubleOrNull(2)?.toFloat() ?: 1.0f
val pitch = params.getDoubleOrNull(3)?.toFloat() ?: 1.0f
val packet =
UnvalidatedPlaySoundS2CPacket(sound, soundSource, player.x, player.y, player.z, volume, pitch)
packet.sendToPlayer(player)
}
map["is_party_at_full_health"] = { _ ->
DoubleValue(player.party().none(Pokemon::canBeHealed))
}
map["can_heal_at_healer"] = put@{ params ->
val pos = params.get<ArrayStruct>(0).asBlockPos()
val healer = player.level().getBlockEntity(pos, CobblemonBlockEntities.HEALING_MACHINE).orElse(null)
?: return@put DoubleValue.ZERO
val party = player.party()
return@put DoubleValue(healer.canHeal(party))
}
map["put_pokemon_in_healer"] = put@{ params ->
val healer = player.level()
.getBlockEntity(params.get<ArrayStruct>(0).asBlockPos(), CobblemonBlockEntities.HEALING_MACHINE)
.orElse(null) ?: return@put DoubleValue.ZERO
val party = player.party()
if (healer.canHeal(party)) {
healer.activate(player.uuid, party)
return@put DoubleValue.ONE
} else {
return@put DoubleValue.ZERO
val packet =
UnvalidatedPlaySoundS2CPacket(
sound,
soundSource,
player.x,
player.y,
player.z,
volume,
pitch,
)
packet.sendToPlayer(player)
}
}
map["party"] = { player.party().struct }
map["pc"] = { player.pc().struct }
map["has_permission"] = { params ->
DoubleValue(
Cobblemon.permissionValidator.hasPermission(
player,
params.getString(0),
params.getIntOrNull(1) ?: 4
functions["is_party_at_full_health"] = { DoubleValue(player.party().none(Pokemon::canBeHealed)) }
functions["can_heal_at_healer"] = canHealAtHealer@{ params ->
val pos = params.get<ArrayStruct>(0).asBlockPos()
val healer =
player.level().getBlockEntity(pos, CobblemonBlockEntities.HEALING_MACHINE).orElse(null)
?: return@canHealAtHealer DoubleValue.ZERO
val party = player.party()
DoubleValue(healer.canHeal(party))
}
functions["put_pokemon_in_healer"] = putPokemonAtHealer@{ params ->
val healer =
player
.level()
.getBlockEntity(
params.get<ArrayStruct>(0).asBlockPos(),
CobblemonBlockEntities.HEALING_MACHINE,
).orElse(null) ?: return@putPokemonAtHealer DoubleValue.ZERO
val party = player.party()
if (healer.canHeal(party)) {
healer.activate(player.uuid, party)
return@putPokemonAtHealer DoubleValue.ONE
}
DoubleValue.ZERO
}
functions["party"] = { player.party().struct }
functions["pc"] = { player.pc().struct }
functions["has_permission"] = { params ->
DoubleValue(
Cobblemon.permissionValidator.hasPermission(
player,
params.getString(0),
params.getIntOrNull(1) ?: 4,
),
)
)
}
map["data"] = { params -> Cobblemon.molangData.load(player.uuid, params.getStringOrNull(0)) }
map["save_data"] = { params -> Cobblemon.molangData.save(player.uuid, params.getStringOrNull(0)) }
map["in_battle"] = { DoubleValue(player.isInBattle()) }
map["battle"] = { player.getBattleState()?.first?.struct ?: DoubleValue.ZERO }
map["get_npc_data"] = put@{ params ->
val npcId = ((params.get<MoValue>(0) as? ObjectValue<*>)?.obj as? NPCEntity)?.stringUUID
?: params.getString(0)
val data = Cobblemon.molangData.load(player.uuid, params.getStringOrNull(1))
if (data.map.containsKey(npcId)) {
return@put data.map[npcId]!!
} else {
}
functions["data"] = { params -> Cobblemon.molangData.load(player.uuid, params.getStringOrNull(0)) }
functions["save_data"] = { params -> Cobblemon.molangData.save(player.uuid, params.getStringOrNull(0)) }
functions["in_battle"] = { DoubleValue(player.isInBattle()) }
functions["battle"] = { player.getBattleState()?.first?.struct ?: DoubleValue.ZERO }
functions["get_npc_data"] = getNpcData@{ params ->
val npcId =
((params.get<MoValue>(0) as? ObjectValue<*>)?.obj as? NPCEntity)?.stringUUID
?: params.getString(0)
val data = Cobblemon.molangData.load(player.uuid, params.getStringOrNull(1))
if (data.map.containsKey(npcId)) {
return@getNpcData data.map[npcId]!!
}
val vars = VariableStruct()
data.map[npcId] = vars
return@put vars
vars
}
}
map["get_npc_variable"] = put@{ params ->
val npcId = ((params.get<MoValue>(0) as? ObjectValue<*>)?.obj as? NPCEntity)?.stringUUID
?: params.getString(0)
val variable = params.getString(1)
val data = Cobblemon.molangData.load(player.uuid, params.getStringOrNull(2))
if (data.map.containsKey(npcId)) {
return@put (data.map[npcId] as VariableStruct).map[variable] ?: DoubleValue.ZERO
} else {
return@put DoubleValue.ZERO
}
}
map["set_npc_variable"] = put@{ params ->
val npcId = ((params.get<MoValue>(0) as? ObjectValue<*>)?.obj as? NPCEntity)?.stringUUID
?: params.getString(0)
val variable = params.getString(1)
val value = params.get<MoValue>(2)
val saveAfterwards = params.getBooleanOrNull(3) != false
val path = params.getStringOrNull(4)
val data = Cobblemon.molangData.load(player.uuid, path)
val npcData = data.map.getOrPut(npcId) { VariableStruct() } as VariableStruct
npcData.map[variable] = value
if (saveAfterwards) {
Cobblemon.molangData.save(player.uuid, path)
}
return@put DoubleValue.ONE
}
map["pokedex"] = { player.pokedex().struct }
map["has_advancement"] = put@{ params ->
val requiredAdvancement = ResourceLocation.parse(params.getString(0))
for (entry in player.advancements.progress) {
if (entry.key.id == requiredAdvancement && entry.value.isDone) {
return@put DoubleValue.ONE
functions["get_npc_variable"] = getNpcVariable@{ params ->
val npcId =
((params.get<MoValue>(0) as? ObjectValue<*>)?.obj as? NPCEntity)?.stringUUID
?: params.getString(0)
val variable = params.getString(1)
val data = Cobblemon.molangData.load(player.uuid, params.getStringOrNull(2))
if (data.map.containsKey(npcId)) {
return@getNpcVariable (data.map[npcId] as VariableStruct).map[variable] ?: DoubleValue.ZERO
}
DoubleValue.ZERO
}
return@put DoubleValue.ZERO
}
map["start_battle"] = put@{ params ->
val opponentValue = params.get<MoValue>(0)
val opponent = if (opponentValue is ObjectValue<*>) {
opponentValue.obj as ServerPlayer
} else {
val paramString = opponentValue.asString()
val playerUUID = paramString.asUUID
if (playerUUID != null) {
server()?.playerList?.getPlayer(playerUUID) ?: return@put DoubleValue.ZERO
} else {
server()?.playerList?.getPlayerByName(paramString) ?: return@put DoubleValue.ZERO
functions["set_npc_variable"] = { params ->
val npcId =
((params.get<MoValue>(0) as? ObjectValue<*>)?.obj as? NPCEntity)?.stringUUID
?: params.getString(0)
val variable = params.getString(1)
val value = params.get<MoValue>(2)
val saveAfterwards = params.getBooleanOrNull(3) != false
val path = params.getStringOrNull(4)
val data = Cobblemon.molangData.load(player.uuid, path)
val npcData = data.map.getOrPut(npcId) { VariableStruct() } as VariableStruct
npcData.map[variable] = value
if (saveAfterwards) {
Cobblemon.molangData.save(player.uuid, path)
}
DoubleValue.ONE
}
val format = params.getStringOrNull(1)
?.let(BattleFormat::fromFormatIdentifier)
?: BattleFormat.GEN_9_SINGLES
functions["pokedex"] = { player.pokedex().struct }
functions["has_advancement"] = hasAdvancement@{ params ->
val requiredAdvancement = ResourceLocation.parse(params.getString(0))
player.advancements.progress.forEach { (holder, progress) ->
if (holder.id == requiredAdvancement && progress.isDone) {
return@hasAdvancement DoubleValue.ONE
}
}
DoubleValue.ZERO
}
functions["has_tm_move_unlocked"] = hasTmMoveUnlocked@{ params ->
val move = Moves.getByName(params.getString(0)) ?: return@hasTmMoveUnlocked DoubleValue.ZERO
val tm = TechnicalMachines.moveToTM[move] ?: return@hasTmMoveUnlocked DoubleValue.ZERO
DoubleValue(if (player.tmList().learnedTMs.contains(tm.id)) 1.0 else 0.0)
}
functions["unlock_tm_move"] = unlockTmMove@{ params ->
val move = Moves.getByName(params.getString(0)) ?: return@unlockTmMove DoubleValue.ZERO
val tm = TechnicalMachines.moveToTM[move] ?: return@unlockTmMove DoubleValue.ZERO
player.tmList().syncTMFromMove(move)
DoubleValue(if (player.tmList().learnedTMs.contains(tm.id)) 1.0 else 0.0)
}
functions["lock_tm_move"] = lockTmMove@{ params ->
val move = Moves.getByName(params.getString(0)) ?: return@lockTmMove DoubleValue.ZERO
val tm = TechnicalMachines.moveToTM[move] ?: return@lockTmMove DoubleValue.ZERO
player.tmList().syncTMLockFromMove(move)
DoubleValue(if (player.tmList().learnedTMs.contains(tm.id)) 0.0 else 1.0)
}
functions["start_battle"] = startBattle@{ params ->
val opponentValue = params.get<MoValue>(0)
val opponent =
if (opponentValue is ObjectValue<*>) {
opponentValue.obj as ServerPlayer
} else {
val paramString = opponentValue.asString()
val playerUUID = paramString.asUUID
if (playerUUID != null) {
server()?.playerList?.getPlayer(playerUUID) ?: return@startBattle DoubleValue.ZERO
} else {
server()?.playerList?.getPlayerByName(paramString)
?: return@startBattle DoubleValue.ZERO
}
}
val format =
params
.getStringOrNull(1)
?.let(BattleFormat::fromFormatIdentifier)
?: BattleFormat.GEN_9_SINGLES
val setLevel = params.getIntOrNull(2) ?: -1
format.adjustLevel = setLevel
val setLevel = params.getIntOrNull(2) ?: -1
format.adjustLevel = setLevel
val rules = params.getStringOrNull(5)
?.split(",")
?.toSet()
?: emptySet()
val rules =
params
.getStringOrNull(5)
?.split(",")
?.toSet()
?: emptySet()
val modifiedBattleFormat = BattleFormat.setBattleRules(
battleFormat = format,
rules = rules
)
val modifiedBattleFormat =
BattleFormat.setBattleRules(
battleFormat = format,
rules = rules,
)
val cloneParties = (setLevel != -1) || (params.getBooleanOrNull(3) ?: false)
val healFirst = params.getBooleanOrNull(4) ?: false
val cloneParties = (setLevel != -1) || (params.getBooleanOrNull(3) ?: false)
val healFirst = params.getBooleanOrNull(4) ?: false
val battleStartResult = BattleBuilder.pvp1v1(
player1 = player,
player2 = opponent,
battleFormat = modifiedBattleFormat,
cloneParties = cloneParties,
healFirst = healFirst
)
var returnValue: MoValue = DoubleValue.ZERO
battleStartResult.ifSuccessful { returnValue = it.struct }
return@put returnValue
val battleStartResult =
BattleBuilder.pvp1v1(
player1 = player,
player2 = opponent,
battleFormat = modifiedBattleFormat,
cloneParties = cloneParties,
healFirst = healFirst,
)
var returnValue: MoValue = DoubleValue.ZERO
battleStartResult.ifSuccessful { returnValue = it.struct }
returnValue
}
functions["get_custom_stat"] = getCustomStat@{ params ->
val statName = params.getString(0)
val resourceLocation = ResourceLocation.tryParse(statName) ?: return@getCustomStat DoubleValue.ZERO
val statResourceLocation =
BuiltInRegistries.CUSTOM_STAT.get(resourceLocation) ?: return@getCustomStat DoubleValue.ZERO
val exists = Stats.CUSTOM.contains(statResourceLocation)
if (!exists) return@getCustomStat DoubleValue.ZERO
val stat = Stats.CUSTOM.get(statResourceLocation)
val value = player.stats.getValue(stat)
DoubleValue(value)
}
functions["get_starter_uuid"] = {
Cobblemon.playerDataManager.getGenericData(player).starterUUID?.let {
StringValue(
it.toString(),
)
} ?: DoubleValue.ZERO
}
functions["has_chosen_starter"] = {
DoubleValue(Cobblemon.playerDataManager.getGenericData(player).starterSelected)
}
}
map["get_custom_stat"] = put@{ params ->
val statName = params.getString(0)
val resourceLocation = ResourceLocation.tryParse(statName) ?: return@put DoubleValue.ZERO
val statResourceLocation =
BuiltInRegistries.CUSTOM_STAT.get(resourceLocation) ?: return@put DoubleValue.ZERO
val exists = net.minecraft.stats.Stats.CUSTOM.contains(statResourceLocation)
if (!exists) return@put DoubleValue.ZERO
val stat = net.minecraft.stats.Stats.CUSTOM.get(statResourceLocation)
val value = player.stats.getValue(stat)
DoubleValue(value)
}
map["get_starter_uuid"] = { _ ->
Cobblemon.playerDataManager.getGenericData(player).starterUUID?.let {
StringValue(
it.toString()
)
} ?: DoubleValue.ZERO
}
map["has_chosen_starter"] = { _ -> DoubleValue(Cobblemon.playerDataManager.getGenericData(player).starterSelected) }
}
return map
return functions
}
}
@@ -27,57 +27,69 @@ import com.cobblemon.mod.common.util.getStringOrNull
object PokedexMoLangFunctions : AbstractMoLangFunctionHolder<AbstractPokedexManager>() {
override fun AbstractPokedexManager.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
val pokedex = this
val map = mutableMapOf<String, (MoParams) -> Any>()
map["get_species_record"] = { params ->
val functions = hashMapOf<String, (MoParams) -> Any>()
functions["get_species_record"] = { params ->
val speciesId = params.getString(0).asIdentifierDefaultingNamespace()
pokedex.speciesRecords[speciesId]?.struct ?: QueryStruct(hashMapOf())
}
map["has_seen"] = put@{ params ->
functions["has_seen"] = hasSeen@{ params ->
val speciesId = params.getString(0).asIdentifierDefaultingNamespace()
val formName = params.getStringOrNull(1)
val formName =
params.getStringOrNull(1)
?: return@hasSeen DoubleValue(
pokedex.getHighestKnowledgeForSpecies(speciesId).ordinal >= PokedexEntryProgress.SEEN.ordinal,
)
if (formName == null) {
return@put DoubleValue(pokedex.getHighestKnowledgeForSpecies(speciesId).ordinal >= PokedexEntryProgress.SEEN.ordinal)
} else {
return@put DoubleValue(
(pokedex.getSpeciesRecord(speciesId)?.getFormRecord(formName)?.knowledge?.ordinal
?: 0) >= PokedexEntryProgress.SEEN.ordinal
)
}
DoubleValue(
(
pokedex
.getSpeciesRecord(speciesId)
?.getFormRecord(formName)
?.knowledge
?.ordinal
?: 0
) >= PokedexEntryProgress.SEEN.ordinal,
)
}
map["has_caught"] = put@{ params ->
functions["has_caught"] = hasCaught@{ params ->
val speciesId = params.getString(0).asIdentifierDefaultingNamespace()
val formName = params.getStringOrNull(1)
if (formName == null) {
return@put DoubleValue(pokedex.getHighestKnowledgeForSpecies(speciesId) == PokedexEntryProgress.OWNED)
} else {
return@put DoubleValue(
pokedex.getSpeciesRecord(speciesId)
?.getFormRecord(formName)?.knowledge == PokedexEntryProgress.OWNED
val formName =
params.getStringOrNull(1) ?: return@hasCaught DoubleValue(
pokedex.getHighestKnowledgeForSpecies(
speciesId,
) == PokedexEntryProgress.OWNED,
)
}
DoubleValue(
pokedex
.getSpeciesRecord(speciesId)
?.getFormRecord(formName)
?.knowledge == PokedexEntryProgress.OWNED,
)
}
map["caught_count"] = { DoubleValue(pokedex.getGlobalCalculatedValue(CaughtCount)) }
map["seen_count"] = { DoubleValue(pokedex.getGlobalCalculatedValue(SeenCount)) }
map["caught_percent"] = { DoubleValue(pokedex.getGlobalCalculatedValue(CaughtPercent)) }
map["seen_percent"] = { DoubleValue(pokedex.getGlobalCalculatedValue(SeenPercent)) }
functions["caught_count"] = { DoubleValue(pokedex.getGlobalCalculatedValue(CaughtCount)) }
functions["seen_count"] = { DoubleValue(pokedex.getGlobalCalculatedValue(SeenCount)) }
functions["caught_percent"] = { DoubleValue(pokedex.getGlobalCalculatedValue(CaughtPercent)) }
functions["seen_percent"] = { DoubleValue(pokedex.getGlobalCalculatedValue(SeenPercent)) }
if (pokedex is PokedexManager) {
map["player_id"] = { StringValue(pokedex.uuid.toString()) }
map["see"] = put@{ params ->
val pokemon = params.get<ObjectValue<Pokemon>>(0).obj
pokedex.encounter(pokemon)
return@put DoubleValue.ONE
}
map["catch"] = put@{ params ->
val pokemon = params.get<ObjectValue<Pokemon>>(0).obj
pokedex.obtain(pokemon)
return@put DoubleValue.ONE
when (pokedex) {
is PokedexManager -> {
functions["player_id"] = { StringValue(pokedex.uuid.toString()) }
functions["see"] = { params ->
val pokemon = params.get<ObjectValue<Pokemon>>(0).obj
pokedex.encounter(pokemon)
DoubleValue.ONE
}
functions["catch"] = { params ->
val pokemon = params.get<ObjectValue<Pokemon>>(0).obj
pokedex.obtain(pokemon)
DoubleValue.ONE
}
}
}
return map
return functions
}
}
@@ -32,100 +32,113 @@ import com.cobblemon.mod.common.util.withQueryValue
object PokemonEntityMoLangFunctions : AbstractMoLangFunctionHolder<PokemonEntity>() {
override fun PokemonEntity.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
val pokemonEntity = this
val map = mutableMapOf<String, (MoParams) -> Any>()
map["is_busy"] = { DoubleValue(pokemonEntity.isBusy) }
map["in_battle"] = { DoubleValue(pokemonEntity.isBattling) }
map["is_moving"] = { DoubleValue((pokemonEntity.moveControl as? PokemonMoveControl)?.hasWanted() == true) }
map["is_flying"] = { DoubleValue(pokemonEntity.getBehaviourFlag(PokemonBehaviourFlag.FLYING)) }
map["get_riding_state"] = { params ->
val functions = hashMapOf<String, (MoParams) -> Any>()
functions["is_busy"] = { DoubleValue(pokemonEntity.isBusy) }
functions["in_battle"] = { DoubleValue(pokemonEntity.isBattling) }
functions["is_moving"] =
{ DoubleValue((pokemonEntity.moveControl as? PokemonMoveControl)?.hasWanted() == true) }
functions["is_flying"] = { DoubleValue(pokemonEntity.getBehaviourFlag(PokemonBehaviourFlag.FLYING)) }
functions["get_riding_state"] = { params ->
val name = params.getStringOrNull(0)
pokemonEntity.ifRidingAvailableSupply(DoubleValue.ZERO) { behaviour, settings, state ->
val moValue = behaviour.asMoLangValue(settings, state, pokemonEntity)
return@ifRidingAvailableSupply moValue.functions[name]?.apply(MoParams.EMPTY) as? MoValue ?: moValue
}
}
map["is_gliding"] = {
functions["is_gliding"] = {
pokemonEntity.ifRidingAvailableSupply(DoubleValue.ZERO) { behaviour, settings, state ->
val moValue = behaviour.asMoLangValue(settings, state, pokemonEntity)
return@ifRidingAvailableSupply moValue.functions["gliding"]?.apply(MoParams.EMPTY) as? MoValue
?: DoubleValue.ZERO
}
}
map["is_sprinting"] = {
functions["is_sprinting"] = {
pokemonEntity.ifRidingAvailableSupply(DoubleValue.ZERO) { behaviour, settings, state ->
val moValue = behaviour.asMoLangValue(settings, state, pokemonEntity)
return@ifRidingAvailableSupply moValue.functions["sprinting"]?.apply(MoParams.EMPTY) as? MoValue
?: DoubleValue.ZERO
}
}
map["is_drifting"] = {
functions["is_drifting"] = {
pokemonEntity.ifRidingAvailableSupply(DoubleValue.ZERO) { behaviour, settings, state ->
val moValue = behaviour.asMoLangValue(settings, state, pokemonEntity)
return@ifRidingAvailableSupply moValue.functions["drifting"]?.apply(MoParams.EMPTY) as? MoValue
?: DoubleValue.ZERO
}
}
map["is_powered_drifting"] = {
functions["is_powered_drifting"] = {
pokemonEntity.ifRidingAvailableSupply(DoubleValue.ZERO) { behaviour, settings, state ->
val moValue = behaviour.asMoLangValue(settings, state, pokemonEntity)
return@ifRidingAvailableSupply moValue.functions["powered_drifting"]?.apply(MoParams.EMPTY) as? MoValue
?: DoubleValue.ZERO
}
}
map["in_air"] = {
functions["in_air"] = {
pokemonEntity.ifRidingAvailableSupply(DoubleValue.ZERO) { behaviour, settings, state ->
val moValue = behaviour.asMoLangValue(settings, state, pokemonEntity)
return@ifRidingAvailableSupply moValue.functions["in_air"]?.apply(MoParams.EMPTY) as? MoValue
?: DoubleValue.ZERO
}
}
map["is_wild"] = { DoubleValue(pokemonEntity.ownerUUID == null) }
map["is_in_party"] = { DoubleValue(pokemonEntity.pokemon.storeCoordinates.get()?.store is PartyStore) }
map["is_ridden"] = { DoubleValue(pokemonEntity.hasControllingPassenger()) }
map["has_aspect"] = { DoubleValue(it.getString(0) in pokemonEntity.aspects) }
map["is_pokemon"] = { DoubleValue.ONE }
map["is_holding_item"] = {
DoubleValue(!pokemonEntity.entityData.get(PokemonEntity.SHOWN_HELD_ITEM).let {
it.isEmpty || it.`is`(CobblemonItemTags.WEARABLE_HAT_ITEMS) || it.`is`(CobblemonItemTags.WEARABLE_FACE_ITEMS)
})
functions["is_wild"] = { DoubleValue(pokemonEntity.ownerUUID == null) }
functions["is_in_party"] = {
DoubleValue(
pokemonEntity.pokemon.storeCoordinates
.get()
?.store is PartyStore,
)
}
map["riding_style"] = {
StringValue(pokemonEntity.ifRidingAvailableSupply("") { behaviour, settings, state ->
behaviour.getRidingStyle(settings, state).name
})
functions["is_ridden"] = { DoubleValue(pokemonEntity.hasControllingPassenger()) }
functions["has_aspect"] = { DoubleValue(it.getString(0) in pokemonEntity.aspects) }
functions["is_pokemon"] = { DoubleValue.ONE }
functions["is_holding_item"] = {
DoubleValue(
!pokemonEntity.entityData.get(PokemonEntity.SHOWN_HELD_ITEM).let {
it.isEmpty || it.`is`(CobblemonItemTags.WEARABLE_HAT_ITEMS) || it.`is`(CobblemonItemTags.WEARABLE_FACE_ITEMS)
},
)
}
map["is_wearing_hat"] = {
functions["riding_style"] = {
StringValue(
pokemonEntity.ifRidingAvailableSupply("") { behaviour, settings, state ->
behaviour.getRidingStyle(settings, state).name
},
)
}
functions["is_wearing_hat"] = {
DoubleValue(
pokemonEntity.entityData
.get(PokemonEntity.SHOWN_HELD_ITEM)
.`is`(CobblemonItemTags.WEARABLE_HAT_ITEMS)
.`is`(CobblemonItemTags.WEARABLE_HAT_ITEMS),
)
}
map["is_wearing_face"] = {
functions["is_wearing_face"] = {
DoubleValue(
pokemonEntity.entityData
.get(PokemonEntity.SHOWN_HELD_ITEM)
.`is`(CobblemonItemTags.WEARABLE_FACE_ITEMS)
.`is`(CobblemonItemTags.WEARABLE_FACE_ITEMS),
)
}
map["is_pastured"] = {
functions["is_pastured"] = {
DoubleValue((pokemonEntity.tethering != null))
}
map["pasture_conflict_enabled"] = {
functions["pasture_conflict_enabled"] = {
DoubleValue(pokemonEntity.getBehaviourFlag(PokemonBehaviourFlag.PASTURE_CONFLICT))
}
map["run_action_effect"] = runActionEffect@{ params ->
functions["run_action_effect"] = runActionEffect@{ params ->
val runtime = MoLangRuntime().setup()
runtime.environment.cloneFrom(params.environment)
runtime.withQueryValue("entity", pokemonEntity.struct)
val actionEffect = ActionEffects.actionEffects[params.getString(0).asIdentifierDefaultingNamespace()]
if (actionEffect != null) {
val context = ActionEffectContext(
actionEffect = actionEffect,
providers = mutableListOf(TargetsProvider(pokemonEntity)),
runtime = runtime,
level = pokemonEntity.level()
)
val context =
ActionEffectContext(
actionEffect = actionEffect,
providers = mutableListOf(TargetsProvider(pokemonEntity)),
runtime = runtime,
level = pokemonEntity.level(),
)
// This was taken from NPC run_action_effect but this variable isn't used, so I just commented it out.
// pokemonEntity.actionEffect = context
pokemonEntity.brain.setMemory(CobblemonMemories.ACTIVE_ACTION_EFFECT, context)
@@ -135,14 +148,16 @@ object PokemonEntityMoLangFunctions : AbstractMoLangFunctionHolder<PokemonEntity
pokemonEntity.brain.getMemory(CobblemonMemories.ACTIVE_ACTION_EFFECT).orElse(null)
if (pokemonActionEffect == context && pokemonEntity.brain.isActive(CobblemonActivities.ACTION_EFFECT)) {
pokemonEntity.brain.eraseMemory(CobblemonMemories.ACTIVE_ACTION_EFFECT)
//pokemonEntity.actionEffect = null
// pokemonEntity.actionEffect = null
}
}
return@runActionEffect DoubleValue.ONE
}
return@runActionEffect DoubleValue.ZERO
DoubleValue.ZERO
}
return map
return functions
}
}
@@ -33,218 +33,214 @@ import com.cobblemon.mod.common.util.getDoubleOrNull
import com.cobblemon.mod.common.util.getIntOrNull
import com.cobblemon.mod.common.util.server
import com.cobblemon.mod.common.util.toProperties
import kotlin.random.Random
import net.minecraft.resources.ResourceLocation
import kotlin.random.Random
object PokemonMoLangFunctions : AbstractMoLangFunctionHolder<Pokemon>() {
override fun Pokemon.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
val pokemon = this
val map = mutableMapOf<String, (MoParams) -> Any>()
map["id"] = { StringValue(pokemon.uuid.toString()) }
map["nickname"] = { StringValue(pokemon.nickname.toString()) }
map["level"] = { DoubleValue(pokemon.level.toDouble()) }
map["max_hp"] = { DoubleValue(pokemon.maxHealth.toDouble()) }
map["current_hp"] = { DoubleValue(pokemon.currentHealth.toDouble()) }
map["friendship"] = { DoubleValue(pokemon.friendship.toDouble()) }
map["max_fullness"] = { DoubleValue(pokemon.getMaxFullness().toDouble()) }
map["fullness"] = { DoubleValue(pokemon.currentFullness.toDouble()) }
map["lose_fullness"] = { params ->
val functions = hashMapOf<String, (MoParams) -> Any>()
functions["id"] = { StringValue(pokemon.uuid.toString()) }
functions["nickname"] = { StringValue(pokemon.nickname.toString()) }
functions["level"] = { DoubleValue(pokemon.level.toDouble()) }
functions["max_hp"] = { DoubleValue(pokemon.maxHealth.toDouble()) }
functions["current_hp"] = { DoubleValue(pokemon.currentHealth.toDouble()) }
functions["friendship"] = { DoubleValue(pokemon.friendship.toDouble()) }
functions["max_fullness"] = { DoubleValue(pokemon.getMaxFullness().toDouble()) }
functions["fullness"] = { DoubleValue(pokemon.currentFullness.toDouble()) }
functions["lose_fullness"] = { params ->
val amount = params.getDouble(0)
pokemon.loseFullness(amount.toInt())
}
map["feed_pokemon"] = { params ->
functions["feed_pokemon"] = { params ->
val amount = params.getDouble(0)
val playSound = params.getBooleanOrNull(1) ?: true
pokemon.feedPokemon(amount.toInt(), playSound)
}
map["behaviour"] = { pokemon.form.behaviour.struct }
map["behavior"] = { pokemon.form.behaviour.struct } // Inferior
map["pokeball"] = { StringValue(pokemon.caughtBall.toString()) }
map["ability"] = { StringValue(pokemon.ability.name) }
map["has_learned"] = put@{ params ->
functions["behaviour"] = { pokemon.form.behaviour.struct }
functions["behavior"] = { pokemon.form.behaviour.struct } // Inferior
functions["pokeball"] = { StringValue(pokemon.caughtBall.toString()) }
functions["ability"] = { StringValue(pokemon.ability.name) }
functions["has_learned"] = hasLearned@{ params ->
val moveName = params.getString(0)
val move = pokemon.allAccessibleMoves.find { it.name == moveName }
if (move != null) {
return@put DoubleValue.ONE
} else {
return@put DoubleValue.ZERO
}
if (move != null) return@hasLearned DoubleValue.ONE
DoubleValue.ZERO
}
map["moveset"] = {
functions["moveset"] = {
val struct = QueryStruct(hashMapOf())
for ((index, move) in pokemon.moveSet.withIndex()) {
struct.addFunction(index.toString()) { move.struct }
}
struct
}
map["evs"] = {
functions["evs"] = {
val struct = QueryStruct(hashMapOf())
for (stat in Stats.PERMANENT) {
struct.addFunction(stat.showdownId) { DoubleValue(pokemon.evs.getOrDefault(stat).toDouble()) }
}
struct
}
map["ivs"] = {
functions["ivs"] = {
val struct = QueryStruct(hashMapOf())
for (stat in Stats.PERMANENT) {
struct.addFunction(stat.showdownId) { DoubleValue(pokemon.ivs.getOrDefault(stat).toDouble()) }
}
struct
}
map["hyper_trained_ivs"] = {
functions["hyper_trained_ivs"] = {
val struct = QueryStruct(hashMapOf())
for (stat in Stats.PERMANENT) {
struct.addFunction(stat.showdownId) { DoubleValue(pokemon.ivs.hyperTrainedIVs[stat] ?: -1.0) }
}
struct
}
map["ride_boosts"] = {
functions["ride_boosts"] = {
val struct = QueryStruct(hashMapOf())
for (stat in RidingStat.entries) {
struct.addFunction(stat.name.lowercase()) { DoubleValue(pokemon.getRideBoost(stat)) }
}
struct
}
map["natdex_number"] = {
functions["natdex_number"] = {
DoubleValue(pokemon.species.nationalPokedexNumber.toDouble())
}
map["types"] = {
pokemon.form.types.map { it.toString() }.asArrayValue(::StringValue)
functions["types"] = {
pokemon.form.types
.map { it.showdownId }
.asArrayValue(::StringValue)
}
map["gender_ratio"] = {
functions["gender_ratio"] = {
DoubleValue(pokemon.form.maleRatio.toDouble())
}
map["ev_yield"] = {
functions["ev_yield"] = {
val struct = QueryStruct(hashMapOf())
for (stat in Stats.PERMANENT) {
struct.addFunction(stat.showdownId) { DoubleValue(pokemon.form.evYield[stat]?.toDouble()) }
}
struct
}
map["base_stats"] = {
functions["base_stats"] = {
val struct = QueryStruct(hashMapOf())
for (stat in Stats.PERMANENT) {
struct.addFunction(stat.showdownId) { DoubleValue(pokemon.form.baseStats[stat]?.toDouble()) }
}
struct
}
map["catch_rate"] = {
functions["catch_rate"] = {
DoubleValue(pokemon.form.catchRate.toDouble())
}
map["base_experience_yield"] = {
functions["base_experience_yield"] = {
DoubleValue(pokemon.form.baseExperienceYield.toDouble())
}
map["drops"] = {
functions["drops"] = {
val struct = QueryStruct(hashMapOf())
for ((index, drop) in pokemon.form.drops.entries.withIndex()) {
for ((index, drop) in pokemon.form.drops.entries
.withIndex()) {
struct.addFunction(index.toString()) { drop }
}
struct
}
map["tm_learnset"] = {
functions["tm_learnset"] = {
val struct = QueryStruct(hashMapOf())
for ((index, move) in pokemon.form.moves.tmMoves.withIndex()) {
for ((index, move) in pokemon.form.moves.tmMoves
.withIndex()) {
struct.addFunction(index.toString()) { move.struct }
}
struct
}
map["egg_learnset"] = {
functions["egg_learnset"] = {
val struct = QueryStruct(hashMapOf())
for ((index, move) in pokemon.form.moves.eggMoves.withIndex()) {
for ((index, move) in pokemon.form.moves.eggMoves
.withIndex()) {
struct.addFunction(index.toString()) { move.struct }
}
struct
}
map["tutor_learnset"] = {
functions["tutor_learnset"] = {
val struct = QueryStruct(hashMapOf())
for ((index, move) in pokemon.form.moves.tutorMoves.withIndex()) {
for ((index, move) in pokemon.form.moves.tutorMoves
.withIndex()) {
struct.addFunction(index.toString()) { move.struct }
}
struct
}
map["level_learnset"] = {
functions["level_learnset"] = {
val struct = QueryStruct(hashMapOf())
for ((index, move) in pokemon.form.moves.levelUpMoves) {
struct.addFunction(index.toString()) { move }
}
struct
}
map["ability_pool"] = {
functions["ability_pool"] = {
val struct = QueryStruct(hashMapOf())
for ((index, ability) in pokemon.form.abilities.withIndex()) {
struct.addFunction(index.toString()) { StringValue(ability.toString()) }
}
struct
}
map["egg_groups"] = {
functions["egg_groups"] = {
val struct = QueryStruct(hashMapOf())
for ((index, group) in pokemon.form.eggGroups.withIndex()) {
struct.addFunction(index.toString()) { StringValue(group.toString()) }
}
struct
}
map["egg_cycles"] = {
functions["egg_cycles"] = {
DoubleValue(pokemon.species.eggCycles.toDouble())
}
map["labels"] = {
functions["labels"] = {
val struct = QueryStruct(hashMapOf())
for ((index, label) in pokemon.form.labels.withIndex()) {
struct.addFunction(index.toString()) { StringValue(label) }
}
struct
}
map["aspects"] = {
functions["aspects"] = {
val aspects = pokemon.aspects
aspects.asArrayValue { StringValue(it) }
}
map["form_aspects"] = {
functions["form_aspects"] = {
val aspects = pokemon.form.aspects
aspects.asArrayValue { StringValue(it) }
}
map["form_name"] = {
functions["form_name"] = {
StringValue(pokemon.form.name)
}
// Yes, this is the lazy call for a single hardcoded pre-evolution.
// Lol. Lmao, even.
// TO-DO: Subscribe to [PokemonSpecies.observable].
// Gito: surely this is just a typo, right? not touching the function name just in case
map["prevolution"] = preEvolution@{
val preEvolution = pokemon.species.preEvolution ?: return@preEvolution DoubleValue.ZERO
return@preEvolution preEvolution
}
map["nature"] = { StringValue(pokemon.nature.toString()) }
map["is_wild"] = { DoubleValue(pokemon.entity?.let { it.ownerUUID == null } == true) }
map["is_shiny"] = { DoubleValue(pokemon.shiny) }
map["is_in_party"] = { DoubleValue(pokemon.storeCoordinates.get()?.store is PartyStore) }
map["species"] = { pokemon.species.struct }
map["form"] = { StringValue(pokemon.form.name) }
map["weight"] = { DoubleValue(pokemon.species.weight.toDouble()) }
map["matches"] = { params -> DoubleValue(params.getString(0).toProperties().matches(pokemon)) }
map["apply"] = { params ->
functions["pre_evolution"] = { pokemon.species.preEvolution ?: DoubleValue.ZERO }
functions["nature"] = { StringValue(pokemon.nature.toString()) }
functions["is_wild"] = { DoubleValue(pokemon.entity?.let { it.ownerUUID == null } == true) }
functions["is_shiny"] = { DoubleValue(pokemon.shiny) }
functions["is_in_party"] = { DoubleValue(pokemon.storeCoordinates.get()?.store is PartyStore) }
functions["species"] = { pokemon.species.struct }
functions["form"] = { StringValue(pokemon.form.name) }
functions["weight"] = { DoubleValue(pokemon.species.weight.toDouble()) }
functions["matches"] = { params -> DoubleValue(params.getString(0).toProperties().matches(pokemon)) }
functions["apply"] = { params ->
params.getString(0).toProperties().apply(pokemon)
DoubleValue.ONE
}
map["owner"] = { pokemon.getOwnerPlayer()?.asMoLangValue() ?: DoubleValue.ZERO }
map["held_item"] = { pokemon.heldItem().asMoLangValue(server()!!.registryAccess()) }
map["remove_held_item"] = { _ ->
pokemon.removeHeldItem()
}
map["add_aspects"] = { params ->
functions["owner"] = { pokemon.getOwnerPlayer()?.asMoLangValue() ?: DoubleValue.ZERO }
functions["held_item"] = { pokemon.heldItem().asMoLangValue(server()!!.registryAccess()) }
functions["remove_held_item"] = { pokemon.removeHeldItem() }
functions["add_aspects"] = { params ->
for (aspect in params.params) pokemon.forcedAspects += aspect.asString()
pokemon.updateAspects()
}
map["remove_aspects"] = { params ->
functions["remove_aspects"] = { params ->
for (aspect in params.params) pokemon.forcedAspects -= aspect.asString()
pokemon.updateAspects()
}
map["cosmetic_item"] = {
functions["cosmetic_item"] = {
pokemon.cosmeticItem().asMoLangValue(server()!!.registryAccess())
}
map["remove_cosmetic_item"] = { _ ->
pokemon.removeCosmeticItem()
}
map["marks"] = { _ -> pokemon.marks.asArrayValue { StringValue(it.identifier.toString()) } }
map["has_mark"] = { params ->
functions["remove_cosmetic_item"] = { pokemon.removeCosmeticItem() }
functions["marks"] = { pokemon.marks.asArrayValue { StringValue(it.identifier.toString()) } }
functions["has_mark"] = { params ->
var hasMark = false
val identifier = params.getString(0).asIdentifierDefaultingNamespace()
val mark = Marks.getByIdentifier(identifier)
@@ -254,7 +250,7 @@ object PokemonMoLangFunctions : AbstractMoLangFunctionHolder<Pokemon>() {
DoubleValue(hasMark)
}
map["remove_marks"] = { params ->
functions["remove_marks"] = { params ->
var removedMark = false
for (param in params.params) {
val identifier = param.asString().asIdentifierDefaultingNamespace()
@@ -267,7 +263,7 @@ object PokemonMoLangFunctions : AbstractMoLangFunctionHolder<Pokemon>() {
DoubleValue(removedMark)
}
map["add_marks"] = { params ->
functions["add_marks"] = { params ->
var appliedMark = false
for (param in params.params) {
val identifier = param.asString().asIdentifierDefaultingNamespace()
@@ -280,7 +276,7 @@ object PokemonMoLangFunctions : AbstractMoLangFunctionHolder<Pokemon>() {
DoubleValue(appliedMark)
}
map["add_marks_with_chance"] = { params ->
functions["add_marks_with_chance"] = { params ->
var appliedMark = false
for (param in params.params) {
val identifier = param.asString().asIdentifierDefaultingNamespace()
@@ -298,17 +294,15 @@ object PokemonMoLangFunctions : AbstractMoLangFunctionHolder<Pokemon>() {
DoubleValue(appliedMark)
}
map["add_potential_marks"] = { params ->
functions["add_potential_marks"] = { params ->
for (param in params.params) {
val identifier = param.asString().asIdentifierDefaultingNamespace()
val mark = Marks.getByIdentifier(identifier)
if (mark != null) pokemon.addPotentialMark(mark)
}
}
map["apply_potential_marks"] = put@{
return@put DoubleValue(pokemon.applyPotentialMarks())
}
map["hyper_train_iv"] = hyperTrainIv@{ params ->
functions["apply_potential_marks"] = { DoubleValue(pokemon.applyPotentialMarks()) }
functions["hyper_train_iv"] = hyperTrainIv@{ params ->
val statId = params.getString(0)
val stat = Stats.getStat(statId)
val value = params.getIntOrNull(1) ?: IVs.MAX_VALUE
@@ -321,12 +315,12 @@ object PokemonMoLangFunctions : AbstractMoLangFunctionHolder<Pokemon>() {
return@hyperTrainIv DoubleValue.ZERO
}
}
map["add_exp"] = { params ->
functions["add_exp"] = { params ->
val exp = params.getDouble(0).toInt()
pokemon.addExperience(SidemodExperienceSource("molang"), exp)
DoubleValue.ONE
}
map["set_iv"] = setIv@{ params ->
functions["set_iv"] = setIv@{ params ->
val statId = params.getString(0)
val stat = Stats.getStat(statId)
val value = params.getIntOrNull(1)?.coerceIn(0, IVs.MAX_VALUE) ?: IVs.MAX_VALUE
@@ -338,7 +332,7 @@ object PokemonMoLangFunctions : AbstractMoLangFunctionHolder<Pokemon>() {
return@setIv DoubleValue.ZERO
}
}
map["set_ev"] = setEv@{ params ->
functions["set_ev"] = setEv@{ params ->
val statId = params.getString(0)
val stat = Stats.getStat(statId)
val value = (params.getIntOrNull(1) ?: 0).coerceIn(0, 255)
@@ -350,49 +344,54 @@ object PokemonMoLangFunctions : AbstractMoLangFunctionHolder<Pokemon>() {
return@setEv DoubleValue.ZERO
}
}
map["set_ride_boost"] = setRideBoost@{ params ->
functions["set_ride_boost"] = setRideBoost@{ params ->
val statName = params.getString(0).uppercase()
val stat = RidingStat.entries.find { it.name.equals(statName, ignoreCase = true) }
?: return@setRideBoost DoubleValue.ZERO
val stat =
RidingStat.entries.find { it.name.equals(statName, ignoreCase = true) }
?: return@setRideBoost DoubleValue.ZERO
val value = params.getDoubleOrNull(1)?.toFloat() ?: return@setRideBoost DoubleValue.ZERO
pokemon.setRideBoost(stat, value)
return@setRideBoost DoubleValue.ONE
DoubleValue.ONE
}
map["add_ride_boost"] = addRideBoost@{ params ->
functions["add_ride_boost"] = addRideBoost@{ params ->
val statName = params.getString(0).uppercase()
val stat = RidingStat.entries.find { it.name.equals(statName, ignoreCase = true) }
?: return@addRideBoost DoubleValue.ZERO
val stat =
RidingStat.entries.find { it.name.equals(statName, ignoreCase = true) }
?: return@addRideBoost DoubleValue.ZERO
val value = params.getDoubleOrNull(1)?.toFloat() ?: return@addRideBoost DoubleValue.ZERO
return@addRideBoost DoubleValue(if (pokemon.addRideBoost(stat, value)) 1.0 else 0.0)
DoubleValue(if (pokemon.addRideBoost(stat, value)) 1.0 else 0.0)
}
map["initialize_moveset"] = put@{ params ->
functions["initialize_moveset"] = initializeMoveset@{ params ->
val param = params.get<MoValue>(0) ?: StringValue(pokemon.form.defaultWildMovesetBuilder.toString())
if (param is DoubleValue) {
pokemon.initializeMoveset(param == DoubleValue.ONE)
} else {
val movesetBuilderId = ResourceLocation.parse(param.asString())
val movesetBuilder = CobblemonMovesetBuilders.movesetBuilders[movesetBuilderId]
?: run {
Cobblemon.LOGGER.error("Tried initializing moveset from moveset builder ${param.asString()} but it does not exist.")
return@put DoubleValue.ZERO
}
val movesetBuilder =
CobblemonMovesetBuilders.movesetBuilders[movesetBuilderId]
?: run {
Cobblemon.LOGGER.error(
"Tried initializing moveset from moveset builder ${param.asString()} but it does not exist.",
)
return@initializeMoveset DoubleValue.ZERO
}
pokemon.initializeMovesetFrom(movesetBuilder)
}
DoubleValue.ONE
}
map["validate_moveset"] = { params ->
functions["validate_moveset"] = { params ->
val includeLegacy = params.getBooleanOrNull(0) ?: true
pokemon.validateMoveset(includeLegacy)
DoubleValue.ONE
}
map["teach_learnable_moves"] = { params ->
functions["teach_learnable_moves"] = { params ->
val includeLegacy = params.getBooleanOrNull(0) ?: true
pokemon.teachLearnableMoves(includeLegacy)
DoubleValue.ONE
}
map["teach_move"] = teachMove@{ params ->
functions["teach_move"] = teachMove@{ params ->
val moveName = params.getString(0)
val moveTemplate = Moves.getByName(moveName) ?: return@teachMove DoubleValue.ZERO
val bypass = params.getBooleanOrNull(1) ?: false
@@ -402,8 +401,9 @@ object PokemonMoLangFunctions : AbstractMoLangFunctionHolder<Pokemon>() {
return@teachMove DoubleValue.ZERO
}
val alreadyKnows = pokemon.moveSet.getMoves().any { it.template == moveTemplate } ||
pokemon.benchedMoves.any { it.moveTemplate == moveTemplate }
val alreadyKnows =
pokemon.moveSet.getMoves().any { it.template == moveTemplate } ||
pokemon.benchedMoves.any { it.moveTemplate == moveTemplate }
if (alreadyKnows) {
return@teachMove DoubleValue.ZERO
}
@@ -416,42 +416,43 @@ object PokemonMoLangFunctions : AbstractMoLangFunctionHolder<Pokemon>() {
DoubleValue.ONE
}
map["can_learn_move"] = canLearnMove@{ params ->
functions["can_learn_move"] = canLearnMove@{ params ->
val moveName = params.getString(0)
val moveTemplate = Moves.getByName(moveName) ?: return@canLearnMove DoubleValue.ZERO
val includeLegacy = params.getBooleanOrNull(1) ?: true
val canLearn = if (includeLegacy) {
LearnsetQuery.ANY.canLearn(moveTemplate, pokemon.form.moves)
} else {
LearnsetQuery.LEGAL.canLearn(moveTemplate, pokemon.form.moves)
}
return@canLearnMove DoubleValue(if (canLearn) 1.0 else 0.0)
val canLearn =
if (includeLegacy) {
LearnsetQuery.ANY.canLearn(moveTemplate, pokemon.form.moves)
} else {
LearnsetQuery.LEGAL.canLearn(moveTemplate, pokemon.form.moves)
}
DoubleValue(if (canLearn) 1.0 else 0.0)
}
map["unlearn_move"] = unlearnMove@{ params ->
functions["unlearn_move"] = unlearnMove@{ params ->
val moveName = params.getString(0)
val moveTemplate = Moves.getByName(moveName)
if (moveTemplate != null) {
pokemon.unlearnMove(moveTemplate)
return@unlearnMove DoubleValue.ONE
} else {
return@unlearnMove DoubleValue.ZERO
}
DoubleValue.ZERO
}
map["can_evolve"] = { _ ->
DoubleValue(pokemon.evolutions.any())
}
map["force_evolve"] = forceEvolve@{ params ->
functions["can_evolve"] = { DoubleValue(pokemon.evolutions.any()) }
functions["force_evolve"] = forceEvolve@{ params ->
val idx = params.getInt(0)
val evolution = if (idx >= 0) pokemon.evolutions.elementAtOrNull(idx) else return@forceEvolve DoubleValue.ZERO
val evolution =
if (idx >= 0) pokemon.evolutions.elementAtOrNull(idx) else return@forceEvolve DoubleValue.ZERO
evolution?.perform(pokemon)
StringValue(evolution.toString())
}
map["entity"] = { pokemon.entity?.asMoLangValue() ?: DoubleValue.ZERO }
map["is_alpha"] = { DoubleValue(pokemon.isAlpha) }
map["size_category"] = { _ -> StringValue(pokemon.getSizeCategory().name) }
functions["entity"] = { pokemon.entity?.asMoLangValue() ?: DoubleValue.ZERO }
functions["is_alpha"] = { DoubleValue(pokemon.isAlpha) }
functions["size_category"] = { StringValue(pokemon.getSizeCategory().name) }
return map
return functions
}
}
@@ -20,52 +20,50 @@ import com.cobblemon.mod.common.util.getStringOrNull
object PokemonPropertiesMoLangFunctions : AbstractMoLangFunctionHolder<PokemonProperties>() {
override fun PokemonProperties.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
val props = this
val map = mutableMapOf<String, (MoParams) -> Any>()
val functions = hashMapOf<String, (MoParams) -> Any>()
map["level"] = { DoubleValue(props.level?.toDouble() ?: 0) }
map["set_level"] = { params ->
functions["level"] = { DoubleValue(props.level?.toDouble() ?: 0) }
functions["set_level"] = { params ->
props.level = params.getIntOrNull(0)
DoubleValue.ONE
}
map["shiny"] = { DoubleValue(props.shiny) }
map["set_shiny"] = { params ->
functions["shiny"] = { DoubleValue(props.shiny) }
functions["set_shiny"] = { params ->
props.shiny = params.getBooleanOrNull(0)
DoubleValue.ONE
}
map["species"] = { props.species?.let { StringValue(it) } ?: DoubleValue.ZERO }
map["set_species"] = { params ->
functions["species"] = { props.species?.let { StringValue(it) } ?: DoubleValue.ZERO }
functions["set_species"] = { params ->
props.species = params.getStringOrNull(0)
DoubleValue.ONE
}
map["gender"] = { props.gender?.let { StringValue(it.name) } ?: DoubleValue.ZERO }
map["set_gender"] = { params ->
functions["gender"] = { props.gender?.let { StringValue(it.name) } ?: DoubleValue.ZERO }
functions["set_gender"] = { params ->
props.gender = params.getStringOrNull(0)?.let { Gender.valueOf(it) }
DoubleValue.ONE
}
map["form"] = { props.form?.let { StringValue(it) } ?: DoubleValue.ZERO }
map["ivs"] = ivs@{
functions["form"] = { props.form?.let { StringValue(it) } ?: DoubleValue.ZERO }
functions["ivs"] = ivs@{
val ivs = props.ivs ?: return@ivs DoubleValue.ZERO
return@ivs ivs.struct
ivs.struct
}
map["evs"] = evs@{
functions["evs"] = evs@{
val evs = props.evs ?: return@evs DoubleValue.ZERO
return@evs evs.struct
evs.struct
}
map["friendship"] = { DoubleValue(props.friendship?.toDouble() ?: DoubleValue.ZERO) }
map["set_friendship"] = { params ->
functions["friendship"] = { DoubleValue(props.friendship?.toDouble() ?: DoubleValue.ZERO) }
functions["set_friendship"] = { params ->
props.friendship = params.getIntOrNull(0)
DoubleValue.ONE
}
map["create"] = {
val pokemon = props.create()
pokemon.struct
}
map["to_string"] = { params ->
functions["create"] = { (props.create()).struct }
functions["to_string"] = { params ->
val separator = params.getStringOrNull(0) ?: " "
StringValue(props.asString(separator = separator))
}
return map
return functions
}
}
@@ -22,83 +22,84 @@ import com.cobblemon.mod.common.util.asUUID
import com.cobblemon.mod.common.util.getPlayer
import com.cobblemon.mod.common.util.toProperties
object PokemonStoreMoLangFunctions: AbstractMoLangFunctionHolder<PokemonStore<*>>() {
object PokemonStoreMoLangFunctions : AbstractMoLangFunctionHolder<PokemonStore<*>>() {
override fun PokemonStore<*>.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
val store = this
val map = mutableMapOf<String, (MoParams) -> Any>()
map["uuid"] = { StringValue(store.uuid.toString()) }
map["add"] = { params ->
val functions = hashMapOf<String, (MoParams) -> Any>()
functions["uuid"] = { StringValue(store.uuid.toString()) }
functions["add"] = { params ->
val pokemon = params.get<ObjectValue<Pokemon>>(0)
DoubleValue(store.add(pokemon.obj))
}
map["add_by_properties"] = { params ->
functions["add_by_properties"] = { params ->
val props = params.getString(0).toProperties()
val player = (store as? PlayerPartyStore)?.playerUUID?.getPlayer() // Only really know for sure when it's a player party store
val player =
(store as? PlayerPartyStore)?.playerUUID?.getPlayer() // Only really know for sure when it's a player party store
val pokemon = props.create(player = player)
DoubleValue(store.add(pokemon))
}
map["find_by_properties"] = { params ->
functions["find_by_properties"] = { params ->
val props = params.getString(0).toProperties()
val pokemon = store.find { props.matches(it) }
pokemon?.asStruct() ?: DoubleValue.ZERO
}
map["find_all_by_properties"] = { params ->
functions["find_all_by_properties"] = { params ->
val props = params.getString(0).toProperties()
val pokemon = store.filter { props.matches(it) }
ArrayStruct(pokemon.mapIndexed { index, value -> "$index" to value.asStruct() }.toMap())
}
map["find_by_id"] = { params ->
functions["find_by_id"] = { params ->
val id = params.getString(0).asUUID
val pokemon = store.find { it.uuid == id }
pokemon?.asStruct() ?: DoubleValue.ZERO
}
map["remove_by_id"] = removeById@{ params ->
functions["remove_by_id"] = removeById@{ params ->
val id = params.getString(0).asUUID
val pokemon = store.find { it.uuid == id } ?: return@removeById DoubleValue.ZERO
return@removeById DoubleValue(store.remove(pokemon))
DoubleValue(store.remove(pokemon))
}
map["average_level"] = averageLevel@{ _ ->
functions["average_level"] = averageLevel@{
var numberOfPokemon = 0
var totalLevel = 0
for (pokemon in store) {
totalLevel += pokemon.level
numberOfPokemon++
}
if (numberOfPokemon == 0) {
return@averageLevel DoubleValue.ZERO
}
return@averageLevel DoubleValue(totalLevel.toDouble() / numberOfPokemon)
if (numberOfPokemon == 0) return@averageLevel DoubleValue.ZERO
DoubleValue(totalLevel.toDouble() / numberOfPokemon)
}
map["count"] = { _ -> DoubleValue(store.count()) }
map["count_by_properties"] = { params ->
functions["count"] = { DoubleValue(store.count()) }
functions["count_by_properties"] = { params ->
val props = params.getString(0).toProperties()
DoubleValue(store.count { props.matches(it) })
}
map["highest_level"] = {
functions["highest_level"] = {
val highest = store.maxOfOrNull { it.level } ?: 0
DoubleValue(highest)
}
map["lowest_level"] = {
functions["lowest_level"] = {
val lowest = store.minOfOrNull { it.level } ?: 0
DoubleValue(lowest)
}
map["heal"] = {
functions["heal"] = {
for (pokemon in store) {
pokemon.heal()
}
DoubleValue.ONE
}
map["healing_remainder_percent"] = { _ ->
functions["healing_remainder_percent"] = {
var totalPercent = 0.0f
for (pokemon in store) {
totalPercent += (1.0f - (pokemon.currentHealth.toFloat() / pokemon.maxHealth))
}
DoubleValue(totalPercent)
}
map["has_usable_pokemon"] = { _ -> DoubleValue(store.any { !it.isFainted() }) }
map["pokemon"] = {
store.map { it.asStruct() }.asArrayValue()
}
return map
functions["has_usable_pokemon"] = { DoubleValue(store.any { !it.isFainted() }) }
functions["pokemon"] = { store.map { it.asStruct() }.asArrayValue() }
return functions
}
}
@@ -25,62 +25,63 @@ import java.util.*
object ServerMoLangFunctions : AbstractMoLangFunctionHolder<MinecraftServer>() {
override fun MinecraftServer.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
val server = this
val map = mutableMapOf<String, (MoParams) -> Any>()
val functions = hashMapOf<String, (MoParams) -> Any>()
map["get_world"] = getWorld@{ params ->
val world = server.getLevel(
ResourceKey.create(
Registries.DIMENSION,
params.getString(0).asIdentifierDefaultingNamespace(namespace = "minecraft")
)
) ?: return@getWorld DoubleValue.ZERO
functions["get_world"] = getWorld@{ params ->
val world =
server.getLevel(
ResourceKey.create(
Registries.DIMENSION,
params.getString(0).asIdentifierDefaultingNamespace(namespace = "minecraft"),
),
) ?: return@getWorld DoubleValue.ZERO
return@getWorld world
world
.registryAccess()
.registryOrThrow(Registries.DIMENSION)
.wrapAsHolder(world)
.asWorldMoLangValue()
}
map["broadcast"] = { params ->
functions["broadcast"] = { params ->
val message = params.getString(0)
server.playerList.broadcastSystemMessage(message.text(), params.getBooleanOrNull(1) == true)
DoubleValue.ONE
}
map["get_player_by_uuid"] = getPlayerByUuid@{ params ->
functions["get_player_by_uuid"] = getPlayerByUuid@{ params ->
val uuid = UUID.fromString(params.getString(0))
val player = server.playerList.getPlayer(uuid) ?: return@getPlayerByUuid DoubleValue.ZERO
return@getPlayerByUuid player.asMoLangValue()
player.asMoLangValue()
}
map["get_player_by_username"] = getPlayerByUsername@{ params ->
functions["get_player_by_username"] = getPlayerByUsername@{ params ->
val username = params.getString(0)
val player = server.playerList.getPlayerByName(username) ?: return@getPlayerByUsername DoubleValue.ZERO
return@getPlayerByUsername player.asMoLangValue()
player.asMoLangValue()
}
map["data"] = { params ->
functions["data"] = { params ->
Cobblemon.molangData.load(
UUID(0L, 0L),
params.getStringOrNull(0)
params.getStringOrNull(0),
)
}
map["save_data"] = { params ->
functions["save_data"] = { params ->
Cobblemon.molangData.save(
UUID(0L, 0L),
params.getStringOrNull(index = 0)
params.getStringOrNull(index = 0),
)
DoubleValue.ONE
}
// Maybe later...
// map.put("stop") { params ->
// functions.put("stop") { params ->
// Cobblemon.LOGGER.warn("Server is being stopped from a MoLang script.")
// server.stopServer()
// }
return map
return functions
}
}
@@ -20,27 +20,34 @@ import net.minecraft.server.level.ServerPlayer
object SpawnablePositionMoLangFunctions : AbstractMoLangFunctionHolder<SpawnablePosition>() {
override fun SpawnablePosition.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
val spawningContext = this
val map = mutableMapOf<String, (MoParams) -> Any>()
val worldValue = spawningContext.world.registryAccess().registryOrThrow(Registries.DIMENSION)
.wrapAsHolder(spawningContext.world).asWorldMoLangValue()
val biomeValue = spawningContext.biomeHolder.asBiomeMoLangValue()
map["biome"] = { _ -> biomeValue }
map["world"] = { _ -> worldValue }
map["light"] = { _ -> DoubleValue(spawningContext.light.toDouble()) }
map["x"] = { _ -> DoubleValue(spawningContext.position.x.toDouble()) }
map["y"] = { _ -> DoubleValue(spawningContext.position.y.toDouble()) }
map["z"] = { _ -> DoubleValue(spawningContext.position.z.toDouble()) }
map["moon_phase"] = { _ -> DoubleValue(spawningContext.moonPhase.toDouble()) }
map["can_see_sky"] = { _ -> DoubleValue(spawningContext.canSeeSky) }
map["sky_light"] = { _ -> DoubleValue(spawningContext.skyLight.toDouble()) }
map["player"] = put@{ _ ->
val causeEntity = spawningContext.cause.entity ?: return@put DoubleValue.ZERO
if (causeEntity is ServerPlayer) {
return@put causeEntity.asMoLangValue()
} else {
return@put DoubleValue.ZERO
}
val worldValue =
spawningContext
.world
.registryAccess()
.registryOrThrow(Registries.DIMENSION)
.wrapAsHolder(spawningContext.world)
.asWorldMoLangValue()
val biomeValue =
spawningContext
.biomeHolder
.asBiomeMoLangValue()
val functions = hashMapOf<String, (MoParams) -> Any>()
functions["biome"] = { biomeValue }
functions["world"] = { worldValue }
functions["light"] = { DoubleValue(spawningContext.light.toDouble()) }
functions["x"] = { DoubleValue(spawningContext.position.x.toDouble()) }
functions["y"] = { DoubleValue(spawningContext.position.y.toDouble()) }
functions["z"] = { DoubleValue(spawningContext.position.z.toDouble()) }
functions["moon_phase"] = { DoubleValue(spawningContext.moonPhase.toDouble()) }
functions["can_see_sky"] = { DoubleValue(spawningContext.canSeeSky) }
functions["sky_light"] = { DoubleValue(spawningContext.skyLight.toDouble()) }
functions["player"] = player@{
val causeEntity = spawningContext.cause.entity ?: return@player DoubleValue.ZERO
if (causeEntity is ServerPlayer) return@player causeEntity.asMoLangValue()
DoubleValue.ZERO
}
return map
return functions
}
}
@@ -17,23 +17,23 @@ import com.cobblemon.mod.common.util.asArrayValue
object SpeciesMoLangFunctions : AbstractMoLangFunctionHolder<Species>() {
override fun Species.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
val species = this
val map = mutableMapOf<String, (MoParams) -> Any>(
"identifier" to { StringValue(species.resourceIdentifier.toString()) },
"name" to { StringValue(species.name) },
"primary_type" to { StringValue(species.primaryType.showdownId) },
"secondary_type" to { StringValue(species.secondaryType?.showdownId ?: "null") },
"experience_group" to { StringValue(species.experienceGroup.name) },
"height" to { DoubleValue(species.height) },
"weight" to { DoubleValue(species.weight) },
"base_scale" to { DoubleValue(species.baseScale) },
"hitbox_width" to { DoubleValue(species.hitbox.width) },
"hitbox_height" to { DoubleValue(species.hitbox.height) },
"hitbox_fixed" to { DoubleValue(species.hitbox.fixed) },
"catch_rate" to { DoubleValue(species.catchRate) },
"labels" to { species.labels.asArrayValue { StringValue(it) } },
"has_label" to { params -> DoubleValue(species.labels.contains(params.getString(0))) }
)
val functions = hashMapOf<String, (MoParams) -> Any>()
return map
functions["identifier"] = { StringValue(species.resourceIdentifier.toString()) }
functions["name"] = { StringValue(species.name) }
functions["primary_type"] = { StringValue(species.primaryType.showdownId) }
functions["secondary_type"] = { StringValue(species.secondaryType?.showdownId ?: "null") }
functions["experience_group"] = { StringValue(species.experienceGroup.name) }
functions["height"] = { DoubleValue(species.height) }
functions["weight"] = { DoubleValue(species.weight) }
functions["base_scale"] = { DoubleValue(species.baseScale) }
functions["hitbox_width"] = { DoubleValue(species.hitbox.width) }
functions["hitbox_height"] = { DoubleValue(species.hitbox.height) }
functions["hitbox_fixed"] = { DoubleValue(species.hitbox.fixed) }
functions["catch_rate"] = { DoubleValue(species.catchRate) }
functions["labels"] = { species.labels.asArrayValue { StringValue(it) } }
functions["has_label"] = { params -> DoubleValue(species.labels.contains(params.getString(0))) }
return functions
}
}
@@ -24,7 +24,6 @@ import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
import com.cobblemon.mod.common.net.messages.client.sound.UnvalidatedPlaySoundS2CPacket
import com.cobblemon.mod.common.util.*
import java.util.UUID
import net.minecraft.core.BlockPos
import net.minecraft.core.Holder
import net.minecraft.core.registries.Registries
@@ -46,85 +45,89 @@ import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets
import net.minecraft.world.level.storage.loot.parameters.LootContextParams
import net.minecraft.world.phys.AABB
import net.minecraft.world.phys.Vec3
import java.util.UUID
object WorldMoLangFunctions : AbstractMoLangFunctionHolder<Holder<Level>>() {
override fun Holder<Level>.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
val world = this.value()
val map = mutableMapOf<String, (MoParams) -> Any>(
)
val functions = hashMapOf<String, (MoParams) -> Any>()
map["is_time_of_day"] = put@{ params ->
functions["is_time_of_day"] = isTimeOfDay@{ params ->
val timeOfDay =
TimeRange.timeRanges[params.getString(0).lowercase()]
?: return@isTimeOfDay DoubleValue.ZERO
val time = world.dayTime % 24000
val timeOfDay = TimeRange.timeRanges[params.getString(0).lowercase()]
?: return@put DoubleValue.ZERO
val time = world.dayTime % 24000
return@put DoubleValue(timeOfDay.contains(time.toInt()))
DoubleValue(timeOfDay.contains(time.toInt()))
}
map["game_time"] = { _ -> DoubleValue(world.gameTime.toDouble()) }
map["time_of_day"] = put@{
val time = world.dayTime % 24000
return@put DoubleValue(time.toDouble())
}
map["server"] = { _ -> server()?.asMoLangValue() ?: DoubleValue.ZERO }
map["is_raining_at"] = put@{ params ->
functions["game_time"] = { DoubleValue(world.gameTime.toDouble()) }
functions["time_of_day"] = { DoubleValue((world.dayTime % 24000).toDouble()) }
functions["server"] = { server()?.asMoLangValue() ?: DoubleValue.ZERO }
functions["is_raining_at"] = { params ->
val x = params.getInt(0)
val y = params.getInt(1)
val z = params.getInt(2)
return@put DoubleValue(world.isRainingAt(BlockPos(x, y, z)))
DoubleValue(world.isRainingAt(BlockPos(x, y, z)))
}
map["is_snowing_at"] = put@{ params ->
functions["is_snowing_at"] = { params ->
val x = params.getInt(0)
val y = params.getInt(1)
val z = params.getInt(2)
val blockPos = BlockPos(x, y, z)
when {
!world.isRaining -> return@put DoubleValue.ZERO
!world.canSeeSky(blockPos) -> return@put DoubleValue.ZERO
!world.isRaining -> {
DoubleValue.ZERO
}
!world.canSeeSky(blockPos) -> {
DoubleValue.ZERO
}
(world.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, blockPos).y > blockPos.y) -> {
return@put DoubleValue.ZERO
DoubleValue.ZERO
}
else -> {
val biome = world.getBiome(blockPos).value() as Biome
return@put DoubleValue(biome.getPrecipitationAt(blockPos) == Biome.Precipitation.SNOW)
DoubleValue(biome.getPrecipitationAt(blockPos) == Biome.Precipitation.SNOW)
}
}
}
map["is_chunk_loaded_at"] = put@{ params ->
functions["is_chunk_loaded_at"] = { params ->
val x = params.getInt(0)
val y = params.getInt(1)
val z = params.getInt(2)
return@put DoubleValue(world.isLoaded(BlockPos(x, y, z)))
DoubleValue(world.isLoaded(BlockPos(x, y, z)))
}
map["is_thundering"] = { _ -> DoubleValue(world.isThundering) }
map["is_raining"] = { _ -> DoubleValue(world.isRaining) }
map["set_block"] = put@{ params ->
functions["is_thundering"] = { DoubleValue(world.isThundering) }
functions["is_raining"] = { DoubleValue(world.isRaining) }
functions["set_block"] = setBlock@{ params ->
val x = params.getInt(0)
val y = params.getInt(1)
val z = params.getInt(2)
val block = world.blockRegistry.get(params.getString(3).asIdentifierDefaultingNamespace())
?: run {
Cobblemon.LOGGER.error("Unknown block: ${params.getString(3)}")
return@put DoubleValue.ZERO
}
val block =
world.blockRegistry.get(params.getString(3).asIdentifierDefaultingNamespace())
?: run {
Cobblemon.LOGGER.error("Unknown block: ${params.getString(3)}")
return@setBlock DoubleValue.ZERO
}
world.setBlock(BlockPos(x, y, z), block.defaultBlockState(), Block.UPDATE_ALL)
}
map["is_air"] = put@{ params ->
functions["is_air"] = { params ->
val x = params.getDouble(0).toInt()
val y = params.getDouble(1).toInt()
val z = params.getDouble(2).toInt()
val blockState = world.getBlockState(BlockPos(x, y, z))
return@put DoubleValue(blockState.isAir)
DoubleValue(blockState.isAir)
}
map["get_block"] = put@{ params ->
functions["get_block"] = { params ->
val x = params.getInt(0)
val y = params.getInt(1)
val z = params.getInt(2)
val block = world.getBlockState(BlockPos(x, y, z)).block
return@put world.blockRegistry.wrapAsHolder(block).asMoLangValue(Registries.BLOCK)
world.blockRegistry.wrapAsHolder(block).asMoLangValue(Registries.BLOCK)
}
map["spawn_explosion"] = { params ->
functions["spawn_explosion"] = { params ->
val x = params.getDouble(0)
val y = params.getDouble(1)
val z = params.getDouble(2)
@@ -136,32 +139,33 @@ object WorldMoLangFunctions : AbstractMoLangFunctionHolder<Holder<Level>>() {
z,
range,
ExplosionInteraction.valueOf(
params.getStringOrNull(4)?.uppercase() ?: ExplosionInteraction.TNT.name
)
params.getStringOrNull(4)?.uppercase() ?: ExplosionInteraction.TNT.name,
),
)
}
map["spawn_lightning"] = put@{ params ->
functions["spawn_lightning"] = { params ->
val x = params.getDouble(0)
val y = params.getDouble(1)
val z = params.getDouble(2)
val lightning = LightningBolt(EntityType.LIGHTNING_BOLT, world)
lightning.setPos(x, y, z)
world.addFreshEntity(lightning)
return@put DoubleValue.ONE
DoubleValue.ONE
}
// q.entity.world.spawn_bedrock_particles(effect, x, y, z, [player]) - sends to everyone nearby or just to the player if they're set.
map["spawn_bedrock_particles"] = { params ->
functions["spawn_bedrock_particles"] = { params ->
val particle = params.getString(0).asResource()
val x = params.getDouble(1)
val y = params.getDouble(2)
val z = params.getDouble(3)
val player = params.getOrNull<MoValue>(4)?.let {
when (it) {
is StringValue -> world.getPlayerByUUID(UUID.fromString(it.value))
is ObjectValue<*> -> it.obj
else -> null
}
} as? ServerPlayer
val player =
params.getOrNull<MoValue>(4)?.let {
when (it) {
is StringValue -> world.getPlayerByUUID(UUID.fromString(it.value))
is ObjectValue<*> -> it.obj
else -> null
}
} as? ServerPlayer
val pos = Vec3(x, y, z)
val packet = SpawnSnowstormParticlePacket(particle, pos)
@@ -171,7 +175,7 @@ object WorldMoLangFunctions : AbstractMoLangFunctionHolder<Holder<Level>>() {
packet.sendToPlayersAround(x, y, z, 64.0, world.dimension())
}
}
map["spawn_pokemon"] = put@{ params ->
functions["spawn_pokemon"] = spawnPokemon@{ params ->
val x = params.getInt(0)
val y = params.getInt(1)
val z = params.getInt(2)
@@ -179,49 +183,50 @@ object WorldMoLangFunctions : AbstractMoLangFunctionHolder<Holder<Level>>() {
val pos = BlockPos(x, y, z)
if (!Level.isInSpawnableBounds(pos)) {
return@put DoubleValue.ZERO
}
if (!Level.isInSpawnableBounds(pos)) return@spawnPokemon DoubleValue.ZERO
val pokemon = props.createEntity(world)
pokemon.moveTo(pos, pokemon.yRot, pokemon.xRot)
if (world.addFreshEntity(pokemon)) {
return@put pokemon.struct
} else {
return@put DoubleValue.ZERO
}
if (world.addFreshEntity(pokemon)) return@spawnPokemon pokemon.struct
DoubleValue.ZERO
}
map["spawn_npc"] = put@{ params ->
functions["spawn_npc"] = spawnNpc@{ params ->
val x = params.getDouble(0)
val y = params.getDouble(1)
val z = params.getDouble(2)
val npcClass = params.getStringOrNull(3)
?.let { NPCClasses.getByIdentifier(it.asIdentifierDefaultingNamespace()) }
val npcClass =
params
.getStringOrNull(3)
?.let { NPCClasses.getByIdentifier(it.asIdentifierDefaultingNamespace()) }
val level = params.getInt(4)
if (npcClass == null) return@put DoubleValue.ZERO
if (npcClass == null) return@spawnNpc DoubleValue.ZERO
val npc = NPCEntity(world)
npc.moveTo(x, y, z, npc.yRot, npc.xRot)
npc.npc = npcClass
npc.initialize(level)
if (world.addFreshEntity(npc)) {
return@put npc.asMoLangValue()
}
return@put DoubleValue.ZERO
if (world.addFreshEntity(npc)) return@spawnNpc npc.asMoLangValue()
DoubleValue.ZERO
}
map["play_sound_on_server"] = { params ->
functions["play_sound_on_server"] = { params ->
val sound = params.getString(0).asResource()
val soundSource = params.getString(1).uppercase()
val x = params.getDouble(2)
val y = params.getDouble(3)
val z = params.getDouble(4)
val player = params.getOrNull<MoValue>(5)?.let {
when (it) {
is StringValue -> world.getPlayerByUUID(UUID.fromString(it.value))
is ObjectValue<*> -> it.obj
else -> null
}
} as? ServerPlayer
val player =
params.getOrNull<MoValue>(5)?.let {
when (it) {
is StringValue -> world.getPlayerByUUID(UUID.fromString(it.value))
is ObjectValue<*> -> it.obj
else -> null
}
} as? ServerPlayer
val volume = params.getDoubleOrNull(6)?.toFloat() ?: 1.0f
val pitch = params.getDoubleOrNull(7)?.toFloat() ?: 1.0f
@@ -233,29 +238,30 @@ object WorldMoLangFunctions : AbstractMoLangFunctionHolder<Holder<Level>>() {
packet.sendToPlayersAround(x, y, z, 16.0, world.dimension())
}
}
map["get_entities_around"] = put@{ params ->
functions["get_entities_around"] = { params ->
val x = params.getDouble(0)
val y = params.getDouble(1)
val z = params.getDouble(2)
val range = params.getDouble(3) * 2
val entities = world.getEntities(null, AABB.ofSize(Vec3(x, y, z), range, range, range))
return@put entities
entities
.filterIsInstance<LivingEntity>()
.map { it.asMostSpecificMoLangValue() }
.asArrayValue()
}
map["is_healer_in_use"] = put@{ params ->
functions["is_healer_in_use"] = isHealerInUse@{ params ->
val pos = params.get<ArrayStruct>(0).asBlockPos()
val healer = world.getBlockEntity(pos, CobblemonBlockEntities.HEALING_MACHINE).orElse(null)
?: return@put DoubleValue.ONE
return@put DoubleValue(healer.isInUse)
val healer =
world.getBlockEntity(pos, CobblemonBlockEntities.HEALING_MACHINE).orElse(null)
?: return@isHealerInUse DoubleValue.ONE
return@isHealerInUse DoubleValue(healer.isInUse)
}
map["spawn_loot_table_items"] = put@{ params ->
functions["spawn_loot_table_items"] = spawnLootTableItems@{ params ->
val serverLevel = world as? ServerLevel
if (serverLevel == null) {
Cobblemon.LOGGER.warn("spawn_loot_table_items: world is not a ServerLevel")
return@put DoubleValue.ZERO
return@spawnLootTableItems DoubleValue.ZERO
}
val lootTableId = params.getString(0).asIdentifierDefaultingNamespace()
val x = params.getDouble(1)
@@ -265,9 +271,11 @@ object WorldMoLangFunctions : AbstractMoLangFunctionHolder<Holder<Level>>() {
val lootTableKey = ResourceKey.create(Registries.LOOT_TABLE, lootTableId)
val lootTable = serverLevel.server.reloadableRegistries().getLootTable(lootTableKey)
val lootParams = LootParams.Builder(serverLevel)
.withParameter(LootContextParams.ORIGIN, Vec3(x, y, z))
.create(LootContextParamSets.COMMAND)
val lootParams =
LootParams
.Builder(serverLevel)
.withParameter(LootContextParams.ORIGIN, Vec3(x, y, z))
.create(LootContextParamSets.COMMAND)
val items = lootTable.getRandomItems(lootParams)
val spawnedItems = ArrayStruct(hashMapOf())
@@ -285,9 +293,10 @@ object WorldMoLangFunctions : AbstractMoLangFunctionHolder<Holder<Level>>() {
}
}
}
return@put spawnedItems
spawnedItems
}
return map
return functions
}
}
@@ -67,6 +67,10 @@ object CobblemonPermissions {
val SPAWN_POKEMON = this.create("${COMMAND_PREFIX}spawnpokemon", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@JvmStatic
val SPAWN_NPC = this.create("${COMMAND_PREFIX}spawnnpc", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@JvmStatic
val SPAWN_METEOR = this.create("${COMMAND_PREFIX}spawnmeteor", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@JvmStatic
val SET_MEGA_BOND = this.create("${COMMAND_PREFIX}setmegabond", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@JvmStatic
@@ -269,7 +269,7 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
createDefault("dive_ball", CatchRateModifiers.SUBMERGED_IN_WATER, waterDragValue = 0.99F)
createDefault("nest_ball", CatchRateModifiers.NEST)
createDefault("repeat_ball", CatchRateModifiers.REPEAT)
createDefault("timer_ball", CatchRateModifiers.turnBased { turn -> (1F * turn * (1229F / 4096F)).coerceAtMost(4F) })
createDefault("timer_ball", CatchRateModifiers.turnBased { turn -> (1F + turn * (1229F / 4096F)).coerceAtMost(4F) })
createDefault("luxury_ball", effects = listOf(FriendshipEarningBoostEffect(2F)))
createDefault("premier_ball")
createDefault("dusk_ball", CatchRateModifiers.LIGHT_LEVEL)
@@ -278,7 +278,7 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
createDefault("cherish_ball")
createDefault("park_ball", CatchRateModifiers.PARK)
createDefault("dream_ball", CatchRateModifiers.statusBoosting(4F, Statuses.SLEEP))
createDefault("beast_ball", LabelModifier(5F, true, CobblemonPokemonLabels.ULTRA_BEAST)/*, LabelModifier(0.1F, false, CobblemonPokemonLabels.ULTRA_BEAST))*/)
createDefault("beast_ball", DynamicMultiplierModifier({ _, pokemon -> if (pokemon.hasLabels(CobblemonPokemonLabels.ULTRA_BEAST)) 5F else 0.1F }) { _, _ -> true })
createDefault("ancient_poke_ball", ancient = true)
createDefault("ancient_citrine_ball", ancient = true)
createDefault("ancient_verdant_ball", ancient = true)
@@ -297,13 +297,15 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
createDefault("ancient_origin_ball", GuaranteedModifier(), ancient = true)
// Luxury ball effect, low priority as it must be triggered before soothe bell as of gen 4
CobblemonEvents.FRIENDSHIP_UPDATED.subscribe(priority = Priority.LOW) { event ->
var increment = (event.newFriendship - event.pokemon.friendship).toFloat()
if (increment <= 0) //these affects are only meant to affect positive gains
if (!event.earned) {
return@subscribe
if (increment <= 1F) {
event.pokemon.caughtBall.effects.filterIsInstance<FriendshipEarningBoostEffect>()
.forEach { increment *= it.multiplier }
}
var increment = (event.newFriendship - event.pokemon.friendship).toFloat()
if (increment <= 0) {
return@subscribe
}
event.pokemon.caughtBall.effects.filterIsInstance<FriendshipEarningBoostEffect>()
.forEach { increment *= it.multiplier }
event.newFriendship = event.pokemon.friendship + increment.roundToInt()
}
}
@@ -338,7 +340,6 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
ancient: Boolean = false
): PokeBall {
val identifier = cobblemonResource(name)
//val finalModifiers = if (appendUltraBeastPenalty) modifiers + listOf(LabelModifier(0.1F, true, CobblemonPokemonLabels.ULTRA_BEAST)) else modifiers
val pokeball = PokeBall(identifier, modifier, effects, waterDragValue, model2d, model3d, throwPower, ancient)
this.defaults[identifier] = pokeball
return pokeball
@@ -54,22 +54,20 @@ object CatchRateModifiers {
/**
* Used by [PokeBalls.LOVE_BALL].
* Boosts the catch rate if the Pokémon is of the same species and opposite gender
* Boosts the catch rate 2.5× if any of the player's active Pokémon is of the opposite gender,
* or 8× if that Pokémon is also of the same species.
*/
val LOVE = BattleModifier { _, playerPokemon, pokemon ->
if (
pokemon.gender != Gender.GENDERLESS
&& playerPokemon.mapNotNull { it.battlePokemon?.originalPokemon }
.any {
it.gender != Gender.GENDERLESS &&
it.species.name.equals(pokemon.species.name, true) &&
it.gender != pokemon.gender
}
) {
if (playerPokemon.any { it.battlePokemon?.originalPokemon?.species?.resourceIdentifier == pokemon.species.resourceIdentifier }) 8F else 2.5F
if (pokemon.gender == Gender.GENDERLESS) {
return@BattleModifier 1F
}
else {
1F
val oppositeGender = playerPokemon
.mapNotNull { it.battlePokemon?.originalPokemon }
.filter { it.gender != Gender.GENDERLESS && it.gender != pokemon.gender }
when {
oppositeGender.any { it.species.resourceIdentifier == pokemon.species.resourceIdentifier } -> 8F
oppositeGender.isNotEmpty() -> 2.5F
else -> 1F
}
}
@@ -81,7 +79,7 @@ object CatchRateModifiers {
* 1× otherwise.
*/
val MOON_PHASES: CatchRateModifier = WorldStateModifier { _, entity ->
if (entity.level().gameTime in 12000..24000)
if (entity.level().dayTime % 24000 < 12000)
return@WorldStateModifier 1F
when (entity.level().moonPhase) {
2, 6 -> 1.5F
@@ -42,7 +42,7 @@ class PokedexManager(
}
// Java interop isn't great lol
@Deprecated("Use encounter(PokedexEntityData) instead")
@Deprecated("Use obtain(Pokemon) instead")
fun catch(pokemon: Pokemon) {
obtain(pokemon)
}
@@ -778,6 +778,7 @@ open class PokemonProperties {
shiny?.let { json.addProperty(DataKeys.POKEMON_SHINY, it) }
gender?.let { json.addProperty(DataKeys.POKEMON_GENDER, it.name) }
species?.let { json.addProperty(DataKeys.POKEMON_SPECIES_TEXT, it) }
nickname?.let { json.addProperty(DataKeys.POKEMON_NICKNAME, it.string) }
form?.let { json.addProperty(DataKeys.POKEMON_FORM_ID, it) }
friendship?.let { json.addProperty(DataKeys.POKEMON_FRIENDSHIP, it) }
fullness?.let { json.addProperty(DataKeys.POKEMON_FULLNESS, it) }
@@ -814,6 +815,7 @@ open class PokemonProperties {
shiny = json.get(DataKeys.POKEMON_SHINY)?.asBoolean
gender = json.get(DataKeys.POKEMON_GENDER)?.asString?.let { Gender.valueOf(it) }
species = json.get(DataKeys.POKEMON_SPECIES_TEXT)?.asString
nickname = json.get(DataKeys.POKEMON_NICKNAME)?.asString?.let(Component::literal)
form = json.get(DataKeys.POKEMON_FORM_ID)?.asString
friendship = json.get(DataKeys.POKEMON_FRIENDSHIP)?.asInt
fullness = json.get(DataKeys.POKEMON_FULLNESS)?.asInt
@@ -109,8 +109,10 @@ open class Learnset {
evolutionMoves
}
// We only sync level up moves atm
// Syncs level up, TM, and egg moves.
fun shouldSynchronize(other: Learnset) = other.levelUpMoves != this.levelUpMoves
|| other.tmMoves != this.tmMoves
|| other.eggMoves != this.eggMoves
fun decode(buffer: RegistryFriendlyByteBuf) {
this.levelUpMoves.clear()
@@ -122,6 +124,8 @@ open class Learnset {
}
levelUpMoves[level] = moves
}
decodeMoveList(buffer, this.tmMoves)
decodeMoveList(buffer, this.eggMoves)
}
fun encode(buffer: RegistryFriendlyByteBuf) {
@@ -133,5 +137,21 @@ open class Learnset {
buffer.writeInt(move.num)
}
}
encodeMoveList(buffer, this.tmMoves)
encodeMoveList(buffer, this.eggMoves)
}
private fun decodeMoveList(buffer: RegistryFriendlyByteBuf, list: MutableList<MoveTemplate>) {
list.clear()
repeat(times = buffer.readSizedInt(IntSize.U_SHORT)) {
Moves.getByNumericalId(buffer.readInt())?.let(list::add)
}
}
private fun encodeMoveList(buffer: RegistryFriendlyByteBuf, list: List<MoveTemplate>) {
buffer.writeSizedInt(IntSize.U_SHORT, list.size)
for (move in list) {
buffer.writeInt(move.num)
}
}
}
@@ -54,8 +54,8 @@ enum class Stats(override val identifier: ResourceLocation, override val display
"hp" -> HP
"atk", "attack" -> ATTACK // Hyper Cutter states the full stat name "Attack"
"def", "defense", "defence" -> DEFENCE // Big Pecks states the full stat name "Defense"
"spa" -> SPECIAL_ATTACK
"spd" -> SPECIAL_DEFENCE
"spa", "special_attack" -> SPECIAL_ATTACK
"spd", "special_defence", "special_defense" -> SPECIAL_DEFENCE
"spe", "speed" -> SPEED
"evasion" -> EVASION
else -> ACCURACY
@@ -52,6 +52,10 @@ import org.joml.Vector3f
class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
companion object {
val KEY = cobblemonResource("air/bird")
private const val FLY_POSE_SPEED = 0.1
private const val GLIDE_ENTRY_SPEED = 0.19
private const val GLIDE_EXIT_SPEED = 0.13
}
override val key: ResourceLocation = KEY
@@ -61,7 +65,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
}
val poseProvider = PoseProvider<BirdSettings, BirdState>(PoseType.HOVER)
.with(PoseOption(PoseType.FLY) { _, state, _ -> state.rideVelocity.get().z > 0.2 })
.with(PoseOption(PoseType.FLY) { _, state, _ -> state.rideVelocity.get().z > FLY_POSE_SPEED })
val globalBird: BirdSettings
get() = CobblemonRideSettings.bird
@@ -204,21 +208,23 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
}
// speed up and slow down based on input
if (driver.zza != 0.0f && state.stamina.get() > 0.0 && newVelocity.length() < glideTopSpeed) {
//make sure it can't exceed top speed
val forwardInput = when {
state.stamina.get() == 0.0f -> 0.0
driver.zza > 0 && newVelocity.z > topSpeed -> 0.0
driver.zza < 0 && newVelocity.z < (-topSpeed / 3.0) -> 0.0
else -> driver.zza.sign
}
newVelocity = Vec3(
newVelocity.x,
newVelocity.y,
(newVelocity.z + (accel * forwardInput.toDouble())))
if (driver.zza != 0.0f && state.stamina.get() > 0.0) {
activeInput = true
if (newVelocity.length() < glideTopSpeed) {
//make sure it can't exceed top speed
val forwardInput = when {
state.stamina.get() == 0.0f -> 0.0
driver.zza > 0 && newVelocity.z > topSpeed -> 0.0
driver.zza < 0 && newVelocity.z < (-topSpeed / 3.0) -> 0.0
else -> driver.zza.sign
}
newVelocity = Vec3(
newVelocity.x,
newVelocity.y,
(newVelocity.z + (accel * forwardInput.toDouble())))
}
}
val controller = (vehicle as? OrientationControllable)?.orientationController
@@ -276,11 +282,9 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
//Check if the ride should be gliding
if (driver.isLocalPlayer) {
if ((activeInput && state.stamina.get() > 0.0) || state.rideVelocity.get().z < 0.2) {
state.gliding.set(false)
} else {
state.gliding.set(true)
}
val powered = activeInput && state.stamina.get() > 0.0
val requiredSpeed = if (state.gliding.get()) GLIDE_EXIT_SPEED else GLIDE_ENTRY_SPEED
state.gliding.set(!powered && newVelocity.z >= requiredSpeed)
}
//air resistance
@@ -193,7 +193,7 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
val newStam = if (state.sprinting.get()) {
max(0.0f,stam - stamDrainRate)
} else {
min(1.0f,stam + stamDrainRate * 4)
min(1.0f,stam + stamDrainRate * 8) // Need to change this to probably be in seconds eventually? idk something that isn't this magic number.
}
state.stamina.set(newStam.coerceIn(0F, 1F))
@@ -147,7 +147,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
val stamDrainRate = (1.0f / boostTime).toFloat()
val newStam = if (state.boosting.get()) max(0.0f,stam - stamDrainRate)
else (stam + stamDrainRate).coerceIn(0F, 1F)
else (stam + stamDrainRate * 2).coerceIn(0F, 1F)
state.stamina.set(newStam)
}
@@ -19,6 +19,7 @@ import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.SubmergedSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.SurfaceSpawningCondition
import com.cobblemon.mod.common.api.spawning.detail.EmptySpawnDetail
import com.cobblemon.mod.common.api.spawning.detail.FallingStarSpawnDetail
import com.cobblemon.mod.common.api.spawning.detail.NPCSpawnDetail
import com.cobblemon.mod.common.api.spawning.detail.PokemonHerdSpawnDetail
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
@@ -122,6 +123,7 @@ object BestSpawner {
SpawnDetail.registerSpawnType(name = NPCSpawnDetail.TYPE, NPCSpawnDetail::class.java)
SpawnDetail.registerSpawnType(name = PokemonHerdSpawnDetail.TYPE, PokemonHerdSpawnDetail::class.java)
SpawnDetail.registerSpawnType(name = EmptySpawnDetail.TYPE, EmptySpawnDetail::class.java)
SpawnDetail.registerSpawnType(name = FallingStarSpawnDetail.TYPE, FallingStarSpawnDetail::class.java)
LOGGER.info("Loaded ${SpawnDetail.spawnDetailTypes.size} spawn detail types.")
loadConfig()
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.spawning.MoonPhaseRange
import com.cobblemon.mod.common.api.spawning.TimeRange
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
import com.cobblemon.mod.common.block.entity.PokeSnackBlockEntity
import com.cobblemon.mod.common.util.Merger
import com.cobblemon.mod.common.util.math.orMax
import com.cobblemon.mod.common.util.math.orMin
@@ -59,6 +60,7 @@ abstract class SpawningCondition<T : SpawnablePosition> {
var structures: MutableList<Either<ResourceLocation, TagKey<Structure>>>? = null
var isSlimeChunk: Boolean? = null
var markers: MutableList<String>? = null
var isPokeSnack: Boolean? = null
@Transient
var appendages = mutableListOf<AppendageCondition>()
@@ -75,7 +77,9 @@ abstract class SpawningCondition<T : SpawnablePosition> {
}
protected open fun fits(spawnablePosition: T): Boolean {
if (spawnablePosition.position.x < minX.orMin() || spawnablePosition.position.x > maxX.orMax()) {
if (isPokeSnack != null && (spawnablePosition.spawner is PokeSnackBlockEntity.Spawner != isPokeSnack!!)) {
return false
} else if (spawnablePosition.position.x < minX.orMin() || spawnablePosition.position.x > maxX.orMax()) {
return false
} else if (spawnablePosition.position.y < minY.orMin() || spawnablePosition.position.y > maxY.orMax()) {
return false
@@ -32,6 +32,7 @@ import com.cobblemon.mod.common.api.spawning.selection.SpawnSelectionData
class EmptySpawnDetail : SpawnDetail() {
companion object {
val TYPE = "empty"
val EMPTY = EmptySpawnDetail()
}
override val type: String = TYPE
@@ -0,0 +1,47 @@
/*
* 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.spawning.detail
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonEntities
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
import com.cobblemon.mod.common.entity.projectile.FallingStarEntity
import kotlin.math.min
import kotlin.random.Random
import net.minecraft.world.phys.Vec3
class FallingStarSpawnAction(
spawnablePosition: SpawnablePosition,
bucket: String,
override val detail: FallingStarSpawnDetail
) : SpawnAction<EntitySpawnResult>(spawnablePosition, bucket, detail) {
override fun run(): EntitySpawnResult {
Cobblemon.LOGGER.info("Falling towards ${spawnablePosition.position}")
val world = spawnablePosition.world
val fallingStar = FallingStarEntity(CobblemonEntities.FALLING_STAR, world)
fallingStar.resultType = detail.resultType
val spawnPos = spawnablePosition.position
fallingStar.horizontalDirection = Random.nextDouble(2 * Math.PI).toFloat()
val skyHeight = min(spawnPos.y + 64, world.maxBuildHeight - 10).toDouble()
val fallDistance = skyHeight - spawnPos.y
val ticksToFall = kotlin.math.sqrt(fallDistance / 0.015)
val horizontalDistance = ticksToFall * 0.8
val offsetX = -kotlin.math.sin(fallingStar.horizontalDirection) * horizontalDistance
val offsetZ = -kotlin.math.cos(fallingStar.horizontalDirection) * horizontalDistance
val skyPos = Vec3(spawnPos.x + 0.5 + offsetX, skyHeight, spawnPos.z + 0.5 + offsetZ)
fallingStar.setPos(skyPos)
world.addFreshEntity(fallingStar)
return EntitySpawnResult(listOf(fallingStar))
}
}
@@ -0,0 +1,28 @@
/*
* 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.spawning.detail
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
import com.cobblemon.mod.common.api.spawning.selection.SpawnSelectionData
import com.cobblemon.mod.common.entity.projectile.FallingStarResultType
class FallingStarSpawnDetail : SpawnDetail() {
companion object {
const val TYPE = "falling_star"
}
override val type = TYPE
var resultType: FallingStarResultType = FallingStarResultType.MINIOR
override fun createSpawnAction(
spawnablePosition: SpawnablePosition,
bucket: String,
selectionData: SpawnSelectionData
) = FallingStarSpawnAction(spawnablePosition, bucket, this)
}
@@ -11,14 +11,19 @@ package com.cobblemon.mod.common.api.spawning.detail
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.api.drop.DropTable
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.api.spawning.position.AreaSpawnablePosition
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
import com.cobblemon.mod.common.api.spawning.selection.SpawnSelectionData
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.weightedSelection
import com.google.gson.annotations.SerializedName
import net.minecraft.core.registries.Registries
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth.clamp
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import kotlin.math.ceil
/**
* A spawn detail that spawns a herd of Pokémon. This relies on the same [PokemonSpawnAction]s as
@@ -36,7 +41,6 @@ import net.minecraft.world.item.Items
class PokemonHerdSpawnDetail : SpawnDetail() {
companion object {
val TYPE = "pokemon-herd"
const val HERD_LEADER = "leader"
}
override val type: String = TYPE
@@ -50,14 +54,14 @@ class PokemonHerdSpawnDetail : SpawnDetail() {
get() = "${id}__LEVEL"
class Herdable {
var pokemon: PokemonProperties = PokemonProperties()
var pokemon = PokemonProperties()
val levelRange: IntRange? = null
var levelRangeOffset: IntRange? = null
val herdLevelRange: IntRange? = null
var dropTable: DropTable? = null
var isLeader: Boolean = false
var isFollower: Boolean = true
var weight: Float = 1F
var isLeader = false
var isFollower = true
var weight = 1F
var maxTimes = 10
var heldItem: ResourceLocation? = null
}
@@ -69,22 +73,23 @@ class PokemonHerdSpawnDetail : SpawnDetail() {
selectionData: SpawnSelectionData
) {
selectionData.removeSpawnDetails { it != this }
selectionData.guaranteedBucket = bucket
selectionData.removeSpawnablePositions { _, pos -> pos.distanceTo(spawnablePosition) < minDistanceBetweenSpawns }
val herdSpawnCount = selectionData.spawnActions.count { it.detail == this } + 1
val lacksPossibleLeader = lacksPossibleLeader(selectionData)
if (herdSpawnCount >= maxHerdSize || getValidHerdMembers(selectionData).isEmpty()) {
if (herdSpawnCount >= maxHerdSize || getValidHerdMembersForRole(selectionData, useLeaderRole = lacksPossibleLeader).isEmpty()) {
// We can't spawn any more in the herd so remove this spawn from the selection pool.
selectionData.removeSpawnDetails { it == this }
}
}
fun lacksPossibleLeader(selectionData: SpawnSelectionData): Boolean {
val level = getOrSetHerdLevel(selectionData)
val herdable = getValidHerdMembers(selectionData)
val herdCounts = getHerdMemberCounts(selectionData)
val leaderIsPossible = herdable.any { it.herdLevelRange?.contains(level) != false && it.isLeader }
val leaderIsPossible = herdable.any { it.isLeader }
val leaderIsSelected = herdCounts.any { it.key?.isLeader == true && it.value > 0 }
return leaderIsPossible && !leaderIsSelected
}
@@ -93,25 +98,42 @@ class PokemonHerdSpawnDetail : SpawnDetail() {
return (selectionData.context[herdLevelKey] as? Int) ?: run {
val level = levelRange.random()
selectionData.context[herdLevelKey] = level
return level
level
}
}
private fun getValidHerdMembersForRole(selectionData: SpawnSelectionData, useLeaderRole: Boolean): List<Herdable> {
return getValidHerdMembers(selectionData).filter { (useLeaderRole && it.isLeader) || (!useLeaderRole && it.isFollower) }
}
override fun createSpawnAction(
spawnablePosition: SpawnablePosition,
bucket: String,
selectionData: SpawnSelectionData
): PokemonSpawnAction {
val validHerdMembers = getValidHerdMembers(selectionData)
var searchedForLeader = false
val herdable = if (lacksPossibleLeader(selectionData)) {
searchedForLeader = true
validHerdMembers.filter { it.isLeader }
} else {
validHerdMembers.filter { it.isFollower }
}.weightedSelection { it.weight } ?: herdablePokemon[0]
): SpawnAction<*> {
val herdable = getValidHerdMembersForRole(
selectionData = selectionData,
useLeaderRole = lacksPossibleLeader(selectionData)
).weightedSelection { it.weight } ?: herdablePokemon[0]
var level = selectionData.context[herdLevelKey] as? Int ?: 1
// Attempt to do more detailed anti-wall-spawn logic. Herd members could spawn in walls because
// we've just been assuming they're 1 block wide (or at least, all in the herd have the same size).
// Some pokemon are not that though.
if (spawnablePosition is AreaSpawnablePosition) {
val species = herdable.pokemon.species?.let { PokemonSpecies.getByIdentifier(it.asIdentifierDefaultingNamespace()) }
if (species != null) {
val height = ceil(species.hitbox.height * species.baseScale).toInt()
val width = ceil(species.hitbox.width * species.baseScale).toInt()
if (!spawnablePosition.hasSpace(width, height)) {
return EmptySpawnAction(spawnablePosition, bucket, EmptySpawnDetail.EMPTY)
}
}
}
level = herdable.levelRange?.let { clamp(level, it.first, it.last) } ?: level
val level = selectionData.context[herdLevelKey] as? Int ?: 1
val levelRange = herdable.levelRangeOffset?.let { offset ->
val min = level + offset.first
val max = level + offset.last
@@ -136,11 +158,7 @@ class PokemonHerdSpawnDetail : SpawnDetail() {
drops = herdable.dropTable,
heldItem = heldItem,
levelRange = levelRange
).also {
if (herdable.isLeader && searchedForLeader) {
it.labels += HERD_LEADER
}
}
)
}
fun getHerdMemberCounts(selectionData: SpawnSelectionData): Map<Herdable?, Int> {
@@ -71,7 +71,7 @@ class FishingSpawnCause(
}
fun alterSize(pokemonEntity: PokemonEntity, effect: SpawnBait.Effect) {
pokemonEntity.pokemon.scaleModifier *= effect.value.toFloat() / 1000.0f
pokemonEntity.pokemon.scaleModifier += effect.value.toFloat() / 1000.0f
}
fun saveDropsReroll(pokemonEntity: PokemonEntity, effect: SpawnBait.Effect) {
@@ -53,34 +53,39 @@ open class AreaSpawnablePosition(
*/
open fun isSafeSpace(world: ServerLevel, pos: BlockPos, state: BlockState): Boolean = !state.isSolid
fun hasSpace(width: Int, height: Int): Boolean {
val sizeX = width.takeIf { it > 0 } ?: 1
val sizeY = height.takeIf { it > 0 } ?: 1
val minX = floor(position.x + 0.5 - (sizeX - 1) / 2F).toInt() - 1
val maxX = ceil(position.x + 0.5 + (sizeX + 1) / 2F).toInt() + 1
val maxY = ceil(position.y + (sizeY + 1) / 2F).toInt() + 1
val minZ = floor(position.z + 0.5 - (sizeX - 1) / 2F).toInt() - 1
val maxZ = ceil(position.z + 0.5 + (sizeX + 1) / 2F).toInt() + 1
val mutable = BlockPos.MutableBlockPos()
for (x in minX until maxX) {
for (y in (position.y + 1)..maxY) {
for (z in minZ until maxZ) {
val state = zone.getBlockState(mutable.set(x, y, z))
if (!isSafeSpace(world, mutable, state)) {
return false
}
}
}
}
return true
}
override fun postFilter(detail: SpawnDetail): Boolean {
if (!super.postFilter(detail)) {
return false
}
if (detail.width > 1 || detail.height > 1) {
val sizeX = detail.width.takeIf { it > 0 } ?: 1
val sizeY = detail.height.takeIf { it > 0 } ?: 1
val minX = floor(position.x + 0.5 - (sizeX - 1) / 2F).toInt() - 1
val maxX = ceil(position.x + 0.5 + (sizeX + 1) / 2F).toInt() + 1
val maxY = ceil(position.y + (sizeY + 1) / 2F).toInt() + 1
val minZ = floor(position.z + 0.5 - (sizeX - 1) / 2F).toInt() - 1
val maxZ = ceil(position.z + 0.5 + (sizeX + 1) / 2F).toInt() + 1
val mutable = BlockPos.MutableBlockPos()
for (x in minX until maxX) {
for (y in (position.y + 1)..maxY) {
for (z in minZ until maxZ) {
val state = zone.getBlockState(mutable.set(x, y, z))
if (!isSafeSpace(world, mutable, state)) {
return false
}
}
}
}
return hasSpace(detail.width, detail.height)
}
return true
@@ -28,7 +28,7 @@ import java.io.InputStreamReader
* @since July 8th, 2022
*/
class BestSpawnerConfig {
val version = 0
val version = 2
/** Whether an external config will be replaced by an internal one once [version] is higher on the internal. */
val replaceWithNewVersion = true
@SerializedName("spawnablePositionTypeWeights", alternate = ["contextWeights"])
@@ -40,11 +40,11 @@ class BestSpawnerConfig {
)
val worldBuckets = mutableMapOf(
"common" to 94F,
"common" to 94.05F,
"uncommon" to 5F,
"rare" to 0.5F,
"ultra-rare" to 0.2F,
"boss" to 0.3F,
"boss" to 0.25F,
)
val fishingBuckets = mutableMapOf(
@@ -89,6 +89,7 @@ open class FlatSelector : SpawningSelector<FlatSelector.SpawnablePositionSelecti
val spawnablePositions: List<SpawnablePosition>,
val selectingSpawnInformation: MutableMap<String, SpawnablePositionBucketSelectionData>,
): SpawnSelectionData {
override var guaranteedBucket: String? = null
override val spawnActions = mutableListOf<SpawnAction<*>>()
override val context = mutableMapOf<String, Any>()
@@ -101,6 +101,7 @@ open class FlatSpawnablePositionWeightedSelector : SpawningSelector<FlatSpawnabl
val spawner: Spawner,
val spawnablePositions: List<SpawnablePosition>
) : SpawnSelectionData {
override var guaranteedBucket: String? = null
override val spawnActions = mutableListOf<SpawnAction<*>>()
override val context = mutableMapOf<String, Any>()
@@ -21,6 +21,7 @@ import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
* @since April 21st, 2025
*/
interface SpawnSelectionData {
var guaranteedBucket: String?
val context: MutableMap<String, Any>
val spawnActions: MutableList<SpawnAction<*>>
fun removeSpawnDetails(shouldRemove: (SpawnDetail) -> Boolean)
@@ -28,6 +29,7 @@ interface SpawnSelectionData {
companion object {
val EMPTY = object : SpawnSelectionData {
override var guaranteedBucket: String? = null
override val context: MutableMap<String, Any> = mutableMapOf()
override val spawnActions: MutableList<SpawnAction<*>> = mutableListOf()
override fun removeSpawnDetails(shouldRemove: (SpawnDetail) -> Boolean) {}
@@ -47,7 +47,7 @@ interface SpawningSelector<T : SpawnSelectionData> {
val spawnActions = selectionData.spawnActions
while (spawnActions.size < maxSpawns) {
val bucket = bucketSelector()
val bucket = selectionData.guaranteedBucket ?: bucketSelector()
val spawnAction = selectSpawnAction(
spawner = spawner,
bucket = bucket,
@@ -17,6 +17,7 @@ import com.cobblemon.mod.common.api.spawning.influence.BucketNormalizingInfluenc
import com.cobblemon.mod.common.api.spawning.influence.SpawnBaitInfluence
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.api.spawning.rules.SpawnRule
import com.cobblemon.mod.common.block.entity.PokeSnackBlockEntity
import com.cobblemon.mod.common.util.mutableLazy
import net.minecraft.core.BlockPos
import net.minecraft.server.level.ServerLevel
@@ -66,10 +67,10 @@ object PokeSnackSpawnerFactory {
},
)
fun create(ctx: Context): FixedAreaSpawner {
fun create(ctx: Context): PokeSnackBlockEntity.Spawner {
val influences = influenceBuilders.mapNotNull { it(ctx) }
return FixedAreaSpawner(
return PokeSnackBlockEntity.Spawner(
name = ctx.name,
spawnPool = spawnPool,
buckets = Cobblemon.bestSpawner.config.pokeSnackBuckets,
@@ -46,6 +46,7 @@ open class BottomlessStore(override val uuid: UUID) : PokemonStore<BottomlessPos
pokemon.forEachIndexed { index, pokemon ->
pokemon.storeCoordinates.set(StoreCoordinates(this, BottomlessPosition(index)))
}
rebuildPokemonByUuid()
}
override fun saveToNBT(nbt: CompoundTag, registryAccess: RegistryAccess): CompoundTag {
@@ -17,10 +17,10 @@ import com.cobblemon.mod.common.api.storage.factory.PokemonStoreFactory
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.DataKeys
import com.google.gson.JsonObject
import java.util.UUID
import net.minecraft.core.RegistryAccess
import net.minecraft.nbt.CompoundTag
import net.minecraft.server.level.ServerPlayer
import java.util.UUID
/**
* The base class for all stores of [Pokemon]. A store specifies the kind of coordinate it needs to be given
@@ -53,7 +53,7 @@ abstract class PokemonStore<T : StorePosition> : Iterable<Pokemon> {
* Runs initialization logic for this store, knowing that it has just been constructed in a [PokemonStoreFactory].
*
* The minimum of what this function should do is iterate over all the Pokémon in this store and set their store
* coordinates.
* coordinates. Built-in stores then rebuild the UUID index via [rebuildPokemonByUuid].
*
* If this does not get called, or it does not do its job properly, serious de-sync issues may follow.
*/
@@ -77,7 +77,6 @@ abstract class PokemonStore<T : StorePosition> : Iterable<Pokemon> {
remove(pokemon)
val position = getFirstAvailablePosition() ?: return false // Couldn't fit, shrug emoji
set(position, pokemon)
Cobblemon.playerDataManager.getTMData(uuid)?.syncTMsFromPokemon(pokemon)
return true
}
@@ -99,6 +98,7 @@ abstract class PokemonStore<T : StorePosition> : Iterable<Pokemon> {
setAtPosition(position, pokemon)
pokemon.storeCoordinates.set(StoreCoordinates(this, position))
pokemonByUuid[pokemon.uuid] = pokemon
}
/** Swaps the Pokémon at the specified positions. If one of the spaces is empty, it will simply move the not-null one to that space. */
@@ -147,10 +147,26 @@ abstract class PokemonStore<T : StorePosition> : Iterable<Pokemon> {
pokemon.recall()
pokemon.storeCoordinates.set(null)
setAtPosition(currentPosition.position, null)
pokemonByUuid.remove(pokemon.uuid)
return true
}
operator fun get(uuid: UUID) = find { it.uuid.equals(uuid) }
/** In-memory UUID index. Not serialized; rebuilt by [rebuildPokemonByUuid]. */
private val pokemonByUuid = hashMapOf<UUID, Pokemon>()
/** Gets the [Pokemon] with this UUID, or null if it is not in this store. */
operator fun get(uuid: UUID): Pokemon? = pokemonByUuid[uuid]
/**
* Rebuilds the UUID index from the store's current contents.
*
* Call after load or any structural mutation that does not go through [set] / [remove], such as
* filling slots directly from NBT/JSON or shrinking a PC.
*/
protected fun rebuildPokemonByUuid() {
pokemonByUuid.clear()
forEach { pokemonByUuid[it.uuid] = it }
}
open fun handleInvalidSpeciesNBT(nbt: CompoundTag) {
Cobblemon.LOGGER.error("Failed to read unknown species: ${nbt.getString(DataKeys.POKEMON_SPECIES_IDENTIFIER)}")
@@ -138,6 +138,7 @@ open class PartyStore(override val uuid: UUID) : PokemonStore<PartyPosition>() {
val pokemon = get(slot) ?: continue
pokemon.storeCoordinates.set(StoreCoordinates(this, PartyPosition(slot)))
}
rebuildPokemonByUuid()
}
override fun onPokemonChanged(pokemon: Pokemon) {
@@ -279,7 +280,7 @@ open class PartyStore(override val uuid: UUID) : PokemonStore<PartyPosition>() {
}
}
return result
return result.sortedBy { it.health <= 0 }
}
fun clearParty() {
forEach {
@@ -105,6 +105,7 @@ open class PCStore(
override fun initialize() {
boxes.forEach { it.initialize() }
backupStore.initialize()
rebuildPokemonByUuid()
}
fun relocateEvictedBoxPokemon(pokemon: Pokemon) {
@@ -135,6 +136,7 @@ open class PCStore(
tryRestoreBackedUpPokemon()
}
rebuildPokemonByUuid()
pcChangeObservable.emit(Unit)
}
@@ -142,6 +144,7 @@ open class PCStore(
this.lockedSize = lockNewSize
boxes.removeAll(boxList)
boxList.flatMap { it.asIterable() }.forEach(overflowHandler)
rebuildPokemonByUuid()
pcChangeObservable.emit(Unit)
}
@@ -66,6 +66,8 @@ object CobblemonBiomeTags {
val IS_WARM_OCEAN = create("is_warm_ocean")
@JvmField
val IS_DEEP_DARK = create("is_deep_dark")
@JvmField
val IS_MOUNTAIN = create("is_mountain")
// Has Block tags
@JvmField
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.api.tms
import com.cobblemon.mod.common.tms.obtain.DefaultObtainMethod
import com.cobblemon.mod.common.tms.obtain.ImpossibleObtainMethod
import com.cobblemon.mod.common.tms.obtain.PlayerHasAdvancementObtainMethod
import com.cobblemon.mod.common.tms.obtain.PlayerYObtainMethod
import com.cobblemon.mod.common.tms.obtain.UnlockableObtainMethod
@@ -36,6 +37,7 @@ interface ObtainMethod {
val variant = buffer.readUtf()
return when (variant) {
"cobblemon:unlockable" -> UnlockableObtainMethod()
"cobblemon:impossible" -> ImpossibleObtainMethod()
"cobblemon:default" -> DefaultObtainMethod()
"cobblemon:advancement" -> {
val advancement = buffer.readResourceLocation()
@@ -14,6 +14,8 @@ import com.cobblemon.mod.common.advancement.CobblemonCriteria
import com.cobblemon.mod.common.advancement.criterion.CountableContext
import com.cobblemon.mod.common.advancement.criterion.LearnAllTMContext
import com.cobblemon.mod.common.advancement.criterion.LearnTMContext
import com.cobblemon.mod.common.api.abilities.Ability
import com.cobblemon.mod.common.api.abilities.AbilityPool
import com.cobblemon.mod.common.api.moves.MoveTemplate
import com.cobblemon.mod.common.api.scheduling.ScheduledTask
import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
@@ -50,6 +52,11 @@ class TMMoveManager(
learn(listOf(tmId))
}
fun syncTMLockFromMove(moveTemplate: MoveTemplate) {
val tmId = TechnicalMachines.moveToTM[moveTemplate]?.id ?: return
unlearn(listOf(tmId))
}
fun getLearnableTMsFromPokemon(pokemon: Pokemon): Collection<ResourceLocation> {
val learnableMoves = pokemon.allAccessibleMoves.toMutableSet()
learnableMoves.addAll(pokemon.moveSet.map { it.template })
@@ -12,8 +12,9 @@ import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.moves.MoveTemplate
import com.cobblemon.mod.common.api.moves.adapters.MoveTemplateAdapter
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.net.messages.client.tms.TechnicalMachineRegistrySyncPacket
import com.cobblemon.mod.common.util.adapters.TMObtainMethodAdapter
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
@@ -70,5 +71,7 @@ object TechnicalMachines : JsonDataRegistry<TechnicalMachine> {
fun getByResourceLocation(resourceLocation: ResourceLocation) = tmMap[resourceLocation]
fun getAllResourceLocations() = tmMap.keys.toSet()
override fun sync(player: ServerPlayer) {}
override fun sync(player: ServerPlayer) {
TechnicalMachineRegistrySyncPacket(tmMap.values.toList()).sendToPlayer(player)
}
}
@@ -42,10 +42,12 @@ class ElementalType(
companion object {
@JvmStatic
val BY_STRING_CODEC: Codec<ElementalType> = CodecUtils.createByStringCodec(
ElementalTypes::get,
{it.showdownId}
) { id -> "No ElementalType for ID $id" }
val BY_STRING_CODEC: Codec<ElementalType> by lazy {
CodecUtils.createByStringCodec(
ElementalTypes::get,
{ it.showdownId }
) { id -> "No ElementalType for ID $id" }
}
}
}
}
@@ -336,11 +336,12 @@ object BattleBuilder {
npcPartyStores.add(npcParty)
}
if (playerActor.pokemonList.size < battleFormat.battleType.slotsPerActor) {
val playerAvailablePokemon = playerActor.pokemonList.count { it.health > 0 }
if (playerAvailablePokemon < battleFormat.battleType.slotsPerActor) {
errors.participantErrors[playerActor] += BattleStartError.insufficientPokemon(
actorEntity = player,
requiredCount = battleFormat.battleType.slotsPerActor,
hadCount = playerActor.pokemonList.size
hadCount = playerAvailablePokemon
)
}
@@ -17,6 +17,8 @@ import com.cobblemon.mod.common.util.*
import com.google.gson.*
import com.google.gson.reflect.TypeToken
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.world.entity.EquipmentSlot
import net.minecraft.world.item.Items
import java.lang.Integer.max
import java.lang.reflect.Type
import java.util.*
@@ -80,7 +82,8 @@ class ShowdownActionRequest(
ShowdownMoveset.Gimmick.TERASTALLIZATION -> cobblemonResource("tera_orb")
else -> cobblemonResource("z_ring")
}
if (!player.hasKeyItem(triggerItem)) moveset.blockGimmick(gimmick)
// lets do something silly like an iron helmet for now in place of a keystone TODO change this later lol
if (!player.hasKeyItem(triggerItem) && !player.getItemBySlot(EquipmentSlot.HEAD).`is`(Items.IRON_HELMET)) moveset.blockGimmick(gimmick)
}
}
}
@@ -166,7 +166,7 @@ object AIUtility {
"geargrind" to Pair(2, 2),
"twinbeam" to Pair(2, 2),
"twineedle" to Pair(2, 2),
"suringstrikes" to Pair(3, 3),
"surgingstrikes" to Pair(3, 3),
"tripledive" to Pair(3, 3),
"watershuriken" to Pair(3, 3),
@@ -163,7 +163,8 @@ class TrackerActor(val uuid: UUID) {
//we assume that AI knows it all about our allies
trackerActor.party.addAll(actor.pokemonList.map { createAllied(it) })
actor.activePokemon.forEach {
val active = trackerActor.party.first { trackMon -> trackMon.id == it.battlePokemon!!.uuid }
val battlePokemon = it.battlePokemon ?: return@forEach
val active = trackerActor.party.first { trackMon -> trackMon.id == battlePokemon.uuid }
trackerActor.party.remove(active)
trackerActor.activePokemon.add(active)
}
@@ -176,7 +177,8 @@ class TrackerActor(val uuid: UUID) {
//only know basics of what we see right now
trackerActor.party.addAll(actor.pokemonList.map { createOpposing(it) })
actor.activePokemon.forEach {
val active = trackerActor.party.first { trackMon -> trackMon.id == it.battlePokemon!!.uuid }
val battlePokemon = it.battlePokemon ?: return@forEach
val active = trackerActor.party.first { trackMon -> trackMon.id == battlePokemon.uuid }
trackerActor.party.remove(active)
trackerActor.activePokemon.add(active)
}
@@ -39,7 +39,6 @@ class FaintInstruction(battle: PokemonBattle, val message: BattleMessage) : Inte
battle.dispatchFuture {
val (pnx, _) = message.pnxAndUuid(0) ?: return@dispatchFuture CompletableFuture.completedFuture(Unit)
val pokemon = message.battlePokemon(0, battle) ?: return@dispatchFuture CompletableFuture.completedFuture(Unit)
battle.sendUpdate(BattleFaintPacket(pnx))
val actor = pokemon.actor
pokemon.effectedPokemon.currentHealth = 0
val preamble = if (actor is EntityBackedBattleActor<*>) {
@@ -50,6 +49,8 @@ class FaintInstruction(battle: PokemonBattle, val message: BattleMessage) : Inte
val context = ShowdownInterpreter.getContextFromFaint(faintingPokemon, battle)
preamble.thenAccept {
// Sent after the faint/recall animation so clients don't learn about the KO early
battle.sendUpdate(BattleFaintPacket(pnx))
faintingPokemon.effectedPokemon.currentHealth = 0
faintingPokemon.faintedAt = battle.faintCounter++
faintingPokemon.sendUpdate()
@@ -73,7 +73,7 @@ class BerryBlock(private val berryIdentifier: ResourceLocation, settings: Proper
override fun playerWillDestroy(world: Level, pos: BlockPos, state: BlockState, player: Player): BlockState {
if (!player.isCreative && state.getValue(AGE) == FRUIT_AGE) {
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
treeEntity.harvest(world, state, pos, player).forEach { drop -> Block.popResource(world, pos, drop) }
treeEntity.harvest(world, state, pos, player).forEach { drop -> popResource(world, pos, drop) }
}
return super.playerWillDestroy(world, pos, state, player)
}
@@ -95,7 +95,7 @@ class BerryBlock(private val berryIdentifier: ResourceLocation, settings: Proper
// Random Tick handler: uses time compensation to ensure correct growth speed
@Deprecated("Deprecated in Java")
override fun randomTick(state: BlockState, world: ServerLevel, pos: BlockPos, random: RandomSource) {
val blockEntity = world.getBlockEntity(pos) as? BerryBlockEntity ?: return
val blockEntity = world.getBlockEntity(pos) as? BerryBlockEntity? ?: return
// processTick calculates elapsed time and updates timer, returns whether growth is needed
if (blockEntity.processTick(world, pos, state) && !world.getBlockTicks().hasScheduledTick(pos, this)) {
@@ -131,7 +131,10 @@ class BerryBlock(private val berryIdentifier: ResourceLocation, settings: Proper
val newAge = curAge + 1
if (newAge > FRUIT_AGE) return
val newState = state.setValue(AGE, newAge)
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
val treeEntity = world.getBlockEntity(pos) as? BerryBlockEntity? ?: run {
world.setBlock(pos, Blocks.AIR.defaultBlockState(), UPDATE_CLIENTS)
return
}
if (curAge == MATURE_AGE) {
treeEntity.generateGrowthPoints(world, newState, pos, null)
determineMutation(world, random, pos, newState)
@@ -149,7 +152,7 @@ class BerryBlock(private val berryIdentifier: ResourceLocation, settings: Proper
for (direction in this.lookupDirections) {
val redirectedPos = pos.offset(direction.normal)
val redirectedState = world.getBlockState(redirectedPos)
val berryBlock = redirectedState.block as? BerryBlock ?: continue
val berryBlock = redirectedState.block as? BerryBlock? ?: continue
val berry = berryBlock.berry() ?: continue
val mutation = this.berry()?.mutationWith(berry) ?: continue
mutations += mutation
@@ -163,7 +166,7 @@ class BerryBlock(private val berryIdentifier: ResourceLocation, settings: Proper
treeEntity.decrementMulchDuration(world, pos, state)
}
val mutation = if (random.nextInt(1000) < mutateChance) mutations.random() else null
(world.getBlockEntity(pos) as? BerryBlockEntity)?.let { blockEntity ->
(world.getBlockEntity(pos) as? BerryBlockEntity?)?.let { blockEntity ->
CobblemonEvents.BERRY_MUTATION_RESULT.post(BerryMutationResultEvent(berry, world, state, pos, berryMutationOffer.mutations, mutation)) { berryMutationResult ->
berryMutationResult.pickedMutation?.let { mutation -> blockEntity.mutate(mutation) }
}
@@ -181,7 +184,7 @@ class BerryBlock(private val berryIdentifier: ResourceLocation, settings: Proper
): Boolean {
val underBlockState = world.getBlockState(pos.below())
val validSoil = state.getValue(IS_ROOTED) || underBlockState.`is`(CobblemonBlockTags.BERRY_SOIL)
val treeEntity = world.getBlockEntity(pos) as? BerryBlockEntity ?: return false
val treeEntity = world.getBlockEntity(pos) as? BerryBlockEntity? ?: return false
return getMulch(treeEntity) == MulchVariant.NONE && state.getValue(AGE) < FLOWER_AGE && validSoil
}
@@ -356,7 +359,7 @@ class BerryBlock(private val berryIdentifier: ResourceLocation, settings: Proper
fun getMulch(entity: BerryBlockEntity) = entity.mulchVariant
fun convertMulchToEntity(world: ServerLevel, state: BlockState, pos: BlockPos) {
val entity = world.getBlockEntity(pos) as? BerryBlockEntity ?: return
val entity = world.getBlockEntity(pos) as? BerryBlockEntity? ?: return
if (state.getValue(MULCH) != MulchVariant.NONE && state.getValue(MULCH) != entity.mulchVariant) {
entity.mulchVariant = state.getValue(MULCH)
world.setBlockAndUpdate(pos, state.setValue(MULCH, MulchVariant.NONE))
@@ -373,9 +376,9 @@ class BerryBlock(private val berryIdentifier: ResourceLocation, settings: Proper
}
private fun harvestBerry(world: Level, state: BlockState, pos: BlockPos, player: Player? = null): Boolean {
val blockEntity = world.getBlockEntity(pos) as? BerryBlockEntity ?: return false
val blockEntity = world.getBlockEntity(pos) as? BerryBlockEntity? ?: return false
blockEntity.harvest(world, state, pos, player).forEach { drop ->
Block.popResource(world, pos, drop)
popResource(world, pos, drop)
}
val sound = if (player != null) CobblemonSounds.BERRY_HARVEST else SoundEvents.SHEEP_SHEAR
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.block
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonBlocks
import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
import com.cobblemon.mod.common.block.TypeGemClusterBlock.Companion.SHOULD_GROW
@@ -34,6 +35,9 @@ class TypeGemCoreBlock(properties: Properties) : Block(properties) {
const val MIN_DISTANCE_BETWEEN_GEMS = 1
const val CONTINUATION_CHANCE = 0.8f
// Upper safety check to prevent infinite loops
const val MAX_FORCE_GROW_ATTEMPTS = 64
val BLOCK_TO_CLUSTER: Map<ResourceLocation, Block> by lazy {
mapOf(
ResourceLocation.parse("cobblemon:normal_gem_block") to CobblemonBlocks.TYPE_GEM_CLUSTER_NORMAL,
@@ -113,13 +117,16 @@ class TypeGemCoreBlock(properties: Properties) : Block(properties) {
fun forceGrow(level: WorldGenLevel, pos: BlockPos, random: RandomSource, percentage: Float) {
val desiredLength = MAX_CONNECTED_GEMS * percentage
while (true) {
val growInfo = grow(level, pos, random, true)
val grown = growInfo.first
val clusterSize = growInfo.second
if (!grown || clusterSize >= desiredLength) break
repeat(MAX_FORCE_GROW_ATTEMPTS) {
val (grown, clusterSize) = grow(level, pos, random, true)
if (!grown || clusterSize >= desiredLength) return
}
Cobblemon.LOGGER.warn(
"Forced type gem growth at {} stopped after {} attempts without reaching its target size; keeping the partial formation.",
pos,
MAX_FORCE_GROW_ATTEMPTS
)
}
private fun grow(
@@ -131,7 +138,7 @@ class TypeGemCoreBlock(properties: Properties) : Block(properties) {
val connectedGems = getConnectedGemBlocks(level, pos) // maybe we can store this and access it for better performance? check for changes in list rather than checking each time grow is called? idk
val gemCount = connectedGems.size
val overLimit = gemCount >= MAX_CONNECTED_GEMS
if (overLimit) {
// if there is no open air pockets to grow any new clusters then exit early
if (!hasBreathingRoom(level, connectedGems)) {
@@ -166,6 +173,11 @@ class TypeGemCoreBlock(properties: Properties) : Block(properties) {
level.setBlock(targetPos, placeState, UPDATE_ALL)
// During world gen, the region silently discards writes into neighboring chunks that are already fully generated.
// Through ImposterProtoChunk with writes set to disabled. Checks if the block is still air after the write and skips if so as it can't be filled.
// Preventing infinite loops.
if (!level.getBlockState(targetPos).`is`(clusterBlock)) continue
if (forced) {
forceAdvanceClusterGrowth(level, targetPos, random)
}
@@ -211,4 +223,4 @@ class TypeGemCoreBlock(properties: Properties) : Block(properties) {
}
override fun isRandomlyTicking(state: BlockState): Boolean = true
}
}
@@ -16,24 +16,20 @@ import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.core.HolderLookup
import net.minecraft.core.NonNullList
import net.minecraft.core.component.DataComponents
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket
import net.minecraft.sounds.SoundEvent
import net.minecraft.sounds.SoundSource
import net.minecraft.world.ContainerHelper
import net.minecraft.world.Containers
import net.minecraft.world.InteractionResult
import net.minecraft.world.ItemInteractionResult
import net.minecraft.world.WorldlyContainer
import net.minecraft.world.*
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.phys.BlockHitResult
import net.minecraft.world.level.gameevent.GameEvent
import java.util.OptionalInt
import net.minecraft.world.phys.BlockHitResult
import java.util.*
import kotlin.math.pow
class DiscShelfBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(CobblemonBlockEntities.DISC_SHELF, pos, state), WorldlyContainer {
@@ -134,25 +130,7 @@ class DiscShelfBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobbl
return stack.item is TechnicalMachineItem
|| stack.item == CobblemonItems.UPGRADE
|| stack.item == CobblemonItems.DUBIOUS_DISC
|| stack.item == Items.MUSIC_DISC_13
|| stack.item == Items.MUSIC_DISC_CAT
|| stack.item == Items.MUSIC_DISC_BLOCKS
|| stack.item == Items.MUSIC_DISC_CHIRP
|| stack.item == Items.MUSIC_DISC_FAR
|| stack.item == Items.MUSIC_DISC_MALL
|| stack.item == Items.MUSIC_DISC_MELLOHI
|| stack.item == Items.MUSIC_DISC_STAL
|| stack.item == Items.MUSIC_DISC_STRAD
|| stack.item == Items.MUSIC_DISC_WARD
|| stack.item == Items.MUSIC_DISC_11
|| stack.item == Items.MUSIC_DISC_WAIT
|| stack.item == Items.MUSIC_DISC_OTHERSIDE
|| stack.item == Items.MUSIC_DISC_5
|| stack.item == Items.MUSIC_DISC_RELIC
|| stack.item == Items.MUSIC_DISC_PRECIPICE
|| stack.item == Items.MUSIC_DISC_CREATOR
|| stack.item == Items.MUSIC_DISC_CREATOR_MUSIC_BOX
|| stack.item == Items.MUSIC_DISC_PIGSTEP
|| stack.has(DataComponents.JUKEBOX_PLAYABLE)
}
fun onNoteBlockPulse(level: Level, noteBlockPos: BlockPos, soundEvent: SoundEvent) {
@@ -14,7 +14,6 @@ import com.cobblemon.mod.common.api.multiblock.MultiblockEntity
import com.cobblemon.mod.common.api.multiblock.MultiblockStructure
import com.cobblemon.mod.common.api.multiblock.builder.MultiblockStructureBuilder
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.tms.TechnicalMachines
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.block.MonitorBlock
import com.cobblemon.mod.common.block.multiblock.FossilMultiblockStructure
@@ -307,8 +306,7 @@ open class FossilMultiblockEntity(
private fun unlockTmForPlayer(player: ServerPlayer, stack: ItemStack) {
if (stack.item !is TechnicalMachineItem) return
val move = TMMoveComponent.getTMMove(stack) ?: return
val tmId = TechnicalMachines.moveToTM[move]?.id ?: return
player.tmList()?.learn(listOf(tmId))
player.tmList().syncTMFromMove(move)
}
private fun updateMonitorScreen() {
@@ -18,10 +18,7 @@ import com.cobblemon.mod.common.api.fishing.SpawnBait
import com.cobblemon.mod.common.api.fishing.SpawnBait.Effect
import com.cobblemon.mod.common.api.fishing.SpawnBaitEffects
import com.cobblemon.mod.common.api.spawning.SpawnCause
import com.cobblemon.mod.common.api.spawning.detail.EntitySpawnResult
import com.cobblemon.mod.common.api.spawning.detail.PokemonHerdSpawnDetail
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.api.spawning.detail.*
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
import com.cobblemon.mod.common.api.spawning.spawner.FixedAreaSpawner
@@ -33,7 +30,6 @@ import com.cobblemon.mod.common.item.components.FoodColourComponent
import com.cobblemon.mod.common.item.components.IngredientComponent
import com.cobblemon.mod.common.net.messages.client.effect.PokeSnackBlockParticlesPacket
import com.cobblemon.mod.common.util.DataKeys
import java.util.UUID
import net.minecraft.core.BlockPos
import net.minecraft.core.HolderLookup
import net.minecraft.nbt.CompoundTag
@@ -47,6 +43,7 @@ import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
import java.util.*
open class PokeSnackBlockEntity(pos: BlockPos, state: BlockState) :
BlockEntity(CobblemonBlockEntities.POKE_SNACK, pos, state), TintBlockEntity,
@@ -59,9 +56,29 @@ open class PokeSnackBlockEntity(pos: BlockPos, state: BlockState) :
const val POKE_SNACK_CRUMBED_ASPECT = "poke_snack_crumbed"
}
class Spawner(
name: String,
spawnPool: SpawnPool,
buckets: Map<String, Float>,
world: ServerLevel,
position: BlockPos,
horizontalRadius: Int,
verticalRadius: Int,
maxPokemonPerChunk: Float
) : FixedAreaSpawner(
name = name,
spawnPool = spawnPool,
buckets = buckets,
world = world,
position = position,
horizontalRadius = horizontalRadius,
verticalRadius = verticalRadius,
maxPokemonPerChunk = maxPokemonPerChunk
)
override var tint: Int? = null
val spawner: FixedAreaSpawner by lazy {
val spawner: Spawner by lazy {
val server = level as ServerLevel
val baitEffects = getBaitEffects()
@@ -25,6 +25,7 @@ import net.minecraft.world.level.block.SoundType
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.entity.BlockEntityTicker
import net.minecraft.world.level.block.entity.BlockEntityType
import net.minecraft.world.level.block.entity.CommandBlockEntity
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BooleanProperty
@@ -80,11 +81,13 @@ class HabitatBlock(properties: Properties) : Block(properties), EntityBlock {
}
override fun useWithoutItem(state: BlockState, level: Level, pos: BlockPos, player: Player, hitResult: BlockHitResult): InteractionResult {
if (!level.isClientSide) {
val be = level.getBlockEntity(pos) as? HabitatBlockEntity ?: return InteractionResult.PASS
return be.onUse(player, hitResult)
val blockEntity = level.getBlockEntity(pos)
val be = level.getBlockEntity(pos) as? HabitatBlockEntity ?: return InteractionResult.PASS
return if (blockEntity is HabitatBlockEntity && player.canUseGameMasterBlocks()) {
be.onUse(player, hitResult)
} else {
InteractionResult.PASS
}
return InteractionResult.SUCCESS
}
override fun getDrops(state: BlockState, params: LootParams.Builder): MutableList<ItemStack> {
@@ -60,10 +60,12 @@ class HabitatBlockEntity(pos: BlockPos, state: BlockState) :
var spawningStyle: HabitatSpawningStyle = ActivatedHabitatSpawning(this)
var numberOfPhases: Int = 1
var phaseOrder = HabitatPhaseOrder.SIMPLE
var currentPhase = 1
val numberOfPhases: Int
get() = pool.getPhaseCount()
var levelRange: IntRange = 1..Cobblemon.config.maxPokemonLevel
var modifiers: PokemonProperties = PokemonProperties()
var pool: HabitatPool = HabitatPool.default()
@@ -139,7 +141,6 @@ class HabitatBlockEntity(pos: BlockPos, state: BlockState) :
val activatedSettings = habitatSettingsDTO.activatedSettings
val naturalSettings = habitatSettingsDTO.naturalSettings
val isActivated = habitatSettingsDTO.isActivatedSpawning
this.numberOfPhases = habitatSettingsDTO.numberOfPhases
this.phaseOrder = habitatSettingsDTO.phaseOrder
this.levelRange = habitatSettingsDTO.levelRange
this.modifiers = PokemonProperties.parse(habitatSettingsDTO.modifiers)
@@ -169,7 +170,6 @@ class HabitatBlockEntity(pos: BlockPos, state: BlockState) :
override fun saveAdditional(tag: CompoundTag, registryLookup: HolderLookup.Provider) {
super.saveAdditional(tag, registryLookup)
tag.putString(DataKeys.HABITAT_MIMIC, mimicId.toString())
tag.putInt(DataKeys.HABITAT_PHASE_COUNT, numberOfPhases)
tag.putString(DataKeys.HABITAT_PHASE_ORDER, phaseOrder.toString())
// Only save the level range when it's a non-trivial one
if (levelRange.first != 1 || levelRange.last != Cobblemon.config.maxPokemonLevel) {
@@ -201,7 +201,6 @@ class HabitatBlockEntity(pos: BlockPos, state: BlockState) :
return // Client doesn't need to try loading the rest
}
numberOfPhases = tag.getInt(DataKeys.HABITAT_PHASE_COUNT)
phaseOrder = HabitatPhaseOrder.valueOf(tag.getString(DataKeys.HABITAT_PHASE_ORDER))
levelRange = tag.getString(DataKeys.HABITAT_LEVEL_RANGE).takeIf { it.isNotBlank() }?.let(IntRangeAdapter::deserialize) ?: 1..Cobblemon.config.maxPokemonLevel
modifiers = tag.getString(DataKeys.HABITAT_MODIFIERS).takeIf { it.isNotBlank() }?.let(PokemonProperties::parse) ?: PokemonProperties()
@@ -279,7 +278,7 @@ class HabitatBlockEntity(pos: BlockPos, state: BlockState) :
override fun injectSpawns(bucket: String, spawnablePosition: SpawnablePosition): List<SpawnDetail>? {
val spawningStyle = spawningStyle
return if (spawningStyle is NaturalHabitatSpawning) {
spawnDetails.filter { it.spawnablePositionType.clazz == spawnablePosition::class.java && it.isSatisfiedBy(spawnablePosition) }
spawnDetails.filter { it.bucket == bucket && it.spawnablePositionType.clazz == spawnablePosition::class.java && it.isSatisfiedBy(spawnablePosition) }
} else {
null
}

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