Compare commits

..
Author SHA1 Message Date
Sterrezo a602df2fb8 Removed a missing sound from sounds.json 2025-09-27 12:11:09 +02:00
Sterrezo f4d64eb05b Merge branch 'main' into sterr/bugfixes 2025-09-27 12:08:56 +02: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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
Carmendar beddea1ecb Merge branch 'main' into implement_pokemon 2025-09-06 16:08:48 -05:00
Neovitalism 2a62b49eab Update PokemonSpecies.kt 2025-09-04 21:10:49 -05:00
Test 43dbfc772e Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon 2025-08-30 22:06:27 -04:00
Tyzillion eee3296120 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into implement_pokemon 2025-08-27 09:54:51 -04:00
Paul be5ee8a6a3 Potentially got root bone animation working for players on pokes 2025-05-16 22:22:05 +10:00
Sterrezo 21e5ac6b96 Removed licenses from particle texture folders 2025-04-28 17:21:37 +02:00
Sterrezo d67eca46e6 Merge branch 'main' into sterr/bugfixes 2025-04-28 17:19:11 +02:00
Sterrezo ebbd92fb7a Even less redundancy 2024-12-06 19:15:37 +01:00
Sterrezo 60c4423ab7 Fixed the overlap but without redundancy this time 2024-12-06 18:14:40 +01:00
Sterrezo 82dbbc7f9f Accidental import 2024-12-06 14:49:50 +01:00
Sterrezo 534307df87 Fixed the overlap but efficiently this time 2024-12-06 14:47:57 +01:00
Sterrezo 6c4738fe82 Fixed the overlap but properly this time 2024-12-05 13:36:05 +01:00
Sterrezo 6dcddb612e Adjusted portrait space detection in the Pokedex to prevent overlap
Closes #1055
2024-12-04 17:14:45 +01:00
1338 changed files with 138518 additions and 20408 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.
+1
View File
@@ -22,6 +22,7 @@
### 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.
+144
View File
@@ -143,6 +143,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
@@ -277,6 +279,19 @@
- 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
@@ -383,6 +398,10 @@
- Starly
- Staravia
- Staraptor
- Varoom
- Revavroom
- Nickit
- Thievul
### Changes
- Changed pokemon caught and seen count to update based on the current pokedex being looked
@@ -446,6 +465,7 @@
- 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
@@ -528,6 +548,7 @@
- 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.
### Developer
- A finished battle now has winners and losers set inside of `PokemonBattle` instead of them always being empty.
@@ -658,6 +679,129 @@
- 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;
}
}
}
@@ -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);
}
}
}
@@ -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;
@@ -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);
}
@@ -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
@@ -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)))
@@ -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()))
}
}
}
@@ -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,6 @@ 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.dialogue.DialogueClosedHandler
import com.cobblemon.mod.common.client.net.dialogue.DialogueOpenedHandler
import com.cobblemon.mod.common.client.net.effect.PokeSnackBlockParticlesHandler
@@ -65,8 +64,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 +156,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 +170,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 +208,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 +223,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
@@ -388,7 +389,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()))
@@ -578,6 +578,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))
@@ -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
@@ -57,21 +56,21 @@ class FindHerdLeaderTaskConfig : SingleTaskConfig {
!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
@@ -119,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,
@@ -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
@@ -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
@@ -41,24 +44,27 @@ class WanderTaskConfig : SingleTaskConfig {
}
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,
@@ -77,7 +83,7 @@ class WanderTaskConfig : SingleTaskConfig {
(1..MAX_LOOK_DOWN_DISTANCE).firstOrNull {
val newPos = pos.below(it)
if (!world.getBlockState(newPos).isAir) {
return@firstOrNull true
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,
@@ -126,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
}
@@ -134,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)
@@ -157,14 +162,28 @@ class WanderTaskConfig : SingleTaskConfig {
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,
@@ -182,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)))
@@ -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() }
}
}
}
@@ -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].
@@ -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)
}
}
@@ -1236,7 +1236,7 @@ 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
@@ -15,6 +15,7 @@ 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
@@ -113,7 +114,7 @@ open class OrientationController(val entity: LivingEntity) {
fun applyGlobalPitch(deltaPitchDegrees: Float) = updateOrientation { original ->
val currQuat = Quaternionf().setFromUnnormalized(original)
val horzLeftVector = Vector3f(0.0f, this.upVector.y.sign, 0.0f).cross(this.forwardVector)
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
@@ -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)
}
}
}
}
@@ -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
/**
@@ -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()
)
}
}
@@ -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.
@@ -27,10 +25,9 @@ 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)
@@ -49,6 +49,7 @@ 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) }
context = ActiveRidingContext(behaviourSettings.key, behaviourSettings, newState, newTransition)
lastTransitionAge = entity.ticksLived
} else {
@@ -23,15 +23,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.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.*
class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
@@ -49,7 +50,18 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
.with(PoseOption(PoseType.FLY) { _, state, _ -> state.rideVelocity.get().z > 0.2 })
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 {
@@ -74,6 +86,24 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
) {
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)
}
}
@@ -92,7 +122,8 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
val stamDrainRate = (1.0f / totalStam).toFloat()
val newStam = if (driver.zza > 0.0) max(0.0f,stam - stamDrainRate)
else min(1.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)
}
@@ -113,8 +144,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
@@ -133,7 +162,10 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
forwardForce += sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().y
}
val velocity = Vec3(state.rideVelocity.get().x , upForce, forwardForce)
// 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
val velocity = Vec3(state.rideVelocity.get().x , upForce + extraDownwardForce, forwardForce)
return velocity
}
@@ -228,7 +260,7 @@ 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)
@@ -256,18 +288,20 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
val topSpeed = vehicle.runtime.resolveDouble(settings.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(state.rideVelocity.get().length(), 0.0, topSpeed))
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))
// Dampen yaw when upside down
val yawDampen = (1 - abs(min(cos(controller.roll.toRadians()),0.0f)))
val yawDampen = (1 - abs(min(cos(controller.roll.toRadians()),0.0f)).pow(2))
controller.applyGlobalYaw(trueYawDelt.toFloat() * yawDampen)
// 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, state.currPitchCorrectionForce.get(), state.currRollCorrectionForce.get())
return Vec3(0.0, 0.0, state.currRollCorrectionForce.get())
}
private fun correctOrientation(
@@ -280,10 +314,10 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
// 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)
val maxRollCorrectionRate = if (state.rideVelocity.get().length() < 0.1) 15.0f else 8.0f * (cos(controller.pitch.toRadians())).pow(2) * ((state.noInputTime.get() - rollCorrectionTimer) * 2).coerceIn(0.0, 1.0).toFloat()
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.noInputTime.get() > rollCorrectionTimer) {
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()
@@ -305,13 +339,18 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
}
//Calculate correcting pitch force.
val maxPitchCorrectionRate = 15
val maxPitchForce = maxPitchCorrectionRate / 40.0
val pitchArrivalDeg = 90.0
if(state.rideVelocity.get().length() < 0.1) {
val arrivalInfluence = (min(pitchArrivalDeg,abs(controller.pitch).toDouble()) / pitchArrivalDeg)
val desiredPitchForce = (0 - controller.pitch).sign * maxPitchCorrectionRate * arrivalInfluence
var steeredPitchForce = desiredPitchForce - state.currPitchCorrectionForce.get()
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) {
@@ -342,12 +381,23 @@ 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 (abs(mouseX) < 1 && abs(mouseY) < 1) {
state.noInputTime.set(state.noInputTime.get() + deltaTime)
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.noInputTime.set(0.0)
state.noInputTimePitch.set(0.0)
}
return when {
@@ -445,7 +495,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult)
//Smooth out mouse input.
val smoothingSpeed = 4.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
@@ -454,12 +504,13 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
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
// Yaw locally when at or below hoverSpeed
// 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 (currSpeed < hoverSpeed) xInput * (1 - sqrt(RidingBehaviour.scaleToRange(currSpeed, hoverSpeed, topSpeed))).coerceIn(0.0, 1.0)
else xInput * 0.4 * cos(controller.roll.toRadians()).coerceIn(0.0f, 1.0f) * yawDampen
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
// 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
@@ -467,14 +518,17 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
// - pitch faster at higher speeds
val p = 1.0 - vehicle.runtime.resolveDouble(settings.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) RidingBehaviour.scaleToRange(currSpeed, hoverSpeed, topSpeed)
localPitch *= if (currSpeed > hoverSpeed || yInput > 0.0) RidingBehaviour.scaleToRange(currSpeed, 0.0, topSpeed).coerceIn(0.2, 1.0)
else 0.0
// 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 // if ( currSpeed > hoverSpeed * 2) 0.4*(abs(yInput - localPitch) * controller.roll.sign * -1.0) else 0.0
val rollForce = if (currSpeed > hoverSpeed) xInput * sqrt(abs(RidingBehaviour.scaleToRange(currSpeed, hoverSpeed, topSpeed)).coerceIn(0.0,1.0)) + pitchInfluencedRollCorrection
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
@@ -588,6 +642,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,
@@ -612,15 +677,15 @@ class BirdSettings : RidingBehaviourSettings {
var timeToRollCorrect: Expression = "0.5".asExpression()
private set
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 90.0, 45.0)".asExpression()
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 45.0, 10.0)".asExpression()
var horizontalPitchExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 0.2, 0.1)".asExpression()
var mouseInputMult: Expression = "q.get_ride_stats('SKILL', 'AIR', 1.2, 0.8)".asExpression()
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 14.0, 8.0)".asExpression()
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 20.0, 4.0)".asExpression()
// Seconds from stationary to top speed
var accelerationExpr: Expression = "q.get_ride_stats('ACCELERATION', 'AIR', 2.0, 5.0)".asExpression()
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 22.0, 12.0)".asExpression()
var accelerationExpr: Expression = "q.get_ride_stats('ACCELERATION', 'AIR', 2.0, 8.0)".asExpression()
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 15.0, 2.0)".asExpression()
var glidespeedExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 2.2, 1.5)".asExpression()
var glidespeedExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 2.0, 1.0)".asExpression()
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
@@ -662,11 +727,12 @@ class BirdState : RidingBehaviourState() {
var lastGlide = ridingState(-100L, Side.CLIENT)
var currRollCorrectionForce = ridingState(0.0, Side.CLIENT)
var currPitchCorrectionForce = ridingState(0.0, Side.CLIENT)
var noInputTime = 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())
}
@@ -681,7 +747,8 @@ class BirdState : RidingBehaviourState() {
lastGlide.set(-100L, forced = true)
currRollCorrectionForce.set(0.0, forced = true)
currPitchCorrectionForce.set(0.0, forced = true)
noInputTime.set(0.0, forced = true)
noInputTimeRoll.set(0.0, forced = true)
noInputTimePitch.set(0.0, forced = true)
}
override fun toString(): String {
@@ -695,7 +762,8 @@ class BirdState : RidingBehaviourState() {
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.noInputTime.set(this.noInputTime.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 {
@@ -704,3 +772,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
}
@@ -75,7 +75,6 @@ class GliderBehaviour : RidingBehaviour<GliderSettings, RidingBehaviourState> {
val xVector = if (vehicle.runtime.resolveBoolean(settings.canStrafe)) driver.xxa.toDouble() else 0.0
val yVector = -vehicle.runtime.resolveDouble(settings.glideSpeed)
val zVector = driver.zza.toDouble()
val speedStat = settings.calculate(RidingStat.SPEED, 0)
return Vec3(xVector, yVector, zVector)
}
@@ -11,8 +11,6 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.air
import com.bedrockk.molang.Expression
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.land.HorseSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseState
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
@@ -27,7 +25,6 @@ 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
@@ -97,7 +94,31 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
driver: Player,
input: Vec3
) {
tickStamina(settings, state, vehicle)
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)
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(
@@ -107,8 +128,8 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
) {
val stam = state.stamina.get()
val stamDrainRate = (1.0f / vehicle.runtime.resolveDouble(settings.staminaExpr)).toFloat() / 20.0f
val newStam = min(1.0f,stam + stamDrainRate)
val stamReplenishRate = (1.0f / vehicle.runtime.resolveDouble(settings.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)
}
@@ -206,6 +227,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
// Vertical movement based on driver input.
val vertInput = when {
state.stamina.get() == 0.0f -> -1.0
driver.jumping -> 1.0
driver.isShiftKeyDown -> -1.0
else -> 0.0
@@ -219,28 +241,6 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
driver.zza.sign.toDouble()
).normalize()
/******************************************************
* Boosting Logic
*****************************************************/
if (driver.level().isClientSide) {
if(Minecraft.getInstance().options.keySprint.isDown() && state.stamina.get() == 1.0f && !state.isBoosting.get()) {
state.isBoosting.set(true)
val localBoostVel = if (inputVel.horizontalDistance() == 0.0) Vec3(0.0, 0.0, 1.0) else Vec3(inputVel.x, 0.0, inputVel.z)
state.boostVec.set(localBoostVel)
state.boostTicks.set(0)
state.stamina.set(0.0f)
}
}
val maxBoostTicks = 20
val boostPower = (topSpeed * 20) * 0.1 * vehicle.runtime.resolveDouble(settings.jumpExpr)
if (state.isBoosting.get() && state.boostTicks.get() < maxBoostTicks) {
newVelocity = newVelocity.add(state.boostVec.get().scale(boostPower / maxBoostTicks))
state.boostTicks.set(state.boostTicks.get() + 1)
} else if (state.isBoosting.get()) {
state.isBoosting.set(false)
}
// 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.
@@ -430,25 +430,28 @@ class HoverSettings : RidingBehaviourSettings {
var canJump = "true".asExpression()
private set
var stamReplenishTimeSeconds = "2.0".asExpression()
private set
// Speed in block per second
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 13.0, 6.0)".asExpression()
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 7.0, 1.0)".asExpression()
private set
// Acceleration in number of seconds to top speed
var accelerationExpr: Expression =
"q.get_ride_stats('ACCELERATION', 'AIR', 2.0, 5.0)".asExpression()
"q.get_ride_stats('ACCELERATION', 'AIR', 2.0, 8.0)".asExpression()
private set
// Amount of seconds between boosts
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 1, 4)".asExpression()
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 30.0, 2.0)".asExpression()
private set
// Power of boost as a fraction of top speed
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 1.2, 0.2)".asExpression()
// Blocks before stamina drain
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 16.0, 2.0)".asExpression()
private set
// Turn rate in degrees per second when stationary
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 360.0, 90)".asExpression()
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 270.0, 45.0)".asExpression()
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
@@ -481,17 +484,20 @@ class HoverState : RidingBehaviourState() {
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() {
@@ -500,6 +506,7 @@ class HoverState : RidingBehaviourState() {
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 {
@@ -508,6 +515,7 @@ class HoverState : RidingBehaviourState() {
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 {
@@ -26,6 +26,7 @@ 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.util.math.geometry.toRadians
import net.minecraft.client.Minecraft
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.SmoothDouble
@@ -34,6 +35,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 org.joml.Vector3f
import kotlin.math.*
class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
@@ -87,7 +89,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
val boostKeyPressed = Minecraft.getInstance().options.keySprint.isDown()
if(state.stamina.get() != 0.0f && boostKeyPressed) {
if (state.stamina.get() >= 0.25f) {
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
@@ -114,10 +116,16 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
val stamDrainRate = (1.0f / vehicle.runtime.resolveDouble(settings.staminaExpr)).toFloat() / 20.0f
if (state.boosting.get()) {
newStam = max(0.0f,stam - stamDrainRate)
newStam = max(0.0f,stam - stamDrainRate * 1.5f)
} else {
newStam = min(1.0f,stam + stamDrainRate * 0.25f)
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)
@@ -139,23 +147,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
val velocity = Vec3(0.0, upForce, forwardForce)
// 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.
return velocity
return worldVelVec
}
/*
@@ -168,16 +179,27 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
driver: Player
) {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr) / 400.0
val minSpeed = vehicle.runtime.resolveDouble(settings.minSpeed) / 20.0
val speed = state.rideVelocity.get().length()
val accel = topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr) * 20.0)
val deccel = vehicle.runtime.resolveDouble(settings.deccelRate)//0.005
val minSpeed = topSpeed * vehicle.runtime.resolveDouble(settings.minSpeedFactor)
val speed = state.rideVelocity.get().z
val boostMult = vehicle.runtime.resolveDouble(settings.jumpExpr)
val boostTopSpeed = topSpeed * boostMult
val boostAccel = accel * boostMult
//speed up and slow down based on input
if (state.boosting.get() && speed < boostTopSpeed) {
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,
@@ -201,7 +223,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
Vec3(
state.rideVelocity.get().x,
state.rideVelocity.get().y,
max(state.rideVelocity.get().z - ((accel) / 2), minSpeed)
max(state.rideVelocity.get().z - (deccel), minSpeed)
)
)
} else if (speed > topSpeed) {
@@ -209,6 +231,17 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
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(
@@ -305,7 +338,8 @@ 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) * handlingDebuff
//convert it to delta time
handling *= (cappedDeltaTime)
@@ -358,7 +392,8 @@ 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) * handlingDebuff
//convert it to delta time
handling *= (cappedDeltaTime)
@@ -434,7 +469,12 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
vehicle: PokemonEntity,
driver: Player
): Float {
return if (state.boosting.get()) 1.2f else 1.0f
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 {
@@ -510,7 +550,11 @@ class JetSettings : RidingBehaviourSettings {
var gravity: Expression = "0".asExpression()
private set
var minSpeed: Expression = "12.0".asExpression()
var deccelRate: Expression = "0.005".asExpression()
private set
// Mult to top speed in order to derive minSpeed
var minSpeedFactor: Expression = "0.25".asExpression()
private set
var handlingYawExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 50.0, 25.0)".asExpression()
@@ -521,21 +565,21 @@ class JetSettings : RidingBehaviourSettings {
private set
// Boost power. Mult for top speed and accel while boosting
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 1.7, 1.2)".asExpression()
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 2.0, 1.0)".asExpression()
private set
// Turn rate in degrees per second
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 120.0, 60.0)".asExpression()
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 60.0, 10.0)".asExpression()
private set
// Top Speed in blocks per second
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 36.0, 20.0)".asExpression()
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 24.0, 4.0)".asExpression()
private set
// Acceleration in blocks per s^2
// Seconds to get to top speed
var accelerationExpr: Expression =
"q.get_ride_stats('ACCELERATION', 'AIR', 5.0, 2.5)".asExpression()
"q.get_ride_stats('ACCELERATION', 'AIR', 2.0, 8.0)".asExpression()
private set
// Time in seconds to drain full bar of stamina when boosting
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 8.0, 4.0)".asExpression()
// Time in seconds to drain full bar of stamina flying
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 60.0, 4.0)".asExpression()
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
@@ -545,7 +589,8 @@ class JetSettings : RidingBehaviourSettings {
buffer.writeRidingStats(stats)
rideSounds.encode(buffer)
buffer.writeExpression(gravity)
buffer.writeExpression(minSpeed)
buffer.writeExpression(deccelRate)
buffer.writeExpression(minSpeedFactor)
buffer.writeExpression(handlingYawExpr)
buffer.writeExpression(infiniteStamina)
buffer.writeExpression(jumpExpr)
@@ -559,7 +604,8 @@ class JetSettings : RidingBehaviourSettings {
stats.putAll(buffer.readRidingStats())
rideSounds = RideSoundSettingsList.decode(buffer)
gravity = buffer.readExpression()
minSpeed = buffer.readExpression()
deccelRate = buffer.readExpression()
minSpeedFactor = buffer.readExpression()
handlingYawExpr = buffer.readExpression()
infiniteStamina = buffer.readExpression()
jumpExpr = buffer.readExpression()
@@ -577,6 +623,7 @@ class JetState : RidingBehaviourState() {
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)
@@ -603,6 +650,7 @@ class JetState : RidingBehaviourState() {
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 {
@@ -86,7 +86,7 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
// Use this as a "tick" function and check to see if the driver is "boosting"
if(vehicle.level().isClientSide) {
handleBoosting(settings, state, vehicle, driver)
tickStamina(settings, state, vehicle)
tickStamina(settings, state, vehicle, driver)
}
return state.rideVelocity.get().length().toFloat()
@@ -101,7 +101,6 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
//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 (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
@@ -109,7 +108,6 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
}
//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)
@@ -121,7 +119,8 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
fun tickStamina(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity
vehicle: PokemonEntity,
driver: Player
) {
val stam = state.stamina.get()
@@ -131,8 +130,7 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
val boostTime = vehicle.runtime.resolveDouble(settings.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)
val newStam = if(state.boosting.get() || driver.jumping) max(0.0f,stam - stamDrainRate) else stam
state.stamina.set(newStam)
}
@@ -266,7 +264,7 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
if(state.canSpeedBurst.get()) {
burst = 0.5f
state.stamina.set(max(0.0f,state.stamina.get() - 0.1f))
state.stamina.set(max(0.0f,state.stamina.get() - 0.05f))
state.canSpeedBurst.set(false)
}
@@ -301,7 +299,7 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
} else {
if (driver.jumping && !(vehicle.deltaMovement.y > maxVertSpeed && (newVelocity.y.sign.toFloat() > 0.0))) {
// More force if traveling downwards to allow for quicker fall stops
vertInput = if (newVelocity.y < 0) jump * 3 else jump
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()
newVelocity = Vec3(
@@ -474,6 +472,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,
@@ -509,8 +519,8 @@ class RocketSettings : RidingBehaviourSettings {
"q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 1.5)), (1.0 / (20.0 * 3.0))) * 0.5".asExpression()
private set
// Boost time in seconds
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 7.5, 2.5)".asExpression()
// air time in seconds
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 20.0, 10.0)".asExpression()
private set
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 2.0, 1.0)".asExpression()
@@ -18,6 +18,7 @@ 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.math.geometry.toRadians
import net.minecraft.core.Direction
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
@@ -30,6 +31,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 org.joml.Matrix3f
import kotlin.math.*
class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
@@ -243,8 +245,21 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
* Gather the previous velocity and check for horizontal
* collisions
*****************************************************/
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())
}
@@ -507,23 +522,23 @@ class HorseSettings : RidingBehaviourSettings {
var lookYawLimit = "90.0f".asExpression()
private set
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'LAND', 1.0, 0.4)".asExpression()
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'LAND', 1.2, 0.1)".asExpression()
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', 0.5, 2.0)".asExpression()
"q.get_ride_stats('ACCELERATION', 'LAND', 0.1, 12.0)".asExpression()
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 = "q.get_ride_stats('STAMINA', 'LAND', 60.0, 4.0)".asExpression()
private set
//Between a one block jump and a six block jump
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'LAND', 1.2, 0.45)".asExpression()
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'LAND', 1.6, 0.2)".asExpression()
private set
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'LAND', 180.0, 40.0)".asExpression()
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'LAND', 90.0, 30.0)".asExpression()
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
@@ -18,6 +18,7 @@ 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 net.minecraft.core.BlockPos
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth
@@ -134,7 +135,7 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
private fun applyVelocityFromInput(velocity: Vec3, vehicle: PokemonEntity, driver: Player, settings: BoatSettings, state: BoatState): Vec3 {
val speed = vehicle.runtime.resolveDouble(settings.speedExpr)
val acceleration = vehicle.runtime.resolveDouble(settings.accelerationExpr)
val acceleration = speed / (vehicle.runtime.resolveDouble(settings.accelerationExpr) * 20)
if (state.jumpBuffer.get() != -1 || !(vehicle.isInWater || vehicle.isUnderWater)) {
return velocity
@@ -170,14 +171,25 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
private fun applyGravity(velocity: Vec3, vehicle: PokemonEntity, settings: BoatSettings, state: BoatState): Vec3 {
if (state.jumpBuffer.get() == -1 && (vehicle.isInWater || vehicle.isUnderWater)) {
val verticalVelocity = if (vehicle.isUnderWater) 0.5 else 0.0
return Vec3(velocity.x, verticalVelocity, velocity.z)
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 gravity = (9.8 / ( 20.0)) * 0.2
return Vec3(velocity.x, max(velocity.y - gravity, terminalVelocity), velocity.z)
}
private fun shouldFloatHigher(vehicle: PokemonEntity, settings: BoatSettings): Boolean {
val surfaceOffset = vehicle.runtime.resolveFloat(settings.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)
val strafe = driver.xxa
@@ -394,14 +406,16 @@ class BoatSettings : RidingBehaviourSettings {
var speedExpr = "q.get_ride_stats('SPEED', 'LIQUID', 2.0, 0.5)".asExpression()
private set
var accelerationExpr = "q.get_ride_stats('ACCELERATION', 'LIQUID', 0.5, 0.05)".asExpression()
var accelerationExpr = "q.get_ride_stats('ACCELERATION', 'LIQUID', 0.1, 12.0)".asExpression()
private set
var sprintSpeedModifier = "1.5".asExpression()
var sprintSpeedModifier = "q.get_ride_stats('JUMP', 'LIQUID', 1.5, 1.25)".asExpression()
private set
var sprintFovModifier = "1.2".asExpression()
var surfaceLevelOffset = "0".asExpression()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
buffer.writeRidingStats(stats)
@@ -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
@@ -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) }
@@ -34,7 +34,7 @@ object CobblemonSpawnPools {
SpawnablePositionTypePrecalculation,
BucketPrecalculation,
BiomePrecalculation
)
), reloadable = true
)
}
@@ -100,7 +100,7 @@ class HeartyGrainsBlock(settings: Properties) : CropBlock(settings), SimpleWater
override fun randomTick(state: BlockState, world: ServerLevel, pos: BlockPos, random: RandomSource) {
if (state.getValue(HALF) == DoubleBlockHalf.UPPER || !canGrow(world, pos, state)) return
if (hasSufficientLight(world, pos) && random.nextInt(7) == 0) {
if (hasSufficientLight(world, pos) && random.nextInt(16) == 0) {
grow(world, pos, state, 1)
}
}
@@ -0,0 +1,19 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block.campfirepot
enum class CampfirePotColor(val suffix: String) {
RED("red"),
BLUE("blue"),
GREEN("green"),
PINK("pink"),
WHITE("white"),
YELLOW("yellow"),
BLACK("black");
}
@@ -56,7 +56,7 @@ class CookingPotMenu : RecipeBookMenu<CraftingInput, CookingPotRecipeBase>, Cont
CampfireBlockEntity.Companion.CRAFTING_GRID_WIDTH
)
this.resultContainer = ResultContainer()
this.containerData = SimpleContainerData(3)
this.containerData = SimpleContainerData(4)
this.addDataSlots(containerData)
this.player = playerInventory.player
this.level = playerInventory.player.level()
@@ -280,4 +280,4 @@ class CookingPotMenu : RecipeBookMenu<CraftingInput, CookingPotRecipeBase>, Cont
value: Int
) {
}
}
}
@@ -57,8 +57,8 @@ import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.gameevent.GameEvent
import net.minecraft.world.phys.Vec3
import org.joml.Vector4f
import com.cobblemon.mod.common.item.CampfirePotItem
import java.util.*
import kotlin.math.sign
class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlockEntity(
CobblemonBlockEntities.CAMPFIRE,
@@ -84,6 +84,7 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
const val COOKING_PROGRESS_INDEX = 0
const val COOKING_PROGRESS_TOTAL_TIME_INDEX = 1
const val IS_LID_OPEN_INDEX = 2
const val COOKING_POT_COLOR_INDEX = 3
const val BASE_BROTH_COLOR = 0xFDFACF
const val BASE_BROTH_BUBBLE_COLOR = 0xFFFEFDE4.toInt()
@@ -242,6 +243,7 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
COOKING_PROGRESS_INDEX -> this@CampfireBlockEntity.cookingProgress
COOKING_PROGRESS_TOTAL_TIME_INDEX -> this@CampfireBlockEntity.cookingTotalTime
IS_LID_OPEN_INDEX -> if (this@CampfireBlockEntity.blockState.getValue(CampfireBlock.LID)) 0 else 1
COOKING_POT_COLOR_INDEX -> (this@CampfireBlockEntity.getPotItem()?.item as? CampfirePotItem)?.color?.ordinal ?: 0
else -> 0
}
}
@@ -251,11 +253,12 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
COOKING_PROGRESS_INDEX -> this@CampfireBlockEntity.cookingProgress = value
COOKING_PROGRESS_TOTAL_TIME_INDEX -> this@CampfireBlockEntity.cookingTotalTime = value
IS_LID_OPEN_INDEX -> this@CampfireBlockEntity.toggleLid(value == 1)
COOKING_POT_COLOR_INDEX -> {}
}
}
override fun getCount(): Int {
return 3
return 4
}
}
@@ -481,4 +484,4 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
}
}
}
@@ -104,7 +104,7 @@ object CobblemonClient {
teamData = ClientPlayerTeamData()
clientPokedexData = ClientPokedexManager(mutableMapOf())
storage.onLogin()
CobblemonDataProvider.canReload = false
// CobblemonDataProvider.canReload = false
}
fun onLogout() {
@@ -113,7 +113,7 @@ object CobblemonClient {
battleOverlay.onLogout()
ClientTaskTracker.clear()
checkedStarterScreen = false
CobblemonDataProvider.canReload = true
// CobblemonDataProvider.canReload = true
DeferredRenderer.clearAll()
ClientPlayerIcon.clear()
}
@@ -135,7 +135,11 @@ class RideControlsOverlay : Gui(Minecraft.getInstance()) {
// Configure what controls to show for each behaviour
when (currentBehaviourKey) {
BirdBehaviour.KEY -> {}
BoatBehaviour.KEY -> {}
BoatBehaviour.KEY -> {
showVerticalMouse = false
showHorizontalMouse = false
showSneakKey = false
}
BurstBehaviour.KEY -> {}
DolphinBehaviour.KEY -> {}
GliderBehaviour.KEY -> {}
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.client.gui.cookingpot
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.block.campfirepot.CampfirePotColor
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Button
@@ -21,6 +22,7 @@ import net.minecraft.sounds.SoundEvents
class CookButton(
pX: Int, pY: Int,
var selected: Boolean = false,
var color: CampfirePotColor,
onPress: OnPress
): Button(pX, pY, SIZE.toInt(), SIZE.toInt(), Component.literal("Cook"), onPress, DEFAULT_NARRATION) {
@@ -28,11 +30,11 @@ class CookButton(
private const val SIZE = 20F
private val buttonResource = cobblemonResource("textures/gui/campfirepot/button.png")
private val buttonIconResource = cobblemonResource("textures/item/campfire_pots/campfire_pot_red.png")
private val buttonIconOpenResource = cobblemonResource("textures/item/campfire_pots/campfire_pot_red_open.png")
}
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
val closedIcon = cobblemonResource("textures/item/campfire_pots/campfire_pot_${color.suffix}.png")
val openIcon = cobblemonResource("textures/item/campfire_pots/campfire_pot_${color.suffix}_open.png")
blitk(
matrixStack = context.pose(),
texture = buttonResource,
@@ -46,7 +48,7 @@ class CookButton(
blitk(
matrixStack = context.pose(),
texture = if (selected) buttonIconResource else buttonIconOpenResource,
texture = if (selected) closedIcon else openIcon,
x = x + 2,
y = y + 2,
width = 16,
@@ -11,8 +11,10 @@ package com.cobblemon.mod.common.client.gui.cookingpot
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonNetwork.sendToServer
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.block.campfirepot.CampfirePotColor
import com.cobblemon.mod.common.block.campfirepot.CookingPotMenu
import com.cobblemon.mod.common.block.campfirepot.SeasoningSlot
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.COOKING_POT_COLOR_INDEX
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.IS_LID_OPEN_INDEX
import com.cobblemon.mod.common.integration.jei.CobblemonJeiPlugin
import com.cobblemon.mod.common.integration.jei.cooking.CampfirePotRecipeCategory
@@ -118,7 +120,8 @@ class CookingPotScreen(
this.titleLabelY = 6
if (::cookButton.isInitialized) removeWidget(cookButton)
cookButton = CookButton(this.leftPos + 97, topPos + 56, menu.containerData.get(IS_LID_OPEN_INDEX) == 0) {
val color = CampfirePotColor.entries[menu.containerData.get(COOKING_POT_COLOR_INDEX).coerceIn(0, CampfirePotColor.entries.lastIndex)]
cookButton = CookButton(this.leftPos + 97, topPos + 56, menu.containerData.get(IS_LID_OPEN_INDEX) == 0, color) {
val isLidClosed = menu.containerData.get(IS_LID_OPEN_INDEX) == 0
sendToServer(ToggleCookingPotLidPacket(isLidClosed))
}
@@ -161,6 +164,7 @@ class CookingPotScreen(
mouseY: Int,
partialTicks: Float
) {
cookButton.color = CampfirePotColor.entries[menu.containerData.get(COOKING_POT_COLOR_INDEX).coerceIn(0, CampfirePotColor.entries.lastIndex)]
cookButton.selected = menu.containerData.get(IS_LID_OPEN_INDEX) == 0
cookButton.setPosition(this.leftPos + 97, topPos + 56)
@@ -0,0 +1,179 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.debug.riding
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.CobblemonNetwork
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.net.messages.server.debug.ServerboundUpdateRidingSettingsPacket
import com.cobblemon.mod.common.net.messages.server.debug.ServerboundUpdateRidingStatsPacket
import com.cobblemon.mod.common.net.serverhandling.debug.ServerboundUpdateRidingSettingsHandler
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.getString
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.resolve
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Button
import net.minecraft.client.gui.components.EditBox
import net.minecraft.client.gui.screens.Screen
import java.awt.Color
/**
* Ride Settings Editor to change the underlying expressions and
* stat mins and maxes of your ride on the fly (pun intended)
*
* Description
*
* @author Jackson
* @since September 13, 2025
*/
class RideSettingsEditorGUI(val parentScreen: Screen, val vehicle: PokemonEntity, val rideStyle: RidingStyle, val rideSettings: RidingBehaviourSettings) : Screen(lang("ui.debug.riding_settings")), CobblemonRenderable {
private val settingWidgets = mutableListOf<Pair<String, EditBox>>()
private val originalValues = mutableMapOf<String, String>() // Used to keep track of saved state of editboxes
override fun init() {
super.init()
clearWidgets()
// Take note of the current saved values in the settings
if (originalValues.isEmpty()) {
val expressionVariables = getExpressionVariables(rideSettings)
for ((name, value) in expressionVariables) {
originalValues[name] = value
}
}
addRenderableWidget(
Button.builder("Back".text()) {
this.minecraft?.setScreen(parentScreen)
}.bounds(10, 10, 40, 20).build()
)
addRenderableWidget(
Button.builder("Save".text()) { button ->
saveRideSettings()
}.bounds(300 + 110, 10, 40, 20).build()
)
createExpressionEditBoxes()
}
override fun render(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int, partialTick: Float) {
// Make the background blank and transparent?
guiGraphics.fill(0, 0, width, height, Color(0, 0, 0, 100).rgb)
super.render(guiGraphics, mouseX, mouseY, partialTick)
for ((name, editBox) in settingWidgets) {
// Position the text next to the EditBox
guiGraphics.drawString(font, name, editBox.x - 105, editBox.y + 5, 0xFFFFFF)
}
}
private fun createExpressionEditBoxes() {
val expressionVariables = getExpressionVariables(rideSettings)
var currentY = 40
for (variable in expressionVariables) {
val (name, value) = variable
val editBox = EditBox(this.minecraft!!.font, 110, currentY, 300, 20, name.text())
editBox.setMaxLength(256)
editBox.value = value
// Dry run expression changes and if valid then turn white and if
// invalid then turn red. If it remains the same it is green
editBox.setResponder { newExpression ->
when {
!validateExpression(newExpression) -> editBox.setTextColor(0xFF5555)
newExpression == originalValues[name] -> editBox.setTextColor(0x55FF55)
else -> editBox.setTextColor(0xFFFFFF)
}
}
// Set all Boxes to green as they are currently saved
editBox.setTextColor(0x55FF55)
// Add widget
addRenderableWidget(editBox)
// Add widget to list
settingWidgets.add(name to editBox)
currentY += 25
}
}
private fun saveRideSettings() {
for ((name, editBox) in settingWidgets) {
// If expression is valid then save and turn green
if (validateExpression(editBox.value)) {
ServerboundUpdateRidingSettingsHandler.modifyRideSettingsExpression(
vehicle,
rideStyle,
name,
editBox.value
)
// Update the running list of last saved values too
originalValues[name] = editBox.value
// Update server with
CobblemonNetwork.sendToServer(
ServerboundUpdateRidingSettingsPacket(
vehicle.id,
rideStyle,
name,
editBox.value
)
)
editBox.setTextColor(0x55FF55)
}
}
}
private fun validateExpression(expression: String): Boolean {
return try {
// Attempt to parse as expression
val parsedExpression = expression.asExpression()
// Dry run to ensure its not borked.
vehicle.runtime.resolve(parsedExpression)
true
} catch (e: Exception) {
false
}
}
private fun getExpressionVariables(settings: RidingBehaviourSettings): List<Pair<String, String>> {
val clazz = settings.javaClass
val fields = clazz.declaredFields
// Iterate through all expression fields and return the list of those that
// aer expressions
return fields.mapNotNull { field ->
if (field.type == Expression::class.java) {
field.isAccessible = true // Make private fields accessible
val name = field.name
// Get the value, cast it, and call .getString() on it
val value = (field.get(settings) as Expression).getString()
// Create the pair and return it for the list
name to value
} else {
// If it's not an Expression (they all honestly should be if being used by the
// behaviour in question) then ignore
null
}
}
}
}
@@ -33,26 +33,28 @@ class RidingStatsDebugGUI(val vehicle: PokemonEntity) : Screen(lang("ui.debug.ri
var ridingStyle: RidingStyle = RidingStyle.LAND
val changeRidingStyle: Button
private lateinit var changeRidingStyle: Button
val speedSlider: SettingsSlider
val accelerationSlider: SettingsSlider
val skillSlider: SettingsSlider
val jumpSlider: SettingsSlider
val staminaSlider: SettingsSlider
private lateinit var speedSlider: SettingsSlider
private lateinit var accelerationSlider: SettingsSlider
private lateinit var skillSlider: SettingsSlider
private lateinit var jumpSlider: SettingsSlider
private lateinit var staminaSlider: SettingsSlider
val minSpeedInput: RidingStatInputWidget
val maxSpeedInput: RidingStatInputWidget
val minAccelerationInput: RidingStatInputWidget
val maxAccelerationInput: RidingStatInputWidget
val minSkillInput: RidingStatInputWidget
val maxSkillInput: RidingStatInputWidget
val minJumpInput: RidingStatInputWidget
val maxJumpInput: RidingStatInputWidget
val minStaminaInput: RidingStatInputWidget
val maxStaminaInput: RidingStatInputWidget
private lateinit var minSpeedInput: RidingStatInputWidget
private lateinit var maxSpeedInput: RidingStatInputWidget
private lateinit var minAccelerationInput: RidingStatInputWidget
private lateinit var maxAccelerationInput: RidingStatInputWidget
private lateinit var minSkillInput: RidingStatInputWidget
private lateinit var maxSkillInput: RidingStatInputWidget
private lateinit var minJumpInput: RidingStatInputWidget
private lateinit var maxJumpInput: RidingStatInputWidget
private lateinit var minStaminaInput: RidingStatInputWidget
private lateinit var maxStaminaInput: RidingStatInputWidget
init {
public override fun init() {
super.init()
clearWidgets()
ridingStyle = vehicle.ifRidingAvailableSupply(RidingStyle.LAND) { behaviour, settings, state ->
behaviour.getRidingStyle(settings, state)
}
@@ -94,6 +96,15 @@ class RidingStatsDebugGUI(val vehicle: PokemonEntity) : Screen(lang("ui.debug.ri
}.bounds(getScaledWidth() - 210, 220, 200, 20).build()
)
addRenderableWidget(
Button.builder("Edit Ride Settings".text()) { button ->
// Grab the ride settings for the currently being configured style
val rideSettings = vehicle.ridingController?.behaviours?.get(ridingStyle) ?: return@builder
// Open up GUI for editing the given settings
this.minecraft?.setScreen(RideSettingsEditorGUI(this, vehicle, ridingStyle, rideSettings))
}.bounds(10, 240, 200, 20).build()
)
refresh()
}
@@ -266,6 +266,7 @@ class PCGUI(
tooltipKey = "ui.sort.${typeName}",
label = "sort_${typeName}"
) {
storageWidget.resetSelected()
SortPCBoxPacket(pc.uuid, storageWidget.box, sortType, hasShiftDown()).sendToServer()
}.also {
it.visible = displayOptions
@@ -719,7 +720,7 @@ class PCGUI(
if (pasture.pastureScrollList.isHovered) pasture.pastureScrollList.mouseScrolled(mouseX, mouseY, amount, verticalAmount)
}
if (storageWidget.isHovered && mouseX < (storageWidget.x + StorageWidget.SCREEN_WIDTH)) this.storageWidget.box -= verticalAmount.toInt()
if (wallpaperWidget.isHovered) wallpaperWidget.mouseScrolled(mouseX, mouseY, amount, verticalAmount)
if (::wallpaperWidget.isInitialized && wallpaperWidget.isHovered) wallpaperWidget.mouseScrolled(mouseX, mouseY, amount, verticalAmount)
val infoBoxX = ((width - BASE_WIDTH) / 2) + 9
val infoBoxY = ((height - BASE_HEIGHT) / 2) + 128
@@ -492,7 +492,7 @@ class StorageWidget(
}
}
private fun resetSelected() {
fun resetSelected() {
selectedPosition = null
grabbedSlot = null
pcGui.setPreviewPokemon(null)
@@ -82,9 +82,6 @@ class PokedexGUI private constructor(
private val arrowUpIcon = cobblemonResource("textures/gui/pokedex/arrow_up.png")
private val arrowDownIcon = cobblemonResource("textures/gui/pokedex/arrow_down.png")
private val tooltipEdge = cobblemonResource("textures/gui/pokedex/tooltip_edge.png")
private val tooltipBackground = cobblemonResource("textures/gui/pokedex/tooltip_background.png")
private val tabSelectArrow = cobblemonResource("textures/gui/pokedex/select_arrow.png")
private val tabIcons = arrayOf(
cobblemonResource("textures/gui/pokedex/tab_info.png"),
@@ -325,17 +322,15 @@ class PokedexGUI private constructor(
// Search type tooltip
if (searchByTypeButton.isButtonHovered(mouseX, mouseY)) {
matrices.pushPose()
matrices.translate(0.0, 0.0, 1000.0)
val searchTypeText = lang("ui.pokedex.search.search_by", lang("ui.pokedex.search.type.${selectedSearchByType.name.lowercase()}")).bold()
val searchTypeTextWidth = Minecraft.getInstance().font.width(searchTypeText.font(CobblemonResources.DEFAULT_LARGE))
val tooltipWidth = searchTypeTextWidth + 6
blitk(matrixStack = matrices, texture = tooltipEdge, x = mouseX - (tooltipWidth / 2) - 1, y = mouseY - 16, width = 1, height = 11)
blitk(matrixStack = matrices, texture = tooltipBackground, x = mouseX - (tooltipWidth / 2), y = mouseY - 16, width = tooltipWidth, height = 11)
blitk(matrixStack = matrices, texture = tooltipEdge, x = mouseX + (tooltipWidth / 2), y = mouseY - 16, width = 1, height = 11)
drawScaledText(context = context, font = CobblemonResources.DEFAULT_LARGE, text = searchTypeText, x = mouseX, y = mouseY - 15, shadow = true, centered = true)
matrices.popPose()
renderTooltip(
context,
searchTypeText,
mouseX,
mouseY,
delta,
-14
)
}
}
@@ -481,6 +476,8 @@ class PokedexGUI private constructor(
fun displaytabInfoElement(tabIndex: Int, update: Boolean = true) {
val showActiveTab = selectedEntry?.let { selectedForm in CobblemonClient.clientPokedexData.getCaughtForms(it) } == true
var statSubIndex = 0
if (tabButtons.isNotEmpty() && tabButtons.size > tabIndex) {
tabButtons.forEachIndexed { index, tab -> tab.isWidgetActive = showActiveTab && index == tabIndex
}
@@ -491,6 +488,14 @@ class PokedexGUI private constructor(
removeWidget((tabInfoElement as AbilitiesWidget).rightButton)
}
if (tabInfoIndex == TAB_STATS && tabInfoElement is StatsWidget) {
statSubIndex = (tabInfoElement as StatsWidget).selectedStatTypeIndex
removeWidget((tabInfoElement as StatsWidget).leftButton)
removeWidget((tabInfoElement as StatsWidget).rightButton)
removeWidget((tabInfoElement as StatsWidget).leftSubButton)
removeWidget((tabInfoElement as StatsWidget).rightSubButton)
}
tabInfoIndex = tabIndex
if (::tabInfoElement.isInitialized) removeWidget(tabInfoElement)
@@ -508,7 +513,9 @@ class PokedexGUI private constructor(
tabInfoElement = SizeWidget(x + 180, y + 135)
}
TAB_STATS -> {
tabInfoElement = StatsWidget(x + 180, y + 135)
tabInfoElement = StatsWidget(x + 180, y + 135).also {
it.selectedStatTypeIndex = statSubIndex
}
}
TAB_DROPS -> {
tabInfoElement = DropsScrollingWidget(x + 189, y + 135)
@@ -555,6 +562,17 @@ class PokedexGUI private constructor(
}
TAB_STATS -> {
(tabInfoElement as StatsWidget).baseStats = form.baseStats
(tabInfoElement as StatsWidget).rideProperties = form.riding
form.riding.behaviours?.let {
addRenderableWidget((tabInfoElement as StatsWidget).leftButton)
addRenderableWidget((tabInfoElement as StatsWidget).rightButton)
if (it.size > 1) {
addRenderableWidget((tabInfoElement as StatsWidget).leftSubButton)
addRenderableWidget((tabInfoElement as StatsWidget).rightSubButton)
}
}
}
TAB_DROPS -> {
(tabInfoElement as DropsScrollingWidget).dropTable = form.drops
@@ -0,0 +1,50 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.pokedex
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.font
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.network.chat.MutableComponent
private val tooltipEdge = cobblemonResource("textures/gui/pokedex/tooltip_edge.png")
private val tooltipBackground = cobblemonResource("textures/gui/pokedex/tooltip_background.png")
fun renderTooltip(context: GuiGraphics, text: MutableComponent, posX: Int, posY: Int, delta: Float, offsetY: Int = 0) {
val poseStack = context.pose()
val textWidth = Minecraft.getInstance().font.width(text.font(CobblemonResources.DEFAULT_LARGE))
val tooltipWidth = textWidth + 6
val tooltipHeight = 11
val tooltipTop = posY + offsetY
poseStack.pushPose()
poseStack.translate(0.0, 0.0, 1000.0)
Minecraft.getInstance().let {
it.mainRenderTarget.bindWrite(false)
context.enableScissor(
posX - (tooltipWidth / 2),
tooltipTop + 1,
(posX - (tooltipWidth / 2)) + tooltipWidth,
tooltipTop + 10
)
it.gameRenderer.processBlurEffect(delta)
context.disableScissor()
it.mainRenderTarget.bindWrite(true)
}
blitk(matrixStack = poseStack, texture = tooltipEdge, x = posX - (tooltipWidth / 2) - 1, y = tooltipTop, width = 1, height = tooltipHeight)
blitk(matrixStack = poseStack, texture = tooltipBackground, x = posX - (tooltipWidth / 2), y = tooltipTop, width = tooltipWidth, height = tooltipHeight)
blitk(matrixStack = poseStack, texture = tooltipEdge, x = posX + (tooltipWidth / 2), y = tooltipTop, width = 1, height = tooltipHeight)
drawScaledText(context = context, font = CobblemonResources.DEFAULT_LARGE, text = text, x = posX, y = tooltipTop + 1, shadow = true, centered = true)
poseStack.popPose()
}
@@ -31,6 +31,7 @@ class ScaledButton(
): Button(buttonX.toInt(), buttonY.toInt(), buttonWidth.toInt(), buttonHeight.toInt(), "".text(), clickAction, DEFAULT_NARRATION), CobblemonRenderable {
var isWidgetActive = false
var isVisible = true
override fun mouseDragged(d: Double, e: Double, i: Int, f: Double, g: Double) = false
override fun defaultButtonNarrationText(builder: NarrationElementOutput) {
@@ -38,8 +39,7 @@ class ScaledButton(
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
val matrices = context.pose()
if (resource != null) {
if (isVisible && resource != null) {
blitk(
matrixStack = matrices,
texture = resource,
@@ -55,14 +55,14 @@ class ScaledButton(
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
if (active && isButtonHovered(mouseX, mouseY)) {
if (isVisible && active && isButtonHovered(mouseX, mouseY)) {
super.mouseClicked(mouseX, mouseY, button)
}
return false
}
override fun playDownSound(soundManager: SoundManager) {
if (active && !this.silent) {
if (isVisible && active && !this.silent) {
soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.POKEDEX_CLICK_SHORT, 1.0F))
}
}
@@ -71,4 +71,4 @@ class ScaledButton(
return mouseX.toFloat() in (buttonX..(buttonX + (buttonWidth.toFloat() * scale)))
&& mouseY.toFloat() in (buttonY..(buttonY + (buttonHeight.toFloat() * scale)))
}
}
}
@@ -48,12 +48,11 @@ class AbilitiesWidget(x: Int, y: Int): InfoTextScrollWidget(pX = x, pY = y) {
init { setAbility() }
private fun switchAbility(nextIndex: Boolean) {
if (nextIndex) {
if (selectedAbilitiesIndex < abilitiesList.lastIndex) selectedAbilitiesIndex++
else selectedAbilitiesIndex = 0
val size = abilitiesList.size
selectedAbilitiesIndex = if (nextIndex) {
(selectedAbilitiesIndex + 1) % size
} else {
if (selectedAbilitiesIndex > 0) selectedAbilitiesIndex--
else selectedAbilitiesIndex = abilitiesList.lastIndex
(selectedAbilitiesIndex - 1 + size) % size
}
setAbility()
}
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.client.gui.pokedex.widgets
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.gui.ParentWidget
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.pokedex.entry.PokedexEntry
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
@@ -33,6 +34,7 @@ import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.PORTRAIT_
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.PORTRAIT_POKE_BALL_WIDTH
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.SCALE
import com.cobblemon.mod.common.client.gui.pokedex.ScaledButton
import com.cobblemon.mod.common.client.gui.pokedex.renderTooltip
import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.render.drawScaledTextJustifiedRight
@@ -94,9 +96,6 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
private val buttonGenderFemale = cobblemonResource("textures/gui/pokedex/button_female.png")
private val buttonNone = cobblemonResource("textures/gui/pokedex/button_none.png")
private val buttonShiny = cobblemonResource("textures/gui/pokedex/button_shiny.png")
private val tooltipEdge = cobblemonResource("textures/gui/pokedex/tooltip_edge.png")
private val tooltipBackground = cobblemonResource("textures/gui/pokedex/tooltip_background.png")
}
var currentEntry : PokedexEntry? = null
@@ -440,13 +439,14 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
// Tooltip
if (it.isVisible() && it.getWidget().isButtonHovered(mouseX, mouseY)) {
val variationText = it.variation.displayName.asTranslated().bold()
val variationTextWidth = Minecraft.getInstance().font.width(variationText.font(CobblemonResources.DEFAULT_LARGE))
val tooltipWidth = variationTextWidth + 6
blitk(matrixStack = matrices, texture = tooltipEdge, x = mouseX - (tooltipWidth / 2) - 1, y = mouseY + 8, width = 1, height = 11)
blitk(matrixStack = matrices, texture = tooltipBackground, x = mouseX - (tooltipWidth / 2), y = mouseY + 8, width = tooltipWidth, height = 11)
blitk(matrixStack = matrices, texture = tooltipEdge, x = mouseX + (tooltipWidth / 2), y = mouseY + 8, width = 1, height = 11)
drawScaledText(context = context, font = CobblemonResources.DEFAULT_LARGE, text = variationText, x = mouseX, y = mouseY + 9, shadow = true, centered = true)
renderTooltip(
context,
variationText,
mouseX,
mouseY,
delta,
10
)
}
}
@@ -11,18 +11,24 @@ package com.cobblemon.mod.common.client.gui.pokedex.widgets
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.riding.RidingProperties
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.HALF_OVERLAY_WIDTH
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.POKEMON_DESCRIPTION_HEIGHT
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.SCALE
import com.cobblemon.mod.common.client.gui.pokedex.ScaledButton
import com.cobblemon.mod.common.client.gui.pokedex.renderTooltip
import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.render.drawScaledTextJustifiedRight
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.network.chat.Component
import net.minecraft.util.FastColor
class StatsWidget(val pX: Int, val pY: Int) : SoundlessWidget(
pX,
@@ -33,6 +39,18 @@ class StatsWidget(val pX: Int, val pY: Int) : SoundlessWidget(
) {
companion object {
const val STAT_TYPE_BASE = 0
const val STAT_TYPE_RIDE = 1
const val MAX_BAR_WIDTH = 75
private val statTypes = listOf(STAT_TYPE_BASE, STAT_TYPE_RIDE)
private val divider = cobblemonResource("textures/gui/pokedex/info_stat_divider.png")
private val arrowLeft = cobblemonResource("textures/gui/pokedex/info_arrow_left.png")
private val arrowRight = cobblemonResource("textures/gui/pokedex/info_arrow_right.png")
private val subArrowLeft = cobblemonResource("textures/gui/pokedex/info_sub_arrow_left.png")
private val subArrowRight = cobblemonResource("textures/gui/pokedex/info_sub_arrow_right.png")
private val statLabels = arrayOf(
lang("ui.stats.hp"),
lang("ui.stats.atk"),
@@ -52,12 +70,91 @@ class StatsWidget(val pX: Int, val pY: Int) : SoundlessWidget(
)
}
val leftButton: ScaledButton = ScaledButton(
pX + 2.5F,
pY - 8F,
7,
10,
arrowLeft,
clickAction = { switchStatType(false) }
)
val rightButton: ScaledButton = ScaledButton(
pX + 133F,
pY - 8F,
7,
10,
arrowRight,
clickAction = { switchStatType(true) }
)
val leftSubButton: ScaledButton = ScaledButton(
pX + 2.5F,
pY + 18.5F,
7,
10,
subArrowLeft,
clickAction = { switchRideBehaviour(false) }
)
val rightSubButton: ScaledButton = ScaledButton(
pX + 133F,
pY + 18.5F,
7,
10,
subArrowRight,
clickAction = { switchRideBehaviour(true) }
)
var selectedRideBehavioursIndex: Int = 0
var selectedStatTypeIndex: Int = STAT_TYPE_BASE
set(value) {
field = value
val isRideStatVisible = value == STAT_TYPE_RIDE
leftSubButton.visible = isRideStatVisible
rightSubButton.visible = isRideStatVisible
}
var baseStats: Map<Stat, Int>? = null
var rideProperties: RidingProperties? = null
private fun switchStatType(nextIndex: Boolean) {
val size = statTypes.size
selectedStatTypeIndex = if (nextIndex) {
(selectedStatTypeIndex + 1) % size
} else {
(selectedStatTypeIndex - 1 + size) % size
}
val showSubArrowButtons = selectedStatTypeIndex == STAT_TYPE_RIDE
leftSubButton.visible = showSubArrowButtons
rightSubButton.visible = showSubArrowButtons
}
private fun switchRideBehaviour(nextIndex: Boolean) {
val size = rideProperties?.behaviours?.size ?: 0
selectedRideBehavioursIndex = if (nextIndex) {
(selectedRideBehavioursIndex + 1) % size
} else {
(selectedRideBehavioursIndex - 1 + size) % size
}
}
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
if (baseStats == null) return
when (selectedStatTypeIndex) {
STAT_TYPE_BASE -> {
renderBaseStats(context)
}
STAT_TYPE_RIDE -> {
renderRideStats(context, mouseX, mouseY, delta)
}
}
}
val matrices = context.pose()
private fun renderBaseStats(context: GuiGraphics) {
if (baseStats == null) return
drawScaledText(
context = context,
@@ -68,18 +165,9 @@ class StatsWidget(val pX: Int, val pY: Int) : SoundlessWidget(
shadow = true
)
// Divider
blitk(
matrixStack = matrices,
texture = CobblemonResources.WHITE,
x = pX + 50.5,
y = pY + 4,
height = 33.5,
width = 1,
red = 0.71F,
green = 0.95F,
blue = 0.95F
)
val matrices = context.pose()
blitk(matrixStack = matrices, texture = divider, x = pX + 51, y = pY + 4, height = 33.5, width = 1)
for (i in statLabels.indices) {
drawScaledText(
@@ -107,10 +195,10 @@ class StatsWidget(val pX: Int, val pY: Int) : SoundlessWidget(
blitk(
matrixStack = matrices,
texture = CobblemonResources.WHITE,
x = pX + 54.5,
x = pX + 55,
y = pY + 4 + (6 * i),
height = 3.5F,
width = (statValue / 255F) * 77,
width = (statValue / 255F) * MAX_BAR_WIDTH,
red = red,
green = green,
blue = blue
@@ -119,6 +207,119 @@ class StatsWidget(val pX: Int, val pY: Int) : SoundlessWidget(
}
}
private fun renderRideStats(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
rideProperties?.behaviours?.let { behaviours ->
val selectedBehaviour = behaviours.entries.toList()[selectedRideBehavioursIndex]
val behaviourStats = selectedBehaviour.value.stats
val matrices = context.pose()
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = Component.translatable("cobblemon.ui.pokedex.info.stats_ride").bold(),
x = pX + 9,
y = pY - 10,
shadow = true
)
rideProperties?.seats?.size?.let { seats ->
drawScaledTextJustifiedRight(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = Component.translatable("cobblemon.ui.pokedex.info.stats_ride.seats", seats).bold(),
x = pX + 130,
y = pY - 10,
shadow = true
)
}
blitk(matrixStack = matrices, texture = divider, x = pX + 51, y = pY + 10, height = 27.5, width = 1)
blitk(matrixStack = matrices, texture = divider, x = pX + 9, y = pY + 2, height = 6, width = 121)
drawScaledText(
context = context,
text = lang("ui.ride_style.${selectedBehaviour.key.name.lowercase()}").bold(),
x = pX + 69.5,
y = pY + 3,
colour = 0x3A96B6,
scale = SCALE,
centered = true
)
RidingStat.entries.forEachIndexed { index, stat ->
drawScaledText(
context = context,
text = lang("ui.stats.ride.${stat.name.lowercase()}").bold(),
x = pX + 9,
y = pY + 10 + (6 * index),
colour = 0x606B6E,
scale = SCALE
)
behaviourStats[stat]?.let { range ->
val startPosY = pY + 10 + (6F * index)
val red = FastColor.ARGB32.red(stat.flavour.colour) / 255F
val green = FastColor.ARGB32.green(stat.flavour.colour) / 255F
val blue = FastColor.ARGB32.blue(stat.flavour.colour) / 255F
val barStart = pX + 55F
val baseStatWidth = (range.start / 100F) * MAX_BAR_WIDTH
val boostedStatWidth = ((range.endInclusive / 100F) * MAX_BAR_WIDTH) - baseStatWidth
blitk(
matrixStack = matrices,
texture = CobblemonResources.WHITE,
x = barStart,
y = startPosY,
height = 3.5F,
width = baseStatWidth,
red = red,
green = green,
blue = blue
)
val isBoostedStatHovered = mouseX.toFloat() in ((barStart + baseStatWidth)..(barStart + baseStatWidth + boostedStatWidth)) && mouseY.toFloat() in (startPosY..(startPosY + 3.5F))
drawScaledTextJustifiedRight(
context = context,
text = if (isBoostedStatHovered) range.endInclusive.toString().text().bold() else range.start.toString().text(),
x = pX + 48,
y = startPosY,
colour = 0x3A96B6,
scale = SCALE
)
// Boostable range
blitk(
matrixStack = matrices,
texture = CobblemonResources.WHITE,
x = barStart + baseStatWidth,
y = startPosY,
height = 3.5F,
width = boostedStatWidth,
red = red,
green = green,
blue = blue,
alpha = if (isBoostedStatHovered) 1F else 0.5F
)
if (isBoostedStatHovered) renderTooltip(
context,
lang("ui.stats.ride.boost_maximum").bold(),
mouseX,
mouseY,
delta,
-16
)
}
}
} ?: run {
// If ride properties null, reset index to base stats
selectedStatTypeIndex = STAT_TYPE_BASE
}
}
private fun getStatValueRGB(value: Int): Triple<Float, Float, Float> {
when {
value >= 150 -> return Triple(0.647F, 0.929F, 0.647F)
@@ -128,4 +329,4 @@ class StatsWidget(val pX: Int, val pY: Int) : SoundlessWidget(
}
return Triple(0.964F, 0.64F, 0.502F)
}
}
}
@@ -53,7 +53,7 @@ class SummaryButton(
override fun defaultButtonNarrationText(builder: NarrationElementOutput) {
}
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
public override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
if (!this.renderRequirement.invoke(this)) {
return
}
@@ -28,83 +28,91 @@ import net.minecraft.world.phys.Vec3
* @author Hiroku
* @since November 13th, 2023
*/
class BarSummarySpeciesFeatureRenderer(
open class BarSummarySpeciesFeatureRenderer(
override val name: String,
val displayName: MutableComponent,
val min: Int,
val max: Int,
val colour: Vec3,
val underlay: ResourceLocation,
val overlay: ResourceLocation,
val pokemon: Pokemon
val pokemon: Pokemon,
val min: Int,
val max: Int,
val value: Int = 0,
val colour: Vec3? = null
) : SummarySpeciesFeatureRenderer<IntSpeciesFeature> {
override fun render(GuiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon, feature: IntSpeciesFeature) {
val value = feature.value
val barRatio = (value - min) / (max - min).toFloat()
val barWidth = Mth.ceil(barRatio * 108)
override fun render(guiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon, feature: IntSpeciesFeature) {
renderElement(guiGraphics, x, y, pokemon, feature.value)
}
open fun renderElement(guiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon, barValue: Int = value) {
val barRatio = (barValue - min) / (max - min).toFloat()
val barWidth = Mth.ceil(barRatio * 110)
blitk(
matrixStack = GuiGraphics.pose(),
matrixStack = guiGraphics.pose(),
texture = underlay,
x = x,
y = y,
height = 28,
width = 124
height = 24,
width = 116
)
val red = colour.x / 255F
val green = colour.y / 255F
val blue = colour.z / 255F
renderBar(guiGraphics, x, y, barValue, barRatio, barWidth)
blitk(
matrixStack = GuiGraphics.pose(),
texture = CobblemonResources.WHITE,
x = x + 8,
y = y + 16,
height = 10,
width = barWidth,
red = red,
green = green,
blue = blue
)
blitk(
matrixStack = GuiGraphics.pose(),
matrixStack = guiGraphics.pose(),
texture = overlay,
x = x / StatWidget.SCALE,
y = (y + 16) / StatWidget.SCALE,
x = (x + 3) / StatWidget.SCALE,
y = (y + 13) / StatWidget.SCALE,
height = 20,
width = 248,
width = 220,
scale = StatWidget.SCALE
)
// Label
drawScaledText(
context = GuiGraphics,
context = guiGraphics,
font = CobblemonResources.DEFAULT_LARGE,
text = displayName.bold(),
x = x + 62,
x = x + 58,
y = y + 2.5,
centered = true,
shadow = true
)
drawScaledText(
context = GuiGraphics,
text = value.toString().text(),
x = x + 11,
context = guiGraphics,
text = barValue.toString().text(),
x = x + 9,
y = y + 6,
scale = StatWidget.SCALE,
centered = true
)
drawScaledText(
context = GuiGraphics,
context = guiGraphics,
text = "${Mth.floor(barRatio * 100)}%".text(),
x = x + 113,
x = x + 107,
y = y + 6,
scale = StatWidget.SCALE,
centered = true
)
}
}
open fun renderBar(guiGraphics: GuiGraphics, x: Float, y: Float, barValue: Int, barRatio: Float, barWidth: Int) {
val red = (colour?.x ?: 255.0) / 255.0
val green = (colour?.y ?: 255.0) / 255.0
val blue = (colour?.z ?: 255.0) / 255.0
blitk(
matrixStack = guiGraphics.pose(),
texture = CobblemonResources.WHITE,
x = x + 3,
y = y + 13,
height = 10,
width = barWidth,
red = red,
green = green,
blue = blue
)
}
}
@@ -23,10 +23,10 @@ interface SummarySpeciesFeatureRenderer<T : SynchronizedSpeciesFeature> {
/** The name of the feature (so we know where to look to find it in the [Pokemon]) */
val name: String
/** Draws it at a particular position. */
fun render(GuiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon, feature: T)
fun render(GuiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon): Boolean {
fun render(guiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon, feature: T)
fun render(guiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon): Boolean {
val feature = pokemon.getFeature<T>(name) ?: return false
render(GuiGraphics, x, y, pokemon, feature)
render(guiGraphics, x, y, pokemon, feature)
return true
}
}
}
@@ -14,12 +14,16 @@ import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
import com.cobblemon.mod.common.api.pokemon.feature.SynchronizedSpeciesFeatureProvider
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.api.text.italicise
import com.cobblemon.mod.common.api.text.onHover
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.summary.SummaryButton
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.BarSummarySpeciesFeatureRenderer
import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.stats.features.FriendshipFeatureRenderer
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.stats.features.FullnessFeatureRenderer
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.cobblemonResource
@@ -39,8 +43,6 @@ import net.minecraft.util.Mth.ceil
import net.minecraft.util.Mth.floor
import net.minecraft.world.phys.Vec2
import org.joml.Vector3f
import kotlin.math.cos
import kotlin.math.sin
class StatWidget(
pX: Int, pY: Int,
@@ -51,63 +53,129 @@ class StatWidget(
companion object {
// Stat tab options
private const val STATS = "stats"
private const val IV = "iv"
private const val EV = "ev"
private const val IV = "ivs"
private const val EV = "evs"
private const val RIDE = "ride"
private const val OTHER = "other"
private val statOptions = listOf(STATS, IV, EV, OTHER)
private const val statTabWidth = 24
private const val OTHER_STAT_BARS_PER_PAGE = 4
private const val STAT_TAB_WIDTH_6 = 22
private const val STAT_TAB_WIDTH_5 = 24
private const val MARKER_WIDTH = 8
private const val WIDTH = 134
private const val HEIGHT = 148
const val SCALE = 0.5F
private const val WHITE = 0x00FFFFFF
private const val GREY = 0x00AAAAAA
private const val GREY = 0x008F8F8F
private const val BLUE = 0x00548BFB
private const val RED = 0x00FB5454
private val statsBaseResource = cobblemonResource("textures/gui/summary/summary_stats_chart_base.png")
private val statsChartResource = cobblemonResource("textures/gui/summary/summary_stats_chart.png")
private val statsChartPentagonResource = cobblemonResource("textures/gui/summary/summary_stats_chart_pentagon.png")
private val statsOtherBaseResource = cobblemonResource("textures/gui/summary/summary_stats_other_base.png")
private val statsOtherBarTemplate = cobblemonResource("textures/gui/summary/summary_stats_other_bar.png")
private val friendshipOverlayResource = cobblemonResource("textures/gui/summary/summary_stats_friendship_overlay.png")
private val fullnessOverlayResource = cobblemonResource("textures/gui/summary/summary_stats_fullness_overlay.png")
private val statsOtherSidebarsResource = cobblemonResource("textures/gui/summary/summary_stats_other_sidebars.png")
private val statsOtherSidebarArrowLeft = cobblemonResource("textures/gui/summary/summary_stats_other_sidebar_arrow_left.png")
private val statsOtherSidebarArrowRight = cobblemonResource("textures/gui/summary/summary_stats_other_sidebar_arrow_right.png")
private val tabMarkerResource = cobblemonResource("textures/gui/summary/summary_stats_tab_marker.png")
private val statIncreaseResource = cobblemonResource("textures/gui/summary/summary_stats_icon_increase.png")
private val statDecreaseResource = cobblemonResource("textures/gui/summary/summary_stats_icon_decrease.png")
private val statsLabel = lang("ui.stats")
private val ivLabel = lang("ui.stats.ivs")
private val evLabel = lang("ui.stats.evs")
private val otherLabel = lang("ui.stats.other")
private val statLabels: Map<MutableComponent, Stats> = mapOf(
lang("ui.stats.hp").bold() to Stats.HP,
lang("ui.stats.atk").bold() to Stats.ATTACK,
lang("ui.stats.def").bold() to Stats.DEFENCE,
lang("ui.stats.speed").bold() to Stats.SPEED,
lang("ui.stats.sp_def").bold() to Stats.SPECIAL_DEFENCE,
lang("ui.stats.sp_atk").bold() to Stats.SPECIAL_ATTACK
)
private val hpLabel = lang("ui.stats.hp")
private val spAtkLabel = lang("ui.stats.sp_atk")
private val atkLabel = lang("ui.stats.atk")
private val spDefLabel = lang("ui.stats.sp_def")
private val defLabel = lang("ui.stats.def")
private val speedLabel = lang("ui.stats.speed")
private val hexagonVerticesOffset = listOf(
Pair(67.0, 10.5), // 12 o'clock
Pair(122.0, 42.5), // 2 o'clock
Pair(122.0, 93.5), // 4 o'clock
Pair(67.0, 124.5), // 6 o'clock
Pair(12.0, 93.5), // 8 o'clock
Pair(12.0, 42.5) // 10 o'clock
)
private val pentagonVerticesOffset = listOf(
Pair(67.0, 10.5), // 12 o'clock
Pair(123.0, 47.5), // 2 o'clock
Pair(103.0, 112.5), // 5 o'clock
Pair(31.0, 112.5), // 7 o'clock
Pair(11.0, 47.5), // 10 o'clock
)
}
var effectiveBattleIVs: Map<Stat, Int> = Stats.PERMANENT.associateWith { stat ->
pokemon.ivs.getEffectiveBattleIV(stat)
}
private val statOptions = if (pokemon.form.riding.behaviours != null) listOf(STATS, IV, EV, RIDE, OTHER) else listOf(STATS, IV, EV, OTHER)
private var _statTabIndex: Int = tabIndex
var statTabIndex: Int
get() = _statTabIndex.coerceIn(0, statOptions.size - 1)
set(value) {
_statTabIndex = value.coerceIn(0, statOptions.size - 1)
}
private var _rideBehaviourIndex: Int = 0
var rideBehaviourIndex: Int
get() = _rideBehaviourIndex.coerceIn(0, (pokemon.form.riding.behaviours?.size ?: 1) - 1)
set(value) {
_rideBehaviourIndex = value.coerceIn(0, (pokemon.form.riding.behaviours?.size ?: 1) - 1)
}
val universalFeatures = listOf(
FriendshipFeatureRenderer(pokemon),
FullnessFeatureRenderer(pokemon)
)
var statTabIndex = tabIndex
val renderableFeatures = SpeciesFeatures
.getFeaturesFor(pokemon.species)
.filterIsInstance<SynchronizedSpeciesFeatureProvider<*>>()
.mapNotNull { it.getRenderer(pokemon) }
private fun drawTriangle(
colour: Vector3f,
v1: Vec2,
v2: Vec2,
v3: Vec2
) {
var otherStatsPageIndex: Int = 0
var otherStatLeftButton: SummaryButton? = null
var otherStatRightButton: SummaryButton? = null
init {
if (renderableFeatures.size + universalFeatures.size > OTHER_STAT_BARS_PER_PAGE) {
otherStatLeftButton = SummaryButton(
x - 1F,
y + 67F,
14,
14,
resource = statsOtherSidebarArrowLeft,
scale = SCALE,
clickAction = { switchOtherStatsPage(false) }
).also { addWidget(it) }
otherStatRightButton = SummaryButton(
x + 128F,
y + 67F,
14,
14,
resource = statsOtherSidebarArrowRight,
scale = SCALE,
clickAction = { switchOtherStatsPage(true) }
).also { addWidget(it) }
}
}
private fun switchOtherStatsPage(nextIndex: Boolean) {
val pages = ceil((renderableFeatures.size + universalFeatures.size).toDouble() / OTHER_STAT_BARS_PER_PAGE.toDouble())
otherStatsPageIndex = if (nextIndex) {
(otherStatsPageIndex + 1) % pages
} else {
(otherStatsPageIndex - 1 + pages) % pages
}
}
private fun drawTriangle(colour: Vector3f, v1: Vec2, v2: Vec2, v3: Vec2, opacity: Float = 0.6F) {
CobblemonResources.WHITE.let { RenderSystem.setShaderTexture(0, it) }
RenderSystem.setShaderColor(colour.x, colour.y, colour.z, 0.6F)
RenderSystem.setShaderColor(colour.x, colour.y, colour.z, opacity)
val bufferBuilder = Tesselator.getInstance().begin(VertexFormat.Mode.TRIANGLES, DefaultVertexFormat.POSITION)
bufferBuilder.addVertex(v1.x, v1.y, 10F)
bufferBuilder.addVertex(v2.x, v2.y, 10F)
@@ -116,396 +184,326 @@ class StatWidget(
RenderSystem.setShaderColor(1F, 1F, 1F, 1F)
}
private fun drawStatHexagon(stats: Map<Stat, Int>, colour: Vector3f, maximum: Int) {
val hexLeftX = x + 25.5
val hexTopY = y + 22
val hexAttackY = hexTopY + 24.5
val hexDefenceY = hexAttackY + 47.0
val hexBottomY = hexDefenceY + 24.5
val hexRightX = x + 108.5
val hexCenterX = (hexLeftX + hexRightX) / 2
val hexCenterY = (hexTopY + hexBottomY) / 2
val minTriangleSize = 8F
val minXTriangleLen = sin(Math.toRadians(61.0)).toFloat() * minTriangleSize * 0.95F
val minYTriangleLen = cos(Math.toRadians(60.0)).toFloat() * minTriangleSize
/**
* Render a 5 or 6 sided polygon
*
* @param ratios list of ratios for how far each vertex is from the center, starting from top vertex going clockwise
* @param colour the colour to render
*/
private fun drawStatPolygon(ratios: List<Float>, colour: Vector3f) {
val sides = ratios.size
if (sides !in 5 .. 6) return
val triangleLongEdge = (hexCenterY - hexTopY - minTriangleSize).toFloat()
val triangleMediumEdge = (triangleLongEdge * sin(Math.toRadians(61.0))).toFloat()
val triangleShortEdge = (triangleLongEdge * cos(Math.toRadians(61.0))).toFloat()
val topVertexY = y + 22F
val centerX = x + 67F
val centerY = topVertexY + if (sides > 5) 48F else 49F
val hpRatio = (stats.getOrDefault(Stats.HP, 0).toFloat() / maximum).coerceIn(0F, 1F)
val atkRatio = (stats.getOrDefault(Stats.ATTACK, 0).toFloat() / maximum).coerceIn(0F, 1F)
val defRatio = (stats.getOrDefault(Stats.DEFENCE, 0).toFloat() / maximum).coerceIn(0F, 1F)
val spAtkRatio = (stats.getOrDefault(Stats.SPECIAL_ATTACK, 0).toFloat() / maximum).coerceIn(0F, 1F)
val spDefRatio = (stats.getOrDefault(Stats.SPECIAL_DEFENCE, 0).toFloat() / maximum).coerceIn(0F, 1F)
val spdRatio = (stats.getOrDefault(Stats.SPEED, 0).toFloat() / maximum).coerceIn(0F, 1F)
val radius = centerY - topVertexY
val hpPoint = Vec2(
hexCenterX.toFloat(),
hexCenterY.toFloat() - minTriangleSize - hpRatio * triangleLongEdge
)
val startAngleDeg = -90.0
val angleStepDeg = 360.0 / sides
val attackPoint = Vec2(
hexCenterX.toFloat() + minXTriangleLen + atkRatio * triangleMediumEdge,
hexCenterY.toFloat() - minYTriangleLen - atkRatio * triangleShortEdge
)
// Generate the angles for each vertex
val anglesDeg = List(sides) { i -> startAngleDeg + i * angleStepDeg }
val anglesRad = anglesDeg.map { Math.toRadians(it) }
val defencePoint = Vec2(
hexCenterX.toFloat() + minXTriangleLen + defRatio * triangleMediumEdge,
hexCenterY.toFloat() + minYTriangleLen + defRatio * triangleShortEdge
)
val specialAttackPoint = Vec2(
hexCenterX.toFloat() - minXTriangleLen - spAtkRatio * triangleMediumEdge,
hexCenterY.toFloat() - minYTriangleLen - spAtkRatio * triangleShortEdge
)
val specialDefencePoint = Vec2(
hexCenterX.toFloat() - minXTriangleLen - spDefRatio * triangleMediumEdge,
hexCenterY.toFloat() + minYTriangleLen + spDefRatio * triangleShortEdge
)
val speedPoint = Vec2(
hexCenterX.toFloat(),
hexCenterY.toFloat() + minTriangleSize + spdRatio * triangleLongEdge
)
val centerPoint = Vec2(
hexCenterX.toFloat(),
hexCenterY.toFloat()
)
// 1-o'clock
drawTriangle(colour, hpPoint, centerPoint, attackPoint)
// 3-o'clock
drawTriangle(colour, attackPoint, centerPoint, defencePoint)
// 5-o'clock
drawTriangle(colour, defencePoint, centerPoint, speedPoint)
// 7-o'clock
drawTriangle(colour, speedPoint, centerPoint, specialDefencePoint)
// 9-o'clock
drawTriangle(colour, specialDefencePoint, centerPoint, specialAttackPoint)
// 11-o'clock
drawTriangle(colour, specialAttackPoint, centerPoint, hpPoint)
}
private fun drawFriendship(moduleX: Int, moduleY: Int, matrices: PoseStack, context: GuiGraphics, friendship: Int) {
val barRatio = friendship / 255F
val barWidth = ceil(barRatio * 108)
blitk(
matrixStack = matrices,
texture = statsOtherBarTemplate,
x = moduleX,
y = moduleY,
height = 28,
width = 124
)
val red = 1
val green: Number = if (pokemon.friendship >= 160) 0.28 else 0.56
val blue: Number = if (pokemon.friendship >= 160) 0.4 else 0.64
blitk(
matrixStack = matrices,
texture = CobblemonResources.WHITE,
x = moduleX + 8,
y = moduleY + 18,
height = 8,
width = barWidth,
red = red,
green = green,
blue = blue
)
blitk(
matrixStack = matrices,
texture = friendshipOverlayResource,
x = moduleX / SCALE,
y = (moduleY + 16) / SCALE,
height = 20,
width = 248,
scale = SCALE
)
// Label
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = lang("ui.stats.friendship").bold(),
x = moduleX + 62,
y = moduleY + 2.5,
centered = true,
shadow = true
)
drawScaledText(
context = context,
text = friendship.toString().text(),
x = moduleX + 11,
y = moduleY + 6,
scale = SCALE,
centered = true
)
drawScaledText(
context = context,
text = "${floor(barRatio * 100)}%".text(),
x = moduleX + 113,
y = moduleY + 6,
scale = SCALE,
centered = true
)
}
private fun drawFullness(moduleX: Int, moduleY: Int, matrices: PoseStack, context: GuiGraphics, pokemon: Pokemon) {
val currentFullness = pokemon.currentFullness
val maxFullness = pokemon.getMaxFullness()
val barRatio = currentFullness.toFloat() / maxFullness.toFloat()
val barWidth = ceil(barRatio * 108)
blitk(
matrixStack = matrices,
texture = statsOtherBarTemplate,
x = moduleX,
y = moduleY,
height = 28,
width = 124
)
val (red, green, blue) = when {
barRatio <= 0.33 -> Triple(0f, 1f, 0f) // Green (full green)
barRatio <= 0.66 -> Triple(1f, 1f, 0f) // Yellow (red + green)
else -> Triple(1f, 0f, 0f) // Red (full red)
// Calculate maximum points on the circle
val maxPoints = anglesRad.map { angle ->
Vec2(
centerX + radius * Math.cos(angle).toFloat(),
centerY + radius * Math.sin(angle).toFloat()
)
}
blitk(
matrixStack = matrices,
texture = CobblemonResources.WHITE,
x = moduleX + 8,
y = moduleY + 18,
height = 8,
width = barWidth,
red = red,
green = green,
blue = blue
)
// Coerce values as 0.0 will prevent triangles from rendering
val coercedRatios = ratios.map { it.coerceIn(5 / radius, 1F) }
// Draw the overlay
blitk(
matrixStack = matrices,
texture = fullnessOverlayResource,
x = moduleX / SCALE,
y = (moduleY + 16) / SCALE,
height = 20,
width = 248,
scale = SCALE
)
// Interpolate vertices based on ratios
val vertices = maxPoints.mapIndexed { index, maxPoint ->
Vec2(
centerX + (maxPoint.x - centerX) * coercedRatios[index],
centerY + (maxPoint.y - centerY) * coercedRatios[index]
)
}
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = "Fullness".text().bold(),
x = moduleX + 62,
y = moduleY + 2.5,
centered = true,
shadow = true
)
val centerPoint = Vec2(centerX, centerY)
drawScaledText(
context = context,
text = currentFullness.toString().text(),
x = moduleX + 11,
y = moduleY + 6,
scale = SCALE,
centered = true
)
drawScaledText(
context = context,
text = "${floor(barRatio * 100)}%".text(),
x = moduleX + 113,
y = moduleY + 6,
scale = SCALE,
centered = true
)
// Draw triangles between each vertex and the next clockwise starting from top vertex
RenderSystem.disableDepthTest()
for (i in vertices.indices) {
val nextIndex = (i + 1) % vertices.size
drawTriangle(colour, vertices[i], centerPoint, vertices[nextIndex])
}
RenderSystem.enableDepthTest()
}
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
val renderChart = statOptions.get(statTabIndex) != OTHER
val renderOtherStats = statOptions.get(statTabIndex) == OTHER
val renderPentagonStats = statOptions.get(statTabIndex) == RIDE
val matrices = context.pose()
// Background
blitk(
matrixStack = matrices,
texture = if (renderChart) statsBaseResource else statsOtherBaseResource,
texture = if (renderOtherStats) statsOtherBaseResource else statsBaseResource,
x= x,
y = y,
width = width,
height = height
)
// Chart
if (renderChart) {
blitk(
matrixStack = matrices,
texture = statsChartResource,
x= (x + 25.5) / SCALE,
y = (y + 22) / SCALE,
width = 166,
height = 192,
scale = SCALE
)
}
when (statOptions.get(statTabIndex)) {
STATS -> drawStatHexagon(
mapOf(
Stats.HP to pokemon.maxHealth,
Stats.ATTACK to pokemon.attack,
Stats.DEFENCE to pokemon.defence,
Stats.SPECIAL_ATTACK to pokemon.specialAttack,
Stats.SPECIAL_DEFENCE to pokemon.specialDefence,
Stats.SPEED to pokemon.speed
),
colour = Vector3f(50F/255, 215F/255F, 1F),
maximum = 400
)
IV -> drawStatHexagon(
effectiveBattleIVs,
colour = Vector3f(216F/255, 100F/255, 1F),
maximum = 31
)
EV -> drawStatHexagon(
pokemon.evs.associate { it.key to it.value },
colour = Vector3f(1F, 1F, 100F/255),
maximum = 252
)
}
if (renderOtherStats) {
val barOffsetY = 29
val barPosX = x + 9F
var drawY = y + 15F
drawScaledText(
context = context,
text = statsLabel.bold(),
x = x + 31,
y = y + 143,
scale = SCALE,
colour = if (statOptions.get(statTabIndex) == STATS) WHITE else GREY,
centered = true
)
val featuresList: MutableList<Any> = renderableFeatures.toMutableList()
featuresList.addAll(0, universalFeatures)
drawScaledText(
context = context,
text = ivLabel.bold(),
x = x + 55,
y = y + 143,
scale = SCALE,
colour = if (statOptions.get(statTabIndex) == IV) WHITE else GREY,
centered = true
)
drawScaledText(
context = context,
text = evLabel.bold(),
x = x + 79,
y = y + 143,
scale = SCALE,
colour = if (statOptions.get(statTabIndex) == EV) WHITE else GREY,
centered = true
)
drawScaledText(
context = context,
text = otherLabel.bold(),
x = x + 103,
y = y + 143,
scale = SCALE,
colour = if (statOptions.get(statTabIndex) == OTHER) WHITE else GREY,
centered = true
)
val paddingLeft = (WIDTH - ((statOptions.size + 1) * statTabWidth)) / 2
blitk(
matrixStack = context.pose(),
texture = tabMarkerResource,
x= ((x + paddingLeft + ((statTabIndex + 1) * statTabWidth)) / SCALE) - 2,
y = (y + 140) / SCALE,
width = 8,
height = 4,
scale = SCALE,
)
if (renderChart) {
// Stat Labels
renderTextAtVertices(
context = context,
hp = hpLabel.bold(),
spAtk = spAtkLabel.bold(),
atk = atkLabel.bold(),
spDef = spDefLabel.bold(),
def = defLabel.bold(),
speed = speedLabel.bold()
val pageFeatures = featuresList.subList(
otherStatsPageIndex * OTHER_STAT_BARS_PER_PAGE,
minOf((otherStatsPageIndex * OTHER_STAT_BARS_PER_PAGE) + OTHER_STAT_BARS_PER_PAGE, featuresList.size)
)
// Stat Values
renderTextAtVertices(
context = context,
offsetY = 5.5,
enableColour = false,
hp = getStatValueAsText(Stats.HP),
spAtk = getStatValueAsText(Stats.SPECIAL_ATTACK),
atk = getStatValueAsText(Stats.ATTACK),
spDef = getStatValueAsText(Stats.SPECIAL_DEFENCE),
def = getStatValueAsText(Stats.DEFENCE),
speed = getStatValueAsText(Stats.SPEED)
)
// Nature-modified Stat Icons
if (statOptions.get(statTabIndex) == STATS) {
val nature = pokemon.effectiveNature
renderModifiedStatIcon(matrices, nature.increasedStat, true)
renderModifiedStatIcon(matrices, nature.decreasedStat, false)
for (feature in pageFeatures) {
if (feature is BarSummarySpeciesFeatureRenderer) {
val rendered = feature.render(
guiGraphics = context,
x = barPosX,
y = drawY,
pokemon = pokemon
)
if (rendered) drawY += barOffsetY
}
}
if (featuresList.size > 4) {
blitk(
matrixStack = matrices,
texture = statsOtherSidebarsResource,
x= x,
y = y + 13,
width = 134,
height = 115
)
otherStatLeftButton?.renderWidget(context, pMouseX, pMouseY, pPartialTicks)
otherStatRightButton?.renderWidget(context, pMouseX, pMouseY, pPartialTicks)
}
} else {
var drawY = y + 11
drawFriendship(x + 5, drawY, matrices, context, pokemon.friendship)
drawY += 30
drawFullness(x + 5, drawY, matrices, context, pokemon)
drawY += 30
for (renderableFeature in renderableFeatures) {
val rendered = renderableFeature.render(
GuiGraphics = context,
x = x + 5F,
y = drawY.toFloat(),
pokemon = pokemon
if (renderPentagonStats) {
blitk(
matrixStack = matrices,
texture = statsChartPentagonResource,
x= (x + 20.5) / SCALE,
y = (y + 22) / SCALE,
width = 186,
height = 176,
scale = SCALE
)
if (rendered) {
drawY += 30
pokemon.form.riding.behaviours?.let {
val behaviours = it.entries.toList()
val selectedBehaviour = behaviours[rideBehaviourIndex]
val pentagonColour = when (selectedBehaviour.key) {
RidingStyle.AIR -> {
if (behaviours.size > 1 && centerHovered(pMouseX, pMouseY))
Vector3f(70F/255F, 235F/225F, 195F/255F)
else Vector3f(40F/255F, 205F/255F, 165F/255F)
}
RidingStyle.LIQUID -> {
if (behaviours.size > 1 && centerHovered(pMouseX, pMouseY))
Vector3f(95F/255F, 165F/255F, 1F)
else Vector3f(65F/255F, 135F/255F, 1F)
}
else -> {
if (behaviours.size > 1 && centerHovered(pMouseX, pMouseY))
Vector3f(1F, 195F/255F, 30F/255F)
else Vector3f(1F, 165F/255F, 0F)
}
}
drawStatPolygon(
ratios = RidingStat.entries.map { pokemon.getRideStat(selectedBehaviour.key, it) / 100F },
colour = pentagonColour
)
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = lang("ui.ride_style.${selectedBehaviour.key.name.lowercase()}").bold(),
x = x + (WIDTH / 2),
y = y + 66,
shadow = true,
centered = true
)
// Stat Labels
renderPolygonLabels(
context = context,
labels = RidingStat.entries.toList().map { stat ->
if (statLabelsHovered(pentagonVerticesOffset, pMouseX, pMouseY))
"${floor(pokemon.getRideStat(selectedBehaviour.key, stat))}/${selectedBehaviour.value.stats[stat]?.endInclusive}".text()
else lang("ui.stats.ride.${stat.name.lowercase()}").bold()
},
verticesOffset = pentagonVerticesOffset
)
// Stat Values
renderPolygonLabels(
context = context,
labels = RidingStat.entries.toList().map { stat ->
if (statLabelsHovered(pentagonVerticesOffset, pMouseX, pMouseY))
"${floor((pokemon.getRideStat(selectedBehaviour.key, stat) / (selectedBehaviour.value.stats[stat]?.endInclusive ?: 1)) * 100)}%".text()
else floor(pokemon.getRideStat(selectedBehaviour.key, stat)).toString().text()
},
verticesOffset = pentagonVerticesOffset,
offsetY = 5.5
)
}
} else {
blitk(
matrixStack = matrices,
texture = statsChartResource,
x= (x + 25.5) / SCALE,
y = (y + 22) / SCALE,
width = 166,
height = 192,
scale = SCALE
)
when (statOptions.get(statTabIndex)) {
STATS -> drawStatPolygon(
listOf(
pokemon.maxHealth,
pokemon.attack,
pokemon.defence,
pokemon.speed,
pokemon.specialDefence,
pokemon.specialAttack
).map { it / 400F },
colour = Vector3f(50F/255F, 215F/255F, 1F)
)
IV -> drawStatPolygon(
statLabels.values.map { pokemon.ivs.getEffectiveBattleIV(it) / 31F },
colour = Vector3f(216F/255, 100F/255, 1F)
)
EV -> drawStatPolygon(
statLabels.values.map { (pokemon.evs[it]?.toFloat() ?: 0F) / 252F },
colour = Vector3f(1F, 1F, 100F/255F)
)
}
val labelsHovered = statLabelsHovered(hexagonVerticesOffset, pMouseX, pMouseY)
val labels = statLabels.map { stat ->
if (labelsHovered) {
when (statOptions.get(statTabIndex)) {
IV -> {
"${
if (pokemon.ivs.isHyperTrained(stat.value)) "${pokemon.ivs[stat.value]} (${pokemon.ivs.hyperTrainedIVs[stat.value]})"
else pokemon.ivs[stat.value].toString()
}/31".text()
}
EV -> "${pokemon.evs.getOrDefault(stat.value)}/252".text()
else -> stat.key
}
} else { stat.key }
}
// Stat Labels
renderPolygonLabels(
context = context,
labels = labels,
verticesOffset = hexagonVerticesOffset,
enableColour = true
)
// Stat Values
renderPolygonLabels(
context = context,
labels = statValuesAsText(
statLabels.values.toList(),
labelsHovered
),
verticesOffset = hexagonVerticesOffset,
offsetY = 5.5,
enableColour = true
)
// Nature-modified Stat Icons
if (statOptions.get(statTabIndex) == STATS) {
val nature = pokemon.effectiveNature
renderModifiedStatIcon(matrices, nature.increasedStat, true)
renderModifiedStatIcon(matrices, nature.decreasedStat, false)
}
}
}
// Stat type select bar
val hasRideBehaviour = pokemon.form.riding.behaviours != null
val startPosX = x + if (hasRideBehaviour) 23 else 31
val statTabWidth = if (hasRideBehaviour) STAT_TAB_WIDTH_6 else STAT_TAB_WIDTH_5
statOptions.forEachIndexed { index, stat ->
val statKey = if (stat == STATS) "" else ".$stat"
drawScaledText(
context = context,
text = lang("ui.stats$statKey").bold(),
x = startPosX + (statTabWidth * index),
y = y + 143,
scale = SCALE,
colour = if (statOptions.get(statTabIndex) == stat) WHITE else GREY,
centered = true
)
}
blitk(
matrixStack = context.pose(),
texture = tabMarkerResource,
x= (startPosX + (statTabIndex * statTabWidth) - (MARKER_WIDTH / 4)) / SCALE,
y = (y + 140) / SCALE,
width = MARKER_WIDTH,
height = 4,
scale = SCALE
)
}
override fun mouseClicked(pMouseX: Double, pMouseY: Double, pButton: Int): Boolean {
val index = getTabIndexFromPos(pMouseX, pMouseY)
// Only play sound here as the rest of the widget is meant to be silent
if (index in 0..4 && statTabIndex != index) {
if (index in 0 until statOptions.size && statTabIndex != index) {
statTabIndex = index
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.GUI_CLICK, 1.0F))
}
if (statOptions.get(statTabIndex) == RIDE && ((pokemon.riding.behaviours?.size ?: 0) > 1) && centerHovered(pMouseX, pMouseY)) {
rideBehaviourIndex = (rideBehaviourIndex + 1) % (pokemon.riding.behaviours?.size ?: 1)
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.GUI_CLICK, 1.0F))
}
return super.mouseClicked(pMouseX, pMouseY, pButton)
}
private fun getStatValueAsText(stat: Stat): MutableComponent {
val value = when (statOptions.get(statTabIndex)) {
STATS -> if (stat == Stats.HP) "${pokemon.currentHealth} / ${pokemon.maxHealth}" else pokemon.getStat(stat).toString()
IV -> if (pokemon.ivs.isHyperTrained(stat)) "${pokemon.ivs[stat]} (${pokemon.ivs.hyperTrainedIVs[stat]})" else pokemon.ivs[stat].toString()
EV -> pokemon.evs.getOrDefault(stat).toString()
else -> "0"
private fun statValuesAsText(stats: List<Stat>, asPercent: Boolean): List<MutableComponent> {
return stats.map { stat ->
when (statOptions.get(statTabIndex)) {
STATS -> (if (stat == Stats.HP) "${pokemon.currentHealth} / ${pokemon.maxHealth}" else pokemon.getStat(stat).toString()).text()
IV -> {
(if (asPercent) {
"${floor((((if (pokemon.ivs.isHyperTrained(stat)) pokemon.ivs.hyperTrainedIVs[stat] else pokemon.ivs[stat]) ?: 0) / 31.0) * 100)}%"
} else {
if (pokemon.ivs.isHyperTrained(stat)) "${pokemon.ivs[stat]} (${pokemon.ivs.hyperTrainedIVs[stat]})"
else pokemon.ivs[stat].toString()
}).text()
}
EV -> {
(if (asPercent) "${floor((pokemon.evs.getOrDefault(stat) / 252.0) * 100)}%"
else pokemon.evs.getOrDefault(stat).toString()).text()
}
else -> "0".text()
}
}
return value.text()
}
private fun renderModifiedStatIcon(pPoseStack: PoseStack, stat: Stat?, increasedStat: Boolean) {
@@ -534,7 +532,7 @@ class StatWidget(
}
}
private fun getModifiedStatColour(stat: Stat, enableColour: Boolean): Int {
private fun getModifiedStatColour(stat: Stat?, enableColour: Boolean): Int {
if (statOptions.get(statTabIndex) == STATS && enableColour) {
val nature = pokemon.effectiveNature
@@ -544,81 +542,38 @@ class StatWidget(
return WHITE
}
private fun renderTextAtVertices(
context: GuiGraphics,
offsetY: Double = 0.0,
enableColour: Boolean = true,
hp: MutableComponent,
spAtk: MutableComponent,
atk: MutableComponent,
spDef: MutableComponent,
def: MutableComponent,
speed: MutableComponent
) {
drawScaledText(
context = context,
text = hp,
x = x + 67,
y = y + 10.5 + offsetY,
scale = SCALE,
colour = getModifiedStatColour(Stats.HP, enableColour),
centered = true
)
private fun renderPolygonLabels(context: GuiGraphics, labels: List<MutableComponent>, verticesOffset: List<Pair<Double, Double>>, offsetY: Double = 0.0, enableColour: Boolean = false) {
if (labels.size != verticesOffset.size) return
drawScaledText(
context = context,
text = spAtk,
x = x + 12,
y = y + 42.5 + offsetY,
scale = SCALE,
colour = getModifiedStatColour(Stats.SPECIAL_ATTACK, enableColour),
centered = true
)
labels.forEachIndexed { index, label ->
drawScaledText(
context = context,
text = label,
x = x + verticesOffset[index].first,
y = y + verticesOffset[index].second + offsetY,
scale = SCALE,
colour = getModifiedStatColour(statLabels[label], enableColour),
centered = true
)
}
}
drawScaledText(
context = context,
text = atk,
x = x + 122,
y = y + 42.5 + offsetY,
scale = SCALE,
colour = getModifiedStatColour(Stats.ATTACK,enableColour),
centered = true
)
private fun centerHovered(pMouseX: Number, pMouseY: Number): Boolean {
return (pMouseX.toInt() in (x + 44)..(x + 90)) && (pMouseY.toInt() in (y + 47)..(y + 93))
}
drawScaledText(
context = context,
text = spDef,
x = x + 12,
y = y + 93.5 + offsetY,
scale = SCALE,
colour = getModifiedStatColour(Stats.SPECIAL_DEFENCE, enableColour),
centered = true
)
drawScaledText(
context = context,
text = def,
x = x + 122,
y = y + 93.5 + offsetY,
scale = SCALE,
colour = getModifiedStatColour(Stats.DEFENCE, enableColour),
centered = true
)
drawScaledText(
context = context,
text = speed,
x = x + 67,
y = y + 124.5 + offsetY,
scale = SCALE,
colour = getModifiedStatColour(Stats.SPEED, enableColour),
centered = true
)
private fun statLabelsHovered(labelOffsets: List<Pair<Double, Double>>, mouseX: Int, mouseY: Int): Boolean {
return labelOffsets.any { pos ->
(mouseX.toDouble() in (x + pos.first - 12)..(x + pos.first + 12))
&& (mouseY.toDouble() in (y + pos.second - 1)..(y + pos.second + 10))
}
}
private fun getTabIndexFromPos(mouseX: Double, mouseY: Double): Int {
val paddingLeft = ((WIDTH - ((statOptions.size + 1) * statTabWidth)) / 2.0) + (statTabWidth / 2)
val left = x + paddingLeft
val hasRideBehaviour = pokemon.form.riding.behaviours != null
val left = x + if (hasRideBehaviour) ((23 + 1) / 2).toFloat() else ((31 + 1) / 2).toFloat()
val statTabWidth = if (hasRideBehaviour) STAT_TAB_WIDTH_6 else STAT_TAB_WIDTH_5
val top = y + 140.0
if (mouseX in left..(left + (statTabWidth * (statOptions.size + 1))) && mouseY in top..(top + 9.0)) {
var startX = left
@@ -631,4 +586,4 @@ class StatWidget(
}
return -1
}
}
}
@@ -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.client.gui.summary.widgets.screens.stats.features
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.BarSummarySpeciesFeatureRenderer
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.gui.GuiGraphics
class FriendshipFeatureRenderer(
val selectedPokemon: Pokemon
) : BarSummarySpeciesFeatureRenderer(
"friendship",
lang("ui.stats.friendship"),
cobblemonResource("textures/gui/summary/summary_stats_other_bar.png"),
cobblemonResource("textures/gui/summary/summary_stats_friendship_overlay.png"),
selectedPokemon,
0,
255,
selectedPokemon.friendship
) {
override fun render(guiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon): Boolean {
renderElement(guiGraphics, x, y, pokemon)
return true
}
override fun renderBar(guiGraphics: GuiGraphics, x: Float, y: Float, barValue: Int, barRatio: Float, barWidth: Int) {
val red = 1
val green: Number = if (barValue >= 160) 0.28 else 0.56
val blue: Number = if (barValue >= 160) 0.4 else 0.64
blitk(
matrixStack = guiGraphics.pose(),
texture = CobblemonResources.WHITE,
x = x + 3,
y = y + 13,
height = 10,
width = barWidth,
red = red,
green = green,
blue = blue
)
}
}
@@ -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.client.gui.summary.widgets.screens.stats.features
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.BarSummarySpeciesFeatureRenderer
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.gui.GuiGraphics
class FullnessFeatureRenderer(
val selectedPokemon: Pokemon
) : BarSummarySpeciesFeatureRenderer(
"fullness",
lang("ui.stats.fullness"),
cobblemonResource("textures/gui/summary/summary_stats_other_bar.png"),
cobblemonResource("textures/gui/summary/summary_stats_fullness_overlay.png"),
selectedPokemon,
0,
selectedPokemon.getMaxFullness(),
selectedPokemon.currentFullness
) {
override fun render(guiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon): Boolean {
renderElement(guiGraphics, x, y, pokemon)
return true
}
override fun renderBar(guiGraphics: GuiGraphics, x: Float, y: Float, barValue: Int, barRatio: Float, barWidth: Int) {
val (red, green, blue) = when {
barRatio <= 0.33 -> Triple(120F/255F, 200F/255F, 80F/255F) // Green
barRatio <= 0.66 -> Triple(240F/255F, 200F/255F, 65F/255F) // Yellow
else -> Triple(230F/255F, 80F/255F, 65F/255F) // Red
}
blitk(
matrixStack = guiGraphics.pose(),
texture = CobblemonResources.WHITE,
x = x + 3,
y = y + 13,
height = 10,
width = barWidth,
red = red,
green = green,
blue = blue
)
}
}
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.client.keybind.keybinds
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonNetwork.sendToServer
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.battles.BattleFormat
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.battle.ClientBattle
@@ -84,7 +85,7 @@ object PartySendBinding : CobblemonBlockingKeyBinding(
if (isRidingPokemon(player) && canAttemptDismount(player, selectedPartyPokemon)) {
sendToServer(DismountPokemonPacket())
}
else {
else if (!isRidingSelectedPokemon(player, selectedPartyPokemon)){
checkForTargetInteractions(player, selectedPartyPokemon)
}
}
@@ -145,6 +146,11 @@ object PartySendBinding : CobblemonBlockingKeyBinding(
if (player != vehicle.controllingPassenger) {
return true
}
val isAirRide = vehicle.ridingController?.context?.style == RidingStyle.AIR
val hasLandRide = vehicle.rideProp.behaviours?.get(RidingStyle.LAND) != null
if (isAirRide && hasLandRide) {
return false
}
else {
return vehicle.pokemon.uuid == selectedPartyPokemon.uuid
}
@@ -157,6 +163,14 @@ object PartySendBinding : CobblemonBlockingKeyBinding(
return true
}
private fun isRidingSelectedPokemon(player: LocalPlayer, selectedPartyPokemon: Pokemon, ignoreControlling: Boolean = false): Boolean {
if (!player.isPassenger) return false
if (player.vehicle !is PokemonEntity) return false
val vehicle = player.vehicle as PokemonEntity
if (ignoreControlling && player.vehicle!!.controllingPassenger == player) return false
return vehicle.pokemon.uuid == selectedPartyPokemon.uuid
}
override fun onPress() {
}
}
@@ -1,20 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.net.data
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
import com.cobblemon.mod.common.data.CobblemonDataProvider
import com.cobblemon.mod.common.net.messages.client.data.UnlockReloadPacket
import net.minecraft.client.Minecraft
internal object UnlockReloadPacketHandler : ClientNetworkPacketHandler<UnlockReloadPacket> {
override fun handle(packet: UnlockReloadPacket, client: Minecraft) {
CobblemonDataProvider.canReload = true
}
}
@@ -204,8 +204,6 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
/**
* Animation group : animation name [: primary]
* e.g. "particle_dummy:animation.particle_dummy.dragon_claw_target:primary"
* e.g. "particle_dummy:animation.particle.dummy.stat_up
*/
fun extractAnimation(string: String): ActiveAnimation? {
val group = string.substringBefore(":")
@@ -231,8 +231,8 @@ data class BedrockAnimation(
if (!timeline.scale.isEmpty()) {
var scale = timeline.scale.resolve(animationSeconds.toDouble(), runtime)
// If the goal is to make the invisible then kick that into gear after 0.5. Maybe could work better somehow else.
if (scale == Vec3.ZERO && intensity > 0.5) {
// If the goal is to make the invisible then kick that into gear after or on 0.5. Maybe could work better somehow else.
if (scale == Vec3.ZERO && intensity >= 0.5) {
part.xScale *= scale.x.toFloat()
part.yScale *= scale.y.toFloat()
part.zScale *= scale.z.toFloat()
@@ -1,88 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen1
import com.cobblemon.mod.common.client.render.models.blockbench.createTransformation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.ModelPartTransformation
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.PoseType.Companion.MOVING_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.STATIONARY_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class MewModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
override val rootPart = root.registerChildWithAllChildren("mew")
override val head = getPart("head")
override var portraitScale = 2.3F
override var portraitTranslation = Vec3(-0.15, 0.4, 0.0)
override var profileScale = 0.7F
override var profileTranslation = Vec3(0.0, 0.65, 0.0)
lateinit var standing: Pose
lateinit var walk: Pose
lateinit var shoulderLeft: Pose
lateinit var shoulderRight: Pose
val shoulderOffset = 1.5
override fun registerPoses() {
standing = registerPose(
poseName = "standing",
poseTypes = STATIONARY_POSES + UI_POSES,
animations = arrayOf(
singleBoneLook(),
bedrock("mew", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = MOVING_POSES,
animations = arrayOf(
singleBoneLook(),
bedrock("mew", "ground_walk")
)
)
shoulderLeft = registerPose(
poseType = PoseType.SHOULDER_LEFT,
//quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("mew", "ground_idle")
),
transformedParts = arrayOf(
rootPart.createTransformation().addPosition(ModelPartTransformation.X_AXIS, shoulderOffset)
)
)
shoulderRight = registerPose(
poseType = PoseType.SHOULDER_RIGHT,
//quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("mew", "ground_idle")
),
transformedParts = arrayOf(
rootPart.createTransformation().addPosition(ModelPartTransformation.X_AXIS, -shoulderOffset)
)
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PosableState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("mew", "faint") else null
}
@@ -1,100 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen2
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.util.isBattling
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class CrobatModel(root: ModelPart) : PokemonPosableModel(root) {
override val rootPart = root.registerChildWithAllChildren("crobat")
override var portraitScale = 1.2F
override var portraitTranslation = Vec3(-0.1, 1.8, 0.0)
override var profileScale = 0.5F
override var profileTranslation = Vec3(0.0, 1.2, 0.0)
lateinit var sleep: Pose
lateinit var standing: Pose
lateinit var walk: Pose
lateinit var hover: Pose
lateinit var fly: Pose
lateinit var battleidle: Pose
override val cryAnimation = CryProvider { bedrockStateful("crobat", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("crobat", "blink") }
sleep = registerPose(
poseType = PoseType.SLEEP,
animations = arrayOf(bedrock("crobat", "sleep"))
)
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES - PoseType.HOVER,
condition = { !it.isBattling },
transformTicks = 10,
animations = arrayOf(
bedrock("crobat", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES - PoseType.FLY,
transformTicks = 10,
animations = arrayOf(
bedrock("crobat", "ground_walk")
)
)
hover = registerPose(
poseName = "hover",
poseType = PoseType.HOVER,
transformTicks = 10,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("crobat", "air_idle")
)
)
fly = registerPose(
poseName = "fly",
poseType = PoseType.FLY,
transformTicks = 10,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("crobat", "air_fly")
)
)
battleidle = registerPose(
poseName = "battle_idle",
poseTypes = PoseType.STATIONARY_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
condition = { it.isBattling },
animations = arrayOf(
bedrock("crobat", "battle_idle")
)
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PosableState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("crobat", "faint") else null
}
@@ -1,65 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen3
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BimanualSwingAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedWalkAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BimanualFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class NuzleafModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame, BipedFrame, BimanualFrame {
override val rootPart = root.registerChildWithAllChildren("nuzleaf")
override val head = getPart("head")
override val leftArm = getPart("arm_left")
override val rightArm = getPart("arm_right")
override val leftLeg = getPart("leg_left")
override val rightLeg = getPart("leg_right")
override var portraitScale = 2.6F
override var portraitTranslation = Vec3(-0.15, -0.65, 0.0)
override var profileScale = 0.9F
override var profileTranslation = Vec3(0.0, 0.44, 0.0)
lateinit var standing: Pose
lateinit var walk: Pose
override fun registerPoses() {
val blink = quirk { bedrockStateful("nuzleaf", "blink") }
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("nuzleaf", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("nuzleaf", "ground_idle"),
BipedWalkAnimation(this, periodMultiplier = 0.6F, amplitudeMultiplier = 0.9F),
BimanualSwingAnimation(this, swingPeriodMultiplier = 0.6F, amplitudeMultiplier = 0.9F)
//bedrock("nuzleaf", "ground_walk")
)
)
}
}
@@ -1,55 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen3
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedWalkAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class SeedotModel (root: ModelPart) : PokemonPosableModel(root), BipedFrame {
override val rootPart = root.registerChildWithAllChildren("seedot")
override val leftLeg = getPart("foot_left")
override val rightLeg = getPart("foot_right")
override var portraitScale = 2.8F
override var portraitTranslation = Vec3(-0.1, -2.5, 0.0)
override var profileScale = 1.0F
override var profileTranslation = Vec3(0.0, 0.3, 0.0)
lateinit var standing: Pose
lateinit var walk: Pose
override fun registerPoses() {
val blink = quirk { bedrockStateful("seedot", "blink") }
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("seedot", "idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
BipedWalkAnimation(this, amplitudeMultiplier = 0.9F, periodMultiplier = 1F),
bedrock("seedot", "idle")
)
)
}
}
@@ -1,65 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen3
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BimanualSwingAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedWalkAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BimanualFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class ShiftryModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame, BipedFrame, BimanualFrame {
override val rootPart = root.registerChildWithAllChildren("shiftry")
override val head = getPart("head")
override val leftArm = getPart("arm_left")
override val rightArm = getPart("arm_right")
override val leftLeg = getPart("leg_left")
override val rightLeg = getPart("leg_right")
override var portraitScale = 2.0F
override var portraitTranslation = Vec3(-0.15, 0.5, 0.0)
override var profileScale = 0.74F
override var profileTranslation = Vec3(0.0, 0.7, 0.0)
lateinit var standing: Pose
lateinit var walk: Pose
override fun registerPoses() {
val blink = quirk { bedrockStateful("shiftry", "blink") }
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("shiftry", "idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("shiftry", "idle"),
BipedWalkAnimation(this, periodMultiplier = 0.6F, amplitudeMultiplier = 0.9F),
BimanualSwingAnimation(this, swingPeriodMultiplier = 0.6F, amplitudeMultiplier = 0.9F)
//bedrock("shiftry", "ground_walk")
)
)
}
}
@@ -1,105 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen3
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.createTransformation
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.util.isBattling
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class WailordModel(root: ModelPart) : PokemonPosableModel(root) {
override val rootPart = root.registerChildWithAllChildren("wailord")
override var portraitScale = 0.45F
override var portraitTranslation = Vec3(-0.38, 0.8, 6.69)
override var profileScale = 0.25F
override var profileTranslation = Vec3(0.0, 1.2, -10.0)
lateinit var standing: Pose
lateinit var walk: Pose
lateinit var floating: Pose
lateinit var swimming: Pose
lateinit var sleep: Pose
lateinit var battleidle: Pose
val offsetY = 0.0
override fun registerPoses() {
val blink = quirk { bedrockStateful("wailord", "blink")}
sleep = registerPose(
poseType = PoseType.SLEEP,
animations = arrayOf(bedrock("wailord", "sleep"))
)
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STANDING_POSES - PoseType.FLOAT,
condition = { !it.isBattling },
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("wailord", "ground_idle")
),
transformedParts = arrayOf(
rootPart.createTransformation().addPosition(0.0, offsetY, 0.0)
)
)
walk = registerPose(
poseName = "walking",
poseTypes = PoseType.MOVING_POSES - PoseType.SWIM,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("wailord", "ground_idle"),
bedrock("wailord", "ground_walk")
),
transformedParts = arrayOf(
rootPart.createTransformation().addPosition(0.0, offsetY, 0.0)
)
)
floating = registerPose(
poseName = "floating",
poseTypes = PoseType.UI_POSES + PoseType.FLOAT,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("wailord", "water_idle")
)
)
swimming = registerPose(
poseName = "swimming",
poseType = PoseType.SWIM,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("wailord", "water_swim")
)
)
battleidle = registerPose(
poseName = "battle_idle",
poseTypes = PoseType.STATIONARY_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
condition = { it.isBattling },
animations = arrayOf(
bedrock("wailord", "battle_idle")
),
transformedParts = arrayOf(
rootPart.createTransformation().addPosition(0.0, offsetY, 0.0)
)
)
}
override fun getFaintAnimation(state: PosableState) = if (state.isPosedIn(standing, walk, sleep, battleidle)) bedrockStateful("wailord", "faint") else
if (state.isPosedIn(floating, swimming)) bedrockStateful("wailord", "faint_water")
else null
}
@@ -1,90 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen4
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BimanualFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.PoseType.Companion.MOVING_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.STATIONARY_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import com.cobblemon.mod.common.util.isBattling
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class LickilickyModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame, BipedFrame, BimanualFrame {
override val rootPart = root.registerChildWithAllChildren("lickilicky")
override val head = getPart("head")
override val leftLeg = getPart("left_leg")
override val rightLeg = getPart("right_leg")
override val leftArm = getPart("left_upper_arm")
override val rightArm = getPart("right_upper_arm")
override var portraitScale = 1.4F
override var portraitTranslation = Vec3(-0.2, 1.8, 0.0)
override var profileScale = 0.5F
override var profileTranslation = Vec3(0.0, 1.0, 0.0)
lateinit var standing: Pose
lateinit var walk: Pose
lateinit var battleidle: Pose
lateinit var sleep: Pose
override fun registerPoses() {
val blink = quirk { bedrockStateful("lickilicky", "blink") }
sleep = registerPose(
poseType = PoseType.SLEEP,
animations = arrayOf(bedrock("lickilicky", "sleep"))
)
standing = registerPose(
poseName = "standing",
poseTypes = STATIONARY_POSES + UI_POSES,
quirks = arrayOf(blink),
condition = { !it.isBattling },
animations = arrayOf(
singleBoneLook(),
bedrock("lickilicky", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = MOVING_POSES,
condition = { !it.isBattling },
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("lickilicky", "ground_walk")
)
)
battleidle = registerPose(
poseName = "battle_idle",
poseTypes = PoseType.STATIONARY_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
condition = { it.isBattling },
animations = arrayOf(
singleBoneLook(),
bedrock("lickilicky", "battle_idle")
)
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PosableState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk, battleidle, sleep)) bedrockStateful("lickilicky", "faint") else null
}
@@ -1,100 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen4
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedWalkAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.createTransformation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pose.ModelPartTransformation
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.PoseType.Companion.MOVING_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.STATIONARY_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class PachirisuModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame, BipedFrame {
override val rootPart = root.registerChildWithAllChildren("pachirisu")
override val head = getPart("head")
override val leftLeg = getPart("leftfoot")
override val rightLeg = getPart("rightfoot")
override var portraitScale = 2.3F
override var portraitTranslation = Vec3(-0.2, -1.1, 0.0)
override var profileScale = 0.8F
override var profileTranslation = Vec3(0.0, 0.55, 0.0)
lateinit var standing: Pose
lateinit var walk: Pose
lateinit var shoulderLeft: Pose
lateinit var shoulderRight: Pose
val shoulderOffset = 5
val shoulderZOffset = 4.8
override fun registerPoses() {
val blink = quirk { bedrockStateful("pachirisu", "blink") }
standing = registerPose(
poseName = "standing",
poseTypes = STATIONARY_POSES + UI_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("pachirisu", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = MOVING_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
BipedWalkAnimation(this, periodMultiplier = 0.8F, amplitudeMultiplier = 0.7F),
bedrock("pachirisu", "ground_idle")
)
)
shoulderLeft = registerPose(
poseType = PoseType.SHOULDER_LEFT,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("pachirisu", "ground_idle")
),
transformedParts = arrayOf(
rootPart.createTransformation().addPosition(ModelPartTransformation.X_AXIS, shoulderOffset),
rootPart.createTransformation().addPosition(ModelPartTransformation.Z_AXIS, shoulderZOffset)
)
)
shoulderRight = registerPose(
poseType = PoseType.SHOULDER_RIGHT,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("pachirisu", "ground_idle")
),
transformedParts = arrayOf(
rootPart.createTransformation().addPosition(ModelPartTransformation.X_AXIS, -shoulderOffset),
rootPart.createTransformation().addPosition(ModelPartTransformation.Z_AXIS, shoulderZOffset)
)
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PosableState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("pachirisu", "faint") else null
}
@@ -1,93 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen8
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.QuadrupedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.util.isBattling
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class NickitModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame, QuadrupedFrame {
override val rootPart = root.registerChildWithAllChildren("nickit")
override val head = getPart("head")
override val foreLeftLeg = getPart("leg_front_left")
override val foreRightLeg = getPart("leg_front_right")
override val hindLeftLeg = getPart("leg_back_left")
override val hindRightLeg = getPart("leg_back_right")
override var portraitScale = 1.8F
override var portraitTranslation = Vec3(-0.3, -0.1, 0.0)
override var profileScale = 0.75F
override var profileTranslation = Vec3(0.0, 0.65, 0.0)
lateinit var standing: Pose
lateinit var walk: Pose
lateinit var sleep: Pose
lateinit var battleidle: Pose
override val cryAnimation = CryProvider { bedrockStateful("nickit", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("nickit", "blink")}
val glanceleft = quirk { bedrockStateful("nickit", "quirk_shiftyglance_left")}
val glanceright = quirk { bedrockStateful("nickit", "quirk_shiftyglance_right")}
val eartwitchleft = quirk { bedrockStateful("nickit", "quirk_eartwitch_left")}
val eartwitchright = quirk { bedrockStateful("nickit", "quirk_eartwitch_right")}
sleep = registerPose(
poseType = PoseType.SLEEP,
animations = arrayOf(bedrock("nickit", "sleep"))
)
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
transformTicks = 10,
quirks = arrayOf(blink, glanceleft, glanceright, eartwitchleft, eartwitchright),
condition = { !it.isBattling },
animations = arrayOf(
singleBoneLook(),
bedrock("nickit", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
transformTicks = 10,
quirks = arrayOf(blink, eartwitchleft, eartwitchright),
animations = arrayOf(
singleBoneLook(),
bedrock("nickit", "ground_walk")
)
)
battleidle = registerPose(
poseName = "battle_idle",
poseTypes = PoseType.STATIONARY_POSES,
transformTicks = 10,
quirks = arrayOf(blink, glanceleft, glanceright, eartwitchleft, eartwitchright),
condition = { it.isBattling },
animations = arrayOf(
singleBoneLook(),
bedrock("nickit", "battle_idle")
)
)
}
override fun getFaintAnimation(state: PosableState) = if (state.isPosedIn(standing, walk, sleep, battleidle)) bedrockStateful("nickit", "faint") else null
}
@@ -1,93 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen8
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.QuadrupedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.util.isBattling
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class ThievulModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame, QuadrupedFrame {
override val rootPart = root.registerChildWithAllChildren("thievul")
override val head = getPart("head")
override val foreLeftLeg = getPart("leg_front_left")
override val foreRightLeg = getPart("leg_front_right")
override val hindLeftLeg = getPart("leg_back_left")
override val hindRightLeg = getPart("leg_back_right")
override var portraitScale = 2.0F
override var portraitTranslation = Vec3(-0.7, 1.11, 0.0)
override var profileScale = 0.65F
override var profileTranslation = Vec3(0.0, 0.7, 0.0)
lateinit var standing: Pose
lateinit var walk: Pose
lateinit var sleep: Pose
lateinit var battleidle: Pose
override val cryAnimation = CryProvider { bedrockStateful("thievul", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("thievul", "blink")}
val sleepquirk = quirk { bedrockStateful("thievul", "quirk_sleep")}
sleep = registerPose(
poseType = PoseType.SLEEP,
quirks = arrayOf(sleepquirk),
animations = arrayOf(bedrock("thievul", "sleep"))
)
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
condition = { !it.isBattling },
animations = arrayOf(
singleBoneLook(),
bedrock("thievul", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("thievul", "ground_walk")
)
)
battleidle = registerPose(
poseName = "battle_idle",
poseTypes = PoseType.STATIONARY_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
condition = { it.isBattling },
animations = arrayOf(
singleBoneLook(),
bedrock("thievul", "battle_idle")
)
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PosableState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk, sleep, battleidle)) bedrockStateful("thievul", "faint") else null
}
@@ -1,89 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen9
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BimanualFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.util.isBattling
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class AnnihilapeModel (root: ModelPart) : PokemonPosableModel(root), BipedFrame, BimanualFrame {
override val rootPart = root.registerChildWithAllChildren("annihilape")
override val leftArm = getPart("arm_left")
override val rightArm = getPart("arm_right")
override val leftLeg = getPart("leg_left")
override val rightLeg = getPart("leg_right")
override var portraitScale = 1.4F
override var portraitTranslation = Vec3(0.0, 0.1, 0.0)
override var profileScale = 0.85F
override var profileTranslation = Vec3(0.0, 0.45, 0.0)
lateinit var standing: Pose
lateinit var walking: Pose
lateinit var sleep: Pose
lateinit var battleidle: Pose
override val cryAnimation = CryProvider { bedrockStateful("annihilape", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("annihilape", "blink") }
sleep = registerPose(
poseType = PoseType.SLEEP,
animations = arrayOf(
bedrock("annihilape", "sleep"),
bedrock("annihilape", "hair_overlay")
)
)
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
transformTicks = 10,
condition = { !it.isBattling },
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("annihilape", "ground_idle"),
bedrock("annihilape", "hair_overlay")
)
)
walking = registerPose(
poseName = "walking",
poseTypes = PoseType.MOVING_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("annihilape", "ground_walk"),
bedrock("annihilape", "hair_overlay")
)
)
battleidle = registerPose(
poseName = "battle_idle",
poseTypes = PoseType.STATIONARY_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
condition = { it.isBattling },
animations = arrayOf(
bedrock("annihilape", "battle_idle"),
bedrock("annihilape", "hair_overlay")
)
)
}
override fun getFaintAnimation(state: PosableState) = if (state.isPosedIn(standing, walking, battleidle, sleep)) bedrockStateful("annihilape", "faint") else null
}
@@ -1,60 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen9
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class RevavroomModel (root: ModelPart) : PokemonPosableModel(root) {
override val rootPart = root.registerChildWithAllChildren("revavroom")
override var portraitScale = 3.1F
override var portraitTranslation = Vec3(-1.4, -1.7, 1.1)
override var profileScale = 0.9F
override var profileTranslation = Vec3(-0.2, 0.3, 1.1)
lateinit var standing: Pose
lateinit var walk: Pose
lateinit var sleep: Pose
override val cryAnimation = CryProvider { bedrockStateful("revavroom", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("revavroom", "blink") }
sleep = registerPose(
poseType = PoseType.SLEEP,
animations = arrayOf(bedrock("revavroom", "sleep"))
)
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("revavroom", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("revavroom", "ground_walk")
)
)
}
}
@@ -1,60 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen9
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class VaroomModel (root: ModelPart) : PokemonPosableModel(root) {
override val rootPart = root.registerChildWithAllChildren("varoom")
override var portraitScale = 3.0F
override var portraitTranslation = Vec3(0.5, -2.5, 0.0)
override var profileScale = 1.1F
override var profileTranslation = Vec3(0.0, 0.0, 0.0)
lateinit var standing: Pose
lateinit var walk: Pose
lateinit var sleep: Pose
override val cryAnimation = CryProvider { bedrockStateful("varoom", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("varoom", "blink") }
sleep = registerPose(
poseType = PoseType.SLEEP,
animations = arrayOf(bedrock("varoom", "sleep"))
)
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
transformTicks = 0,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("varoom", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
transformTicks = 0,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("varoom", "ground_walk")
)
)
}
}
@@ -252,7 +252,6 @@ object VaryingModelRepository {
inbuilt("arbok", ::ArbokModel)
inbuilt("arcanine", ::ArcanineModel)
inbuilt("chansey", ::ChanseyModel)
inbuilt("crobat", ::CrobatModel)
inbuilt("cubone", ::CuboneModel)
inbuilt("ditto", ::DittoModel)
inbuilt("dodrio", ::DodrioModel)
@@ -279,7 +278,6 @@ object VaryingModelRepository {
inbuilt("lickitung", ::LickitungModel)
inbuilt("marowak", ::MarowakModel)
inbuilt("meowth", ::MeowthModel)
inbuilt("mew", ::MewModel)
inbuilt("nidoqueen", ::NidoqueenModel)
inbuilt("nidoranf", ::NidoranfModel)
inbuilt("nidoranm", ::NidoranmModel)
@@ -307,7 +305,6 @@ object VaryingModelRepository {
inbuilt("smoochum", ::SmoochumModel)
inbuilt("hitmontop", ::HitmontopModel)
inbuilt("electivire", ::ElectivireModel)
inbuilt("lickilicky", ::LickilickyModel)
inbuilt("tangrowth", ::TangrowthModel)
inbuilt("blissey", ::BlisseyModel)
inbuilt("piloswine", ::PiloswineModel)
@@ -322,7 +319,6 @@ object VaryingModelRepository {
inbuilt("buneary", ::BunearyModel)
inbuilt("lopunny", ::LopunnyModel)
inbuilt("mamoswine", ::MamoswineModel)
inbuilt("pachirisu", ::PachirisuModel)
inbuilt("piplup", ::PiplupModel)
inbuilt("yanmega", ::YanmegaModel)
inbuilt("basculin", ::BasculinModel)
@@ -345,7 +341,6 @@ object VaryingModelRepository {
inbuilt("sableye", ::SableyeModel)
inbuilt("natu", ::NatuModel)
inbuilt("xatu", ::XatuModel)
inbuilt("wailord", ::WailordModel)
inbuilt("nacli", :: NacliModel)
inbuilt("naclstack", :: NaclstackModel)
inbuilt("garganacl", ::GarganaclModel)
@@ -392,9 +387,6 @@ object VaryingModelRepository {
inbuilt("chimchar", :: ChimcharModel)
inbuilt("monferno", :: MonfernoModel)
inbuilt("infernape", :: InfernapeModel)
inbuilt("seedot", ::SeedotModel)
inbuilt("nuzleaf", ::NuzleafModel)
inbuilt("shiftry", ::ShiftryModel)
inbuilt("kricketot", ::KricketotModel)
inbuilt("kricketune", ::KricketuneModel)
inbuilt("durant", ::DurantModel)
@@ -429,8 +421,6 @@ object VaryingModelRepository {
inbuilt("tandemaus", ::TandemausModel)
inbuilt("maushold", ::MausholdModel)
inbuilt("mausholdfour", ::MausholdfourModel)
inbuilt("varoom", ::VaroomModel)
inbuilt("revavroom", ::RevavroomModel)
inbuilt("lanturn", ::LanturnModel)
inbuilt("chingling", ::ChinglingModel)
inbuilt("chimecho", ::ChimechoModel)
@@ -466,8 +456,6 @@ object VaryingModelRepository {
inbuilt("sirfetchd", ::SirfetchdModel)
inbuilt("duskull", ::DuskullModel)
inbuilt("dusclops", ::DusclopsModel)
inbuilt("nickit", ::NickitModel)
inbuilt("thievul", ::ThievulModel)
inbuilt("glimmet", ::GlimmetModel)
inbuilt("glimmora", ::GlimmoraModel)
inbuilt("bonsly", ::BonslyModel)
@@ -502,7 +490,6 @@ object VaryingModelRepository {
inbuilt("shroodle", ::ShroodleModel)
inbuilt("grafaiai", ::GrafaiaiModel)
inbuilt("squawkabilly", ::SquawkabillyModel)
inbuilt("annihilape", ::AnnihilapeModel)
inbuilt("ponyta_galarian", ::PonytaGalarianModel)
inbuilt("rapidash_galarian", ::RapidashGalarianModel)
inbuilt("volbeat", ::VolbeatModel)
@@ -9,30 +9,63 @@
package com.cobblemon.mod.common.client.render.player
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.riding.RidingAnimation
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockAnimationRepository
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.math.Axis
import net.minecraft.client.model.AnimationUtils
import net.minecraft.client.model.HumanoidModel
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.client.player.AbstractClientPlayer
import net.minecraft.world.InteractionHand
import net.minecraft.world.entity.HumanoidArm
import net.minecraft.world.phys.Vec3
/**
* @author landonjw
*/
object MountedPlayerRenderer {
@JvmField
var shouldApplyRootAnimation = false
@JvmField
val relevantPartsByName = mutableMapOf(
"body" to ModelPart(emptyList(), emptyMap()),
)
val defaultAnimations = listOf(
RidingAnimation(
fileName = "player",
animationName = "animation.player.default_ride_pose"
)
)
fun animate(
model: HumanoidModel<AbstractClientPlayer>,
pokemonEntity: PokemonEntity,
player: AbstractClientPlayer,
relevantPartsByName: Map<String, ModelPart>,
headYaw: Float,
headPitch: Float,
ageInTicks: Float,
limbSwing: Float,
limbSwingAmount: Float
) {
// We need to reset the root because it's a synthetic part, Minecraft won't do this for us.
val root = relevantPartsByName["body"] ?: return
root.x = 0F
root.y = 0F
root.z = 0F
root.xRot = 0F
root.yRot = 0F
root.zRot = 0F
val seatIndex = pokemonEntity.passengers.indexOf(player).takeIf { it != -1 && it < pokemonEntity.seats.size } ?: return
val seat = pokemonEntity.seats[seatIndex]
val animations = seat.poseAnimations?.firstOrNull { it.poseTypes.isEmpty() || it.poseTypes.contains(pokemonEntity.getCurrentPoseType()) }?.animations
?: return
val animations = seat.poseAnimations
?.firstOrNull { it.poseTypes.isEmpty() || it.poseTypes.contains(pokemonEntity.getCurrentPoseType()) }?.animations
?: defaultAnimations
val state = pokemonEntity.delegate as PokemonClientDelegate
relevantPartsByName.values.forEach {
@@ -44,6 +77,8 @@ object MountedPlayerRenderer {
state.runtime.environment.setSimpleVariable("age_in_ticks", DoubleValue(ageInTicks.toDouble()))
state.runtime.environment.setSimpleVariable("limb_swing", DoubleValue(limbSwing.toDouble()))
state.runtime.environment.setSimpleVariable("limb_swing_amount", DoubleValue(limbSwingAmount.toDouble()))
state.runtime.environment.setSimpleVariable("head_yaw", DoubleValue(headYaw.toDouble()))
state.runtime.environment.setSimpleVariable("head_pitch", DoubleValue(headPitch.toDouble()))
// TODO add the q.player reference, requires a cached thing or something on the client idk
@@ -61,5 +96,59 @@ object MountedPlayerRenderer {
intensity = 1F
)
}
applyRegularAnimation(model, player, ageInTicks)
}
/**
* This does what Minecraft would typically do for various additional body part
* rendering thingies. It's largely copy paste.
*/
fun applyRegularAnimation(
model: HumanoidModel<AbstractClientPlayer>,
player: AbstractClientPlayer,
ageInTicks: Float
) {
val rightHanded = player.mainArm == HumanoidArm.RIGHT
if (player.isUsingItem) {
val usingMainHand = player.usedItemHand == InteractionHand.MAIN_HAND
if (usingMainHand == rightHanded) {
model.poseRightArm(player)
} else {
model.poseLeftArm(player)
}
} else {
val offHandIsTwoHanded: Boolean = if (rightHanded) model.leftArmPose.isTwoHanded else model.rightArmPose.isTwoHanded
if (rightHanded != offHandIsTwoHanded) {
model.poseLeftArm(player)
model.poseRightArm(player)
} else {
model.poseRightArm(player)
model.poseLeftArm(player)
}
}
model.setupAttackAnimation(player, ageInTicks)
if (model.rightArmPose != HumanoidModel.ArmPose.SPYGLASS) {
AnimationUtils.bobModelPart(model.rightArm, ageInTicks, 1.0F);
}
if (model.leftArmPose != HumanoidModel.ArmPose.SPYGLASS) {
AnimationUtils.bobModelPart(model.leftArm, ageInTicks, -1.0F);
}
model.hat.copyFrom(model.head)
}
const val RIDING_SITTING_OFFSET = -10.0
fun animateRoot(stack: PoseStack) {
val root = relevantPartsByName["body"] ?: return
val rootOffset = Vec3(root.x.toDouble(), root.y.toDouble() + RIDING_SITTING_OFFSET, root.z.toDouble()).scale(1/16.0)
val rotation = Vec3(root.xRot.toDouble(), root.yRot.toDouble(), root.zRot.toDouble())
stack.mulPose(Axis.ZP.rotation(rotation.z.toFloat()))
stack.mulPose(Axis.YP.rotation(rotation.y.toFloat()))
stack.mulPose(Axis.XP.rotation(rotation.x.toFloat()))
stack.translate(rootOffset.x.toFloat(), rootOffset.y.toFloat(), rootOffset.z.toFloat())
}
}
@@ -11,8 +11,6 @@ package com.cobblemon.mod.common.client.tooltips
import com.cobblemon.mod.common.CobblemonItemComponents
import com.cobblemon.mod.common.CobblemonRecipeTypes
import com.cobblemon.mod.common.api.cooking.Flavour
import com.cobblemon.mod.common.api.cooking.Food
import com.cobblemon.mod.common.api.cooking.PokePuffUtils
import com.cobblemon.mod.common.api.cooking.Seasonings
import com.cobblemon.mod.common.api.fishing.SpawnBait
import com.cobblemon.mod.common.api.fishing.SpawnBaitEffects
@@ -20,20 +18,16 @@ import com.cobblemon.mod.common.api.fishing.SpawnBaitUtils
import com.cobblemon.mod.common.api.pokemon.egg.EggGroup
import com.cobblemon.mod.common.api.text.*
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.item.components.FoodComponent
import com.cobblemon.mod.common.item.crafting.CookingPotRecipe
import com.cobblemon.mod.common.item.crafting.CookingPotRecipeBase
import com.cobblemon.mod.common.item.interactive.PokerodItem
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.util.lang
import net.minecraft.ChatFormatting
import net.minecraft.client.Minecraft
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.Style
import net.minecraft.world.effect.MobEffectInstance
import net.minecraft.world.effect.MobEffectUtil
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.crafting.RecipeManager
import net.minecraft.world.level.Level
import java.text.DecimalFormat
val cookingPotRecipeAbsorbHeader by lazy { lang("item_class.cooking_pot_recipe_absorbs").gray() }
@@ -121,8 +115,7 @@ fun generateAdditionalFlavorTooltip(flavours: Map<Flavour, Int>): MutableList<Co
val combinedFlavorsLine = Component.literal("")
flavours.filter { it.key != Flavour.MILD }.forEach { (flavour, value) ->
var flavourText = lang("seasoning_flavor.${flavour.name.lowercase()}").withStyle(flavour.chatFormatting)
val flavourText = lang("seasoning_flavor.${flavour.name.lowercase()}").setStyle(Style.EMPTY.withColor(flavour.colour))
if (combinedFlavorsLine.string.isNotEmpty()) {
combinedFlavorsLine.append(" ")
}
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.command
import com.cobblemon.mod.common.api.abilities.Abilities
import com.cobblemon.mod.common.api.item.HeldItems
import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.battles.BagItems
@@ -36,7 +37,7 @@ object ReloadShowdownCommand {
ShowdownService.service.resetAllRegistries()
ShowdownService.service.sendRegistryData(Abilities.abilityScripts, "ability")
ShowdownService.service.sendRegistryData(BagItems.bagItemsScripts, "bagItem")
// TODO: ShowdownService.service.sendRegistryData(notYetImplemented, "heldItem")
ShowdownService.service.sendRegistryData(HeldItems.heldItemsScripts, "heldItem")
ShowdownService.service.sendRegistryData(Moves.moveScripts, "move")
ShowdownService.service.sendRegistryData(PokemonSpecies.allShowdownSpecies(), "species")
context.source.sendSystemMessage(Component.literal("Reloaded showdown"))
@@ -55,7 +55,8 @@ class CobblemonConfig {
@CobblemonConfigField(Category.Storage, lang = "default_box_count")
@IntConstraint(min = 1, max = 1000)
var defaultBoxCount = 30
@LastChangedVersion("1.7.0")
var defaultBoxCount = 40
@CobblemonConfigField(Category.Storage, lang = "pokemon_save_interval_seconds")
@IntConstraint(min = 1, max = 120)
var pokemonSaveIntervalSeconds = 30
@@ -10,9 +10,9 @@ package com.cobblemon.mod.common.data
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.CobblemonCosmeticItems
import com.cobblemon.mod.common.CobblemonCallbacks
import com.cobblemon.mod.common.CobblemonBehaviours
import com.cobblemon.mod.common.CobblemonCallbacks
import com.cobblemon.mod.common.CobblemonCosmeticItems
import com.cobblemon.mod.common.CobblemonMechanics
import com.cobblemon.mod.common.CobblemonUnlockableWallpapers
import com.cobblemon.mod.common.api.abilities.Abilities
@@ -20,15 +20,15 @@ import com.cobblemon.mod.common.api.berry.Berries
import com.cobblemon.mod.common.api.cooking.Seasonings
import com.cobblemon.mod.common.api.data.DataProvider
import com.cobblemon.mod.common.api.data.DataRegistry
import com.cobblemon.mod.common.api.pokedex.Dexes
import com.cobblemon.mod.common.api.dialogue.Dialogues
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.fishing.PokeRods
import com.cobblemon.mod.common.api.fishing.SpawnBait
import com.cobblemon.mod.common.api.fishing.SpawnBaitEffects
import com.cobblemon.mod.common.api.fishing.PokeRods
import com.cobblemon.mod.common.api.fossil.Fossils
import com.cobblemon.mod.common.api.fossil.NaturalMaterials
import com.cobblemon.mod.common.api.interaction.PokemonInteractions
import com.cobblemon.mod.common.api.item.HeldItems
import com.cobblemon.mod.common.api.mark.Marks
import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
@@ -36,6 +36,7 @@ import com.cobblemon.mod.common.api.npc.NPCClasses
import com.cobblemon.mod.common.api.npc.NPCPresets
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokedex.DexAdditions
import com.cobblemon.mod.common.api.pokedex.Dexes
import com.cobblemon.mod.common.api.pokedex.entry.DexEntries
import com.cobblemon.mod.common.api.pokedex.entry.DexEntryAdditions
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
@@ -47,72 +48,71 @@ import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnRules
import com.cobblemon.mod.common.api.spawning.SpawnDetailPresets
import com.cobblemon.mod.common.battles.BagItems
import com.cobblemon.mod.common.net.messages.client.data.UnlockReloadPacket
import com.cobblemon.mod.common.platform.events.PlatformEvents
import com.cobblemon.mod.common.pokemon.SpeciesAdditions
import com.cobblemon.mod.common.pokemon.properties.PropertiesCompletionProvider
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.ifClient
import com.cobblemon.mod.common.util.server
import java.util.UUID
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.server.packs.resources.ResourceManager
import net.minecraft.server.packs.resources.ResourceManagerReloadListener
import java.util.*
object CobblemonDataProvider : DataProvider {
internal var canReload = true
// Both Forge n Fabric keep insertion order so if a registry depends on another simply register it after
private val registries = linkedSetOf<DataRegistry>()
private val reloadableRegistries = linkedSetOf<DataRegistry>()
private val synchronizedPlayerIds = mutableListOf<UUID>()
private val scheduledActions = mutableMapOf<UUID, MutableList<() -> Unit>>()
fun registerDefaults() {
this.register(CobblemonScripts)
this.register(SpeciesFeatures)
this.register(GlobalSpeciesFeatures)
this.register(SpeciesFeatureAssignments)
this.register(ActionEffects)
this.register(Moves)
this.register(Abilities)
this.register(CobblemonBehaviours)
this.register(PokemonSpecies)
this.register(SpeciesAdditions)
this.register(PokeBalls)
this.register(PropertiesCompletionProvider)
this.register(SpawnDetailPresets)
this.register(CobblemonSpawnRules)
this.register(CobblemonMechanics)
this.register(BagItems)
this.register(Dialogues)
this.register(NaturalMaterials)
this.register(Fossils)
this.register(NPCPresets)
this.register(NPCClasses)
this.register(DexEntries)
this.register(DexEntryAdditions)
this.register(Dexes)
this.register(DexAdditions)
this.register(CobblemonCosmeticItems)
this.register(CobblemonCallbacks)
this.register(CobblemonUnlockableWallpapers)
this.register(Marks)
this.register(StarterDataLoader)
this.register(CobblemonScripts, reloadable = true)
this.register(SpeciesFeatures, reloadable = false)
this.register(GlobalSpeciesFeatures, reloadable = false)
this.register(SpeciesFeatureAssignments, reloadable = false)
this.register(ActionEffects, reloadable = true)
this.register(Moves, reloadable = false)
this.register(Abilities, reloadable = false)
this.register(CobblemonBehaviours, reloadable = false)
this.register(PokemonSpecies, reloadable = false)
this.register(SpeciesAdditions, reloadable = false)
this.register(PokeBalls, reloadable = false)
this.register(PropertiesCompletionProvider, reloadable = false)
this.register(SpawnDetailPresets, reloadable = true)
this.register(CobblemonSpawnRules, reloadable = true)
this.register(CobblemonMechanics, reloadable = true)
this.register(BagItems, reloadable = false)
this.register(HeldItems, reloadable = false)
this.register(Dialogues, reloadable = true)
this.register(NaturalMaterials, reloadable = true)
this.register(Fossils, reloadable = true)
this.register(NPCPresets, reloadable = false)
this.register(NPCClasses, reloadable = false)
this.register(DexEntries, reloadable = false)
this.register(DexEntryAdditions, reloadable = false)
this.register(Dexes, reloadable = false)
this.register(DexAdditions, reloadable = false)
this.register(CobblemonCosmeticItems, reloadable = true)
this.register(CobblemonCallbacks, reloadable = true)
this.register(CobblemonUnlockableWallpapers, reloadable = true)
this.register(Marks, reloadable = false)
this.register(StarterDataLoader, reloadable = true)
CobblemonSpawnPools.load()
this.register(PokeRods)
this.register(Berries)
this.register(Seasonings)
this.register(PokemonInteractions)
this.register(SpawnBaitEffects)
this.register(PokeRods, reloadable = false)
this.register(Berries, reloadable = false)
this.register(Seasonings, reloadable = false)
this.register(PokemonInteractions, reloadable = false)
this.register(SpawnBaitEffects, reloadable = false)
SpawnBait.Effects.setupEffects()
PlatformEvents.SERVER_PLAYER_LOGOUT.subscribe {
synchronizedPlayerIds.remove(it.player.uuid)
UnlockReloadPacket().sendToPlayer(it.player)
}
ifClient {
@@ -121,12 +121,15 @@ object CobblemonDataProvider : DataProvider {
Cobblemon.implementation.registerResourceReloader(cobblemonResource("data_resources"), SimpleResourceReloader(PackType.SERVER_DATA), PackType.SERVER_DATA, emptyList())
}
override fun <T : DataRegistry> register(registry: T): T {
override fun <T : DataRegistry> register(registry: T, reloadable: Boolean): T {
// Only send message once
if (this.registries.isEmpty()) {
LOGGER.info("Note: Cobblemon data registries are only loaded once per server instance as Pokémon species are not safe to reload.")
}
this.registries.add(registry)
if (reloadable) {
this.reloadableRegistries.add(registry)
}
LOGGER.info("Registered the {} registry", registry.id.toString())
LOGGER.debug("Registered the {} registry of class {}", registry.id.toString(), registry::class.qualifiedName)
return registry
@@ -157,15 +160,9 @@ object CobblemonDataProvider : DataProvider {
private class SimpleResourceReloader(private val type: PackType) : ResourceManagerReloadListener {
override fun onResourceManagerReload(manager: ResourceManager) {
// Check for a server running, this is due to the create a world screen triggering datapack reloads, these are fine to happen as many times as needed as players may be in the process of adding their datapacks.
val isInGame = server() != null
if (isInGame && this.type == PackType.SERVER_DATA && !canReload) {
return
}
registries.filter { it.type == this.type }
val reloadAllowed = server()?.isReady != true
registries.filter { it.type == this.type && (reloadAllowed || it in reloadableRegistries) }
.forEach { it.reload(manager) }
if (isInGame && this.type == PackType.SERVER_DATA) {
canReload = false
}
}
}
}
@@ -21,7 +21,9 @@ import net.minecraft.world.level.material.FluidState
interface OmniPathingEntity {
fun canWalk(): Boolean
fun canSwimInWater(): Boolean
fun canWalkOnWater(): Boolean
fun canSwimInLava(): Boolean
fun canWalkOnLava(): Boolean
fun canSwimUnderFluid(fluidState: FluidState): Boolean
fun canFly(): Boolean
fun isFlying(): Boolean
@@ -49,7 +49,7 @@ enum class PoseType {
val UI_POSES = EnumSet.of(PROFILE, PORTRAIT)
val MOVING_POSES = EnumSet.of(WALK, SWIM, FLY)
val STATIONARY_POSES = EnumSet.of(STAND, FLOAT, HOVER)
val NO_GRAV_POSES = EnumSet.of(FLY, HOVER, FLOAT, SWIM)
val NO_GRAV_POSES = EnumSet.of(FLY, HOVER, SWIM)
fun identified() : List<ResourceLocation> {
return ALL_POSES.stream()
@@ -0,0 +1,125 @@
/*
* 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.entity.ai
import com.cobblemon.mod.common.entity.OmniPathingEntity
import net.minecraft.core.BlockPos
import net.minecraft.tags.FluidTags
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.util.GoalUtils
import net.minecraft.world.entity.ai.util.RandomPos
import net.minecraft.world.phys.Vec3
import java.util.*
import java.util.function.Supplier
import java.util.function.ToDoubleFunction
/*
*
* This is a rewrite of Minecraft's LandRandomPos tuned for
* water and lava walkers.
* The only real difference is movePosUpOutOfSolid() which
* moves the target pos out of liquids
*
* */
class CobblemonRandomSurfacePos {
companion object {
fun getPos(mob: PathfinderMob, radius: Int, verticalRange: Int): Vec3? {
Objects.requireNonNull(mob)
return getPos(
mob,
radius,
verticalRange,
{ pos: BlockPos? ->
if (pos != null) {
mob.getWalkTargetValue(pos).toDouble()
}
0.0
})
}
fun getPos(mob: PathfinderMob, radius: Int, yRange: Int, toDoubleFunction: ToDoubleFunction<BlockPos?>): Vec3? {
val bl = GoalUtils.mobRestricted(mob, radius)
return RandomPos.generateRandomPos(Supplier {
val blockPos = RandomPos.generateRandomDirection(mob.getRandom(), radius, yRange)
val blockPos2 = generateRandomPosTowardDirection(mob, radius, bl, blockPos)
if (blockPos2 == null) null else movePosUpOutOfSolid(mob, blockPos2)
}, toDoubleFunction)
}
fun getPosTowards(mob: PathfinderMob, radius: Int, yRange: Int, vectorPosition: Vec3): Vec3? {
val vec3 = vectorPosition.subtract(mob.getX(), mob.getY(), mob.getZ())
val bl = GoalUtils.mobRestricted(mob, radius)
return getPosInDirection(mob, radius, yRange, vec3, bl)
}
private fun getPosInDirection(
mob: PathfinderMob,
radius: Int,
yRange: Int,
vectorPosition: Vec3,
shortCircuit: Boolean
): Vec3? {
return RandomPos.generateRandomPos(mob, Supplier {
val blockPos: BlockPos? =
RandomPos.generateRandomDirectionWithinRadians(
mob.getRandom(),
radius,
yRange,
0,
vectorPosition.x,
vectorPosition.z,
(Math.PI.toFloat() / 2f).toDouble()
)
if (blockPos == null) {
return@Supplier null
} else {
val blockPos2: BlockPos? =
generateRandomPosTowardDirection(
mob,
radius,
shortCircuit,
blockPos
)
return@Supplier if (blockPos2 == null) null else movePosUpOutOfSolid(
mob,
blockPos2
)
}
})
}
fun generateRandomPosTowardDirection(
mob: PathfinderMob,
radius: Int,
shortCircuit: Boolean,
pos: BlockPos
): BlockPos? {
val blockPos = RandomPos.generateRandomPosTowardDirection(mob, radius, mob.getRandom(), pos)
return if (!GoalUtils.isOutsideLimits(blockPos, mob) && !GoalUtils.isRestricted(
shortCircuit,
mob,
blockPos
) && !GoalUtils.isNotStable(mob.getNavigation(), blockPos)
) blockPos else null
}
fun movePosUpOutOfSolid(mob: PathfinderMob, pos: BlockPos): BlockPos? {
var pos = pos
pos = RandomPos.moveUpOutOfSolid(
pos,
mob.level().maxBuildHeight
) { blockPos: BlockPos ->
if (GoalUtils.isSolid(mob, blockPos)) true
val fluidState = mob.level().getFluidState(blockPos)
!fluidState.isEmpty && (mob !is OmniPathingEntity || ((mob.canWalkOnWater() && fluidState.type == FluidTags.WATER) || (mob.canWalkOnLava() && fluidState.type == FluidTags.LAVA)))
}
return if (!GoalUtils.hasMalus(mob, pos)) pos else null
}
}
}
Loaded 100 of 1338 files, more files were not shown because too many files have changed in this diff. Show more