Compare commits

..
Author SHA1 Message Date
Nadja Reitzenstein 78dfb30759 Add a hacky riding debug system
Enable it with `/debugutils cobblemon:riding true`
2025-10-02 19:02:27 +02:00
Landon db10ae2183 Merge branch 'riding-behaviour-network-fixes' into 'main'
Make RidingBehaviourSettings::encode/decode symmetric

See merge request cable-mc/cobblemon!1888
2025-10-02 15:39:53 +00:00
Nadja Reitzenstein 548b8522a8 Make RidingBehaviourSettings::encode/decode symmetric
Having the encode/decode functionality symmetric is the expected behaviour and not doing so will introduce a lot of accidental bugs when people assume them to be. Case in point: `RideSettingsSyncPacket.kt` (which this commit notably *does not touch*) expected them to be and was thus entirely broken.
2025-10-02 16:02:24 +02:00
Polymeta 4791b146dc Merge branch 'hotfix-BufferUtils-NPE' into 'main'
Fix a logic error in write-/readNullable resulting in NPE on server joins.

See merge request cable-mc/cobblemon!1886
2025-10-02 13:43:18 +00:00
Nadja Reitzenstein b7a9931393 Fix a logic error in write-/readNullable resulting in NPE on server joins.
This error occurs because `writeNullable` will write `true` if the encoded value is `null` while `readNullable` will treat a `true` value as a value being present. Since it makes more sense to have a true value as a go-ahead this commit changes the behaviour of `writeNullable` to write `true` if a value is present and `false` if a value is `null`
2025-10-02 15:20:33 +02:00
Polymeta 9a35a11440 Merge branch 'the-case-of-disappearing-cubes' into 'main'
The Case of Disappearing Cubes

See merge request cable-mc/cobblemon!1883
2025-10-02 10:46:15 +00:00
Polymeta 6d4a50cee2 Merge branch 'cooking-pot-hopper-bugfix' into 'main'
Fixed bug with seasonings and hoppers in the campfire cooking pot

See merge request cable-mc/cobblemon!1885
2025-10-02 08:16:43 +00:00
PieDuck 818a637897 Merge branch cobblemon:main into cooking-pot-hopper-bugfix 2025-10-01 22:34:45 +01:00
Polymeta 4c96ab9b28 Merge branch 'Carm-Signfix' into 'main'
Added missing Saccharine sign loot table.

See merge request cable-mc/cobblemon!1884
2025-10-01 17:12:08 +00:00
PieDuck d5a316b9ae Fixed bug with seasonings and hoppers in the campfire cooking pot 2025-10-01 17:29:29 +01:00
Carmendar 87e32f0e9b Added missing Saccharine sign loot table. 2025-09-30 23:59:15 -05:00
Gertimoshka 53372e5400 Fixed a very specific case of how a model was made causing dissapearing cubes ingame 2025-09-29 20:15:14 +03:00
Polymeta 2fbb1f354c Merge branch '#1568-npcdelete' into 'main'
changes to /npcdelete uuid prameter and console support

Closes #1568

See merge request cable-mc/cobblemon!1851
2025-09-29 13:13:46 +00:00
Cyrus bf06476558 changes to /npcdelete uuid prameter and console support 2025-09-29 13:13:46 +00:00
Paul 2e3a742477 Merge remote-tracking branch 'origin/lang-updates' 2025-09-29 19:45:46 +10:00
Watuneni iteite 94c7542d2c Translated using Weblate (Japanese)
Currently translated at 86.7% (7693 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-09-29 09:27:58 +00:00
Polymeta fa2b6b5996 Merge branch 'regional-food-use-fix' into 'main'
Small fixes

Closes #1708, #1689, #1696, #1710, and #1711

See merge request cable-mc/cobblemon!1877
2025-09-29 08:13:44 +00:00
Watuneni iteite 8e77dc70f6 Translated using Weblate (Japanese)
Currently translated at 86.7% (7692 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-09-29 07:04:26 +00:00
Hiroku 51e3748adb Merge branch 'NoMoreAwkwardSurfaceSwimmingPlz' into 'main'
Fix Awkward water swimming

See merge request cable-mc/cobblemon!1882
2025-09-29 05:50:14 +00:00
Hiroku d8cf2701c7 Merge branch 'sterr/sigh-need-a-new-branch-for-this' into 'main'
Remove an unused sound from sounds.json

See merge request cable-mc/cobblemon!1881
2025-09-29 05:07:36 +00:00
JazzMcNade 31b7c83369 Merge branch 'main' into NoMoreAwkwardSurfaceSwimmingPlz 2025-09-28 22:50:07 -05:00
JazzMcNade a170123e2c Enforce minimum depth requirement for swim wander, fix mistake in omnipathnav 2025-09-28 22:43:15 -05:00
Hiroku c3c17a228e Merge branch 'riding-work-hiro' into 'main'
Riding setting and stamina changes

See merge request cable-mc/cobblemon!1880
2025-09-29 02:13:24 +00:00
Paul 41d607ad56 Remember the last camera type used for a mount type and switch to it if the config is enabled 2025-09-29 11:05:41 +10:00
Hiroku 58b3596948 Wait that wasn't meant to still be in there 2025-09-28 11:12:33 +00:00
Paul ed4456d756 API and config and controller options for infinite stamina 2025-09-28 15:47:17 +10:00
Test 481734e6b1 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon 2025-09-27 20:44:47 -04:00
Test 636792fa1f rm unused function 2025-09-27 20:44:40 -04:00
Test ad65002b19 fix stat color 2025-09-27 20:43:44 -04:00
Sterrezo 903f3b6d5a Removed an unused sound from sounds.json 2025-09-27 12:15:22 +02:00
Paul 4456743e97 Merge remote-tracking branch 'origin/main' into riding-work-hiro
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonNetwork.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/data/CobblemonDataProvider.kt
2025-09-27 18:41:27 +10:00
Paul d0989e5ece Migrated all the other controllers to support the global config 2025-09-27 18:40:57 +10:00
Hiroku ce3f703de2 Merge branch 'matrix-reloaded' into 'main'
Allow reload of some registries

See merge request cable-mc/cobblemon!1878
2025-09-27 08:31:10 +00:00
Paul 180229db61 Merge remote-tracking branch 'origin/main' into riding-work-hiro 2025-09-27 16:18:31 +10:00
Paul b6e10c016e Migrate global ride controller settings to use a different solution 2025-09-27 16:06:13 +10:00
Hiroku 8ed68742df Merge branch 'exp-actionbar-fix' into 'main'
Fixes EXP gained actionbar not using the AddExperienceResult.

See merge request cable-mc/cobblemon!1875
2025-09-27 05:53:33 +00:00
Paul ce09236dab Added global controller settings for riding, moved jet over to it 2025-09-27 12:39:17 +10:00
Guilherme Pereira 0bb968dd73 add pokémon fullness to pokémon properties 2025-09-26 11:42:58 -03:00
Guilherme Pereira f3d4cb34e0 fix poke puff tooltip and mild multiplier #1711 #1710 2025-09-26 10:41:17 -03:00
Landon 16aa930735 Merge branch 'boat-tweaks' into 'main'
Boat tweaks

See merge request cable-mc/cobblemon!1879
2025-09-26 03:41:03 +00:00
Landon Wills 55733e8ad5 Add offset setting for determining surface level 2025-09-25 22:52:18 -04:00
Landon Wills 67591efdad Change sprint modifier expression, adjust acceleration formula 2025-09-25 22:26:15 -04:00
Paul 5d300c8157 Change how reloadability is checked for /reload, maybe this will fix server stuff 2025-09-26 11:46:25 +10:00
Paul 3b6e1bd429 Persist stamina between dismount and mount 2025-09-26 11:24:44 +10:00
Test c77198a845 rm unused imports 2025-09-25 20:00:02 -04:00
Test bd32c07cc1 fix ride stat percentage 2025-09-25 19:59:23 -04:00
Guilherme Pereira 1eb7354fc0 update CHANGELOG.md 2025-09-25 16:49:59 -03:00
Guilherme Pereira 52ad6a3250 fix mochi tooltips #1689 2025-09-25 16:24:15 -03:00
Guilherme Pereira aa57bbbf34 fix glint on enchanted pokémon item models #1696 2025-09-25 16:20:17 -03:00
Guilherme Pereira 26278eeb46 fix regional food items not usable on pokémon #1708 2025-09-25 14:55:41 -03:00
Hiroku 99cfc4f147 Merge branch 'stat' into 'main'
🏇🏻

Closes #1687 and #1684

See merge request cable-mc/cobblemon!1870
2025-09-25 12:38:18 +00:00
Paul 89281a1f6d Experimental reload support for various datapack folders 2025-09-25 21:31:42 +10:00
Paul ad044fb05c Aprijuice back to flat boost mechanics, UI corrections 2025-09-25 20:38:00 +10:00
Polymeta 7088ce3828 Merge branch 'fix-scanner-sensitivity' into 'main'
Fix the scanner sensitivity I broke a few hours ago.

See merge request cable-mc/cobblemon!1876
2025-09-25 06:08:39 +00:00
JPAKx4 fc6a712906 Fix the scanner sensitivity I broke a few hours ago. 2025-09-24 21:51:41 -05:00
Neovitalism 1d0397c399 Update Pokemon.kt 2025-09-24 21:13:14 -05:00
Polymeta 8ad443a4c5 Merge branch 'riding-behaviour-tweaks-four' into 'main'
Quick riding tweaks

See merge request cable-mc/cobblemon!1873
2025-09-24 21:31:07 +00:00
Jackson Bowes 0d1f9a0093 Quick riding tweaks 2025-09-24 21:31:07 +00:00
Polymeta 7587344cb4 Merge branch 'NavigationAndWanderTargetImprovements' into 'main'
NavigationAndWanderTargetImprovements

Closes #1693

See merge request cable-mc/cobblemon!1872
2025-09-24 21:24:48 +00:00
Polymeta f0926d7524 Merge branch 'scan-ride-crash' into 'main'
Fix crash when you zoom in using a pokedex and begin rolling

See merge request cable-mc/cobblemon!1874
2025-09-24 21:16:53 +00:00
JPAKx4 7c3b17d841 Fix crash when you zoom in using a pokedex and begin rolling 2025-09-24 14:27:11 -05:00
Polymeta 263e3cd79f Merge branch 'caring-farmer' into 'main'
Tag Support for Interactions

See merge request cable-mc/cobblemon!1871
2025-09-23 06:37:15 +00:00
JazzMcNade 2ba8062915 clean up 2025-09-23 01:36:14 -05:00
JazzMcNade edc298bf64 Fixed water walkers targeting lava surfaces and vice versa 2025-09-23 01:32:57 -05:00
JazzMcNade aa1158fcfe Fixed null exception , fixed water walkers being unable to rise up in water. 2025-09-23 01:01:48 -05:00
JazzMcNade ab40ae81c4 Merge branch 'main' into NavigationAndWanderTargetImprovements 2025-09-22 20:37:21 -05:00
dhi_holo b5df1878ed Update furfrou.json
Now can use shears from anything
2025-09-22 21:30:43 +10:00
dhi_holo 800965cb40 Tag!
Added support for the Brushes and Fertilizer item tags in the Interactions (brushes interaction tested with More Brushes and a small datapack to confirm correct behaviours)
2025-09-22 21:20:43 +10:00
Polymeta b00f1d9352 fix typo in pokemon interactions
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-09-22 10:53:31 +01:00
Polymeta a588cfadf8 Merge branch 'riding-scrape' into 'main'
Riding scrape

See merge request cable-mc/cobblemon!1869
2025-09-22 09:29:38 +00:00
Polymeta 2eb4062f67 [skip-ci] re-add client config for consistency
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-09-22 10:23:14 +01:00
Polymeta 43321d8872 licenses
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-09-22 10:07:19 +01:00
Hiroku a5ea584b75 Merge branch 'held-item-datapack' into 'main'
Implemented user-driven held item effects

See merge request cable-mc/cobblemon!1811
2025-09-22 08:10:13 +00:00
Crater 6a9edc2e67 Implemented user-driven held item effects 2025-09-22 08:10:13 +00:00
Test 12377deff7 Merge branch 'dynlight-refactor' into 'main'
Number pushing and Dynlight betterments

See merge request cable-mc/cobblemon!1864
2025-09-22 07:47:42 +00:00
Test 583377d39b Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into stat 2025-09-22 01:31:33 -04:00
Test 2c1da8df06 license 2025-09-22 01:31:21 -04:00
Paul 737ddd973b Tiny cleanup stuff dw about it [skip-ci] 2025-09-22 15:16:25 +10:00
Test d749b04261 fix cyclizar mount idle 2025-09-22 05:14:50 +00:00
Test 93deb3a669 fix stat bar 2025-09-22 00:33:30 -04:00
Test dca45a95e9 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into stat
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/riding/behaviour/RidingBehaviourSettings.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/riding/behaviour/types/air/GliderBehaviour.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonEntity.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/item/group/CobblemonItemGroups.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/Pokemon.kt
#	common/src/main/resources/data/cobblemon/recipe/campfire_pot/black_aprijuice.json
#	common/src/main/resources/data/cobblemon/recipe/campfire_pot/blue_aprijuice.json
#	common/src/main/resources/data/cobblemon/recipe/campfire_pot/green_aprijuice.json
#	common/src/main/resources/data/cobblemon/recipe/campfire_pot/pink_aprijuice.json
#	common/src/main/resources/data/cobblemon/recipe/campfire_pot/red_aprijuice.json
#	common/src/main/resources/data/cobblemon/recipe/campfire_pot/white_aprijuice.json
#	common/src/main/resources/data/cobblemon/recipe/campfire_pot/yellow_aprijuice.json
2025-09-21 21:42:19 -04:00
Test 0412f16460 ride stat 2025-09-21 20:20:45 -04:00
Nico Merenq efd4a72287 Translated using Weblate (Russian)
Currently translated at 63.1% (5598 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-09-21 23:09:39 +00:00
Landon Wills a62e5a6571 Revert Golurk changes 2025-09-21 18:54:27 -04:00
Landon Wills 0ec337ee6c Scrape riding updates 2025-09-21 18:20:32 -04:00
Carmendar 76df0a429d changelog 2025-09-21 16:58:50 -05:00
Carmendar 08ea9378de Dewgong ready to scrape. 2025-09-21 16:57:07 -05:00
Carmendar c2bd21ab96 Merge branch 'main' into implement_pokemon 2025-09-21 11:42:32 -05:00
Paul 5db8ccfad4 Fixed various things about aprijuice ride boosts 2025-09-21 22:39:55 +10:00
Polymeta aa206d386a Merge branch 'stop-the-disco-spin' into 'main'
Fixed Pokémon spinning a bunch when re-calculating a path to a moving target, closes #1703

Closes #1703

See merge request cable-mc/cobblemon!1868
2025-09-21 10:11:11 +00:00
Watuneni iteite bba8a12a2a Translated using Weblate (Japanese)
Currently translated at 85.7% (7608 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-09-21 10:00:32 +00:00
Paul a235af6c01 Fixed fliers taking fall damage upon landing without a passenger, fixed rarely flying pokemon joining a herd and launching into space 2025-09-21 19:24:23 +10:00
Carmendar a666a74a32 Slaking jump seat fix. 2025-09-21 03:22:51 -05:00
Carmendar c285120ff4 Merge branch 'main' into implement_pokemon 2025-09-21 03:17:33 -05:00
Paul dbd2890120 oop 2025-09-21 18:09:17 +10:00
Carmendar a5db46cdc5 Rhyhorn line ready for scrape & ride overlays for Slaking. 2025-09-21 01:17:58 -05:00
Carmendar 765cf9b96a Send in the bulls. (Tauros is ready to scrape.) 2025-09-21 01:12:38 -05:00
Paul efe7952869 Fixed Pokémon spinning a bunch when re-calculating a path to a moving target, closes #1703 2025-09-21 14:29:30 +10:00
Carmendar 9e434de285 Implemented spicy chicken legs animations. 2025-09-20 22:11:22 -05:00
Carmendar 712fe2c250 Merge branch 'main' into implement_pokemon 2025-09-20 22:09:16 -05:00
Hiroku 5db5a2ed35 Merge branch 'root-bone-animations-in-riding' into 'main'
Root bone animations in riding

See merge request cable-mc/cobblemon!1866
2025-09-21 02:15:02 +00:00
Paul 49c96e6fc8 Merge remote-tracking branch 'origin/main' 2025-09-21 10:46:21 +10:00
Paul d56c9a3f5a put da aprijuice recipes back 2025-09-21 10:42:53 +10:00
JazzMcNade 6c4c973ed8 Fixed large mobs unable to climb blocks. Enabled wandering for seafloor, water surface, and lava surface walkers. 2025-09-20 18:10:55 -05:00
Carmendar a117303147 Merge branch 'main' into implement_pokemon 2025-09-20 17:50:10 -05:00
Landon dbf248a508 Merge branch 'riding-behaviour-tweaks-03' into 'main'
ride tweaks extended cut

See merge request cable-mc/cobblemon!1865
2025-09-20 17:40:28 +00:00
Jackson Bowes 56d75b14fa ride tweaks extended cut 2025-09-20 17:40:27 +00:00
Paul 87311e2d7d Added default ride pose and use that instead of letting minecraft animate them 2025-09-20 22:03:44 +10:00
Hiroku 1b525ca3a5 Merge branch 'the-curse-of-gradle-island' into 'main'
Stop using Architectury Runtime Transfomers implicitly

See merge request cable-mc/cobblemon!1867
2025-09-20 10:27:52 +00:00
Nadja Reitzenstein 78ec9d3723 Stop using Architectury Runtime Transfomers implicitly
We don't use the Architectury Runtime Transformers in any of our direct code, so there is no downside for setting compile_only and entirely dropping them.
This necessitates the other implemented changes to our build system though as the transformers also inject code and resources from the :common project into the platform-specific ones in dev-env runs. Production jars use compile-time transformed and remapped jar shadowing for the same purpose so they are wholly unaffected by this removal.
Removing ART has several upsides: For one their dev-env injections have proven very unreliable so with this change a lot of caching issues have been fundamentally removed. They also inject deep into different parts of the JVM, making hot code reloads almost impossible to function so with this change reloads should be able to apply more often.
2025-09-20 11:42:14 +02:00
Paul afa5d22c8e Re-enabled aprijuice and made creative menu aprijuice 10 in all flavours because w/e 2025-09-20 18:50:02 +10:00
阿鱼 1cdf664dce Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 99.5% (8833 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-09-20 07:30:00 +00:00
Paul bd63fb686d Support marking some seats as making the hands busy 2025-09-20 17:00:35 +10:00
Carmendar 0f9a422d44 no more garchomp quirks while mount flying. 2025-09-20 00:52:41 -05:00
Paul 32d18b47f9 Positional scaling fix for root-based ride player animation 2025-09-20 15:46:11 +10:00
Paul c618bf95fa Merge remote-tracking branch 'origin/main' into root-bone-animations-in-riding
# Conflicts:
#	common/src/main/java/com/cobblemon/mod/common/mixin/client/HumanoidModelMixin.java
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/player/MountedPlayerRenderer.kt
#	common/src/main/resources/mixins.cobblemon-common.json
2025-09-20 14:46:37 +10:00
Paul 18a8aa12d1 Release the held pokemon before sorting, closes #1663 2025-09-20 14:16:21 +10:00
Paul 308ca6369d Fixed persistence issues for pokemon item pickup behaviours 2025-09-20 12:44:17 +10:00
Paul 79fd9f4e04 Fixed pokemon having the NPC dialogues memory 2025-09-20 12:28:06 +10:00
Paul 38219bbca2 Fixed pokemon in the party following and looking around while asleep, closes #1668 2025-09-20 12:27:48 +10:00
Paul 87de288777 Fixed crash when adding stationary Behaviour without a movement providing behaviour. Closes #1678 2025-09-20 11:25:04 +10:00
Paul b6f1d6c62e Fixed terralith:shrubland not counting as plains for pokemon spawning, closes #1685 2025-09-20 10:44:36 +10:00
Nadja Reitzenstein a7bd70e334 Make only held Pokedexes cast light 2025-09-19 13:10:12 +02:00
Polymeta 43310d3a3b tweak default stat ranges for some flying behaviours
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-09-19 10:29:34 +01:00
GenotypeXD-Dev d12bdc3745 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-09-18 21:53:03 -05:00
GenotypeXD-Dev 08c8623951 Flygon Size Fix 2025-09-18 21:38:51 -05:00
Paul 6ae375cf4e Added math.sign and math.copy_sigh molang dep update 2025-09-19 10:34:19 +10:00
Polymeta 29c65c18ff Merge branch 'what-a-grain' into 'main'
Grain Improvements

Closes #1702

See merge request cable-mc/cobblemon!1863
2025-09-18 17:10:08 +00:00
dhi_holo b90384fe08 Grain Improvements 2025-09-18 17:10:08 +00:00
Nadja Reitzenstein d2ff7e3748 Make Charmander glow 2025-09-18 17:48:30 +02:00
Nadja Reitzenstein ea6452287b Refactor dynamic light support to be future-proof 2025-09-18 17:48:28 +02:00
Nadja Reitzenstein b1f7731563 Update dependencies all around 2025-09-18 17:47:39 +02:00
Test 5ad881235f update fabric-permissions-api to 0.3.1 2025-09-18 04:53:02 +00:00
Landon 05796998e8 Merge branch 'riding-behaviour-tweaks-02' into 'main'
Behaviour tweaks

See merge request cable-mc/cobblemon!1862
2025-09-17 18:36:53 +00:00
Jackson Bowes aaf1aad9a4 Behaviour tweaks 2025-09-17 18:36:53 +00:00
Polymeta b315fdcadc Merge branch 'sand-witches' into 'main'
Small Data Changes

See merge request cable-mc/cobblemon!1861
2025-09-17 18:23:42 +00:00
dhi_holo 5b95f0a1c7 Update mochis.json
Added missing and reorganised
2025-09-18 00:01:56 +10:00
dhi_holo 7641a839c2 Merge branch 'main' into sand-witches 2025-09-17 23:33:08 +10:00
dhi_holo 6ed6ca6937 IV Drip
Added new candy tags for IV Candies (additive, subtractive, general, and general candy that includes Exp Candies)
Added Vivichoke, Cobblemon Apples, and Beetroots to Sandwich Vegetable tag
Added advancement for using IV candy
2025-09-17 23:28:18 +10:00
Landon be106a465e Merge branch 'riding-debug-ui-tweaks-02' into 'main'
Ride settings editor

See merge request cable-mc/cobblemon!1858
2025-09-17 12:53:24 +00:00
Jackson Bowes e7c0374c83 Ride settings editor 2025-09-17 12:53:24 +00:00
Polymeta 7fd1a443ac Merge branch 'riding-fix-air-dismount' into 'main'
Fix in air dismount

See merge request cable-mc/cobblemon!1831
2025-09-17 06:22:21 +00:00
Jackson Bowes 31359077ed Fix in air dismount 2025-09-17 06:22:20 +00:00
Polymeta 7f683bd618 Merge branch 'riding-fix-waterwalk' into 'main'
Fix riding waterwalk

See merge request cable-mc/cobblemon!1838
2025-09-17 06:21:36 +00:00
Jackson Bowes 012f15878e Fix riding waterwalk 2025-09-17 06:21:36 +00:00
Watuneni iteite 2ea5eb2823 Translated using Weblate (Japanese)
Currently translated at 85.7% (7602 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-09-16 18:48:56 +00:00
Polymeta f406b9695d Merge branch 'main' into 'main'
Skip battle check if the mobTargetInBattle game rule is set to false

See merge request cable-mc/cobblemon!1857
2025-09-16 08:17:01 +00:00
Polymeta f6dbe40246 Merge branch 'implement_pokemon' into 'main'
lots o' implementing, animating and fixing.

See merge request cable-mc/cobblemon!1846
2025-09-16 08:15:52 +00:00
Polymeta a1f6e47f42 Merge branch 'fix-cooking-pot-button-color' into 'main'
Fix Campfire Pot Lid Button Sprite

See merge request cable-mc/cobblemon!1859
2025-09-16 08:15:13 +00:00
Polymeta 04f5dbfca0 swap out textures
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-09-16 09:11:38 +01:00
martin fd940b5549 Remove unnecessary check on resource 2025-09-15 16:12:36 +08:00
Hiroku 3e37c1af32 Merge branch 'animation-scale-shenanigan' into 'main'
Removed Animators/Engineer Frame Ghost Scale Jumpscare

See merge request cable-mc/cobblemon!1855
2025-09-15 01:15:16 +00:00
Paul 8b6b9ffede Changelog [skip-ci] 2025-09-15 10:07:03 +10:00
Hiroku 329a13867c Merge branch 'implemented-patch' into 'main'
Fixes PokemonSpecies.implemented not containing pokémon marked implemented via species additions.

See merge request cable-mc/cobblemon!1835
2025-09-15 00:02:47 +00:00
Nico Merenq e109b0fc9f Translated using Weblate (Russian)
Currently translated at 63.0% (5595 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-09-14 14:39:57 +00:00
Watuneni iteite 5de7f96fef Translated using Weblate (Japanese)
Currently translated at 85.6% (7595 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-09-14 14:39:56 +00:00
Carmendar 5c6632c3fb Forgor camera offsets for extra seats 2025-09-14 03:20:07 -05:00
Carmendar ebde125895 Bronzong ready to scrape. 2025-09-14 03:18:17 -05:00
martin c45c74c508 Fix double lid on campfire pot open sprites 2025-09-14 14:31:13 +08:00
martin cd6bc23825 Rename CookingPotColor to CampfirePotColor 2025-09-14 06:16:57 +00:00
martin 3df95df1f9 Add color property in CampfirePotItem
To make it really easy to get it in the future
2025-09-14 06:16:57 +00:00
martin ad3ee75c22 Sync cooking pot button color with open / closed state 2025-09-14 06:16:57 +00:00
martin ce3935950a Add cooking pot open sprites 2025-09-14 06:16:57 +00:00
Paul e1380df7d1 Fixed herd counts exceeding the max, smooth the following timing so it's less robotic 2025-09-14 14:03:13 +10:00
Carmendar cf8cfb8dad Nickel no longer defies gravity when sleeping & corrected underwater behaviors on wiglett line. 2025-09-13 18:52:42 -05:00
Instrafe 3729728124 Implemented locators for Omanyte & Omastar 2025-09-13 17:16:41 -04:00
Nico Merenq 1f62f447b5 Translated using Weblate (Russian)
Currently translated at 62.8% (5572 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-09-13 18:04:21 +00:00
MemencioPerez 3092abfdd0 Inline mobTargetInBattle game rule check in TargetingConditions mixin 2025-09-12 15:07:30 -04:00
Carmendar 3150f33e9e underwater walkers now walk underwater, and some swimmers now avoid land. (Crabrawler line avoids water entirely.) 2025-09-12 01:27:34 -05:00
Carmendar 40d7e7bccb Merge branch 'main' into implement_pokemon 2025-09-12 01:27:10 -05:00
MemencioPerez cdbe61843f Skip battle check if the mobTargetInBattle game rule is set to false 2025-09-11 23:00:53 -04:00
Paul b13c168347 Increased default PC box count from 30 to 40 2025-09-12 12:12:39 +10:00
Carmendar f77fa82e4f Implemented finalized Mochi sprites. 2025-09-11 19:02:26 -05:00
Konoha 2e2b10311d Implement Unown ! and ? lang 2025-09-11 17:36:42 -04:00
Gertimoshka 2af5de8ce4 Fixed issue where Animation Transition has a frame non zero scale render on a part that is scale 0 for both animations 2025-09-12 00:02:56 +03:00
Nico Merenq 674e1df96d Translated using Weblate (Russian)
Currently translated at 62.8% (5571 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-09-11 17:08:21 +00:00
Watuneni iteite a4a2ae6133 Translated using Weblate (Japanese)
Currently translated at 85.6% (7595 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-09-11 17:08:20 +00:00
Jerzykv 66eea576f3 comma 2025-09-11 10:45:55 +02:00
Carmendar 53a516266f Annihilape anims and particle! -- jump anims for Garchomp and Ursaring + jump fixes for a few other mons. 2025-09-11 02:43:41 -05:00
Carmendar f732442133 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-09-11 02:41:16 -05:00
GenotypeXD-Dev 34cad13f57 Pachirisu scale fix 2025-09-11 02:19:23 -05:00
GenotypeXD-Dev e879a755a4 Implemented Mew and Pachirisu Remodels, Updated gengars uv/textures 2025-09-11 02:16:58 -05:00
Carmendar 2ac9c827d4 Merge branch 'refs/heads/main' into implement_pokemon 2025-09-10 23:02:07 -05:00
Test a0ef46a18c Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon 2025-09-10 21:09:09 -04:00
Test 9dc15c9096 fix pasture scroll crash 2025-09-10 21:08:49 -04:00
Landon 346e2cd094 Merge branch 'horse-stepup-tweak' into 'main'
Vanilla-Like Horse Ride Style Stepup

See merge request cable-mc/cobblemon!1854
2025-09-10 20:08:24 +00:00
Konoha f7794d50dd Import for cleanliness 2025-09-10 15:24:04 -04:00
Konoha 3b8d67ce64 Tweak the Horse Ride-style "StepUp" to check for the ground similar to vanilla horses. 2025-09-10 14:52:57 -04:00
Test 105d3a7d90 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon 2025-09-09 20:15:56 -04:00
Tyzillion 8a685cefc0 Poodle trimmin tweaks. 2025-09-09 13:35:09 -04:00
Tyzillion 3ff536dc02 Updated Changelog. 2025-09-09 11:16:56 -04:00
Tyzillion 638ec03244 The trimming particles no longer glow. 2025-09-09 11:14:00 -04:00
Tyzillion 37438c9fdd CYAN AND LIGHT BLUE ARE NOT THE SAME. WAAAAAAAAAH 2025-09-09 09:58:30 -04:00
Tyzillion cbc4b52193 Finishing touches on Furfrou. 2025-09-09 09:48:18 -04:00
Tyzillion 9a760d8eae Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-09-09 08:36:17 -04:00
Tyzillion b7d26d99cd Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon
# Conflicts:
#	common/src/main/resources/data/cobblemon/cosmetic_items/dyes.json
2025-09-09 08:33:02 -04:00
Tyzillion 04c8b1e95b Let me pet dat dawg! Can I pet dat dawg? 2025-09-09 08:32:21 -04:00
Tyzillion e4149b2a5e Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into implement_pokemon 2025-09-09 08:29:44 -04:00
Hiroku b446af166d Merge branch 'ImproveHoverWander' into 'main'
Improvements to hover (air) wandering

See merge request cable-mc/cobblemon!1845
2025-09-09 11:50:00 +00:00
Carmendar 3cdaa513f8 Lickilicky ready for scrape with placeholder controller, and various other small fixes and tweaks. 2025-09-09 03:09:19 -05:00
Polymeta 75e0da38c4 Merge branch 'pot-jei-catalysts' into 'main'
Added JEI recipe catalysts for campfire pots

See merge request cable-mc/cobblemon!1848
2025-09-09 08:06:53 +00:00
GenotypeXD-Dev ca43fcf3a2 Implemented New cosmetics [Watermelon Electrodes, Pokemopolis alakazam, gengar, and jigglypuff, Dots Tinkatink, Tinkatuff, and Tinkaton, Golden Sudowoodo] 2025-09-09 01:39:33 -05:00
gruxor 4f71e11383 Added JEI recipe catalysts for campfire pots 2025-09-09 01:48:58 -04:00
Tyzillion 5b56a5758a Drednaw, more like, DredMAH THERE'S A WEIRD MF CAT OUTSIDE. 2025-09-08 21:54:46 -04:00
Tyzillion 424889e820 You know what, screw you. *Downgrades your Chewtle* 2025-09-08 21:24:10 -04:00
Tyzillion 6d3f00cd58 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into implement_pokemon 2025-09-08 21:07:08 -04:00
Carmendar 1d375bdffa Last Bouffalant fix I swear... also Garchomp head fix in sand. 2025-09-08 19:23:24 -05:00
Carmendar e563bbee49 bouff blink fix 2025-09-08 18:05:22 -05:00
Polymeta e2ccb20402 Merge branch 'hydrophobic-gates' into 'main'
Removed Hydrophobia from cobblemon gates + consistency changes

See merge request cable-mc/cobblemon!1847
2025-09-08 21:30:49 +00:00
Sterrezo 8fe2dc8304 Restored Golurk's ride sounds 2025-09-08 22:27:49 +02:00
Gertimoshka 4ddc167254 Gates no longer self destruct then open and near water at random rate, also can be ingited by lava 2025-09-08 23:22:16 +03:00
Lennie e3502ec320 Added wing flap sounds to updated noivern & volcarona animations, adjusted noivern's portrait 2025-09-08 10:20:00 +02:00
Carmendar d8d0ade350 luigi and netherite hoe controller updates. 2025-09-08 02:34:31 -05:00
GenotypeXD-Dev e2ac4449b3 Implemented Seedot line Remodels 2025-09-07 22:15:30 -05:00
GenotypeXD-Dev ecec7ccee3 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-09-07 21:16:30 -05:00
GenotypeXD-Dev 228282f083 Added regional caps for raichu and pichu, added ukulele pichu, and fixes for pikachu cap cosmetics 2025-09-07 21:15:25 -05:00
Carmendar 3bc7d39800 Forgor changelog 2025-09-07 21:06:30 -05:00
Carmendar 3dc5873642 Nickel line remodels, Garchomp anims, fixed Bouffalant battle_idle, fixed Noivern clipping, updated Volcarona anim file, implemented Chewtle line cries and Axew cry, removed reference to clobbopus cry anim (it doesn't have one), and fixed third person camera offset for some mons. 2025-09-07 21:03:52 -05:00
Nico Merenq a5c3d2f800 Translated using Weblate (Russian)
Currently translated at 62.5% (5549 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-09-07 20:08:56 +00:00
Teggy 00d2bbbe42 Translated using Weblate (Spanish)
Currently translated at 85.5% (7586 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-09-07 20:08:55 +00:00
Lennie 8d6291c4c2 Fixed a crash due to a poser issue 2025-09-07 13:36:37 +02:00
Jerzykv 2b77d030e8 You can drive now! (Implemented Varoom & Revavroom remodels + placeholder ride data) 2025-09-07 11:45:27 +02:00
Carmendar 029927af6a Merge branch 'main' into implement_pokemon 2025-09-07 04:12:19 -05:00
Carmendar bf791dc4d1 Bouffalant can eepy. 2025-09-07 04:08:13 -05:00
Polymeta 56ad481304 Merge branch 'implement_pokemon' into 'main'
Implement pokemon

See merge request cable-mc/cobblemon!1842
2025-09-07 09:04:19 +00:00
Polymeta cc7a9ae44a Merge branch 'Move-Particles-1.7' into 'main'
YOU GET PARTICLES, YOU GET PARTICLES, AND YOU GET PARTICLES. SO MANY MOVES WITH PARTICLES.

See merge request cable-mc/cobblemon!1839
2025-09-07 09:02:16 +00:00
Tyzillion 664f79f3a7 YOU GET PARTICLES, YOU GET PARTICLES, AND YOU GET PARTICLES. SO MANY MOVES WITH PARTICLES. 2025-09-07 09:02:16 +00:00
JazzMcNade 9a2ed78b0d Improve air wanders 2025-09-07 03:54:55 -05:00
Carmendar 558907eb5c Drednaw cry and sobble also smol 2025-09-07 03:31:26 -05:00
Carmendar 370b689ab4 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-09-07 03:21:59 -05:00
Carmendar e2740babc5 Togetic smol 2025-09-07 03:21:15 -05:00
Lennie 3393ab17d8 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-09-07 09:06:05 +02:00
Lennie 645029a9c7 Fixed wings being invisible for wattrel in its battle stance 2025-09-07 09:05:41 +02:00
Carmendar f168744ccd Merge branch 'main' into implement_pokemon 2025-09-06 23:49:08 -05:00
Carmendar 67210cb5f9 Fixed Parasect crash. 2025-09-06 23:15:44 -05:00
Carmendar 779fbb4f38 Removed walking privileges from Hydreigon and Volcarona, and fixed Magnezone's magnets being too spinny. 2025-09-06 21:01:31 -05:00
Landon ce04f546e7 Merge branch 'ride_pokemon_event' into 'main'
Molang and Flowback for Riding Pokemon

See merge request cable-mc/cobblemon!1843
2025-09-07 01:46:58 +00:00
Landon a36437ea16 Merge branch 'riding-behaviour-tweaks-01' into 'main'
Ride behaviour tweaks based on team feedback

See merge request cable-mc/cobblemon!1844
2025-09-07 01:35:50 +00:00
Jackson Bowes aa5af84283 Ride behaviour tweaks based on team feedback 2025-09-07 01:35:50 +00:00
Konoha e3031d2618 Use 'this' in tryRidingPokemon instead of unnecessary parameter. 2025-09-06 21:34:03 -04:00
Konoha 9e44ba1fb1 Fix getRideBoost function call 2025-09-06 21:31:55 -04:00
Konoha 1af66d4175 Revert function refactoring, set rideBoosts to private. 2025-09-06 21:21:43 -04:00
Konoha 144213cee8 Remove redundant check in tryRidingPokemon 2025-09-06 21:17:40 -04:00
Konoha cc95b4450e Make set_ride_boost and add_ride_boost to case insensitive. 2025-09-06 21:04:37 -04:00
Konoha 4bda306a3b Refactor "getRideBoosts" helper to avoid signature conflict on public "rideBoosts" 2025-09-06 20:18:14 -04:00
Carmendar 2ee556dc31 Updated Bastiodon third person camera. 2025-09-06 18:21:05 -05:00
Carmendar 4457df9aba Updated Bastiodon ride overlays and fixed Magnezone portrait. 2025-09-06 18:15:47 -05:00
Konoha 6dfb073242 Add Ride Boost Molang Functions 2025-09-06 19:04:44 -04:00
Jakotens e1bd0d688f Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-09-06 17:00:23 -06:00
Jakotens 0bce2d8645 Fix third-person camera positions for most rideable Pokemon. 2025-09-06 17:00:14 -06:00
Landon dfe240f1d3 Merge branch 'riding-scrape' into 'main'
More riding scrape stuff

See merge request cable-mc/cobblemon!1841
2025-09-06 22:11:16 +00:00
Landon Wills f1ac0fe99f Scrape riding updates 2025-09-06 17:27:08 -04:00
Landon Wills c2f575bf22 Add condition so boat jumps do not run land jump animations 2025-09-06 17:11:22 -04:00
Konoha 5f76966678 Implement event handling function on InteractPokemonHandler
Update licenses.
2025-09-06 17:09:14 -04:00
Carmendar beddea1ecb Merge branch 'main' into implement_pokemon 2025-09-06 16:08:48 -05:00
Carmendar 04236fcf33 Prepped Bouffalant for scrape. 2025-09-06 15:59:12 -05:00
Landon 05e87b7faf Merge branch 'riding-scrape' into 'main'
Add new scraper files

See merge request cable-mc/cobblemon!1840
2025-09-06 20:55:34 +00:00
Landon Wills e9d298834a Merge branch 'riding-scrape' into implement_pokemon 2025-09-06 16:14:59 -04:00
Landon Wills ce113cbb80 Add new condition to poser for ride style 2025-09-06 16:11:13 -04:00
Tyzillion 61ebe7124a Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-09-06 16:01:04 -04:00
Tyzillion 462c0b35b8 Starly Line remodels implemented. 2025-09-06 16:00:51 -04:00
Carmendar 0b55c88ec4 Prepped Rampardos for scrape, includes placeholder controller. 2025-09-06 14:51:38 -05:00
Landon Wills 942da6b2f6 Merge branch 'implement_pokemon' into riding-scrape 2025-09-06 15:45:20 -04:00
Konoha 224c38e0a7 Ride Pokemon Event and Flowback 2025-09-06 15:36:10 -04:00
Landon Wills 056917f7df Adjust basic behaviour in scraper, run scraper 2025-09-06 15:28:15 -04:00
Carmendar 2aa9379987 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-09-06 14:06:16 -05:00
Carmendar 3a6a3253a3 Prepped Carracosta for scrape, removed controller from Unfezant. 2025-09-06 14:05:48 -05:00
Landon Wills 1580a06a8e Run scraper 2025-09-06 14:32:26 -04:00
Tyzillion e2bfdd3e76 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/repository/VaryingModelRepository.kt
2025-09-06 14:30:50 -04:00
Jerzykv 1d24565aa6 Changelog 2025-09-06 18:03:55 +02:00
Jerzykv 2d80402688 Added Gholdengo and Gimmighoul remodels/retextures + cosmetics 2025-09-06 16:13:13 +02:00
Teggy 40b351a303 Translated using Weblate (Spanish)
Currently translated at 85.5% (7586 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-09-06 12:01:45 +00:00
Not telling you that dc32bd1845 Translated using Weblate (Spanish)
Currently translated at 85.5% (7586 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-09-06 12:01:45 +00:00
Nico Merenq a096485155 Translated using Weblate (Russian)
Currently translated at 62.0% (5508 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-09-06 11:58:11 +00:00
Teggy f1ae7c4476 Translated using Weblate (Spanish)
Currently translated at 85.4% (7579 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-09-06 11:58:10 +00:00
Not telling you that bcfac2f68a Translated using Weblate (Spanish)
Currently translated at 85.4% (7579 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-09-06 11:58:10 +00:00
Nico Merenq ff1902770c Translated using Weblate (Russian)
Currently translated at 62.0% (5503 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-09-06 11:52:11 +00:00
Not telling you that ebb01d4fcc Translated using Weblate (Spanish)
Currently translated at 85.3% (7571 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-09-06 11:52:11 +00:00
Carmendar 20614b6b07 Merge branch 'main' into implement_pokemon 2025-09-06 04:58:47 -05:00
Carmendar b32b5a66a4 Prepped Venusaur, Parasect and Dusknoir for scraping. Adjusted portraits for Squirtle and Wartortle. - updated changelog. 2025-09-06 04:57:38 -05:00
Carmendar ad92804fce Prepped Amaura, Bastiodon and Ursaring for Scraping. Includes placeholder controllers. 2025-09-06 01:08:35 -05:00
GenotypeXD-Dev ca93955134 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-09-06 00:55:37 -05:00
Hiroku 01d99676f9 Merge branch 'mo-molang-mo-problems' into 'main'
Add ability to specify file paths for MoLang stores

See merge request cable-mc/cobblemon!1744
2025-09-06 05:55:06 +00:00
GenotypeXD-Dev 2c8fd29cf1 Implemented Pikachu Cosmetics (Ash caps, Captain Cap, Hair Fluff) and added more usable leaves for hawluchas cosmetic [Detective pikachu and Partner Pikachu Tomorrow] 2025-09-06 00:55:06 -05:00
Carmendar a9eec5f568 Removed Hisuian Avalugg from evo selection. 2025-09-06 00:47:30 -05:00
Hiroku e54eaa728b Merge branch 'main' into 'mo-molang-mo-problems'
# Conflicts:
#   common/src/main/kotlin/com/cobblemon/mod/common/api/molang/MoLangFunctions.kt
2025-09-06 04:17:27 +00:00
Paul 4e550151e5 Merge remote-tracking branch 'origin/main' 2025-09-06 14:16:02 +10:00
Paul 67f5259742 Pokemon that don't defend themselves will instead run for their life when hurt 2025-09-06 14:15:52 +10:00
jackowes eb783e13ba un t-pose gyarados 2025-09-05 22:33:25 -05:00
Hiroku 68237e7161 Merge branch 'Special-Candy-Recipes-2-Electric-Boogaloo' into 'main'
Special candy balancing.

See merge request cable-mc/cobblemon!1832
2025-09-06 02:52:59 +00:00
Wi2tert ec9f7665e2 Push files!! 2025-09-06 10:16:19 +08:00
Wi2tert f19ae0120e Push files!! 2025-09-06 10:07:08 +08:00
Wi2tert d674ff1d40 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-09-06 10:01:51 +08:00
Wi2tert 37d6f58536 "Remodels": dusknoir 2025-09-06 10:00:26 +08:00
Jakotens 2ce23e072d Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-09-05 19:08:15 -06:00
Jakotens d37dfee032 Updated models and animations for Sneasel, Sneasel (Hisui), Weavile, and Sneasler. Added animations for Capsakid and Scovillain. Updated changelog to reflect previous changes. 2025-09-05 19:08:08 -06:00
Hiroku ee567a63b6 Merge branch 'hero-holo' into 'main'
Data Tweaks

See merge request cable-mc/cobblemon!1836
2025-09-05 23:45:13 +00:00
dhi_holo 868dd9cc44 Data Tweaks 2025-09-05 23:45:12 +00:00
Hiroku 3f9b0002a9 Merge branch 'FixDespawns' into 'main'
Fixed despawning blocking on empty held items, fixed forced spawns having...

See merge request cable-mc/cobblemon!1834
2025-09-05 23:44:37 +00:00
Hiroku e07b15c8fc Use named parameters where same type arguments are chained 2025-09-05 23:41:40 +00:00
Hiroku 44c5c21fb5 Merge branch 'storage-saver' into 'main'
Protect the storages!

See merge request cable-mc/cobblemon!1837
2025-09-05 23:39:27 +00:00
Hiroku 9d13e86487 Merge branch 'wild-interact' into 'main'
Prevent interact wheel on wild pokemon

See merge request cable-mc/cobblemon!1833
2025-09-05 23:26:06 +00:00
Landon Wills afa395cfeb Just mount without interact UI for passengers 2025-09-05 15:00:29 -04:00
Konoha 831903b8e3 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-09-05 14:58:19 -04:00
Konoha d382f48e2d Implement stubbed item evo conditions for Duraludon and Kubfu 2025-09-05 14:57:59 -04:00
Landon Wills 2b2b44a535 Merge branch 'main' into wild-interact 2025-09-05 14:16:22 -04:00
GenotypeXD-Dev ac9d85929e Implemented Articuno, Zapdos, Moltres, And Mewtwo Remodels 2025-09-05 11:29:13 -05:00
GenotypeXD-Dev 88c981bd6a Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-09-05 09:33:32 -05:00
GenotypeXD-Dev f0baa21b1f Fearow line, Vulpix line, And hawlucha placeholder cries 2025-09-05 09:33:13 -05:00
Neovitalism 1d5d78b953 Protect the storages! 2025-09-05 04:29:14 -05:00
Neovitalism 2a62b49eab Update PokemonSpecies.kt 2025-09-04 21:10:49 -05:00
Paul f771248936 oop 2025-09-05 12:05:16 +10:00
JazzMcNade 35850ea996 Merge branch 'main' into FixDespawns 2025-09-04 20:31:04 -05:00
Landon Wills caaccf0e67 Prevent mounting if pokemon is in pasture 2025-09-04 21:29:45 -04:00
JazzMcNade e5de8bd26f Fixed despawning blocking on empty held items, fixed forced spawns having persistence do to having a held item. 2025-09-04 20:28:33 -05:00
Landon Wills 9528d375f4 Prevent interact wheel on wild pokemon 2025-09-04 21:21:24 -04:00
Paul 9aac7dc8e3 Fixed crash when baiting rods on servers 2025-09-05 11:12:12 +10:00
Paul 0bebd2a71e Fixed sleepers not waking up properly in battle if they were sleeping before 2025-09-05 11:09:51 +10:00
Hiroku 55874e6a84 Merge branch 'poser-tpconvb' into 'main'
Add new offset option for third person offset + renamed the third person...

See merge request cable-mc/cobblemon!1817
2025-09-05 00:38:11 +00:00
Paul cda0e10524 Combee can herd after Vespiquen specifically (because it's funnier this way) 2025-09-05 10:24:50 +10:00
Paul 153f888cf5 Don't follow drowsy pokemon (get a designated driver smh) 2025-09-05 10:14:01 +10:00
Paul 6972b7d49d Eject passengers from pokemon the moment it starts getting recalled, combee no herd 2025-09-05 10:10:26 +10:00
Hiroku d7f442e2d0 Merge branch 'main' into 'poser-tpconvb'
# Conflicts:
#   common/src/main/resources/assets/cobblemon/bedrock/pokemon/posers/0967_cyclizar/cyclizar.json
2025-09-04 23:51:50 +00:00
Carmendar cc15c0a487 Special candy balancing. 2025-09-04 18:10:29 -05:00
Landon a37828e288 Merge branch 'riding-birddolphin-polish-01' into 'main'
Polish and improve bird ride

See merge request cable-mc/cobblemon!1829
2025-09-04 22:21:59 +00:00
Jackson Bowes 91d00cb864 Polish and improve bird ride 2025-09-04 22:21:59 +00:00
Lennie 4ee628ea7a Implemented riding animations & temp ride controller for piloswine 2025-09-05 00:04:30 +02:00
Lennie 64c0001aff Implemented wing flapping sounds for volcarona 2025-09-04 23:17:55 +02:00
Lennie 320b2ff528 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-09-04 23:08:35 +02:00
Lennie cf8a1f5447 Implemented Mamoswine animation changes & model edits and added placeholder ride controller 2025-09-04 23:08:19 +02:00
Sterrezo 696d98f7cd Rebalanced wing flap sound volumes and properly added sounds for large chitin wings 2025-09-04 23:07:42 +02:00
Sterrezo 909b90aea1 Update gyarados_cry.ogg 2025-09-04 22:56:39 +02:00
GenotypeXD-Dev 04296ae04e Implemented Hawlucha Tweaks+Forest Champion Mask Cosmetic 2025-09-04 14:24:02 -05:00
Kenji_64 025dea4201 Dragapult remodels! (will come back for new poser and anims for Draga) 2025-09-04 15:21:00 +02:00
阿鱼 0b392c66eb Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 99.5% (8833 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-09-04 12:47:12 +00:00
Nico Merenq 5733074bd8 Translated using Weblate (Russian)
Currently translated at 61.5% (5460 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-09-04 12:47:11 +00:00
Sterrezo a0630ff738 Added sounds for large chitin wings and updated medium steel wing sounds 2025-09-04 14:43:57 +02:00
Polymeta 5b5de641cb poof and its gone
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-09-04 11:28:33 +01:00
Polymeta 5a8101e535 fix hydreigon behaviour and horse settings sync
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-09-04 11:02:54 +01:00
Polymeta 054c841ec7 Merge branch 'implement_pokemon' into 'main'
mergin' imp to main

See merge request cable-mc/cobblemon!1830
2025-09-04 08:25:24 +00:00
Carmendar b02ac04fe3 rabsca crash fix & prep for lugia scrape. 2025-09-04 00:44:33 -05:00
Landon 57b4159691 Merge branch 'Special-Candy-Recipes' into 'main'
Special candy recipes

See merge request cable-mc/cobblemon!1826
2025-09-04 02:31:36 +00:00
Landon 41e3b61657 Merge branch 'riding-scraper-3' into 'main'
Scrape new rideable pokemon

See merge request cable-mc/cobblemon!1828
2025-09-04 02:06:16 +00:00
Landon f48cd648fc Merge branch 'boat-expressions' into 'main'
Add expressions to behaviour settings for boat

See merge request cable-mc/cobblemon!1827
2025-09-04 01:57:05 +00:00
Landon Wills 76c457dcbc Update scraped pokemon 2025-09-03 21:20:49 -04:00
Landon Wills cae828dc48 Merge branch 'main' into riding-scraper-3 2025-09-03 21:16:34 -04:00
Landon 39aa9d3ff1 Merge branch 'riding-scraper-ranges' into 'main'
Update scraped data

See merge request cable-mc/cobblemon!1823
2025-09-04 01:15:57 +00:00
Landon Wills 43d4b127ae Merge branch 'implement_pokemon' into riding-scraper-3 2025-09-03 21:12:07 -04:00
Landon Wills 008c75d591 Add expressions to behaviour settings for boat 2025-09-03 21:09:53 -04:00
Landon c32d8a5389 Merge branch 'boat-changes' into 'main'
Modify stamina logic, add 'sprint' to boat

See merge request cable-mc/cobblemon!1825
2025-09-03 23:14:02 +00:00
Landon Wills 24bbfdf038 Modify stamina logic, add 'sprint' to boat 2025-09-03 12:10:56 -04:00
Polymeta b3590eb49b Merge branch 'Mo-Functions-MOLANG' into 'main'
Battle Themes Fixed and A BUNCH of MoLang.

Closes #1468

See merge request cable-mc/cobblemon!1797
2025-09-03 08:31:43 +00:00
Carmendar 0b51e0fe48 Typo fix 2025-09-03 00:29:46 -05:00
阿鱼 88ff4e37b8 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 99.5% (8833 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-09-03 03:48:57 +00:00
Carmendar 68c144e366 Added recipes for... candy. 2025-09-02 21:05:00 -05:00
Carmendar 506b0e8c70 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-09-02 17:55:09 -05:00
Carmendar 5091abc8ef Implemented new animations for Baltoy & Claydol. 2025-09-02 17:54:30 -05:00
Tyzillion 2a7503db06 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into implement_pokemon 2025-09-02 15:55:55 -04:00
Tyzillion 1480bd97c2 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/repository/VaryingModelRepository.kt
2025-09-02 15:55:42 -04:00
Segfault Guy 11239ff856 Add flag to BattleMusicPacket to determine whether a sound is restarted if played consecutively. 2025-09-02 15:36:27 -04:00
Polymeta eaca59a420 Merge branch 'cooking-fixes-never-stop' into 'main'
Another Serving of Cooking Pot Fixes

See merge request cable-mc/cobblemon!1820
2025-09-02 14:47:59 +00:00
Gertimoshka 6e5a09d74d Silly Eevee make silly mistake - Fixed cooking pot cooking only then open instead of closed 2025-09-02 16:47:06 +03:00
Nico Merenq 35b7a80501 Translated using Weblate (Russian)
Currently translated at 61.4% (5455 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-09-02 12:29:43 +00:00
Watuneni iteite 91949414cc Translated using Weblate (Japanese)
Currently translated at 85.6% (7595 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-09-02 12:29:42 +00:00
Jerzykv 79d6f4de1f Bellibolt species file fix 2025-09-02 11:27:40 +02:00
violet 3e212a8eb1 bellibolt fixes + houndour & houndoom placeholder cries 2025-09-02 04:45:19 -04:00
Polymeta 1084f40282 Merge branch 'snack' into 'main'
♨️

See merge request cable-mc/cobblemon!1815
2025-09-02 08:34:49 +00:00
Polymeta e82609176f Merge branch 'client-on-server-bug' into 'main'
Fix bug with LivingEntityMixin$pick() on server with riding.

See merge request cable-mc/cobblemon!1816
2025-09-02 08:30:20 +00:00
Carmendar 169464ca47 Merge branch 'main' into implement_pokemon 2025-09-02 00:27:51 -05:00
Gertimoshka 3a3a505e92 Fixed Merge Conflicts 2025-09-02 05:03:19 +00:00
Konoha df727d8619 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-09-01 21:50:03 -04:00
Konoha c44e2f43f8 Fix conflicting regional evolution anticonditions.
Implement Galarian Weezing evolution.
Stub Hisuian Avalugg evolution.
2025-09-01 21:49:50 -04:00
Carmendar 7223bd77d9 Updated Zweilous model. 2025-09-01 20:46:09 -05:00
Konoha 59cb3017a2 Implement Bergmite Hisuian Evolution 2025-09-01 20:12:13 -04:00
Landon Wills 1f00890cec Remove debug logs from scraper 2025-09-01 18:20:29 -04:00
Landon Wills c7e01ae07b Update ride stats for real this time 2025-09-01 18:19:42 -04:00
Landon Wills f0fa87e06a Update script for new columns and stat ranges, update scraped riding stats 2025-09-01 17:19:12 -04:00
Landon Wills b4c211ac08 Merge branch 'implement_pokemon' into riding-scraper-ranges 2025-09-01 17:08:38 -04:00
Landon 82da0e8337 Merge branch 'riding-bug-fixes' into 'main'
Riding bug fixes

See merge request cable-mc/cobblemon!1822
2025-09-01 20:43:40 +00:00
Landon Wills 75bb5d8ac7 Reset animation data on driver dismount 2025-09-01 15:59:40 -04:00
Landon 2ae3da859a Merge branch 'riding-rocket-polish-01' into 'main'
Finalize rocket functionality

See merge request cable-mc/cobblemon!1803
2025-09-01 19:39:46 +00:00
Jackson Bowes ef3e49529b Finalize rocket functionality 2025-09-01 19:39:46 +00:00
Landon 121afaf478 Merge branch 'riding-jet-polish-01' into 'main'
Jet Polish

See merge request cable-mc/cobblemon!1819
2025-09-01 19:39:44 +00:00
Jackson Bowes a6128cd36a Jet Polish 2025-09-01 19:39:44 +00:00
Landon c8cd9adb4f Merge branch 'riding-hover-polish-01' into 'main'
Hover Ride polish

See merge request cable-mc/cobblemon!1818
2025-09-01 19:39:27 +00:00
Jackson Bowes b2d5b9d6a2 Hover Ride polish 2025-09-01 19:39:27 +00:00
Landon Wills ab6051f88a Fix boats behaviours sinking, fix boat mount logic if submerged 2025-09-01 15:34:22 -04:00
Landon Wills 98800f695a Prevent transitions to air if pokemon does not have air behaviour 2025-09-01 15:24:20 -04:00
Gertimoshka fdebf4db73 General cleanup of unused/redundant code of cooking pot related stuff 2025-09-01 20:25:22 +03:00
Lennie 5f21a50e7e Animation updates for Klink, Klang & Klinklang
Implemented a temporary ride controller for klinklang
2025-09-01 18:41:34 +02:00
Gertimoshka 0d233f109d Fixed the Achievement for placing cooking pot on the campfire 2025-09-01 19:36:08 +03:00
Gertimoshka 467a6901c7 Fixed the raycast collision of campfire with pot not counting pot 2025-09-01 19:12:10 +03:00
Kenji_64 d4cd6b0ac6 Updated Pikachu line's emissives + minor head rotation adjustments for Alolan Raichu 2025-09-01 15:59:43 +02:00
Gertimoshka ee7e0b2e91 Fixed default color broth bubbles not apearing as well as fixing some new blockstate caused minor bugs 2025-09-01 16:31:49 +03:00
Jerzykv 1da1cdcb10 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-09-01 15:00:07 +02:00
Jerzykv 35c5e2fec6 Unmuted Abra and Kadabra 2025-09-01 14:59:42 +02:00
Kenji_64 efa1a9f383 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-09-01 14:13:15 +02:00
Paul 6bb749d806 Duplicate JSON keys in Unown oops 2025-09-01 20:15:57 +10:00
Carmendar 104755343b Texture updates for Weedle. 2025-08-31 21:13:11 -05:00
Carmendar 9e0ab5363f Animations added for Deino line and Slaking, and added placeholder ride controllers. 2025-08-31 20:26:28 -05:00
Paul 19d5903e2c Fixed R-bind misbehaving 2025-09-01 10:47:38 +10:00
Lennie dbd55b9382 Corrected a typo in dwebble's poser 2025-09-01 00:12:35 +02:00
JPAKx4 3bb5ecd2b6 Add new offset option for third person offset + renamed the third person offset with no view bobbing. 2025-08-31 16:59:38 -05:00
JPAKx4 def44930e4 Fix bug with pick() on server with riding. 2025-08-31 16:05:22 -05:00
Jakotens 3d7b95d7fe Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-08-31 13:23:27 -06:00
Jakotens 0f44b40d21 Added riding animations to Dragonite. Implemented animations for Serperior. 2025-08-31 13:23:20 -06:00
Lennie 987c1e2244 Removed kotlin posers for espathra, flygon, larvesta and volcarona 2025-08-31 20:49:16 +02:00
Lennie 1b184af306 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-08-31 20:24:48 +02:00
Lennie fc37cf403b Implemented espathra ride animations and placeholder ride controller 2025-08-31 20:24:30 +02:00
Jerzykv ae160cca80 Implemented Kadabra and Alakazam animations + locators for the whole line. Fixed Abra's portrait. Kadabra can sleep now 2025-08-31 18:04:47 +02:00
Kenji_64 ea21596460 Changelog for the Pikas 2025-08-31 15:25:17 +02:00
Kenji_64 32233737c1 Implemented Pikachu line remodels and some clean-up for their regional bias forms! 2025-08-31 15:24:30 +02:00
Kenji_64 f0ab01ed0c Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-08-31 15:19:56 +02:00
Konoha 3a8354fb1f Legacy Learnable move MoLang query implementation 2025-08-31 09:08:52 -04:00
Kenji_64 25493b521e Fixed Spinarak's shoulder pose position 2025-08-31 14:16:01 +02:00
Gertimoshka df5407e200 Lid and Cooking is now Blockstates for the Campfire with Pot fixing several bugs as well as introducing additional observer interactions 2025-08-31 13:15:06 +03:00
Lennie 2bdc29873a Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-08-31 10:30:35 +02:00
Lennie 6ca89e1091 Implemented animations and placeholder ride controller for flygon 2025-08-31 10:30:12 +02:00
violet 92c488861c Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-08-31 04:24:43 -04:00
violet 259bf693b6 ⭐ Added Cleffa, Clefairy, and Clefable's remodels 2025-08-31 04:24:34 -04:00
Test 43dbfc772e Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon 2025-08-30 22:06:27 -04:00
GenotypeXD-Dev ab8dd5f477 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-08-30 19:23:59 -05:00
GenotypeXD-Dev f1d2145359 Implemented Remodels for the Spearow line, Nidoking, Vulpix line, and Grimer line 2025-08-30 19:21:50 -05:00
Test 52a74bd182 Merge branch 'sterr/wind-noises-lite' into 'main'
Updated ambient ride sounds

See merge request cable-mc/cobblemon!1814
2025-08-30 23:21:45 +00:00
Sterrezo ef9f84d127 Updated ambient ride sounds 2025-08-31 00:07:25 +02:00
Test d27a30d5f1 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into snack 2025-08-30 17:59:40 -04:00
Test c1b4d98b6d ender pearl cosmetic mixin, kingler bubbles 2025-08-30 17:59:26 -04:00
Tyzillion e14e44a1c5 Mime.Jr and Mr.Mime model updated, Mr.Mime animations updated. 2025-08-30 17:58:00 -04:00
Test eb64aa3207 add particles for poke snack, update item sprite for differentiation 2025-08-30 17:50:59 -04:00
Tyzillion 46cc276e2e Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-08-30 17:17:52 -04:00
Jakotens d663461c51 Added riding animations to Mudsdale. Implemented animations for Rookidee, Corvisquire, and riding animations for Corviknight. Adjusted third-person riding camera offsets for Skiddo, Gogoat, Girafarig, Farigiraf, Braviary, Braviary-Hisuian, and Cyclizar. 2025-08-30 14:55:14 -06:00
Kenji_64 8c1d4f9790 Backtracked and made Dudunsparce rideable! (with placeholder stats) 2025-08-30 21:23:49 +02:00
Kenji_64 82bf374cad Replaced the placeholder cry for Cyclizar with the approved cry! 2025-08-30 18:16:33 +02:00
Kenji_64 5c9c42705a Added placeholder cry for Cyclizar! 2025-08-30 16:42:39 +02:00
Kenji_64 4c77dba0b5 Tadbulb base texture fix 2025-08-30 15:49:45 +02:00
Kenji_64 dcd431c699 Shiny Tirtouga palette fix 2025-08-30 15:49:34 +02:00
Kenji_64 165b137de2 Shiny Emboar nose fix 2025-08-30 15:49:18 +02:00
Kenji_64 e40a2386c2 Latias and Latios are rideable again (with placeholder stats) 2025-08-30 15:48:32 +02:00
Kenji_64 8933bab24f New metal textures for Beldum line 2025-08-30 15:47:52 +02:00
Lennie b7b58019cd Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-08-30 14:29:32 +02:00
Lennie edffb938d6 Implemented animations for dwebble and crustle 2025-08-30 14:28:39 +02:00
Paul 3a074bff8a Fixed dummy moves from removed addons corrupting pokemon stores 2025-08-30 21:35:08 +10:00
Jerzykv 0e33023517 Fixed Politoed's drop 2025-08-30 13:35:06 +02:00
Paul 3874e21c65 Fixed dummy abilities from removed addons corrupting pokemon stores 2025-08-30 21:21:20 +10:00
washashu0905 10bfe3b29d Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 96.8% (8593 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hant/
2025-08-30 11:18:38 +00:00
阿鱼 ff88a3c756 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 99.5% (8832 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-08-30 11:18:38 +00:00
Jerzykv 5132b47427 Leavanny no longer ominously t-poses when walking 2025-08-30 11:56:41 +02:00
Jerzykv 526b1d145b Adjusted Sewaddle line hitboxes 2025-08-30 11:56:13 +02:00
Hiroku 70659cd818 Merge branch 'events_plus' into 'main'
Community Event Improvements

See merge request cable-mc/cobblemon!1798
2025-08-30 09:28:51 +00:00
Aram acdd6692c8 Community Event Improvements 2025-08-30 09:28:51 +00:00
violet 5607eb0bd8 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-08-30 05:24:30 -04:00
Jerzykv 5634b94c35 Forgot changelog 2025-08-30 10:56:51 +02:00
Jerzykv 272209c268 Unovan Zorua and Zoroark model and texture updates 2025-08-30 10:53:30 +02:00
violet f26b3242d5 added togetic animations + togepi placeholder cry 2025-08-30 04:45:02 -04:00
Paul b6ea8f728e Merge remote-tracking branch 'origin/main' into flows-be-callbacks 2025-08-30 17:20:23 +10:00
Paul e76818d7d8 Renamed "Flows" to Callbacks so it's less eccentric 2025-08-30 17:20:01 +10:00
Hiroku bcf379f218 Merge branch 'recipe-obtainment' into 'main'
Recipe Obtainment Fixes

Closes #1661

See merge request cable-mc/cobblemon!1813
2025-08-30 05:21:57 +00:00
Test 6cb0f296ce Fix summary name text cursor 2025-08-30 01:20:46 -04:00
Hiroku 433f4f6ca5 Merge branch 'pokemon-form-abilities-fix' into 'main'
Fix 'Forced' tag on Abilities for Alternate Forms

Closes #1649

See merge request cable-mc/cobblemon!1787
2025-08-30 05:09:27 +00:00
Paul dc8d55a7d2 Changelog oopsies [skip-ci] 2025-08-30 11:02:44 +10:00
washashu0905 4d47cb8385 Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 96.7% (8582 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hant/
2025-08-30 00:42:40 +00:00
Konoha 219bbbe077 Implement Galarian Mr. Mime evo 2025-08-29 20:26:24 -04:00
Konoha 70704b6330 Implement Galarian Weezing evo 2025-08-29 20:25:17 -04:00
dhi_holo 7f511a391c Recipe Obtainment Fixes
Fixes some campfire pot recipes not showing up in the recipe book

Closes #1661
2025-08-30 10:03:58 +10:00
washashu0905 8aba2bc279 Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 96.1% (8530 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hant/
2025-08-29 23:50:13 +00:00
Lennie 932f366c83 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-08-30 00:43:35 +02:00
Lennie 434218dda3 Implemented animations for Larvesta & Volcarona
Implemented placeholder ride stats for volcarona
2025-08-30 00:43:06 +02:00
Polymeta e4dbba026c Merge branch 'josh/model-widget-fixes' into 'main'
Model Widget Item Fixes

See merge request cable-mc/cobblemon!1812
2025-08-29 20:56:26 +00:00
Jakotens db1834f6bc Tweaked pokemon_follows_owner behavior so that Pokemon follow you more closely and don't teleport until you are much further away. 2025-08-29 14:49:44 -06:00
Jakotens dbbf96bb36 Updated Cyclizar animations and ride controller. 2025-08-29 13:11:40 -06:00
Josh 3a23f7a239 added item MoLang queries to Shoulder Mounts 2025-08-29 12:32:09 -04:00
Polymeta 9f6de64089 fix dolphins crashing... still looks odd, but progress
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-08-29 17:22:20 +01:00
Polymeta e86ee8a7d4 forcefully stop player sneaking when mounting pokemon
Fixes #1534

Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-08-29 16:30:43 +01:00
Josh 7829e83ba0 fixed item visibility button 2025-08-29 11:03:31 -04:00
Josh ab3747a0c4 added MoLang queries to ModelWidget.kt 2025-08-29 11:02:43 -04:00
Polymeta 501d81e4a6 fix cooking achievement not working on soul campfire
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-08-29 15:53:59 +01:00
Polymeta f62b566610 fix some particles not appearing on owned pokemon when thrown out
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-08-29 15:52:56 +01:00
Nico Merenq fe91e912d0 Translated using Weblate (Russian)
Currently translated at 59.5% (5284 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-08-29 13:16:35 +00:00
Kenji_64 32a847af34 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-08-29 14:44:34 +02:00
Kenji_64 1d28672978 New posers for Plusle and Minun 2025-08-29 14:44:13 +02:00
Kenji_64 74a2a2b0ee Implemented Sobble line remodels! 2025-08-29 14:43:46 +02:00
Jerzykv 354ef66d2a Fixed oopsie with Murkrow poser 2025-08-29 14:43:38 +02:00
Kenji_64 a6f1182f02 Plusle and Minun remodels + animations! 2025-08-29 14:42:27 +02:00
Jerzykv 8825e7af3d Added Murkrow remodel and Honchkrow animations. Gave Honchkrow placeholder ride stats 2025-08-29 14:35:28 +02:00
Kenji_64 bcf119bf5d Fixed Bronzor's sleep not looping 2025-08-29 14:32:27 +02:00
Jerzykv d9cb59ca37 Changed Golurk's hitbox and made it able to breathe underwater 2025-08-29 14:21:00 +02:00
Jerzykv e33960effb Fixed Low-Key Toxtricity having no shiny 2025-08-29 14:11:46 +02:00
violet a20eae7cd8 fixed luigi's poser, labeled croagunk and toxicroak as implemented, gave alolan muk and grimer proper scales, fixed unown d's shiny 2025-08-29 04:15:54 -04:00
Hiroku 4adf40c5ec Merge branch 'LessRestrictiveWanders' into 'main'
Water Pathing Improvements

See merge request cable-mc/cobblemon!1807
2025-08-29 06:53:57 +00:00
Jakotens a27e0ffc7f Implemented riding animation for Drifblim. Implemented animations for Girafarig and Farigiraf. 2025-08-29 00:30:11 -06:00
JazzMcNade 3bea35eea2 Replace isInWater with isInLiquid 2025-08-29 01:04:06 -05:00
Jakotens 3db7ecd71f Temporary tweaks to Drifblim. 2025-08-28 19:26:09 -06:00
Paul 723eccc88a Fixed hypertrain IV fix running on already-converted data, causing random entities. Closes #1634 2025-08-29 11:08:43 +10:00
Paul 75785e0706 Fixed global species features not working, closes #810 2025-08-29 10:15:18 +10:00
Paul f21da15591 Fixed model sharing across posers causing cascading animation stacking 2025-08-29 09:53:39 +10:00
Polymeta 1be44c75d5 fix ride sound deserialization
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-08-29 00:50:30 +01:00
Jakotens cfea52f488 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-08-28 16:24:44 -06:00
Jakotens 53e94365ad Minor tweaks to Skiddo and Gogoat. 2025-08-28 16:24:34 -06:00
SamIr0n e393fd7a47 Teddiursa, Ursaring & Ursaluna Implementation
- Ride Mon Ursaluna
- Anims/Posers for all 3
- Fixed File Naming conventions
2025-08-28 22:57:24 +01:00
Polymeta 63eb0a1d9c Merge branch 'hoppers-like-vanilla' into 'main'
Revert Brewing Stand-Hopper interaction to Vanilla behavior

Closes #1658

See merge request cable-mc/cobblemon!1810
2025-08-28 21:29:55 +00:00
Alexis Salgueiro 680bc1d205 Translated using Weblate (French)
Currently translated at 100.0% (8870 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-08-28 20:17:51 +00:00
Jakotens 7a5908a0d3 Fixes to Braviary and Braviary-Hisuian posers. 2025-08-28 13:37:50 -06:00
Jakotens c234291e8c Temporary tweaks to Braviary and Braviary-Hisuian posers. Corrected sizes for Whirlipede and Scolipede. 2025-08-28 13:02:18 -06:00
Lennie b97115f1bd Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-08-28 19:55:24 +02:00
Lennie 8720a682c3 Created more personal space for the player when riding noivern 2025-08-28 19:55:10 +02:00
Sterrezo fd0d811b47 Retimed Noivern's wing flaps 2025-08-28 19:39:07 +02:00
Lennie 520341dfd3 Added animations, wing flap sounds and riding sounds for noivern.
More poser tweaks for zebstrika (he deserves it)
2025-08-28 19:14:17 +02:00
CeeJay d66b8d04c7 Change hopper removing from brewing stand to vanilla Minecraft
Resolves #1658
2025-08-28 11:51:40 -04:00
Tyzillion 5418407d1e Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into Mo-Functions-MOLANG 2025-08-28 09:55:19 -04:00
Kenji_64 4606d9e1c5 Magnezone flight animation added! 2025-08-28 15:54:31 +02:00
Kenji_64 4f839a2589 Added new animations for the Metagross line! 2025-08-28 15:54:01 +02:00
Kenji_64 257c362765 Added dive animation for Togekiss 2025-08-28 15:36:35 +02:00
Lennie 009a8c34e5 Tweaked seat positions for tropius and zebstrika
Small poser fixes to tropius, zebstrika and blitzle
2025-08-28 15:09:39 +02:00
Tyzillion 7e0ddb5b79 Now using readNullable and writeNullable. I made an attempt to fix the possible race condition but I don't know if it even fixed it. Removed the rideable check. I'll mess with that when I add the Riding flows. 2025-08-28 07:47:11 -04:00
Gertimoshka 8b5ef32d93 Fixed cooking pot JEI integration still allowing progress arrow to be pressed causing a crash without JEI 2025-08-28 14:22:11 +03:00
Polymeta 6acf2655b8 Merge branch 'main' into implement_pokemon 2025-08-28 12:16:35 +01:00
Lennie 0e40f0f3ad Updated changelog 2025-08-28 13:11:11 +02:00
Polymeta dfb2e21721 tweak golurk poser to show correct poses inside summary and pokedex GUI
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-08-28 12:10:36 +01:00
Polymeta 67cda56be5 Merge branch 'riding-scraper-2' into 'main'
Add scraper and results

See merge request cable-mc/cobblemon!1808
2025-08-28 11:06:48 +00:00
Polymeta b15545148b always run molang pose conditions instead of only when entity is attached, leaving control over this to the actual poser molang via q.has_entity
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-08-28 12:02:43 +01:00
Lennie b37a70c5db Updated changelog
Made unfezant un-ridable
Made kilowattrel ridable for testing purposes
Added ride_dive molang to tropius and kilowattrel posers
2025-08-28 12:58:40 +02:00
Landon Wills 0b9e0ac9c7 Add new scraped pokemon after implementation merge 2025-08-28 06:22:46 -04:00
Landon Wills f5383665f8 ridingSounds => rideSounds 2025-08-28 06:21:17 -04:00
Lennie 35bef5a008 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-08-28 12:17:51 +02:00
Lennie 17c25def97 Implemented unfezant line remodels
Implemented unfezant line animations
Made unfezant ridable for testing purposes
2025-08-28 12:14:10 +02:00
Landon Wills 5f9088dbf5 Merge branch 'implement_pokemon' into riding-scraper-2
# Conflicts:
#	common/src/main/resources/data/cobblemon/species/generation8a/wyrdeer.json
2025-08-28 06:11:56 -04:00
Landon Wills 8748ef6884 Add scraper and results 2025-08-28 06:09:01 -04:00
GenotypeXD-Dev ee270f59e1 Added Implemented Tag To Ho-oh 2025-08-28 04:42:31 -05:00
GenotypeXD-Dev a2c5866026 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-08-28 04:40:49 -05:00
GenotypeXD-Dev ab408c4246 Kentucky Fried Combusken 2025-08-28 04:40:21 -05:00
GenotypeXD-Dev 9e9a98b6f7 Implemented Ho-oh 2025-08-28 04:19:13 -05:00
violet 0b5fbb4d55 Added Luigi. 2025-08-28 05:07:25 -04:00
JazzMcNade d7a8e0fe92 Fix maths. Removed references to isSwimming b/c it empirically does not occur, even when the pokemon is actively traversing underwater. 2025-08-28 03:47:09 -05:00
JazzMcNade 3bd5867143 swimming wander can now target blocks without a solid below position. Fixed a bug in AABB.getRanges() for negative coordinates 2025-08-28 02:05:38 -05:00
Jakotens c8ab370720 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-08-27 20:49:09 -06:00
Jakotens 4a0ab9f092 Implemented animations and ride animations for Skiddo and Gogoat. Fixed ride animations for Braviary and Braviary-Hisuian. Implemented remodels and animations for Staryu, Starmie, Venipede, Whirlipede, and ride animations for Scolipede. 2025-08-27 20:49:02 -06:00
Jakotens a565f1cdb7 Revert "Implemented animations and ride animations for Skiddo and Gogoat. Fixed ride animations for Braviary and Braviary-Hisuian. Implemented remodels and animations for Staryu, Starmie, Venipede, Whirlipede, and ride animations for Scolipede."
This reverts commit 3a2075d00c.
2025-08-27 20:47:48 -06:00
Jakotens 3a2075d00c Implemented animations and ride animations for Skiddo and Gogoat. Fixed ride animations for Braviary and Braviary-Hisuian. Implemented remodels and animations for Staryu, Starmie, Venipede, Whirlipede, and ride animations for Scolipede. 2025-08-27 20:46:48 -06:00
Polymeta 9ef3caef53 Merge branch 'universal-healer-care' into 'main'
Healing at a healer heals PC Pokemon

Closes #731

See merge request cable-mc/cobblemon!1806
2025-08-27 21:07:28 +00:00
Ceejay c449b84fff Healing at a healer heals PC Pokemon 2025-08-27 21:07:27 +00:00
Kenji_64 bcf429589a Some extra poser adjustments for Galarian Corsola 2025-08-27 23:05:54 +02:00
Kenji_64 4282445359 Added riding molang to Wyrdeer 2025-08-27 22:26:04 +02:00
Kenji_64 3e449ae1f5 Yoshi's Island looked different- Tyrantrum is now rideable! (+ attack anims) 2025-08-27 22:24:36 +02:00
Lennie 8510290e87 Implemented tropius animation updates & flapping sounds. Updated zebstrika's ride molang implementation 2025-08-27 21:30:05 +02:00
Tyzillion c854fc5745 Added q.player.riding_pokemon Molang function. Made an attempt of adding a Molang check for if a Pokemon is rideable but no luck. 2025-08-27 15:01:41 -04:00
Tyzillion 1a3a6443d8 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into Mo-Functions-MOLANG 2025-08-27 14:58:36 -04:00
Kenji_64 b461dbce62 You unlocked Ariados as a mount! (+ new animations for the whole line) 2025-08-27 20:01:48 +02:00
Kenji_64 86e33b3ffd You unlocked Togekiss as a mount! 2025-08-27 19:56:05 +02:00
Kenji_64 78c34fb7d0 You unlocked Wyrdeer as a mount! (+ animation update) 2025-08-27 19:53:53 +02:00
Kenji_64 2ce993718b You unlocked Zebstrika as a mount! 2025-08-27 19:47:38 +02:00
Tyzillion eee3296120 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into implement_pokemon 2025-08-27 09:54:51 -04:00
Polymeta 9cf8b57457 Merge branch 'do-the-brew-fixes' into 'main'
Cobblemon Brewing Fixes

Closes #1656 and #1631

See merge request cable-mc/cobblemon!1805
2025-08-27 13:44:26 +00:00
Ceejay 208937defc Cobblemon Brewing Fixes 2025-08-27 13:44:25 +00:00
Polymeta 3e9b6cffdb Merge branch 'pot' into 'main'
Pot, PC

See merge request cable-mc/cobblemon!1800
2025-08-27 13:31:53 +00:00
JazzMcNade 2cf253a1fe Allow amphibious pokemon to wander water. Allow walking pokemon to wander air. 2025-08-27 02:57:58 -05:00
Tyzillion 29bb481363 Added q.pokemon.is_busy Molang Function. 2025-08-26 21:52:59 -04:00
Tyzillion 1f3589a592 Added q.entity.make_intangible() Molang Function. 2025-08-26 21:31:35 -04:00
Jakotens 66d697aa69 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-08-26 16:11:17 -06:00
Jakotens f93ebf19b0 Fixed Rufflet, Braviary, and Braviary-Hisuian not having their sleep poses set or resting behaviors defined. 2025-08-26 16:11:06 -06:00
Lennie b7a30d8f62 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-08-26 23:26:53 +02:00
Lennie e75e54e7ff Added seat_1 locator to kilowattrel and updated changelog 2025-08-26 23:25:45 +02:00
Kenji_64 65707cbfde Finalized Golurk's poser with attacks + minor fixes! 2025-08-26 23:01:00 +02:00
Lennie 680bc25983 Added animations for wattrel and kilowattrel 2025-08-26 22:46:21 +02:00
阿鱼 d0c4bd5a16 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 99.5% (8832 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-08-26 20:41:46 +00:00
Watuneni iteite 500069e5d8 Translated using Weblate (Japanese)
Currently translated at 85.2% (7561 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-08-26 20:41:45 +00:00
Lennie f4a1e67f9b Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-08-26 22:35:16 +02:00
Lennie c269a808ea Added animations for wattrel and kilowattrel 2025-08-26 22:34:15 +02:00
Jakotens dbec251408 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-08-26 14:18:24 -06:00
Jakotens 1f9466412b Implemented animations for Rufflet, Braviary, and Braviary (Hisuian). Also fixed sounds.json to correctly include Braviary (Hisuian)'s cry. 2025-08-26 14:18:01 -06:00
Lennie aa6038c704 Riding animation edits for zebstrika, and tweaks for blitzle 2025-08-26 21:16:00 +02:00
Tyzillion 0d5c84c396 Changed battle theme related soundevents to all be resourcelocations instead. No more casting, yayyyy!
q.force_evolve now does just that.
Moved some variables in Battle Builder.
Updated Changelogs!
2025-08-26 13:24:24 -04:00
Tyzillion dd9bc0d1d5 Merge remote-tracking branch 'origin/Mo-Functions-MOLANG' into Mo-Functions-MOLANG 2025-08-26 13:00:43 -04:00
Tyzillion 8aa00c1c31 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into Mo-Functions-MOLANG 2025-08-26 13:00:16 -04:00
Jakotens eaec3ea2c8 Added animations for Rufflet, Braviary, and, Braviary (Hisuian) 2025-08-26 10:30:12 -06:00
Jakotens efd32d2eee Merge branch 'implement_pokemon' of https://gitlab.com/cable-mc/cobblemon into implement_pokemon 2025-08-26 10:27:01 -06:00
Jakotens 49580bd65b Added animations for Rufflet, Braviary, and, Braviary (Hisuian) 2025-08-26 10:26:35 -06:00
Jakotens 7b761bfa8e Added animations for Rufflet, Braviary, and, Braviary (Hisuian) 2025-08-26 10:25:28 -06:00
Tyzillion caa46e8bbf Added spawn_npc Molang! 2025-08-25 17:55:41 -04:00
Nico Merenq 13ae174c66 Translated using Weblate (Russian)
Currently translated at 58.0% (5148 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-08-25 21:25:28 +00:00
Tyzillion 047172e30f Battle theme handling now uses ResourceLocation directly and added a Fading check to resolve issue of prior battle music instance not being reset for next battle. 2025-08-25 12:12:59 -04:00
Tyzillion 923d6f2a4d Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into Mo-Functions-MOLANG 2025-08-25 12:11:49 -04:00
Tyzillion 21646d4646 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into Mo-Functions-MOLANG 2025-08-25 09:44:17 -04:00
Test 11f7158e8e changelog 2025-08-24 17:22:15 -04:00
Test 5d503a90f7 add shading to berries on berry trees 2025-08-24 17:19:26 -04:00
Test 116db0aa7c update mark molang conditions 2025-08-24 16:59:57 -04:00
Test b90e700538 fixed background field, added textColor field for dialogues 2025-08-24 15:44:36 -04:00
Test 57372335b3 add alternate wallpapers, assets 2025-08-24 01:14:02 -04:00
Tyzillion cd06bc9903 Varibles goes brrrrrr 2025-08-23 17:53:22 -04:00
Tyzillion a2531d359a Merged in Amo's branch that allows resourcepack sounds in battle music. Made it compatible with BattleBuilder.kt. I think I fixed BattleMusicController.music only letting the battle theme play for one battle? 2025-08-23 16:50:52 -04:00
Tyzillion 1ee12c592b Merge remote-tracking branch 'origin/battle-music-fix' into Mo-Functions-MOLANG 2025-08-23 14:41:48 -04:00
Tyzillion f52e31c770 You can now add a battle theme to a player and NPC through Molang. 2025-08-23 12:41:21 -04:00
Test f5debdb14f update pot to use model+tint instead of texture atlas 2025-08-23 12:34:57 -04:00
Test 25544681ae update pot to use model+tint instead of texture atlas 2025-08-23 12:32:49 -04:00
Nico Merenq 3bf777c2a9 Translated using Weblate (Russian)
Currently translated at 58.0% (5147 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-08-23 10:45:45 +00:00
Tyzillion 529df4e8b4 Added Molang for checking if an entity is a certain thing and added the ability to apply, remove, and check effects of an entity. 2025-08-23 06:33:37 -04:00
Tyzillion d2bc8613c9 Added Molang evolution check and forcing an evolution. 2025-08-23 05:47:47 -04:00
Tyzillion 1706b7c516 Player Molang functions to control Battle Music. 2025-08-23 03:30:03 -04:00
Tyzillion c1263ad674 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into Mo-Functions-MOLANG 2025-08-23 02:52:27 -04:00
CeeJay 32ef93d79d update changelog 2025-08-22 22:19:30 +00:00
CeeJay 5a06602503 make sure form setting happens before checking ability legality for applying Pokemon properties 2025-08-22 22:19:30 +00:00
Tyzillion 24a68fefa7 Added more Molang functions.
You can now check if the player is in survival, seen the credits, is in a dialogue and you can obtain the active dialogue.
Also check if an entity is looking at a certain entity.
2025-08-22 17:43:55 -04:00
Tyzillion b0dec54576 Added a creative and spectator mode check for the player via Molang. 2025-08-22 16:31:10 -04:00
Tyzillion fb7712a5ce Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into Mo-Functions-MOLANG 2025-08-22 16:28:19 -04:00
Nico Merenq b5174686b4 Translated using Weblate (Russian)
Currently translated at 57.9% (5138 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-08-22 06:04:58 +00:00
Watuneni iteite 26a7bc29b3 Translated using Weblate (Japanese)
Currently translated at 85.1% (7555 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-08-22 06:04:57 +00:00
Nico Merenq 05cd37eb38 Translated using Weblate (Russian)
Currently translated at 57.9% (5136 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-08-20 23:46:22 +00:00
Nico Merenq 3ac3fcbcda Translated using Weblate (Russian)
Currently translated at 57.4% (5092 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-08-18 18:30:18 +00:00
Tyzillion fa07b93d54 Splitting an atom has never been easier. 2025-08-17 18:39:02 -04:00
Weblate 27b3884f27 Update translation files
Updated by "Remove blank strings" add-on in Weblate.

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/
2025-08-16 21:52:57 +00:00
dhi_holo 15e8f46702 Translated using Weblate (English (en_PT))
Currently translated at 24.6% (2189 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/en_PT/
2025-08-16 21:52:53 +00:00
dhi_holo e91b1061d3 Translated using Weblate (English (United Kingdom))
Currently translated at 0.8% (71 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/en_GB/
2025-08-16 21:52:51 +00:00
阿鱼 f042d167d1 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 99.5% (8832 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-08-14 07:35:19 +00:00
Watuneni iteite eff3bd91d7 Translated using Weblate (Japanese)
Currently translated at 84.9% (7538 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-08-13 11:19:51 +00:00
JPAKx4 50cb670eee Merge remote-tracking branch 'origin/mo-molang-mo-problems' into mo-molang-mo-problems
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/molang/MoLangFunctions.kt
2025-08-11 11:33:57 -05:00
JPAK cfd3eb6c4c Add ability to access a global MoLang Store (multiserver for example) 2025-08-11 11:33:27 -05:00
Nico Merenq 1391bfee01 Translated using Weblate (Russian)
Currently translated at 56.9% (5054 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-08-10 10:56:14 +00:00
Watuneni iteite e8b6bee139 Translated using Weblate (Japanese)
Currently translated at 84.9% (7538 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-08-09 09:35:43 +00:00
Nico Merenq a98bc80005 Translated using Weblate (Russian)
Currently translated at 56.9% (5052 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-08-08 15:26:11 +00:00
Zack the Ghast 06d25a043c Translated using Weblate (Portuguese (Portugal))
Currently translated at 68.7% (6097 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2025-08-08 02:52:08 +00:00
Zack the Ghast 6aa3b8b92f Translated using Weblate (Portuguese (Portugal))
Currently translated at 68.5% (6082 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2025-08-08 00:40:47 +00:00
Nico Merenq 7381b47830 Translated using Weblate (Russian)
Currently translated at 56.1% (4981 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-08-07 13:28:54 +00:00
Watuneni iteite b2a704fa12 Translated using Weblate (Japanese)
Currently translated at 84.8% (7528 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-08-07 13:28:52 +00:00
Zack the Ghast 41ed476c00 Translated using Weblate (Portuguese (Portugal))
Currently translated at 68.4% (6072 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2025-08-07 04:26:31 +00:00
Nico Merenq 44ddf097d6 Translated using Weblate (Russian)
Currently translated at 55.7% (4948 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-08-06 20:04:57 +00:00
Zack the Ghast cb1386fdb3 Translated using Weblate (Portuguese (Portugal))
Currently translated at 68.2% (6051 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2025-08-06 20:04:56 +00:00
Nico Merenq 0dafe79032 Translated using Weblate (Russian)
Currently translated at 55.3% (4911 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-08-06 14:49:22 +00:00
Nico Merenq 51cd929d1c Translated using Weblate (Russian)
Currently translated at 55.3% (4911 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-08-06 12:06:36 +00:00
Nico Merenq 5468d5e888 Translated using Weblate (Russian)
Currently translated at 54.6% (4851 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-08-06 11:23:20 +00:00
Nico Merenq 0174b8bc67 Translated using Weblate (Russian)
Currently translated at 54.5% (4840 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-08-06 11:18:06 +00:00
Watuneni iteite e3b8fbb973 Translated using Weblate (Japanese)
Currently translated at 84.8% (7527 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-08-06 11:18:05 +00:00
Nico Merenq 7103df0116 Translated using Weblate (Russian)
Currently translated at 53.5% (4752 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-08-06 10:25:46 +00:00
Zack the Ghast 42fba85241 Translated using Weblate (Portuguese (Portugal))
Currently translated at 68.2% (6050 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2025-08-06 10:25:45 +00:00
Watuneni iteite f9f11c7137 Translated using Weblate (Japanese)
Currently translated at 84.0% (7452 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-08-06 10:25:45 +00:00
Zack the Ghast a51acead21 Translated using Weblate (Portuguese (Portugal))
Currently translated at 68.1% (6048 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2025-08-06 08:53:38 +00:00
Watuneni iteite 7a3ab17e57 Translated using Weblate (Japanese)
Currently translated at 82.0% (7280 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-08-06 08:53:37 +00:00
Watuneni iteite d4e741c614 Translated using Weblate (Japanese)
Currently translated at 81.7% (7248 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-08-06 08:11:59 +00:00
Watuneni iteite bee1797ff5 Translated using Weblate (Japanese)
Currently translated at 81.7% (7247 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-08-06 08:06:41 +00:00
Watuneni iteite 72fa7d42d5 Translated using Weblate (Japanese)
Currently translated at 81.6% (7243 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-08-06 07:47:36 +00:00
Zack the Ghast 7a4bf4cc5d Translated using Weblate (Portuguese (Portugal))
Currently translated at 68.1% (6046 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2025-08-06 01:23:25 +00:00
Zack the Ghast ab66e13c8c Translated using Weblate (Portuguese (Portugal))
Currently translated at 68.0% (6038 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2025-08-06 00:45:38 +00:00
Zack the Ghast eaeea493a1 Translated using Weblate (Portuguese (Portugal))
Currently translated at 67.8% (6022 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2025-08-05 23:49:36 +00:00
Nico Merenq 0bfaedbf6e Translated using Weblate (Russian)
Currently translated at 53.5% (4752 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-08-05 20:57:37 +00:00
Zack the Ghast 52f9f45608 Translated using Weblate (Portuguese (Portugal))
Currently translated at 67.8% (6016 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2025-08-05 20:57:36 +00:00
Zack the Ghast dfa9dc1e2b Translated using Weblate (Portuguese (Portugal))
Currently translated at 67.5% (5992 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2025-08-05 12:37:13 +00:00
Nico Merenq c8f5538ae8 Translated using Weblate (Russian)
Currently translated at 53.3% (4728 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-08-05 12:12:59 +00:00
Nico Merenq 5af5a59ff1 Translated using Weblate (Russian)
Currently translated at 52.7% (4679 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-08-05 10:18:42 +00:00
Zack the Ghast eb37ecbe56 Translated using Weblate (Portuguese (Portugal))
Currently translated at 67.5% (5991 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2025-08-05 03:22:34 +00:00
Zack the Ghast d7b528aef1 Translated using Weblate (Portuguese (Portugal))
Currently translated at 67.4% (5981 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2025-08-05 02:27:49 +00:00
Nico Merenq 2908ca3a89 Translated using Weblate (Russian)
Currently translated at 52.5% (4660 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-08-04 23:50:47 +00:00
Nico Merenq aa04dc077b Translated using Weblate (Russian)
Currently translated at 52.5% (4657 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-08-04 21:31:48 +00:00
Nico Merenq c1c064e657 Translated using Weblate (Russian)
Currently translated at 52.4% (4653 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-08-04 20:35:53 +00:00
washashu0905 c0a509820f Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 95.8% (8498 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hant/
2025-08-04 20:30:32 +00:00
Nico Merenq aa0b71aad7 Translated using Weblate (Russian)
Currently translated at 52.4% (4651 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-08-04 20:30:31 +00:00
Nico Merenq 55c5dfddc4 Translated using Weblate (Russian)
Currently translated at 51.9% (4610 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-08-04 16:16:22 +00:00
Nico Merenq b40c69a1d9 Translated using Weblate (Russian)
Currently translated at 51.9% (4610 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-08-04 16:15:06 +00:00
Watuneni iteite ca20f432b3 Translated using Weblate (Japanese)
Currently translated at 81.6% (7238 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-08-04 16:15:05 +00:00
Watuneni iteite 63fe61b913 Translated using Weblate (Japanese)
Currently translated at 80.9% (7177 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-08-04 15:53:50 +00:00
Nico Merenq 7605b526c8 Translated using Weblate (Russian)
Currently translated at 51.9% (4606 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-08-04 15:53:34 +00:00
Watuneni iteite 189f39f1c1 Translated using Weblate (Japanese)
Currently translated at 80.9% (7176 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-08-04 15:53:33 +00:00
Nico Merenq 8c718069c7 Translated using Weblate (Russian)
Currently translated at 51.7% (4586 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-08-04 14:15:27 +00:00
Nico Merenq c2facdf5ce Translated using Weblate (Russian)
Currently translated at 51.1% (4539 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-08-04 10:23:03 +00:00
Watuneni iteite 0c5dcb26f6 Translated using Weblate (Japanese)
Currently translated at 79.8% (7084 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-08-04 10:23:02 +00:00
Watuneni iteite b88634691b Translated using Weblate (Japanese)
Currently translated at 79.8% (7082 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-08-04 08:28:46 +00:00
Watuneni iteite df9dcdd622 Translated using Weblate (Japanese)
Currently translated at 79.7% (7070 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-08-04 07:51:11 +00:00
Watuneni iteite 15943fae47 Translated using Weblate (Japanese)
Currently translated at 79.6% (7069 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-08-04 07:50:58 +00:00
Watuneni iteite 78a71ba252 Translated using Weblate (Japanese)
Currently translated at 79.6% (7064 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-08-04 07:46:32 +00:00
Watuneni iteite 7f1acd16b5 Translated using Weblate (Japanese)
Currently translated at 79.5% (7060 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-08-04 07:43:04 +00:00
Watuneni iteite be038d2348 Translated using Weblate (Japanese)
Currently translated at 78.9% (7004 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-08-04 06:54:08 +00:00
阿鱼 b3b11012f4 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 99.5% (8832 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-08-04 03:55:56 +00:00
Watuneni iteite 095c466b89 Translated using Weblate (Japanese)
Currently translated at 78.8% (6992 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-08-03 19:29:01 +00:00
Watuneni iteite 4c3cd6fdc4 Translated using Weblate (Japanese)
Currently translated at 78.2% (6943 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-08-03 17:05:25 +00:00
Watuneni iteite 2379b22135 Translated using Weblate (Japanese)
Currently translated at 77.7% (6893 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-08-03 16:18:02 +00:00
Watuneni iteite f36fa861da Translated using Weblate (Japanese)
Currently translated at 77.5% (6876 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-08-03 16:12:31 +00:00
Watuneni iteite 3b4f9d0fba Translated using Weblate (Japanese)
Currently translated at 77.0% (6838 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-08-03 15:47:39 +00:00
Watuneni iteite 42dabbf16a Translated using Weblate (Japanese)
Currently translated at 75.6% (6714 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-08-03 14:46:18 +00:00
Watuneni iteite 07abff7583 Translated using Weblate (Japanese)
Currently translated at 75.1% (6665 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-08-03 13:05:07 +00:00
Nico Merenq dbd9bfacc5 Translated using Weblate (Russian)
Currently translated at 50.5% (4486 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-08-03 11:59:16 +00:00
Nico Merenq 6ed1ea0cad Translated using Weblate (Russian)
Currently translated at 50.5% (4486 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-08-03 11:33:17 +00:00
Yasito 5384fec57e Translated using Weblate (French)
Currently translated at 100.0% (8870 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-08-03 04:33:05 +00:00
Yasito 1868bcf4d7 Translated using Weblate (French (Canada))
Currently translated at 100.0% (8870 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-08-03 04:33:04 +00:00
Yasito 42059b9931 Translated using Weblate (French)
Currently translated at 100.0% (8870 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-08-03 04:02:52 +00:00
Yasito 24890225a2 Translated using Weblate (French (Canada))
Currently translated at 100.0% (8870 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-08-03 04:02:52 +00:00
Yasito e4e32769dc Translated using Weblate (French)
Currently translated at 100.0% (8870 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-08-03 03:50:45 +00:00
Yasito 3420422a4b Translated using Weblate (French (Canada))
Currently translated at 100.0% (8870 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-08-03 03:50:44 +00:00
Yasito 18564baec7 Translated using Weblate (French)
Currently translated at 100.0% (8870 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-08-03 03:43:40 +00:00
Yasito b0c6d83913 Translated using Weblate (French (Canada))
Currently translated at 100.0% (8870 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-08-03 03:43:39 +00:00
Yasito 5016b2b227 Translated using Weblate (French)
Currently translated at 99.8% (8860 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-08-03 03:38:25 +00:00
Yasito 4727321d47 Translated using Weblate (French)
Currently translated at 99.8% (8857 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-08-03 03:37:46 +00:00
Yasito 6e96e92d33 Translated using Weblate (French (Canada))
Currently translated at 100.0% (8870 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-08-03 03:37:45 +00:00
Yasito a8c92ce641 Translated using Weblate (French)
Currently translated at 99.8% (8854 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-08-03 03:32:47 +00:00
Yasito 032d2e5c95 Translated using Weblate (French (Canada))
Currently translated at 100.0% (8870 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-08-03 03:32:46 +00:00
Yasito ec9d7d9dae Translated using Weblate (French)
Currently translated at 99.8% (8853 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-08-03 03:26:48 +00:00
Yasito 217dab4dea Translated using Weblate (French (Canada))
Currently translated at 100.0% (8870 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-08-03 03:26:47 +00:00
Yasito 452a92daa9 Translated using Weblate (French (Canada))
Currently translated at 99.9% (8862 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-08-03 03:12:08 +00:00
Yasito fb364deff9 Translated using Weblate (French)
Currently translated at 99.5% (8831 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-08-03 03:06:55 +00:00
Yasito ac824ce39c Translated using Weblate (French (Canada))
Currently translated at 99.8% (8859 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-08-03 03:06:54 +00:00
Nico Merenq fa74a1c45a Translated using Weblate (Russian)
Currently translated at 50.5% (4480 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-08-02 13:19:58 +00:00
Nico Merenq 1cce1d1a44 Translated using Weblate (Russian)
Currently translated at 50.1% (4451 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-08-02 12:39:24 +00:00
Yasito b7f537e34c Translated using Weblate (French)
Currently translated at 99.5% (8830 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-31 04:25:44 +00:00
Yasito 0311a8a4ef Translated using Weblate (French (Canada))
Currently translated at 99.5% (8830 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-07-31 04:25:43 +00:00
Yasito 14ecc13cb3 Translated using Weblate (French)
Currently translated at 99.5% (8830 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-31 03:53:38 +00:00
Yasito 5baca56d6d Translated using Weblate (French (Canada))
Currently translated at 99.5% (8830 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-07-31 03:53:37 +00:00
Yasito 91c9692db1 Translated using Weblate (French)
Currently translated at 99.5% (8830 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-31 03:52:50 +00:00
Yasito bb7b1a8dee Translated using Weblate (French (Canada))
Currently translated at 99.5% (8830 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-07-31 03:52:49 +00:00
Yasito e6322e5fb7 Translated using Weblate (French)
Currently translated at 99.1% (8798 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-31 03:42:06 +00:00
Yasito 3c5f04b52a Translated using Weblate (French (Canada))
Currently translated at 99.4% (8825 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-07-31 03:42:05 +00:00
Yasito 5d78a5b573 Translated using Weblate (French)
Currently translated at 99.1% (8796 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-31 03:39:37 +00:00
Yasito 4f58615065 Translated using Weblate (French (Canada))
Currently translated at 99.4% (8823 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-07-31 03:39:36 +00:00
Yasito 32a4161a69 Translated using Weblate (French)
Currently translated at 99.1% (8793 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-30 16:09:48 +00:00
Yasito c5708145cd Translated using Weblate (French (Canada))
Currently translated at 99.1% (8793 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-07-30 16:09:47 +00:00
Kakuno 118f207adb Translated using Weblate (French)
Currently translated at 98.7% (8762 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-30 03:59:57 +00:00
Yasito a5826028b3 Translated using Weblate (French)
Currently translated at 98.7% (8762 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-30 03:59:57 +00:00
Yasito fa3f6053c3 Translated using Weblate (French (Canada))
Currently translated at 98.7% (8762 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-07-30 03:59:56 +00:00
Kakuno bc60fe58b1 Translated using Weblate (French)
Currently translated at 98.7% (8756 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-30 03:51:13 +00:00
Yasito f9a5e9a9e4 Translated using Weblate (French)
Currently translated at 98.7% (8756 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-30 03:51:13 +00:00
Yasito 6f6ee6f160 Translated using Weblate (French (Canada))
Currently translated at 98.7% (8755 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-07-30 03:51:12 +00:00
Kakuno 8a3dbd5e4a Translated using Weblate (French)
Currently translated at 98.6% (8754 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-30 03:48:18 +00:00
Yasito e052ad2281 Translated using Weblate (French)
Currently translated at 98.6% (8754 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-30 03:48:17 +00:00
Yasito 8938ea13a4 Translated using Weblate (French (Canada))
Currently translated at 98.6% (8753 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-07-30 03:48:17 +00:00
Yasito 907aed4c63 Translated using Weblate (French)
Currently translated at 98.6% (8752 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-30 03:45:17 +00:00
Kakuno 60798aae03 Translated using Weblate (French)
Currently translated at 98.6% (8752 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-30 03:45:16 +00:00
Yasito 6f09957fbf Translated using Weblate (French (Canada))
Currently translated at 98.6% (8751 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-07-30 03:45:16 +00:00
Kakuno 6fc2344473 Translated using Weblate (French)
Currently translated at 98.5% (8741 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-30 03:38:53 +00:00
Yasito c82059ae72 Translated using Weblate (French)
Currently translated at 98.5% (8741 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-30 03:38:53 +00:00
Yasito a080db690f Translated using Weblate (French (Canada))
Currently translated at 98.5% (8740 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-07-30 03:38:53 +00:00
Kakuno d82849ddd3 Translated using Weblate (French)
Currently translated at 98.5% (8740 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-30 03:36:11 +00:00
Yasito 71a3fe6e48 Translated using Weblate (French)
Currently translated at 98.5% (8740 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-30 03:36:11 +00:00
Yasito 5e934ac7a6 Translated using Weblate (French (Canada))
Currently translated at 98.5% (8739 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-07-30 03:36:10 +00:00
Natchakot Chuchokjatuporn 6aa5bde424 Translated using Weblate (Thai)
Currently translated at 54.3% (4818 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/th/
2025-07-30 03:22:26 +00:00
Kakuno fee7488090 Translated using Weblate (French)
Currently translated at 98.3% (8726 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-30 03:22:25 +00:00
Yasito f5e5e3411e Translated using Weblate (French (Canada))
Currently translated at 98.3% (8725 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-07-30 03:22:24 +00:00
Natchakot Chuchokjatuporn 818f9365ec Translated using Weblate (Thai)
Currently translated at 52.6% (4669 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/th/
2025-07-29 03:34:54 +00:00
Kakuno f81db9df3d Translated using Weblate (French)
Currently translated at 98.3% (8722 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-29 03:34:52 +00:00
Yasito 8162e3a30d Translated using Weblate (French)
Currently translated at 98.3% (8722 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-29 03:34:52 +00:00
Yasito 5a00bbf02d Translated using Weblate (French (Canada))
Currently translated at 98.3% (8722 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-07-29 03:34:50 +00:00
Natchakot Chuchokjatuporn ff7fff52ec Translated using Weblate (Thai)
Currently translated at 52.0% (4620 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/th/
2025-07-29 03:23:48 +00:00
Kakuno 087fab4065 Translated using Weblate (French)
Currently translated at 98.2% (8719 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-29 03:23:48 +00:00
Yasito bed2897950 Translated using Weblate (French)
Currently translated at 98.2% (8719 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-29 03:23:47 +00:00
Yasito d51f0711b7 Translated using Weblate (French (Canada))
Currently translated at 98.2% (8719 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-07-29 03:23:46 +00:00
Felipe Bermudes Ormeño 728860332d Translated using Weblate (Spanish (Mexico))
Currently translated at 26.9% (2390 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es_MX/
2025-07-29 03:23:45 +00:00
Felipe Bermudes Ormeño fe0ca14b3b Translated using Weblate (Spanish (Mexico))
Currently translated at 26.2% (2330 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es_MX/
2025-07-28 23:07:55 +00:00
Yasito 7e57c3c862 Translated using Weblate (French)
Currently translated at 96.7% (8586 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-28 17:41:18 +00:00
Yasito 0f4fdfcedd Translated using Weblate (French (Canada))
Currently translated at 96.7% (8586 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-07-28 17:41:17 +00:00
Felipe Bermudes Ormeño 1cb77f35b5 Translated using Weblate (Spanish (Mexico))
Currently translated at 25.3% (2247 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es_MX/
2025-07-28 17:41:16 +00:00
阿鱼 9ac23be59b Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 99.5% (8832 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-07-28 01:58:42 +00:00
Kakuno e469409a22 Translated using Weblate (French)
Currently translated at 96.6% (8572 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-28 01:58:41 +00:00
Yasito 28db8e98d7 Translated using Weblate (French)
Currently translated at 96.6% (8572 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-28 01:58:41 +00:00
Yasito 08bf3d4f8f Translated using Weblate (French (Canada))
Currently translated at 96.6% (8572 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-07-28 01:58:39 +00:00
Felipe Bermudes Ormeño 88380bfb76 Translated using Weblate (Spanish (Mexico))
Currently translated at 25.2% (2240 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es_MX/
2025-07-28 01:58:38 +00:00
Kakuno 83d7cbbefa Translated using Weblate (French)
Currently translated at 96.0% (8524 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-27 23:23:04 +00:00
Yasito 426d7c8e5e Translated using Weblate (French)
Currently translated at 96.0% (8524 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-27 23:23:04 +00:00
Yasito 602d88db5e Translated using Weblate (French (Canada))
Currently translated at 95.8% (8506 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-07-27 23:23:03 +00:00
Felipe Bermudes Ormeño 751d3be872 Translated using Weblate (Spanish (Mexico))
Currently translated at 25.0% (2220 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es_MX/
2025-07-27 23:23:00 +00:00
Kakuno 358a62d5b1 Translated using Weblate (French)
Currently translated at 96.0% (8521 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-27 22:45:12 +00:00
Yasito 409814823b Translated using Weblate (French (Canada))
Currently translated at 95.7% (8490 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-07-27 22:45:11 +00:00
Kakuno 5d8f45b058 Translated using Weblate (French)
Currently translated at 96.0% (8520 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-27 22:42:03 +00:00
Yasito 3001e8bf84 Translated using Weblate (French (Canada))
Currently translated at 95.6% (8488 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-07-27 22:42:02 +00:00
阿鱼 80de268987 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 99.5% (8832 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-07-27 22:40:38 +00:00
Kakuno b08004b6c6 Translated using Weblate (French)
Currently translated at 96.0% (8519 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-27 22:40:37 +00:00
Yasito 2783a52495 Translated using Weblate (French)
Currently translated at 96.0% (8519 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-27 22:40:36 +00:00
Yasito 3ae3f0db95 Translated using Weblate (French (Canada))
Currently translated at 95.6% (8486 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-07-27 22:40:35 +00:00
阿鱼 a15f026739 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 99.5% (8832 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-07-27 18:38:05 +00:00
阿鱼 bc8ed8bb38 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 98.9% (8776 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-07-27 16:54:12 +00:00
阿鱼 efa246272b Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 98.7% (8758 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-07-27 05:55:51 +00:00
阿鱼 e29d84a71a Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 97.1% (8617 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-07-26 18:52:35 +00:00
阿鱼 6c671b5e08 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 96.9% (8598 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-07-25 22:35:43 +00:00
Weblate 3ffd9ecc28 Update translation files
Updated by "Remove blank strings" add-on in Weblate.

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/
2025-07-25 07:41:41 +00:00
阿鱼 281131f5b3 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 96.3% (8543 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-07-25 07:41:37 +00:00
Nico Merenq 3c67cd2a59 Translated using Weblate (Russian)
Currently translated at 49.7% (4411 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-07-25 07:41:36 +00:00
阿鱼 74f5c69bb5 Translated using Weblate (French)
Currently translated at 95.9% (8513 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-25 07:41:35 +00:00
Teggy 8f82e1d960 Translated using Weblate (Spanish)
Currently translated at 85.3% (7567 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-07-25 07:41:34 +00:00
Nico Merenq c5497cfb2b Translated using Weblate (Russian)
Currently translated at 49.6% (4406 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-07-22 22:10:58 +00:00
Nico Merenq 94b6b3b948 Translated using Weblate (Russian)
Currently translated at 49.6% (4405 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-07-22 21:58:42 +00:00
Kakuno 8855c5ad7f Translated using Weblate (French)
Currently translated at 95.9% (8513 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-22 15:14:45 +00:00
Alexis Salgueiro 5015a0a56c Translated using Weblate (French)
Currently translated at 95.9% (8513 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-22 15:14:44 +00:00
Kakuno ec51fc0701 Translated using Weblate (French)
Currently translated at 95.9% (8512 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-22 14:33:22 +00:00
Alexis Salgueiro 13287e6f76 Translated using Weblate (French)
Currently translated at 95.9% (8512 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-22 14:33:22 +00:00
Kakuno a65dfcd3cd Translated using Weblate (French)
Currently translated at 95.9% (8509 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-22 14:28:56 +00:00
Kakuno 18183dd649 Translated using Weblate (French)
Currently translated at 95.9% (8508 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-22 14:26:11 +00:00
Alexis Salgueiro 40c1f074bb Translated using Weblate (French)
Currently translated at 95.9% (8508 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-22 14:26:10 +00:00
Kakuno 5d1219e467 Translated using Weblate (French)
Currently translated at 95.6% (8482 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-22 14:18:30 +00:00
Kakuno 568773f1a8 Translated using Weblate (French)
Currently translated at 95.6% (8481 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-22 14:18:08 +00:00
Alexis Salgueiro f0c3cde213 Translated using Weblate (French)
Currently translated at 95.6% (8481 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-07-22 14:18:08 +00:00
Tobias (Targ) def5d09903 Translated using Weblate (German)
Currently translated at 58.8% (5218 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/de/
2025-07-18 13:37:42 +00:00
Watuneni iteite ad5e2ddc41 Translated using Weblate (Japanese)
Currently translated at 75.0% (6661 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-07-16 08:59:20 +00:00
JPAK 38cfd7eca8 Add ability to access a global MoLang Store (multiserver for example) 2025-07-15 15:03:04 -05:00
Teggy 2d2f92c7a7 Translated using Weblate (Spanish)
Currently translated at 84.2% (7470 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-07-15 18:19:05 +00:00
JPAK 2ba2eea801 Add functions to remove entity and spawn NPCs for Molangs 2025-07-13 13:16:44 -05:00
Watuneni iteite 2bd4d1fb08 Translated using Weblate (Japanese)
Currently translated at 74.0% (6565 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-07-13 18:01:49 +00:00
Teggy 4c342c2047 Translated using Weblate (Spanish)
Currently translated at 83.1% (7373 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-07-13 18:01:48 +00:00
Watuneni iteite 24cee748d5 Translated using Weblate (Japanese)
Currently translated at 73.8% (6548 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-07-10 18:47:55 +00:00
Teggy fbec6f03e7 Translated using Weblate (Spanish)
Currently translated at 81.1% (7199 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-07-10 18:47:52 +00:00
Teggy 8ab0288ab2 Translated using Weblate (Spanish)
Currently translated at 79.7% (7072 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-07-10 06:04:53 +00:00
Nico Merenq cc426a5ca9 Translated using Weblate (Russian)
Currently translated at 49.6% (4403 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-07-08 22:17:00 +00:00
Teggy e4f04b605a Translated using Weblate (Spanish)
Currently translated at 79.1% (7025 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-07-08 22:16:58 +00:00
Teggy 48441b1232 Translated using Weblate (Spanish)
Currently translated at 76.0% (6748 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-07-06 09:38:57 +00:00
Watuneni iteite 589d3614b8 Translated using Weblate (Japanese)
Currently translated at 68.7% (6099 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-07-05 04:12:04 +00:00
Teggy fe386095de Translated using Weblate (Spanish)
Currently translated at 75.3% (6687 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-07-04 22:04:28 +00:00
Watuneni iteite 5640177c99 Translated using Weblate (Japanese)
Currently translated at 68.7% (6097 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-07-04 11:45:00 +00:00
Zack the Ghast ff8c8b1bfa Translated using Weblate (Portuguese (Portugal))
Currently translated at 67.3% (5973 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2025-07-04 05:53:12 +00:00
Watuneni iteite 4bccd9b5aa Translated using Weblate (Japanese)
Currently translated at 64.2% (5702 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-07-04 05:53:11 +00:00
阿鱼 8c1c5ac957 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 95.8% (8498 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-07-03 05:30:56 +00:00
Watuneni iteite a20ec4d1c5 Translated using Weblate (Japanese)
Currently translated at 64.1% (5694 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-07-03 05:30:55 +00:00
Teggy 12b4e58dea Translated using Weblate (Spanish)
Currently translated at 75.1% (6669 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-07-03 05:30:53 +00:00
Zack the Ghast 940ec82182 Translated using Weblate (Portuguese (Portugal))
Currently translated at 67.1% (5952 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2025-07-02 02:26:55 +00:00
Kyo 67c01ce5f2 Translated using Weblate (Vietnamese)
Currently translated at 65.8% (5842 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/vi/
2025-07-01 12:38:56 +00:00
washashu0905 e603325eef Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 94.6% (8398 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hant/
2025-07-01 12:38:54 +00:00
阿鱼 afd09e93eb Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 95.7% (8497 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-07-01 12:38:53 +00:00
Zack the Ghast 92775a4b8a Translated using Weblate (Portuguese (Portugal))
Currently translated at 66.2% (5877 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2025-07-01 12:38:52 +00:00
Watuneni iteite 6a2668171e Translated using Weblate (Japanese)
Currently translated at 63.2% (5607 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-07-01 12:38:50 +00:00
Teggy 5b7dc9f8f4 Translated using Weblate (Spanish)
Currently translated at 74.2% (6583 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-07-01 12:38:47 +00:00
washashu0905 75dc76020f Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 92.5% (8209 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hant/
2025-06-29 06:11:55 +00:00
阿鱼 56289ce5bf Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 95.7% (8496 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-06-29 06:11:53 +00:00
Zack the Ghast e4ec7b0ae7 Translated using Weblate (Portuguese (Portugal))
Currently translated at 60.7% (5387 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2025-06-29 06:11:52 +00:00
Anonymous f38ef0b6b9 Translated using Weblate (Vietnamese)
Currently translated at 63.0% (5592 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/vi/
2025-06-29 01:50:06 +00:00
阿鱼 d5bb3e2d97 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 95.3% (8458 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-06-29 01:50:04 +00:00
Zack the Ghast 66f6555711 Translated using Weblate (Portuguese (Portugal))
Currently translated at 60.4% (5362 of 8870 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2025-06-29 01:50:00 +00:00
Zack the Ghast 472ca9c748 Translated using Weblate (Portuguese (Portugal))
Currently translated at 63.4% (5398 of 8505 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2025-06-29 00:30:29 +00:00
阿鱼 054e4dffdc Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 98.2% (8358 of 8505 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-06-29 00:30:29 +00:00
Paul be5ee8a6a3 Potentially got root bone animation working for players on pokes 2025-05-16 22:22:05 +10:00
amo 44c27be590 oops 2025-05-08 17:53:42 +01:00
amo a704972dea oops 2025-05-06 04:33:11 +01:00
amo 0b43342a5c fix battle music attempting to use registered sound events instead of just creating and using one from the id (allows resourcepackable etc) 2025-05-06 04:32:59 +01:00
2530 changed files with 513128 additions and 72430 deletions

No files matched your search

+3
View File
@@ -15,6 +15,9 @@
- baseScale in NPCs has been removed from classes and presets, and in the entity it is replaced with hitboxScale and renderScale.
- Added `EntityCallbacks` to `NPCEntity` and `PokemonEntity`. You can trigger custom callback types by using `[Entity].callbacks.process(...)`
### Fixes
- Fixed Pokémon with a moving walk target frequently spinning in place as they re-calculate their path.
### MoLang & Datapacks
- Added a `behaviours` datapack folder which motivates the Behaviour Editor screen on NPCs and Pokémon.
- Added "callback" support on PokemonEntity and NPCEntity that allows MoLang scripts to be executed in response to certain scenarios.
+2 -1
View File
@@ -1,6 +1,7 @@
### Additions
- Added functionality for marks.
- Added data for all marks and all title ribbons from Pokémon.
- Added data for all marks and ribbons from the mainline Pokémon games.
- Implemented fishing, personality, weather, and time related marks, that can rarely be granted to a wild-spawned Pokémon.
- Added marks screen within the summary UI.
- Added `/givemark <player> <partySlot> <mark>` command to give a mark to a Pokémon.
- Added `/takemark <player> <partySlot> <mark>` command to remove a mark from a Pokémon.
+11 -2
View File
@@ -1,19 +1,28 @@
### Additions
- Added ability to rename PC Boxes by clicking on the name of a box.
- Added box options buttons for boxes, toggleable by clicking the right icon button in the bottom bar.
- The button on the right allows for changing the box wallpaper.
- The option buttons on the left side allow for sorting the box by name, level, Pokédex number, gender, and type. Shift clicking allows for sorting in reverse order.
- The button on the right allows for changing the box wallpaper.
- 10 additional color-based wallpapers have been added.
- 5 unlockable biome-based wallpapers have been added, which can be unlocked by visiting:
- Forest biome
- Ocean biome
- Cave Biome
- The End
- The Nether
- Added filter functionality in PC UI, which supports `PokemonProperties` (e.g. `shiny=yes` shows all shiny Pokémon).
- Names can be filtered by exact or partial matches, e.g. entering "cha" will show Charmander, Charmeleon, etc.
- Added `/changewallpaper <player> <boxNumber> <wallpaper>` command to change a box wallpaper through commands.
- Added `/renamebox <player> <boxNumber> <name>` command to rename a PC box through commands.
- Added IVs and EVs stat displays in PC.
- Added ability to change boxes by scrolling.
- The displays can be cycled through by scrolling when the display is hovered over.
- Added ability to cycle through boxes by scrolling when the box is hovered over.
- PC will now open to last box viewed within a session.
### Changes
- Slots in pastures will now have darker backgrounds and italicised Pokémon names if the Pokémon is not owned by the player.
- The pasture slot will show the name of the Pokémon owner when hovered if the Pokémon is not owned.
- Increased the default PC box count from 30 to 40 to accommodate the fact that we have way more Pokémon now. If you've changed this config option then you'll need to edit it again after starting up with 1.7!
### Developer
- Added `RenamePCBoxEvent.Pre` and `RenamePCBoxEvent.Post` events to prevent players from renaming a box or changing their input.
+1
View File
@@ -4,6 +4,7 @@
### Fixes
- Fixed the "enabled" property in spawn files not actually being respected. Where do they find these devs?
- Fixed Terralith's shrubland not counting as plains for spawning purposes the way it was intended to.
### Developer
- Spawning Influences now have the context of what the other buckets are when adjusting bucket weights. This will break existing influences that do bucket weight adjustment.
+312 -12
View File
@@ -2,6 +2,7 @@
## [1.7.0 (Month xth, 2025)](#1-7-0)
### Additions
- Added `/npcdelete` uuid parameter support and usuable to console
- Added `/spectateBattle <player>` command to spectate battles without having to manually walk up to the target.
- Added an in-game configuration screen, allowing all settings from `main.json` to be edited directly in-game.
- Added `/cobblemonconfig reload` command to reload `main.json` configuration. **Note:** Some settings require a server restart to take effect; use this command cautiously.
@@ -11,11 +12,11 @@
- Pokémon's held items can now be rendered, with a visibility toggle in the Summary screen.
- Added cosmetic item functionality for Pokémon. Certain cosmetic items can be given to applicable Pokémon via the interact menu.
- Added the various log blocks as cosmetic items for Timburr and Komala.
- Added cosmetics for Spoink, Gurdurr, Conkeldurr, Squirtle Line, Sneasler, Sandile line, Treecko line, Braixen, Delphox, and Dragonite.
- Added Gilded Chest cosmetics for Chest Gimmighoul and Gholdengo
- Added `visibility/hidden`, `visibility/hat` and `visibility/face` tags to control where and how certain items are rendered.
- Added Pokémon markings, toggleable within the summary.
- Added `/boxcount` command to change PC boxes amount
- Added cosmetics for Gurdurr, Conkeldurr, Squirtle Line, Sneasler, Sandile line, Treecko line, Braixen, Delphox, and Dragonite.
- Added cosmetics for Gurdurr, Conkeldurr, Squirtle Line, Sneasler, Sandle line, Treecko line, Braixen, Delphox, and Dragonite.
- Added `/transformmodelpart (position|rotation|scale) <modelPart> <transform: x y z>` command that can add transformations to a pokemon's model part.
- The player executing the command must be facing the target pokemon entity. Transformations are not persistent and will revert when resources are reloaded.
- Added `legacy` and `special` sourced moves to Pokémon.
@@ -23,7 +24,7 @@
- Added `translucent_cull` boolean option into resolver's layer to allow for translucent textures with culling
- Added [LambDynamicLights](https://modrinth.com/mod/lambdynamiclights) support for items held by Pokémon, evolution stone blocks, evolution stone items, Pokédex, Luminous Moss, Flame Orb, and Magmarizer.
- Added the Clear Amulet, Grip Claw, Lagging Tail, Luminous Moss, Metal Alloy, Scroll of Darkness, Scroll of Waters
- Added Recipes for Masterpiece Cup, Eject Pack
- Added Recipes for Masterpiece Cup, Eject Pack.
- Added modification to Minecraft Creative Inventory search to account for item names that contain `poké` when input contains `poke`.
- Added Campfire Pot as well as loads of new food items (Poke Puffs, Ponigiri, Sinister Tea, etc)
- Added Hearty Grains, a new crop used in the new cooking mechanic
@@ -39,6 +40,7 @@
- Pokémon with the ability Volt Absorb are immune to lightning damage and receive Regeneration II for a short duration
- Ground type Pokémon are immune to lightning damage
- Added functionality to Everstone when held by a Pokémon; suppresses evolution notification and hides evolve button in summary interface.
- Added optional `filePath` parameter to MoLang functions to allow for global stores (ex. `../global/`). Path must end in a forward slash.
- Added new optional property `attachment_options` for most EmitterShapes to be attached to the locator/entities scale, rotation, and/or position. Position is true by default.
- Galarica Nut bushes now generate on beaches.
- Some Pokémon now pitch their bodies in the direction they're moving.
@@ -46,9 +48,11 @@
- Berries can now be smelted into their respective dyes.
- Added Syrupy Apples.
- Added `/runmolang <molang> [<npc>|<player>|<pokemon>]` command that executes a MoLang expression with the provided options as environment variables, as well as the entity (as `q.entity`) that executed the command.
- Added [LambDynamicLights](https://modrinth.com/mod/lambdynamiclights) support for items held by Pokémon.
- Fixed Moon Ball moon phase logic to actually work correctly
- Added the Moves tab to the Pokédex, allowing players to view all of the move categories a form can obtain.
- Added bubble quirk to Kingler; like Krabby, Kingler will blows bubbles during dusk.
- Added `.Pre` and `.Post` to the following events:
- `PokemonRecallEvent`
- `TradeEvent`
- Added a new gamerule, 'healerHealsPC', when set to true a successful use of a healer will also heal all of the Pokemon in that player's PC.
### Pokémon Added
@@ -64,6 +68,9 @@
- Unown
- Houndour
- Houndoom
- Ho-Oh
- Lugia
- Alola Bias Pichu (built-in resourcepack)
#### Gen 3
- Azurill
@@ -79,8 +86,6 @@
- Glalie
- Latias
- Latios
- Swablu
- Altaria
#### Gen 4
- Bronzor
@@ -139,6 +144,8 @@
- Dedenne
- Noibat
- Noivern
- Furfrou
- You can change Furfrou's form by using shears on it while it holds a certain dye in its cosmetic slot.
#### Gen 7
- Yungoos
@@ -193,9 +200,10 @@
### Animation updates for the following Pokémon
- Garchomp
- Tropius
- Torpius
- Nosepass
- Probopass
- Sneasel
- Weavile
- Sneasler
- Braixen
- Delphox
@@ -217,8 +225,79 @@
- Porygon
- Porygon2
- Porygon-Z
- Wattrel
- Kilowattrel
- Golurk
- Spinarak
- Ariados
- Wyrdeer
- Tyrantrum
- Pidove
- Tranquill
- Unfezant
- Beldum
- Metang
- Metagross
- Plusle
- Minun
- Murkrow
- Honchkrow
- Larvesta
- Volcarona
- Dwebble
- Crustle
- Mr. Mime
- Flygon
- Pichu
- Pikachu
- Raichu
- Alolan Raichu
- Espathra
- Abra
- Kadabra
- Alakazam
- Deino
- Zweilous
- Hydreigon
- Slaking
- Klink
- Klang
- Klinklang
- Baltoy
- Claydol
- Mamoswine
- Rufflet
- Braviary
- Girafarig
- Farigiraf
- Rookidee
- Corvisquire
- Corviknight
- Venipede
- Whirlipede
- Scolipede
- Venusaur
- Parasect
- Bastiodon
- Amaura
- Varoom
- Revavroom
- Bouffalant
- Lickilicky
- Blaziken
- Rhyhorn
- Rhydon
- Rhyperior
- Tauros
- Tauros - Paldea Aqua
- Tauros - Paldea Blaze
- Tauros - Paldea Combat
- Dewgong
### Model updates for the following Pokémon
- Cleffa
- Clefairy
- Clefable
- Gyarados
- Dragonite
- Eevee
@@ -279,6 +358,51 @@
- Klink
- Klank
- Klinklang
- Spinarak
- Ariados
- Pidove
- Tranquill
- Unfezant
- Sobble
- Drizzile
- Inteleon
- Plusle
- Minun
- Murkrow
- Zorua
- Zoroark
- Mime.Jr
- Mr. Mime
- Pichu
- Pikachu
- Raichu
- Alolan Raichu
- Dusknoir
- Deino
- Zweilous
- Hydreigon
- Dreepy
- Drakloak
- Dragapult
- Mamoswine
- Rookidee
- Corvisquire
- Corviknight
- Venipede
- Whirlipede
- Scolipede
- Farigiraf
- Staryu
- Starmie
- Gimmighoul
- Gholdengo
- Starly
- Staravia
- Staraptor
- Varoom
- Revavroom
- Nickit
- Thievul
### Changes
- Changed pokemon caught and seen count to update based on the current pokedex being looked
@@ -332,9 +456,17 @@
- Updated Pokémon state icons shown in party interfaces
- MoLang `face` function can now be run on any `PosableEntity` (Like Pokémon!) instead of just NPCs.
- MoLang `run_action_effect` now works on Pokémon.
- Changed MoLang entity function `is_standing_on` to allow for block tags in the list.
- Changed MoLang entity function `is_standing_on` to allow for block tags in the list.
- Added entity (as `q.entity`) that executed the command to the `executemolangscript` command.
- Refactored the following events to `.Pre` and `.Post` for consistency:
- `PokemonSentEvent`
- `ExperienceGainedEvent`
- `BattleStartedEvent`
- Updated `PokemonSentEvent` parameters to include the Position and Level of the Pokémon being sent out.
- Updated `EvolutionCompleteEvent` parameters to include the Source Pokemon that evolved.
- Updated `HatchEggEvent.Post` to include the Pokemon that hatched.
- Roseli Berry Trees now naturally generate in their preferred biomes.
- Refactored dynamic lights compat to be more future proof. The dynamic light support is only tested with LambDynamicLights, on NeoForged use that mod in combination with Sinytra Connector
### Fixes
- Fixed game crashing when removing national pokedex using datapacks
@@ -409,6 +541,16 @@
- Fixed a vulnerability that could cause party and PC rollbacks under specific circumstances.
- Fixed a rare edge case where sorting your PC could be rolled back later.
- Fixed BotanyPots built-in integration
- Fixed shading of berries on berry trees.
- Fixed static text cursor for nickname input in summary.
- Fixed Cobblemon brewing recipes placing result into all slots, and not just slots with correct ingredients inside the brewing stand.
- Fixed an issue where hoppers and brewing stands were not recognizing Cobblemon brewing recipes.
- Fixed global species features... not working. Since they were created. Oops.
- Fixed Pokémon with alternate forms being created with an incorrect 'forced' tag on their ability.
- Fixed busted abilities and moves in Pokémon data due to removed datapacks etc. causing storage corruption. It now just rerolls their ability / uses Tackle.
- Fixed singular Pokémon corruption causing entire storage corruption. Storages will now skip corrupted Pokémon and print an error to console.
- Fixed species additions not being able to properly mark a species as implemented.
- Fixed Pokémon item models not showing a glint when enchanted.
### Developer
- A finished battle now has winners and losers set inside of `PokemonBattle` instead of them always being empty.
@@ -422,7 +564,6 @@
- Reworked observable handling in `Pokemon.kt` to cut down on RAM usage and clarify the file.
- Note: This will break mods that used our observable functionality there or in MoveSet, IVs, EVs, or BenchedMoves.
- Using `Pokemon#onChange()` is now the way to mark a Pokémon as needing a save.
- Using `[Pokemon].changeObservable` is now the way to get an `Observable` for any save-worthy changes.
- Updated NPCEntity beam positioning to properly account for the baseScale property.
- Updated NPCEntity pokeball throw positioning to properly account for the baseScale property.
- Fixed `[Pokemon].copyFrom` error causing forms, IVs, and EVs to not be applied properly when using `[Pokemon].loadFromJSON` or `[Pokemon].loadFromNBT`
@@ -451,7 +592,6 @@
- Added `Pokemon#hyperTrainIV()` and `IVs#setHyperTrainedIV(Stat, Int)`
- `ElementalType` now implements `ShowdownIdentifiable` to ensure the communcation with showdown stays consistent (also in regards to TeraTypes)
- Pokemon no longer have a change observable
- Fixed `[SpawnPool].observable` not emitting when the spawn pool is reloaded.
### MoLang & Datapacks
- The following usages for item predicates can now use item conditions like advancements do, you can learn about them in the [Minecraft wiki](https://minecraft.wiki/w/Advancement_definition#minecraft:filled_bucket)
@@ -504,6 +644,166 @@
- Added `is_included`, `to_lower`, `to_upper`, and `string_length` as available Molang functions.
- Fixed a crash that would occur during battles if the opponent wild Pokémon species comes from a namespace other than cobblemon
- Fixed `clientActions` inside Dialogue pages being executed twice
- Added `q.split_string(<text>, <delimiter/comma>)` Outputs an array with the divided text.
- Added `q.spawn_npc(<x>, <y>, <z>, <npc>)` Spawns an NPC at the given coordinates and returns the NPC.
- Added `q.player.seen_credits` Returns whether the player has seen the end credits.
- Added `q.player.is_in_dialogue` Returns whether the player is currently in a dialogue.
- Added `q.player.active_dialogue` Returns the active dialogue of the player, or null if none.
- Added `q.player.is_spectator` Returns whether the player is in spectator mode.
- Added `q.player.is_adventure` Returns whether the player is in adventure mode.
- Added `q.player.is_creative` Returns whether the player is in creative mode.
- Added `q.player.is_survival` Returns whether the player is in survival mode.
- Added `q.player.set_battle_theme(<sound resource>)` Sets the player's battle theme to the given theme.
- Added `q.player.battle_music(<sound_resource>, <volume>, <pitch>)` Plays the given battle music for the player.
- Added `q.player.stop_battle_music()` Stops any currently playing battle music for the player. (With a fade out).
- Added `q.player.riding_pokemon` Returns the Pokémon the player is currently riding.
- Added `q.entity.is_player` Returns whether the entity is a player.
- Added `q.entity.is_pokemon` Returns whether the entity is a Pokémon.
- Added `q.entity.is_npc` Returns whether the entity is an NPC.
- Added `q.entity.is_mob` Returns whether the entity is a vanilla mob.
- Added `q.entity.is_animal` Returns whether the entity is an animal.
- Added `q.entity.is_hostile` Returns whether the entity is a hostile mob.
- Added `q.entity.is_baby` Returns whether the entity is a baby.
- Added `q.entity.is_adult` Returns whether the entity is an adult.
- Added `q.entity.is_tamable` Returns whether the entity is tamable.
- Added `q.entity.is_tamed` Returns whether the entity is tamed.
- Added `q.entity.add_effect(<effect>, <duration>, <amplifier>, <ambient> <show_particles>)` Adds a status effect to the entity.
- Added `q.entity.remove_effect(<effect>)` Removes a status effect from the entity.
- Added `q.entity.has_effect(<effect>)` Returns whether the entity has the given status effect.
- Added `q.entity.is_looking_at(<entity>)` Returns whether the entity is looking at the given entity.
- Added `q.entity.tags` Returns an array of the entity's tags.
- Added `q.entity.add_tag(<tag>)` Adds the given tag to the entity.
- Added `q.entity.remove_tag(<tag>)` Removes the given tag from the entity.
- Added `q.entity.has_tag(<tag>)` Returns whether the entity has the given tag.
- Added `q.entity.make_intangible(<true/false>)` Makes the entity intangible.
- Added `q.pokemon.force_evolve(<index>)` Forces the Pokémon at the given index.
- Added `q.pokemon.can_evolve` Returns whether the Pokémon at the given index can evolve.
- Added `q.pokemon.is_busy` Returns whether the Pokémon is busy.
- Added `q.pokemon.is_rideable` Returns whether the Pokémon is rideable.
- Fixed `background` field and added `textColor` field for dialogues.
- Added support to reload some data registries:
- MoLang scripts
- Callbacks
- Spawn Detail Presets
- Spawn Pools
- Spawn Rules
- Cosmetic Items
- Dialogues
- Fossils
- Natural Materials
- Action Effects
- Mechanics
- Unlockable Wallpapers
- Starter Data
### Particles
Added new/updated particles for the following moves:
- Absorb
- Acid
- Acid Spray
- Aerial Ace
- Amnesia
- Aurora Beam
- Bite
- Body Slam
- Bubble
- Bubble Beam
- Bulldoze
- Bullet Punch
- Close Combat
- Confusion
- Crunch
- Cotton Guard
- Double Team
- Dragon Claw
- Egg Bomb
- Ember
- Eruption
- Explosion
- Fire Blast
- Fire Punch
- Fire Spin
- Flame Burst
- Flame Charge
- Flamethrower
- Flame Wheel
- Frost Breath
- Frustration
- Giga Drain
- Growl
- Haze
- Hidden Power
- Horn Attack
- Hyper Fang
- Hypnosis
- Ice Beam
- Ice Punch
- Ice Shard
- Icy Wind
- Infestation
- Kinesis
- Lava Plume
- Leech Life
- Leech Seed
- Leer
- Lick
- Magical Leaf
- Mega Drain
- Minimize
- Mist
- Mud Bomb
- MudSport
- Mystical Fire
- Nasty Plot
- Poison Fang
- Poison Gas
- Poison Powder
- Population Bomb
- Pound
- Powder
- Powder Snow
- Present
- Protect
- Psybeam
- Psychic
- Pursuit
- Quick Attack
- Rage Powder
- Razor Leaf
- Return
- Rock Throw
- Sand Attack
- Scary Face
- Scratch
- Seed Bomb
- Seismic Toss
- Self Destruct
- Shadow Ball
- Shadow Claw
- Sing
- Sleep Powder
- Sludge Bomb
- Smokescreen
- Spike
- Spore
- Stomp
- String Shot
- Stun Spore
- Super Fang
- Swords Dance
- Tackle
- Tail Whip
- Thunder
- Thunder Punch
- Thunderbolt
- Thunder Shock
- Thunder Wave
- Toxic
- Water Gun
- Water Pulse
- Water Sport
- Will-O-Wisp
- Withdraw
## [1.6.1 (January 26th, 2025)](#1-6-1)
+8 -1
View File
@@ -31,6 +31,7 @@ repositories {
maven(url = "${rootProject.projectDir}/deps")
maven(url = "https://api.modrinth.com/maven")
maven(url = "https://maven.neoforged.net/releases")
maven(url = "https://maven.gegy.dev")
mavenLocal()
}
@@ -44,6 +45,12 @@ dependencies {
modCompileOnly(libs.bundles.common.integrations.compileOnly) {
isTransitive = false
}
// LambDynamicLights is handled differently because we need the Mojang-mappings version of it.
modCompileOnly(libs.lambDynamicLights) {
capabilities {
requireCapability("dev.lambdaurora.lambdynamiclights:api-mojmap")
}
}
// Flywheel has no common dep so just pick one and don't use any platform specific code in common
// modCompileOnly(libs.flywheelFabric)
@@ -94,4 +101,4 @@ sourceSets {
}
}
}
}
}
@@ -1,102 +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.compat;
import com.cobblemon.mod.common.CobblemonEntities;
import com.cobblemon.mod.common.client.render.layer.PokemonOnShoulderRenderer;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.item.PokedexItem;
import com.cobblemon.mod.common.pokemon.FormData;
import com.cobblemon.mod.common.pokemon.lighthing.LightingData;
import dev.lambdaurora.lambdynlights.LambDynLights;
import dev.lambdaurora.lambdynlights.api.DynamicLightHandler;
import dev.lambdaurora.lambdynlights.api.DynamicLightHandlers;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
import net.minecraft.world.entity.player.Player;
// Java class due to relying on entrypoint on Fabric side
public class LambDynamicLightsCompat {
// This is all called on client side
// These things are checked every tick so try to keep it as free as possible
// That being said it's unlikely school Toshiba person is running dynamic lighting and shaders to enjoy this
// Make sure not to call anything Fabric specific :)
public static void hookCompat() {
// Pokémon entities
DynamicLightHandlers.registerDynamicLightHandler(
CobblemonEntities.POKEMON,
DynamicLightHandler.makeHandler(
LambDynamicLightsCompat::resolvedPokemonLightLevel,
pokemon -> false
)
);
// Shouldered Pokémon
DynamicLightHandlers.registerDynamicLightHandler(
EntityType.PLAYER,
DynamicLightHandler.makeHandler(
LambDynamicLightsCompat::resolvedPlayerLightLevel,
player -> false
)
);
}
private static int resolvedPokemonLightLevel(PokemonEntity pokemon) {
var underwater = !pokemon.level().getFluidState(BlockPos.containing(pokemon.getX(), pokemon.getEyeY(), pokemon.getZ())).isEmpty();
ItemStack item = pokemon.getShownItem();
int itemLightLevel = item.isEmpty()?0:LambDynLights.getLuminanceFromItemStack(item, underwater);
int formLightLevel = extractFormLightLevel(pokemon.getForm(), underwater).orElse(0);
return Math.max(itemLightLevel, formLightLevel);
}
private static int resolvedPlayerLightLevel(Player player) {
var underwater = !player.level().getFluidState(BlockPos.containing(player.getX(), player.getEyeY(), player.getZ())).isEmpty();
final int leftShoulderLightLevel = extractShoulderLightLevel(PokemonOnShoulderRenderer.shoulderDataFrom(player.getShoulderEntityLeft()), underwater);
final int rightShoulderLightLevel = extractShoulderLightLevel(PokemonOnShoulderRenderer.shoulderDataFrom(player.getShoulderEntityRight()), underwater);
final int itemLightLevel = customHeldItemLightLevel(player.getMainHandItem(), player.getOffhandItem());
return Math.max(itemLightLevel, Math.max(leftShoulderLightLevel, rightShoulderLightLevel));
}
private static Optional<Integer> extractFormLightLevel(@NotNull FormData form, boolean underwater) {
if (form.getLightingData() == null || !liquidGlowModeSupport(form.getLightingData().getLiquidGlowMode(), underwater)) {
return Optional.empty();
}
return Optional.of(form.getLightingData().getLightLevel());
}
private static int extractShoulderLightLevel(@Nullable PokemonOnShoulderRenderer.ShoulderData shoulderData, boolean underwater) {
if (shoulderData == null) {
return 0;
}
ItemStack item = shoulderData.getShownItem();
int itemLightLevel = item.isEmpty() ? 0 :LambDynLights.getLuminanceFromItemStack(item, underwater);
int formLightLevel = extractFormLightLevel(shoulderData.getForm(), underwater).orElse(0);
return Math.max(itemLightLevel, formLightLevel);
}
private static int customHeldItemLightLevel(ItemStack mainhand, ItemStack offhand) {
return (mainhand.getItem() instanceof PokedexItem || offhand.getItem() instanceof PokedexItem) ? 13 : 0;
}
private static boolean liquidGlowModeSupport(@NotNull LightingData.LiquidGlowMode liquidGlowMode, boolean underwater) {
return underwater ? liquidGlowMode.getGlowsUnderwater() : liquidGlowMode.getGlowsInLand();
}
}
@@ -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.compat.lambdynamiclights;
import com.cobblemon.mod.common.pokemon.FormData;
import com.cobblemon.mod.common.pokemon.lighthing.LightingData;
import org.jetbrains.annotations.NotNull;
import java.util.Optional;
public abstract class CustomLuminance {
protected static Optional<Integer> extractFormLightLevel(@NotNull FormData form, boolean underwater) {
if (form.getLightingData() == null || !liquidGlowModeSupport(form.getLightingData().getLiquidGlowMode(), underwater)) {
return Optional.empty();
}
return Optional.of(form.getLightingData().getLightLevel());
}
protected static boolean liquidGlowModeSupport(@NotNull LightingData.LiquidGlowMode liquidGlowMode, boolean underwater) {
return underwater ? liquidGlowMode.getGlowsUnderwater() : liquidGlowMode.getGlowsInLand();
}
}
@@ -0,0 +1,42 @@
/*
* 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.compat.lambdynamiclights;
import com.cobblemon.mod.common.Cobblemon;
import dev.lambdaurora.lambdynlights.api.DynamicLightsContext;
import dev.lambdaurora.lambdynlights.api.DynamicLightsInitializer;
import dev.lambdaurora.lambdynlights.api.entity.luminance.EntityLuminance;
import dev.lambdaurora.lambdynlights.api.item.ItemLightSourceManager;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.EntityType;
public class LambDynamicLightsInitializer implements DynamicLightsInitializer {
public static final EntityLuminance.Type POKEMON_LUMINANCE = EntityLuminance.Type.registerSimple(
ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, "pokemon"),
PokemonLuminance.INSTANCE
);
public static final EntityLuminance.Type PLAYER_LUMINANCE = EntityLuminance.Type.registerSimple(
ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, "shoulder"),
PlayerLuminance.INSTANCE
);
@Override
public void onInitializeDynamicLights(DynamicLightsContext context) {
Cobblemon.LOGGER.info("Lamb Dynamic Lights compatibility enabled");
context.entityLightSourceManager().onRegisterEvent().register(ctx -> {
ctx.register(EntityType.PLAYER, PlayerLuminance.INSTANCE);
});
}
@SuppressWarnings({"removal", "UnstableApiUsage"})
@Override
public void onInitializeDynamicLights(ItemLightSourceManager itemLightSourceManager) {}
}
@@ -0,0 +1,72 @@
/*
* 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.compat.lambdynamiclights;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.client.render.layer.PokemonOnShoulderRenderer;
import dev.lambdaurora.lambdynlights.api.entity.luminance.EntityLuminance;
import dev.lambdaurora.lambdynlights.api.item.ItemLightSourceManager;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Range;
public final class PlayerLuminance extends CustomLuminance implements EntityLuminance {
public static final PlayerLuminance INSTANCE = new PlayerLuminance();
private static final TagKey<Item> POKEDEX = TagKey.create(Registries.ITEM, ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, "pokedex"));
@Override
public @NotNull Type type() {
return LambDynamicLightsInitializer.PLAYER_LUMINANCE;
}
@Override
public @Range(from = 0L, to = 15L) int getLuminance(@NotNull ItemLightSourceManager itemLightSourceManager, @NotNull Entity entity) {
if (entity instanceof Player player) {
final int shoulderLightLevel = shoulderLuminance(itemLightSourceManager, player);
final int itemLightLevel = player.isHolding(stack -> stack.is(POKEDEX)) ? 13 : 0;
return Math.max(shoulderLightLevel, itemLightLevel);
} else {
return 0;
}
}
private @Range(from = 0L, to = 15L) static int shoulderLuminance(@NotNull ItemLightSourceManager itemLightSourceManager, @NotNull Player player) {
final var leftShoulder = PokemonOnShoulderRenderer.shoulderDataFrom(player.getShoulderEntityLeft());
final var rightShoulder = PokemonOnShoulderRenderer.shoulderDataFrom(player.getShoulderEntityRight());
if (leftShoulder == null && rightShoulder == null) {
return 0;
}
final int leftShoulderLightLevel = extractShoulderLightLevel(leftShoulder, itemLightSourceManager, player.isUnderWater());
final int rightShoulderLightLevel = extractShoulderLightLevel(rightShoulder, itemLightSourceManager, player.isUnderWater());
return Math.max(leftShoulderLightLevel, rightShoulderLightLevel);
}
private static int extractShoulderLightLevel(@Nullable PokemonOnShoulderRenderer.ShoulderData shoulderData, @NotNull ItemLightSourceManager itemLightSourceManager, boolean underwater) {
if (shoulderData == null) {
return 0;
}
ItemStack item = shoulderData.getShownItem();
int itemLightLevel = item.isEmpty() ? 0 : itemLightSourceManager.getLuminance(item, underwater);
int formLightLevel = extractFormLightLevel(shoulderData.getForm(), underwater).orElse(0);
return Math.max(itemLightLevel, formLightLevel);
}
}
@@ -0,0 +1,45 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.compat.lambdynamiclights;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import dev.lambdaurora.lambdynlights.api.entity.luminance.EntityLuminance;
import dev.lambdaurora.lambdynlights.api.item.ItemLightSourceManager;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Range;
public final class PokemonLuminance extends CustomLuminance implements EntityLuminance {
public static final PokemonLuminance INSTANCE = new PokemonLuminance();
private PokemonLuminance() {}
@Override
public @NotNull Type type() {
return LambDynamicLightsInitializer.POKEMON_LUMINANCE;
}
@Override
public @Range(from = 0L, to = 15L) int getLuminance(
@NotNull ItemLightSourceManager itemLightSourceManager,
@NotNull Entity entity
) {
if (entity instanceof PokemonEntity pokemon) {
boolean underwater = pokemon.isUnderWater();
ItemStack item = pokemon.getShownItem();
int itemLightLevel = item.isEmpty() ? 0 : itemLightSourceManager.getLuminance(item, underwater);
int formLightLevel = extractFormLightLevel(pokemon.getForm(), underwater).orElse(0);
return Math.max(itemLightLevel, formLightLevel);
} else {
return 0;
}
}
}
@@ -0,0 +1,62 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.EnderpearlItem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.Comparator;
@Mixin(EnderpearlItem.class)
public abstract class EnderPearlItemMixin {
@Inject(method = "use", at = @At(value = "HEAD"), cancellable = true)
private void cobblemon$use(Level level, Player player, InteractionHand usedHand, CallbackInfoReturnable<InteractionResultHolder<ItemStack>> cir) {
double range = player.entityInteractionRange();
Entity closestEntity = level.getEntities(player, AABB.ofSize(player.position(), range, range, range)).stream()
.filter(entity -> isLookingAt(player, entity))
.min(Comparator.comparingDouble(entity -> entity.distanceTo(player)))
.orElse(null);
if (closestEntity instanceof PokemonEntity pokemonEntity) {
if (player.isCrouching() && pokemonEntity.getOwnerUUID() == player.getUUID()) {
cir.cancel();
}
}
}
public boolean isLookingAt(Player player, Entity other) {
float maxDistance = 10F;
float stepDistance = 0.1F;
double step = stepDistance;
Vec3 startPos = player.getEyePosition();
Vec3 direction = player.getLookAngle();
while (step <= maxDistance) {
Vec3 location = startPos.add(direction.scale(step));
step += stepDistance;
if (other.getBoundingBox().contains(location)) {
return true;
}
}
return false;
}
}
@@ -58,11 +58,9 @@ public abstract class LivingEntityMixin extends Entity implements OrientationCon
@Override
public @NotNull HitResult pick(double hitDistance, float partialTicks, boolean hitFluids) {
Entity vehicle = this.getVehicle();
if (vehicle instanceof PokemonEntity pokemonEntity) {
if (vehicle instanceof PokemonEntity pokemonEntity && pokemonEntity.getDelegate() instanceof PokemonClientDelegate delegate) {
int seatIndex = pokemonEntity.getPassengers().indexOf(this);
Seat seat = pokemonEntity.getSeats().get(seatIndex);
PokemonClientDelegate delegate = (PokemonClientDelegate) pokemonEntity.getDelegate();
MatrixWrapper locator = delegate.getLocatorStates().get(seat.getLocator());
Vec3 locatorOffset = new Vec3(locator.getMatrix().getTranslation(new Vector3f()));
@@ -25,9 +25,9 @@ public class TargetingConditionsMixin {
@Inject(method = "test", at = @At("HEAD"), cancellable = true)
public void test(@Nullable LivingEntity baseEntity, LivingEntity targetEntity, CallbackInfoReturnable<Boolean> ci) {
if (targetEntity instanceof ServerPlayer player) {
boolean canTargetInBattle = player.level().getGameRules().getBoolean(CobblemonGameRules.MOB_TARGET_IN_BATTLE);
if (player.level().getGameRules().getBoolean(CobblemonGameRules.MOB_TARGET_IN_BATTLE)) return;
boolean targetInBattle = Cobblemon.INSTANCE.getBattleRegistry().getBattleByParticipatingPlayer(player) != null;
if (!canTargetInBattle && targetInBattle) ci.setReturnValue(false);
if (targetInBattle) ci.setReturnValue(false);
}
}
}
@@ -15,9 +15,11 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.NonNullList;
import net.minecraft.world.Containers;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.alchemy.PotionBrewing;
import net.minecraft.world.item.crafting.RecipeHolder;
import net.minecraft.world.item.crafting.RecipeManager;
@@ -27,6 +29,8 @@ import net.minecraft.world.level.block.entity.BrewingStandBlockEntity;
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.List;
import java.util.Optional;
@@ -43,7 +47,9 @@ public class BrewingStandBlockEntityMixin {
//copied from vanilla doBrew logic, but slightly tweaked to work for cobblemon, this was needed as replacing the loop was not quite possible inside doBrew
ItemStack itemStack = slots.get(3);
for(int i = 0; i < 3; ++i) {
slots.set(i, recipe.getResult().copy());
if (!slots.get(i).isEmpty()){
slots.set(i, recipe.getResult().copy());
}
}
itemStack.shrink(1);
@@ -91,4 +97,35 @@ public class BrewingStandBlockEntityMixin {
return recipeHolder.map(RecipeHolder::value).orElse(null);
}
@Inject(method = "canPlaceItem", at = @At("HEAD"), cancellable = true)
private void cobblemon$canPlaceItem(int slot, ItemStack stack, CallbackInfoReturnable<Boolean> cir) {
if (stack == null || stack.isEmpty()) return;
BrewingStandBlockEntity self = (BrewingStandBlockEntity) (Object) this;
Level level = self.getLevel();
if (level != null) {
var rm = level.getRecipeManager();
//Input slot
if (slot == 3) {
if (BrewingStandRecipe.Companion.isInput(stack, rm)) {
cir.setReturnValue(true);
return;
}
}
// Output slots
if (slot >= 0 && slot < 3) {
if (BrewingStandRecipe.Companion.isBottle(stack, rm)) {
if (self.getItem(slot).isEmpty()) {
cir.setReturnValue(true);
} else {
cir.setReturnValue(false);
}
}
}
}
}
}
@@ -31,14 +31,15 @@ public class AbstractClientPlayerMixin {
PokedexUsageContext usageContext = CobblemonClient.INSTANCE.getPokedexUsageContext();
if (usageContext.getScanningGuiOpen()) {
cir.setReturnValue(usageContext.getFovMultiplier());
}
} else {
// Only modify fov through riding if the pokedex is not open
LocalPlayer player = (LocalPlayer) (Object) this;
if (player != null && player.isPassenger() && player.getVehicle() instanceof PokemonEntity) {
PokemonEntity ride = (PokemonEntity) player.getVehicle();
LocalPlayer player = (LocalPlayer) (Object) this;
if (player != null && player.isPassenger() && player.getVehicle() instanceof PokemonEntity) {
PokemonEntity ride = (PokemonEntity) player.getVehicle();
//Return custom fov mult
cir.setReturnValue(ride.rideFovMult());
// Return custom fov mult for riding
cir.setReturnValue(ride.rideFovMult());
}
}
}
}
@@ -0,0 +1,46 @@
/*
* 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.client;
import com.cobblemon.mod.common.client.render.player.MountedPlayerRenderer;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.model.AgeableListModel;
import net.minecraft.client.model.HumanoidModel;
import net.minecraft.client.model.Model;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
* This mixin adds to the head of the renderToBuffer function and allows us to detect if it is a HumanoidModel and
* is being rendered as part of a player in the world who is riding a PokemonEntity. If so we need to obey any
* root animations that have been applied earlier.
*
* @author Hiroku
* @since May 16, 2025
*/
@Mixin(AgeableListModel.class)
public class AgeableListModelMixin {
@Inject(method = "renderToBuffer", at = @At("HEAD"))
private void cobblemon$renderToBuffer(
PoseStack poseStack,
VertexConsumer vertexConsumer,
int packedLight,
int packedOverlay,
int colour,
CallbackInfo ci
) {
Model model = (Model) (Object) this;
if (model instanceof HumanoidModel<?> && MountedPlayerRenderer.shouldApplyRootAnimation) {
MountedPlayerRenderer.INSTANCE.animateRoot(poseStack);
}
}
}
@@ -19,20 +19,17 @@ import net.minecraft.world.entity.LivingEntity;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.HashMap;
@Mixin(HumanoidModel.class)
public class HumanoidModelMixin {
@Unique HashMap<String, ModelPart> relevantPartsByName = new HashMap<>();
@Shadow @Final
public ModelPart head;
@Shadow @Final
public ModelPart hat;
@Shadow @Final
public ModelPart rightArm;
@Shadow @Final
public ModelPart leftArm;
@@ -40,6 +37,8 @@ public class HumanoidModelMixin {
public ModelPart rightLeg;
@Shadow @Final
public ModelPart leftLeg;
@Shadow @Final
public ModelPart body;
@Inject(method = "setupAnim*", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/model/geom/ModelPart;copyFrom(Lnet/minecraft/client/model/geom/ModelPart;)V", shift = At.Shift.BEFORE))
public void cobblemon$animateRiders(
@@ -51,19 +50,23 @@ public class HumanoidModelMixin {
float headPitch,
CallbackInfo ci
) {
if (relevantPartsByName.isEmpty()) {
relevantPartsByName.put("arm_left", leftArm);
relevantPartsByName.put("arm_right", rightArm);
relevantPartsByName.put("leg_left", leftLeg);
relevantPartsByName.put("leg_right", rightLeg);
relevantPartsByName.put("head", head);
MountedPlayerRenderer.shouldApplyRootAnimation = false;
if (MountedPlayerRenderer.relevantPartsByName.size() <= 1) {
MountedPlayerRenderer.relevantPartsByName.put("arm_left", leftArm);
MountedPlayerRenderer.relevantPartsByName.put("arm_right", rightArm);
MountedPlayerRenderer.relevantPartsByName.put("leg_left", leftLeg);
MountedPlayerRenderer.relevantPartsByName.put("leg_right", rightLeg);
MountedPlayerRenderer.relevantPartsByName.put("head", head);
MountedPlayerRenderer.relevantPartsByName.put("hat", hat);
MountedPlayerRenderer.relevantPartsByName.put("torso", body);
}
Entity vehicle = entity.getVehicle();
if (vehicle instanceof PokemonEntity pokemonEntity && entity instanceof OrientationControllable) {
var shouldRotatePlayerHead = pokemonEntity.ifRidingAvailableSupply(false, (behaviour, settings, state) -> {
return behaviour.shouldRotateRiderHead(settings, state, pokemonEntity);
});
var shouldRotatePlayerHead = pokemonEntity.ifRidingAvailableSupply(false, (behaviour, settings, state) ->
behaviour.shouldRotateRiderHead(settings, state, pokemonEntity)
);
if (!shouldRotatePlayerHead) {
netHeadYaw = 0f;
@@ -76,7 +79,8 @@ public class HumanoidModelMixin {
if (!(entity instanceof AbstractClientPlayer player)) return;
MountedPlayerRenderer.INSTANCE.animate(pokemonEntity, player, relevantPartsByName, netHeadYaw, headPitch, ageInTicks, limbSwing, limbSwingAmount);
MountedPlayerRenderer.shouldApplyRootAnimation = true;
MountedPlayerRenderer.INSTANCE.animate((HumanoidModel<AbstractClientPlayer>)(Object)this, pokemonEntity, player, netHeadYaw, headPitch, ageInTicks, limbSwing, limbSwingAmount);
}
}
}
@@ -174,6 +174,16 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
return this.cobblemon$isDoubleJumping;
}
@Inject(method = "isHandsBusy", at = @At("HEAD"), cancellable = true)
private void cobblemon$isHandsBusy(CallbackInfoReturnable<Boolean> cir) {
Entity vehicle = this.getVehicle();
if (vehicle instanceof PokemonEntity pokemonEntity) {
int seatIndex = pokemonEntity.getPassengers().indexOf(this);
Seat seat = pokemonEntity.getSeats().get(seatIndex);
cir.setReturnValue(seat.getHandsBusy());
}
}
@Override
public HitResult pick(double hitDistance, float partialTicks, boolean hitFluids) {
Entity vehicle = this.getVehicle();
@@ -117,6 +117,8 @@ public abstract class MouseHandlerMixin {
@Local(argsOnly = true) double d,
@Local(argsOnly = true) double movementTime
) {
boolean returnValue = true;
PokedexUsageContext usageContext = CobblemonClient.INSTANCE.getPokedexUsageContext();
if (usageContext.getScanningGuiOpen()) {
this.smoothTurnY.reset();
@@ -127,7 +129,7 @@ public abstract class MouseHandlerMixin {
var sensitivity = Mth.lerp(usageContext.getFovMultiplier(), spyglassSensitivity, lookSensitivity);
var yRotationFlip = this.minecraft.options.invertYMouse().get() ? -1 : 1;
player.turn(this.accumulatedDX * sensitivity, (this.accumulatedDY * sensitivity * yRotationFlip));
return false;
returnValue = false;
}
// Clamp player rotation if riding and the vehicle demands it
@@ -143,35 +145,33 @@ public abstract class MouseHandlerMixin {
pitchSmoother.reset();
rollSmoother.reset();
yawSmoother.reset();
return true;
return returnValue;
}
//Send mouse input to be interpreted into rotation
//deltas by the ride controller
// Send mouse input to be interpreted into rotation
// deltas by the ride controller
Vec3 angVecMouse = cobblemon$getRideMouseRotation(cursorDeltaX, cursorDeltaY, movementTime);
//Perform Rotation using mouse influenced rotation deltas.
// Perform Rotation using mouse influenced rotation deltas.
controllable.getOrientationController().rotate(
(float) angVecMouse.x,
(float) angVecMouse.y,
(float) angVecMouse.z
);
//Gather and apply the current rotation deltas
// Gather and apply the current rotation deltas
var angRot = cobblemon$getAngularVelocity(movementTime);
//Apply smoothing if requested by the controller.
//This Might be best if done by the controller itself?
if(cobblemon$shouldUseAngVelSmoothing())
{
// Apply smoothing if requested by the controller.
// This Might be best if done by the controller itself?
if(cobblemon$shouldUseAngVelSmoothing()) {
var yaw = yawSmoother.getNewDeltaValue(angRot.x * 0.5f, d);
var pitch = pitchSmoother.getNewDeltaValue(angRot.y * 0.5f, d);
var roll = rollSmoother.getNewDeltaValue(angRot.z * 0.5f, d);
controllable.getOrientationController().rotate((float) yaw, (float) pitch, (float) roll);
}
//Otherwise simply apply the smoothing
else
{
// Otherwise simply apply the smoothing
else {
controllable.getOrientationController().rotate((float) (angRot.x * 10 * d), (float) (angRot.y * 10 * d), (float) (angRot.z * 10 * d));
}
return false;
@@ -10,8 +10,7 @@ package com.cobblemon.mod.common.mixin.client.snapshots;
import com.cobblemon.mod.common.CobblemonBuildDetails;
import com.cobblemon.mod.common.client.gui.snapshots.SnapshotWarningScreen;
import com.cobblemon.mod.common.client.snapshots.SnapshotAcknowledgementData;
import com.cobblemon.mod.common.client.snapshots.SnapshotAcknowledgementReader;
import com.cobblemon.mod.common.client.persisted.ClientPersistedData;
import kotlin.Unit;
import net.minecraft.SharedConstants;
import net.minecraft.client.Minecraft;
@@ -42,7 +41,7 @@ public final class SnapshotWarningMixin {
return Unit.INSTANCE;
}
SnapshotAcknowledgementReader.INSTANCE.write(new SnapshotAcknowledgementData(CobblemonBuildDetails.VERSION, dsa));
ClientPersistedData.INSTANCE.writeSnapshotsData(new ClientPersistedData.SnapshotAcknowledgementData(CobblemonBuildDetails.VERSION, dsa));
runnable.run();
return Unit.INSTANCE;
@@ -54,7 +53,7 @@ public final class SnapshotWarningMixin {
@Unique
@SuppressWarnings("ResultOfMethodCallIgnored")
private boolean cobblemon$isAcknowledged() {
SnapshotAcknowledgementData data = SnapshotAcknowledgementReader.INSTANCE.read();
ClientPersistedData.SnapshotAcknowledgementData data = ClientPersistedData.INSTANCE.readSnapshotsData();
if (data == null) {
return false;
}
@@ -11,7 +11,10 @@ package com.cobblemon.mod.common.mixin.entity;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@@ -39,10 +42,17 @@ public abstract class EntityMixin {
target = "Lnet/minecraft/world/entity/Entity;onGround()Z"
)
)
public boolean cobblemon$forceOnGroundForStepUp(Entity entity, Operation<Boolean> original)
{
public boolean cobblemon$forceOnGroundForStepUp(Entity entity, Operation<Boolean> original) {
if (entity instanceof PokemonEntity && entity.hasControllingPassenger()) {
return true;
BlockPos below = entity.blockPosition().below();
Level level = entity.level();
var blockStateBelow = level.getBlockState(below);
boolean isAirOrLiquid = blockStateBelow.isAir() || !blockStateBelow.getFluidState().isEmpty();
boolean canSupportEntity = blockStateBelow.isFaceSturdy(level, below, Direction.UP);
boolean standingOnSolid = canSupportEntity && !isAirOrLiquid;
if (standingOnSolid) {
return true;
}
}
return original.call(entity);
}
@@ -76,7 +76,7 @@ import com.cobblemon.mod.common.config.starter.StarterConfig
import com.cobblemon.mod.common.data.CobblemonDataProvider
import com.cobblemon.mod.common.events.AdvancementHandler
import com.cobblemon.mod.common.events.EntityCallbackHandler
import com.cobblemon.mod.common.events.FlowHandler
import com.cobblemon.mod.common.events.CallbackHandler
import com.cobblemon.mod.common.events.PokedexHandler
import com.cobblemon.mod.common.events.ServerTickHandler
import com.cobblemon.mod.common.net.messages.client.settings.ServerSettingsPacket
@@ -313,7 +313,7 @@ object Cobblemon {
CobblemonEvents.POKEMON_PROPERTY_INITIALISED.emit(Unit)
FlowHandler.setup()
CallbackHandler.setup()
EntityCallbackHandler.setup()
ifDedicatedServer {
@@ -128,7 +128,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
@JvmField
val APRICORN_FENCE = this.create("apricorn_fence", FenceBlock(BlockBehaviour.Properties.of().mapColor(APRICORN_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
@JvmField
val APRICORN_FENCE_GATE = this.create("apricorn_fence_gate", FenceGateBlock(APRICORN_WOOD_TYPE, BlockBehaviour.Properties.of().mapColor(APRICORN_PLANKS.defaultMapColor()).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
val APRICORN_FENCE_GATE = this.create("apricorn_fence_gate", FenceGateBlock(APRICORN_WOOD_TYPE, BlockBehaviour.Properties.of().mapColor(APRICORN_PLANKS.defaultMapColor()).strength(2.0f, 3.0f).sound(SoundType.WOOD).ignitedByLava().forceSolidOn()))
@JvmField
val APRICORN_BUTTON = this.create("apricorn_button", BlocksInvoker.createWoodenButtonBlock(APRICORN_BLOCK_SET_TYPE))
@JvmField
@@ -205,7 +205,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
@JvmField
val SACCHARINE_FENCE = this.create("saccharine_fence", FenceBlock(BlockBehaviour.Properties.of().mapColor(SACCHARINE_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
@JvmField
val SACCHARINE_FENCE_GATE = this.create("saccharine_fence_gate", FenceGateBlock(SACCHARINE_WOOD_TYPE, BlockBehaviour.Properties.of().mapColor(SACCHARINE_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
val SACCHARINE_FENCE_GATE = this.create("saccharine_fence_gate", FenceGateBlock(SACCHARINE_WOOD_TYPE, BlockBehaviour.Properties.of().mapColor(SACCHARINE_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD).ignitedByLava().forceSolidOn()))
@JvmField
val SACCHARINE_BUTTON = this.create("saccharine_button", BlocksInvoker.createWoodenButtonBlock(BlockSetType.OAK))
@JvmField
@@ -213,7 +213,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
@JvmField
val SACCHARINE_SIGN = this.create("saccharine_sign", CobblemonSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_SIGN), SACCHARINE_WOOD_TYPE))
@JvmField
val SACCHARINE_WALL_SIGN = this.create("saccharine_wall_sign", CobblemonWallSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_WALL_SIGN), SACCHARINE_WOOD_TYPE))
val SACCHARINE_WALL_SIGN = this.create("saccharine_wall_sign", CobblemonWallSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_SIGN), SACCHARINE_WOOD_TYPE))
@JvmField
val SACCHARINE_HANGING_SIGN = this.create("saccharine_hanging_sign", CobblemonHangingSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_WALL_HANGING_SIGN), SACCHARINE_WOOD_TYPE))
@JvmField
@@ -18,72 +18,78 @@ import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
import com.cobblemon.mod.common.net.messages.client.data.FlowRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.CallbackRegistrySyncPacket
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.endsWith
import java.util.concurrent.ExecutionException
import java.util.function.Predicate
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.server.packs.resources.ResourceManager
/**
* Holds all the flows that are loaded from the server's data packs. A flow is foldered under the event identifier,
* Holds all the callbacks that are loaded from the server's data packs. A callback is foldered under the event identifier,
* which can be Cobblemon's namespace but can also be minecraft or any other namespace someone has added hooks for.
* The value of the map is a list of MoLang scripts that will execute when that event occurs.
*
* @author Hiroku
* @since February 24th, 2024
*/
object CobblemonFlows : DataRegistry {
override val id = cobblemonResource("flows")
override val observable = SimpleObservable<CobblemonFlows>()
object CobblemonCallbacks : DataRegistry {
override val id = cobblemonResource("callbacks")
override val observable = SimpleObservable<CobblemonCallbacks>()
override val type = PackType.SERVER_DATA
override fun sync(player: ServerPlayer) {
player.sendPacket(FlowRegistrySyncPacket(clientFlows.entries))
player.sendPacket(CallbackRegistrySyncPacket(clientCallbacks.entries))
}
val runtime by lazy { MoLangRuntime().setup() } // Lazy for if someone adds to generalFunctions in MoLangFunctions
val clientFlows = hashMapOf<ResourceLocation, MutableList<ExpressionLike>>()
val flows = hashMapOf<ResourceLocation, MutableList<ExpressionLike>>()
val clientCallbacks = hashMapOf<ResourceLocation, MutableList<ExpressionLike>>()
val callbacks = hashMapOf<ResourceLocation, MutableList<ExpressionLike>>()
override fun reload(manager: ResourceManager) {
clientFlows.clear()
flows.clear()
clientCallbacks.clear()
callbacks.clear()
val unsortedFlows = mutableMapOf<ResourceLocation, MutableList<Pair<String, ExpressionLike>>>()
val unsortedCallbacks = mutableMapOf<ResourceLocation, MutableList<Pair<String, ExpressionLike>>>()
val folderBeforeNameRegex = ".*\\/([^\\/]+)\\/[^\\/]+\$".toRegex()
manager.listResources("flows") { path -> path.endsWith(CobblemonScripts.MOLANG_EXTENSION) }.forEach { (identifier, resource) ->
resource.openAsReader().use { stream ->
stream.buffered().use { reader ->
try {
val expression = reader.readText().asExpressionLike()
val predicate: Predicate<ResourceLocation> = Predicate { path -> path.endsWith(CobblemonScripts.MOLANG_EXTENSION) }
val event = folderBeforeNameRegex.find(identifier.path)?.groupValues?.get(1)
?: throw IllegalArgumentException("Invalid flow path: $identifier. Should have a folder structure that includes the name of the event being flowed.")
manager.listResources("callbacks", predicate)
.plus(manager.listResources("flows", predicate)) // old name
.forEach { (identifier, resource) ->
resource.openAsReader().use { stream ->
stream.buffered().use { reader ->
try {
val expression = reader.readText().asExpressionLike()
val flowKey = ResourceLocation.fromNamespaceAndPath(identifier.namespace, event)
unsortedFlows.putIfAbsent(flowKey, mutableListOf())
unsortedFlows[flowKey]!!.add(identifier.path to expression)
} catch (exception: Exception) {
throw ExecutionException("Error loading MoLang script for flow: $identifier", exception)
val event = folderBeforeNameRegex.find(identifier.path)?.groupValues?.get(1)
?: throw IllegalArgumentException("Invalid callback path: $identifier. Should have a folder structure that includes the name of the event to callback.")
val callbackKey = ResourceLocation.fromNamespaceAndPath(identifier.namespace, event)
unsortedCallbacks.putIfAbsent(callbackKey, mutableListOf())
unsortedCallbacks[callbackKey]!!.add(identifier.path to expression)
} catch (exception: Exception) {
throw ExecutionException("Error loading MoLang script for callback: $identifier", exception)
}
}
}
}
}
unsortedFlows.forEach { (key, value) ->
unsortedCallbacks.forEach { (key, value) ->
value.sortBy { it.first }
if (key.path.startsWith("flows/client/")) {
clientFlows.getOrPut(key) { mutableListOf() }.addAll(value.map { it.second })
if (key.path.startsWith("flows/client/") || key.path.startsWith("callbacks/client/")) {
clientCallbacks.getOrPut(key) { mutableListOf() }.addAll(value.map { it.second })
} else {
flows.getOrPut(key) { mutableListOf() }.addAll(value.map { it.second })
callbacks.getOrPut(key) { mutableListOf() }.addAll(value.map { it.second })
}
}
Cobblemon.LOGGER.info("Loaded ${flows.size} flows and ${clientFlows.size} client flows")
Cobblemon.LOGGER.info("Loaded ${callbacks.size} callbacks and ${clientCallbacks.size} client callbacks")
observable.emit(this)
}
@@ -93,7 +99,7 @@ object CobblemonFlows : DataRegistry {
functions: Map<String, (MoParams) -> Any> = emptyMap(),
cancelable: Cancelable? = null
) {
if (eventResourceLocation !in flows) return
if (eventResourceLocation !in callbacks) return
if (cancelable == null) {
runtime.environment.query.functions.remove("cancel")
@@ -106,7 +112,7 @@ object CobblemonFlows : DataRegistry {
functions.forEach { (name, function) -> runtime.environment.query.addFunction(name, function) }
flows[eventResourceLocation]?.forEach {
callbacks[eventResourceLocation]?.forEach {
if (cancelable != null && cancelable.isCanceled) {
return
}
@@ -26,10 +26,10 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
.networkSynchronized(PokemonItemComponent.PACKET_CODEC)
.build())
val HELD_ITEM_REP: DataComponentType<HeldItemCapableComponent> = DataComponentType.builder<HeldItemCapableComponent>()
.persistent(HeldItemCapableComponent.CODEC)
.networkSynchronized(HeldItemCapableComponent.PACKET_CODEC)
.build()
val HELD_ITEM_EFFECT: DataComponentType<HeldItemEffectComponent> = create("held_item_effect", DataComponentType.builder<HeldItemEffectComponent>()
.persistent(HeldItemEffectComponent.CODEC)
.networkSynchronized(HeldItemEffectComponent.PACKET_CODEC)
.build())
val BAIT: DataComponentType<RodBaitComponent> = create("bait", DataComponentType.builder<RodBaitComponent>()
.persistent(RodBaitComponent.CODEC)
@@ -86,6 +86,7 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:ingredient"), INGREDIENT)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:food"), FOOD)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:mob_effects"), MOB_EFFECTS)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:held_item_effect"), HELD_ITEM_EFFECT)
}
override val registry = BuiltInRegistries.DATA_COMPONENT_TYPE
@@ -22,6 +22,7 @@ import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.block.MintBlock
import com.cobblemon.mod.common.block.MintBlock.MintType
import com.cobblemon.mod.common.client.pokedex.PokedexType
import com.cobblemon.mod.common.block.campfirepot.CampfirePotColor
import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
import com.cobblemon.mod.common.item.*
import com.cobblemon.mod.common.item.armor.CobblemonArmorTrims
@@ -192,19 +193,19 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
val campfire_pots = mutableListOf<CampfirePotItem>()
@JvmField
val CAMPFIRE_POT_RED = campfirePotItem(CobblemonBlocks.RED_CAMPFIRE_POT, "red")
val CAMPFIRE_POT_RED = campfirePotItem(CobblemonBlocks.RED_CAMPFIRE_POT, CampfirePotColor.RED)
@JvmField
val CAMPFIRE_POT_YELLOW = campfirePotItem(CobblemonBlocks.YELLOW_CAMPFIRE_POT, "yellow")
val CAMPFIRE_POT_YELLOW = campfirePotItem(CobblemonBlocks.YELLOW_CAMPFIRE_POT, CampfirePotColor.YELLOW)
@JvmField
val CAMPFIRE_POT_GREEN = campfirePotItem(CobblemonBlocks.GREEN_CAMPFIRE_POT, "green")
val CAMPFIRE_POT_GREEN = campfirePotItem(CobblemonBlocks.GREEN_CAMPFIRE_POT, CampfirePotColor.GREEN)
@JvmField
val CAMPFIRE_POT_BLUE = campfirePotItem(CobblemonBlocks.BLUE_CAMPFIRE_POT, "blue")
val CAMPFIRE_POT_BLUE = campfirePotItem(CobblemonBlocks.BLUE_CAMPFIRE_POT, CampfirePotColor.BLUE)
@JvmField
val CAMPFIRE_POT_PINK = campfirePotItem(CobblemonBlocks.PINK_CAMPFIRE_POT, "pink")
val CAMPFIRE_POT_PINK = campfirePotItem(CobblemonBlocks.PINK_CAMPFIRE_POT, CampfirePotColor.PINK)
@JvmField
val CAMPFIRE_POT_BLACK = campfirePotItem(CobblemonBlocks.BLACK_CAMPFIRE_POT, "black")
val CAMPFIRE_POT_BLACK = campfirePotItem(CobblemonBlocks.BLACK_CAMPFIRE_POT, CampfirePotColor.BLACK)
@JvmField
val CAMPFIRE_POT_WHITE = campfirePotItem(CobblemonBlocks.WHITE_CAMPFIRE_POT, "white")
val CAMPFIRE_POT_WHITE = campfirePotItem(CobblemonBlocks.WHITE_CAMPFIRE_POT, CampfirePotColor.WHITE)
@JvmField
val HEARTY_GRAINS = compostableItem("hearty_grains", ItemNameBlockItem(CobblemonBlocks.HEARTY_GRAINS, Properties().rarity(Rarity.COMMON)))
@@ -400,7 +401,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val OLD_GATEAU = regionalFoodItem("old_gateau", 64, 9, 0.1335f, false)
@JvmField
val CASTELIACONE = regionalFoodItem("casteliacone", 64, 5, .28f, false)
val CASTELIACONE = regionalFoodItem("casteliacone", 64, 5, 0.28f, false)
@JvmField
val LUMIOSE_GALETTE = regionalFoodItem("lumiose_galette", 64, 4, 0.4f, false)
@JvmField
@@ -695,7 +696,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val SUPERB_REMEDY = compostableItem("superb_remedy", RemedyItem(RemedyItem.SUPERB), 0.85f)
@JvmField
val MOOMOO_MILK = create("moomoo_milk", CobblemonItem(Item.Properties().craftRemainder(Items.GLASS_BOTTLE)))
val MOOMOO_MILK = create("moomoo_milk", CobblemonItem(Item.Properties().craftRemainder(Items.GLASS_BOTTLE).stacksTo((16))))
@JvmField
val POTION = create("potion", PotionItem(PotionType.POTION))
@@ -1500,8 +1501,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
}
)
private fun campfirePotItem(block: Block, type: String): CampfirePotItem {
val item = create("campfire_pot_${type}", CampfirePotItem(block))
private fun campfirePotItem(block: Block, color: CampfirePotColor): CampfirePotItem {
val item = create("campfire_pot_${color.suffix}", CampfirePotItem(block, color))
campfire_pots.add(item)
return item
}
@@ -1648,4 +1649,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()))
}
}
}
@@ -39,6 +39,7 @@ object CobblemonMechanics : DataRegistry {
var potions = PotionsMechanic()
override fun sync(player: ServerPlayer) {}
override fun reload(manager: ResourceManager) {
remedies = loadMechanic(manager, "remedies", RemediesMechanic::class.java)
berries = loadMechanic(manager, "berries", BerriesMechanic::class.java)
@@ -9,8 +9,8 @@
package com.cobblemon.mod.common
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.client.net.OpenBehaviourEditorHandler
import com.cobblemon.mod.common.client.net.CalculateSeatPositionsHandler
import com.cobblemon.mod.common.client.net.OpenBehaviourEditorHandler
import com.cobblemon.mod.common.client.net.PlayerInteractOptionsHandler
import com.cobblemon.mod.common.client.net.SetClientPlayerDataHandler
import com.cobblemon.mod.common.client.net.animation.PlayPosableAnimationHandler
@@ -20,7 +20,7 @@ import com.cobblemon.mod.common.client.net.callback.party.OpenPartyCallbackHandl
import com.cobblemon.mod.common.client.net.callback.partymove.OpenPartyMoveCallbackHandler
import com.cobblemon.mod.common.client.net.cooking.ToggleCookingPotLidHandler
import com.cobblemon.mod.common.client.net.data.DataRegistrySyncPacketHandler
import com.cobblemon.mod.common.client.net.data.UnlockReloadPacketHandler
import com.cobblemon.mod.common.client.net.data.RideSettingsSyncHandler
import com.cobblemon.mod.common.client.net.dialogue.DialogueClosedHandler
import com.cobblemon.mod.common.client.net.dialogue.DialogueOpenedHandler
import com.cobblemon.mod.common.client.net.effect.PokeSnackBlockParticlesHandler
@@ -65,8 +65,8 @@ import com.cobblemon.mod.common.client.net.trade.TradeProcessStartedHandler
import com.cobblemon.mod.common.client.net.trade.TradeStartedHandler
import com.cobblemon.mod.common.client.net.trade.TradeUpdatedHandler
import com.cobblemon.mod.common.net.PacketRegisterInfo
import com.cobblemon.mod.common.net.messages.client.OpenBehaviourEditorPacket
import com.cobblemon.mod.common.net.messages.client.CalculateSeatPositionsPacket
import com.cobblemon.mod.common.net.messages.client.OpenBehaviourEditorPacket
import com.cobblemon.mod.common.net.messages.client.PlayerInteractOptionsPacket
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
@@ -157,6 +157,7 @@ import com.cobblemon.mod.common.net.messages.server.callback.party.PartyPokemonS
import com.cobblemon.mod.common.net.messages.server.callback.party.PartySelectCancelledPacket
import com.cobblemon.mod.common.net.messages.server.callback.partymove.PartyMoveSelectCancelledPacket
import com.cobblemon.mod.common.net.messages.server.callback.partymove.PartyPokemonMoveSelectedPacket
import com.cobblemon.mod.common.net.messages.server.debug.ServerboundUpdateRidingSettingsPacket
import com.cobblemon.mod.common.net.messages.server.debug.ServerboundUpdateRidingStatRangePacket
import com.cobblemon.mod.common.net.messages.server.debug.ServerboundUpdateRidingStatsPacket
import com.cobblemon.mod.common.net.messages.server.dialogue.EscapeDialoguePacket
@@ -170,11 +171,11 @@ import com.cobblemon.mod.common.net.messages.server.pasture.UnpasturePokemonPack
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.FinishScanningPacket
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.StartScanningPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.interact.InteractPokemonPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetItemHiddenPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetNicknamePacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetActiveMarkPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetMarkingsPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.ServerboundUpdateRidingStatePacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetActiveMarkPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetItemHiddenPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetMarkingsPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetNicknamePacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.evolution.AcceptEvolutionPacket
import com.cobblemon.mod.common.net.messages.server.riding.DismountPokemonPacket
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateDriverInputPacket
@@ -208,6 +209,7 @@ import com.cobblemon.mod.common.net.serverhandling.callback.party.PartyPokemonSe
import com.cobblemon.mod.common.net.serverhandling.callback.party.PartySelectCancelledHandler
import com.cobblemon.mod.common.net.serverhandling.callback.partymove.PartyMoveSelectCancelledHandler
import com.cobblemon.mod.common.net.serverhandling.callback.partymove.PartyPokemonMoveSelectedHandler
import com.cobblemon.mod.common.net.serverhandling.debug.ServerboundUpdateRidingSettingsHandler
import com.cobblemon.mod.common.net.serverhandling.debug.ServerboundUpdateRidingStatRangeHandler
import com.cobblemon.mod.common.net.serverhandling.debug.ServerboundUpdateRidingStatsHandler
import com.cobblemon.mod.common.net.serverhandling.dialogue.EscapeDialogueHandler
@@ -222,11 +224,11 @@ import com.cobblemon.mod.common.net.serverhandling.pasture.UnpasturePokemonHandl
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.FinishScanningHandler
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.StartScanningHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.interact.InteractPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetItemHiddenHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetNicknameHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetActiveMarkHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetMarkingsHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.ServerboundUpdateRidingStateHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetActiveMarkHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetItemHiddenHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetMarkingsHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetNicknameHandler
import com.cobblemon.mod.common.net.serverhandling.riding.DismountPokemonPacketHandler
import com.cobblemon.mod.common.net.serverhandling.riding.DriverInputPacketHandler
import com.cobblemon.mod.common.net.serverhandling.starter.RequestStarterScreenHandler
@@ -310,6 +312,7 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(MarksPotentialUpdatePacket.ID, MarksPotentialUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(MarkingsUpdatePacket.ID, MarkingsUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(RideBoostsUpdatePacket.ID, RideBoostsUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(RideStaminaUpdatePacket.ID, RideStaminaUpdatePacket::decode, PokemonUpdatePacketHandler()))
// Evolution start
list.add(PacketRegisterInfo(AddEvolutionPacket.ID, AddEvolutionPacket::decode, PokemonUpdatePacketHandler()))
@@ -388,7 +391,6 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(BerryRegistrySyncPacket.ID, BerryRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(SpeciesRegistrySyncPacket.ID, SpeciesRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(PropertiesCompletionRegistrySyncPacket.ID, PropertiesCompletionRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(UnlockReloadPacket.ID, UnlockReloadPacket::decode, UnlockReloadPacketHandler))
list.add(PacketRegisterInfo(StandardSpeciesFeatureSyncPacket.ID, StandardSpeciesFeatureSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(GlobalSpeciesFeatureSyncPacket.ID, GlobalSpeciesFeatureSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(SpeciesFeatureAssignmentSyncPacket.ID, SpeciesFeatureAssignmentSyncPacket::decode, DataRegistrySyncPacketHandler()))
@@ -401,10 +403,11 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(DexEntrySyncPacket.ID, DexEntrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(SpawnBaitRegistrySyncPacket.ID, SpawnBaitRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(SeasoningRegistrySyncPacket.ID, SeasoningRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(FlowRegistrySyncPacket.ID, FlowRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(CallbackRegistrySyncPacket.ID, CallbackRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(CosmeticItemAssignmentSyncPacket.ID, CosmeticItemAssignmentSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(BehaviourSyncPacket.ID, BehaviourSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(MarkRegistrySyncPacket.ID, MarkRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(RideSettingsSyncPacket.ID, RideSettingsSyncPacket::decode, RideSettingsSyncHandler))
// Effects
list.add(PacketRegisterInfo(SpawnSnowstormParticlePacket.ID, SpawnSnowstormParticlePacket::decode, SpawnSnowstormParticleHandler))
@@ -578,6 +581,7 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(ServerboundUpdateRidingStatePacket.ID, ServerboundUpdateRidingStatePacket::decode, ServerboundUpdateRidingStateHandler))
list.add(PacketRegisterInfo(ServerboundUpdateRidingStatsPacket.ID, ServerboundUpdateRidingStatsPacket::decode, ServerboundUpdateRidingStatsHandler))
list.add(PacketRegisterInfo(ServerboundUpdateRidingStatRangePacket.ID, ServerboundUpdateRidingStatRangePacket::decode, ServerboundUpdateRidingStatRangeHandler))
list.add(PacketRegisterInfo(ServerboundUpdateRidingSettingsPacket.ID, ServerboundUpdateRidingSettingsPacket::decode, ServerboundUpdateRidingSettingsHandler))
list.add(PacketRegisterInfo(DismountPokemonPacket.ID, DismountPokemonPacket::decode, DismountPokemonPacketHandler))
list.add(PacketRegisterInfo(ServerboundUpdateDriverInputPacket.ID, ServerboundUpdateDriverInputPacket::decode, DriverInputPacketHandler))
@@ -0,0 +1,106 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.data.DataRegistry
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.air.BirdSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.air.GliderSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.air.HelicopterSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.air.HoverSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.air.JetSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.air.RocketSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.land.MinekartSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.land.VehicleSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BoatSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BurstSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.DolphinSettings
import com.cobblemon.mod.common.net.messages.client.data.RideSettingsSyncPacket
import com.cobblemon.mod.common.util.adapters.ExpressionAdapter
import com.cobblemon.mod.common.util.adapters.ExpressionLikeAdapter
import com.cobblemon.mod.common.util.adapters.FloatNumberRangeAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.GsonBuilder
import net.minecraft.advancements.critereon.MinMaxBounds
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.server.packs.resources.ResourceManager
object CobblemonRideSettings : DataRegistry {
override val id: ResourceLocation = cobblemonResource("ride_settings")
override val type = PackType.SERVER_DATA
override val observable = SimpleObservable<CobblemonRideSettings>()
val gson = GsonBuilder()
.setPrettyPrinting()
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
.registerTypeAdapter(MinMaxBounds.Doubles::class.java, FloatNumberRangeAdapter)
.create()
var bird = BirdSettings()
var glider = GliderSettings()
var helicopter = HelicopterSettings()
var hover = HoverSettings()
var jet = JetSettings()
var rocket = RocketSettings()
var horse = HorseSettings()
var minekart = MinekartSettings()
var vehicle = VehicleSettings()
var boat = BoatSettings()
var burst = BurstSettings()
var dolphin = DolphinSettings()
override fun sync(player: ServerPlayer) {
player.sendPacket(
RideSettingsSyncPacket(
bird = bird,
glider = glider,
helicopter = helicopter,
hover = hover,
jet = jet,
rocket = rocket,
horse = horse,
minekart = minekart,
vehicle = vehicle,
boat = boat,
burst = burst,
dolphin = dolphin
)
)
}
override fun reload(manager: ResourceManager) {
bird = loadStyle(manager, "bird", BirdSettings::class.java)
glider = loadStyle(manager, "glider", GliderSettings::class.java)
helicopter = loadStyle(manager, "helicopter", HelicopterSettings::class.java)
hover = loadStyle(manager, "hover", HoverSettings::class.java)
jet = loadStyle(manager, "jet", JetSettings::class.java)
rocket = loadStyle(manager, "rocket", RocketSettings::class.java)
horse = loadStyle(manager, "horse", HorseSettings::class.java)
minekart = loadStyle(manager, "minekart", MinekartSettings::class.java)
vehicle = loadStyle(manager, "vehicle", VehicleSettings::class.java)
boat = loadStyle(manager, "boat", BoatSettings::class.java)
burst = loadStyle(manager, "burst", BurstSettings::class.java)
dolphin = loadStyle(manager, "dolphin", DolphinSettings::class.java)
}
private fun <T : RidingBehaviourSettings> loadStyle(manager: ResourceManager, name: String, clazz: Class<T>): T {
manager.getResourceOrThrow(cobblemonResource("ride_settings/$name.json")).open().use {
return gson.fromJson(it.reader(), clazz)
}
}
}
@@ -78,6 +78,7 @@ object Abilities : DataRegistry {
fun all() = this.abilityMap.values.toList()
fun first() = this.abilityMap.values.first()
fun get(name: String) = abilityMap[name.lowercase()]
fun getOrDummy(name: String) = get(name) ?: DUMMY
fun getOrException(name: String) = get(name) ?: throw IllegalArgumentException("Unable to find ability of name: $name")
fun count() = this.abilityMap.size
@@ -49,10 +49,9 @@ class AbilityTemplate(
@JvmStatic
val CODEC: Codec<AbilityTemplate> = CodecUtils.createByStringCodec(
Abilities::get,
Abilities::getOrDummy,
AbilityTemplate::name
) { id -> "No ability for ID $id" }
}
}
@@ -117,7 +117,7 @@ Example behaviour (`uses_healing_machine.json`):
"configurations": [
{
"type": "add_tasks_to_activity",
"activity": "idle",
"activity": "minecraft:idle",
"tasksByPriority": {
"1": [
"heal_using_healing_machine",
@@ -12,11 +12,9 @@ import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.util.Either
import kotlin.jvm.optionals.getOrNull
import net.minecraft.world.entity.LivingEntity
@@ -27,7 +25,9 @@ import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.sensing.SensorType
/**
* Checks nearby entities and counts how many have nominated this entity as their leader.
* Checks nearby entities and counts how many have nominated this entity as their leader. We normally update this
* as people join and leave the herd, but this is a fallback to make sure we don't get stuck with an incorrect value
* like if the entities die or whatever.
*
* @author Hiroku
* @since June 17th, 2025
@@ -53,7 +53,7 @@ class FindAirTaskConfig : SingleTaskConfig {
it.absent(CobblemonMemories.PATH_COOLDOWN)
).apply(it) { walkTarget, pathCooldown ->
Trigger { world, entity, time ->
if (!entity.getIsSubmerged()) {
if (!entity.getIsSubmerged() || entity.isInLava) {
return@Trigger false
}
@@ -8,17 +8,16 @@
package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.api.storage.party.PartyStore
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.ai.ToleratedHerdLeader
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
@@ -53,24 +52,25 @@ class FindHerdLeaderTaskConfig : SingleTaskConfig {
it is PokemonEntity &&
it != currentLeader &&
it.pokemon.storeCoordinates.get()?.store !is PartyStore &&
it.getHerdSize() < it.behaviour.herd.maxSize
it.getHerdSize() < it.behaviour.herd.maxSize &&
!it.brain.hasMemoryValue(CobblemonMemories.POKEMON_DROWSY)
}.forEach { possibleLeader ->
possibleLeader as PokemonEntity
val matchingHerdLeader = entity.behaviour.herd.bestMatchLeader(entity, possibleLeader) ?: return@forEach
val alreadyIsALeader = possibleLeader.brain.hasMemoryValue(CobblemonMemories.HERD_SIZE)
val nestedHerdTier = currentLeaderEntity?.getHerdTier() ?: 0
if (nestedHerdTier >= matchingHerdLeader.tier) {
val bestMatchingHerdLeader = entity.behaviour.herd.bestMatchLeader(entity, possibleLeader) ?: return@forEach
val possibleLeaderIsAlreadyALeader = possibleLeader.brain.hasMemoryValue(CobblemonMemories.HERD_SIZE)
val currentLeaderHerdTier = currentLeaderEntity?.getHerdTier() ?: entity.getHerdTier()
if (currentLeaderHerdTier >= bestMatchingHerdLeader.tier) {
return@forEach // This leader is following a leader of a higher tier, we can't replace them
}
val distance = entity.distanceTo(possibleLeader)
if (matchingHerdLeader.tier < bestTier) {
if (bestMatchingHerdLeader.tier < bestTier) {
return@forEach // No need to check further if this leader is worse than the best found so far
} else if ((distance > bestDistance && !alreadyIsALeader) && matchingHerdLeader.tier == bestTier) {
} else if ((distance > bestDistance && !possibleLeaderIsAlreadyALeader) && bestMatchingHerdLeader.tier == bestTier) {
return@forEach // No need to check further if this leader is farther than the best found so far, unless they are already a leader - I'd prefer to join a pack
} else {
bestLeader = possibleLeader
bestDistance = distance
bestTier = matchingHerdLeader.tier
bestTier = bestMatchingHerdLeader.tier
}
}
return bestLeader ?: currentLeaderEntity
@@ -118,6 +118,7 @@ class FindHerdLeaderTaskConfig : SingleTaskConfig {
herdLeader.set(bestLeader.uuid.toString())
bestLeader.brain.eraseMemory(CobblemonMemories.HERD_LEADER)
bestLeader.adjustHerdSize(1)
return@Trigger true
}
}
@@ -14,15 +14,19 @@ import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.FindRestingPlaceTask
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveBoolean
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.memory.MemoryModuleType
class FindRestingPlaceTaskConfig : SingleTaskConfig {
val condition: ExpressionLike = "true".asExpressionLike()
val horizontalSearchDistance: ExpressionOrEntityVariable = Either.left("16".asExpression())
val verticalSearchDistance: ExpressionOrEntityVariable = Either.left("5".asExpression())
@@ -31,6 +35,13 @@ class FindRestingPlaceTaskConfig : SingleTaskConfig {
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (entity !is PokemonEntity) {
return null
}
if (!behaviourConfigurationContext.runtime.resolveBoolean(condition)) {
return null
}
behaviourConfigurationContext.addMemories(
CobblemonMemories.PATH_COOLDOWN,
CobblemonMemories.POKEMON_DROWSY,
@@ -9,44 +9,36 @@
package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.ai.config.task.SharedEntityVariables.FLEE_DESIRED_DISTANCE
import com.cobblemon.mod.common.api.ai.config.task.SharedEntityVariables.FLEE_SPEED_MULTIPLIER
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.util.withQueryValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.ai.FleeFromAttackerTask
import com.cobblemon.mod.common.util.asExpression
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.SetWalkTargetAwayFrom
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.sensing.SensorType
class FleeAttackerTaskConfig : SingleTaskConfig {
var condition = booleanVariable(SharedEntityVariables.FEAR_CATEGORY, "flee_attacker", true).asExpressible()
var speedMultiplier = numberVariable(SharedEntityVariables.FEAR_CATEGORY, FLEE_SPEED_MULTIPLIER, 0.5).asExpressible()
var desiredDistance = numberVariable(SharedEntityVariables.FEAR_CATEGORY, FLEE_DESIRED_DISTANCE, 9).asExpressible()
val avoidDurationTicks: ExpressionOrEntityVariable = Either.left("600".asExpression())
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(
condition,
speedMultiplier,
desiredDistance
).asVariables()
override fun getVariables(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): List<MoLangConfigVariable> {
return listOf(avoidDurationTicks).asVariables()
}
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (!condition.resolveBoolean(behaviourConfigurationContext.runtime) || entity !is PathfinderMob) return null
behaviourConfigurationContext.addMemories(MemoryModuleType.HURT_BY_ENTITY, MemoryModuleType.WALK_TARGET)
behaviourConfigurationContext.addSensors(SensorType.HURT_BY)
val speedMultiplier = speedMultiplier.resolveFloat(behaviourConfigurationContext.runtime)
val desiredDistance = desiredDistance.resolveInt(behaviourConfigurationContext.runtime)
return WrapperLivingEntityTask(
SetWalkTargetAwayFrom.entity(MemoryModuleType.HURT_BY_ENTITY, speedMultiplier, desiredDistance, false),
PathfinderMob::class.java
): BehaviorControl<in LivingEntity> {
behaviourConfigurationContext.addMemories(
MemoryModuleType.HURT_BY_ENTITY,
MemoryModuleType.AVOID_TARGET
)
behaviourConfigurationContext.addSensors(SensorType.HURT_BY)
return FleeFromAttackerTask.create(avoidDurationTicks.asExpression())
}
}
@@ -57,7 +57,7 @@ class GoToLandTaskConfig : SingleTaskConfig {
it.absent(CobblemonMemories.PATH_COOLDOWN)
).apply(it) { walkTarget, pathCooldown ->
Trigger { world, entity, time ->
if (!entity.isInLiquid) {
if (!entity.isInWater) {
return@Trigger false
}
@@ -11,15 +11,20 @@ package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.CobblemonSensors
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.GoToSleepTask
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.resolveBoolean
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.memory.MemoryModuleType
class GoToSleepTaskConfig : SingleTaskConfig {
var onlyFromStatus: ExpressionOrEntityVariable = Either.left("false".asExpression())
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = emptyList<MoLangConfigVariable>()
override fun createTask(
entity: LivingEntity,
@@ -34,6 +39,6 @@ class GoToSleepTaskConfig : SingleTaskConfig {
CobblemonMemories.POKEMON_SLEEPING
)
behaviourConfigurationContext.addSensors(CobblemonSensors.POKEMON_DROWSY)
return WrapperLivingEntityTask(GoToSleepTask.create(), PokemonEntity::class.java)
return WrapperLivingEntityTask(GoToSleepTask.create(onlyFromStatus.resolveBoolean(behaviourConfigurationContext.runtime)), PokemonEntity::class.java)
}
}
@@ -10,17 +10,28 @@ package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.npc.ai.LookAtSpeakerTask
import com.cobblemon.mod.common.util.asExpression
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.memory.MemoryModuleType
class LookAtSpeakerTaskConfig : SingleTaskConfig {
val condition: ExpressionOrEntityVariable = Either.left("true".asExpression())
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = emptyList<MoLangConfigVariable>()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
) = LookAtSpeakerTask.create().also {
behaviourConfigurationContext.addMemories(MemoryModuleType.LOOK_TARGET, CobblemonMemories.DIALOGUES)
): BehaviorControl<in LivingEntity>? {
if (!condition.resolveBoolean(behaviourConfigurationContext.runtime)) {
return null
}
return LookAtSpeakerTask.create().also {
behaviourConfigurationContext.addMemories(MemoryModuleType.LOOK_TARGET, CobblemonMemories.DIALOGUES)
}
}
}
@@ -8,15 +8,14 @@
package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.util.Either
import java.util.UUID
import net.minecraft.world.entity.LivingEntity
@@ -82,6 +81,8 @@ class MaintainHerdLeaderTaskConfig : SingleTaskConfig {
if (bestLeader != null && bestLeader != leader) {
bestLeader.brain.eraseMemory(CobblemonMemories.HERD_LEADER)
entity.brain.setMemory(CobblemonMemories.HERD_LEADER, bestLeader.uuid.toString())
bestLeader.adjustHerdSize(1)
leader.adjustHerdSize(-1)
}
}
return@Trigger true
@@ -162,11 +162,11 @@ on the entity type and this piggy-backs off the Minecraft list of aggressive mob
The 'calm_down' type creates a task that will make the entity calm down when it is no longer being hurt and is far from hostiles and its attacker.
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
### flee_attacker
The 'flee_attacker' type creates a task that will make the entity flee from its attacker when it is hurt.
### walk_away_from_avoid_target
The 'flee_attacker' type creates a task that will make the entity flee from its avoid target.
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
- `speedMultiplier`: A MoLang expression that determines the speed multiplier for fleeing from the attacker. Defaults to 0.5 which is an elevated speed to usual.
- `desiredDistance`: A MoLang expression that determines the distance from the attacker that the entity should try to maintain. Defaults to 9.
- `speedMultiplier`: A MoLang expression that determines the speed multiplier for fleeing from the avoid target. Defaults to 0.5 which is an elevated speed to usual.
- `desiredDistance`: A MoLang expression that determines the distance from the target that the entity should try to maintain. Defaults to 9.
### flee_nearest_hostile
The 'flee_nearest_hostile' type creates a task that will make the entity flee from the nearest hostile entity when it is hurt.
@@ -78,8 +78,9 @@ interface TaskConfig {
cobblemonResource("switch_to_panic_when_hurt") to SwitchToPanicWhenHurtTaskConfig::class.java,
cobblemonResource("switch_to_panic_when_hostiles_nearby") to SwitchToPanicWhenHostilesNearbyTaskConfig::class.java,
cobblemonResource("calm_down") to CalmDownTaskConfig::class.java,
cobblemonResource("flee_attacker") to FleeAttackerTaskConfig::class.java,
cobblemonResource("walk_away_from_avoid_target") to WalkAwayFromAvoidTargetTaskConfig::class.java,
cobblemonResource("flee_nearest_hostile") to FleeNearestHostileTaskConfig::class.java,
cobblemonResource("flee_attacker") to FleeAttackerTaskConfig::class.java,
cobblemonResource("fly_in_circles") to FlyInCirclesTaskConfig::class.java,
cobblemonResource("run_script") to RunScript::class.java,
cobblemonResource("look_in_direction") to LookInDirectionTaskConfig::class.java,
@@ -0,0 +1,53 @@
/*
* 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.ai.config.task
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.ai.config.task.SharedEntityVariables.FLEE_DESIRED_DISTANCE
import com.cobblemon.mod.common.api.ai.config.task.SharedEntityVariables.FLEE_SPEED_MULTIPLIER
import com.cobblemon.mod.common.util.asExpression
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.SetWalkTargetAwayFrom
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.sensing.SensorType
class WalkAwayFromAvoidTargetTaskConfig : SingleTaskConfig {
var condition: ExpressionOrEntityVariable = Either.left("true".asExpression())
var speedMultiplier = numberVariable(SharedEntityVariables.FEAR_CATEGORY, FLEE_SPEED_MULTIPLIER, 0.7).asExpressible()
var desiredDistance = numberVariable(SharedEntityVariables.FEAR_CATEGORY, FLEE_DESIRED_DISTANCE, 9).asExpressible()
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(
condition,
speedMultiplier,
desiredDistance
).asVariables()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (!condition.resolveBoolean(behaviourConfigurationContext.runtime) || entity !is PathfinderMob) return null
behaviourConfigurationContext.addMemories(MemoryModuleType.HURT_BY_ENTITY, MemoryModuleType.WALK_TARGET)
behaviourConfigurationContext.addSensors(SensorType.HURT_BY)
val speedMultiplier = speedMultiplier.resolveFloat(behaviourConfigurationContext.runtime)
val desiredDistance = desiredDistance.resolveInt(behaviourConfigurationContext.runtime)
return WrapperLivingEntityTask(
SetWalkTargetAwayFrom.entity(MemoryModuleType.HURT_BY_ENTITY, speedMultiplier, desiredDistance, false),
PathfinderMob::class.java
)
}
}
@@ -14,6 +14,8 @@ import com.cobblemon.mod.common.api.ai.CobblemonWanderControl
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.entity.OmniPathingEntity
import com.cobblemon.mod.common.entity.ai.CobblemonRandomSurfacePos
import com.cobblemon.mod.common.entity.ai.CobblemonWalkTarget
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.mainThreadRuntime
@@ -26,6 +28,7 @@ import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.BehaviorUtils
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
@@ -37,27 +40,31 @@ import net.minecraft.world.level.pathfinder.PathType
class WanderTaskConfig : SingleTaskConfig {
companion object {
const val WANDER = "wander" // Category
const val MAX_LOOK_DOWN_DISTANCE = 64
}
val condition = booleanVariable(WANDER, "wanders", true).asExpressible()
val wanderChance = numberVariable(WANDER, "wander_chance", 1/(20 * 6F)).asExpressible()
val wanderChance = numberVariable(WANDER, "wander_chance", 1 / (20 * 6F)).asExpressible()
val horizontalRange = numberVariable(WANDER, "horizontal_wander_range", 10).asExpressible()
val verticalRange = numberVariable(WANDER, "vertical_wander_range", 5).asExpressible()
val speedMultiplier = numberVariable(SharedEntityVariables.MOVEMENT_CATEGORY, SharedEntityVariables.WALK_SPEED, 0.35).asExpressible()
val avoidTargetingAir: ExpressionOrEntityVariable = Either.left("true".asExpression()) // Whether to avoid air blocks when wandering
val speedMultiplier =
numberVariable(SharedEntityVariables.MOVEMENT_CATEGORY, SharedEntityVariables.WALK_SPEED, 0.35).asExpressible()
val avoidTargetingAir: ExpressionOrEntityVariable =
Either.left("true".asExpression()) // Whether to avoid air blocks when wandering
val minimumHeight: ExpressionOrEntityVariable = Either.left("0".asExpression()) // Height off the ground
val maximumHeight: ExpressionOrEntityVariable = Either.left("-1".asExpression()) // Height off the ground
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(
condition,
wanderChance,
horizontalRange,
verticalRange,
speedMultiplier,
avoidTargetingAir,
minimumHeight,
maximumHeight
).asVariables()
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) =
listOf(
condition,
wanderChance,
horizontalRange,
verticalRange,
speedMultiplier,
avoidTargetingAir,
minimumHeight,
maximumHeight
).asVariables()
private fun applyHeightConstraints(
pos: BlockPos,
@@ -73,10 +80,18 @@ class WanderTaskConfig : SingleTaskConfig {
val altitude = if (!block.isAir) {
0
} else {
(1..64).firstOrNull {
(1..MAX_LOOK_DOWN_DISTANCE).firstOrNull {
val newPos = pos.below(it)
return@firstOrNull !world.getBlockState(newPos).isAir
} ?: Int.MAX_VALUE
if (!world.getBlockState(newPos).isAir) {
return@firstOrNull true
} else if (newPos.y <= world.minBuildHeight) {
// Don't adjust downward into the void (Mostly if we're in the end)
// This doesn't stop fliers from wandering off end islands,
// but it does stop them from diving directly into the void.
return pos
}
return@firstOrNull false
} ?: MAX_LOOK_DOWN_DISTANCE
}
if (altitude > maximumHeight && maximumHeight >= 0) {
@@ -117,7 +132,7 @@ class WanderTaskConfig : SingleTaskConfig {
it.registered(CobblemonMemories.WANDER_CONTROL)
).apply(it) { walkTarget, lookTarget, pathCooldown, wanderControl ->
Trigger { world, entity, time ->
if (entity !is PathfinderMob || entity.isUnderWater) {
if (entity !is PathfinderMob || (entity.isUnderWater && !(entity.canBreatheUnderwater() && !((entity as OmniPathingEntity).canSwimInWater())))) {
return@Trigger false
}
@@ -125,10 +140,9 @@ class WanderTaskConfig : SingleTaskConfig {
mainThreadRuntime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
val avoidsTargetingAir = avoidTargetingAir.resolveBoolean(mainThreadRuntime)
val wanderChance = mainThreadRuntime.resolveFloat(wanderChanceExpression)
if (wanderChance <= 0 || world.random.nextFloat() > wanderChance || !wanderControl.allowLand) {
if (wanderChance <= 0 || world.random.nextFloat() > wanderChance) {
return@Trigger false
}
pathCooldown.setWithExpiry(true, wanderControl.pathCooldownTicks.toLong())
val minimumHeight = minimumHeight.resolveInt(mainThreadRuntime)
val maximumHeight = maximumHeight.resolveInt(mainThreadRuntime)
@@ -141,21 +155,35 @@ class WanderTaskConfig : SingleTaskConfig {
HoverRandomPos.getPos(
entity,
horizontalRange.resolveInt(mainThreadRuntime),
maxOf(verticalRange.resolveInt(mainThreadRuntime), maximumHeight), // In case they fly up pretty high and need to find a way down
verticalRange.resolveInt(mainThreadRuntime),
entity.random.nextFloat() - 0.5,
entity.random.nextFloat() - 0.5,
Math.PI.toFloat(),
maximumHeight,
minimumHeight
)
} else if (entity.isUnderWater && entity.canBreatheUnderwater() && !((entity as OmniPathingEntity).canSwimInWater())) {
BehaviorUtils.getRandomSwimmablePos(
entity,
horizontalRange.resolveInt(mainThreadRuntime),
verticalRange.resolveInt(mainThreadRuntime)
)
} else if (entity is OmniPathingEntity && (entity.canWalkOnWater() || entity.canWalkOnLava())) {
CobblemonRandomSurfacePos.getPos(
entity,
horizontalRange.resolveInt(mainThreadRuntime),
verticalRange.resolveInt(mainThreadRuntime)
)
} else {
LandRandomPos.getPos(
entity,
horizontalRange.resolveInt(mainThreadRuntime),
verticalRange.resolveInt(mainThreadRuntime)
)
LandRandomPos.getPos(
entity,
horizontalRange.resolveInt(mainThreadRuntime),
verticalRange.resolveInt(mainThreadRuntime)
)
} ?: continue
if (targetVec == null) continue
pos = applyHeightConstraints(
pos = BlockPos.containing(targetVec),
minimumHeight = minimumHeight,
@@ -173,8 +201,17 @@ class WanderTaskConfig : SingleTaskConfig {
pos = pos,
speedModifier = speedMultiplier.resolveFloat(mainThreadRuntime),
completionRange = 0,
nodeTypeFilter = { nodeType -> nodeType !in listOf(PathType.WATER, PathType.WATER_BORDER) },
destinationNodeTypeFilter = { nodeType -> !avoidsTargetingAir || nodeType !in listOf(PathType.OPEN) }
nodeTypeFilter = { nodeType ->
entity.canBreatheUnderwater() || nodeType !in listOf(
PathType.WATER,
PathType.WATER_BORDER
)
},
destinationNodeTypeFilter = { nodeType ->
!avoidsTargetingAir || nodeType !in listOf(
PathType.OPEN
)
}
)
)
lookTarget.set(BlockPosTracker(pos.toVec3d().add(0.0, entity.eyeHeight.toDouble(), 0.0)))
@@ -31,15 +31,17 @@ import net.minecraft.world.entity.ai.behavior.BlockPosTracker
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.level.material.Fluids
import net.minecraft.world.level.pathfinder.PathType
import net.minecraft.world.phys.Vec3
import kotlin.math.ceil
class WaterWanderTaskConfig : SingleTaskConfig {
val condition = booleanVariable(WANDER, "water_wanders", true).asExpressible()
val wanderChance = numberVariable(WANDER, "water_wander_chance", 1/(20 * 3F)).asExpressible()
val speedMultiplier = numberVariable(SharedEntityVariables.MOVEMENT_CATEGORY, SharedEntityVariables.WALK_SPEED, 0.35).asExpressible()
val horizontalRange: ExpressionOrEntityVariable = Either.left("10.0".asExpression())
val verticalRange: ExpressionOrEntityVariable = Either.left("3.0".asExpression())
val verticalRange: ExpressionOrEntityVariable = Either.left("7.0".asExpression())
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext): List<MoLangConfigVariable> {
return listOf(condition, wanderChance, speedMultiplier).asVariables()
@@ -100,6 +102,33 @@ class WaterWanderTaskConfig : SingleTaskConfig {
return@Trigger false
}
// Move target pos down so that we don't move out of water too much
val requiredDepth = ceil(0.5 + entity.eyeHeight)
// Get the current depth of the block
var depth = 1
var testPos = pos.above()
var blockState = entity.level().getBlockState(testPos)
while (blockState.fluidState.type == Fluids.WATER) {
depth++
testPos = testPos!!.above()
blockState = entity.level().getBlockState(testPos)
}
if (depth < requiredDepth) {
// Iterate down until we reach the desired min depth or we reach a non water block
pos = pos.below()
blockState = entity.level().getBlockState(pos)
while (blockState.fluidState.type == Fluids.WATER && depth < requiredDepth) {
depth ++
pos = pos!!.below()
blockState = entity.level().getBlockState(pos)
}
}
if (depth < requiredDepth) {
return@Trigger false
}
walkTarget.set(
CobblemonWalkTarget(
pos = pos,
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.api.cooking
import net.minecraft.ChatFormatting
import net.minecraft.util.StringRepresentable
/**
@@ -18,16 +17,16 @@ import net.minecraft.util.StringRepresentable
* @author Licious
* @since November 28th, 2022
*/
enum class Flavour(val colour: Int, val chatFormatting: ChatFormatting): StringRepresentable {
SPICY(0x00FF00, ChatFormatting.RED),
DRY(0xFF0000, ChatFormatting.DARK_AQUA),
SWEET(0x0000FF, ChatFormatting.LIGHT_PURPLE),
BITTER(0xFFFF00, ChatFormatting.GREEN),
SOUR(0xFF00FF, ChatFormatting.YELLOW),
MILD(0xFF00FF, ChatFormatting.DARK_PURPLE);
enum class Flavour(val colour: Int): StringRepresentable {
SPICY(0xFA795E),
DRY(0x65AFF3),
SWEET(0xFF82CF),
BITTER(0x77C96A),
SOUR(0xF3D465),
MILD(0x9D99BF);
override fun getSerializedName() = name
companion object {
val CODEC = StringRepresentable.fromEnum<Flavour> { Flavour.entries.toTypedArray() }
}
}
}
@@ -9,13 +9,11 @@
package com.cobblemon.mod.common.api.cooking
import com.cobblemon.mod.common.CobblemonItemComponents
import com.cobblemon.mod.common.item.PokePuffItem
import com.cobblemon.mod.common.item.PokePuffItem.Companion.DISLIKED_FLAVOR_MULTIPLIER
import com.cobblemon.mod.common.item.PokePuffItem.Companion.LIKED_FLAVOR_MULTIPLIER
import com.cobblemon.mod.common.pokemon.Nature
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.item.ItemStack
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.world.item.ItemStack
object PokePuffUtils {
@@ -25,11 +23,12 @@ object PokePuffUtils {
val (baseFriendship) = calculateBaseFriendship(stack)
return when {
dominantFlavour == nature.favouriteFlavour -> (baseFriendship / LIKED_FLAVOR_MULTIPLIER).toInt()
dominantFlavour == Flavour.MILD && isNeutralNature -> (baseFriendship / 2.0).coerceAtLeast(1.0).toInt()
else -> (-baseFriendship / DISLIKED_FLAVOR_MULTIPLIER).toInt()
}
val multiplier = if (
dominantFlavour == nature.favouriteFlavour ||
(dominantFlavour == Flavour.MILD && isNeutralNature)
) LIKED_FLAVOR_MULTIPLIER else -DISLIKED_FLAVOR_MULTIPLIER
return (baseFriendship / multiplier).toInt()
}
fun calculateBaseFriendship(stack: ItemStack): Pair<Double, Double> {
@@ -77,8 +76,8 @@ object PokePuffUtils {
Flavour.SPICY -> "attack"
Flavour.DRY -> "special_attack"
Flavour.SWEET -> "speed"
Flavour.BITTER -> "special_defense"
Flavour.SOUR -> "defense"
Flavour.BITTER -> "special_defence"
Flavour.SOUR -> "defence"
Flavour.MILD -> "neutral"
}
}
@@ -18,14 +18,13 @@ import net.minecraft.resources.ResourceLocation
* @since August 5th, 2022
*/
interface DataProvider {
/**
* Registers a [DataRegistry] to listen for updates.
* The updates will automatically happen on the correct sides based on [DataRegistry.type].
*
* @param registry The [DataRegistry] being registered.
*/
fun <T : DataRegistry> register(registry: T): T
fun <T : DataRegistry> register(registry: T, reloadable: Boolean): T
/**
* Attempts to find a [DataRegistry] with the given [ResourceLocation].
@@ -29,6 +29,7 @@ class DialoguePage(
var id: String = "",
var speaker: String? = null,
var lines: MutableList<DialogueText> = mutableListOf(),
var textColor: String? = null,
var input: DialogueInput = DialogueNoInput(),
var background: ResourceLocation? = null,
var gibber: DialogueGibber? = null,
@@ -42,6 +43,7 @@ class DialoguePage(
id: String = "",
speaker: String? = null,
lines: Iterable<MutableComponent>,
textColor: String? = null,
input: DialogueInput = DialogueNoInput(),
gibber: DialogueGibber? = null,
background: ResourceLocation? = null,
@@ -53,6 +55,7 @@ class DialoguePage(
id = id,
speaker = speaker,
lines = lines.map { WrappedDialogueText(it) }.toMutableList(),
textColor = textColor,
input = input,
gibber = gibber,
background = background,
@@ -75,4 +78,4 @@ class DialoguePage(
)
)
}
}
}
@@ -10,8 +10,7 @@ package com.cobblemon.mod.common.api.events
import com.cobblemon.mod.common.api.events.battles.BattleFaintedEvent
import com.cobblemon.mod.common.api.events.battles.BattleFledEvent
import com.cobblemon.mod.common.api.events.battles.BattleStartedPostEvent
import com.cobblemon.mod.common.api.events.battles.BattleStartedPreEvent
import com.cobblemon.mod.common.api.events.battles.BattleStartedEvent
import com.cobblemon.mod.common.api.events.battles.BattleVictoryEvent
import com.cobblemon.mod.common.api.events.battles.instruction.FormeChangeEvent
import com.cobblemon.mod.common.api.events.battles.instruction.MegaEvolutionEvent
@@ -22,11 +21,7 @@ import com.cobblemon.mod.common.api.events.berry.BerryMutationOfferEvent
import com.cobblemon.mod.common.api.events.berry.BerryMutationResultEvent
import com.cobblemon.mod.common.api.events.berry.BerryYieldCalculationEvent
import com.cobblemon.mod.common.api.events.drops.LootDroppedEvent
import com.cobblemon.mod.common.api.events.entity.PokemonEntityLoadEvent
import com.cobblemon.mod.common.api.events.entity.PokemonEntitySaveEvent
import com.cobblemon.mod.common.api.events.entity.PokemonEntitySaveToWorldEvent
import com.cobblemon.mod.common.api.events.entity.SpawnBucketChosenEvent
import com.cobblemon.mod.common.api.events.entity.SpawnEvent
import com.cobblemon.mod.common.api.events.entity.*
import com.cobblemon.mod.common.api.events.farming.ApricornHarvestEvent
import com.cobblemon.mod.common.api.events.fishing.*
import com.cobblemon.mod.common.api.events.item.LeftoversCreatedEvent
@@ -43,11 +38,7 @@ import com.cobblemon.mod.common.api.events.pokemon.healing.PokemonHealedEvent
import com.cobblemon.mod.common.api.events.pokemon.interaction.ExperienceCandyUseEvent
import com.cobblemon.mod.common.api.events.pokemon.interaction.PokemonInteractionGUICreationEvent
import com.cobblemon.mod.common.api.events.starter.StarterChosenEvent
import com.cobblemon.mod.common.api.events.storage.ChangePCBoxWallpaperEvent
import com.cobblemon.mod.common.api.events.storage.ReleasePokemonEvent
import com.cobblemon.mod.common.api.events.storage.RenamePCBoxEvent
import com.cobblemon.mod.common.api.events.storage.WallpaperCollectionEvent
import com.cobblemon.mod.common.api.events.storage.WallpaperUnlockedEvent
import com.cobblemon.mod.common.api.events.storage.*
import com.cobblemon.mod.common.api.events.world.BigRootPropagatedEvent
import com.cobblemon.mod.common.api.reactive.CancelableObservable
import com.cobblemon.mod.common.api.reactive.EventObservable
@@ -63,69 +54,109 @@ object CobblemonEvents {
@JvmField
val POKEMON_PROPERTY_INITIALISED = SimpleObservable<Unit>()
@JvmField
val COBBLEMON_INITIALISED = SimpleObservable<Unit>()
@JvmField
val DATA_SYNCHRONIZED = SimpleObservable<ServerPlayer>()
@JvmField
val SHOULDER_MOUNT = CancelableObservable<ShoulderMountEvent>()
@JvmField
val FRIENDSHIP_UPDATED = EventObservable<FriendshipUpdatedEvent>()
@JvmField
val FULLNESS_UPDATED = EventObservable<FullnessUpdatedEvent>()
@JvmField
val POKEMON_FAINTED = EventObservable<PokemonFaintedEvent>()
@JvmField
val EVOLUTION_ACCEPTED = CancelableObservable<EvolutionAcceptedEvent>()
@JvmField
val EVOLUTION_DISPLAY = EventObservable<EvolutionDisplayEvent>()
@JvmField
val EVOLUTION_TESTED = EventObservable<EvolutionTestedEvent>()
@JvmField
val EVOLUTION_COMPLETE = EventObservable<EvolutionCompleteEvent>()
@JvmField
val POKEMON_NICKNAMED = CancelableObservable<PokemonNicknamedEvent>()
@JvmField
val THROWN_POKEBALL_HIT = CancelableObservable<ThrownPokeballHitEvent>()
@JvmField
val POKEMON_CATCH_RATE = EventObservable<PokemonCatchRateEvent>()
@JvmField
val POKE_BALL_CAPTURE_CALCULATED = EventObservable<PokeBallCaptureCalculatedEvent>()
@JvmField
val POKEMON_CAPTURED = EventObservable<PokemonCapturedEvent>()
@JvmField
val FOSSIL_REVIVED = EventObservable<FossilRevivedEvent>()
@JvmField
val BATTLE_STARTED_PRE = CancelableObservable<BattleStartedPreEvent>()
val BATTLE_STARTED_PRE = CancelableObservable<BattleStartedEvent.Pre>()
@JvmField
val BATTLE_STARTED_POST = EventObservable<BattleStartedPostEvent>()
val BATTLE_STARTED_POST = EventObservable<BattleStartedEvent.Post>()
@JvmField
val BATTLE_FLED = EventObservable<BattleFledEvent>()
@JvmField
val BATTLE_VICTORY = EventObservable<BattleVictoryEvent>()
@JvmField
val BATTLE_FAINTED = EventObservable<BattleFaintedEvent>()
// instructions
@JvmField
val MEGA_EVOLUTION = EventObservable<MegaEvolutionEvent>()
@JvmField
val TERASTALLIZATION = EventObservable<TerastallizationEvent>()
@JvmField
val ZPOWER_USED = EventObservable<ZMoveUsedEvent>()
@JvmField
val FORME_CHANGE = EventObservable<FormeChangeEvent>()
@JvmField
val POKEMON_SENT_PRE = CancelableObservable<PokemonSentPreEvent>()
@JvmField
val POKEMON_SENT_POST = EventObservable<PokemonSentPostEvent>()
@JvmField
val POKEMON_RECALLED = EventObservable<PokemonRecalledEvent>()
val POKEMON_SENT_PRE = CancelableObservable<PokemonSentEvent.Pre>()
@JvmField
val TRADE_COMPLETED = EventObservable<TradeCompletedEvent>()
val POKEMON_SENT_POST = EventObservable<PokemonSentEvent.Post>()
@JvmField
val POKEMON_RECALL_PRE = CancelableObservable<PokemonRecallEvent.Pre>()
@JvmField
val POKEMON_RECALL_POST = EventObservable<PokemonRecallEvent.Post>()
@JvmField
val TRADE_EVENT_PRE = CancelableObservable<TradeEvent.Pre>()
@JvmField
val TRADE_EVENT_POST = EventObservable<TradeEvent.Post>()
@JvmField
val RIDE_EVENT_PRE = CancelableObservable<RidePokemonEvent.Pre>()
@JvmField
val RIDE_EVENT_APPLY_STAMINA = EventObservable<RidePokemonEvent.ApplyStamina>()
@JvmField
val RIDE_EVENT_POST = EventObservable<RidePokemonEvent.Post>()
@JvmField
val LEVEL_UP_EVENT = EventObservable<LevelUpEvent>()
@@ -134,16 +165,21 @@ object CobblemonEvents {
val POKEMON_HEALED = CancelableObservable<PokemonHealedEvent>()
@JvmField
/** CLIENT ONLY! */
/** CLIENT ONLY! */
val POKEMON_INTERACTION_GUI_CREATION = EventObservable<PokemonInteractionGUICreationEvent>()
@JvmField
val POKEMON_ENTITY_SAVE = EventObservable<PokemonEntitySaveEvent>()
@JvmField
val POKEMON_ENTITY_LOAD = CancelableObservable<PokemonEntityLoadEvent>()
@JvmField
val POKEMON_ENTITY_SAVE_TO_WORLD = CancelableObservable<PokemonEntitySaveToWorldEvent>()
@JvmField
val ENTITY_SPAWN = CancelableObservable<SpawnEvent<*>>()
@JvmField
val SHINY_CHANCE_CALCULATION = EventObservable<ShinyChanceCalculationEvent>()
@@ -158,46 +194,56 @@ object CobblemonEvents {
)
@JvmField
val EXPERIENCE_GAINED_EVENT_PRE = CancelableObservable<ExperienceGainedPreEvent>()
val EXPERIENCE_GAINED_EVENT_PRE = CancelableObservable<ExperienceGainedEvent.Pre>()
@JvmField
val EXPERIENCE_GAINED_EVENT_POST = EventObservable<ExperienceGainedPostEvent>()
val EXPERIENCE_GAINED_EVENT_POST = EventObservable<ExperienceGainedEvent.Post>()
@JvmField
val EXPERIENCE_CANDY_USE_PRE = CancelableObservable<ExperienceCandyUseEvent.Pre>()
@JvmField
val EXPERIENCE_CANDY_USE_POST = EventObservable<ExperienceCandyUseEvent.Post>()
@JvmField
val EV_GAINED_EVENT_PRE = CancelableObservable<EvGainedEvent.Pre>()
@JvmField
val EV_GAINED_EVENT_POST = EventObservable<EvGainedEvent.Post>()
@JvmField
val HYPER_TRAINED_IV_PRE = CancelableObservable<HyperTrainedIvEvent.Pre>()
@JvmField
val HYPER_TRAINED_IV_POST = EventObservable<HyperTrainedIvEvent.Post>()
@JvmField
val POKEMON_RELEASED_EVENT_PRE = CancelableObservable<ReleasePokemonEvent.Pre>()
@JvmField
val POKEMON_RELEASED_EVENT_POST = EventObservable<ReleasePokemonEvent.Post>()
@JvmField
val RENAME_PC_BOX_EVENT_PRE = CancelableObservable<RenamePCBoxEvent.Pre>()
@JvmField
val RENAME_PC_BOX_EVENT_POST = EventObservable<RenamePCBoxEvent.Post>()
@JvmField
val WALLPAPER_COLLECTION_EVENT = EventObservable<WallpaperCollectionEvent>()
@JvmField
val WALLPAPER_UNLOCKED_EVENT = CancelableObservable<WallpaperUnlockedEvent>()
@JvmField
val CHANGE_PC_BOX_WALLPAPER_EVENT_PRE = CancelableObservable<ChangePCBoxWallpaperEvent.Pre>()
@JvmField
val CHANGE_PC_BOX_WALLPAPER_EVENT_POST = EventObservable<ChangePCBoxWallpaperEvent.Post>()
@JvmField
val LOOT_DROPPED = CancelableObservable<LootDroppedEvent>()
@JvmField
val STARTER_CHOSEN = CancelableObservable<StarterChosenEvent>()
@@ -206,58 +252,81 @@ object CobblemonEvents {
@JvmField
val APRICORN_HARVESTED = EventObservable<ApricornHarvestEvent>()
// Berries
@JvmField
val BERRY_HARVEST = EventObservable<BerryHarvestEvent>()
@JvmField
val BERRY_MUTATION_OFFER = EventObservable<BerryMutationOfferEvent>()
@JvmField
val BERRY_MUTATION_RESULT = EventObservable<BerryMutationResultEvent>()
@JvmField
val BERRY_YIELD = EventObservable<BerryYieldCalculationEvent>()
@JvmField
val LEFTOVERS_CREATED = CancelableObservable<LeftoversCreatedEvent>()
@JvmField
val BIG_ROOT_PROPAGATED = CancelableObservable<BigRootPropagatedEvent>()
@JvmField
val HELD_ITEM_PRE = CancelableObservable<HeldItemEvent.Pre>()
@JvmField
val HELD_ITEM_POST = EventObservable<HeldItemEvent.Post>()
@JvmField
val COSMETIC_ITEM_PRE = CancelableObservable<HeldItemEvent.Pre>()
@JvmField
val COSMETIC_ITEM_POST = EventObservable<HeldItemEvent.Post>()
@JvmField
val POKEMON_GAINED = EventObservable<PokemonGainedEvent>()
@JvmField
val POKEMON_SEEN = CancelableObservable<PokemonSeenEvent>()
@JvmField
val POKEMON_ASPECTS_CHANGED = EventObservable<PokemonAspectsChangedEvent>()
@JvmField
val POKEDEX_DATA_CHANGED_PRE = CancelableObservable<PokedexDataChangedEvent.Pre>()
@JvmField
val POKEDEX_DATA_CHANGED_POST = EventObservable<PokedexDataChangedEvent.Post>()
// Fishing
@JvmField
val BAIT_SET = CancelableObservable<BaitSetEvent>()
@JvmField
val BAIT_SET_PRE = CancelableObservable<BaitSetEvent>()
@JvmField
val BAIT_CONSUMED = CancelableObservable<BaitConsumedEvent>()
@JvmField
val POKEROD_CAST_PRE = CancelableObservable<PokerodCastEvent.Pre>()
@JvmField
val POKEROD_CAST_POST = EventObservable<PokerodCastEvent.Post>()
@JvmField
val POKEROD_REEL = CancelableObservable<PokerodReelEvent>()
@JvmField
val BOBBER_SPAWN_POKEMON_PRE = CancelableObservable<BobberSpawnPokemonEvent.Pre>()
@JvmField
val BOBBER_SPAWN_POKEMON_MODIFY = EventObservable<BobberSpawnPokemonEvent.Modify>()
@JvmField
val BOBBER_SPAWN_POKEMON_POST = EventObservable<BobberSpawnPokemonEvent.Post>()
@JvmField
val BAIT_EFFECT_REGISTRATION = EventObservable<BaitEffectFunctionRegistryEvent>()
@@ -271,8 +340,10 @@ object CobblemonEvents {
@JvmField
val COLLECT_EGG = CancelableObservable<CollectEggEvent>()
@JvmField
val HATCH_EGG_PRE = CancelableObservable<HatchEggEvent.Pre>()
@JvmField
val HATCH_EGG_POST = EventObservable<HatchEggEvent.Post>()
@@ -12,40 +12,75 @@ import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.battles.ActiveBattlePokemon
import com.cobblemon.mod.common.battles.BattleSide
import com.cobblemon.mod.common.util.asArrayValue
import net.minecraft.network.chat.MutableComponent
/**
* Event fired before a [PokemonBattle] is started. Canceling this event prevents the battle from being
* created and launched.
* Events regarding the starting of a [PokemonBattle].
*
* @property reason The text used to inform the participants why the battle was canceled. Keep null for a default error.
*
* @author Segfault Guy
* @author Segfault Guy/MeAlam
* @since March 26th 2023
*/
data class BattleStartedPreEvent (override val battle: PokemonBattle, var reason: MutableComponent? = null) : BattleEvent, Cancelable() {
val context = mutableMapOf(
"battle" to battle.struct,
"players" to battle.actors.filter { it.type == ActorType.PLAYER }.asArrayValue { it.struct },
"npcs" to battle.actors.filter { it.type == ActorType.NPC }.asArrayValue { it.struct },
"wild_pokemon" to battle.actors.filter { it.type == ActorType.WILD }.asArrayValue { it.struct }
)
interface BattleStartedEvent : BattleEvent {
/**
* The battle that is starting.
*/
override val battle: PokemonBattle
val functions = moLangFunctionMap(cancelFunc)
}
/**
* Event fired before a [PokemonBattle] is started. Canceling this event prevents the battle from being
* created and launched.
*
* @property reason The text used to inform the participants why the battle was canceled. Keep null for a default error.
*
* @author Segfault Guy/MeAlam
* @since March 26th 2023
*/
data class Pre(
override val battle: PokemonBattle,
var reason: MutableComponent? = null
) : BattleStartedEvent, Cancelable() {
val context = mutableMapOf(
"battle" to battle.struct,
"players" to battle.actors.filter { it.type == ActorType.PLAYER }.asArrayValue { it.struct },
"npcs" to battle.actors.filter { it.type == ActorType.NPC }.asArrayValue { it.struct },
"wild_pokemon" to battle.actors.filter { it.type == ActorType.WILD }.asArrayValue { it.struct }
)
val functions = moLangFunctionMap(cancelFunc)
fun getBattleSide(side: Int) : BattleSide? {
return when (side) {
1 -> battle.side1
2 -> battle.side2
else -> null
}
}
fun getPokemonOnSide(side: Int) : List<ActiveBattlePokemon>? {
val battleSide = getBattleSide(side)
val activePokemon = battleSide?.activePokemon
return activePokemon
}
}
/**
* Event fired after a [PokemonBattle] starts.
*
* @author Segfault Guy/MeAlam
* @since March 26th 2023
*/
data class Post(
override val battle: PokemonBattle
) : BattleStartedEvent {
val context = mutableMapOf(
"battle" to battle.struct,
"players" to battle.actors.filter { it.type == ActorType.PLAYER }.asArrayValue { it.struct },
"npcs" to battle.actors.filter { it.type == ActorType.NPC }.asArrayValue { it.struct },
"wild_pokemon" to battle.actors.filter { it.type == ActorType.WILD }.asArrayValue { it.struct }
)
}
/**
* Event fired after a [PokemonBattle] starts.
*
* @author Segfault Guy
* @since March 26th 2023
*/
data class BattleStartedPostEvent (override val battle: PokemonBattle) : BattleEvent {
val context = mutableMapOf(
"battle" to battle.struct,
"players" to battle.actors.filter { it.type == ActorType.PLAYER }.asArrayValue { it.struct },
"npcs" to battle.actors.filter { it.type == ActorType.NPC }.asArrayValue { it.struct },
"wild_pokemon" to battle.actors.filter { it.type == ActorType.WILD }.asArrayValue { it.struct }
)
}
@@ -8,11 +8,9 @@
package com.cobblemon.mod.common.api.events.fishing
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.api.reactive.CancelableObservable
import com.cobblemon.mod.common.util.server
import net.minecraft.world.item.ItemStack
@@ -22,10 +20,12 @@ import net.minecraft.world.item.ItemStack
* @param bait The ItemStack of the bait that is set on the rod.
*/
class BaitSetEvent(val rod: ItemStack, val bait: ItemStack) : Cancelable() {
val context = mutableMapOf(
"rod" to rod.asMoLangValue(server()!!.registryAccess()),
"bait" to bait.asMoLangValue(server()!!.registryAccess())
)
val context = server()?.let { server ->
mutableMapOf(
"rod" to rod.asMoLangValue(server.registryAccess()),
"bait" to bait.asMoLangValue(server.registryAccess())
)
} ?: emptyMap()
val functions = moLangFunctionMap(
cancelFunc
)
@@ -17,67 +17,88 @@ import com.cobblemon.mod.common.api.pokemon.experience.*
import com.cobblemon.mod.common.pokemon.Pokemon
/**
* Event fired when experience is about to be gained. Cancelling this event prevents
* any experience being added, and the amount of experience can be changed from [experience].
* Event fired when experience will be gained.
*
* @author Hiroku
* @since August 5th, 2022
*/
class ExperienceGainedPreEvent(
val pokemon: Pokemon,
val source: ExperienceSource,
interface ExperienceGainedEvent {
/**
* The [Pokemon] gaining experience.
*/
val pokemon: Pokemon
/**
* The source of the experience being gained.
*/
val source: ExperienceSource
/**
* The amount of experience being gained.
*/
var experience: Int
) : Cancelable() {
val context = mutableMapOf(
"pokemon" to pokemon.struct,
"source" to StringValue(
when (source) {
is BattleExperienceSource -> "battle"
is CandyExperienceSource -> "interaction"
is CommandExperienceSource -> "command"
is SidemodExperienceSource -> source.sidemodId
else -> "unknown"
}
),
"experience" to DoubleValue(experience.toDouble())
)
val functions = moLangFunctionMap(
cancelFunc,
"set_amount" to {
experience = it.getInt(0)
DoubleValue.ONE
}
)
}
/**
* Event fired when experience has been gained. Information about whether it leveled up or not is
* available.
*
* @author Hiroku
* @since August 5th, 2022
*/
class ExperienceGainedPostEvent(
val pokemon: Pokemon,
val source: ExperienceSource,
val experience: Int,
val previousLevel: Int,
val currentLevel: Int,
val learnedMoves: MutableSet<MoveTemplate>
) {
val context = mutableMapOf(
"pokemon" to pokemon.struct,
"source" to StringValue(
when (source) {
is BattleExperienceSource -> "battle"
is CandyExperienceSource -> "interaction"
is CommandExperienceSource -> "command"
is SidemodExperienceSource -> source.sidemodId
else -> "unknown"
/**
* Event fired when experience is about to be gained. Cancelling this event prevents
* any experience being added, and the amount of experience can be changed from [experience].
*
* @author Hiroku/MeAlam
* @since August 5th, 2022
*/
data class Pre(
override val pokemon: Pokemon,
override val source: ExperienceSource,
override var experience: Int
) : ExperienceGainedEvent, Cancelable() {
val context = mutableMapOf(
"pokemon" to pokemon.struct,
"source" to StringValue(
when (source) {
is BattleExperienceSource -> "battle"
is CandyExperienceSource -> "interaction"
is CommandExperienceSource -> "command"
is SidemodExperienceSource -> source.sidemodId
else -> "unknown"
}
),
"experience" to DoubleValue(experience.toDouble())
)
val functions = moLangFunctionMap(
cancelFunc,
"set_amount" to {
experience = it.getInt(0)
DoubleValue.ONE
}
),
"experience" to DoubleValue(experience.toDouble()),
"previous_level" to DoubleValue(previousLevel.toDouble()),
"current_level" to DoubleValue(currentLevel.toDouble())
)
)
}
/**
* Event fired when experience has been gained. Information about whether it leveled up or not is
* available.
*
* @author Hiroku/MeAlam
* @since August 5th, 2022
*/
data class Post(
override val pokemon: Pokemon,
override val source: ExperienceSource,
override var experience: Int,
val previousLevel: Int,
val currentLevel: Int,
val learnedMoves: MutableSet<MoveTemplate>
) : ExperienceGainedEvent {
val context = mutableMapOf(
"pokemon" to pokemon.struct,
"source" to StringValue(
when (source) {
is BattleExperienceSource -> "battle"
is CandyExperienceSource -> "interaction"
is CommandExperienceSource -> "command"
is SidemodExperienceSource -> source.sidemodId
else -> "unknown"
}
),
"experience" to DoubleValue(experience.toDouble()),
"previous_level" to DoubleValue(previousLevel.toDouble()),
"current_level" to DoubleValue(currentLevel.toDouble())
)
}
}
@@ -13,14 +13,14 @@ import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.getPlayer
import net.minecraft.server.level.ServerPlayer
interface HatchEggEvent {
val egg: PokemonProperties
val player: ServerPlayer
data class Pre(override var egg : PokemonProperties, override var player: ServerPlayer) : HatchEggEvent, Cancelable() {
data class Pre(var egg : PokemonProperties, override val player: ServerPlayer) : HatchEggEvent, Cancelable() {
val context = mutableMapOf(
"player" to (player.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO)
)
@@ -29,7 +29,7 @@ interface HatchEggEvent {
)
}
data class Post(override var egg : PokemonProperties, override var player: ServerPlayer) : HatchEggEvent {
data class Post(override var player: ServerPlayer, val pokemon : Pokemon) : HatchEggEvent {
val context = mutableMapOf(
"player" to (player.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO)
)
@@ -0,0 +1,55 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.pokemon
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.Pokemon
/**
* Event fired when a [PokemonEntity] is recalled.
*
* @author Segfault Guy
* @since March 25th, 2023
*/
interface PokemonRecallEvent {
val pokemon: Pokemon
val oldEntity: PokemonEntity?
/**
* Event fired before a [PokemonEntity] is recalled.
*/
data class Pre(
override val pokemon: Pokemon,
override val oldEntity: PokemonEntity?
) : PokemonRecallEvent, Cancelable() {
val context = mutableMapOf(
"pokemon" to pokemon.struct,
"old_entity" to (oldEntity?.struct ?: StringValue("null"))
)
val functions = moLangFunctionMap(
cancelFunc
)
}
data class Post(
override val pokemon: Pokemon,
override val oldEntity: PokemonEntity?
) : PokemonRecallEvent {
val context = mutableMapOf(
"pokemon" to pokemon.struct,
"old_entity" to (oldEntity?.struct ?: StringValue("null"))
)
}
}
@@ -1,23 +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.api.events.pokemon
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.Pokemon
/**
* Event fired when a [PokemonEntity] is recalled.
*
* @author Segfault Guy
* @since March 25th, 2023
*/
data class PokemonRecalledEvent (
val pokemon: Pokemon,
val oldEntity: PokemonEntity?
)
@@ -17,39 +17,64 @@ import net.minecraft.server.level.ServerLevel
import net.minecraft.world.phys.Vec3
/**
* Event fired when a party [Pokemon] is sent out. Cancelling this event prevents a corresponding
* [PokemonEntity] from being instantiated and spawned into the world.
* Event fired when a party [Pokemon] is sent out.
*
* @author Segfault Guy
* @since March 25th, 2023
*/
data class PokemonSentPreEvent(
val pokemon: Pokemon,
val level: ServerLevel,
val position: Vec3
) : Cancelable() {
val context = mutableMapOf(
"pokemon" to pokemon.struct,
"position" to StringValue(position.toString())
)
val functions = moLangFunctionMap(
cancelFunc
)
}
interface PokemonSentEvent {
/**
* The [Pokemon] being sent out.
*/
val pokemon: Pokemon
/**
* Event fired after a [PokemonEntity] is spawned from a player's party and after its animations are finished.
* Only fired for party [Pokemon] sent out with animations.
*
* @author Segfault Guy
* @since March 25th, 2023
*/
data class PokemonSentPostEvent(
val pokemon: Pokemon,
val pokemonEntity: PokemonEntity
) {
val context = mutableMapOf(
"pokemon" to pokemon.struct,
"pokemon_entity" to pokemonEntity.struct
)
/**
* The [ServerLevel] the [Pokemon] is being sent out into.
*/
val level: ServerLevel
/**
* The [Vec3] position the [Pokemon] is being sent out at.
*/
val position: Vec3
/**
* Event fired when a party [Pokemon] is sent out. Cancelling this event prevents a corresponding
* [PokemonEntity] from being instantiated and spawned into the world.
*
* @author Segfault Guy/MeAlam
* @since March 25th, 2023
*/
data class Pre(
override val pokemon: Pokemon,
override val level: ServerLevel,
override val position: Vec3
) : PokemonSentEvent, Cancelable() {
val context = mutableMapOf(
"pokemon" to pokemon.struct,
"position" to StringValue(position.toString())
)
val functions = moLangFunctionMap(
cancelFunc
)
}
/**
* Event fired after a [PokemonEntity] is spawned from a player's party and after its animations are finished.
* Only fired for party [Pokemon] sent out with animations.
*
* @author Segfault Guy/MeAlam
* @since March 25th, 2023
*/
data class Post(
override val pokemon: Pokemon,
override val level: ServerLevel,
override val position: Vec3,
val pokemonEntity: PokemonEntity
) : PokemonSentEvent {
val context = mutableMapOf(
"pokemon" to pokemon.struct,
"pokemon_entity" to pokemonEntity.struct
)
}
}
@@ -0,0 +1,82 @@
/*
* 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.events.pokemon
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.server.level.ServerPlayer
/**
* The base for all events related to riding a Pokemon.
*
* @see [Pre]
* @see [Post]
*/
interface RidePokemonEvent {
val player: ServerPlayer
val pokemon: PokemonEntity
class Pre(
override val player: ServerPlayer,
override val pokemon: PokemonEntity
) : RidePokemonEvent, Cancelable() {
val context = mutableMapOf(
"player" to player.asMoLangValue(),
"pokemon" to pokemon.struct
)
val functions = moLangFunctionMap(
cancelFunc
)
}
class Post(
override val player: ServerPlayer,
override val pokemon: PokemonEntity
) : RidePokemonEvent {
val context = mutableMapOf(
"player" to player.asMoLangValue(),
"pokemon" to pokemon.struct
)
}
/**
* Event published when stamina is being applied. At this point you can modify the stamina that will get used on the
* mount or you can make the stamina infinite. Handy hey.
*/
class ApplyStamina(
override val player: ServerPlayer,
override val pokemon: PokemonEntity,
var rideStamina: Float
) : RidePokemonEvent {
val context = mutableMapOf(
"player" to player.asMoLangValue(),
"pokemon" to pokemon.struct,
"ride_stamina" to DoubleValue(rideStamina)
)
val functions = moLangFunctionMap(
"set_ride_stamina" to { params ->
rideStamina = params.getDouble(0).toFloat().coerceIn(0F, 1F)
DoubleValue.ONE
},
"set_infinite_stamina" to { _ ->
rideStamina = -1F
DoubleValue.ONE
},
)
fun setInfiniteStamina() {
rideStamina = -1F
}
fun isInfiniteStamina() = rideStamina == -1F
}
}
@@ -1,24 +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.api.events.pokemon
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.trade.TradeParticipant
import com.cobblemon.mod.common.util.getPlayer
class TradeCompletedEvent(val tradeParticipant1 : TradeParticipant, val tradeParticipant1Pokemon : Pokemon, val tradeParticipant2 : TradeParticipant, val tradeParticipant2Pokemon : Pokemon) {
val context = mutableMapOf(
"trade_participant_1" to (tradeParticipant1.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
"trade_participant_2" to (tradeParticipant2.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
"trade_participant_1_pokemon" to tradeParticipant1Pokemon.struct,
"trade_participant_2_pokemon" to tradeParticipant2Pokemon.struct
)
}
@@ -0,0 +1,91 @@
/*
* 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.events.pokemon
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.trade.TradeParticipant
import com.cobblemon.mod.common.util.getPlayer
/**
* Event fired when a trade is initiated or completed.
*
* @author MeAlam
* @since August 23th, 2025
*/
interface TradeEvent {
/**
* The first [TradeParticipant].
*/
val tradeParticipant1: TradeParticipant
/**
* The [Pokemon] being traded by the first participant.
*/
val tradeParticipant1Pokemon: Pokemon
/**
* The second [TradeParticipant].
*/
val tradeParticipant2: TradeParticipant
/**
* The [Pokemon] being traded by the second participant.
*/
val tradeParticipant2Pokemon: Pokemon
/**
* Event fired when a trade is about to happen. Cancelling this event prevents the trade from occurring.
*
* @author MeAlam
* @since August 23th, 2025
*/
data class Pre(
override val tradeParticipant1: TradeParticipant,
override val tradeParticipant1Pokemon: Pokemon,
override val tradeParticipant2: TradeParticipant,
override val tradeParticipant2Pokemon: Pokemon
) : TradeEvent, Cancelable() {
val context = mutableMapOf(
"trade_participant_1" to (tradeParticipant1.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
"trade_participant_2" to (tradeParticipant2.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
"trade_participant_1_pokemon" to tradeParticipant1Pokemon.struct,
"trade_participant_2_pokemon" to tradeParticipant2Pokemon.struct
)
val functions = moLangFunctionMap(
cancelFunc
)
}
/**
* Event fired after a trade has occurred.
*
* @author MeAlam
* @since August 23th, 2025
*/
data class Post(
override val tradeParticipant1: TradeParticipant,
override val tradeParticipant1Pokemon: Pokemon,
override val tradeParticipant2: TradeParticipant,
override val tradeParticipant2Pokemon: Pokemon
) : TradeEvent {
val context = mutableMapOf(
"trade_participant_1" to (tradeParticipant1.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
"trade_participant_2" to (tradeParticipant2.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
"trade_participant_1_pokemon" to tradeParticipant1Pokemon.struct,
"trade_participant_2_pokemon" to tradeParticipant2Pokemon.struct
)
}
}
@@ -23,11 +23,22 @@ import com.cobblemon.mod.common.pokemon.Pokemon
* @since October 2nd, 2022
*/
data class EvolutionCompleteEvent(
/**
* The [Pokemon] resulting from the evolution.
*/
override val pokemon: Pokemon,
/**
* The [Pokemon] that was the source of the evolution.
*/
val sourcePokemon: Pokemon,
/**
* The [Evolution] that was used.
*/
override val evolution: Evolution
) : EvolutionEvent {
val context = mutableMapOf<String, MoValue>(
"pokemon" to pokemon.struct,
"source_pokemon" to sourcePokemon.struct,
"evolution" to evolution.asMoLangValue()
)
}
@@ -37,19 +37,28 @@ interface ChangePCBoxWallpaperEvent {
/**
* The location of the wallpaper that is being changed to. Can be modified in the [Pre] event.
* NOTE: Changing this to a wallpaper that does not exist on the client, will result in a purple/black fallback texture being displayed.
* NOTE: Changing this to a wallpaper that does not exist on the client, will result in a default fallback texture being displayed.
*/
val wallpaper: ResourceLocation
/**
* The location of the alternative wallpaper that is being changed to if available. Can be modified in the [Pre] event.
* NOTE: Changing this to a wallpaper that does not exist on the client, will result in a default fallback texture being displayed.
*/
val altWallpaper: ResourceLocation?
class Pre(
override val player: ServerPlayer,
override val box: PCBox,
override var wallpaper: ResourceLocation
override var wallpaper: ResourceLocation,
override var altWallpaper: ResourceLocation?
) : ChangePCBoxWallpaperEvent, Cancelable() {
val context = mutableMapOf(
"player" to player.asMoLangValue(),
"box" to StringValue(box.toString()),
"wallpaper" to StringValue(wallpaper.toString())
"wallpaper" to StringValue(wallpaper.toString()),
"altWallpaper" to StringValue(altWallpaper?.toString())
)
val functions = mapOf(
cancelFunc
@@ -59,12 +68,14 @@ interface ChangePCBoxWallpaperEvent {
class Post(
override val player: ServerPlayer,
override val box: PCBox,
override val wallpaper: ResourceLocation
override val wallpaper: ResourceLocation,
override val altWallpaper: ResourceLocation?
) : ChangePCBoxWallpaperEvent {
val context = mutableMapOf(
"player" to player.asMoLangValue(),
"box" to StringValue(box.toString()),
"wallpaper" to StringValue(wallpaper.toString())
"wallpaper" to StringValue(wallpaper.toString()),
"altWallpaper" to StringValue(altWallpaper?.toString())
)
}
}
@@ -227,7 +227,7 @@ fun drawPosablePortrait(
matrixStack.scale(scale, scale, -scale)
matrixStack.translate(0.0, -PORTRAIT_DIAMETER / 18.0, 0.0)
val sprite = VaryingModelRepository.getSprite(identifier, state, SpriteType.PORTRAIT);
val sprite = VaryingModelRepository.getSprite(identifier, state, SpriteType.PORTRAIT)
if (sprite == null) {
val model = VaryingModelRepository.getPoser(identifier, state)
@@ -0,0 +1,57 @@
/*
* 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.item
import com.cobblemon.mod.common.api.data.DataRegistry
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.battles.runner.ShowdownService
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.server.packs.resources.ResourceManager
import java.io.File
object HeldItems : DataRegistry {
override val id = cobblemonResource("held_items")
override val type = PackType.SERVER_DATA
override val observable = SimpleObservable<HeldItems>()
override fun sync(player: ServerPlayer) {}
// val heldItems = PrioritizedList<BagItemLike>()
internal val heldItemsScripts = mutableMapOf<String, String>() // showdown item ID to JavaScript
val showdownItems = hashSetOf<String>()
override fun reload(manager: ResourceManager) {
heldItemsScripts.clear()
ShowdownService.service.resetRegistryData("heldItem")
manager.listResources("held_items") { it.path.endsWith(".js") }.forEach { (identifier, resource) ->
resource.open().use { stream ->
stream.bufferedReader().use { reader ->
val resolvedIdentifier = ResourceLocation.fromNamespaceAndPath(identifier.namespace, File(identifier.path).nameWithoutExtension)
val js = reader.readText()
heldItemsScripts[resolvedIdentifier.path] = js
}
}
}
ShowdownService.service.sendRegistryData(heldItemsScripts, "heldItem")
val itemsJson = ShowdownService.service.getRegistryData("heldItem")
for (i in 0 until itemsJson.size()) {
val jsItem = itemsJson[i].asJsonObject
showdownItems += jsItem.get("id").asString
}
CobblemonHeldItemManager.load()
this.observable.emit(this)
}
}
@@ -40,6 +40,7 @@ import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
import com.cobblemon.mod.common.api.moves.animations.NPCProvider
import com.cobblemon.mod.common.api.moves.animations.TargetsProvider
import com.cobblemon.mod.common.api.npc.NPCClass
import com.cobblemon.mod.common.api.npc.NPCClasses
import com.cobblemon.mod.common.api.npc.configuration.interaction.DialogueNPCInteractionConfiguration
import com.cobblemon.mod.common.api.npc.configuration.interaction.ScriptNPCInteractionConfiguration
@@ -56,6 +57,8 @@ import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.api.pokemon.experience.SidemodExperienceSource
import com.cobblemon.mod.common.api.pokemon.moves.LearnsetQuery
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.riding.Rideable
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker
import com.cobblemon.mod.common.api.scheduling.Schedulable
import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
@@ -84,6 +87,7 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonBehaviourFlag
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.PokemonMoveControl
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
import com.cobblemon.mod.common.net.messages.client.battle.BattleMusicPacket
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
@@ -125,21 +129,28 @@ import net.minecraft.sounds.SoundSource
import net.minecraft.tags.TagKey
import net.minecraft.world.damagesource.DamageSource
import net.minecraft.world.damagesource.DamageTypes
import net.minecraft.world.effect.MobEffectInstance
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.EntityDimensions
import net.minecraft.world.entity.EntityType
import net.minecraft.world.entity.LightningBolt
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.Mob
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.PlayerRideable
import net.minecraft.world.entity.TamableAnimal
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.memory.MemoryStatus
import net.minecraft.world.entity.ai.memory.WalkTarget
import net.minecraft.world.entity.animal.Animal
import net.minecraft.world.entity.item.ItemEntity
import net.minecraft.world.entity.monster.Enemy
import net.minecraft.world.entity.monster.Monster
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.ClipContext
import net.minecraft.world.level.GameType
import net.minecraft.world.level.Level
import net.minecraft.world.level.Level.ExplosionInteraction
import net.minecraft.world.level.biome.Biome
@@ -149,6 +160,9 @@ import net.minecraft.world.level.levelgen.Heightmap
import net.minecraft.world.level.pathfinder.PathType
import net.minecraft.world.phys.AABB
import net.minecraft.world.phys.Vec3
import kotlin.text.get
import kotlin.text.toInt
import kotlin.toString
/**
* Holds a bunch of useful MoLang trickery that can be used or extended in API
@@ -199,6 +213,14 @@ object MoLangFunctions {
"string_length" to java.util.function.Function { params ->
return@Function DoubleValue(params.getString(0).length)
},
"split_string" to java.util.function.Function { 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) }
return@Function struct
},
"is_blank" to java.util.function.Function { params ->
val arg = params.get<MoValue>(0)
return@Function DoubleValue((arg is StringValue && (arg.value.isBlank() || arg.value.toDoubleOrNull() == 0.0)) || (arg is DoubleValue && arg.value == 0.0))
@@ -466,6 +488,22 @@ object MoLangFunctions {
return@put DoubleValue.ZERO
}
}
map.put("spawn_npc") { 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 level = params.getInt(4)
if(npcClass == null) return@put 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
}
map.put("play_sound_on_server") { params ->
val sound = params.getString(0).asResource()
val soundSource = params.getString(1).uppercase()
@@ -550,11 +588,62 @@ object MoLangFunctions {
environment
}
map.put("is_player") { DoubleValue.ONE }
map.put("riding_pokemon") {
val vehicle = player.vehicle
if (vehicle is PokemonEntity) {
return@put vehicle.struct
} else {
return@put DoubleValue.ZERO
}
}
if (player is ServerPlayer) {
map.put("seen_credits") { _ ->
DoubleValue( DoubleValue(player.seenCredits) )
}
map.put("is_in_dialogue") { _ ->
DoubleValue( DoubleValue(player.isInDialogue) )
}
map.put("active_dialogue") { _ ->
if (player.isInDialogue) {
player.activeDialogue?.dialogueId.toString()
return@put DoubleValue.ONE
} else {
DoubleValue.ZERO
}
}
map.put("is_spectator") { DoubleValue(player.isSpectator) }
map.put("is_creative") { DoubleValue(player.isCreative) }
map.put("is_survival") { DoubleValue(player.gameMode.isSurvival) }
map.put("is_adventure") { DoubleValue(player.gameMode.getGameModeForPlayer() == GameType.ADVENTURE) }
map.put("run_command") { params ->
val command = params.getString(0)
player.server.commands.performPrefixedCommand(player.createCommandSourceStack(), command)
}
map.put("set_battle_theme") { params ->
val soundId = params.getString(0).asResource()
Cobblemon.playerDataManager.getGenericData(player).battleTheme = soundId
return@put DoubleValue.ONE
}
map.put("battle_music") { 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
val music = soundId
if (music != null) {
val packet = BattleMusicPacket(music, volume, pitch, restart)
packet.sendToPlayer(player)
return@put DoubleValue.ONE
} else {
return@put DoubleValue.ZERO
}
}
map.put("stop_battle_music") { _ ->
val packet = BattleMusicPacket(null)
packet.sendToPlayer(player)
return@put DoubleValue.ONE
}
map.put("play_sound_on_server") { params ->
val sound = params.getString(0).asResource()
val soundSource = SoundSource.valueOf(params.getString(1).uppercase())
@@ -587,13 +676,13 @@ object MoLangFunctions {
map.put("party") { player.party().struct }
map.put("pc") { player.pc().struct }
map.put("has_permission") { params -> DoubleValue(Cobblemon.permissionValidator.hasPermission(player, params.getString(0), params.getIntOrNull(1) ?: 4)) }
map.put("data") { _ -> Cobblemon.molangData.load(player.uuid) }
map.put("save_data") { _ -> Cobblemon.molangData.save(player.uuid) }
map.put("data") { params -> Cobblemon.molangData.load(player.uuid, params.getStringOrNull(0)) }
map.put("save_data") { params -> Cobblemon.molangData.save(player.uuid, params.getStringOrNull(0)) }
map.put("in_battle") { DoubleValue(player.isInBattle()) }
map.put("battle") { player.getBattleState()?.first?.struct ?: DoubleValue.ZERO }
map.put("get_npc_data") { params ->
val npcId = (params.get<MoValue>(0) as? ObjectValue<NPCEntity>)?.obj?.stringUUID ?: params.getString(0)
val data = Cobblemon.molangData.load(player.uuid)
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 {
@@ -603,9 +692,9 @@ object MoLangFunctions {
}
}
map.put("get_npc_variable") { params ->
val npcId = (params.get<MoValue>(0) as? ObjectValue<NPCEntity>)?.obj?.stringUUID ?: params.getString(0)
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)
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 {
@@ -613,15 +702,16 @@ object MoLangFunctions {
}
}
map.put("set_npc_variable") { params ->
val npcId = (params.get<MoValue>(0) as? ObjectValue<NPCEntity>)?.obj?.stringUUID ?: params.getString(0)
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 data = Cobblemon.molangData.load(player.uuid)
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)
Cobblemon.molangData.save(player.uuid, path)
}
return@put DoubleValue.ONE
}
@@ -734,7 +824,26 @@ object MoLangFunctions {
map.put("delta_movement") {
return@put listOf(entity.deltaMovement.x, entity.deltaMovement.y, entity.deltaMovement.z).asArrayValue(::DoubleValue)
}
map.put("tags") {
val tags = entity.tags
val array = ArrayStruct(hashMapOf())
tags.forEachIndexed { index, tag -> array.setDirectly("$index", StringValue(tag)) }
return@put array
}
map.put("add_tag") { params ->
val tag = params.getString(0)
entity.addTag(tag)
return@put DoubleValue.ONE
}
map.put("remove_tag") { params ->
val tag = params.getString(0)
entity.removeTag(tag)
return@put DoubleValue.ONE
}
map.put("has_tag") { params ->
val tag = params.getString(0)
return@put DoubleValue(entity.tags.contains(tag))
}
if (entity is MoLangScriptingEntity) {
map.put("add_callback") { params ->
val type = params.getString(0).asIdentifierDefaultingNamespace()
@@ -885,6 +994,9 @@ object MoLangFunctions {
return@put if (entity.isStandingOn(blockStrings, depth)) DoubleValue.ONE else DoubleValue.ZERO
}
map.put("discard") {
entity.discard()
}
if (entity is PosableEntity) {
map.put("play_animation") { params ->
val animation = params.getString(0)
@@ -942,6 +1054,11 @@ object MoLangFunctions {
packet.sendToPlayer(player)
}
}
map.put("make_intangible") { params ->
val intangible = params.getBooleanOrNull(0) ?: true
entity.noPhysics = intangible
return@put DoubleValue.ONE
}
return@mutableListOf map
}
)
@@ -949,10 +1066,57 @@ object MoLangFunctions {
val livingEntityFunctions: MutableList<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
{ entity ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("is_player") { _ -> DoubleValue(entity is Player) }
map.put("is_npc") { _ -> DoubleValue(entity is NPCEntity) }
map.put("is_mob") { _ -> DoubleValue(entity is Mob) }
map.put("is_pokemon") { _ -> DoubleValue(entity is PokemonEntity) }
map.put("is_animal") { _ -> DoubleValue(entity is Animal) }
map.put("is_tamable") { _ -> DoubleValue(entity is TamableAnimal) }
map.put("is_tamed") { _ -> DoubleValue(entity is TamableAnimal && entity.isTame) }
map.put("is_hostile") { _ -> DoubleValue(entity is Monster) }
map.put("is_baby") { _ -> DoubleValue(entity.isBaby) }
map.put("is_adult") { _ -> DoubleValue(!entity.isBaby) }
map.put("remove_effect") { 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
}
}
map.put("add_effect") { params ->
val effectId = params.getString(0).asIdentifierDefaultingNamespace()
val duration = params.getInt(1)
val amplifier = params.getIntOrNull(2) ?: 0
val ambient = params.getBooleanOrNull(3) ?: false
val visible = params.getBooleanOrNull(4) ?: true
val effectHolder = BuiltInRegistries.MOB_EFFECT.getHolder(effectId).orElse(null)
if (effectHolder != null) {
entity.addEffect(MobEffectInstance(effectHolder, duration, amplifier, ambient, visible))
return@put DoubleValue.ONE
}
}
map.put("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)
}
map.put("heal") { params ->
val amount = params.getDouble(0)
entity.heal(amount.toFloat())
}
map.put("is_looking_at") { 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
}
return@put DoubleValue(entity.isLookingAt(entity, maxDistance))
}
map.put("is_living_entity") { DoubleValue.ONE }
map.put("is_flying") { _ -> DoubleValue(entity.isFallFlying) }
map.put("is_sleeping") { _ -> DoubleValue(entity.isSleeping) }
@@ -1072,10 +1236,9 @@ object MoLangFunctions {
}
}
map.put("has_walk_target") { _ ->
DoubleValue(entity.brain.getMemory(MemoryModuleType.WALK_TARGET).isPresent || entity.isPathFinding)
DoubleValue(entity.brain.getMemorySafely(MemoryModuleType.WALK_TARGET).isPresent || entity.isPathFinding)
}
}
map
}
)
@@ -1296,6 +1459,11 @@ object MoLangFunctions {
map.put("has_party") { DoubleValue(npc.party != null) }
map.put("is_npc") { DoubleValue.ONE }
map.put("can_battle") { DoubleValue(npc.party?.any { it.currentHealth > 0 } == true || npc.npc.party?.isStatic == false) }
map.put("set_battle_theme") { params ->
val soundId = params.getString(0).asResource()
npc.npc.battleTheme = soundId
return@put DoubleValue.ONE
}
map
}
)
@@ -1415,11 +1583,18 @@ object MoLangFunctions {
}
struct
}
map.put("ride_boosts") {
val struct = QueryStruct(hashMapOf())
for (stat in RidingStat.entries) {
struct.addFunction(stat.name.lowercase()) { DoubleValue(pokemon.getRideBoost(stat) ?: 0.0) }
}
struct
}
map.put("natdex_number") {
DoubleValue(pokemon.species.nationalPokedexNumber.toDouble())
}
map.put("types") {
pokemon.form.types.map { it.toString() }.asArrayValue(::StringValue)
pokemon.form.types.map { it.toString() }.asArrayValue(::StringValue)
}
map.put("gender_ratio") {
DoubleValue(pokemon.form.maleRatio.toDouble())
@@ -1620,6 +1795,21 @@ object MoLangFunctions {
return@put DoubleValue.ZERO
}
}
map.put("set_ride_boost") { params ->
val statName = params.getString(0).uppercase()
val stat = RidingStat.entries.find { it.name.equals(statName, ignoreCase = true) } ?: return@put DoubleValue.ZERO
val value = params.getDoubleOrNull(1)?.toFloat() ?: return@put DoubleValue.ZERO
pokemon.setRideBoost(stat, value)
return@put DoubleValue.ONE
}
map.put("add_ride_boost") { params ->
val statName = params.getString(0).uppercase()
val stat = RidingStat.entries.find { it.name.equals(statName, ignoreCase = true) } ?: return@put DoubleValue.ZERO
val value = params.getDoubleOrNull(1)?.toFloat() ?: return@put DoubleValue.ZERO
return@put DoubleValue(if (pokemon.addRideBoost(stat, value)) 1.0 else 0.0)
}
map.put("initialize_moveset") { params ->
val preferLatest = params.getBooleanOrNull(0) ?: true
pokemon.initializeMoveset(preferLatest)
@@ -1664,7 +1854,11 @@ object MoLangFunctions {
val moveTemplate = Moves.getByName(moveName) ?: return@put DoubleValue.ZERO
val includeLegacy = params.getBooleanOrNull(1) ?: true
val canLearn = LearnsetQuery.ANY.canLearn(moveTemplate, pokemon.form.moves)
val canLearn = if (includeLegacy) {
LearnsetQuery.ANY.canLearn(moveTemplate, pokemon.form.moves)
} else {
LearnsetQuery.LEGAL.canLearn(moveTemplate, pokemon.form.moves)
}
return@put DoubleValue(if (canLearn) 1.0 else 0.0)
}
map.put("unlearn_move") { params ->
@@ -1677,6 +1871,15 @@ object MoLangFunctions {
return@put DoubleValue.ZERO
}
}
map.put("can_evolve") { _ ->
DoubleValue(pokemon.evolutions.any())
}
map.put("force_evolve") { params ->
val idx = params.getInt(0)
val evolution = if (idx >= 0) pokemon.evolutions.elementAtOrNull(idx) else return@put DoubleValue.ZERO
evolution?.forceEvolve(pokemon)
StringValue(evolution.toString())
}
map
}
)
@@ -1684,10 +1887,10 @@ object MoLangFunctions {
val pokemonEntityFunctions = mutableListOf<(PokemonEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
{ pokemonEntity ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("is_busy") { DoubleValue(pokemonEntity.isBusy) }
map.put("in_battle") { DoubleValue(pokemonEntity.isBattling) }
map.put("is_moving") { DoubleValue((pokemonEntity.moveControl as? PokemonMoveControl)?.hasWanted() == true) }
map.put("is_flying") { DoubleValue(pokemonEntity.getBehaviourFlag(PokemonBehaviourFlag.FLYING)) }
map.put("is_flying") { DoubleValue(pokemonEntity.getBehaviourFlag(PokemonBehaviourFlag.FLYING)) }
map.put("get_alt_pose") { StringValue(pokemonEntity.getAltPose()) }
map.put("is_gliding") { DoubleValue(pokemonEntity.isUsingAltPose(cobblemonResource("gliding"))) }
map.put("is_sprinting") { DoubleValue(pokemonEntity.isUsingAltPose(cobblemonResource("sprinting"))) }
@@ -1941,12 +2144,17 @@ object MoLangFunctions {
}
map.put("data") { params ->
val data = Cobblemon.molangData.load(UUID(0L, 0L))
return@put data
Cobblemon.molangData.load(
UUID(0L, 0L),
params.getStringOrNull(0)
)
}
map.put("save_data") {
Cobblemon.molangData.save(UUID(0L, 0L))
map.put("save_data") { params ->
Cobblemon.molangData.save(
UUID(0L, 0L),
params.getStringOrNull(index = 0)
)
return@put DoubleValue.ONE
}
@@ -2450,3 +2658,4 @@ object MoLangFunctions {
}
fun Either<ResourceLocation, ExpressionLike>.runScript(runtime: MoLangRuntime) = map({ CobblemonScripts.run(it, runtime) }, { it.resolve(runtime) })
@@ -117,7 +117,7 @@ class BenchedMoves : Iterable<BenchedMove> {
.xmap(
{ moveList ->
val benchedMoves = BenchedMoves()
benchedMoves.addAll(moveList)
benchedMoves.addAll(moveList.filter { it.moveTemplate !is MoveTemplate.Dummy })
return@xmap benchedMoves
},
BenchedMoves::toList
@@ -213,7 +213,10 @@ class MoveSet : Iterable<Move> {
.xmap(
{ moveList ->
val moveSet = MoveSet()
moveList.forEach(moveSet::add)
moveList.filter { it.template !is MoveTemplate.Dummy }.forEach(moveSet::add)
if (moveSet.moves.all { it == null }) {
moveSet.add(Moves.getExceptional().create())
}
return@xmap moveSet
},
{ moveSet -> moveSet.toList() }
@@ -96,7 +96,7 @@ open class MoveTemplate(
@JvmStatic
val BY_STRING_CODEC: Codec<MoveTemplate> = CodecUtils.createByStringCodec(
Moves::getByName,
Moves::getByNameOrDummy,
MoveTemplate::name
) { id -> "No MoveTemplate for ID $id" }
}
@@ -15,7 +15,9 @@ import net.minecraft.world.entity.LivingEntity
import org.joml.Matrix3f
import org.joml.Quaternionf
import org.joml.Vector3f
import kotlin.math.abs
import kotlin.math.asin
import kotlin.math.sign
open class OrientationController(val entity: LivingEntity) {
@@ -112,8 +114,11 @@ open class OrientationController(val entity: LivingEntity) {
fun applyGlobalPitch(deltaPitchDegrees: Float) = updateOrientation { original ->
val currQuat = Quaternionf().setFromUnnormalized(original)
val horzLeftVector = Vector3f(this.leftVector.x, 0.0f, this.leftVector.z)
val globalPitch = Quaternionf().fromAxisAngleRad(horzLeftVector.normalize(), -deltaPitchDegrees.toRadians())
val horzLeftVector = Vector3f(0.0f, -abs(this.upVector.y.sign), 0.0f).cross(this.forwardVector)
// Avoid NaN issue when normalizing and the forwardVector and upVector are equal
val pitchAxis = if (horzLeftVector.lengthSquared() < 0.01) this.leftVector else horzLeftVector
val globalPitch = Quaternionf().fromAxisAngleRad(pitchAxis.normalize(), -deltaPitchDegrees.toRadians())
val resultQuat = globalPitch.mul(currQuat)
return@updateOrientation Matrix3f().set(resultQuat)
}
@@ -131,6 +131,7 @@ open class PokemonProperties {
props.species = parseSpeciesIdentifier(keyPairs)
props.form = parseForm(keyPairs)
props.friendship = parseIntProperty(keyPairs, listOf("friendship"))?.coerceIn(0, Cobblemon.config.maxPokemonFriendship)
props.fullness = parseIntProperty(keyPairs, listOf("fullness"))
props.pokeball = parseIdentifierOfRegistry(keyPairs, listOf("pokeball")) { identifier -> PokeBalls.getPokeBall(identifier)?.name?.toString() }
props.nature = parseIdentifierOfRegistry(keyPairs, listOf("nature")) { identifier -> Natures.getNature(identifier)?.name?.toString() }
props.ability = parseStringOfRegistry(keyPairs, listOf("ability")) { Abilities.get(it)?.name }
@@ -326,6 +327,7 @@ open class PokemonProperties {
var gender: Gender? = null
var level: Int? = null
var friendship: Int? = null
var fullness: Int? = null
var pokeball: String? = null
var nature: String? = null
var ability: String? = null
@@ -397,11 +399,12 @@ open class PokemonProperties {
pokemon.setFriendship(pokemon.form.baseFriendship)
}
}
fullness?.let { pokemon.currentFullness = it }
pokeball?.let { PokeBalls.getPokeBall(it.asIdentifierDefaultingNamespace())?.let { pokeball -> pokemon.caughtBall = pokeball } }
nature?.let { Natures.getNature(it.asIdentifierDefaultingNamespace())?.let { nature -> pokemon.nature = nature } }
customProperties.forEach { it.apply(pokemon) }
ability?.let { this.createAbility(it, pokemon.form)?.let(pokemon::updateAbility) }
status?.let { Statuses.getStatus(it)?.let { status -> if (status is PersistentStatus) pokemon.applyStatus(status) } }
customProperties.forEach { it.apply(pokemon) }
ivs?.let { ivs ->
ivs.forEach { stat ->
pokemon.setIV(stat.key, stat.value)
@@ -464,7 +467,7 @@ open class PokemonProperties {
val stack = ItemStack(result.item)
stack.applyComponents(result.components)
if (stack.isEmpty) return@let
pokemon.swapHeldItem(stack)
pokemon.swapHeldItem(stack, decrement = true, aiCanDrop = false)
}
pokemon.updateAspects()
}
@@ -504,6 +507,7 @@ open class PokemonProperties {
nickname?.takeIf { it.string != pokemon.nickname?.string }?.let { return false }
form?.takeIf { !it.equals(pokemon.form.name, true) }?.let { return false }
friendship?.takeIf { it != pokemon.friendship }?.let { return false }
fullness?.takeIf { it != pokemon.currentFullness }?.let { return false }
pokeball?.takeIf { it != pokemon.caughtBall.name.toString() }?.let { return false }
nature?.takeIf { it != pokemon.nature.name.toString() }?.let { return false }
ability?.takeIf { it != pokemon.ability.name }?.let { return false }
@@ -588,6 +592,7 @@ open class PokemonProperties {
originalTrainer?.takeIf { it != properties.originalTrainer }?.let{ return false }
originalTrainerType?.takeIf { it != properties.originalTrainerType }?.let{ return false }
moves?.takeIf { it.any { move -> properties.moves?.none { it == move } == true } }?.let { return false }
fullness?.takeIf { it != properties.fullness }?.let { return false }
return true
}
@@ -636,6 +641,7 @@ open class PokemonProperties {
nickname?.let { nbt.putString(DataKeys.POKEMON_NICKNAME, Component.Serializer.toJson(it, registryLookup)) }
form?.let { nbt.putString(DataKeys.POKEMON_FORM_ID, it) }
friendship?.let { nbt.putInt(DataKeys.POKEMON_FRIENDSHIP, it) }
fullness?.let { nbt.putInt(DataKeys.POKEMON_FULLNESS, it) }
pokeball?.let { nbt.putString(DataKeys.POKEMON_CAUGHT_BALL, it) }
nature?.let { nbt.putString(DataKeys.POKEMON_NATURE, it) }
ability?.let { nbt.putString(DataKeys.POKEMON_ABILITY, it) }
@@ -667,6 +673,7 @@ open class PokemonProperties {
nickname = if (tag.contains(DataKeys.POKEMON_NICKNAME)) Component.Serializer.fromJson(tag.getString(DataKeys.POKEMON_NICKNAME), registryLookup) else null
form = if (tag.contains(DataKeys.POKEMON_FORM_ID)) tag.getString(DataKeys.POKEMON_FORM_ID) else null
friendship = if (tag.contains(DataKeys.POKEMON_FRIENDSHIP)) tag.getInt(DataKeys.POKEMON_FRIENDSHIP) else null
fullness = if (tag.contains(DataKeys.POKEMON_FULLNESS)) tag.getInt(DataKeys.POKEMON_FULLNESS) else null
pokeball = if (tag.contains(DataKeys.POKEMON_CAUGHT_BALL)) tag.getString(DataKeys.POKEMON_CAUGHT_BALL) else null
nature = if (tag.contains(DataKeys.POKEMON_NATURE)) tag.getString(DataKeys.POKEMON_NATURE) else null
ability = if (tag.contains(DataKeys.POKEMON_ABILITY)) tag.getString(DataKeys.POKEMON_ABILITY) else null
@@ -700,6 +707,7 @@ open class PokemonProperties {
//nickname?.let { json.addProperty(DataKeys.POKEMON_NICKNAME, Text.Serialization.toJsonString(it)) }
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)}
pokeball?.let { json.addProperty(DataKeys.POKEMON_CAUGHT_BALL, it) }
nature?.let { json.addProperty(DataKeys.POKEMON_NATURE, it) }
ability?.let { json.addProperty(DataKeys.POKEMON_ABILITY, it) }
@@ -732,6 +740,7 @@ open class PokemonProperties {
//nickname = json.get(DataKeys.POKEMON_NICKNAME)?.asString?.let { Text.Serialization.fromJson(it) }
form = json.get(DataKeys.POKEMON_FORM_ID)?.asString
friendship = json.get(DataKeys.POKEMON_FRIENDSHIP)?.asInt
fullness = json.get(DataKeys.POKEMON_FULLNESS)?.asInt
pokeball = json.get(DataKeys.POKEMON_CAUGHT_BALL)?.asString
nature = json.get(DataKeys.POKEMON_NATURE)?.asString
ability = json.get(DataKeys.POKEMON_ABILITY)?.asString
@@ -763,6 +772,7 @@ open class PokemonProperties {
shiny?.let { pieces.add("shiny=$it") }
gender?.let { pieces.add("gender=$it")}
friendship?.let { pieces.add("friendship=$it") }
fullness?.let {pieces.add("fullness=$it")}
pokeball?.let { pieces.add("pokeball=$it") }
nature?.let { pieces.add("nature=$it") }
ability?.let { pieces.add("ability=$it") }
@@ -43,7 +43,6 @@ import com.cobblemon.mod.common.api.riding.stats.RidingStatDefinition
import com.cobblemon.mod.common.api.spawning.TimeRange
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.api.types.adapters.ElementalTypeAdapter
import com.cobblemon.mod.common.battles.runner.ShowdownService
import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPacket
import com.cobblemon.mod.common.pokemon.FormData
import com.cobblemon.mod.common.pokemon.Species
@@ -54,10 +53,8 @@ import com.cobblemon.mod.common.pokemon.ai.ObtainableItemConditionAdapter
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonEvolutionAdapter
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonPreEvolutionAdapter
import com.cobblemon.mod.common.pokemon.evolution.adapters.LegacyItemConditionWrapperAdapter
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.util.adapters.*
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.ifClient
import com.google.common.collect.HashBasedTable
import com.google.gson.Gson
import com.google.gson.GsonBuilder
@@ -65,13 +62,11 @@ import com.google.gson.reflect.TypeToken
import com.mojang.datafixers.util.Either
import net.minecraft.advancements.critereon.ItemPredicate
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.core.registries.Registries
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.tags.TagKey
import net.minecraft.world.effect.MobEffect
import net.minecraft.world.entity.EntityDimensions
import net.minecraft.world.entity.ai.memory.MemoryModuleType
@@ -151,24 +146,17 @@ object PokemonSpecies : JsonDataRegistry<Species> {
val species: Collection<Species>
get() = this.speciesByIdentifier.values
val implemented = mutableListOf<Species>()
val implemented: List<Species>
get() = this.species.filter { it.implemented }
init {
SpeciesAdditions.observable.subscribe {
implemented.clear()
this.species.forEach(Species::initialize)
this.species.forEach {
if (it.implemented) {
this.implemented.add(it)
}
}
this.species.forEach(Species::resolveEvolutionMoves)
Cobblemon.showdownThread.queue {
it.resetRegistryData("species")
it.sendRegistryData(allShowdownSpecies(), "species")
it.indicateSpeciesInitialized()
// Reload this with the mod
CobblemonHeldItemManager.load()
Cobblemon.LOGGER.info("Loaded {} Pokémon species", this.speciesByIdentifier.size)
this.observable.emit(this)
}
@@ -233,7 +221,6 @@ object PokemonSpecies : JsonDataRegistry<Species> {
override fun reload(data: Map<ResourceLocation, Species>) {
this.speciesByIdentifier.clear()
this.implemented.clear()
this.speciesByDex.clear()
data.forEach { (identifier, species) ->
species.resourceIdentifier = identifier
@@ -241,11 +228,6 @@ object PokemonSpecies : JsonDataRegistry<Species> {
this.speciesByDex.remove(old.resourceIdentifier.namespace, old.nationalPokedexNumber)
}
this.speciesByDex.put(species.resourceIdentifier.namespace, species.nationalPokedexNumber, species)
ifClient {
if (species.implemented) {
this.implemented.add(species)
}
}
}
}
@@ -199,6 +199,8 @@ interface Evolution : EvolutionLike {
// (e.g., Oshawott evolving at level 17 knowing Razor Shell, while Dewott only learns it at level 18).
val previousSpeciesLearnableMoves = pokemon.relearnableMoves
val sourcePokemon = pokemon.clone()
this.result.apply(pokemon)
val movesToLearn = previousSpeciesLearnableMoves + this.learnableMoves
@@ -238,7 +240,7 @@ interface Evolution : EvolutionLike {
)
}
CobblemonEvents.EVOLUTION_COMPLETE.post(EvolutionCompleteEvent(pokemon, this))
CobblemonEvents.EVOLUTION_COMPLETE.post(EvolutionCompleteEvent(pokemon, sourcePokemon, this))
CobblemonEvents.POKEMON_GAINED.post(PokemonGainedEvent(pokemon.getOwnerUUID()!!, pokemon))
}
@@ -70,7 +70,7 @@ object SpeciesFeatures : JsonDataRegistry<SpeciesFeatureProvider<*>> {
fun getCodeFeature(name: String) = codeFeatures[name]
fun getResourceFeature(name: String) = resourceFeatures[name]
fun getFeature(name: String) = getCodeFeature(name) ?: getResourceFeature(name)
fun getFeature(name: String) = getCodeFeature(name) ?: getResourceFeature(name) ?: GlobalSpeciesFeatures.getFeature(name)
fun loadOnClient(entries: Collection<Map.Entry<String, SpeciesFeatureProvider<*>>>) {
codeFeatures.putAll(entries.map { it.toPair() })
}
@@ -109,17 +109,7 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
}
// We only sync level up moves atm
override fun shouldSynchronize(other: Learnset): Boolean {
return when {
other.levelUpMoves.size != this.levelUpMoves.size -> true
other.eggMoves.size != this.eggMoves.size -> true
other.tutorMoves.size != this.tutorMoves.size -> true
other.tmMoves.size != this.tmMoves.size -> true
other.evolutionMoves.size != this.evolutionMoves.size -> true
other.formChangeMoves.size != this.formChangeMoves.size -> true
else -> false
}
}
override fun shouldSynchronize(other: Learnset) = other.levelUpMoves != this.levelUpMoves
override fun decode(buffer: RegistryFriendlyByteBuf) {
this.levelUpMoves.clear()
@@ -131,16 +121,6 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
}
levelUpMoves[level] = moves
}
this.eggMoves.clear()
this.eggMoves.addAll(readMoves(buffer))
this.tutorMoves.clear()
this.tutorMoves.addAll(readMoves(buffer))
this.tmMoves.clear()
this.tmMoves.addAll(readMoves(buffer))
this.evolutionMoves.clear()
this.evolutionMoves.addAll(readMoves(buffer))
this.formChangeMoves.clear()
this.formChangeMoves.addAll(readMoves(buffer))
}
override fun encode(buffer: RegistryFriendlyByteBuf) {
@@ -152,25 +132,5 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
buffer.writeInt(move.num)
}
}
writeMoves(buffer, this.eggMoves)
writeMoves(buffer, this.tutorMoves)
writeMoves(buffer, this.tmMoves)
writeMoves(buffer, this.evolutionMoves)
writeMoves(buffer, this.formChangeMoves)
}
private fun writeMoves(buffer: RegistryFriendlyByteBuf, moves: Collection<MoveTemplate>) {
buffer.writeSizedInt(IntSize.U_SHORT, moves.size)
for (move in moves) {
buffer.writeInt(move.num)
}
}
private fun readMoves(buffer: RegistryFriendlyByteBuf): MutableList<MoveTemplate> {
val moves = mutableListOf<MoveTemplate>()
repeat(times = buffer.readSizedInt(IntSize.U_SHORT)) {
Moves.getByNumericalId(buffer.readInt())?.let(moves::add)
}
return moves
}
}
@@ -8,9 +8,6 @@
package com.cobblemon.mod.common.api.riding
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
import net.minecraft.world.entity.PlayerRideableJumping
/**
@@ -9,10 +9,7 @@
package com.cobblemon.mod.common.api.riding
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.api.riding.stats.RidingStatDefinition
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.adapters.RidingBehaviourSettingsAdapter
import net.minecraft.network.RegistryFriendlyByteBuf
@@ -32,10 +29,10 @@ class RidingProperties(
{ buffer.readEnumConstant(RidingStyle::class.java) },
{
val key = buffer.readResourceLocation()
val settings = RidingBehaviourSettingsAdapter.types[key]?.getConstructor()?.newInstance()
val behaviour = RidingBehaviourSettingsAdapter.types[key]?.getConstructor()?.newInstance()
?: error("Unknown controller key: $key")
settings.decode(buffer)
settings
behaviour.decode(buffer)
behaviour
}
)
}
@@ -51,7 +48,10 @@ class RidingProperties(
buffer.writeMap(
v,
{ _, style -> buffer.writeEnumConstant(style) },
{ _, behaviour -> behaviour.encode(buffer) }
{ _, behaviour ->
buffer.writeResourceLocation(behaviour.key)
behaviour.encode(buffer)
}
)
}
}
@@ -25,7 +25,8 @@ data class Seat(
val locator: String?,
val offset: Vec3?,
val poseOffsets: MutableList<SeatPoseOffset>?,
val poseAnimations: MutableList<SeatPoseAnimations>?
val poseAnimations: MutableList<SeatPoseAnimations>?,
val handsBusy: Boolean = false,
) : Encodable {
fun getOffset(poseType: PoseType) : Vec3 {
return poseOffsets?.firstOrNull { poseType in it.poseTypes }?.offset ?: Vec3.ZERO
@@ -40,6 +41,7 @@ data class Seat(
buffer.writeNullable(poseAnimations) { _, v ->
buffer.writeCollection(v) { _, animations -> animations.encode(buffer) }
}
buffer.writeBoolean(handsBusy)
}
companion object {
@@ -48,7 +50,8 @@ data class Seat(
buffer.readNullable { buffer.readString() },
buffer.readNullable { buffer.readVec3() },
buffer.readNullable { buffer.readList { SeatPoseOffset.decode(buffer) } },
buffer.readNullable { buffer.readList { SeatPoseAnimations.decode(buffer) } }
buffer.readNullable { buffer.readList { SeatPoseAnimations.decode(buffer) } },
buffer.readBoolean()
)
}
}
@@ -18,6 +18,7 @@ import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import kotlin.math.abs
/**
* Represents the behaviour of a Pokemon when being ridden.
@@ -113,7 +114,7 @@ interface RidingBehaviour<Settings : RidingBehaviourSettings, State : RidingBeha
* and 1.0 represents x is at or above it.
*/
internal fun scaleToRange(x: Double, min: Double, max: Double): Double {
return ((x - min) / (max - min)).coerceIn(0.0, 1.0)
return if ((max - min) < 0.01) 0.0 else ((x - min) / (max - min)).coerceIn(0.0, 1.0)
}
}
}
@@ -10,10 +10,8 @@ package com.cobblemon.mod.common.api.riding.behaviour
import com.cobblemon.mod.common.api.net.Decodable
import com.cobblemon.mod.common.api.net.Encodable
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.phys.Vec3
/**
* Represents static settings of a riding behaviour.
@@ -21,17 +19,17 @@ import net.minecraft.world.phys.Vec3
* Typically this will be initialized for each pokemon form during deserialization
* to determine how they should ride.
*
* These also exist in a datapacked folder, ride_settings, which are used for the fallback
* values if non-stat settings are omitted in the pokemon form JSON.
*
* @author landonjw
*/
interface RidingBehaviourSettings: Encodable, Decodable {
val key: ResourceLocation
val stats: MutableMap<RidingStat, IntRange>
fun calculate(stat: RidingStat, boosts: Int): Float {
fun calculate(stat: RidingStat, boostAmount: Float): Float {
val range = stats[stat] ?: return 0F
val boostQuotient = boosts / 255F
return range.first + (range.last - range.first) * boostQuotient
return range.first + boostAmount
}
fun hasStat(stat: RidingStat) = stats.containsKey(stat)
}
@@ -18,7 +18,7 @@ import net.minecraft.world.phys.Vec3
* This is intended to contain mutable state that is passed between the client and server
* or temporary state that is used during the riding process.
*
* This will be destroyed when the a user dismounts the Pokemon.
* This will be destroyed when the main rider dismounts the Pokemon.
*
* @author landonjw
*/
@@ -8,13 +8,17 @@
package com.cobblemon.mod.common.api.riding.behaviour
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.DoubleJump
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.client.MountedCameraTypeHandler
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.resources.ResourceLocation
import net.minecraft.tags.FluidTags
import net.minecraft.world.entity.LivingEntity
import kotlin.collections.get
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.player.Player
/**
* Small wrapper around a RidingBehaviour to provide sane defaults in the event that the behaviour is not active.
@@ -49,6 +53,15 @@ class RidingController(
if (newTransition != null && behaviourSettings != null) {
val newState = RidingBehaviours.get(behaviourSettings.key).createDefaultState(behaviourSettings)
context?.let { newState.stamina.set(it.state.stamina.get(), forced = true) }
context?.let { newState.rideVelocity.set(it.state.rideVelocity.get(), forced = true) }
val previousKey = context?.behaviour
val newKey = behaviourSettings.key
for (passenger in entity.passengers) {
if (passenger is Player && passenger !is ServerPlayer) {
MountedCameraTypeHandler.handleTransition(previousKey ?: continue, newKey)
}
}
context = ActiveRidingContext(behaviourSettings.key, behaviourSettings, newState, newTransition)
lastTransitionAge = entity.ticksLived
} else {
@@ -107,6 +120,7 @@ class RidingController(
private fun canTransitionToAir(driver: LivingEntity?): Boolean {
if (driver != null) {
if (driver !is DoubleJump) return false
if (behaviours[RidingStyle.AIR] == null) return false
// check if style is water and player is submerged
val isEyeInLiquid = entity.isEyeInFluid(FluidTags.WATER) || entity.isEyeInFluid(FluidTags.LAVA)
if (context?.style == RidingStyle.LIQUID && isEyeInLiquid) return false
@@ -11,7 +11,9 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.air
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangMath.lerp
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonRideSettings
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.orientation.OrientationController
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.posing.PoseOption
@@ -25,12 +27,13 @@ import com.cobblemon.mod.common.util.math.geometry.toRadians
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth
import net.minecraft.util.SmoothDouble
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import org.joml.Vector3f
import net.minecraft.world.phys.shapes.Shapes
import kotlin.math.*
class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
@@ -47,8 +50,22 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
val poseProvider = PoseProvider<BirdSettings, BirdState>(PoseType.HOVER)
.with(PoseOption(PoseType.FLY) { _, state, _ -> state.rideVelocity.get().z > 0.2 })
val globalBird: BirdSettings
get() = CobblemonRideSettings.bird
override fun isActive(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity): Boolean {
return true
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
//Need to check other fluids
if (vehicle.isInWater || vehicle.isUnderWater) {
return@any false
}
//This might not actually work, depending on what the yPos actually is. yPos of the middle of the entity? the feet?
if (it.y.toDouble() == (vehicle.position().y)) {
val blockState = vehicle.level().getBlockState(it.below())
return@any !(!blockState.isAir && blockState.fluidState.isEmpty)
}
true
}
}
override fun pose(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity): PoseType {
@@ -64,6 +81,57 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
return state.rideVelocity.get().length().toFloat()
}
override fun tick(
settings: BirdSettings,
state: BirdState,
vehicle: PokemonEntity,
driver: Player,
input: Vec3
) {
if(vehicle.level().isClientSide) {
tickStamina(settings, state, vehicle, driver)
checkUpsideDown(state, vehicle, driver)
}
}
private fun checkUpsideDown(
state: BirdState,
vehicle: PokemonEntity,
driver: Player,
) {
val controller = (driver as OrientationControllable).orientationController
if (cos(controller.roll.toRadians()) < 0.0) {
val upsideDownRate = 1.0 / (2.0 * 20.0) // 2 seconds to max out upsideDown force
state.currUpsideDownForce.set( min(1.0, state.currUpsideDownForce.get() + upsideDownRate * abs(cos(controller.roll.toRadians()))))
} else if (state.stamina.get() == 0.0f) { // checks stamina depletion and forces dive if depleted
val upsideDownRate = 1.0 / (2.0 * 20.0) // 1 seconds to max out
state.currUpsideDownForce.set( min(1.0, state.currUpsideDownForce.get() + upsideDownRate))
} else { // Its right side up or has stamina so reset it
state.currUpsideDownForce.set(0.0)
}
}
fun tickStamina(
settings: BirdSettings,
state: BirdState,
vehicle: PokemonEntity,
driver: Player
) {
val stam = state.stamina.get()
// Grab the boost time in seconds and convert to ticks. Then calculate the drain rate as inversely
// proportional to the number of ticks of boost thus making a full boost take x ticks
// in short: "Stamina drains slower at higher values and also replenishes slower"
val totalStam = vehicle.runtime.resolveDouble(settings.staminaExpr ?: globalBird.staminaExpr!!) * 20.0f
val stamDrainRate = (1.0f / totalStam).toFloat()
val newStam = if (driver.zza > 0.0) max(0.0f,stam - stamDrainRate)
else if (!state.gliding.get()) max(0.0f,stam - stamDrainRate * 0.5f) // if hovering half the stam drain
else min(1.0f,stam + stamDrainRate * 0.2f)
state.stamina.set(newStam)
}
override fun rotation(
settings: BirdSettings,
state: BirdState,
@@ -80,8 +148,6 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
driver: Player,
input: Vec3
): Vec3 {
var yVel = 0.0
var leftForce = 0.0
var upForce = 0.0
var forwardForce = 0.0
@@ -100,14 +166,10 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
forwardForce += sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().y
}
// The downward force used to encourage players to stop flying upside down.
val extraDownwardForce = state.currUpsideDownForce.get().pow(2) * -0.3 // 6 blocks a second downward
//Bring the ride out of the sky when stamina is depleted.
if (state.stamina.get() <= 0.0) {
upForce -= 0.3
}
val velocity = Vec3(state.rideVelocity.get().x , upForce, forwardForce)
val velocity = Vec3(state.rideVelocity.get().x , upForce + extraDownwardForce, forwardForce)
return velocity
}
@@ -115,20 +177,25 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
* Calculates the change in the ride space vector due to player input and ride state
*/
fun calculateRideSpaceVel(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity, driver: Player) {
//retrieve stats
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val glideTopSpeed = vehicle.runtime.resolveDouble(settings.glidespeedExpr)
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
val staminaStat = vehicle.runtime.resolveDouble(settings.staminaExpr)
// retrieve stats
val topSpeed = (vehicle.runtime.resolveDouble(settings.speedExpr ?: globalBird.speedExpr!!) / 20.0)
val accel = topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalBird.accelerationExpr!!) * 20.0)
val glideTopSpeed = topSpeed * vehicle.runtime.resolveDouble(settings.glideSpeedExpr ?: globalBird.glideSpeedExpr!!)
var glideSpeedChange = 0.0
var activeInput = false
var newVelocity = Vec3(state.rideVelocity.get().x, state.rideVelocity.get().y, state.rideVelocity.get().z)
// Account for collision
if (vehicle.verticalCollision || vehicle.horizontalCollision) {
newVelocity = newVelocity.normalize().scale(vehicle.deltaMovement.length())
}
//speed up and slow down based on input
if (driver.zza != 0.0f && state.stamina.get() > 0.0) {
//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
@@ -148,7 +215,6 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
glideSpeedChange = sin(Math.toRadians(controller.pitch.toDouble()))
glideSpeedChange = glideSpeedChange * 0.25
//TODO: Possibly create a deadzone around parallel where glide doesn't affect speed?
if (glideSpeedChange <= 0.0) {
//Ensures that a propelling force is still able to be applied when
//climbing in height
@@ -176,6 +242,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
//Vertical movement based on driver input.
val vertTopSpeed = topSpeed / 2.0
val vertInput = when {
state.stamina.get() == 0.0f -> 0.0
driver.jumping && state.rideVelocity.get().length() < 0.3 -> 1.0
driver.isShiftKeyDown && state.rideVelocity.get().length() < 0.3 -> -1.0
else -> 0.0
@@ -197,28 +264,13 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
//Check if the ride should be gliding
if (driver.isLocalPlayer) {
if (activeInput && state.stamina.get() > 0.0) {
if ((activeInput && state.stamina.get() > 0.0) || state.rideVelocity.get().z < 0.2) {
state.gliding.set(false)
} else {
state.gliding.set(true)
}
}
//TODO: Reintroduce stamina drain once stats start to be polished and tweaked
// //Only perform stamina logic if the ride does not have infinite stamina
// if (!vehicle.runtime.resolveBoolean(settings.infiniteStamina)) {
// if (activeInput) {
// state.stamina.set(state.stamina.get() - (0.05 / staminaStat).toFloat())
// }
//
// //Lose a base amount of stamina just for being airborne
// state.stamina.set(state.stamina.get() - (0.01 / staminaStat).toFloat())
// }
// else
// {
// state.stamina.set(1.0f)
// }
//air resistance
if( abs(newVelocity.z) > 0) {
newVelocity = newVelocity.subtract(0.0, 0.0, min(0.0015 * newVelocity.z.sign, newVelocity.z))
@@ -233,39 +285,92 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
driver: Player,
deltaTime: Double
): Vec3 {
if (Cobblemon.config.disableRoll) return Vec3.ZERO
if (driver !is OrientationControllable) return Vec3.ZERO
val controller = (driver as OrientationControllable).orientationController
//limit rolling based on handling and current speed.
//modulated by speed so that when flapping idle in air you are ont wobbling around to look around
//TODO: Tie handling into yaw rate
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
//This rotation limit will be used to know if the ride has exceeded it and its needs correcting.
val rotMin = 15.0
val rotLimit = max(handling * sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed)), rotMin)
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalBird.handlingExpr!!)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalBird.speedExpr!!) / 20.0
var yawDeltaDeg = deltaTime * handling * sin(Math.toRadians(controller.roll.toDouble())) //sin(Math.toRadians(controller.roll.toDouble())) * abs(cos(Math.toRadians(controller.pitch.toDouble())))
val trueYawDelt = yawDeltaDeg * abs(cos(Math.toRadians(controller.pitch.toDouble())))
//val pitchedYawDelt = yawDeltaDeg - trueYawDelt
val yawDeltaDeg = deltaTime * handling * sin(Math.toRadians(controller.roll.toDouble())) //* sin(Math.toRadians(controller.roll.toDouble())).sign
val trueYawDelt = yawDeltaDeg * abs(cos(Math.toRadians(controller.pitch.toDouble())))// * sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed))
controller.applyGlobalYaw(trueYawDelt.toFloat())
// Dampen yaw when upside down
val yawDampen = (1 - abs(min(cos(controller.roll.toRadians()),0.0f)).pow(2))
controller.applyGlobalYaw(trueYawDelt.toFloat() * yawDampen)
//Calculate correcting roll force.
val correctionRate = 5.0
if(abs(controller.roll) > rotLimit && state.rideVelocity.get().length() < 0.2) {
// Correct orientation when at low speeds
correctOrientation(settings, state, vehicle, controller, deltaTime)
controller.applyGlobalPitch(state.currPitchCorrectionForce.get().toRadians())
//yaw, pitch, roll
return Vec3(0.0, 0.0, state.currRollCorrectionForce.get())
}
private fun correctOrientation(
settings: BirdSettings,
state: BirdState,
vehicle: PokemonEntity,
controller: OrientationController,
deltaTime: Double
) {
// Calculate correcting roll force.
// Mult by cos(pitch) to ensure that it doesn't roll correct at when diving or climbing
val rollCorrectionTimer = vehicle.runtime.resolveDouble(settings.timeToRollCorrect ?: globalBird.timeToRollCorrect!!)
val maxRollCorrectionRate = if (state.rideVelocity.get().length() < 0.1) 15.0f else 8.0f * (cos(controller.pitch.toRadians())).pow(2) * ((state.noInputTimeRoll.get() - rollCorrectionTimer)).coerceIn(0.0, 1.0).toFloat()
val maxRollForce = maxRollCorrectionRate / 40.0
val rollArrivalDeg = 90.0
if(state.rideVelocity.get().length() < 0.1 || state.noInputTimeRoll.get() > rollCorrectionTimer) {
val arrivalInfluence = (min(rollArrivalDeg,abs(controller.roll).toDouble()) / rollArrivalDeg)
val desiredRollForce = (0 - controller.roll).sign * maxRollCorrectionRate * arrivalInfluence
var steeredRollForce = desiredRollForce - state.currRollCorrectionForce.get()
// Ensure the 'steering' correction force doesn't exceed our maxForce
if (abs(steeredRollForce) > maxRollForce) {
steeredRollForce = steeredRollForce.sign * maxRollForce
}
// Apply the 'steered' force (its really not a steering force I need a better name)
state.currRollCorrectionForce.set(
state.currRollCorrectionForce.get() + ((abs(controller.roll) - rotLimit) / 180 - rotLimit) * controller.roll.sign * correctionRate * deltaTime * 0.01
state.currRollCorrectionForce.get() + steeredRollForce
)
} else {
state.currRollCorrectionForce.set(
//reduce rollCorrection gradually
lerp(state.currRollCorrectionForce.get(), 0.0, deltaTime * 0.98)
lerp(state.currRollCorrectionForce.get(), 0.0, deltaTime * 0.96)
)
}
//yaw, pitch, roll
return Vec3(0.0, 0.0, state.currRollCorrectionForce.get())
//Calculate correcting pitch force.
val maxPitchCorrectionRate = if (state.currUpsideDownForce.get() != 0.0) 8f * state.currUpsideDownForce.get() else 15f
val maxPitchForce = maxPitchCorrectionRate.toFloat() / 40.0f
val pitchArrivalDeg = 45.0f
var desiredPitch = if (state.currUpsideDownForce.get() != 0.0) 90.0 else 0.0
// if (cos(controller.roll.toRadians()) < 0.0) {
// desiredPitch *= -1 // If not rolled over then flip the pitch force. There are better ways to do this :(
// }
if((state.rideVelocity.get().length() < 0.1 && controller.pitch < 0.0) || state.currUpsideDownForce.get() != 0.0) {
val arrivalInfluence = (min(pitchArrivalDeg,abs(Mth.wrapDegrees(desiredPitch.toFloat().angleDifference(controller.pitch)))) / pitchArrivalDeg)
val desiredPitchForce = maxPitchCorrectionRate.toFloat() * arrivalInfluence
var steeredPitchForce = (desiredPitchForce - state.currPitchCorrectionForce.get())
// Ensure the 'steering' correction force doesn't exceed our maxForce
if (abs(steeredPitchForce) > maxPitchForce) {
steeredPitchForce = steeredPitchForce.sign * maxPitchForce
}
// Apply the 'steered' force (its really not a steering force I need a better name)
state.currPitchCorrectionForce.set(
state.currPitchCorrectionForce.get() + steeredPitchForce
)
} else {
state.currPitchCorrectionForce.set(
//reduce rollCorrection gradually
lerp(state.currPitchCorrectionForce.get(), 0.0, deltaTime * 0.98)
)
}
}
override fun rotationOnMouseXY(
@@ -280,6 +385,25 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
sensitivity: Double,
deltaTime: Double
): Vec3 {
if (driver !is OrientationControllable) return Vec3.ZERO
val controller = (driver as OrientationControllable).orientationController
// Begin the roll correction time counter if not enough mouse input
// is detected
if (mouseX < 1 && abs(mouseY) < 1) {
state.noInputTimeRoll.set(state.noInputTimeRoll.get() + deltaTime)
} else if (cos(controller.roll.toRadians()) < 0.1) {
state.noInputTimeRoll.set(state.noInputTimeRoll.get() + deltaTime * 0.5)
}
else {
state.noInputTimeRoll.set(0.0)
}
if (abs(mouseY) < 1) {
state.noInputTimePitch.set(state.noInputTimePitch.get() + deltaTime)
} else {
state.noInputTimePitch.set(0.0)
}
return when {
Cobblemon.config.disableRoll -> noRollRotation(
settings,
@@ -326,24 +450,23 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
// Set roll to zero if transitioning to noroll config
controller.rotateRoll(controller.roll * -1.0f)
//TODO: hook up handling in parity with normal bird
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalBird.handlingExpr!!)
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult ?: globalBird.mouseInputMult!!)
//Smooth out mouse input.
val smoothingSpeed = 4.0
val mouseXc = (mouseX).coerceIn(-60.0, 60.0)
val mouseYc = (mouseY).coerceIn(-60.0, 60.0)
val mouseXc = ((mouseX).coerceIn(-60.0, 60.0) / 60.0) * mouseInputMult
val mouseYc = ((mouseY).coerceIn(-60.0, 60.0) / 60.0) * mouseInputMult
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1, deltaTime * smoothingSpeed);
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1, deltaTime * smoothingSpeed);
//Give the ability to yaw with x mouse input when at low speeds.
val yawForce = xInput * ( 1.0 - min(sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed)), 0.5))
val yawForce = xInput * handling //* ( 1.0 - min(sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed)), 0.5))
//Apply yaw globally as we don't want roll or pitch changes due to local yaw when looking up or down.
controller.applyGlobalYaw(yawForce.toFloat())
controller.applyGlobalYaw(yawForce.toFloat() * abs(cos(controller.pitch.toRadians())).pow(2))
var pitchRot = yInput
var pitchRot = yInput * handling
// Pitch up globally
if (abs(controller.pitch + pitchRot) >= 89.5 ) {
@@ -372,62 +495,48 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
if (driver !is OrientationControllable) return Vec3.ZERO
val controller = (driver as OrientationControllable).orientationController
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalBird.speedExpr!!) / 20.0
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult ?: globalBird.mouseInputMult!!)
//Smooth out mouse input.
val smoothingSpeed = 4.0
val mouseXc = (mouseX).coerceIn(-60.0, 60.0)
val mouseYc = (mouseY).coerceIn(-60.0, 60.0)
val smoothingSpeed = 2.0
val mouseXc = (mouseX).coerceIn(-45.0, 45.0) * mouseInputMult
// When upside down apply a pitching force to nudge the player towards a dive. This reduces the 'weirdness'
// of being upside down
val rolledOverPitchCorrection = -8*sqrt(abs(min(cos(controller.roll.toRadians()),0.0f))) * (cos(controller.pitch.toRadians())).pow(2)
val mouseYc = ((mouseY).coerceIn(-45.0, 45.0) * mouseInputMult) + rolledOverPitchCorrection
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1, deltaTime * smoothingSpeed);
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1, deltaTime * smoothingSpeed);
val currSpeed = state.rideVelocity.get().length()
val currHorzSpeed = state.rideVelocity.get().horizontalDistance()
val hoverSpeed = 0.1
//limit rolling based on handling and current speed.
//modulated by speed so that when flapping idle in air you are ont wobbling around to look around
val rotMin = 15.0
var rollForce = xInput
val rotLimit = max(90 * sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed)), rotMin)
// Yaw locally when at or below hoverSpeed but not when pitched up or rolled over
val yawDampen = abs(cos(controller.pitch.toRadians())).pow(2) * abs(cos(controller.roll.toRadians())).pow(2)
val localYaw = if (currHorzSpeed < hoverSpeed) xInput * (1 - sqrt(RidingBehaviour.scaleToRange(currHorzSpeed, hoverSpeed, topSpeed))).coerceIn(0.0, 1.0)
else 0.0 //xInput * 0.4 * cos(controller.roll.toRadians()).coerceIn(0.0f, 1.0f) * yawDampen
//Limit roll by non linearly decreasing inputs towards
// a rotation limit based on the current distance from
// that rotation limit
if (abs(controller.roll + rollForce) < rotLimit) {
if (sign(rollForce) == sign(controller.roll).toDouble()) {
//Grab how far the current roll is away from the roll limit
val d = abs(abs(controller.roll) - rotLimit)
rollForce *= (d.pow(2)) / (rotLimit.pow(2))
}
} else { //if (sign(rollForce) == sign(controller.roll).toDouble()) {
rollForce *= max(abs(sin(controller.pitch.toRadians())),0.1f)
}
// Pitch locally up or down depending upon a number of factors:
// - Reduce pitch substantially when rolled and in a steep yaw. This prevents the player from ignoring a slow handling stat
// - Do not pitch at all when at or below hover speed
// - pitch faster at higher speeds
val p = 1.0 - vehicle.runtime.resolveDouble(settings.horizontalPitchExpr ?: globalBird.horizontalPitchExpr!!)
var localPitch = yInput * (1 - abs(sin(controller.roll.toRadians())).pow(2) * p * hypot(controller.forwardVector.x.toDouble(), controller.forwardVector.z.toDouble()).toFloat())
localPitch *= if (currSpeed > hoverSpeed || yInput > 0.0) RidingBehaviour.scaleToRange(currSpeed, 0.0, topSpeed).coerceIn(0.2, 1.0)
else 0.0
//Give the ability to yaw with x mouse input when at low speeds.
val yawForce = xInput * ( 1.0 - sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed)))
// //Yaw locally a bit when up or down so that its more intuitive to make it out of a dive or a straight vertical
// //climb
// val yawForcePitched = xInput * sin(Math.toRadians(abs(controller.pitch.toDouble()))) * 0.25
//Apply yaw globally as we don't want roll or pitch changes due to local yaw when looking up or down.
if (controller.upVector.dot(Vector3f(0f,1f,0f)) > 0) {
controller.applyGlobalYaw(yawForce.toFloat())
}
// Pitch up globally
controller.applyGlobalPitch(-1 * yInput.toFloat())
// roll to 0 if pitching and not upside down. This is to prevent odd back and forth wobbling when
// pitching globally and experiencing axis changes
if (controller.upVector.dot(Vector3f(0f,1f,0f)) > 0) {
// correct roll equal to the amount the ride is pitching (yInput)
// correct roll only when near to horizontal (cos(pitch))
// correct roll proportional to how rolled the ride currently is
rollForce += controller.roll * -0.01 * abs(yInput) * abs(cos(controller.pitch.toRadians())) * abs(sin(controller.roll.toRadians()))
}
// Roll less when slow and not at all when at hoverSpeed or lower.
// Apply a roll righting force when trying to pitch hard while rolled sideways. This nudges the player towards
// pitching globally
val pitchInfluencedRollCorrection = 0.0
val howRolledAmI = (( cos(controller.roll.toRadians()) - 1).coerceIn(-1.0f, 0.0f) * -2).coerceIn(0.0f, 0.7f) //Coerce into how much roll you want to let in in the deadzone
val inputInRolledDir = if (xInput.sign.toFloat() == controller.roll.sign) 1.0 else -cos(controller.roll.toRadians()).coerceIn(-1.0f, 0.0f).toDouble()
val rollDampen = (1 - howRolledAmI * abs(cos(controller.pitch.toRadians())).pow(2) * inputInRolledDir)
val rollForce = if (currHorzSpeed > hoverSpeed) xInput * sqrt(abs(RidingBehaviour.scaleToRange(currHorzSpeed, hoverSpeed, topSpeed)).coerceIn(0.0,1.0)) * rollDampen + pitchInfluencedRollCorrection
else 0.0
//yaw, pitch, roll
return Vec3(0.0, 0.0, rollForce)
return Vec3(localYaw, localPitch, rollForce)
}
@@ -474,13 +583,14 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
vehicle: PokemonEntity,
driver: Player
): Float {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val glideTopSpeed = vehicle.runtime.resolveDouble(settings.glidespeedExpr)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalBird.speedExpr!!) / 20.0
val glideTopSpeed = topSpeed * vehicle.runtime.resolveDouble(settings.glideSpeedExpr ?: globalBird.glideSpeedExpr!!)
val currSpeed = state.rideVelocity.get().length()
//Must I ensure that topspeed is greater than minimum?
val normalizedGlideSpeed = RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), topSpeed, glideTopSpeed)
// Must I ensure that topspeed is greater than minimum?
val normalizedGlideSpeed = RidingBehaviour.scaleToRange(currSpeed, topSpeed, glideTopSpeed)
//Only ever want the fov change to be a max of 0.2 and for it to have non linear scaling.
// Only ever want the fov change to be a max of 0.2 and for it to have non linear scaling.
return 1.0f + normalizedGlideSpeed.pow(2).toFloat() * 0.2f
}
@@ -536,6 +646,17 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
return false
}
override fun damageOnCollision(
settings: BirdSettings,
state: BirdState,
vehicle: PokemonEntity,
impactVec: Vec3
): Boolean {
if (!state.gliding.get()) return false
val impactSpeed = impactVec.horizontalDistance().toFloat() * 10f
return vehicle.causeFallDamage(impactSpeed, 1f, vehicle.damageSources().flyIntoWall())
}
override fun getRideSounds(
settings: BirdSettings,
state: BirdState,
@@ -551,60 +672,70 @@ class BirdSettings : RidingBehaviourSettings {
override val key = BirdBehaviour.KEY
override val stats = mutableMapOf<RidingStat, IntRange>()
var infiniteAltitude: Expression = "false".asExpression()
var infiniteAltitude: Expression? = null
private set
var infiniteStamina: Expression = "false".asExpression()
var infiniteStamina: Expression? = null
private set
//max y level for the ride
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 200.0, 128.0)".asExpression()
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 135.0, 45.0)".asExpression()
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 1.0, 0.35)".asExpression()
var accelerationExpr: Expression = "q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 3.0)), (1.0 / (20.0 * 8.0)))".asExpression()
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 120.0, 20.0)".asExpression()
var timeToRollCorrect: Expression? = null
private set
var glidespeedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 2.0, 1.0)".asExpression()
var handlingExpr: Expression? = null
var horizontalPitchExpr: Expression? = null
var mouseInputMult: Expression? = null
var speedExpr: Expression? = null
// Seconds from stationary to top speed
var accelerationExpr: Expression? = null
var staminaExpr: Expression? = null
var glideSpeedExpr: Expression? = null
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
buffer.writeRidingStats(stats)
rideSounds.encode(buffer)
buffer.writeExpression(infiniteAltitude)
buffer.writeExpression(infiniteStamina)
buffer.writeExpression(glidespeedExpr)
buffer.writeExpression(jumpExpr)
buffer.writeExpression(handlingExpr)
buffer.writeExpression(speedExpr)
buffer.writeExpression(accelerationExpr)
buffer.writeExpression(staminaExpr)
buffer.writeNullableExpression(infiniteAltitude)
buffer.writeNullableExpression(infiniteStamina)
buffer.writeNullableExpression(glideSpeedExpr)
buffer.writeNullableExpression(handlingExpr)
buffer.writeNullableExpression(horizontalPitchExpr)
buffer.writeNullableExpression(mouseInputMult)
buffer.writeNullableExpression(speedExpr)
buffer.writeNullableExpression(accelerationExpr)
buffer.writeNullableExpression(staminaExpr)
buffer.writeNullableExpression(timeToRollCorrect)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
stats.putAll(buffer.readRidingStats())
rideSounds = RideSoundSettingsList.decode(buffer)
infiniteAltitude = buffer.readExpression()
infiniteStamina = buffer.readExpression()
glidespeedExpr = buffer.readExpression()
jumpExpr = buffer.readExpression()
handlingExpr = buffer.readExpression()
speedExpr = buffer.readExpression()
accelerationExpr = buffer.readExpression()
staminaExpr = buffer.readExpression()
infiniteAltitude = buffer.readNullableExpression()
infiniteStamina = buffer.readNullableExpression()
glideSpeedExpr = buffer.readNullableExpression()
handlingExpr = buffer.readNullableExpression()
horizontalPitchExpr = buffer.readNullableExpression()
mouseInputMult = buffer.readNullableExpression()
speedExpr = buffer.readNullableExpression()
accelerationExpr = buffer.readNullableExpression()
staminaExpr = buffer.readNullableExpression()
timeToRollCorrect = buffer.readNullableExpression()
}
}
class BirdState : RidingBehaviourState() {
var gliding = ridingState(false, Side.CLIENT)
var gliding = ridingState(false, Side.BOTH)
var lastGlide = ridingState(-100L, Side.CLIENT)
var currRollCorrectionForce = ridingState(0.0, Side.CLIENT)
var currPitchCorrectionForce = ridingState(0.0, Side.CLIENT)
var noInputTimeRoll = ridingState(0.0, Side.CLIENT)
var noInputTimePitch = ridingState(0.0, Side.CLIENT)
var currUpsideDownForce = ridingState(0.0, Side.CLIENT)
override fun encode(buffer: FriendlyByteBuf) {
super.encode(buffer)
buffer.writeFloat(stamina.get())
buffer.writeBoolean(gliding.get())
}
@@ -618,6 +749,9 @@ class BirdState : RidingBehaviourState() {
gliding.set(false, forced = true)
lastGlide.set(-100L, forced = true)
currRollCorrectionForce.set(0.0, forced = true)
currPitchCorrectionForce.set(0.0, forced = true)
noInputTimeRoll.set(0.0, forced = true)
noInputTimePitch.set(0.0, forced = true)
}
override fun toString(): String {
@@ -630,6 +764,9 @@ class BirdState : RidingBehaviourState() {
it.gliding.set(this.gliding.get(), forced = true)
it.lastGlide.set(this.lastGlide.get(), forced = true)
it.currRollCorrectionForce.set(this.currRollCorrectionForce.get(), forced = true)
it.currPitchCorrectionForce.set(this.currPitchCorrectionForce.get(), forced = true)
it.noInputTimeRoll.set(this.noInputTimeRoll.get(), forced = true)
it.noInputTimePitch.set(this.noInputTimePitch.get(), forced = true)
}
override fun shouldSync(previous: RidingBehaviourState): Boolean {
@@ -638,3 +775,16 @@ class BirdState : RidingBehaviourState() {
return super.shouldSync(previous)
}
}
/**
* Calculates the shortest difference between two angles.
* The result will be in the range [-180, 180].
* A positive result indicates a clockwise direction from this angle to the other,
* a negative result indicates a counter-clockwise direction.
*/
fun Float.angleDifference(other: Float): Float {
var diff = other - this
while (diff <= -180F) diff += 360F
while (diff > 180F) diff -= 360F
return diff
}
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.air
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonRideSettings
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
@@ -37,6 +38,9 @@ class GliderBehaviour : RidingBehaviour<GliderSettings, RidingBehaviourState> {
override val key = KEY
val globalGlider: GliderSettings
get() = CobblemonRideSettings.glider
override fun getRidingStyle(settings: GliderSettings, state: RidingBehaviourState): RidingStyle {
return RidingStyle.AIR
}
@@ -53,7 +57,7 @@ class GliderBehaviour : RidingBehaviour<GliderSettings, RidingBehaviourState> {
}
override fun speed(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity, driver: Player): Float {
return vehicle.runtime.resolveFloat(settings.speed)
return vehicle.runtime.resolveFloat(settings.speed ?: globalGlider.speed!!)
}
override fun rotation(
@@ -72,10 +76,9 @@ class GliderBehaviour : RidingBehaviour<GliderSettings, RidingBehaviourState> {
driver: Player,
input: Vec3
): Vec3 {
val xVector = if (vehicle.runtime.resolveBoolean(settings.canStrafe)) driver.xxa.toDouble() else 0.0
val yVector = -vehicle.runtime.resolveDouble(settings.glideSpeed)
val xVector = if (vehicle.runtime.resolveBoolean(settings.canStrafe ?: globalGlider.canStrafe!!)) driver.xxa.toDouble() else 0.0
val yVector = -vehicle.runtime.resolveDouble(settings.glideSpeed ?: globalGlider.glideSpeed!!)
val zVector = driver.zza.toDouble()
val speedStat = settings.calculate(RidingStat.SPEED, 0)
return Vec3(xVector, yVector, zVector)
}
@@ -196,31 +199,30 @@ class GliderSettings : RidingBehaviourSettings {
override val key = GliderBehaviour.KEY
override val stats = mutableMapOf<RidingStat, IntRange>()
var glideSpeed: Expression = "0.1".asExpression()
var glideSpeed: Expression? = null
private set
var speed: Expression = "1.0".asExpression()
var speed: Expression? = null
private set
var canStrafe: Expression = "false".asExpression()
var canStrafe: Expression? = null
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
buffer.writeRidingStats(stats)
rideSounds.encode(buffer)
buffer.writeExpression(glideSpeed)
buffer.writeExpression(speed)
buffer.writeExpression(canStrafe)
buffer.writeNullableExpression(glideSpeed)
buffer.writeNullableExpression(speed)
buffer.writeNullableExpression(canStrafe)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
stats.putAll(buffer.readRidingStats())
rideSounds = RideSoundSettingsList.decode(buffer)
glideSpeed = buffer.readExpression()
speed = buffer.readExpression()
canStrafe = buffer.readExpression()
glideSpeed = buffer.readNullableExpression()
speed = buffer.readNullableExpression()
canStrafe = buffer.readNullableExpression()
}
}
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.air
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonRideSettings
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
@@ -21,7 +22,16 @@ import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.readNullableExpression
import com.cobblemon.mod.common.util.readRidingStats
import com.cobblemon.mod.common.util.resolveDouble
import com.cobblemon.mod.common.util.writeNullableExpression
import com.cobblemon.mod.common.util.writeRidingStats
import kotlin.math.abs
import kotlin.math.pow
import kotlin.math.sin
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.SmoothDouble
@@ -30,9 +40,6 @@ import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
import kotlin.math.abs
import kotlin.math.pow
import kotlin.math.sin
class HelicopterBehaviour : RidingBehaviour<HelicopterSettings, RidingBehaviourState> {
companion object {
@@ -41,6 +48,8 @@ class HelicopterBehaviour : RidingBehaviour<HelicopterSettings, RidingBehaviourS
}
override val key = KEY
val globalHelicopter: HelicopterSettings
get() = CobblemonRideSettings.helicopter
override fun getRidingStyle(settings: HelicopterSettings, state: RidingBehaviourState): RidingStyle {
return RidingStyle.AIR
@@ -93,8 +102,8 @@ class HelicopterBehaviour : RidingBehaviour<HelicopterSettings, RidingBehaviourS
val runtime = vehicle.runtime
//If the player is not modulating height then hover
var yVel = if (driver.jumping) runtime.resolveDouble(settings.verticalVelocity)
else if (driver.isShiftKeyDown) -runtime.resolveDouble(settings.verticalVelocity)
var yVel = if (driver.jumping) runtime.resolveDouble(settings.verticalVelocity ?: globalHelicopter.verticalVelocity!!)
else if (driver.isShiftKeyDown) -runtime.resolveDouble(settings.verticalVelocity ?: globalHelicopter.verticalVelocity!!)
else (0.0)
yVel *= 0.25
@@ -290,36 +299,35 @@ class HelicopterSettings : RidingBehaviourSettings {
override val key = HelicopterBehaviour.KEY
override val stats = mutableMapOf<RidingStat, IntRange>()
var gravity: Expression = "1.0".asExpression()
var gravity: Expression? = null
private set
var horizontalAcceleration: Expression = "0.1".asExpression()
var horizontalAcceleration: Expression? = null
private set
var verticalVelocity: Expression = "1.0".asExpression()
var verticalVelocity: Expression? = null
private set
var speed: Expression = "1.0".asExpression()
var speed: Expression? = null
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
buffer.writeRidingStats(stats)
rideSounds.encode(buffer)
buffer.writeExpression(gravity)
buffer.writeExpression(horizontalAcceleration)
buffer.writeExpression(verticalVelocity)
buffer.writeExpression(speed)
buffer.writeNullableExpression(gravity)
buffer.writeNullableExpression(horizontalAcceleration)
buffer.writeNullableExpression(verticalVelocity)
buffer.writeNullableExpression(speed)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
stats.putAll(buffer.readRidingStats())
rideSounds = RideSoundSettingsList.decode(buffer)
gravity = buffer.readExpression()
horizontalAcceleration = buffer.readExpression()
verticalVelocity = buffer.readExpression()
speed = buffer.readExpression()
gravity = buffer.readNullableExpression()
horizontalAcceleration = buffer.readNullableExpression()
verticalVelocity = buffer.readNullableExpression()
speed = buffer.readNullableExpression()
}
}
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.api.riding.behaviour.types.air
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.CobblemonRideSettings
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.posing.PoseOption
@@ -19,13 +20,16 @@ import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.math.geometry.toRadians
import net.minecraft.client.Minecraft
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth
import net.minecraft.util.SmoothDouble
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.level.ClipContext
import net.minecraft.world.phys.HitResult
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
@@ -38,6 +42,8 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
}
override val key = KEY
val globalHover: HoverSettings
get() = CobblemonRideSettings.hover
override fun getRidingStyle(settings: HoverSettings, state: HoverState): RidingStyle {
return RidingStyle.AIR
@@ -81,7 +87,57 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
vehicle: PokemonEntity,
driver: Player
): Float {
return state.rideVelocity.get().length().toFloat()
return state.speed.get().toFloat()
}
override fun tick(
settings: HoverSettings,
state: HoverState,
vehicle: PokemonEntity,
driver: Player,
input: Vec3
) {
if(vehicle.level().isClientSide) {
tickStamina(settings, state, vehicle)
checkTooHigh(settings, state, vehicle)
}
}
fun checkTooHigh(
settings: HoverSettings,
state: HoverState,
vehicle: PokemonEntity,
) {
val heightLimit = vehicle.runtime.resolveDouble(settings.jumpExpr ?: globalHover.jumpExpr!!)
val pos = vehicle.position()
val level = Minecraft.getInstance().player?.level() ?: return
val hit = level.clip(
ClipContext(
pos,
pos.subtract(0.0, heightLimit, 0.0),
ClipContext.Block.COLLIDER,
ClipContext.Fluid.NONE,
vehicle
)
)
state.tooHigh.set(hit.type == HitResult.Type.MISS)
}
fun tickStamina(
settings: HoverSettings,
state: HoverState,
vehicle: PokemonEntity
) {
if (vehicle.runtime.resolveBoolean(settings.infiniteStamina ?: globalHover.infiniteStamina!!)) {
return
}
val stam = state.stamina.get()
val stamDrainRate = (1.0f / vehicle.runtime.resolveDouble(settings.staminaExpr ?: globalHover.staminaExpr!!)).toFloat() / 20.0f
val stamReplenishRate = (1.0f / vehicle.runtime.resolveDouble(settings.stamReplenishTimeSeconds ?: globalHover.stamReplenishTimeSeconds!!) ).toFloat() / 20.0f // 2 seconds to replenish a full bar of stamina
val newStam = if (state.tooHigh.get()) max(0.0f, stam - stamDrainRate) else min(1.0f,stam + stamReplenishRate)
state.stamina.set(newStam)
}
override fun updatePassengerRotation(
@@ -90,24 +146,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
vehicle: PokemonEntity,
driver: LivingEntity
) {
//Take the inverse so that it cancels out how
//much the entity rotates.
val turnAmount = calcRotAmount(settings, state, vehicle, driver)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val maxYawDiff = 90.0f
//Normalize the current rotation diff
val rotMod = Mth.wrapDegrees(driver.yRot - vehicle.yRot) / maxYawDiff
val rotAmount = 10.0f
//Take the inverse so that you turn more at higher speeds
val normSpeed = 1.0f - 0.5f*RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed).toFloat()
//driver.yRot += (entity.riding.deltaRotation.y - turnAmount)
//driver.setYHeadRot(driver.yHeadRot + (entity.riding.deltaRotation.y) - turnAmount)
return
}
override fun clampPassengerRotation(
@@ -116,12 +155,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
vehicle: PokemonEntity,
driver: LivingEntity
) {
val f = Mth.wrapDegrees(driver.yRot - vehicle.yRot)
val lookYawLimit = 90.0f
val g = Mth.clamp(f, -lookYawLimit, lookYawLimit)
driver.yRotO += g - f
driver.yRot = driver.yRot + g - f
driver.setYHeadRot(driver.yRot)
return
}
@@ -132,9 +166,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
driver: LivingEntity
): Vec2 {
val turnAmount = calcRotAmount(settings, state, vehicle, driver)
return Vec2(driver.xRot, vehicle.yRot + turnAmount )
}
/*
@@ -149,27 +181,19 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
vehicle: PokemonEntity,
driver: LivingEntity
): Float {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
//In degrees per second
val handling = 220.0
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalHover.speedExpr!!) / 20.0
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalHover.handlingExpr!!) / 20.0
val maxYawDiff = 90.0f
//Normalize the current rotation diff
val rotDiff = Mth.wrapDegrees(driver.yRot - vehicle.yRot)
val rotDiffNorm = rotDiff / maxYawDiff
// Normalize the current rotation diff
val rotDiff = Mth.wrapDegrees(driver.yRot - vehicle.yRot).coerceIn(-maxYawDiff,maxYawDiff)
val rotDiffInfl = sqrt(abs(rotDiff / maxYawDiff)) * rotDiff.sign
//Take the square root so that the ride levels out quicker when at lower differences between entity
//y and driver y
//This influences the speed of the turn based on how far in one direction you're looking
val rotDiffMod = (sqrt(abs(rotDiffNorm)) * rotDiffNorm.sign)
var turnRate = (handling.toFloat()) * rotDiffInfl
turnRate -= (turnRate * 0.5f) * (RidingBehaviour.scaleToRange(vehicle.deltaMovement.length(), 0.0, topSpeed).coerceIn(0.0, 1.0)).pow(2).toFloat()
//Take the inverse so that you turn less at higher speeds
val normSpeed = 1.0f // = 1.0f - 0.5f*RidingBehaviour.scaleToRange(state.rideVelocityocity.length(), 0.0, topSpeed).toFloat()
val turnRate = (handling.toFloat() / 20.0f)
//Ensure you only ever rotate as much difference as there is between the angles.
val turnSpeed = turnRate * rotDiffMod * normSpeed
// Ensure you only ever rotate as much difference as there is between the angles.
val turnSpeed = turnRate
val rotAmount = turnSpeed.coerceIn(-abs(rotDiff), abs(rotDiff))
return rotAmount
@@ -182,128 +206,77 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
driver: Player,
input: Vec3
): Vec3 {
// if (Minecraft.getInstance().options.keySprint.isDown) {
// val belowBox = vehicle.boundingBox.expandTowards(0.0,-1.5, 0.0).inflate(5.0, 10.0,5.0)
//
// val level = vehicle.level()
// val toBeSucked = level.getEntities(vehicle, belowBox)
// for (suckee in toBeSucked) {
// if (suckee != null) {
// abductify(suckee, vehicle.position().add(0.0,-2.0,0.0))
// suckee.deltaMovement = suckee.deltaMovement.add(0.0, 0.5,0.0)
//
// }
// }
// }
//
// if (firstEntity != null) {
// // Example action: print the entity name
// println("Found nearby entity: ${firstEntity.name.string}")
//
// firstEntity.deltaMovement = firstEntity.deltaMovement.add(0.0, 0.1,0.0)
// }
val retVel = calculateRideSpaceVel(settings, state, vehicle, driver)
//state.rideVelocity.set(retVel)
return retVel
}
private fun abductify(abductee: Entity, pos: Vec3) {
val diff = pos.subtract(abductee.position()).scale(1.0)
abductee.deltaMovement = abductee.deltaMovement.add(diff)
}
private fun calculateRideSpaceVel(
settings: HoverSettings,
state: HoverState,
vehicle: PokemonEntity,
driver: Player
): Vec3 {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
val speed = vehicle.deltaMovement.length()
// Convert these to their tick values
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalHover.speedExpr!!) / 20.0
val accel = topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalHover.accelerationExpr!!) * 20.0)
//Flag for determining if player is actively inputting
var activeInput = false
var newVelocity = state.rideVelocity.get()
val minSpeed = 0.0
var newVelocity = vehicle.deltaMovement
// Check for collisions and if found, update the speed to reflect it
if (vehicle.verticalCollision || vehicle.horizontalCollision) {
state.speed.set(vehicle.deltaMovement.length())
}
// align the velocity vector to be in local vehicle space
val yawAligned = Matrix3f().rotateY(vehicle.yRot.toRadians())
newVelocity = (newVelocity.toVector3f().mul(yawAligned)).toVec3d()
//air Resistance
//newVelocity = newVelocity.lerp(Vec3.ZERO,(((topSpeed - speed) / topSpeed))*0.001)
//speed up and slow down based on input
if (driver.zza != 0.0f && state.stamina.get() > 0.0) {
//make sure it can't exceed top speed
val forwardInput = when {
speed > topSpeed && (driver.zza.sign == newVelocity.z.sign.toFloat()) -> 0.0
else -> driver.zza.sign
}
newVelocity = Vec3(
newVelocity.x,
newVelocity.y,
(newVelocity.z + (accel * forwardInput.toDouble())))
} else {
// newVelocity = Vec3(
// newVelocity.x,
// newVelocity.y,
// newVelocity.z - (((topSpeed - speed) / topSpeed)*0.001)
// )
}
//speed up and slow down based on input
if (driver.xxa != 0.0f && state.stamina.get() > 0.0) {
//make sure it can't exceed top speed
val lateralInput = when {
speed > topSpeed && (driver.xxa.sign == newVelocity.x.sign.toFloat()) -> 0.0
else -> driver.xxa.sign
}
newVelocity = Vec3(
newVelocity.x + (accel * lateralInput.toDouble()),
newVelocity.y,
(newVelocity.z ))
//air resistance
// if (lateralInput == 0.0) {
// newVelocity = Vec3(
// newVelocity.x - (((topSpeed - speed) / topSpeed)*0.001),
// newVelocity.y,
// newVelocity.z
// )
// }
}
//Vertical movement based on driver input.
// Vertical movement based on driver input.
val vertInput = when {
driver.jumping && !(speed > topSpeed && (newVelocity.y.sign.toFloat() > 0.0))-> 1.0
driver.isShiftKeyDown && !(speed > topSpeed && (newVelocity.y.sign.toFloat() < 0.0))-> -1.0
state.stamina.get() == 0.0f -> -1.0
driver.jumping -> 1.0
driver.isShiftKeyDown -> -1.0
else -> 0.0
}
//air resistance
// if (vertInput == 0.0) {
// newVelocity = Vec3(
// newVelocity.x,
// newVelocity.y - (((topSpeed - speed) / topSpeed)*0.001),
// newVelocity.z
// )
// }
// Normalize and scale to accel so that diagonal movement
// doesn't speed up the ride more than input in one direction
var inputVel = Vec3(
driver.xxa.sign.toDouble(),
vertInput,
driver.zza.sign.toDouble()
).normalize()
if (state.stamina.get() > 0.0) {
newVelocity = Vec3(
newVelocity.x,
(newVelocity.y + (accel * vertInput)), //.coerceIn(-vertTopSpeed, vertTopSpeed),
newVelocity.z)
}
// Air Friction/Resistance
// When the dot between friction and input is -1 we want to apply none of it.
// In short we want to avoid applying friction that opposes our input force.
// otherwise we would not be able to reach our top speed when acceleration is
// low.
val frictionConst = 0.02
var frictionForce = newVelocity.scale(-1.0).scale(frictionConst)
val frictionApplied = if(inputVel.lengthSqr() != 0.0) 1 - (0.5*(1 + -1.0 * (inputVel.normalize().dot(frictionForce.normalize()))))
else 1.0
frictionForce = frictionForce.scale((frictionApplied))
newVelocity = newVelocity.add(frictionForce)
// Ensure that if you're not applying force where the ride is facing then you apply a portion of debuff
val strafeDebuff = 0.5
val vehicleForwardVec = Vec3(0.0, 0.0, 1.0)
val playerForwardVec = inputVel //.yRot(vehicle.yRot)
val inputStrength = accel - (accel * strafeDebuff*(1 - vehicleForwardVec.dot(playerForwardVec).coerceIn(0.0, 1.0)))
inputVel = inputVel.scale(inputStrength)
newVelocity = newVelocity.add(inputVel)
// Set speed to new magnitude to avoid the horrible things minecraft friction
// does to our vectors :(
state.speed.set(min(topSpeed, newVelocity.length()))
// Check to see if this new velocity will exceed top speed and if it will then cap it
newVelocity = if (newVelocity.length() > topSpeed) newVelocity.normalize().scale(topSpeed) else newVelocity
val revertYawAligned = Matrix3f().rotateY(-vehicle.yRot.toRadians())
state.rideVelocity.set((newVelocity.toVector3f().mul(revertYawAligned)).toVec3d())
return newVelocity
}
@@ -330,13 +303,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
sensitivity: Double,
deltaTime: Double
): Vec3 {
//Smooth out mouse input.
val smoothingSpeed = 4
val xInput = mouseXSmoother.getNewDeltaValue(mouseX * 0.1, deltaTime * smoothingSpeed);
val yInput = mouseYSmoother.getNewDeltaValue(mouseY * 0.1, deltaTime * smoothingSpeed);
//yaw, pitch, roll
return Vec3(0.0, yInput, xInput)
return Vec3.ZERO
}
override fun canJump(
@@ -383,7 +350,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
vehicle: PokemonEntity,
driver: Player
): Float {
return 1.0f
return if (state.isBoosting.get()) 1.1f else 1.0f
}
override fun useAngVelSmoothing(
@@ -466,66 +433,107 @@ class HoverSettings : RidingBehaviourSettings {
override val key = HoverBehaviour.KEY
override val stats = mutableMapOf<RidingStat, IntRange>()
var rideSound: ResourceLocation = "ride.loop.saucer".asIdentifierDefaultingNamespace()
var volumeExpr: Expression = "math.pow(math.min(q.ride_velocity() / 0.8, 1.0),2)".asExpression()
var pitchExpr: Expression = "math.pow(math.min(1.0 + 0.2*(q.ride_velocity() / 0.8), 1.2),2)".asExpression()
var canJump = "true".asExpression()
var infiniteStamina: Expression? = null
private set
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 0.65, 0.3)".asExpression()
var canJump: Expression? = null
private set
// Max accel is a whole 1.0 in 1 second. The conversion in the function below is to convert seconds to ticks
var accelerationExpr: Expression =
"q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 1.5)), (1.0 / (20.0 * 3.0)))".asExpression()
var stamReplenishTimeSeconds: Expression? = null
private set
// Between 30 seconds and 10 seconds at the lowest when at full speed.
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 30.0, 10.0)".asExpression()
// Speed in block per second
var speedExpr: Expression? = null
private set
//Between a one block jump and a ten block jump
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 10.0, 1.0)".asExpression()
// Acceleration in number of seconds to top speed
var accelerationExpr: Expression? = null
private set
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 140.0, 20.0)".asExpression()
// Amount of seconds between boosts
var staminaExpr: Expression? = null
private set
// Blocks before stamina drain
var jumpExpr: Expression? = null
private set
// Turn rate in degrees per second when stationary
var handlingExpr: Expression? = null
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
buffer.writeRidingStats(stats)
rideSounds.encode(buffer)
buffer.writeExpression(speedExpr)
buffer.writeExpression(accelerationExpr)
buffer.writeExpression(staminaExpr)
buffer.writeExpression(jumpExpr)
buffer.writeExpression(handlingExpr)
buffer.writeNullableExpression(infiniteStamina)
buffer.writeNullableExpression(canJump)
buffer.writeNullableExpression(stamReplenishTimeSeconds)
buffer.writeNullableExpression(speedExpr)
buffer.writeNullableExpression(accelerationExpr)
buffer.writeNullableExpression(staminaExpr)
buffer.writeNullableExpression(jumpExpr)
buffer.writeNullableExpression(handlingExpr)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
stats.putAll(buffer.readRidingStats())
rideSounds = RideSoundSettingsList.decode(buffer)
speedExpr = buffer.readExpression()
accelerationExpr = buffer.readExpression()
staminaExpr = buffer.readExpression()
jumpExpr = buffer.readExpression()
handlingExpr = buffer.readExpression()
infiniteStamina = buffer.readNullableExpression()
canJump = buffer.readNullableExpression()
stamReplenishTimeSeconds = buffer.readNullableExpression()
speedExpr = buffer.readNullableExpression()
accelerationExpr = buffer.readNullableExpression()
staminaExpr = buffer.readNullableExpression()
jumpExpr = buffer.readNullableExpression()
handlingExpr = buffer.readNullableExpression()
}
}
class HoverState : RidingBehaviourState() {
var speed = ridingState(0.0, Side.CLIENT)
var isBoosting = ridingState(false, Side.BOTH)
var boostVec = ridingState(Vec3.ZERO, Side.CLIENT)
var boostTicks = ridingState(0, Side.CLIENT)
var tooHigh = ridingState(false, Side.CLIENT)
override fun encode(buffer: FriendlyByteBuf) {
super.encode(buffer)
buffer.writeDouble(speed.get())
buffer.writeBoolean(isBoosting.get())
buffer.writeBoolean(tooHigh.get())
}
override fun decode(buffer: FriendlyByteBuf) {
super.decode(buffer)
speed.set(buffer.readDouble(), forced = true)
isBoosting.set(buffer.readBoolean(), forced = true)
tooHigh.set(buffer.readBoolean(), forced = true)
}
override fun reset() {
super.reset()
speed.set(0.0, forced = true)
isBoosting.set(false, forced = true)
boostVec.set(Vec3.ZERO, forced = true)
boostTicks.set(0, forced = true)
tooHigh.set(false, forced = true)
}
override fun copy() = HoverState().also {
it.rideVelocity.set(this.rideVelocity.get(), forced = true)
it.stamina.set(this.stamina.get(), forced = true)
it.isBoosting.set(this.isBoosting.get(), forced = true)
it.boostVec.set(this.boostVec.get(), forced = true)
it.boostTicks.set(this.boostTicks.get(), forced = true)
it.tooHigh.set(this.tooHigh.get(), forced = true)
}
override fun shouldSync(previous: RidingBehaviourState): Boolean {
if (previous !is HoverState) return false
if (previous.isBoosting.get() != isBoosting.get()) return true
return super.shouldSync(previous)
}
}
@@ -11,19 +11,35 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.air
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangMath.lerp
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonRideSettings
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
import com.cobblemon.mod.common.api.riding.behaviour.Side
import com.cobblemon.mod.common.api.riding.behaviour.ridingState
import com.cobblemon.mod.common.api.riding.posing.PoseOption
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.*
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.config.CobblemonConfig
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.math.geometry.toRadians
import com.cobblemon.mod.common.util.readNullableExpression
import com.cobblemon.mod.common.util.readRidingStats
import com.cobblemon.mod.common.util.resolveDouble
import com.cobblemon.mod.common.util.toVec3d
import com.cobblemon.mod.common.util.writeNullableExpression
import com.cobblemon.mod.common.util.writeRidingStats
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import net.minecraft.client.Minecraft
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.SmoothDouble
import net.minecraft.world.entity.LivingEntity
@@ -31,7 +47,7 @@ import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
import kotlin.math.*
import org.joml.Vector3f
class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
companion object {
@@ -44,6 +60,9 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
return RidingStyle.AIR
}
val globalJet: JetSettings
get() = CobblemonRideSettings.jet
val poseProvider = PoseProvider<JetSettings, JetState>(PoseType.HOVER)
.with(PoseOption(PoseType.FLY) { _, state, _ -> state.rideVelocity.get().z > 0.1 })
@@ -67,35 +86,65 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
}
override fun speed(settings: JetSettings, state: JetState, vehicle: PokemonEntity, driver: Player): Float {
//retrieve stats
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val staminaStat = vehicle.runtime.resolveDouble(settings.staminaExpr)
//retrieve minSpeed
val minSpeed = vehicle.runtime.resolveDouble(settings.minSpeed)
//TODO: Reintroduce stamina drain once stats start to be polished and tweaked
state.stamina.set(1.0f)
// //Reduce stamina unless stamina is infinite
// if (!vehicle.runtime.resolveBoolean(settings.infiniteStamina)) {
// //Calculate stamina loss due to speed
// //At max speed it will tick down 0.1 a second so the stamina will last ten seconds
// //There has got to be a better way to express this equation. It interpolates between 0.5 and 1.0
// var staminaRate = (RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), minSpeed, topSpeed))
//
// //interpolate between 0.25 and 1.0 so that you always have at least a min of 0.25 stam loss
// staminaRate = 0.25 + (0.75 * staminaRate.pow(3))
//
// //Calculate stamina loss in seconds achievable at top speed
// val staminaLoss = staminaRate * (1.0 / (20.0 * staminaStat))
// state.stamina.set(max(state.stamina.get() - staminaLoss, 0.0).toFloat())
// } else {
// state.stamina.set(1.0f)
// }
return state.rideVelocity.get().length().toFloat()
}
override fun tick(settings: JetSettings, state: JetState, vehicle: PokemonEntity, driver: Player, input: Vec3) {
if(vehicle.level().isClientSide) {
handleBoosting(state)
tickStamina(settings, state, vehicle)
}
}
fun handleBoosting(
state: JetState,
) {
//If the forward key is not held then it cannot be boosting
val boostKeyPressed = Minecraft.getInstance().options.keySprint.isDown()
if(state.stamina.get() != 0.0f && boostKeyPressed) {
if (state.stamina.get() >= 0.0f) {
//If on the previous tick the boost key was held then don't change if the ride is boosting
if(state.boostIsToggleable.get()) {
//flip the boosting state if boost key is pressed
state.boosting.set(!state.boosting.get())
}
//If the boost key is not held then next tick boosting is toggleable
state.boostIsToggleable.set(false)
}
} else {
//Turn off boost and reset boost params
state.boostIsToggleable.set(true)
state.boosting.set(false)
state.canSpeedBurst.set(true)
}
}
fun tickStamina(
settings: JetSettings,
state: JetState,
vehicle: PokemonEntity,
) {
val stam = state.stamina.get()
var newStam = stam
val stamDrainRate = (1.0f / vehicle.runtime.resolveDouble(settings.staminaExpr ?: globalJet.staminaExpr!!)).toFloat() / 20.0f
if (state.boosting.get()) {
newStam = max(0.0f,stam - stamDrainRate * 1.5f)
} else {
newStam = max(0.0f,stam - stamDrainRate)
}
// If out of stamina then increase noStamTickCnt
if (newStam == 0.0f) {
state.noStamTickCnt.set(state.noStamTickCnt.get() + 1)
} else {
state.noStamTickCnt.get()
}
state.stamina.set(newStam)
}
override fun rotation(
settings: JetSettings,
state: JetState,
@@ -112,26 +161,26 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
driver: Player,
input: Vec3
): Vec3 {
var upForce = 0.0
var forwardForce = 0.0
val controller = (driver as? OrientationControllable)?.orientationController
if (controller == null || controller.orientation == null) return Vec3.ZERO
//Calculate ride space velocity
calculateRideSpaceVel(settings, state, vehicle, driver)
//Translate ride space velocity to world space velocity.
if (controller != null) {
upForce = -1.0 * sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().z
forwardForce = cos(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().z
}
// The downward force used to encourage players to stop flying upside down.
val maxDownwardForceTickCnt = 20.0
val extraDownwardForce = if(state.stamina.get() == 0.0f) -0.3 * (state.noStamTickCnt.get() / maxDownwardForceTickCnt).coerceIn(0.0, 1.0) else 0.0 // 6 blocks a second downward
//If stamina has run out then initiate forced glide down.
upForce = if (state.stamina.get() > 0.0) upForce else -0.7
// Convert the local velocity vector into a world vector
val localVelVec = Vector3f(
state.rideVelocity.get().x.toFloat(),
(state.rideVelocity.get().y).toFloat(),
state.rideVelocity.get().z.toFloat() * -1.0f // Flip the z axis to make this left handed? orr.. right handed? idk, flip it though
)
var worldVelVec = localVelVec.mul(controller.orientation).toVec3d().yRot(vehicle.yRot.toRadians()) // Unrotate preemptively as this vector gets rotate later down the line in MC logic.
worldVelVec = worldVelVec.add(0.0, extraDownwardForce, 0.0) // Add the stamina depletion force to bring the ride down.
val velocity = Vec3(0.0, upForce, forwardForce)
return velocity
return worldVelVec
}
/*
@@ -143,13 +192,36 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
vehicle: PokemonEntity,
driver: Player
) {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
val minSpeed = vehicle.runtime.resolveDouble(settings.minSpeed)
val speed = state.rideVelocity.get().length()
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalJet.speedExpr!!) / 20.0
val accel = topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalJet.accelerationExpr!!) * 20.0)
val deccel = vehicle.runtime.resolveDouble(settings.deccelRate ?: globalJet.deccelRate!!)//0.005
val minSpeed = topSpeed * vehicle.runtime.resolveDouble(settings.minSpeedFactor ?: globalJet.minSpeedFactor!!)
val speed = state.rideVelocity.get().z
val boostMult = vehicle.runtime.resolveDouble(settings.jumpExpr ?: globalJet.jumpExpr!!)
val boostTopSpeed = topSpeed * boostMult
val boostAccel = accel * boostMult
//speed up and slow down based on input
if (driver.zza > 0.0 && speed < topSpeed && state.stamina.get() > 0.0f) {
if (state.stamina.get() == 0.0f) {
// Decelerate currently always a constant half of max acceleration.
state.rideVelocity.set(
Vec3(
state.rideVelocity.get().x,
state.rideVelocity.get().y,
max(state.rideVelocity.get().z - (deccel * 1.3), 0.3 * 0.5)
)
)
}
else if (state.boosting.get() && speed < boostTopSpeed) {
state.rideVelocity.set(
Vec3(
state.rideVelocity.get().x,
state.rideVelocity.get().y,
min(state.rideVelocity.get().z + (boostAccel), boostTopSpeed)
)
)
} else if ((speed < minSpeed) || (driver.zza > 0.0 && speed < topSpeed)) {
//modify acceleration to be slower when at closer speeds to top speed
val accelMod = max(-(RidingBehaviour.scaleToRange(speed, minSpeed, topSpeed)) + 1, 0.0)
state.rideVelocity.set(
@@ -159,27 +231,31 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
min(state.rideVelocity.get().z + (accel * accelMod), topSpeed)
)
)
} else if (driver.zza >= 0.0 && (state.stamina.get() == 0.0f)) {
} else if (driver.zza < 0.0 && speed > minSpeed) {
// Decelerate currently always a constant half of max acceleration.
state.rideVelocity.set(
Vec3(
state.rideVelocity.get().x,
state.rideVelocity.get().y,
max(state.rideVelocity.get().z - ((accel) / 4), minSpeed)
max(state.rideVelocity.get().z - (deccel), minSpeed)
)
)
} else if (driver.zza < 0.0 && speed > minSpeed) {
//modify deccel to be slower when at closer speeds to minimum speed
val deccelMod = max((RidingBehaviour.scaleToRange(speed, minSpeed, topSpeed) - 1).pow(2) * 4, 0.1)
//Decelerate currently always a constant half of max acceleration.
} else if (speed > topSpeed) {
state.rideVelocity.set(
Vec3(
state.rideVelocity.get().x,
state.rideVelocity.get().y,
max(state.rideVelocity.get().z - ((accel * deccelMod) / 2), minSpeed)
)
state.rideVelocity.get().scale(0.98)
)
}
/****************************************************************************
* Kill strafing velocities carried over from controller transition. Don't
* kill immediately to give some momentum to transitions.
***************************************************************************/
state.rideVelocity.set(Vec3(
lerp(state.rideVelocity.get().x, 0.0, 0.03),
lerp(state.rideVelocity.get().y, 0.0, 0.03),
state.rideVelocity.get().z
))
}
override fun angRollVel(
@@ -196,7 +272,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
val cappedDeltaTime = min(deltaTime, 0.2)
//Get handling in degrees per second
val yawRotRate = vehicle.runtime.resolveDouble(settings.handlingYawExpr)
val yawRotRate = vehicle.runtime.resolveDouble(settings.handlingYawExpr ?: globalJet.handlingYawExpr!!)
//Base the change off of deltatime.
var handlingYaw = yawRotRate * (cappedDeltaTime)
@@ -276,11 +352,10 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
state.currMouseYForce.set((state.currMouseYForce.get() + (0.0015 * mouseY)).coerceIn(-1.0, 1.0))
//Get handling in degrees per second
var handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
val handlingDebuff = if(state.stamina.get() == 0.0f) 0.5 else 1.0
var handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalJet.handlingExpr!!) * handlingDebuff
//convert it to delta time
handling *= (cappedDeltaTime)
//apply stamina debuff if applicable
handling *= if (state.stamina.get() > 0.0) 1.0 else 0.5
var pitchRot = handling * state.currMouseYForce.get()
@@ -331,13 +406,12 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
state.currMouseYForce.set((state.currMouseYForce.get() + (0.0015 * mouseY)).coerceIn(-1.0, 1.0))
//Get handling in degrees per second
var handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
val handlingDebuff = if(state.stamina.get() == 0.0f) 0.5 else 1.0
var handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalJet.handlingExpr!!) * handlingDebuff
//convert it to delta time
handling *= (cappedDeltaTime)
//apply stamina debuff if applicable
handling *= if (state.stamina.get() > 0.0) 1.0 else 0.5
val poke = driver.vehicle as? PokemonEntity
@@ -409,15 +483,12 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
vehicle: PokemonEntity,
driver: Player
): Float {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val minSpeed = vehicle.runtime.resolveDouble(settings.minSpeed)
//Must I ensure that topspeed is greater than minimum?
val normalizedSpeed = RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), minSpeed, topSpeed)
//TODO: Determine if this should be based on max possible speed instead of top speed.
//Only ever want the fov change to be a max of 0.2 and for it to have non linear scaling.
return 1.0f + normalizedSpeed.pow(2).toFloat() * 0.2f
val maxDownwardForceTickCnt = 20.0f
return when {
state.noStamTickCnt.get() != 0 -> 1f - 0.2f*(state.noStamTickCnt.get() / maxDownwardForceTickCnt).coerceIn(0.0f,1.0f)
state.boosting.get() -> 1.2f
else -> 1.0f
}
}
override fun useAngVelSmoothing(settings: JetSettings, state: JetState, vehicle: PokemonEntity): Boolean {
@@ -490,60 +561,70 @@ class JetSettings : RidingBehaviourSettings {
override val key = JetBehaviour.KEY
override val stats = mutableMapOf<RidingStat, IntRange>()
var gravity: Expression = "0".asExpression()
var gravity: Expression? = null
private set
var minSpeed: Expression = "1.2".asExpression()
var deccelRate: Expression? = null
private set
var handlingYawExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 50.0, 25.0)".asExpression()
// Mult to top speed in order to derive minSpeed
var minSpeedFactor: Expression? = null
private set
var handlingYawExpr: Expression? = null
private set
// Make configurable by json
var infiniteStamina: Expression = "false".asExpression()
var infiniteStamina: Expression? = null
private set
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 300.0, 128.0)".asExpression()
// Boost power. Mult for top speed and accel while boosting
var jumpExpr: Expression? = null
private set
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 140.0, 20.0)".asExpression()
// Turn rate in degrees per second
var handlingExpr: Expression? = null
private set
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 1.8, 1.0)".asExpression()
// Top Speed in blocks per second
var speedExpr: Expression? = null
private set
var accelerationExpr: Expression =
"q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 1.0)), (1.0 / (20.0 * 5.0)))".asExpression()
// Seconds to get to top speed
var accelerationExpr: Expression? = null
private set
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 60.0, 10.0)".asExpression()
// Time in seconds to drain full bar of stamina flying
var staminaExpr: Expression? = null
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
buffer.writeRidingStats(stats)
rideSounds.encode(buffer)
buffer.writeExpression(gravity)
buffer.writeExpression(minSpeed)
buffer.writeExpression(handlingYawExpr)
buffer.writeExpression(infiniteStamina)
buffer.writeExpression(jumpExpr)
buffer.writeExpression(handlingExpr)
buffer.writeExpression(speedExpr)
buffer.writeExpression(accelerationExpr)
buffer.writeExpression(staminaExpr)
buffer.writeNullableExpression(gravity)
buffer.writeNullableExpression(deccelRate)
buffer.writeNullableExpression(minSpeedFactor)
buffer.writeNullableExpression(handlingYawExpr)
buffer.writeNullableExpression(infiniteStamina)
buffer.writeNullableExpression(jumpExpr)
buffer.writeNullableExpression(handlingExpr)
buffer.writeNullableExpression(speedExpr)
buffer.writeNullableExpression(accelerationExpr)
buffer.writeNullableExpression(staminaExpr)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
stats.putAll(buffer.readRidingStats())
rideSounds = RideSoundSettingsList.decode(buffer)
gravity = buffer.readExpression()
minSpeed = buffer.readExpression()
handlingYawExpr = buffer.readExpression()
infiniteStamina = buffer.readExpression()
jumpExpr = buffer.readExpression()
handlingExpr = buffer.readExpression()
speedExpr = buffer.readExpression()
accelerationExpr = buffer.readExpression()
staminaExpr = buffer.readExpression()
gravity = buffer.readNullableExpression()
deccelRate = buffer.readNullableExpression()
minSpeedFactor = buffer.readNullableExpression()
handlingYawExpr = buffer.readNullableExpression()
infiniteStamina = buffer.readNullableExpression()
jumpExpr = buffer.readNullableExpression()
handlingExpr = buffer.readNullableExpression()
speedExpr = buffer.readNullableExpression()
accelerationExpr = buffer.readNullableExpression()
staminaExpr = buffer.readNullableExpression()
}
}
@@ -551,16 +632,26 @@ class JetState : RidingBehaviourState() {
var currSpeed = ridingState(0.0, Side.CLIENT)
var currMouseXForce = ridingState(0.0, Side.CLIENT)
var currMouseYForce = ridingState(0.0, Side.CLIENT)
var boosting = ridingState(false, Side.BOTH)
var boostIsToggleable = ridingState(false, Side.BOTH)
var canSpeedBurst = ridingState(false, Side.BOTH)
var noStamTickCnt = ridingState(0, Side.CLIENT) // Value that is increased for every tick you are out of stamina
override fun encode(buffer: FriendlyByteBuf) {
super.encode(buffer)
buffer.writeDouble(currSpeed.get())
buffer.writeFloat(stamina.get())
buffer.writeBoolean(boosting.get())
buffer.writeBoolean(boostIsToggleable.get())
buffer.writeBoolean(canSpeedBurst.get())
}
override fun decode(buffer: FriendlyByteBuf) {
super.decode(buffer)
currSpeed.set(buffer.readDouble(), forced = true)
boosting.set(buffer.readBoolean(), forced = true)
boostIsToggleable.set(buffer.readBoolean(), forced = true)
canSpeedBurst.set(buffer.readBoolean(), forced = true)
}
override fun reset() {
@@ -568,12 +659,19 @@ class JetState : RidingBehaviourState() {
currSpeed.set(0.0, forced = true)
currMouseXForce.set(0.0, forced = true)
currMouseYForce.set(0.0, forced = true)
boosting.set(false, forced = true)
boostIsToggleable.set(false, forced = true)
canSpeedBurst.set(true, forced = true)
noStamTickCnt.set(0, forced = true)
}
override fun copy() = JetState().also {
it.currSpeed.set(currSpeed.get(), forced = true)
it.stamina.set(stamina.get(), forced = true)
it.rideVelocity.set(rideVelocity.get(), forced = true)
it.boosting.set(this.boosting.get(), forced = true)
it.boostIsToggleable.set(this.boosting.get(), forced = true)
it.canSpeedBurst.set(this.canSpeedBurst.get(), forced = true)
}
override fun shouldSync(previous: RidingBehaviourState): Boolean {
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.air
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangMath.lerp
import com.cobblemon.mod.common.CobblemonRideSettings
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.posing.PoseOption
@@ -40,6 +41,8 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
}
override val key = KEY
val globalRocket: RocketSettings
get() = CobblemonRideSettings.rocket
override fun getRidingStyle(settings: RocketSettings, state: RocketState): RidingStyle {
return RidingStyle.AIR
@@ -84,30 +87,59 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
driver: Player
): Float {
// Use this as a "tick" function and check to see if the driver is "boosting"
if(vehicle.level().isClientSide) {
if (vehicle.level().isClientSide) {
handleBoosting(settings, state, vehicle, driver)
tickStamina(settings, state, vehicle, driver)
}
//If the forward key is not held then it cannot be boosting
if(Minecraft.getInstance().options.keyUp.isDown()) {
val boostKeyPressed = Minecraft.getInstance().options.keySprint.isDown()
return state.rideVelocity.get().length().toFloat()
}
fun handleBoosting(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
driver: Player
) {
//If the forward key is not held then it cannot be boosting
if(Minecraft.getInstance().options.keyUp.isDown() && state.stamina.get() != 0.0f) {
val boostKeyPressed = Minecraft.getInstance().options.keySprint.isDown()
//If on the previous tick the boost key was held then don't change if the ride is boosting
if(state.boostIsToggleable.get() && boostKeyPressed) {
//flip the boosting state if boost key is pressed
state.boosting.set(!state.boosting.get())
}
//If the boost key is not held then next tick boosting is toggleable
state.boostIsToggleable.set(!boostKeyPressed)
} else {
//Turn off boost and reset boost params
state.boostIsToggleable.set(true)
state.boosting.set(false)
state.canSpeedBurst.set(true)
}
} else {
//Turn off boost and reset boost params
state.boostIsToggleable.set(true)
state.boosting.set(false)
state.canSpeedBurst.set(true)
}
}
fun tickStamina(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
driver: Player
) {
val stam = state.stamina.get()
if (vehicle.runtime.resolveBoolean(settings.infiniteStamina ?: globalRocket.infiniteStamina!!)) {
return
}
return state.rideVelocity.get().length().toFloat()
// Grab the boost time in seconds and convert to ticks. Then calculate the drain rate as inversely
// proportional to the number of ticks of boost thus making a full boost take x ticks
// in short: "Stamina drains slower at higher values and also replenishes slower"
val boostTime = vehicle.runtime.resolveDouble(settings.staminaExpr ?: globalRocket.staminaExpr!!) * 20.0f
val stamDrainRate = (1.0f / boostTime).toFloat()
val newStam = if(state.boosting.get() || driver.jumping) max(0.0f,stam - stamDrainRate) else stam
state.stamina.set(newStam)
}
override fun updatePassengerRotation(
@@ -153,22 +185,23 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
): Vec2 {
var newMomentum = state.turnMomentum.get().toDouble()
// Grab turningAcceleration and divide 20 twice to get
val turningAcceleration = (vehicle.runtime.resolveDouble(settings.handlingExpr) / 20.0f) / 20.0f * 2
// Degrees per tick
val maxTurnMomentum = vehicle.runtime.resolveDouble(settings.maxTurnRate ?: globalRocket.maxTurnRate!!) / 20.0f
// Turn rate increase per tick. Based off number of seconds to get to max turn rate
val turningAcceleration = maxTurnMomentum / (vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalRocket.handlingExpr!!) * 20.0f)
val turnInput = (driver.xxa *-1.0f) * turningAcceleration
val maxTurnMomentum = 80.0f / 20.0f
// Base boost stats off of normal turning stats
val boostMaxTurnMomentum = maxTurnMomentum * 0.1f
val boostTurnInput = turnInput * 0.15f
val boostHandlingMod = vehicle.runtime.resolveDouble(settings.boostHandlingMod ?: globalRocket.boostHandlingMod!!)
val boostMaxTurnMomentum = maxTurnMomentum * boostHandlingMod
val boostTurnInput = turnInput * boostHandlingMod
if(state.boosting.get()) {
if(driver.xxa != 0.0f && abs(newMomentum + turnInput) < (boostMaxTurnMomentum)) { //If max momentum will not be exceeded then modulate
newMomentum += boostTurnInput
} else {
newMomentum = lerp(newMomentum, 0.0, 0.05)
newMomentum = newMomentum.coerceIn(-boostMaxTurnMomentum, boostMaxTurnMomentum)
}
} else {
if(driver.xxa == 0.0f) { //If no turning input then lerp to 0
@@ -204,8 +237,9 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
vehicle: PokemonEntity,
driver: Player
): Vec3 {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) * 2
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr) * 0.5 * 2
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalRocket.speedExpr!!)
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalRocket.accelerationExpr!!)
val jump = vehicle.runtime.resolveDouble(settings.jumpExpr ?: globalRocket.jumpExpr!!)
var newVelocity = vehicle.deltaMovement
@@ -213,8 +247,10 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
val yawAligned = Matrix3f().rotateY(vehicle.yRot.toRadians())
newVelocity = (newVelocity.toVector3f().mul(yawAligned)).toVec3d()
//speed up and slow down based on input
if (driver.zza != 0.0f && state.stamina.get() > 0.0 && !state.boosting.get()) {
/******************************************************
* Speed up and slow down based on input
*****************************************************/
if (driver.zza != 0.0f && !state.boosting.get()) {
//make sure it can't exceed top speed
val forwardInput = when {
vehicle.deltaMovement.horizontalDistance() > topSpeed && (driver.zza.sign == newVelocity.z.sign.toFloat()) -> 0.0
@@ -226,14 +262,16 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
newVelocity.y,
(newVelocity.z + (accel * forwardInput.toDouble())))
} else if ((state.stamina.get() > 0.0 && state.boosting.get())) {
val boostSpeed = topSpeed * 5
val boostAccel = if(newVelocity.length() < boostSpeed) accel * 3 else 0.0
} else if ((state.boosting.get())) {
val boostMod = vehicle.runtime.resolveDouble(settings.boostSpeedMod ?: globalRocket.boostSpeedMod!!)
val boostSpeed = topSpeed * boostMod
val boostAccel = if(newVelocity.length() < boostSpeed) accel * (boostMod/1.5) else 0.0
val forwardInput = 1.0f
var burst = 0.0f
if(state.canSpeedBurst.get()) {
burst = 0.25f
burst = 0.5f
state.stamina.set(max(0.0f,state.stamina.get() - 0.05f))
state.canSpeedBurst.set(false)
}
@@ -244,12 +282,13 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
}
//Vertical movement based on driver input.
val maxVertSpeed = 0.5f
val maxVertSpeed = jump
val boostMaxVertSpeed = 0.5f * 0.25f
var vertInput = 0.0
//If boosting then don't lose altitude
//otherwise propel up or fall
if (state.boosting.get() && !driver.jumping && vehicle.deltaMovement.y > (-maxVertSpeed * 0.25)) {
if (state.boosting.get() && !driver.jumping && vehicle.deltaMovement.y > (-boostMaxVertSpeed)) {
vertInput = -0.7
newVelocity = Vec3(
newVelocity.x,
@@ -257,28 +296,33 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
newVelocity.z)
} else if(state.boosting.get() && vehicle.deltaMovement.y < maxVertSpeed) {
vertInput = when {
driver.jumping -> 2.0
driver.jumping -> 1.0
else -> 0.0
}
newVelocity = Vec3(
newVelocity.x,
(newVelocity.y + (accel * 0.5 * vertInput)),
(newVelocity.y + (accel * vertInput)),
newVelocity.z)
} else {
if (driver.jumping && !(vehicle.deltaMovement.y > maxVertSpeed && (newVelocity.y.sign.toFloat() > 0.0))) {
vertInput = 1.0
// More force if traveling downwards to allow for quicker fall stops
vertInput = (if (state.stamina.get() == 0.0f) -1.0f else if(newVelocity.y < 0) jump.toFloat() * 3.0f else jump.toFloat()).toDouble()
// Reset falldistance if upward motion is detected
vehicle.resetFallDistance()
} else {
vertInput = -2.5
}
if (state.stamina.get() > 0.0) {
newVelocity = Vec3(
newVelocity.x,
(newVelocity.y + (accel * vertInput)),
newVelocity.z)
} else {
/******************************************************
* Gravity logic
*****************************************************/
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.6
val terminalVel = 2.0
val fallingForce = gravity
newVelocity = Vec3(newVelocity.x, max(newVelocity.y - fallingForce, -terminalVel), newVelocity.z)
}
}
@@ -361,11 +405,9 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
driver: Player
): Float {
if (state.boosting.get()) {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
//val exceededSpeed = max(vehicle.deltaMovement.length() - topSpeed, 0.0)
//TODO: Remove this magic number and get a better comparison for boost top speed
val normalizedBoostSpeed = RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), topSpeed, topSpeed * 3)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalRocket.speedExpr!!)
val boostMod = vehicle.runtime.resolveDouble(settings.boostSpeedMod ?: globalRocket.boostSpeedMod!!)
val normalizedBoostSpeed = RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), topSpeed, topSpeed * boostMod)
return 1.0f + normalizedBoostSpeed.pow(2).toFloat() * 0.2f
} else {
return 1.0f
@@ -437,6 +479,18 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
return false
}
override fun damageOnCollision(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
impactVec: Vec3
): Boolean {
if (!state.boosting.get()) return false
val impactSpeed = impactVec.horizontalDistance().toFloat() * 10f
return vehicle.causeFallDamage(impactSpeed, 1f, vehicle.damageSources().flyIntoWall())
}
override fun getRideSounds(
settings: RocketSettings,
state: RocketState,
@@ -452,51 +506,67 @@ class RocketSettings : RidingBehaviourSettings {
override val key = RocketBehaviour.KEY
override val stats = mutableMapOf<RidingStat, IntRange>()
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 0.65, 0.3) * 0.25".asExpression()
var infiniteStamina: Expression? = null
private set
// Boost multiplier for speed
var boostSpeedMod: Expression? = null
private set
// Boost multiplier for handling
var boostHandlingMod: Expression? = null
private set
// Max turn rate in degrees per second
var maxTurnRate: Expression? = null
private set
var speedExpr: Expression? = null
private set
// Max accel is a whole 1.0 in 1 second. The conversion in the function below is to convert seconds to ticks
var accelerationExpr: Expression =
"q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 1.5)), (1.0 / (20.0 * 3.0))) * 0.5".asExpression()
var accelerationExpr: Expression? = null
private set
// Between 30 seconds and 10 seconds at the lowest when at full speed.
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 30.0, 10.0)".asExpression()
// air time in seconds
var staminaExpr: Expression? = null
private set
//Between a one block jump and a ten block jump
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 10.0, 1.0)".asExpression()
var jumpExpr: Expression? = null
private set
// Controls the acceleration of the turning speed since the max turning speed is locked at 60 degrees/second
// So this is a range of 60 degrees/second per second to 20. Meaning at max stats when holding a direction it
// takes a full second to be turning at max speed.
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 60.0, 20.0)".asExpression()
// How long it takes to get to max turn rate from rest (in seconds)
var handlingExpr: Expression? = null
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
buffer.writeRidingStats(stats)
rideSounds.encode(buffer)
buffer.writeExpression(speedExpr)
buffer.writeExpression(accelerationExpr)
buffer.writeExpression(staminaExpr)
buffer.writeExpression(jumpExpr)
buffer.writeExpression(handlingExpr)
buffer.writeNullableExpression(infiniteStamina)
buffer.writeNullableExpression(boostSpeedMod)
buffer.writeNullableExpression(boostHandlingMod)
buffer.writeNullableExpression(maxTurnRate)
buffer.writeNullableExpression(speedExpr)
buffer.writeNullableExpression(accelerationExpr)
buffer.writeNullableExpression(staminaExpr)
buffer.writeNullableExpression(jumpExpr)
buffer.writeNullableExpression(handlingExpr)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
stats.putAll(buffer.readRidingStats())
rideSounds = RideSoundSettingsList.decode(buffer)
speedExpr = buffer.readExpression()
accelerationExpr = buffer.readExpression()
staminaExpr = buffer.readExpression()
jumpExpr = buffer.readExpression()
handlingExpr = buffer.readExpression()
infiniteStamina = buffer.readNullableExpression()
boostSpeedMod = buffer.readNullableExpression()
boostHandlingMod = buffer.readNullableExpression()
maxTurnRate = buffer.readNullableExpression()
speedExpr = buffer.readNullableExpression()
accelerationExpr = buffer.readNullableExpression()
staminaExpr = buffer.readNullableExpression()
jumpExpr = buffer.readNullableExpression()
handlingExpr = buffer.readNullableExpression()
}
}
class RocketState : RidingBehaviourState() {
@@ -387,9 +387,10 @@ open class CompositeSettings : RidingBehaviourSettings {
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeRidingStats(stats)
buffer.writeResourceLocation(key)
buffer.writeResourceLocation(transitionStrategy)
buffer.writeResourceLocation(defaultBehaviour.key)
defaultBehaviour.encode(buffer)
buffer.writeResourceLocation(alternateBehaviour.key)
alternateBehaviour.encode(buffer)
}
@@ -41,6 +41,10 @@ interface CompositeRidingStrategy<T : CompositeSettings> {
toState: RidingBehaviourState,
toSettings: RidingBehaviourSettings
) {
if (vehicle.level().isClientSide) {
val fromSettings = if (settings.defaultBehaviour == toSettings) settings.alternateBehaviour else settings.defaultBehaviour
}
toState.stamina.set(fromState.stamina.get())
toState.rideVelocity.set(fromState.rideVelocity.get())
fromState.reset()
@@ -9,15 +9,34 @@
package com.cobblemon.mod.common.api.riding.behaviour.types.land
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.CobblemonRideSettings
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
import com.cobblemon.mod.common.api.riding.behaviour.Side
import com.cobblemon.mod.common.api.riding.behaviour.ridingState
import com.cobblemon.mod.common.api.riding.posing.PoseOption
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.readNullableExpression
import com.cobblemon.mod.common.util.readRidingStats
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.resolveDouble
import com.cobblemon.mod.common.util.resolveFloat
import com.cobblemon.mod.common.util.writeNullableExpression
import com.cobblemon.mod.common.util.writeRidingStats
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import kotlin.math.pow
import kotlin.math.sign
import kotlin.math.sqrt
import net.minecraft.core.Direction
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
@@ -30,7 +49,6 @@ import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
import kotlin.math.*
class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
companion object {
@@ -38,6 +56,8 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
}
override val key = KEY
val globalHorse: HorseSettings
get() = CobblemonRideSettings.horse
override fun getRidingStyle(settings: HorseSettings, state: HorseState): RidingStyle {
return RidingStyle.LAND
@@ -81,13 +101,21 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
vehicle: PokemonEntity,
driver: Player
): Float {
// Use this as a "tick" function and calculate sprinting and inAir state here
handleSprinting(settings, state, vehicle, driver)
inAirCheck(state, vehicle)
tickStamina(settings, state, vehicle, driver)
return state.rideVelocity.get().length().toFloat()
}
override fun tick(
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity,
driver: Player,
input: Vec3
) {
handleSprinting(state, driver)
inAirCheck(state, vehicle)
tickStamina(settings, state, vehicle)
}
fun inAirCheck(
state: HorseState,
vehicle: PokemonEntity,
@@ -100,16 +128,13 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
val canSupportEntity = blockStateBelow.isFaceSturdy(vehicle.level(), posBelow, Direction.UP)
val standingOnSolid = canSupportEntity && !isAirOrLiquid
// inAir if not on the ground
val inAir = !(vehicle.deltaMovement.y == 0.0 || standingOnSolid)
state.inAir.set(inAir)
}
fun handleSprinting(
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity,
driver: Player
) {
if (state.sprinting.get()) {
@@ -125,11 +150,15 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity,
driver: Player
) {
val stam = state.stamina.get()
if (vehicle.runtime.resolveBoolean(settings.infiniteStamina ?: globalHorse.infiniteStamina!!)) {
return
}
var newStam = stam
val stamDrainRate = (1.0f / vehicle.runtime.resolveDouble(settings.staminaExpr)).toFloat() / 20.0f
val stamDrainRate = (1.0f / vehicle.runtime.resolveDouble(settings.staminaExpr ?: globalHorse.staminaExpr!!)).toFloat() / 20.0f
if (state.sprinting.get()) {
newStam = max(0.0f,stam - stamDrainRate)
@@ -179,10 +208,10 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
vehicle: PokemonEntity,
driver: LivingEntity
): Float {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalHorse.speedExpr!!)
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalHorse.handlingExpr!!)
val walkHandlingBoost = 5
val maxYawDiff = vehicle.runtime.resolveFloat(settings.lookYawLimit)
val maxYawDiff = vehicle.runtime.resolveFloat(settings.lookYawLimit ?: globalHorse.lookYawLimit!!)
// Normalize the current rotation diff
val rotDiff = Mth.wrapDegrees(driver.yRot - vehicle.yRot).coerceIn(-maxYawDiff,maxYawDiff)
@@ -226,24 +255,37 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
vehicle: PokemonEntity,
driver: Player
): Vec3 {
val canSprint = vehicle.runtime.resolveBoolean(settings.canSprint)
val canJump = vehicle.runtime.resolveBoolean(settings.canJump)
val jumpForce = vehicle.runtime.resolveDouble(settings.jumpExpr)
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val canSprint = vehicle.runtime.resolveBoolean(settings.canSprint ?: globalHorse.canSprint!!)
val canJump = vehicle.runtime.resolveBoolean(settings.canJump ?: globalHorse.canJump!!)
val jumpForce = vehicle.runtime.resolveDouble(settings.jumpExpr ?: globalHorse.jumpExpr!!) * 0.75
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalHorse.speedExpr!!)
val walkSpeed = getWalkSpeed(vehicle)
val topSpeed = if(canSprint && state.sprinting.get()) rideTopSpeed else walkSpeed
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
val accel = topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalHorse.accelerationExpr!!) * 20.0)
var activeInput = false
/******************************************************
* Gather the previous velocity and set the horizontal
* fields of the velocity vector to the magnitude of the
* ones from deltaMovement. This is important as when a
* collision happens the Deltamovement will reflect it
* but rideVelocity won't.
* Gather the previous velocity and check for horizontal
* collisions
*****************************************************/
var newVelocity = Vec3(state.rideVelocity.get().x, 0.0, state.rideVelocity.get().z).normalize().scale(vehicle.deltaMovement.horizontalDistance())
newVelocity = newVelocity.add(0.0, state.rideVelocity.get().y, 0.0)
val dmSpeed = vehicle.deltaMovement.length()
val rvSpeed = state.rideVelocity.get().length()
var newVelocity = state.rideVelocity.get() //.normalize().scale(vehicle.deltaMovement.horizontalDistance())
/******************************************************
* Gather the previous velocity and check for horizontal
* collisions
*****************************************************/
// if (dmSpeed > rvSpeed) {
// // align the velocity vector to be in local vehicle space
// newVelocity = vehicle.deltaMovement
// val yawAligned = Matrix3f().rotateY(-vehicle.yRot.toRadians())
// newVelocity = (newVelocity.toVector3f().mul(yawAligned)).toVec3d()
// }
if (vehicle.horizontalCollision) {
newVelocity = newVelocity.normalize().scale(vehicle.deltaMovement.length())
}
/******************************************************
* Speed up and slow down based on input
@@ -252,7 +294,7 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
// If on a tight turn then do not speed up past half of top speed in order to turn quicker
// Also determine how fast to be slowing down based on how far turned you are
val lookYawLimit = vehicle.runtime.resolveFloat(settings.lookYawLimit)
val lookYawLimit = vehicle.runtime.resolveFloat(settings.lookYawLimit ?: globalHorse.lookYawLimit!!)
val percOfMaxTurnSpeed = abs(Mth.wrapDegrees(driver.yRot - vehicle.yRot) / lookYawLimit) * 100.0f
val turnPercThresh = 0.0f
val s = min(((percOfMaxTurnSpeed - turnPercThresh) / (100.0f - turnPercThresh)).pow(1),1.0f)
@@ -282,12 +324,14 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
/******************************************************
* Gravity logic
*****************************************************/
if (vehicle.onGround()) { newVelocity = Vec3(newVelocity.x, 0.0, newVelocity.z) }
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.6
val terminalVel = 2.0
if (!vehicle.onGround() && state.jumpTicks.get() <= 0) {
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.6
val terminalVel = 2.0
newVelocity = Vec3(newVelocity.x, max(newVelocity.y - gravity, -terminalVel), newVelocity.z)
} else if(vehicle.onGround()) {
newVelocity = Vec3(newVelocity.x, 0.0, newVelocity.z)
}
val fallingForce = gravity
newVelocity = Vec3(newVelocity.x, max(newVelocity.y - fallingForce, -terminalVel), newVelocity.z)
/******************************************************
* Ground Friction
@@ -299,8 +343,21 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
/******************************************************
* Jump Logic
*****************************************************/
if (driver.jumping && vehicle.onGround() && canJump && driver.deltaMovement.y <= 0.1) {
newVelocity = Vec3(newVelocity.x, jumpForce, newVelocity.z)
if (state.jumpTicks.get() > 0 || (state.jumpTicks.get() >= 0 && driver.jumping && vehicle.onGround() && canJump && driver.deltaMovement.y <= 0.1)) {
// Spread out jumpforce so that variable height jumps are possible
val jumpInputTicks = 6
if (driver.jumping && (state.jumpTicks.get() >= 0 && state.jumpTicks.get() < jumpInputTicks)) {
val appliedJumpForce = ((jumpForce*1.5) / jumpInputTicks) //* (1 - state.jumpTicks.get() / jumpInputTicks)
newVelocity = Vec3(newVelocity.x, newVelocity.y + appliedJumpForce, newVelocity.z)
state.jumpTicks.set(state.jumpTicks.get() + 1)
} else {
// Set delay before next jump is possible after hitting ground
val tickJumpDelay = 3
state.jumpTicks.set(-tickJumpDelay)
}
} else if (vehicle.onGround() && state.jumpTicks.get() < 0) {
// Tick off the delay once reaching the ground
state.jumpTicks.set(state.jumpTicks.get() + 1)
}
//Zero out lateral velocity possibly picked up from a controller transition
@@ -479,78 +536,94 @@ class HorseSettings : RidingBehaviourSettings {
override val key = HorseBehaviour.KEY
override val stats = mutableMapOf<RidingStat, IntRange>()
var canJump = "true".asExpression()
var infiniteStamina: Expression? = null
private set
var canJump: Expression? = null
private set
var canSprint = "true".asExpression()
var canSprint: Expression? = null
private set
var lookYawLimit = "90.0f".asExpression()
var lookYawLimit: Expression? = null
private set
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'LAND', 1.0, 0.4)".asExpression()
var speedExpr: Expression? = null
private set
// Max accel is a whole 1.0 in 1 second. The conversion in the function below is to convert seconds to ticks
var accelerationExpr: Expression =
"q.get_ride_stats('ACCELERATION', 'LAND', (1.0 / (20.0 * 0.8)), (1.0 / (20.0 * 3.0)))".asExpression()
var accelerationExpr: Expression? = null
private set
// Between 30 seconds and 10 seconds at the lowest when at full speed.
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'LAND', 30.0, 10.0)".asExpression()
var staminaExpr: Expression? = null
private set
//Between a one block jump and a ten block jump
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'LAND', 1.5, 0.8)".asExpression()
//Between a one block jump and a six block jump
var jumpExpr: Expression? = null
private set
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'LAND', 180.0, 40.0)".asExpression()
var handlingExpr: Expression? = null
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
buffer.writeRidingStats(stats)
rideSounds.encode(buffer)
buffer.writeExpression(speedExpr)
buffer.writeExpression(accelerationExpr)
buffer.writeExpression(staminaExpr)
buffer.writeExpression(jumpExpr)
buffer.writeExpression(handlingExpr)
buffer.writeNullableExpression(infiniteStamina)
buffer.writeNullableExpression(canJump)
buffer.writeNullableExpression(canSprint)
buffer.writeNullableExpression(lookYawLimit)
buffer.writeNullableExpression(speedExpr)
buffer.writeNullableExpression(accelerationExpr)
buffer.writeNullableExpression(staminaExpr)
buffer.writeNullableExpression(jumpExpr)
buffer.writeNullableExpression(handlingExpr)
buffer.writeNullableExpression(canJump)
buffer.writeNullableExpression(canSprint)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
stats.putAll(buffer.readRidingStats())
rideSounds = RideSoundSettingsList.decode(buffer)
speedExpr = buffer.readExpression()
accelerationExpr = buffer.readExpression()
staminaExpr = buffer.readExpression()
jumpExpr = buffer.readExpression()
handlingExpr = buffer.readExpression()
infiniteStamina = buffer.readNullableExpression()
canJump = buffer.readNullableExpression()
canSprint = buffer.readNullableExpression()
lookYawLimit = buffer.readNullableExpression()
speedExpr = buffer.readNullableExpression()
accelerationExpr = buffer.readNullableExpression()
staminaExpr = buffer.readNullableExpression()
jumpExpr = buffer.readNullableExpression()
handlingExpr = buffer.readNullableExpression()
canJump = buffer.readNullableExpression()
canSprint = buffer.readNullableExpression()
}
}
class HorseState : RidingBehaviourState() {
var sprinting = ridingState(false, Side.CLIENT)
var inAir = ridingState(false, Side.CLIENT)
var jumpTicks = ridingState(0, Side.CLIENT)
override fun encode(buffer: FriendlyByteBuf) {
super.encode(buffer)
buffer.writeBoolean(sprinting.get())
buffer.writeBoolean(inAir.get())
buffer.writeInt(jumpTicks.get())
}
override fun decode(buffer: FriendlyByteBuf) {
super.decode(buffer)
sprinting.set(buffer.readBoolean(), forced = true)
inAir.set(buffer.readBoolean(), forced = true)
jumpTicks.set(buffer.readInt(), forced = true)
}
override fun reset() {
super.reset()
sprinting.set(false, forced = true)
inAir.set(false, forced = true)
jumpTicks.set(0, forced = true)
}
override fun copy() = HorseState().also {
@@ -558,6 +631,7 @@ class HorseState : RidingBehaviourState() {
it.stamina.set(this.stamina.get(), forced = true)
it.sprinting.set(this.sprinting.get(), forced = true)
it.inAir.set(this.inAir.get(), forced = true)
it.jumpTicks.set(this.jumpTicks.get(), forced = true)
}
override fun shouldSync(previous: RidingBehaviourState): Boolean {
@@ -10,18 +10,36 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.land
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangMath.lerp
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonRideSettings
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
import com.cobblemon.mod.common.api.riding.behaviour.Side
import com.cobblemon.mod.common.api.riding.behaviour.SidedRidingState
import com.cobblemon.mod.common.api.riding.behaviour.ridingState
import com.cobblemon.mod.common.api.riding.posing.PoseOption
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.math.geometry.toRadians
import com.cobblemon.mod.common.util.readNullableExpression
import com.cobblemon.mod.common.util.readRidingStats
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.resolveDouble
import com.cobblemon.mod.common.util.resolveFloat
import com.cobblemon.mod.common.util.writeNullableExpression
import com.cobblemon.mod.common.util.writeRidingStats
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import kotlin.math.pow
import kotlin.math.sign
import net.minecraft.core.Direction
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
@@ -32,12 +50,6 @@ import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
import org.joml.Matrix3f
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import kotlin.math.pow
import kotlin.math.sign
class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
companion object {
@@ -45,6 +57,8 @@ class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
}
override val key = KEY
val globalMinekart: MinekartSettings
get() = CobblemonRideSettings.minekart
override fun getRidingStyle(settings: MinekartSettings, state: MinekartState): RidingStyle {
return RidingStyle.LAND
@@ -84,7 +98,7 @@ class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
driver: Player
): Float {
val boostMax = vehicle.runtime.resolveFloat(settings.boostLimit)
val boostMax = vehicle.runtime.resolveFloat(settings.boostLimit ?: globalMinekart.boostLimit!!)
//TODO: should this be by how much turn has accumulated or simply time turning... or both?
val boostPerSecond = 20.0f
val boostLoss = 40.0f
@@ -142,8 +156,8 @@ class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
var newMomentum = state.turnMomentum.get().toDouble()
//TODO: maybe tie this to max turnspeeds rather than the turn acceleration
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val turningAcceleration = (vehicle.runtime.resolveDouble(settings.handlingExpr) * 1.5 / 20.0f) / 20.0f * 4.0
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalMinekart.speedExpr!!)
val turningAcceleration = (vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalMinekart.handlingExpr!!) * 1.5 / 20.0f) / 20.0f * 4.0
val turnInput = (driver.xxa *-1.0f) * turningAcceleration
val driftInput = turnInput * 0.25f
@@ -202,9 +216,9 @@ class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
): Vec3 {
// Check to see if the ride should be walking or sprinting
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) * 0.6 * 2.0
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalMinekart.speedExpr!!) * 0.6 * 2.0
val topSpeed = if (state.drifting.get()) rideTopSpeed * 0.7 else rideTopSpeed
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr) * 0.3 * 3 * 2.0
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalMinekart.accelerationExpr!!) * 0.3 * 3 * 2.0
val driftAngle = 20.0f
//Align the direction of this vector with the vehicles current heading.
@@ -327,7 +341,7 @@ class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
vehicle: PokemonEntity,
driver: Player
): Boolean {
return vehicle.runtime.resolveBoolean(settings.canJump)
return vehicle.runtime.resolveBoolean(settings.canJump ?: globalMinekart.canJump!!)
}
override fun setRideBar(
@@ -447,45 +461,57 @@ class MinekartSettings : RidingBehaviourSettings {
override val key = MinekartBehaviour.KEY
override val stats = mutableMapOf<RidingStat, IntRange>()
var canJump = "true".asExpression()
var infiniteStamina: Expression? = null
private set
var canJump: Expression? = null
private set
var boostLimit = "100.0f".asExpression()
var boostLimit: Expression? = null
private set
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'LAND', 1.0, 0.3)".asExpression()
var speedExpr: Expression? = null
private set
// Max accel is a whole 1.0 in 1 second. The conversion in the function below is to convert seconds to ticks
var accelerationExpr: Expression =
"q.get_ride_stats('ACCELERATION', 'LAND', (1.0 / (20.0 * 1.5)), (1.0 / (20.0 * 5.0)))".asExpression()
var accelerationExpr: Expression? = null
private set
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'LAND', 30.0, 10.0)".asExpression()
var staminaExpr: Expression? = null
private set
//Between a one block jump and a ten block jump
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'LAND', 10.0, 1.0)".asExpression()
var jumpExpr: Expression? = null
private set
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'LAND', 140.0, 20.0)".asExpression()
var handlingExpr: Expression? = null
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
buffer.writeRidingStats(stats)
rideSounds.encode(buffer)
buffer.writeExpression(canJump)
buffer.writeExpression(boostLimit)
buffer.writeNullableExpression(infiniteStamina)
buffer.writeNullableExpression(canJump)
buffer.writeNullableExpression(boostLimit)
buffer.writeNullableExpression(speedExpr)
buffer.writeNullableExpression(accelerationExpr)
buffer.writeNullableExpression(staminaExpr)
buffer.writeNullableExpression(jumpExpr)
buffer.writeNullableExpression(handlingExpr)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
stats.putAll(buffer.readRidingStats())
rideSounds = RideSoundSettingsList.decode(buffer)
canJump = buffer.readExpression()
boostLimit = buffer.readExpression()
infiniteStamina = buffer.readNullableExpression()
canJump = buffer.readNullableExpression()
boostLimit = buffer.readNullableExpression()
speedExpr = buffer.readNullableExpression()
accelerationExpr = buffer.readNullableExpression()
staminaExpr = buffer.readNullableExpression()
jumpExpr = buffer.readNullableExpression()
handlingExpr = buffer.readNullableExpression()
}
}
@@ -10,18 +10,35 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.land
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangMath.lerp
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonRideSettings
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
import com.cobblemon.mod.common.api.riding.behaviour.Side
import com.cobblemon.mod.common.api.riding.behaviour.SidedRidingState
import com.cobblemon.mod.common.api.riding.behaviour.ridingState
import com.cobblemon.mod.common.api.riding.posing.PoseOption
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.math.geometry.toRadians
import com.cobblemon.mod.common.util.readNullableExpression
import com.cobblemon.mod.common.util.readRidingStats
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.resolveDouble
import com.cobblemon.mod.common.util.toVec3d
import com.cobblemon.mod.common.util.writeNullableExpression
import com.cobblemon.mod.common.util.writeRidingStats
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import kotlin.math.sign
import net.minecraft.core.Direction
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
@@ -33,10 +50,6 @@ import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
import org.joml.Matrix3f
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import kotlin.math.sign
class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
companion object {
@@ -44,6 +57,8 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
}
override val key = KEY
val globalVehicle: VehicleSettings
get() = CobblemonRideSettings.vehicle
override fun getRidingStyle(settings: VehicleSettings, state: VehicleState): RidingStyle {
return RidingStyle.LAND
@@ -126,7 +141,7 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
var newMomentum = state.turnMomentum.get().toDouble()
// Grab turningAcceleration and divide 20 twice to get
val turningAcceleration = (vehicle.runtime.resolveDouble(settings.handlingExpr) * 1.5 / 20.0f) / 20.0f * 4.0
val turningAcceleration = (vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalVehicle.handlingExpr!!) * 1.5 / 20.0f) / 20.0f * 4.0
val turnInput = (driver.xxa *-1.0f) * turningAcceleration
val maxTurnMomentum = 80.0f / 20.0f
@@ -137,7 +152,7 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
if(state.drifting.get() || state.inAir.get()) {
if( newMomentum < driftMaxTurnMomentum) {
driftTurnInput = driftTurnInput * (vehicle.deltaMovement.horizontalDistance() / vehicle.runtime.resolveDouble(settings.speedExpr))
driftTurnInput = driftTurnInput * (vehicle.deltaMovement.horizontalDistance() / vehicle.runtime.resolveDouble(settings.speedExpr ?: globalVehicle.speedExpr!!))
newMomentum += driftTurnInput
}
newMomentum = lerp(newMomentum, 0.0, 0.03)
@@ -177,10 +192,10 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
// Check to see if the ride should be walking or sprinting
//val walkSpeed = getWalkSpeed(vehicle)
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) * 0.6 * 2.0
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalVehicle.speedExpr!!) * 0.6 * 2.0
val topSpeed = if (state.drifting.get()) rideTopSpeed * 0.5 else rideTopSpeed
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr) * 0.3 * 3 * 2.0
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalVehicle.accelerationExpr!!) * 0.3 * 3 * 2.0
//Flag for determining if player is actively inputting
var activeInput = false
@@ -250,7 +265,7 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
}
//TODO: Change this so its tied to a jumping stat and representative of the amount of jumps
val canJump = vehicle.runtime.resolveBoolean(settings.canJump)
val canJump = vehicle.runtime.resolveBoolean(settings.canJump ?: globalVehicle.canJump!!)
//Jump the thang!
if (driver.jumping && vehicle.onGround() && canJump) {
val jumpForce = 0.5
@@ -296,7 +311,7 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
vehicle: PokemonEntity,
driver: Player
): Boolean {
return vehicle.runtime.resolveBoolean(settings.canJump)
return vehicle.runtime.resolveBoolean(settings.canJump ?: globalVehicle.canJump!!)
}
override fun setRideBar(
@@ -412,66 +427,79 @@ class VehicleSettings : RidingBehaviourSettings {
override val key = VehicleBehaviour.KEY
override val stats = mutableMapOf<RidingStat, IntRange>()
var canJump = "true".asExpression()
var infiniteStamina: Expression? = null
private set
var speed = "0.3".asExpression()
var canJump: Expression? = null
private set
var driveFactor = "1.0".asExpression()
var speed: Expression? = null
private set
var reverseDriveFactor = "0.25".asExpression()
var driveFactor: Expression? = null
private set
var rotationSpeed = "45/20".asExpression()
var reverseDriveFactor: Expression? = null
private set
var lookYawLimit = "101".asExpression()
var rotationSpeed: Expression? = null
private set
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'LAND', 1.0, 0.3)".asExpression()
var lookYawLimit: Expression? = null
private set
var speedExpr: Expression? = null
private set
// Max accel is a whole 1.0 in 1 second. The conversion in the function below is to convert seconds to ticks
var accelerationExpr: Expression =
"q.get_ride_stats('ACCELERATION', 'LAND', (1.0 / (20.0 * 1.5)), (1.0 / (20.0 * 5.0)))".asExpression()
var accelerationExpr: Expression? = null
private set
// Between 30 seconds and 10 seconds at the lowest when at full speed.
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'LAND', 30.0, 10.0)".asExpression()
var staminaExpr: Expression? = null
private set
//Between a one block jump and a ten block jump
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'LAND', 10.0, 1.0)".asExpression()
var jumpExpr: Expression? = null
private set
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'LAND', 140.0, 20.0)".asExpression()
var handlingExpr: Expression? = null
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
buffer.writeRidingStats(stats)
rideSounds.encode(buffer)
buffer.writeExpression(canJump)
buffer.writeExpression(speed)
buffer.writeExpression(driveFactor)
buffer.writeExpression(reverseDriveFactor)
buffer.writeExpression(rotationSpeed)
buffer.writeExpression(lookYawLimit)
buffer.writeNullableExpression(infiniteStamina)
buffer.writeNullableExpression(canJump)
buffer.writeNullableExpression(speed)
buffer.writeNullableExpression(driveFactor)
buffer.writeNullableExpression(reverseDriveFactor)
buffer.writeNullableExpression(rotationSpeed)
buffer.writeNullableExpression(lookYawLimit)
buffer.writeNullableExpression(speedExpr)
buffer.writeNullableExpression(accelerationExpr)
buffer.writeNullableExpression(staminaExpr)
buffer.writeNullableExpression(jumpExpr)
buffer.writeNullableExpression(handlingExpr)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
stats.putAll(buffer.readRidingStats())
rideSounds = RideSoundSettingsList.decode(buffer)
canJump = buffer.readExpression()
speed = buffer.readExpression()
driveFactor = buffer.readExpression()
reverseDriveFactor = buffer.readExpression()
rotationSpeed = buffer.readExpression()
lookYawLimit = buffer.readExpression()
infiniteStamina = buffer.readNullableExpression()
canJump = buffer.readNullableExpression()
speed = buffer.readNullableExpression()
driveFactor = buffer.readNullableExpression()
reverseDriveFactor = buffer.readNullableExpression()
rotationSpeed = buffer.readNullableExpression()
lookYawLimit = buffer.readNullableExpression()
speedExpr = buffer.readNullableExpression()
accelerationExpr = buffer.readNullableExpression()
staminaExpr = buffer.readNullableExpression()
jumpExpr = buffer.readNullableExpression()
handlingExpr = buffer.readNullableExpression()
}
}
@@ -8,17 +8,34 @@
package com.cobblemon.mod.common.api.riding.behaviour.types.liquid
import com.cobblemon.mod.common.Cobblemon
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.CobblemonRideSettings
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
import com.cobblemon.mod.common.api.riding.behaviour.Side
import com.cobblemon.mod.common.api.riding.behaviour.ridingState
import com.cobblemon.mod.common.api.riding.posing.PoseOption
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.readNullableExpression
import com.cobblemon.mod.common.util.readRidingStats
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.resolveDouble
import com.cobblemon.mod.common.util.resolveFloat
import com.cobblemon.mod.common.util.writeNullableExpression
import com.cobblemon.mod.common.util.writeRidingStats
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import net.minecraft.core.BlockPos
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth
@@ -28,24 +45,24 @@ import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
companion object {
val KEY = cobblemonResource("liquid/boat")
val TOP_SPEED = 2.0
}
override val key = KEY
val globalBoat: BoatSettings
get() = CobblemonRideSettings.boat
override fun getRidingStyle(settings: BoatSettings, state: BoatState): RidingStyle {
return RidingStyle.LIQUID
}
val poseProvider = PoseProvider<BoatSettings, BoatState>(PoseType.FLOAT)
.with(PoseOption(PoseType.SWIM) { _, _, entity -> entity.isSwimming && entity.entityData.get(PokemonEntity.MOVING) })
val poseProvider = PoseProvider<BoatSettings, BoatState>(PoseType.STAND)
.with(PoseOption(PoseType.WALK) { _, state, _ ->
abs(state.rideVelocity.get().horizontalDistance()) > 0.2
})
override fun isActive(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity): Boolean {
if (state.jumpBuffer.get() != -1) {
@@ -76,14 +93,22 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
state.jumpBuffer.set(state.jumpBuffer.get() + 1)
}
if (driver.zza == 0f && (vehicle.isInWater || vehicle.isUnderWater)) {
// Only start sprinting on vehicle if the state has changed and the pokemon has atleast 1/3 stamina
if (driver.isSprinting && !state.isVehicleSprinting.get()) {
state.isVehicleSprinting.set(state.stamina.get() > 0.33f)
}
else if (!driver.isSprinting) {
state.isVehicleSprinting.set(false)
}
if (!state.isVehicleSprinting.get()) {
if (state.staminaBuffer.get() >= 20) {
val staminaIncrease = vehicle.getRideStat(RidingStat.STAMINA, RidingStyle.LIQUID, 1.0, 2.0)
val staminaIncrease = vehicle.runtime.resolveDouble(settings.staminaIncreaseExpr ?: globalBoat.staminaIncreaseExpr!!)
state.stamina.set(min(1.0f, state.stamina.get() + 0.01f * staminaIncrease.toFloat()))
}
}
else if (abs(state.rideVelocity.get().z) > 0.1) {
consumeStamina(vehicle, state, 0.01f)
else if (abs(state.rideVelocity.get().z) > 0.1 && state.isVehicleSprinting.get()) {
consumeStamina(vehicle, driver, settings, state, 0.01f)
}
state.staminaBuffer.set(state.staminaBuffer.get() + 1)
super.tick(settings, state, vehicle, driver, input)
@@ -114,67 +139,79 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
driver: Player,
input: Vec3
): Vec3 {
applyStrafeRotation(vehicle, driver, state)
applyStrafeRotation(vehicle, driver, settings, state)
var velocity = state.rideVelocity.get()
velocity = applyVelocityFromInput(velocity, vehicle, driver, state)
velocity = applyVelocityFromInput(velocity, vehicle, driver, settings, state)
velocity = applyGravity(velocity, vehicle, settings, state)
velocity = applyJump(velocity, vehicle, driver, state)
velocity = applyJump(velocity, vehicle, driver, settings, state)
state.rideVelocity.set(velocity)
return state.rideVelocity.get()
}
private fun applyVelocityFromInput(velocity: Vec3, vehicle: PokemonEntity, driver: Player, state: BoatState): Vec3 {
val speed = vehicle.getRideStat(RidingStat.SPEED, RidingStyle.LIQUID, 0.8, TOP_SPEED)
val acceleration = vehicle.getRideStat(RidingStat.ACCELERATION, RidingStyle.LIQUID, 0.05, 0.5)
private fun applyVelocityFromInput(velocity: Vec3, vehicle: PokemonEntity, driver: Player, settings: BoatSettings, state: BoatState): Vec3 {
val speed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalBoat.speedExpr!!)
val acceleration = speed / (vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalBoat.accelerationExpr!!) * 20)
if (state.jumpBuffer.get() != -1 || !(vehicle.isInWater || vehicle.isUnderWater)) {
return velocity
}
val sprintModifier = vehicle.runtime.resolveDouble(settings.sprintSpeedModifier ?: globalBoat.sprintSpeedModifier!!)
val activeSprintModifier = if (state.isVehicleSprinting.get() && hasStamina(state)) sprintModifier else 1.0
val forwardInput = driver.zza.toDouble()
val delta = when {
!hasStamina(state) || forwardInput == 0.0 -> -velocity.z * 0.04
forwardInput == 0.0 -> -velocity.z * 0.04
forwardInput < 0.0 -> when {
velocity.z < 0.0 -> forwardInput * acceleration * 0.5
else -> min(forwardInput * acceleration * 0.5, -velocity.z * 0.04) // We never want it to be slower to reverse than no input
}
else -> forwardInput * acceleration
else -> forwardInput * acceleration * activeSprintModifier
}
return Vec3(
velocity.x,
velocity.y,
Mth.clamp(velocity.z + delta, -speed * 0.25, speed)
Mth.clamp(velocity.z + delta, -speed * 0.25, speed * activeSprintModifier)
)
}
private fun applyJump(velocity: Vec3, vehicle: PokemonEntity, driver: Player, state: BoatState): Vec3 {
private fun applyJump(velocity: Vec3, vehicle: PokemonEntity, driver: Player, settings: BoatSettings, state: BoatState): Vec3 {
if (!driver.jumping) return velocity // Not jumping
if (state.jumpBuffer.get() != -1) return velocity // Already jumped very recently
if (!hasStamina(state)) return velocity // Has no stamina
val jumpStrength = vehicle.getRideStat(RidingStat.JUMP, RidingStyle.LIQUID, 1.0, 2.5)
val jumpStrength = vehicle.runtime.resolveDouble(settings.jumpStrengthExpr ?: globalBoat.jumpStrengthExpr!!)
state.jumpBuffer.set(0)
consumeStamina(vehicle, state, 0.05f)
return Vec3(velocity.x, velocity.y + jumpStrength, velocity.z)
}
private fun applyGravity(velocity: Vec3, vehicle: PokemonEntity, settings: BoatSettings, state: BoatState): Vec3 {
if (state.jumpBuffer.get() == -1 && vehicle.isInWater || vehicle.isUnderWater) {
return Vec3(velocity.x, -0.05, velocity.z)
if (state.jumpBuffer.get() == -1 && (vehicle.isInWater || vehicle.isUnderWater)) {
if (shouldFloatHigher(vehicle, settings)) {
return Vec3(velocity.x, 0.5, velocity.z)
}
else {
return Vec3(velocity.x, 0.0, velocity.z)
}
}
val terminalVelocity = vehicle.runtime.resolveDouble(settings.terminalVelocity)
val terminalVelocity = vehicle.runtime.resolveDouble(settings.terminalVelocity ?: globalBoat.terminalVelocity!!)
val gravity = (9.8 / ( 20.0)) * 0.2
return Vec3(velocity.x, max(velocity.y - gravity, terminalVelocity), velocity.z)
}
private fun applyStrafeRotation(vehicle: PokemonEntity, driver: Player, state: BoatState) {
val skill = vehicle.getRideStat(RidingStat.SKILL, RidingStyle.LIQUID, 0.5, 1.5)
private fun shouldFloatHigher(vehicle: PokemonEntity, settings: BoatSettings): Boolean {
val surfaceOffset = vehicle.runtime.resolveFloat(settings.surfaceLevelOffset ?: globalBoat.surfaceLevelOffset!!)
val blockPos = BlockPos.containing(vehicle.x, vehicle.eyeY + surfaceOffset, vehicle.z)
val fluidState = vehicle.level().getFluidState(blockPos)
return !fluidState.isEmpty
}
private fun applyStrafeRotation(vehicle: PokemonEntity, driver: Player, settings: BoatSettings, state: BoatState) {
val skillModifier = vehicle.runtime.resolveDouble(settings.rotationSpeedModifierExpr ?: globalBoat.rotationSpeedModifierExpr!!)
val strafe = driver.xxa
if (abs(strafe) > 0) {
val increase = if (strafe > 0) -1 else 1
state.deltaRotation.set(state.deltaRotation.get() + increase * skill)
state.deltaRotation.set(state.deltaRotation.get() + increase * skillModifier)
}
}
@@ -182,9 +219,16 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
return state.stamina.get() != 0f
}
private fun consumeStamina(vehicle: PokemonEntity, state: BoatState, drain: Float) {
val stamina = vehicle.getRideStat(RidingStat.STAMINA, RidingStyle.LIQUID, 1.0, 5.0)
state.stamina.set(max(0f, state.stamina.get() - drain / stamina.toFloat()))
private fun consumeStamina(vehicle: PokemonEntity, driver: Player, settings: BoatSettings, state: BoatState, drain: Float) {
val staminaModifier = vehicle.runtime.resolveFloat(settings.staminaDecreaseExpr ?: globalBoat.staminaDecreaseExpr!!)
if (staminaModifier == 0F) {
return
}
state.stamina.set(max(0f, state.stamina.get() - drain / staminaModifier))
if (state.stamina.get() == 0f) {
state.isVehicleSprinting.set(false)
driver.isSprinting = false
}
state.staminaBuffer.set(0)
}
@@ -285,10 +329,8 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
vehicle: PokemonEntity,
driver: Player
): Float {
if (state.rideVelocity.get().z > TOP_SPEED * 0.8) {
return Mth.lerp((state.rideVelocity.get().z - (TOP_SPEED * 0.8)) / (TOP_SPEED * 0.2), 1.0, 1.2).toFloat()
}
return 1.0f
val sprintFov = vehicle.runtime.resolveFloat(settings.sprintFovModifier ?: globalBoat.sprintFovModifier!!)
return if (state.isVehicleSprinting.get()) sprintFov else 1.0f
}
override fun useAngVelSmoothing(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity): Boolean {
@@ -301,7 +343,14 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
vehicle: PokemonEntity,
driver: Player
): ResourceLocation {
return cobblemonResource("no_pose")
val isInWater = Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
if (vehicle.isInWater || vehicle.isUnderWater) {
return@any true
}
val blockState = vehicle.level().getBlockState(it)
return@any !blockState.fluidState.isEmpty
}
return if (!isInWater) cobblemonResource("in_air") else cobblemonResource("no_pose")
}
override fun inertia(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity): Double {
@@ -325,7 +374,7 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
state: BoatState,
vehicle: PokemonEntity
): Boolean {
return vehicle.runtime.resolveBoolean(settings.rotatePokemonHead)
return vehicle.runtime.resolveBoolean(settings.rotatePokemonHead ?: globalBoat.rotatePokemonHead!!)
}
override fun shouldRotateRiderHead(
@@ -355,23 +404,67 @@ class BoatSettings : RidingBehaviourSettings {
override val stats = mutableMapOf<RidingStat, IntRange>()
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
var terminalVelocity = "-2.0".asExpression()
var terminalVelocity: Expression? = null
private set
var rotatePokemonHead = "true".asExpression()
var rotatePokemonHead: Expression? = null
private set
var staminaIncreaseExpr: Expression? = null
private set
var staminaDecreaseExpr: Expression? = null
private set
var rotationSpeedModifierExpr: Expression? = null
private set
var jumpStrengthExpr: Expression? = null
private set
var speedExpr: Expression? = null
private set
var accelerationExpr: Expression? = null
private set
var sprintSpeedModifier: Expression? = null
private set
var sprintFovModifier: Expression? = null
var surfaceLevelOffset: Expression? = null
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
buffer.writeRidingStats(stats)
rideSounds.encode(buffer)
buffer.writeExpression(terminalVelocity)
buffer.writeNullableExpression(terminalVelocity)
buffer.writeNullableExpression(rotatePokemonHead)
buffer.writeNullableExpression(staminaIncreaseExpr)
buffer.writeNullableExpression(staminaDecreaseExpr)
buffer.writeNullableExpression(rotationSpeedModifierExpr)
buffer.writeNullableExpression(jumpStrengthExpr)
buffer.writeNullableExpression(speedExpr)
buffer.writeNullableExpression(accelerationExpr)
buffer.writeNullableExpression(sprintSpeedModifier)
buffer.writeNullableExpression(sprintFovModifier)
buffer.writeNullableExpression(surfaceLevelOffset)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
stats.putAll(buffer.readRidingStats())
rideSounds = RideSoundSettingsList.decode(buffer)
terminalVelocity = buffer.readExpression()
terminalVelocity = buffer.readNullableExpression()
rotatePokemonHead = buffer.readNullableExpression()
staminaIncreaseExpr = buffer.readNullableExpression()
staminaDecreaseExpr = buffer.readNullableExpression()
rotationSpeedModifierExpr = buffer.readNullableExpression()
jumpStrengthExpr = buffer.readNullableExpression()
speedExpr = buffer.readNullableExpression()
accelerationExpr = buffer.readNullableExpression()
sprintSpeedModifier = buffer.readNullableExpression()
sprintFovModifier = buffer.readNullableExpression()
surfaceLevelOffset = buffer.readNullableExpression()
}
}
@@ -379,6 +472,7 @@ class BoatState : RidingBehaviourState() {
val deltaRotation = ridingState(0.0, Side.CLIENT)
val jumpBuffer = ridingState(-1, Side.CLIENT)
val staminaBuffer = ridingState(0, Side.CLIENT)
val isVehicleSprinting = ridingState(false, Side.CLIENT)
override fun copy() = BoatState().also {
it.deltaRotation.set(deltaRotation.get(), true)
@@ -386,6 +480,7 @@ class BoatState : RidingBehaviourState() {
it.stamina.set(stamina.get(), true)
it.jumpBuffer.set(jumpBuffer.get(), true)
it.staminaBuffer.set(staminaBuffer.get(), true)
it.isVehicleSprinting.set(isVehicleSprinting.get(), true)
}
override fun shouldSync(previous: RidingBehaviourState): Boolean {
@@ -8,10 +8,14 @@
package com.cobblemon.mod.common.api.riding.behaviour.types.liquid
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonRideSettings
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
import com.cobblemon.mod.common.api.riding.behaviour.Side
import com.cobblemon.mod.common.api.riding.behaviour.ridingState
import com.cobblemon.mod.common.api.riding.posing.PoseOption
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
@@ -21,6 +25,7 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.readRidingStats
import com.cobblemon.mod.common.util.writeNullable
import com.cobblemon.mod.common.util.writeRidingStats
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
@@ -38,6 +43,8 @@ class BurstBehaviour : RidingBehaviour<BurstSettings, BurstState> {
}
override val key = KEY
val globalBurst: BurstSettings
get() = CobblemonRideSettings.burst
override fun getRidingStyle(settings: BurstSettings, state: BurstState): RidingStyle {
return RidingStyle.LIQUID
@@ -67,7 +74,7 @@ class BurstBehaviour : RidingBehaviour<BurstSettings, BurstState> {
vehicle: PokemonEntity,
driver: Player
): Float {
if(state.dashing.get()) {
if (state.dashing.get()) {
state.ticks.set(state.ticks.get() + 1)
if(state.ticks.get() >= DASH_TICKS) {
state.dashing.set(false)
@@ -77,7 +84,7 @@ class BurstBehaviour : RidingBehaviour<BurstSettings, BurstState> {
}
state.dashing.set(true)
return settings.dashSpeed
return settings.dashSpeed ?: globalBurst.dashSpeed!!
}
override fun rotation(
@@ -239,22 +246,21 @@ class BurstSettings : RidingBehaviourSettings {
override val key = BurstBehaviour.KEY
override val stats = mutableMapOf<RidingStat, IntRange>()
var dashSpeed = 1F
var dashSpeed: Float? = null
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
buffer.writeRidingStats(stats)
rideSounds.encode(buffer)
buffer.writeFloat(dashSpeed)
buffer.writeNullable(dashSpeed) { buf, speed -> buf.writeFloat(speed) }
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
stats.putAll(buffer.readRidingStats())
rideSounds = RideSoundSettingsList.decode(buffer)
dashSpeed = buffer.readFloat()
dashSpeed = buffer.readNullable { buf -> buf.readFloat() }
}
}
@@ -9,34 +9,44 @@
package com.cobblemon.mod.common.api.riding.behaviour.types.liquid
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.MoLangMath.lerp
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonRideSettings
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.orientation.OrientationController
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.behaviour.types.air.JetSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.air.JetState
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
import com.cobblemon.mod.common.api.riding.behaviour.Side
import com.cobblemon.mod.common.api.riding.behaviour.ridingState
import com.cobblemon.mod.common.api.riding.posing.PoseOption
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.math.geometry.toDegrees
import com.cobblemon.mod.common.util.math.geometry.toRadians
import com.cobblemon.mod.common.util.readNullableExpression
import com.cobblemon.mod.common.util.readRidingStats
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.resolveDouble
import com.cobblemon.mod.common.util.toVec3d
import com.cobblemon.mod.common.util.writeNullableExpression
import com.cobblemon.mod.common.util.writeRidingStats
import kotlin.math.*
import net.minecraft.client.Minecraft
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth
import net.minecraft.util.SmoothDouble
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
import kotlin.math.abs
import kotlin.math.cos
import kotlin.math.min
import kotlin.math.sin
import kotlin.math.sqrt
class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
companion object {
@@ -44,6 +54,8 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
}
override val key = KEY
val globalDolphin: DolphinSettings
get() = CobblemonRideSettings.dolphin
override fun getRidingStyle(settings: DolphinSettings, state: DolphinState): RidingStyle {
return RidingStyle.LIQUID
@@ -53,14 +65,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
.with(PoseOption(PoseType.SWIM) { _, _, entity -> entity.deltaMovement.length() > 0.1 })
override fun isActive(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity): Boolean {
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
if (it.y.toDouble() == (vehicle.position().y)) {
val blockState = vehicle.level().getBlockState(it.below())
return@any (blockState.isAir || !blockState.fluidState.isEmpty ) ||
(vehicle.isInWater || vehicle.isUnderWater)
}
true
}
return vehicle.isInWater || !vehicle.onGround()
}
override fun pose(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity): PoseType {
@@ -68,7 +73,66 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
}
override fun speed(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity, driver: Player): Float {
return vehicle.runtime.resolveFloat(settings.speed)
return state.speed.get().toFloat()
}
override fun tick(
settings: DolphinSettings,
state: DolphinState,
vehicle: PokemonEntity,
driver: Player,
input: Vec3
) {
if(vehicle.level().isClientSide) {
handleBoosting(state, vehicle)
tickStamina(settings, state, vehicle)
}
}
fun handleBoosting(
state: DolphinState,
vehicle: PokemonEntity
) {
//If the forward key is not held then it cannot be boosting
if(Minecraft.getInstance().options.keyUp.isDown() && state.stamina.get() != 0.0f && (vehicle.isInWater || vehicle.isUnderWater)) {
val boostKeyPressed = Minecraft.getInstance().options.keySprint.isDown()
if (state.stamina.get() >= 0.25f) {
//If on the previous tick the boost key was held then don't change if the ride is boosting
if(state.boostIsToggleable.get() && boostKeyPressed) {
//flip the boosting state if boost key is pressed
state.boosting.set(!state.boosting.get())
}
//If the boost key is not held then next tick boosting is toggleable
state.boostIsToggleable.set(!boostKeyPressed)
}
} else {
//Turn off boost and reset boost params
state.boostIsToggleable.set(true)
state.boosting.set(false)
}
}
fun tickStamina(
settings: DolphinSettings,
state: DolphinState,
vehicle: PokemonEntity
) {
val stam = state.stamina.get()
if (vehicle.runtime.resolveBoolean(settings.infiniteStamina ?: globalDolphin.infiniteStamina!!)) {
return
}
// Grab the boost time in seconds and convert to ticks. Then calculate the drain rate as inversely
// proportional to the number of ticks of boost thus making a full boost take x ticks
// in short: "Stamina drains slower at higher values and also replenishes slower"
val boostTime = vehicle.runtime.resolveDouble(settings.staminaExpr ?: globalDolphin.staminaExpr!!) * 20.0f
val stamDrainRate = (1.0f / boostTime).toFloat()
val newStam = if (state.boosting.get()) max(0.0f,stam - stamDrainRate)
else min(1.0f,stam + stamDrainRate)
state.stamina.set(newStam)
}
override fun rotation(
@@ -87,39 +151,71 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
driver: Player,
input: Vec3
): Vec3 {
val runtime = vehicle.runtime
if (driver !is OrientationControllable) return Vec3.ZERO
val controller = (driver as OrientationControllable).orientationController
val runtime = vehicle.runtime
val boostMod = if (state.boosting.get()) runtime.resolveDouble(settings.jumpExpr ?: globalDolphin.jumpExpr!!) else 1.0
val topSpeed = (vehicle.runtime.resolveDouble(settings.speedExpr ?: globalDolphin.speedExpr!!) / 20.0) * boostMod
val accel = (topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalDolphin.accelerationExpr!!) * 20.0)) * boostMod
/*********************************************
* Breaching logic
*********************************************/
if (!vehicle.isInWater && !vehicle.isUnderWater)
{
state.lastVelocity.set(Vec3(state.lastVelocity.get().x, state.lastVelocity.get().y - 0.035, state.lastVelocity.get().z))
val currVel = state.lastVelocity.get()
val breachBoost = 1.5
if (!state.hasBreached.get()) {
// Apply an upwards breaching force if boosting. This will enhance jumping out of water to feel more
// fun
if (state.boosting.get()) {
state.lastVelocity.set(Vec3(currVel.x, currVel.y + breachBoost*sin(-controller.pitch.toRadians()).coerceIn(0.0f,0.5f), currVel.z))
}
state.hasBreached.set(true)
} else {
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.4
val terminalVel = 2.0
state.lastVelocity.set(Vec3(currVel.x, max(-terminalVel, currVel.y - gravity), currVel.z)) //.yRot(vehicle.yRot.toRadians()))
}
return state.lastVelocity.get()
} else {
state.lastVelocity.set(vehicle.deltaMovement.yRot(vehicle.yRot.toRadians()))
state.hasBreached.set(false)
}
val driveFactor = runtime.resolveFloat(settings.driveFactor)
val strafeFactor = runtime.resolveFloat(settings.strafeFactor)
val f = driver.xxa * strafeFactor
var g = driver.zza * driveFactor
if (g <= 0.0f) {
g *= runtime.resolveFloat(settings.reverseDriveFactor)
/*********************************************
* Handle collisions
*********************************************/
if (vehicle.horizontalCollision || vehicle.verticalCollision) {
// not actually friction
val frictionLimit = 0.1
val postColSpeed = min(max(vehicle.deltaMovement.length(), frictionLimit), state.speed.get())
state.speed.set(postColSpeed)
}
var yComp = 0.0
//Get roll to add a left and right strafe during roll
//Not sure if this is desired? Will need to mess around with this
//a bit more
var zComp = 0.0
val controller = (driver as? OrientationControllable)?.orientationController
if (controller != null)
{
//Can be used again if I better figure out how to deadzone it near the tops?
zComp = -1.0 * g.toDouble() * sin(Math.toRadians(controller.roll.toDouble()))
yComp = -1.0 * g.toDouble() * sin(Math.toRadians(controller.pitch.toDouble()))
val currSpeed = state.speed.get()
val strafeFactor = runtime.resolveDouble(settings.strafeFactor ?: globalDolphin.strafeFactor!!)
val reverseDriveFactor = runtime.resolveDouble(settings.reverseDriveFactor ?: globalDolphin.reverseDriveFactor!!)
val f = -driver.xxa * strafeFactor
when {
driver.zza > 0.0 -> state.speed.set(min(topSpeed, currSpeed + accel))
driver.zza < 0.0 -> state.speed.set(max( -topSpeed * reverseDriveFactor, currSpeed - accel))
else -> state.speed.set( lerp(currSpeed, 0.0, 0.05))
}
val vertInput = when {
driver.jumping && state.stamina.get() != 0.0f -> 1.0
driver.isShiftKeyDown -> -1.0
else -> 0.0
} * strafeFactor
val currVelocity = Vec3(0.0, yComp , g.toDouble())
state.lastVelocity.set(currVelocity)
return currVelocity
var newVelocity = Vec3(f, vertInput,-state.speed.get())
// Align to the ride
newVelocity = controller.orientation?.transform(newVelocity.toVector3f())?.toVec3d() ?: Vec3.ZERO
newVelocity = newVelocity.yRot(vehicle.yRot.toRadians())
val desiredVec = newVelocity.subtract(vehicle.deltaMovement)
return vehicle.deltaMovement.add(desiredVec)
}
override fun angRollVel(
@@ -129,30 +225,93 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
driver: Player,
deltaTime: Double
): Vec3 {
if (!vehicle.isInWater && !vehicle.isUnderWater)
{
return Vec3(0.0, 0.0, 0.0)
if (Cobblemon.config.disableRoll) return Vec3.ZERO
if (driver !is OrientationControllable) return Vec3.ZERO
val controller = (driver as OrientationControllable).orientationController
val currSpeed = vehicle.deltaMovement.length()
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalDolphin.handlingExpr!!)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalDolphin.speedExpr!!) / 20.0
val yawDeltaDeg = deltaTime * handling * sin(Math.toRadians(controller.roll.toDouble())).pow(2) * sin(Math.toRadians(controller.roll.toDouble())).sign
val trueYawDelt = yawDeltaDeg * abs(cos(Math.toRadians(controller.pitch.toDouble()))) * sqrt(RidingBehaviour.scaleToRange(currSpeed, 0.0, topSpeed))
// Dampen yaw when upside down
val yawDampen = (1 - abs(min(cos(controller.roll.toRadians()),0.0f)))
controller.applyGlobalYaw(trueYawDelt.toFloat() * yawDampen)
// Correct orientation when at low speeds
correctOrientation(settings, state, vehicle, controller, driver, deltaTime)
//yaw, pitch, roll
return Vec3(0.0, state.currPitchCorrectionForce.get(), state.currRollCorrectionForce.get())
}
private fun correctOrientation(
settings: DolphinSettings,
state: DolphinState,
vehicle: PokemonEntity,
controller: OrientationController,
driver: Player,
deltaTime: Double
) {
// Calculate correcting roll force.
// Mult by cos(pitch) to ensure that it doesn't roll correct at when diving or climbing
val currSpeed = vehicle.deltaMovement.length()
val rollCorrectionTimer = vehicle.runtime.resolveDouble(settings.timeToRollCorrect ?: globalDolphin.timeToRollCorrect!!)
val maxRollCorrectionRate = if (!(vehicle.isInWater || vehicle.isUnderWater)) 15.0f
else 10.0f * (cos(controller.pitch.toRadians())).pow(2) * ((state.noInputTime.get() - rollCorrectionTimer) * 2).coerceIn(0.0, 1.0).toFloat()
val maxRollForce = maxRollCorrectionRate / 40.0
val rollArrivalDeg = 90.0
if((state.speed.get() < 0.1 && driver.zza.toDouble() == 0.0) || state.noInputTime.get() > rollCorrectionTimer || !(vehicle.isInWater || vehicle.isUnderWater)) {
val arrivalInfluence = (min(rollArrivalDeg,abs(controller.roll).toDouble()) / rollArrivalDeg)
val desiredRollForce = (0 - controller.roll).sign * maxRollCorrectionRate * arrivalInfluence
var steeredRollForce = desiredRollForce - state.currRollCorrectionForce.get()
// Ensure the 'steering' correction force doesn't exceed our maxForce
if (abs(steeredRollForce) > maxRollForce) {
steeredRollForce = steeredRollForce.sign * maxRollForce
}
// Apply the 'steered' force (its really not a steering force I need a better name)
state.currRollCorrectionForce.set(
state.currRollCorrectionForce.get() + steeredRollForce
)
} else {
state.currRollCorrectionForce.set(
//reduce rollCorrection gradually
lerp(state.currRollCorrectionForce.get(), 0.0, deltaTime * 0.96)
)
}
val controller = (driver as? OrientationControllable)?.orientationController
//Calculate correcting pitch force.
val maxPitchCorrectionRate = if (!(vehicle.isInWater || vehicle.isUnderWater)) 15.0 else 5.0f * ((state.noInputTime.get() - rollCorrectionTimer) * 2).coerceIn(0.0, 1.0).toFloat() * (1 - (currSpeed * 0.5).coerceIn(0.0, 1.0))
val maxPitchForce = maxPitchCorrectionRate / 40.0
val pitchArrivalDeg = 45.0
val targetPitch = if ((vehicle.isInWater || vehicle.isUnderWater)) 0.0f else (-asin(vehicle.deltaMovement.y / vehicle.deltaMovement.length()).toDegrees()).coerceIn(-45.0f, 45.0f)
//this should be changed to be speed maybe?
val movingForce = driver.zza
if (controller != null) {
var yawAngVel = 3 * sin(Math.toRadians(controller.roll.toDouble())).toFloat()
var pitchAngVel = -1 * Math.abs(sin(Math.toRadians(controller.roll.toDouble())).toFloat())
if((state.noInputTime.get() > rollCorrectionTimer && driver.zza.toDouble() == 0.0) || !(vehicle.isInWater || vehicle.isUnderWater)) {
val arrivalInfluence = min(rollArrivalDeg, abs(Mth.wrapDegrees(targetPitch - controller.pitch.toDouble()))) / pitchArrivalDeg
val desiredPitchForce = (targetPitch - controller.pitch).sign * maxPitchCorrectionRate * arrivalInfluence
var steeredPitchForce = desiredPitchForce - state.currPitchCorrectionForce.get()
//limit rotation modulation when pitched up heavily or pitched down heavily
yawAngVel *= (abs(cos(Math.toRadians(controller.pitch.toDouble())))).toFloat()
pitchAngVel *= (abs(cos(Math.toRadians(controller.pitch.toDouble())))).toFloat()
//if you are not pressing forward then don't turn
//yawAngVel *= movingForce
//Ignore x,y,z its angular velocity:
//yaw, pitch, roll
return Vec3(yawAngVel.toDouble(), pitchAngVel.toDouble(), 0.0)
// Ensure the 'steering' correction force doesn't exceed our maxForce
if (abs(steeredPitchForce) > maxPitchForce) {
steeredPitchForce = steeredPitchForce.sign * maxPitchForce
}
// Apply the 'steered' force (its really not a steering force I need a better name)
state.currPitchCorrectionForce.set(
state.currPitchCorrectionForce.get() + steeredPitchForce
)
} else {
state.currPitchCorrectionForce.set(
//reduce rollCorrection gradually
lerp(state.currPitchCorrectionForce.get(), 0.0, deltaTime * 0.98)
)
}
return Vec3(0.0, 0.0, 0.0)
}
override fun rotationOnMouseXY(
@@ -167,7 +326,13 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
sensitivity: Double,
deltaTime: Double
): Vec3 {
// Begin the roll correction time counter if not enough mouse input
// is detected
if (abs(mouseX) < 1 && abs(mouseY) < 1 && (vehicle.isInWater || vehicle.isUnderWater)) {
state.noInputTime.set(state.noInputTime.get() + deltaTime)
} else {
state.noInputTime.set(0.0)
}
return when {
Cobblemon.config.disableRoll -> noRollRotation(
settings,
@@ -211,41 +376,34 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
if (driver !is OrientationControllable) return Vec3.ZERO
val controller = (driver as OrientationControllable).orientationController
//TODO:hook up handling
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
// Set roll to zero if transitioning to noroll config
controller.rotateRoll(controller.roll * -1.0f)
//Interpret mouse input
val boostingHandleDebuff = if (state.boosting.get()) 0.2 else 1.0
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalDolphin.handlingExpr!!) * boostingHandleDebuff
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult ?: globalDolphin.mouseInputMult!!)
//Smooth out mouse input.
val smoothingSpeed = 4.0
val mouseXc = (mouseX).coerceIn(-60.0, 60.0)
val mouseYc = (mouseY).coerceIn(-60.0, 60.0)
val mouseXc = ((mouseX).coerceIn(-60.0, 60.0) / 60.0) * mouseInputMult
val mouseYc = ((mouseY).coerceIn(-60.0, 60.0) / 60.0) * mouseInputMult
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1, deltaTime * smoothingSpeed);
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1, deltaTime * smoothingSpeed);
val rollRot = xInput
val pitchRot = yInput
// Roll
// if (abs(controller.pitch + rollRot) >= 89.5 ) {
// mouseXSmoother.reset()
// } else {
//
// }
//Give the ability to yaw with x mouse input when at low speeds.
val yawForce = xInput * handling
controller.rotateRoll(rollRot.toFloat())
// Yaw
// TODO:move all magic numbers to handling expression
val yawSpeed = handling * deltaTime * 0.25
val yawForce = sin(controller.roll.toRadians()) * yawSpeed * ( 1.0 - min(sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed)), 0.5))
//Apply yaw globally as we don't want roll or pitch changes due to local yaw when looking up or down.
controller.applyGlobalYaw(yawForce.toFloat())
controller.applyGlobalYaw(yawForce.toFloat() * abs(cos(controller.pitch.toRadians())).pow(2))
// Pitch
var pitchRot = yInput * handling
// Pitch up globally
if (abs(controller.pitch + pitchRot) >= 89.5 ) {
pitchRot = 0.0
mouseYSmoother.reset()
} else {
//controller.applyGlobalPitch(pitchRot.toFloat() * -1.0f)
controller.rotatePitch(pitchRot.toFloat())
controller.applyGlobalPitch(pitchRot.toFloat() * -1.0f)
}
//yaw, pitch, roll
@@ -266,46 +424,39 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
): Vec3 {
if (driver !is OrientationControllable) return Vec3.ZERO
val controller = (driver as OrientationControllable).orientationController
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalDolphin.speedExpr!!) / 20.0
val boostingHandleDebuff = if (state.boosting.get()) 0.2 else 1.0
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult ?: globalDolphin.mouseInputMult!!) * boostingHandleDebuff
//TODO:hook up handling
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
//Interpret mouse input
//Smooth out mouse input.
val smoothingSpeed = 4.0
val mouseXc = (mouseX).coerceIn(-60.0, 60.0)
val mouseYc = (mouseY).coerceIn(-60.0, 60.0)
val mouseXc = (mouseX).coerceIn(-45.0, 45.0) * mouseInputMult
val mouseYc = (mouseY).coerceIn(-45.0, 45.0) * mouseInputMult
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1, deltaTime * smoothingSpeed);
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1, deltaTime * smoothingSpeed);
val rollRot = xInput
val pitchRot = yInput
val currSpeed = vehicle.deltaMovement.length()
// Roll
// if (abs(controller.pitch + rollRot) >= 89.5 ) {
// mouseXSmoother.reset()
// } else {
//
// }
// Yaw locally
// Dampen yaw when rolled or pitched substantially
val yawDampen = abs(cos(controller.pitch.toRadians())).pow(2) * abs(cos(controller.roll.toRadians())).pow(2)
val localYaw = xInput * (1 - (RidingBehaviour.scaleToRange(currSpeed, 0.0, topSpeed))).coerceIn(0.4, 1.0) * yawDampen
controller.rotateRoll(rollRot.toFloat())
// Pitch locally up or down depending upon a number of factors:
// - Reduce pitch substantially when rolled and in a steep yaw. This prevents the player from ignoring a slow handling stat
// - Do not pitch at all when at or below hover speed
// - pitch faster at higher speeds
val p = 1.0 - vehicle.runtime.resolveDouble(settings.horizontalPitchExpr ?: globalDolphin.horizontalPitchExpr!!)
val localPitch = yInput * (1 - abs(sin(controller.roll.toRadians())) * p * hypot(controller.forwardVector.x.toDouble(), controller.forwardVector.z.toDouble()).toFloat())
// localPitch *= if (currSpeed > hoverSpeed) RidingBehaviour.scaleToRange(currSpeed, hoverSpeed, topSpeed)
// else 0.0
// Yaw
// TODO:move all magic numbers to handling expression
val yawSpeed = handling * deltaTime * 0.25
val yawForce = sin(controller.roll.toRadians()) * yawSpeed * ( 1.0 - min(sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed)), 0.5))
//Apply yaw globally as we don't want roll or pitch changes due to local yaw when looking up or down.
controller.applyGlobalYaw(yawForce.toFloat())
// Pitch
if (abs(controller.pitch + pitchRot) >= 89.5 ) {
mouseYSmoother.reset()
} else {
//controller.applyGlobalPitch(pitchRot.toFloat() * -1.0f)
controller.rotatePitch(pitchRot.toFloat())
}
// Roll less when slow and not at all when at hoverSpeed or lower.
// Apply a roll righting force when trying to pitch hard while rolled sideways. This nudges the player towards
// pitching globally
val rollForce = xInput * ((RidingBehaviour.scaleToRange(currSpeed, 0.0, topSpeed))).coerceIn(0.2, 1.0) //+ pitchInfluencedRollCorrection
//yaw, pitch, roll
return Vec3.ZERO
return Vec3(localYaw, localPitch, rollForce)
}
override fun canJump(
@@ -314,7 +465,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
vehicle: PokemonEntity,
driver: Player
): Boolean {
return vehicle.runtime.resolveBoolean(settings.canJump)
return false
}
override fun setRideBar(
@@ -323,7 +474,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
vehicle: PokemonEntity,
driver: Player
): Float {
return 0.0f
return (state.stamina.get() / 1.0f)
}
override fun jumpForce(
@@ -333,10 +484,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
driver: Player,
jumpStrength: Int
): Vec3 {
val runtime = vehicle.runtime
runtime.environment.query.addFunction("jump_strength") { DoubleValue(jumpStrength.toDouble()) }
val jumpVector = settings.jumpVector.map { runtime.resolveFloat(it) }
return Vec3(jumpVector[0].toDouble(), jumpVector[1].toDouble(), jumpVector[2].toDouble())
return Vec3.ZERO
}
override fun gravity(
@@ -345,7 +493,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
vehicle: PokemonEntity,
regularGravity: Double
): Double {
return 0.0
return 0.0 //if (!vehicle.isInWater && !vehicle.isUnderWater) 1.0 else 0.0
}
override fun rideFovMultiplier(
@@ -354,7 +502,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
vehicle: PokemonEntity,
driver: Player
): Float {
return 1.0f
return if (state.boosting.get()) 1.2f else 1.0f
}
override fun useAngVelSmoothing(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity): Boolean {
@@ -371,7 +519,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
}
override fun inertia(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity): Double {
return 0.05
return if (!vehicle.isInWater && !vehicle.isUnderWater) 1.0 else 0.1
}
override fun shouldRoll(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity): Boolean {
@@ -379,7 +527,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
}
override fun turnOffOnGround(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity): Boolean {
return true
return false
}
override fun dismountOnShift(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity): Boolean {
@@ -418,68 +566,99 @@ class DolphinSettings : RidingBehaviourSettings {
override val key = DolphinBehaviour.KEY
override val stats = mutableMapOf<RidingStat, IntRange>()
var canJump = "true".asExpression()
var infiniteStamina: Expression? = null
private set
var jumpVector = listOf("0".asExpression(), "0.3".asExpression(), "0".asExpression())
var canJump: Expression? = null
private set
var speed = "1.0".asExpression()
var reverseDriveFactor: Expression? = null
private set
var driveFactor = "1.0".asExpression()
var strafeFactor: Expression? = null
private set
var reverseDriveFactor = "0.25".asExpression()
var timeToRollCorrect: Expression? = null
private set
var strafeFactor = "0.2".asExpression()
private set
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 200.0, 128.0)".asExpression()
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 135.0, 45.0)".asExpression()
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 1.0, 0.35)".asExpression()
var accelerationExpr: Expression = "q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 3.0)), (1.0 / (20.0 * 8.0)))".asExpression()
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 120.0, 20.0)".asExpression()
// Boost influence when boosting
var jumpExpr: Expression? = null
// Yaw rate in degrees per second
var handlingExpr: Expression? = null
var horizontalPitchExpr: Expression? = null
var mouseInputMult: Expression? = null
// Top speed in Bl/s
var speedExpr: Expression? = null
// Seconds to get to top speed
var accelerationExpr: Expression? = null
// Seconds of boost
var staminaExpr: Expression? = null
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
buffer.writeRidingStats(stats)
rideSounds.encode(buffer)
buffer.writeExpression(canJump)
buffer.writeExpression(jumpVector[0])
buffer.writeExpression(jumpVector[1])
buffer.writeExpression(jumpVector[2])
buffer.writeExpression(speed)
buffer.writeExpression(driveFactor)
buffer.writeExpression(reverseDriveFactor)
buffer.writeExpression(strafeFactor)
buffer.writeNullableExpression(infiniteStamina)
buffer.writeNullableExpression(canJump)
buffer.writeNullableExpression(reverseDriveFactor)
buffer.writeNullableExpression(strafeFactor)
buffer.writeNullableExpression(jumpExpr)
buffer.writeNullableExpression(handlingExpr)
buffer.writeNullableExpression(horizontalPitchExpr)
buffer.writeNullableExpression(mouseInputMult)
buffer.writeNullableExpression(speedExpr)
buffer.writeNullableExpression(accelerationExpr)
buffer.writeNullableExpression(staminaExpr)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
stats.putAll(buffer.readRidingStats())
rideSounds = RideSoundSettingsList.decode(buffer)
canJump = buffer.readExpression()
jumpVector = listOf(
buffer.readExpression(),
buffer.readExpression(),
buffer.readExpression()
)
speed = buffer.readExpression()
driveFactor = buffer.readExpression()
reverseDriveFactor = buffer.readExpression()
strafeFactor = buffer.readExpression()
infiniteStamina = buffer.readNullableExpression()
canJump = buffer.readNullableExpression()
reverseDriveFactor = buffer.readNullableExpression()
strafeFactor = buffer.readNullableExpression()
jumpExpr = buffer.readNullableExpression()
handlingExpr = buffer.readNullableExpression()
horizontalPitchExpr = buffer.readNullableExpression()
mouseInputMult = buffer.readNullableExpression()
speedExpr = buffer.readNullableExpression()
accelerationExpr = buffer.readNullableExpression()
staminaExpr = buffer.readNullableExpression()
}
}
class DolphinState : RidingBehaviourState() {
var lastVelocity = ridingState(Vec3.ZERO, Side.BOTH)
var currRollCorrectionForce = ridingState(0.0, Side.CLIENT)
var currPitchCorrectionForce = ridingState(0.0, Side.CLIENT)
var noInputTime = ridingState(0.0, Side.CLIENT)
var boosting = ridingState(false, Side.BOTH)
var boostIsToggleable = ridingState(false, Side.CLIENT)
var speed = ridingState(0.0, Side.CLIENT)
var hasBreached = ridingState(false, Side.CLIENT)
override fun encode(buffer: FriendlyByteBuf) {
super.encode(buffer)
buffer.writeBoolean(boosting.get())
}
override fun decode(buffer: FriendlyByteBuf) {
super.decode(buffer)
boosting.set(buffer.readBoolean(), forced = true)
}
override fun reset() {
super.reset()
lastVelocity.set(Vec3.ZERO, forced = true)
currRollCorrectionForce.set(0.0, forced = true)
currPitchCorrectionForce.set(0.0, forced = true)
noInputTime.set(0.0, forced = true)
boosting.set(false, forced = true)
boostIsToggleable.set(true, forced = true)
speed.set(0.0, forced = true)
hasBreached.set(false, forced = true)
}
override fun toString(): String {
@@ -490,5 +669,18 @@ class DolphinState : RidingBehaviourState() {
it.stamina.set(stamina.get(), forced = true)
it.rideVelocity.set(rideVelocity.get(), forced = true)
it.lastVelocity.set(lastVelocity.get(), forced = true)
it.currRollCorrectionForce.set(this.currRollCorrectionForce.get(), forced = true)
it.currPitchCorrectionForce.set(this.currPitchCorrectionForce.get(), forced = true)
it.noInputTime.set(this.noInputTime.get(), forced = true)
it.boosting.set(this.boosting.get(), forced = true)
it.boostIsToggleable.set(this.boosting.get(), forced = true)
it.speed.set(this.speed.get(), forced = true)
it.hasBreached.set(this.hasBreached.get(), forced = true)
}
override fun shouldSync(previous: RidingBehaviourState): Boolean {
if (previous !is DolphinState) return false
if (previous.boosting.get() != boosting.get()) return true
return super.shouldSync(previous)
}
}
@@ -42,9 +42,10 @@ class RideLoopSound(val ride: PokemonEntity, val soundSettings: RideSoundSetting
var fade: Float = 0.0f
var shouldMuffle: Boolean = soundSettings.muffleEnabled
var rideAttenuation: RideAttenuationModel = soundSettings.attenuationModel
var muffleAmount: Float = 1.0f // Currently slightly misleading. 1.0 is on muffle and 0.0 is full hFgain muting
// TODO: revisit below flag once multi passenger support is enabled
var isPassenger: Boolean = Minecraft.getInstance().player?.id == ride.controllingPassenger?.id
var muffleAmount: Float = 1.0f // Currently slightly misleading. 1.0 is no muffle and 0.0 is full hFgain muting
var isPassenger: Boolean = ride.passengers.any { passenger ->
Minecraft.getInstance().player?.id == passenger.id
}
init {
this.looping = true
@@ -6,16 +6,16 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.riding.sound;
package com.cobblemon.mod.common.api.riding.sound
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.api.net.Encodable
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.readExpression
import com.cobblemon.mod.common.util.writeExpression
import com.google.gson.annotations.SerializedName
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation;
import net.minecraft.resources.ResourceLocation
/**
* Class to store data for looping sounds played during riding.
@@ -25,31 +25,41 @@ import net.minecraft.resources.ResourceLocation;
*/
data class RideSoundSettings(
val soundLocation: ResourceLocation,
val volumeExpr: Expression = "1.0".asExpression(),
val pitchExpr: Expression = "1.0".asExpression(),
@SerializedName("volumeExpr") private val _volumeExpr: Expression? = "1.0".asExpression(),
@SerializedName("pitchExpr") private val _pitchExpr: Expression? = "1.0".asExpression(),
val playForNonPassengers: Boolean = false,
val muffleEnabled: Boolean = false,
val attenuationModel: RideAttenuationModel = RideAttenuationModel.NONE
@SerializedName("attenuationModel") private val _attenuationModel: RideAttenuationModel? = RideAttenuationModel.NONE
): Encodable {
//this is needed because GSON does NOT support default constructor parameters and will just happily throw nulls all over the place
val volumeExpr: Expression
get() = _volumeExpr ?: "1.0".asExpression()
val pitchExpr: Expression
get() = _pitchExpr ?: "1.0".asExpression()
val attenuationModel: RideAttenuationModel
get() = _attenuationModel ?: RideAttenuationModel.NONE
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(soundLocation)
buffer.writeExpression(volumeExpr)
buffer.writeExpression(pitchExpr)
buffer.writeBoolean(playForNonPassengers)
buffer.writeBoolean(muffleEnabled)
buffer.writeEnum(attenuationModel)
}
companion object {
fun decode(buffer: RegistryFriendlyByteBuf): RideSoundSettings {
// Try to read if available, fallback to default otherwise
val soundLocation = buffer.readResourceLocation()
val volumeExpr = buffer.readExpression()
val pitchExpr = buffer.readExpression()
val playForNonPassengers = buffer.readBoolean()
val muffleEnabled = buffer.readBoolean()
val attenuationModel = RideAttenuationModel.EXPONENTIAL
val attenuationModel = buffer.readEnum(RideAttenuationModel::class.java)
return RideSoundSettings(
soundLocation,
@@ -21,11 +21,11 @@ import net.minecraft.network.chat.Component
enum class RidingStat(
val flavour: Flavour
) {
SPEED(Flavour.SWEET),
ACCELERATION(Flavour.SPICY),
SKILL(Flavour.DRY),
JUMP(Flavour.BITTER),
STAMINA(Flavour.SOUR);
SPEED(Flavour.SWEET),
STAMINA(Flavour.SOUR),
JUMP(Flavour.BITTER);
companion object {
fun getByName(name: String) = RidingStat.entries.firstOrNull { it.name.equals(name, ignoreCase = true) }
Loaded 100 of 2530 files, more files were not shown because too many files have changed in this diff. Show more