Compare commits

...
Author SHA1 Message Date
Polymeta 4f5799e5df yet another seasoning codec tweak
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-05-30 00:39:13 +01:00
Polymeta 54ea4903ba Merge branch 'cooking-tooltip-overhaul' into 'main'
Cooking tooltip overhaul

See merge request cable-mc/cobblemon!1687
2025-05-29 22:07:13 +00:00
Plastered_CrabandPolymeta 28ad0cce83 Cooking tooltip overhaul 2025-05-29 22:07:13 +00:00
Polymeta 7fe1929609 Merge branch 'hyper_trained_ivs' into 'main'
Hyper Trained Stat IVs

See merge request cable-mc/cobblemon!1614
2025-05-29 15:53:12 +00:00
Konoha DandPolymeta e03082a75e Hyper Trained Stat IVs 2025-05-29 15:53:11 +00:00
Polymeta 8d0c27f0e4 [skip-ci] changelog
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-05-29 16:47:14 +01:00
Polymeta 91fc2dc896 Merge branch 'spawn-rules-npc-gisketch' into 'main'
Make Spawn Rules affect NPC spawns

See merge request cable-mc/cobblemon!1682
2025-05-29 15:43:12 +00:00
Ghegi Jimenez ab1ca295a3 Make Spawn Rules affect NPC spawns
- Update NPCSpawnDetail to have autoLabel() function similar to
  PokemonSpawnDetail to support spawn rules

Ref: https://gitlab.com/cable-mc/cobblemon/-/issues/1559
2025-05-29 09:36:37 +08:00
Polymeta 5cbe937e23 Merge branch 'tatami' into 'main'
Tatami Mats

See merge request cable-mc/cobblemon!1684
2025-05-28 09:27:00 +00:00
dhi_holoandPolymeta 4fa196da94 Tatami Mats 2025-05-28 09:27:00 +00:00
Polymeta d35db46c1b Merge branch 'Add-pc-take-and-search-commands' into 'main'
Add PC Search and PC Take Commands

See merge request cable-mc/cobblemon!1677
2025-05-26 21:25:15 +00:00
Polymeta b8a6c94fcb Merge branch 'boat-behaviour' into 'main'
Add boat water behaviour

See merge request cable-mc/cobblemon!1638
2025-05-25 00:26:09 +00:00
Ghegi Jimenez b069e448c1 Add PC Search and PC Take Commands
- Add pcsearch command to search through target's PC and display pokemon info with box slots
- Add pctake command to take pokemon from target's box slot
- Pokemon are removed when using pctake on self or as server
2025-05-25 08:07:17 +08:00
Polymeta 18f41407c8 fix merge conflicts
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-05-25 00:46:35 +01:00
Polymeta b659a662a2 Merge branch 'refs/heads/main' into boat-behaviour
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonEntity.kt
2025-05-25 00:23:15 +01:00
Polymeta dbb2e45e14 Merge branch 'spicy-molang' into 'main'
Added `play_sound_on_server` and `run_molang_after` as available MoLang functions

See merge request cable-mc/cobblemon!1615
2025-05-24 23:20:14 +00:00
SpicyandPolymeta da491015a6 Added play_sound_on_server and run_molang_after as available MoLang functions 2025-05-24 23:20:14 +00:00
Polymeta 55db0d8af1 Merge branch 'battle-side-start-lang-position' into 'main'
Update battle sidestart langkeys formatting

See merge request cable-mc/cobblemon!1678
2025-05-24 23:11:38 +00:00
Ghegi Jimenez 211821bc0f Update battle sidestart langkeys formatting
This commit updates the SideStartInstruction.kt and
SideEndInstruction.kt files to use
position-specific language keys for battle side effects,
improving grammatical consistency.

**The changes**:

1. Modified SideStartInstruction.kt to use separate
language keys for ally and opponent sides

2. Updated en_us.json with new position-specific language keys
for all side effects

3. Added changelog entry documenting the improvement

This approach eliminates grammatical issues that occurred with the
previous implementation where "%1$s" was used in different positions
in sentences, making translations awkward.

The new format uses "sidestart.ally.[effect]" and
"sidestart.opponent.[effect]" keys, allowing for more natural language
in all cases.

Ref: https://gitlab.com/cable-mc/cobblemon/-/issues/984
2025-05-25 06:58:36 +08:00
Polymeta 16a21f9d2e Merge branch 'remedying-remedies' into 'main'
Remedy Rebalance

Closes #1570

See merge request cable-mc/cobblemon!1680
2025-05-24 07:46:50 +00:00
Polymeta e69060bde6 Merge branch 'fossilized-improvement' into 'main'
Fossilized Improvement (with major tangents)

Closes #1548

See merge request cable-mc/cobblemon!1673
2025-05-24 07:45:15 +00:00
dhi_holo ebdbbd5176 Indentation fixed
Looks identical to me in IntelliJ, but it's apparently different
2025-05-24 15:28:46 +10:00
dhi_holo 6e163d08f0 Merge branch 'main' into fossilized-improvement 2025-05-24 15:19:30 +10:00
dhi_holo 13c7b75b19 finished
That's the whole thing done!
2025-05-24 11:03:43 +10:00
Polymeta 1ad36e39c5 change the remedies.json layout to accommodate for individual friendshipDrops
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-05-23 23:43:58 +01:00
Polymeta f4d67b9b64 some rootblock fixes (energy root is shearable now) and energy roots run through same spreading RNG as big roots
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-05-23 22:40:16 +01:00
Polymeta 3c84afb95d Merge branch 'RestorationTankHopperFix' into 'main'
Restoration tank hopper fix

Closes #1569

See merge request cable-mc/cobblemon!1679
2025-05-23 20:43:26 +00:00
JazzMcNade 0fc1239c77 Update CHANGELOG.md 2025-05-23 13:19:40 -05:00
JazzMcNade 38a0d149c5 Update RestorationTankBlockEntity.kt 2025-05-23 13:17:42 -05:00
dhi_holo 8fee0e7f91 Begun Rebalance
Relevant conversation: https://discord.com/channels/725110639554592879/725619428384964678/1375306792523005962

Resolves #1570 indirectly
2025-05-23 13:13:07 +10:00
dhi_holo 7985aed6bf Update en_us.json
Added the three newest tags to the lang file
2025-05-23 11:02:58 +10:00
dhi_holo 5543b8a660 Merge branch 'main' into fossilized-improvement 2025-05-23 10:45:28 +10:00
Polymeta d7d855adc2 small cleanup for structure mixin
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-05-21 14:17:06 +01:00
Hiroku 4fd50b8d2b Merge branch 'y-limit-structures' into 'main'
On request of Slippy, I created a check to make sure certain structures will not spawn below the Sea Level

See merge request cable-mc/cobblemon!1672
2025-05-21 12:28:53 +00:00
Aram 53749e014b License 2025-05-21 12:22:02 +02:00
Aram b4cdf371fe It now checks for Sea Level in stead of hardcoded 65 2025-05-21 12:22:02 +02:00
Aram e0e30e7e11 Updated the Structure Check to also take StructurePieces into account 2025-05-21 12:22:02 +02:00
Aram cfec4e10cb Created a Mixin that prevents certain Structures from generating below a specific Y Level 2025-05-21 12:22:02 +02:00
Polymeta 614799cfcb Merge branch 'feature/improved-filters' into 'main'
improved filter by Name

See merge request cable-mc/cobblemon!1670
2025-05-21 09:06:34 +00:00
DavidandPolymeta ec367fd4df improved filter by Name 2025-05-21 09:06:34 +00:00
dhi_holo e415b281ee Merge branch 'main' into fossilized-improvement 2025-05-20 22:14:12 +10:00
Polymeta 265aa6e124 Merge branch 'advancing' into 'main'
forgot the optional any check

See merge request cable-mc/cobblemon!1671
2025-05-20 07:16:56 +00:00
MoeBoy76 2765a5584f forgot the optional any check 2025-05-20 14:16:29 +10:00
dhi_holo d77cb36149 Merge branch 'main' into fossilized-improvement 2025-05-20 08:37:01 +10:00
Polymeta 63f8e6251e fix for wild pokemon getting jumbled up
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-05-19 11:22:49 +01:00
Polymeta 5a3e9bb236 some tidying up and fixing upper half of rice block not growing with the lower half
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-05-19 11:17:56 +01:00
dhi_holo 2fce916f05 >:D
I feel like rice isn't as good at absorbing a fall's energy, so I think this is a fun little tweak to it
2025-05-19 16:31:29 +10:00
dhi_holo 52c4c35c0d Who up Hearting they Grains
Gotta get the top half growing in sync with the bottom half (currently doesn't grow at all once it spawns in), but progress is progress
2025-05-19 15:14:44 +10:00
dhi_holo 40f4f80d6e we're graining now
hearty grains block assets all nice and pretty
2025-05-19 12:20:33 +10:00
dhi_holo 721025281f Tags+ 2025-05-18 20:31:56 +10:00
dhi_holo 08e6f68693 Merge branch 'main' into fossilized-improvement 2025-05-18 19:55:42 +10:00
dhi_holo 1e3fcf3e84 Update hearty_grains.json 2025-05-18 19:43:47 +10:00
dhi_holo 92a67ca0ca Grain Bale
Hearty Grain Bale implemented fully
2025-05-18 19:27:58 +10:00
dhi_holo 42600fc840 Graining
Attempted to improve hearty grain for the purposes of adding the assets. Top state isn't working properly, not aging, not bonemealable, etc, but it has the right state count now!
2025-05-18 16:23:13 +10:00
Polymeta 9917b57e1a fix for pokemon getting lost in the void after using /data commands on them
Closes #1551

Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-05-18 00:59:28 +01:00
dhi_holo b5e2f69207 Update candied_apple_from_cooking_pot.json
I can see things I swear
2025-05-17 18:16:08 +10:00
dhi_holo c6c83c065b Update candied_apple_from_cooking_pot.json
forgot a thing
2025-05-17 18:03:31 +10:00
dhi_holo 687381dc1b Stick it to the fruit
Candied Apples and Berries are now held by their sticks
2025-05-17 17:23:39 +10:00
dhi_holo f088dd62bc Bands (plus tweak)
Focus, Choice, and Muscle Bands now use an optimised model and new textures, and EXP Share animated texture is now a bit stronger
2025-05-17 16:45:57 +10:00
dhi_holo 3d1f9458f6 Campfire Pot Recipies
I dislike how these items are named but I'm not about to go around dealing with renaming them to the standard format of `COLOUR_campfire_pot`
2025-05-17 13:46:40 +10:00
dhi_holo 470d3807f1 Wearable Upgrades
Glasses, EXP Share, and Rocky Helmet improved upon
2025-05-17 13:35:53 +10:00
Polymeta 1c74d498e6 Merge branch 'full_bellies_1.6' into 'main'
Full bellies 1.6 branch merge

See merge request cable-mc/cobblemon!1663
2025-05-16 20:38:57 +00:00
Polymeta 4d3c46acc4 smol cleanup and prevent remove double sounds in battleApply
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-05-16 21:26:17 +01:00
Plastered_Crab fbec87d2e0 Teehee whoopsies 2025-05-16 12:25:07 -07:00
Plastered_Crab 397f8db5a0 Can feed when in battle and still affect Fullness 2025-05-16 12:21:23 -07:00
Polymeta 026897719d Merge branch 'cooking-pot-recipes' into 'main'
We somehow forgot to add recipes to craft the campfire pot.... Now people can!

See merge request cable-mc/cobblemon!1669
2025-05-16 16:05:57 +00:00
Polymeta c4451ffb4f cleanup and licenses
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-05-16 17:04:58 +01:00
Plastered_Crab 710b6404e7 We somehow forgot to add recipes to craft the campfire pot.... Now people testing this in survival can do so. 2025-05-16 08:31:45 -07:00
Polymeta b8f59110f0 properly detect tags inside entity.find_nearby_block molang
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-05-16 16:04:45 +01:00
Polymeta 0a0c1edffc Merge branch 'cooking-pot-recipe-category-tweak' into 'main'
Tweaks to Category names of recipes that accept Seasoning data + tab icons

See merge request cable-mc/cobblemon!1668
2025-05-16 09:20:35 +00:00
plastered_crab 18403c833d Merge remote-tracking branch 'origin/full_bellies_1.6' into full_bellies_1.6 2025-05-16 00:07:50 -07:00
plastered_crab b9824e5708 Remove redundant coerce 2025-05-15 23:30:11 -07:00
dhi_holo 57c1201ad0 a p p l e (and tagging)
Improved tagging for a couple things, made Candied Apples able to leave Leftovers
2025-05-16 15:18:10 +10:00
dhi_holo 4637bef5d2 Recipes and Tabs
Revised type boosting item recipes, and added some cooking items to relevant creative tabs
2025-05-16 14:19:30 +10:00
Plastered_Crab 37e0030d08 Tweaks to Category names of recipes that accept Seasoning data as well as Category icons for each tab 2025-05-15 18:56:54 -07:00
dhi_holo 6207a01ae3 Merge branch 'main' into fossilized-improvement 2025-05-16 10:49:21 +10:00
Plastered_Crab 40aaef68f9 Fixes for full bellies MR 2025-05-15 15:49:18 -07:00
Polymeta 2dd0d3dcf1 Merge branch 'cooking-recipe-overhaul' into 'main'
Vera's Crafting and Cooking recipe overhaul (with some fixes)

See merge request cable-mc/cobblemon!1665
2025-05-15 19:34:43 +00:00
Plastered_Crab cc5b65a1dd Fix some things 2025-05-15 12:33:38 -07:00
plastered_crab f4e242a270 clean up some todos and CobblemonItems a bit 2025-05-15 10:15:14 -07:00
plastered_crab da3a513905 Merge remote-tracking branch 'origin/cooking-recipe-overhaul' into cooking-recipe-overhaul 2025-05-15 09:39:33 -07:00
plastered_crab 33af6a1557 Fixing Shapeless Recipes not detecting multiple of a single item 2025-05-15 09:39:18 -07:00
dhi_holo 62b07f7f8a whoopsies
typos revoked
2025-05-15 23:35:22 +10:00
dhi_holo 7bddbcfc67 YAY! MURDAH!
Removed unneeded recipes, fixed ordering in the rabbit stew recipe, fixed broken zinc recipe
2025-05-15 23:06:09 +10:00
dhi_holo 993c7a5cf0 Recipe Advancements
Added advancements for all Campfire Pot recipes, to make them unlockable during gameplay
Began cleaning up now redundant recipes/advancements due to cooking pot overwriting older recipes
2025-05-15 22:25:31 +10:00
dhi_holo 44da19f3e0 Translations Added
Tags added, and new foods that I'm fairly sure don't have translation strings elsewhere (if I'm wrong that can be fixed later, only ones I think are at risk are candied apple and fried rice)
2025-05-15 20:21:32 +10:00
dhi_holo 6d97c6ea34 Update rabbit_stew_from_cooking_pot.json
Removed unnecessary bowl from recipe (mushroom stew bowl gets consumed in the cook)
2025-05-15 19:44:00 +10:00
plastered_crab 2a55ef114d Regional Foods starting to work! 2025-05-15 01:49:40 -07:00
plastered_crab c236d8147a MORE MORE MORE changes to cooking recipes >:D 2025-05-14 23:54:38 -07:00
dhi_holo cf90bf0b0f Pots!
+ Allowed all 7 Apricorn Sprouts and Saccharine Sapling to be put in Flower Pots
+ Added all 9 of our potted blocks to the `#minecraft:flower_pots` block tag
+ Minor fix to the Potted Pep-Up Flower loot table
+ Fixed the texture name for Pep-Up Flower block
2025-05-15 16:44:34 +10:00
dhi_holo b109ff6edf Merge branch 'main' into fossilized-improvement 2025-05-15 14:45:42 +10:00
plastered_crab 4ea288cc34 MORE changes to cooking recipes >:D 2025-05-14 19:08:23 -07:00
plastered_crab 98540294b1 Start of big rework to all cooking recipes >:D 2025-05-14 16:20:52 -07:00
Polymeta 5535a6a7cb Merge branch 'cooking-fixes' into 'main'
Fixes for merged bait effects and Flavor tooltips showing on every item

See merge request cable-mc/cobblemon!1664
2025-05-14 22:00:01 +00:00
plastered_crab dd426423a1 Remove Mild flavor on tooltips 2025-05-14 12:29:56 -07:00
plastered_crab 6237e44e7c Fix Seasoning tooltip showing on every item 2025-05-14 12:08:32 -07:00
plastered_crab 09561f736a Rarity Bucket tiers can stack (with diminished returns) 2025-05-14 11:53:21 -07:00
plastered_crab 14e4fe66ac Add new item tags 2025-05-13 20:20:33 -07:00
plastered_crab 975c68cea0 Add new item tags 2025-05-13 20:17:44 -07:00
plastered_crab 6b77adbcef Remove Milking stuff to new branch, metabolism gets ticked from Party and Pasture 2025-05-13 19:55:40 -07:00
plastered_crab d12a8704a9 Merge branch 'refs/heads/main' into full_bellies_1.6 2025-05-13 17:48:48 -07:00
Apion 52fc6b2575 Merge branch 'Lure-Cake-Crash-Fix' into 'main'
Lure cake crash fix

See merge request cable-mc/cobblemon!1662
2025-05-14 00:42:12 +00:00
Plastered_CrabandApion 68ffe0bdbc Lure cake crash fix 2025-05-14 00:42:12 +00:00
Plastered_Crab 4734c0a3cb Changes to Shuckle giving berry juice 2025-05-13 14:28:29 -07:00
Plastered_Crab e16ae2544c Fix sounds on fullness branch 2025-05-13 10:05:21 -07:00
Plastered_Crab cff65ab22e Merge branch 'main' into full_bellies_1.6 2025-05-13 08:33:15 -07:00
Polymeta 1d550b2cac mixin cleanup
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-05-13 16:27:07 +01:00
Polymeta c859cbae76 small seasoning codec change to fix errors when logging into servers
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-05-13 16:26:35 +01:00
Polymeta 767f820b7a Merge branch 'food-fix' into 'main'
Allow for Food and Mob effects to be optional (fixes crash)

See merge request cable-mc/cobblemon!1661
2025-05-13 06:19:57 +00:00
dhi_holo 06e4783f16 Update resurrect_pokemon.json 2025-05-13 16:11:40 +10:00
dhi_holo fe83b9d58e Merge branch 'main' into fossilized-improvement 2025-05-13 13:26:11 +10:00
Plastered_Crab 17f686488e Added fullness values to new Poke Foods 2025-05-12 16:52:17 -07:00
Plastered_Crab db574332e5 Merge branch 'main' into full_bellies_1.6 2025-05-12 15:55:10 -07:00
Plastered_Crab b88f4335ee Fullness now saves on exit and displays in Summary "Other" menu 2025-05-12 15:08:39 -07:00
plastered_crab 1bfe5e2044 Fix Item Tags after merge 2025-05-10 23:33:40 -07:00
plastered_crab ace65eda66 Merge branch 'refs/heads/main' into full_bellies_1.6
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/Cobblemon.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonSounds.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/tags/CobblemonItemTags.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/item/berry/FriendshipRaisingBerryItem.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/item/berry/HealingBerryItem.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/item/berry/PortionHealingBerryItem.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/item/berry/StatusCuringBerryItem.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/item/interactive/BerryJuiceItem.kt
#	common/src/main/resources/assets/cobblemon/lang/en_us.json
#	common/src/main/resources/assets/cobblemon/sounds.json
2025-05-10 22:44:39 -07:00
dhi_holo 874a5d2e8f Update CHANGELOG.md
forgot one sentence, whoopsie daisies
2025-05-03 16:37:08 +10:00
dhi_holo 70e49bbbdb More Tweaks
Eject Pack was fixed at some point, so I readded the recipe, loaded dice was tweaked, and started tweaking the consumed tags
2025-05-03 16:06:42 +10:00
dhi_holo 39fce8ca06 Cupping
Cups are now craftable for real, too, praise the drinkingssiah
2025-05-03 15:21:50 +10:00
dhi_holo d288fe46da Replace Pot Recipes
They were weird, now they're slightly less weird and feel more reasonable imo
2025-05-03 14:47:16 +10:00
dhi_holo cefc97320a Merge branch 'main' into fossilized-improvement 2025-05-03 14:38:35 +10:00
dhi_holo 374fc44089 Fossil Analyzer Visual Tweak
the top texture is now good
2025-05-02 18:30:30 +10:00
dhi_holo f7664a04db L-L-L-Lava
Wrong brainrot, ah well, funny achievement as it should be

Also changed the advancement icon for the galar fossils so it isn't just the same as the regular fossil resurrection advancement
2025-05-02 17:47:25 +10:00
dhi_holo 2d772f5bf7 Update resurrect_galar_fossil.json
Should make one issue of the advancement no longer present, but the advancement itself doesn't work, pls fix Moe
2025-05-02 09:26:23 +10:00
dhi_holo 63af1713b9 Merge branch 'main' into fossilized-improvement 2025-05-01 15:39:28 +10:00
dhi_holo 8fecb12829 Three and a fix or two
Added Metal Alloy, Scroll of Darkness, and Scroll of Waters
Changed instances of `Hearty_Grains` to `HEARTY_GRAINS` to match conventions
Changed the Kings Rock wearable model to Gest's plane version
2025-05-01 15:15:26 +10:00
dhi_holo d0b50a1ee0 Sprites!
Clear Amulet, Grip Claw, Lagging Tail, and Luminous Moss now have sprites! Praise Vera!
2025-05-01 13:45:41 +10:00
dhi_holo a71bad07bd One. Year. Later.
Re-enabled the commented out held items, which almost resolves #1548, as they are still awaiting textures. But, I wanted to work with jsons, so this commit was made.
I also cleaned up/improved some recipe advancement jsons to be more accurate when reasonable
2025-04-29 20:37:22 +10:00
Hiroku 1769af7385 Merge branch 'main' into 'boat-behaviour'
# Conflicts:
#   common/src/main/kotlin/com/cobblemon/mod/common/util/adapters/RidingBehaviourSettingsAdapter.kt
2025-04-27 08:54:17 +00:00
Landon Wills 88e38bfa1a Remove additional references to submarine behaviour 2025-04-26 11:48:09 -04:00
Landon Wills 507a9a54b5 Remove submarine behaviour 2025-04-26 11:47:20 -04:00
Landon Wills 0b72a072bd Fix merge issues 2025-04-26 11:40:44 -04:00
Landon Wills c927cae609 Merge branch 'refs/heads/main' into water-mount-behaviours
# Conflicts:
#	common/src/main/java/com/cobblemon/mod/common/mixin/client/CameraMixin.java
#	common/src/main/kotlin/com/cobblemon/mod/common/api/riding/behaviour/types/GenericSwimBehaviour.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/riding/behaviour/types/air/BirdBehaviour.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/riding/behaviour/types/air/GliderBehaviour.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/riding/behaviour/types/air/JetBehaviour.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/riding/behaviour/types/land/HorseBehaviour.kt
2025-04-26 11:21:33 -04:00
dhi_holo 10bf69a79b Minor Ball Recipe Tweaks
Just cleans up three recipes
2025-04-25 17:11:16 +10:00
dhi_holo 172293c813 Merge branch 'main' into fossilized-improvement 2025-04-25 17:08:43 +10:00
dhi_holo 9bb653b8b7 Update resurrect_galar_fossil.json
should match upcoming clean up
2025-04-25 17:08:28 +10:00
Landon Wills a7b55e5d03 Add submarine behaviour 2025-04-23 05:47:56 -04:00
Landon Wills 28db655376 Fix issues with third person camera offsets 2025-04-23 05:47:40 -04:00
Landon Wills 006736cd71 Refactor behaviour names, hook up max up step to riding controller 2025-04-23 03:09:57 -04:00
Landon Wills f240dc1963 Prevent auto step for boat behaviour 2025-04-22 10:46:20 -04:00
Landon Wills 535c5f2e67 Rename to boat behaviour 2025-04-22 09:55:39 -04:00
Landon Wills 8b209e081b Hook up skill and stamina 2025-04-22 09:29:26 -04:00
Landon Wills f7f276ecfb Clean things up, add acceleration logic 2025-04-22 08:37:32 -04:00
dhi_holo fc0cd0dac4 improvement phase 1
new advancement to trigger when reviving galar hybrid fossils, to help inform players of the combinations needed for it
It doesn't work yet, as the conditions don't actually exist, but I'm just making a commit for it to complain using
2025-04-21 07:46:41 +10:00
Landon Wills 019245472f Add riding stat calcs for jump and speed, some experimenting with fov 2025-04-20 07:55:14 -04:00
Landon Wills cefb502f10 Prevent 360 look on boat controller, add jump, add momentum logic when transitioning from forward/backwards input 2025-04-20 07:11:37 -04:00
Landon Wills ee5e4d33b2 Fix merge issues 2025-04-19 23:14:08 -04:00
Landon Wills 0fd954ffa5 Merge branch 'refs/heads/riding-debug-ui' into water-mount-behaviours 2025-04-19 23:10:49 -04:00
Landon Wills 60d3d737f9 Merge branch 'refs/heads/main' into water-mount-behaviours 2025-04-19 23:10:36 -04:00
Landon Wills 1caf963323 Start implementing boat behaviour 2025-04-14 00:43:47 -04:00
Landon Wills bb94f872a9 Fix player shrinking when riding pokemon that's getting retrieved to pokeball 2025-04-13 21:44:06 -04:00
plastered_crab 65ab0f48e4 - ported over Fullness Mechanics from 1.20.1 to 1.21 2024-07-28 15:07:39 -07:00
675 changed files with 10061 additions and 4755 deletions
+1
View File
@@ -7,6 +7,7 @@
- Added `/changewallpaper <player> <boxNumber> <wallpaper>` command to change a box wallpaper through commands.
- Added `/renamebox <player> <boxNumber> <name>` command to rename a PC box through commands.
- Added IVs and EVs stat displays in PC.
- improved filter by Name, now is available search by exact names or partial matches, for example "cha" will show Charmander, Charmeleon and Charizard among others that match.
### Developer
- Added `RenamePCBoxEvent.Pre` and `RenamePCBoxEvent.Post` events to prevent players from renaming a box or changing their input.
+28 -3
View File
@@ -21,8 +21,15 @@
- Fixed Moon Ball moon phase logic to actually work correctly
- Added `translucent_cull` boolean option into resolver's layer to allow for translucent textures with culling
- Added [LambDynamicLights](https://modrinth.com/mod/lambdynamiclights) support for items held by Pokémon.
- Added the Clear Amulet, Grip Claw, Lagging Tail, Luminous Moss, Metal Alloy, Scroll of Darkness, Scroll of Waters
- Added Recipes for Masterpiece Cup, Eject Pack
- Added modification to Minecraft Creative Inventory search to account for item names that contain `poké` when input contains `poke`.
- Added Campfire Pot as well as loads of new food items (Poke Puffs, Ponigiri, Sinister Tea, etc)
- Added Hearty Grains, a new crop used in the new cooking mechanic
- Added Tatami blocks and Tatami Mat blocks, made from Hearty Grain, for decorating builds
- Added `/pcsearch <player> <pokemonProperties>` command that searches for a specific Pokémon within a player's PC.
- Added `/pctake <player> <box> <slot>` command that takes a specific Pokémon from a player's PC. Removes the pokemon if target is self or ran from the server.
- Added Hyper Training items (IV Modification) as well as some additional candy items to do so (Health Candy, Sickly Candy)
### Pokémon Added
@@ -116,12 +123,13 @@
- Made Blocks of Gold count as Big Nuggets when held by a Pokémon (for Fling functionality)
- Players can now eat Sweet and Tart Apples, Whipped Dreams, and the Alcremie Sweets.
- Updated Sweet and Tart Apple sprites
- Updated the following recipes: Air Balloon, Assault Vest, Binding Band, Black Belt, Blunder Policy, Choice Band, Choice Scarf, Cleanse Tag, Covert Cloak, Destiny Knot, Eject Button, Expert Belt, Focus Band, Focus Sash, Magnet, Metronome, Muscle Band, Power Anklet, Power Band, Power Belt, Power Bracer, Power Lens, Power Weight, Protective Pads, Protein, Punching Glove, Reaper Cloth, Rocky Helmet, Room Service, Sachet, Safety Goggles, Silk Scarf, Spell Tag, Utility Umbrella, Weakness Policy, Zinc
- Added alternate ingredient options to the following recipes: Cell Battery, Charcoal Stick, Dragon Fang, Miracle Seed, Mystic Water, Never Melt Ice, Twisted Spoon, Damp Rock, Heat Rock, Icy Rock, Smooth Rock
- Updated the following recipes: Air Balloon, Assault Vest, Binding Band, Black Belt, Blunder Policy, Choice Band, Choice Scarf, Cleanse Tag, Covert Cloak, Destiny Knot, Eject Button, Expert Belt, Focus Band, Focus Sash, Magnet, Metronome, Muscle Band, Power Anklet, Power Band, Power Belt, Power Bracer, Power Lens, Power Weight, Protective Pads, Protein, Punching Glove, Reaper Cloth, Rocky Helmet, Room Service, Sachet, Safety Goggles, Silk Scarf, Spell Tag, Utility Umbrella, Weakness Policy, Zinc, Chipped Pot, Cracked Pot, Unremarkable Cup, Loaded Dice, Charcoal Stick, Dragon Fang, Miracle Seed, Mystic Water, Never Melt Ice, Twisted Spoon, Black Glasses, Fairy Feather, Hard Stone, Silver Powder, Soft Sand
- Added alternate ingredient options to the following recipes: Cell Battery, Damp Rock, Heat Rock, Icy Rock, Smooth Rock
- Grouped together some recipes within the Recipe Book. Groups include: the seven basic Poké Balls, the seven basic Ancient Poké Balls, Gilded Chests, Pokedexes, and the Weather Rocks.
- Updated some item tags to better integrate behaviours between Cobblemon, Vanilla Minecraft, and other mods
- Removed Cooked Meat, Raw Meat, Protein Ingredients, and Zinc Ingredients, the first two are now using `c` namespace tags, the latter have better integrated use of tags within their recipes which removes need for custom tags.
- Added our seeds tag into `#c:seeds`, which is now made use of for the Miracle Seed recipe.
- Added our seeds tag into `#c:seeds`, which is now made use of for the Miracle Seed recipe.
- Added herbs and snowballs to the consumable in PvE and Wild battle tags
- Tweaked the Natural Materials Vanilla file to fit with the changes to tags
- Substantially optimised spawning checks mainly by front-loading biome filtering.
- When using the `cobblemon` or `generation_9` capture calculators a critical capture with a single shake will always play for successful captures when you've already registered the Pokémon as caught in your Pokédex.
@@ -136,10 +144,15 @@
- Improved parity with vanilla mobs' drop behavior; loot and XP drop on death instead of after the entire death sequence finishes.
- Quirk animations no longer play in the battle GUI since they were pretty distracting.
- A number of Pokemon that float above the ground visually (Gastly, Klingklang, etc.) are no longer considered to be touching the ground
- Changed the recipes for Level, Lure, and Moon Balls to be cleaner
- Renamed `pokemonId` and `type` to `species` in relevant Advancement triggers for conformity, this is a breaking change.
- Added a separate `species` argument to the `pick_starter`, `pasture_use` and `resurrect_pokemon` Advancement triggers.
- Made `CobblemonAgingDespawner` thresholds configurable via the config file.
- Tweaked Berry flavor data to balance cooking pot mechanics
- Made Red, Yellow, Green, Blue, Pink, Black, and White Apricorn Sprout and Saccharine Sapling able to be placed into flower pots.
- Added all Potted Apricorn Sprouts, Potted Saccharine Saplings, and Potted Pep-Up Flowers to the `#minecraft:flower_pots` block tag
- Corrected the healing values of the remedies and energy root, and reintroduced the friendship lowering mechanics for them, revivl herb, and heal powder
- Updated battle language keys for side effects to use position-specific formats, improving grammatical consistency
### Fixes
- Fixed Particles sometimes facing the wrong direction (looking at you, Swords Dance)
@@ -180,6 +193,10 @@
- Fixed the `dimensions` spawning condition using the incorrect ResourceLocation, causing it to not function properly.
- Fix issue where locator X axis was not aligned with blockbench.
- Fix issue where particle effects that play on frame 1 on sendout would never play (Gastly)
- Fixed the block texture name for the Pep-Up Flower being inaccurate
- Fixed issue where the restoration tank would not accept valid items from a hopper.
- Fixed energy root not being shearable
- Fixed energy root always spreading into more energy roots instead of by chance (same as big root)
### Developer
- A finished battle now has winners and losers set inside of `PokemonBattle` instead of them always being empty.
@@ -206,6 +223,8 @@
}
}
`
- The IVs class has now been extended to include Hyper Trained values.
- Added `Pokemon#hyperTrainIV()` and `IVs#setHyperTrainedIV(Stat, Int)`
### MoLang & Datapacks
- The following usages for item predicates can now use item conditions like advancements do, you can learn about them in the [Minecraft wiki](https://minecraft.wiki/w/Advancement_definition#minecraft:filled_bucket)
@@ -225,8 +244,14 @@
- Fixed `heldItem` property inside spawn files not working and causing crashes
- Fixed `spawn_bedrock_particles` MoLang causing crashes when used in a server environment
- The Pokédex form lang key definition now follows `cobblemon.ui.pokedex.info.form.{species}-{formname}` instead of `cobblemon.ui.pokedex.info.form.{formname}`.
- Added `play_sound_on_server` as an available Molang function for the `worldHolder` & `player` functions.
- Added `run_molang_after` as an available Molang function for the `entity` functions when schedulable.
- Added an optional parameter for `run_molang` to schedule the function.
- Added `labels` & `has_label` as available Molang functions for the `speciesFunctions`
- Added datapack-defined starter categories via `data/<namespace>/starters/*.json`, with built-in fallback and `useConfigStarters` merge option.
- The format of the `remedies.json` file has changed to allow for individual friendshipDrop amounts per remedy
- Fixed `entity.find_nearby_block` causing crashes when attempting to use a block tag
- Spawn Filters can now access `v.spawn.class` to get the identifier of an NPC class for when trying to influence NPC spawns
## [1.6.1 (January 26th, 2025)](#1-6-1)
@@ -1,119 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.api.orientation.OrientationController;
import com.cobblemon.mod.common.api.riding.Seat;
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate;
import com.cobblemon.mod.common.client.render.MatrixWrapper;
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState;
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel;
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.mojang.authlib.GameProfile;
import net.minecraft.client.CameraType;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.NotNull;
import org.joml.Matrix3f;
import org.joml.Vector3f;
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.CallbackInfoReturnable;
import java.util.Map;
@Mixin(LocalPlayer.class)
public abstract class LocalPlayerMixin extends AbstractClientPlayer implements OrientationControllable {
@Shadow
private float jumpRidingScale;
public LocalPlayerMixin(ClientLevel clientLevel, GameProfile gameProfile) {
super(clientLevel, gameProfile);
}
@Inject(method = "getJumpRidingScale", at = @At("HEAD"), cancellable = true)
public void modifyJumpRidingScale(CallbackInfoReturnable<Float> cir) {
LocalPlayer player = (LocalPlayer) (Object) this;
if (player.isPassenger() && player.getVehicle() instanceof PokemonEntity pokemonEntity) {
var rideValue = pokemonEntity.<Float>ifRidingAvailableSupply(null, (behaviour, settings, state) -> {
if (behaviour.canJump(settings, state, pokemonEntity, player)) return null;
return behaviour.setRideBar(settings, state, pokemonEntity, player);
});
if (rideValue != null) {
cir.setReturnValue(rideValue);
}
}
}
@Override
public HitResult pick(double hitDistance, float partialTicks, boolean hitFluids) {
Entity vehicle = this.getVehicle();
if (vehicle instanceof PokemonEntity pokemonEntity) {
int seatIndex = pokemonEntity.getPassengers().indexOf(this);
Seat seat = pokemonEntity.getSeats().get(seatIndex);
PokemonClientDelegate delegate = (PokemonClientDelegate) pokemonEntity.getDelegate();
MatrixWrapper locator = delegate.getLocatorStates().get(seat.getLocator());
if (locator == null) {
super.pick(hitDistance, partialTicks, hitFluids);
}
Vec3 locatorOffset = new Vec3(locator.getMatrix().getTranslation(new Vector3f()));
OrientationController controller = this.getOrientationController();
float currEyeHeight = this.getEyeHeight();
Vector3f offset = new Vector3f(new Vector3f(0f, currEyeHeight - (this.getBbHeight() / 2), 0f));
if (Minecraft.getInstance().options.getCameraType() == CameraType.FIRST_PERSON) {
PosableModel model = VaryingModelRepository.INSTANCE.getPoser(pokemonEntity.getPokemon().getSpecies().getResourceIdentifier(), new FloatingState());
offset.add(cobblemon$getFirstPersonOffset(model, seat));
}
Matrix3f orientation = controller.isActive() && controller.getOrientation() != null ? controller.getOrientation() : new Matrix3f();
Vec3 rotatedEyeHeight = new Vec3(orientation.transform(offset));
Vec3 eyePosition = locatorOffset.add(pokemonEntity.position()).add(rotatedEyeHeight);
Vec3 viewVector = this.getViewVector(partialTicks);
Vec3 viewDistanceVector = eyePosition.add(viewVector.x * hitDistance, viewVector.y * hitDistance, viewVector.z * hitDistance);
return this.level()
.clip(
new ClipContext(
eyePosition, viewDistanceVector, ClipContext.Block.OUTLINE, hitFluids ? net.minecraft.world.level.ClipContext.Fluid.ANY : net.minecraft.world.level.ClipContext.Fluid.NONE, this
)
);
}
return super.pick(hitDistance, partialTicks, hitFluids);
}
@Unique
private static @NotNull Vector3f cobblemon$getFirstPersonOffset(PosableModel model, Seat seat) {
Map<String, Vec3> cameraOffsets = model.getFirstPersonCameraOffset();
if (cameraOffsets.containsKey(seat.getLocator())) {
return cameraOffsets.get(seat.getLocator()).toVector3f();
} else {
return new Vector3f(0f, 0f, 0f);
}
}
}
@@ -0,0 +1,67 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.world.CobblemonStructureIDs;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.core.Holder;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.LevelHeightAccessor;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.levelgen.RandomState;
import net.minecraft.world.level.levelgen.structure.Structure;
import net.minecraft.world.level.levelgen.structure.StructurePiece;
import net.minecraft.world.level.levelgen.structure.StructureStart;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Predicate;
@Mixin(Structure.class)
public abstract class StructureMixin {
// Define a set of structures that should not spawn below Sea Level
@Unique
private static final Set<ResourceLocation> RESTRICTED_STRUCTURES = new HashSet<>(Arrays.asList(
CobblemonStructureIDs.STONJOURNER_HENGE,
CobblemonStructureIDs.LUNA_HENGE,
CobblemonStructureIDs.SOL_HENGE
));
@Inject(method = "generate", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/levelgen/structure/StructureStart;isValid()Z"),
cancellable = true)
public void cobblemon$isValid(RegistryAccess registryAccess, ChunkGenerator chunkGenerator, BiomeSource biomeSource,
RandomState randomState, StructureTemplateManager structureTemplateManager, long seed,
ChunkPos chunkPos, int references, LevelHeightAccessor heightAccessor,
Predicate<Holder<Biome>> validBiome,
CallbackInfoReturnable<StructureStart> cir,
@Local StructureStart structureStart) {
ResourceLocation structureKey = registryAccess.registryOrThrow(Registries.STRUCTURE).getKey((Structure) (Object) this);
if(!RESTRICTED_STRUCTURES.contains(structureKey)) {
return;
}
for (StructurePiece piece : structureStart.getPieces()) {
if (piece.getBoundingBox().minY() < chunkGenerator.getSeaLevel()) {
cir.setReturnValue(StructureStart.INVALID_START);
return;
}
}
}
}
@@ -56,7 +56,7 @@ public abstract class ClientRecipeBookMixin {
case MISC -> var7 = CobblemonRecipeCategories.COOKING_POT_MISC.toVanillaCategory();
case FOODS -> var7 = CobblemonRecipeCategories.COOKING_POT_FOODS.toVanillaCategory();
case MEDICINES -> var7 = CobblemonRecipeCategories.COOKING_POT_MEDICINES.toVanillaCategory();
case BAITS -> var7 = CobblemonRecipeCategories.COOKING_POT_BAITS.toVanillaCategory();
case COMPLEX_DISHES -> var7 = CobblemonRecipeCategories.COOKING_POT_COMPLEX_DISHES.toVanillaCategory();
default -> throw new MatchException(null, null);
}
cir.setReturnValue(var7);
@@ -10,27 +10,51 @@ package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.CobblemonNetwork;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.api.orientation.OrientationController;
import com.cobblemon.mod.common.api.riding.Seat;
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate;
import com.cobblemon.mod.common.client.render.MatrixWrapper;
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState;
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel;
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.net.messages.server.orientation.ServerboundUpdateOrientationPacket;
import com.mojang.authlib.GameProfile;
import net.minecraft.client.CameraType;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.NotNull;
import org.joml.Matrix3f;
import org.joml.Vector3f;
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 org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.Map;
@Mixin(LocalPlayer.class)
public abstract class LocalPlayerMixin extends LivingEntity {
public abstract class LocalPlayerMixin extends AbstractClientPlayer implements OrientationControllable{
@Unique Matrix3f cobblemon$lastOrientation;
protected LocalPlayerMixin(EntityType<? extends LivingEntity> entityType, Level level) {
super(entityType, level);
@Shadow
private float jumpRidingScale;
public LocalPlayerMixin(ClientLevel clientLevel, GameProfile gameProfile) {
super(clientLevel, gameProfile);
}
// @Inject(method = "sendPosition", at = @At("TAIL"))
@@ -76,4 +100,73 @@ public abstract class LocalPlayerMixin extends LivingEntity {
if (!(this instanceof OrientationControllable controllable)) return;
controllable.getOrientationController().setActive(false);
}
@Inject(method = "getJumpRidingScale", at = @At("HEAD"), cancellable = true)
public void modifyJumpRidingScale(CallbackInfoReturnable<Float> cir) {
LocalPlayer player = (LocalPlayer) (Object) this;
if (player.isPassenger() && player.getVehicle() instanceof PokemonEntity pokemonEntity) {
var rideValue = pokemonEntity.<Float>ifRidingAvailableSupply(null, (behaviour, settings, state) -> {
if (behaviour.canJump(settings, state, pokemonEntity, player)) return null;
return behaviour.setRideBar(settings, state, pokemonEntity, player);
});
if (rideValue != null) {
cir.setReturnValue(rideValue);
}
}
}
@Override
public HitResult pick(double hitDistance, float partialTicks, boolean hitFluids) {
Entity vehicle = this.getVehicle();
if (vehicle instanceof PokemonEntity pokemonEntity) {
int seatIndex = pokemonEntity.getPassengers().indexOf(this);
Seat seat = pokemonEntity.getSeats().get(seatIndex);
PokemonClientDelegate delegate = (PokemonClientDelegate) pokemonEntity.getDelegate();
MatrixWrapper locator = delegate.getLocatorStates().get(seat.getLocator());
if (locator == null) {
super.pick(hitDistance, partialTicks, hitFluids);
}
Vec3 locatorOffset = new Vec3(locator.getMatrix().getTranslation(new Vector3f()));
OrientationController controller = this.getOrientationController();
float currEyeHeight = this.getEyeHeight();
Vector3f offset = new Vector3f(new Vector3f(0f, currEyeHeight - (this.getBbHeight() / 2), 0f));
if (Minecraft.getInstance().options.getCameraType() == CameraType.FIRST_PERSON) {
PosableModel model = VaryingModelRepository.INSTANCE.getPoser(pokemonEntity.getPokemon().getSpecies().getResourceIdentifier(), new FloatingState());
offset.add(cobblemon$getFirstPersonOffset(model, seat));
}
Matrix3f orientation = controller.isActive() && controller.getOrientation() != null ? controller.getOrientation() : new Matrix3f();
Vec3 rotatedEyeHeight = new Vec3(orientation.transform(offset));
Vec3 eyePosition = locatorOffset.add(pokemonEntity.position()).add(rotatedEyeHeight);
Vec3 viewVector = this.getViewVector(partialTicks);
Vec3 viewDistanceVector = eyePosition.add(viewVector.x * hitDistance, viewVector.y * hitDistance, viewVector.z * hitDistance);
return this.level()
.clip(
new ClipContext(
eyePosition, viewDistanceVector, ClipContext.Block.OUTLINE, hitFluids ? net.minecraft.world.level.ClipContext.Fluid.ANY : net.minecraft.world.level.ClipContext.Fluid.NONE, this
)
);
}
return super.pick(hitDistance, partialTicks, hitFluids);
}
@Unique
private static @NotNull Vector3f cobblemon$getFirstPersonOffset(PosableModel model, Seat seat) {
Map<String, Vec3> cameraOffsets = model.getFirstPersonCameraOffset();
if (cameraOffsets.containsKey(seat.getLocator())) {
return cameraOffsets.get(seat.getLocator()).toVector3f();
} else {
return new Vector3f(0f, 0f, 0f);
}
}
}
@@ -50,10 +50,10 @@ public class RecipeBookCategoriesMixin {
private static void cobblemon$addRecipeBookType(CallbackInfo ci) {
ArrayList<RecipeBookCategories> types = new ArrayList<>(List.of($VALUES));
types.add(cobblemon$createCategory("COOKING_POT_SEARCH", $VALUES.length, new ItemStack(Items.COMPASS)));
types.add(cobblemon$createCategory("COOKING_POT_FOODS", $VALUES.length + 1, new ItemStack(CobblemonItems.ROASTED_LEEK)));
types.add(cobblemon$createCategory("COOKING_POT_MEDICINES", $VALUES.length + 2, new ItemStack(CobblemonItems.HP_UP)));
types.add(cobblemon$createCategory("COOKING_POT_BAITS", $VALUES.length + 3, new ItemStack(CobblemonItems.PERSIM_BERRY)));
types.add(cobblemon$createCategory("COOKING_POT_MISC", $VALUES.length + 4, new ItemStack(CobblemonItems.MIRROR_HERB)));
types.add(cobblemon$createCategory("COOKING_POT_FOODS", $VALUES.length + 1, new ItemStack(CobblemonItems.LEEK_AND_POTATO_STEW)));
types.add(cobblemon$createCategory("COOKING_POT_MEDICINES", $VALUES.length + 2, new ItemStack(CobblemonItems.POTION)));
types.add(cobblemon$createCategory("COOKING_POT_COMPLEX_DISHES", $VALUES.length + 3, new ItemStack(CobblemonItems.POKE_PUFF)));
types.add(cobblemon$createCategory("COOKING_POT_MISC", $VALUES.length + 4, new ItemStack(CobblemonItems.PROTEIN)));
$VALUES = types.toArray(RecipeBookCategories[]::new);
}
@@ -64,7 +64,7 @@ public class RecipeBookCategoriesMixin {
CobblemonRecipeCategories.COOKING_POT_SEARCH.toVanillaCategory(),
CobblemonRecipeCategories.COOKING_POT_FOODS.toVanillaCategory(),
CobblemonRecipeCategories.COOKING_POT_MEDICINES.toVanillaCategory(),
CobblemonRecipeCategories.COOKING_POT_BAITS.toVanillaCategory(),
CobblemonRecipeCategories.COOKING_POT_COMPLEX_DISHES.toVanillaCategory(),
CobblemonRecipeCategories.COOKING_POT_MISC.toVanillaCategory());
cir.setReturnValue(var10000);
return;
@@ -515,6 +515,30 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
@JvmField
val HEARTY_GRAINS = this.create("hearty_grains", HeartyGrainsBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).ignitedByLava().mapColor(MapColor.PLANT).noCollission().randomTicks().instabreak().sound(CobblemonSounds.VIVICHOKE_SOUNDS)))
@JvmField
val POTTED_RED_APRICORN_SAPLING = this.create("potted_red_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(RED_APRICORN_SAPLING))
@JvmField
val POTTED_YELLOW_APRICORN_SAPLING = this.create("potted_yellow_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(YELLOW_APRICORN_SAPLING))
@JvmField
val POTTED_GREEN_APRICORN_SAPLING = this.create("potted_green_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(GREEN_APRICORN_SAPLING))
@JvmField
val POTTED_BLUE_APRICORN_SAPLING = this.create("potted_blue_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(BLUE_APRICORN_SAPLING))
@JvmField
val POTTED_PINK_APRICORN_SAPLING = this.create("potted_pink_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(PINK_APRICORN_SAPLING))
@JvmField
val POTTED_BLACK_APRICORN_SAPLING = this.create("potted_black_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(BLACK_APRICORN_SAPLING))
@JvmField
val POTTED_WHITE_APRICORN_SAPLING = this.create("potted_white_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(WHITE_APRICORN_SAPLING))
@JvmField
val POTTED_SACCHARINE_SAPLING = this.create("potted_saccharine_sapling", BlocksInvoker.createFlowerPotBlock(SACCHARINE_SAPLING))
@JvmField
val HEARTY_GRAIN_BALE = this.create("hearty_grain_bale", HeartyGrainBaleBlock(BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).instrument(NoteBlockInstrument.BANJO).strength(0.5F).sound(SoundType.GRASS)))
@JvmField
val TATAMI_BLOCK = this.create("tatami_block", TatamiBlock(BlockBehaviour.Properties.of().ignitedByLava().mapColor(MapColor.PLANT).sound(SoundType.GRASS)))
@JvmField
val TATAMI_MAT = this.create("tatami_mat", TatamiMatBlock(BlockBehaviour.Properties.of().ignitedByLava().mapColor(MapColor.PLANT).sound(SoundType.GRASS)))
/**
* Returns a map of all the blocks that can be stripped with an axe in the format of input - output.
*
@@ -68,6 +68,8 @@ object CobblemonCommands {
MarkGiveCommand.register(dispatcher)
MarkTakeCommand.register(dispatcher)
MarkGiveAllCommand.register(dispatcher)
PcSearchCommand.register(dispatcher)
PcTakeCommand.register(dispatcher)
// Possibly lock down registration if and only if under dev environment or running in an environment
// with a certain system environment variable set
@@ -11,10 +11,12 @@ package com.cobblemon.mod.common
import com.cobblemon.mod.common.block.PotComponent
import com.cobblemon.mod.common.item.components.*
import com.cobblemon.mod.common.platform.PlatformRegistry
import com.mojang.serialization.Codec
import net.minecraft.core.Registry
import net.minecraft.core.component.DataComponentType
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.core.registries.Registries
import net.minecraft.network.codec.ByteBufCodecs
import net.minecraft.resources.ResourceKey
import net.minecraft.resources.ResourceLocation
@@ -70,6 +72,11 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
.networkSynchronized(MobEffectsComponent.PACKET_CODEC)
.build())
val CRAFTED: DataComponentType<Boolean> = create("crafted", DataComponentType.builder<Boolean>()
.persistent(Codec.BOOL)
.networkSynchronized(ByteBufCodecs.BOOL)
.build())
fun register() {
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:pokemon_item"), POKEMON_ITEM)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:bait"), BAIT)
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.api.item.ability.AbilityChanger
import com.cobblemon.mod.common.api.mulch.MulchVariant
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokemon.Natures
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.api.text.blue
@@ -40,6 +41,7 @@ import com.cobblemon.mod.common.item.interactive.*
import com.cobblemon.mod.common.item.interactive.ability.AbilityChangeItem
import com.cobblemon.mod.common.platform.PlatformRegistry
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.pokemon.IVs
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.core.Registry
@@ -55,6 +57,7 @@ import net.minecraft.world.food.FoodProperties
import net.minecraft.world.item.BlockItem
import net.minecraft.world.item.HangingSignItem
import net.minecraft.world.item.Item
import net.minecraft.world.item.Item.Properties
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.item.Rarity
@@ -203,7 +206,14 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
val CAMPFIRE_POT_YELLOW = campfirePotItem(CobblemonBlocks.YELLOW_CAMPFIRE_POT, "yellow")
@JvmField
val Hearty_Grains = compostableItem("hearty_grains", HeartyGrainsItem(CobblemonBlocks.HEARTY_GRAINS))
val HEARTY_GRAINS = compostableItem("hearty_grains", HeartyGrainsItem(CobblemonBlocks.HEARTY_GRAINS))
@JvmField
val HEARTY_GRAIN_BALE = compostableBlockItem("hearty_grain_bale", CobblemonBlocks.HEARTY_GRAIN_BALE, 0.85f)
@JvmField
val TATAMI_BLOCK = blockItem("tatami_block", CobblemonBlocks.TATAMI_BLOCK)
@JvmField
val TATAMI_MAT = blockItem("tatami_mat", CobblemonBlocks.TATAMI_MAT)
@JvmField
val VIVICHOKE = compostableItem("vivichoke")
@@ -350,19 +360,19 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
val aprijuices = mutableListOf<AprijuiceItem>()
@JvmField
val APRIJUICE_BLACK = aprijuiceItem(Apricorn.BLACK)
@JvmField
val APRIJUICE_RED = aprijuiceItem(Apricorn.RED)
@JvmField
val APRIJUICE_BLUE = aprijuiceItem(Apricorn.BLUE)
@JvmField
val APRIJUICE_GREEN = aprijuiceItem(Apricorn.GREEN)
@JvmField
val APRIJUICE_YELLOW = aprijuiceItem(Apricorn.YELLOW)
@JvmField
val APRIJUICE_WHITE = aprijuiceItem(Apricorn.WHITE)
val APRIJUICE_GREEN = aprijuiceItem(Apricorn.GREEN)
@JvmField
val APRIJUICE_BLUE = aprijuiceItem(Apricorn.BLUE)
@JvmField
val APRIJUICE_PINK = aprijuiceItem(Apricorn.PINK)
@JvmField
val APRIJUICE_BLACK = aprijuiceItem(Apricorn.BLACK)
@JvmField
val APRIJUICE_WHITE = aprijuiceItem(Apricorn.WHITE)
@JvmField
val PONIGIRI = create("ponigiri", PonigiriItem())
@@ -371,138 +381,55 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
val SINISTER_TEA = create("sinister_tea", SinisterTeaItem())
@JvmField
val POKE_PUFF = pokepuffItem("poke_puff") // todo make a PokePuffItem class for friendship boosting purposes
val POKE_PUFF = pokepuffItem("poke_puff")
// FOODS
@JvmField
val SWEET_HEART = noSettingsItem("sweet_heart") // todo make a SweetHeartItem class for breeding purposes
@JvmField
val TASTY_TAIL = create("tasty_tail", object : Item(Properties().stacksTo(16)
.food(FoodProperties.Builder()
.nutrition(8)
.saturationModifier(12.8F)
.alwaysEdible()
.build())) {})
val TASTY_TAIL = noSettingsItem("tasty_tail")
@JvmField
val PEWTER_CRUNCHIES = create("pewter_crunchies", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
.food(FoodProperties.Builder()
.nutrition(10) // todo get final values for this from vera
.saturationModifier(1.2F) // todo get final values for this from vera
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F) // todo get final values for this from vera
.alwaysEdible()
.build())) {})
@JvmField // todo get final values for this from vera
val PEWTER_CRUNCHIES = regionalFoodItem("pewter_crunchies", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f, false)
@JvmField
val RAGE_CANDY_BAR = create("rage_candy_bar", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
.food(FoodProperties.Builder()
.nutrition(10) // todo get final values for this from vera
.saturationModifier(1.2F) // todo get final values for this from vera
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F) // todo get final values for this from vera
.alwaysEdible()
.build())) {})
@JvmField // todo get final values for this from vera
val RAGE_CANDY_BAR = regionalFoodItem("rage_candy_bar", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f, false)
@JvmField
val LAVA_COOKIE = create("lava_cookie", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
.food(FoodProperties.Builder()
.nutrition(10) // todo get final values for this from vera
.saturationModifier(1.2F) // todo get final values for this from vera
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F) // todo get final values for this from vera
.alwaysEdible()
.build())) {})
@JvmField // todo get final values for this from vera
val LAVA_COOKIE = regionalFoodItem("lava_cookie", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f)
@JvmField
val OLD_GATEAU = create("old_gateau", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
.food(FoodProperties.Builder()
.nutrition(10) // todo get final values for this from vera
.saturationModifier(1.2F) // todo get final values for this from vera
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F) // todo get final values for this from vera
.alwaysEdible()
.build())) {})
@JvmField // todo get final values for this from vera
val OLD_GATEAU = regionalFoodItem("old_gateau", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f)
@JvmField
val CASTELIACONE = create("casteliacone", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
.food(FoodProperties.Builder()
.nutrition(10) // todo get final values for this from vera
.saturationModifier(1.2F) // todo get final values for this from vera
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F) // todo get final values for this from vera
.alwaysEdible()
.build())) {})
@JvmField // todo get final values for this from vera
val CASTELIACONE = regionalFoodItem("casteliacone", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f)
@JvmField
val SHALOUR_SABLE = create("shalour_sable", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
.food(FoodProperties.Builder()
.nutrition(10) // todo get final values for this from vera
.saturationModifier(1.2F) // todo get final values for this from vera
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F) // todo get final values for this from vera
.alwaysEdible()
.build())) {})
@JvmField // todo get final values for this from vera
val SHALOUR_SABLE = regionalFoodItem("shalour_sable", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f)
@JvmField
val LUMIOSE_GALETTE = create("lumiose_galette", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
.food(FoodProperties.Builder()
.nutrition(10) // todo get final values for this from vera
.saturationModifier(1.2F) // todo get final values for this from vera
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F) // todo get final values for this from vera
.alwaysEdible()
.build())) {})
@JvmField // todo get final values for this from vera
val LUMIOSE_GALETTE = regionalFoodItem("lumiose_galette", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f)
@JvmField
val BIG_MALASADA = create("big_malasada", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
.food(FoodProperties.Builder()
.nutrition(10) // todo get final values for this from vera
.saturationModifier(1.2F) // todo get final values for this from vera
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F) // todo get final values for this from vera
.alwaysEdible()
.build())) {})
@JvmField // todo get final values for this from vera
val BIG_MALASADA = regionalFoodItem("big_malasada", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f)
/*@JvmField // todo see what we are doing for these (are there different kinds?)
val CURRY = create("curry", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
.food(FoodProperties.Builder()
.nutrition(10)
.saturationModifier(1.2F)
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
.alwaysEdible()
.usingConvertsTo(Items.BOWL)
.build())) {
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
user.removeAllEffects()
return super.finishUsingItem(stack, world, user)
}
})*/
@JvmField // todo get final values for this from vera
val SMOKED_TAIL_CURRY = regionalFoodItem("smoked_tail_curry", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f, false, ItemStack(Items.BOWL, 1))
@JvmField
val JUBILIFE_MUFFIN = create("jubilife_muffin", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
.food(FoodProperties.Builder()
.nutrition(10) // todo get final values for this from vera
.saturationModifier(1.2F) // todo get final values for this from vera
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F) // todo get final values for this from vera
.alwaysEdible()
.build())) {})
@JvmField // todo get final values for this from vera
val JUBILIFE_MUFFIN = regionalFoodItem("jubilife_muffin", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f)
/*@JvmField // todo see what we are doing for these (are there different kinds?)
val SANDWICH = create("sandwich", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
.food(FoodProperties.Builder()
.nutrition(10)
.saturationModifier(1.2F)
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
.alwaysEdible()
.usingConvertsTo(Items.BOWL)
.build())) {
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
user.removeAllEffects()
return super.finishUsingItem(stack, world, user)
}
})*/
@JvmField // todo get final values for this from vera
val OPEN_FACED_SANDWICH = regionalFoodItem("open_faced_sandwich", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f)
// todo we might need to wait on these for later?
// todo we might need to wait on these for later? These impact battles and may be harder to do
/*@JvmField
val CHOICE_DUMPLING = noSettingsItem("choice_dumpling") // todo make a ChoiceDumpingItem class for breeding purposes
val CHOICE_DUMPLING = noSettingsItem("choice_dumpling") // todo make a ChoiceDumpingItem class for battle purposes
@JvmField
val SWAP_SNACK = noSettingsItem("swap_snack") // todo make a SwapSnackItem class for breeding purposes
val SWAP_SNACK = noSettingsItem("swap_snack") // todo make a SwapSnackItem class for battle purposes
@JvmField
val TWICE_SPICED_BEETROOT = noSettingsItem("twice_spiced_beetroot") // todo make a TwiceSpiceBeetrootItem class for breeding purposes
val TWICE_SPICED_BEETROOT = noSettingsItem("twice_spiced_beetroot") // todo make a TwiceSpiceBeetrootItem class for battle purposes
*/
@JvmField
val POTATO_MOCHI = create("potato_mochi", object : Item(Properties().stacksTo(16)
@@ -533,8 +460,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
.alwaysEdible()
.build())) {})
// todo etc?
//@JvmField
//val SCATTER_BANG = this.create("scatter_bang", ScatterBangItem(Item.Settings()))
//@JvmField
@@ -597,6 +522,9 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField val AUSPICIOUS_ARMOR = heldItem("auspicious_armor")
@JvmField val MALICIOUS_ARMOR = heldItem("malicious_armor")
@JvmField val SHELL_HELMET = heldItem("shell_helmet")
@JvmField val METAL_ALLOY = noSettingsItem("metal_alloy")
@JvmField val SCROLL_OF_DARKNESS = noSettingsItem("scroll_of_darkness")
@JvmField val SCROLL_OF_WATERS = noSettingsItem("scroll_of_waters")
private val berries = mutableMapOf<ResourceLocation, BerryItem>()
// Plants
@@ -675,6 +603,33 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val GALARICA_NUTS = create("galarica_nuts", GalaricaNutItem())
// Hyper Training Items
val hyperTrainingItems = mutableListOf<HyperTrainingItem>()
@JvmField
val HEALTH_CANDY = hyperTrainingItem("health_candy", 1, setOf(Stats.HP), 0..IVs.MAX_VALUE)
@JvmField
val MIGHTY_CANDY = hyperTrainingItem("mighty_candy", 1, setOf(Stats.ATTACK), 0..IVs.MAX_VALUE)
@JvmField
val TOUGH_CANDY = hyperTrainingItem("tough_candy", 1, setOf(Stats.DEFENCE), 0..IVs.MAX_VALUE)
@JvmField
val SMART_CANDY = hyperTrainingItem("smart_candy", 1, setOf(Stats.SPECIAL_ATTACK), 0..IVs.MAX_VALUE)
@JvmField
val COURAGE_CANDY = hyperTrainingItem("courage_candy", 1, setOf(Stats.SPECIAL_DEFENCE), 0..IVs.MAX_VALUE)
@JvmField
val QUICK_CANDY = hyperTrainingItem("quick_candy", 1, setOf(Stats.SPEED), 0..IVs.MAX_VALUE)
@JvmField
val SICKLY_CANDY = hyperTrainingItem("sickly_candy", -1, setOf(Stats.HP), 0..IVs.MAX_VALUE)
@JvmField
val WEAK_CANDY = hyperTrainingItem("weak_candy", -1, setOf(Stats.ATTACK), 0..IVs.MAX_VALUE)
@JvmField
val BRITTLE_CANDY = hyperTrainingItem("brittle_candy", -1, setOf(Stats.DEFENCE), 0..IVs.MAX_VALUE)
@JvmField
val NUMB_CANDY = hyperTrainingItem("numb_candy", -1, setOf(Stats.SPECIAL_ATTACK), 0..IVs.MAX_VALUE)
@JvmField
val COWARD_CANDY = hyperTrainingItem("coward_candy", -1, setOf(Stats.SPECIAL_DEFENCE), 0..IVs.MAX_VALUE)
@JvmField
val SLOW_CANDY = hyperTrainingItem("slow_candy", -1, setOf(Stats.SPEED), 0..IVs.MAX_VALUE)
// Medicine
@JvmField
val RARE_CANDY = candyItem("rare_candy", Rarity.RARE) { _, pokemon -> pokemon.getExperienceToNextLevel() }
@@ -760,6 +715,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
val FINE_REMEDY = create("fine_remedy", RemedyItem(RemedyItem.FINE))
@JvmField
val SUPERB_REMEDY = create("superb_remedy", RemedyItem(RemedyItem.SUPERB))
@JvmField
val MOOMOO_MILK = create("moomoo_milk", Item(Item.Properties()))
@JvmField
val POTION = create("potion", PotionItem(PotionType.POTION))
@@ -994,8 +951,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
val CHOICE_SCARF = heldItem("choice_scarf")
@JvmField
val CLEANSE_TAG = heldItem("cleanse_tag")
// @JvmField
// val CLEAR_AMULET = heldItem("clear_amulet")
@JvmField
val CLEAR_AMULET = heldItem("clear_amulet")
@JvmField
val COVERT_CLOAK = heldItem("covert_cloak")
@JvmField
@@ -1024,16 +981,16 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
val FOCUS_SASH = heldItem("focus_sash")
@JvmField
val GRASSY_SEED = heldItem("grassy_seed")
// @JvmField
// val GRIP_CLAW = heldItem("grip_claw")
@JvmField
val GRIP_CLAW = heldItem("grip_claw")
@JvmField
val HARD_STONE = heldItem("hard_stone")
@JvmField
val HEAVY_DUTY_BOOTS = heldItem("heavy_duty_boots")
@JvmField
val IRON_BALL = heldItem("iron_ball")
// @JvmField
// val LAGGING_TAIL = heldItem("lagging_tail")
@JvmField
val LAGGING_TAIL = heldItem("lagging_tail")
@JvmField
val LEFTOVERS = heldItem("leftovers")
@JvmField
@@ -1046,8 +1003,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
val LOADED_DICE = heldItem("loaded_dice")
@JvmField
val LUCKY_EGG = heldItem("lucky_egg")
// @JvmField
// val LUMINOUS_MOSS = heldItem("luminous_moss")
@JvmField
val LUMINOUS_MOSS = heldItem("luminous_moss")
@JvmField
val MAGNET = heldItem("magnet")
@JvmField
@@ -1579,6 +1536,39 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
return item
}
private fun regionalFoodItem(
name: String,
stacksTo: Int,
nutrition: Int,
saturationModifier: Float,
effect: MobEffectInstance? = null,
effectChance: Float? = null,
alwaysEdible: Boolean = false,
convertsToOnUse: ItemStack? = null
): RegionalFoodItem {
val foodPropertiesBuilder = FoodProperties.Builder()
.nutrition(nutrition)
.saturationModifier(saturationModifier)
if (effect != null && effectChance != null) {
foodPropertiesBuilder.effect(effect, effectChance)
}
if (alwaysEdible == true) {
foodPropertiesBuilder.alwaysEdible()
}
if (convertsToOnUse != null && !convertsToOnUse.isEmpty) {
foodPropertiesBuilder.usingConvertsTo(convertsToOnUse.item)
}
val properties = Item.Properties()
.stacksTo(stacksTo)
.food(foodPropertiesBuilder.build())
return create(name, RegionalFoodItem(properties))
}
private fun heldItem(name: String, remappedName: String? = null): CobblemonItem = create(
name,
CobblemonItem(Item.Properties()).also {
@@ -1624,6 +1614,12 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
return item
}
private fun hyperTrainingItem(name: String, increaseAmount: Int, targetStats: Set<Stat>, validRange: IntRange): HyperTrainingItem {
val item = this.create(name, HyperTrainingItem(increaseAmount, targetStats, validRange))
hyperTrainingItems.add(item)
return item
}
private fun apricornItem(name: String, apricornItem: ApricornItem): ApricornItem {
val finalName = "${name}_apricorn"
val item = this.create(finalName, apricornItem)
@@ -259,6 +259,7 @@ object CobblemonNetwork {
// Pokemon Update Packets
list.add(PacketRegisterInfo(FriendshipUpdatePacket.ID, FriendshipUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(FullnessUpdatePacket.ID, FullnessUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(MoveSetUpdatePacket.ID, MoveSetUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(NatureUpdatePacket.ID, NatureUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(ShinyUpdatePacket.ID, ShinyUpdatePacket::decode, PokemonUpdatePacketHandler()))
@@ -15,13 +15,13 @@ enum class CobblemonRecipeCategories {
COOKING_POT_SEARCH("COOKING_POT_SEARCH"),
COOKING_POT_FOODS("COOKING_POT_FOODS"),
COOKING_POT_MEDICINES("COOKING_POT_MEDICINES"),
COOKING_POT_BAITS("COOKING_POT_BAITS"),
COOKING_POT_COMPLEX_DISHES("COOKING_POT_COMPLEX_DISHES"),
COOKING_POT_MISC("COOKING_POT_MISC");
companion object {
val customAggregateCategories: Map<RecipeBookCategories, List<RecipeBookCategories>> = mapOf(
COOKING_POT_SEARCH.toVanillaCategory() to listOf(
COOKING_POT_FOODS.toVanillaCategory(), COOKING_POT_MISC.toVanillaCategory(), COOKING_POT_MEDICINES.toVanillaCategory(), COOKING_POT_BAITS.toVanillaCategory()
COOKING_POT_FOODS.toVanillaCategory(), COOKING_POT_MISC.toVanillaCategory(), COOKING_POT_MEDICINES.toVanillaCategory(), COOKING_POT_COMPLEX_DISHES.toVanillaCategory()
)
)
}
@@ -125,6 +125,7 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
@JvmField
val MOCHI_USE = this.create("item.medicine.feather.use")
@JvmField
val MULCH_PLACE = this.create("block.mulch.place")
@JvmField
@@ -216,6 +217,8 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
val BERRY_HARVEST = this.create("block.berry_bush.harvest")
@JvmField
val BERRY_EAT = this.create("item.berry.eat")
@JvmField
val BERRY_EAT_FULL = this.create("item.berry.eat.full")
@JvmField
val BIG_ROOT_BREAK = this.create("block.big_root.break")
@@ -32,6 +32,7 @@ class PokemonCriterion(
}
override fun matches(player: ServerPlayer, context: Pokemon): Boolean {
return (species.equals(context.species.resourceIdentifier.path) || species.equals(context.species.resourceIdentifier.toString())) && properties.matches(context)
var speciesCheck = (species.equals(context.species.resourceIdentifier.path) || species.equals(context.species.resourceIdentifier.toString())) || species.equals("any")
return speciesCheck && properties.matches(context)
}
}
@@ -29,9 +29,12 @@ private val bubbleColourMap = mapOf(
fun getColourMixFromSeasonings(seasonings: List<ItemStack>, forBubbles: Boolean = false): Int? {
val flavors = seasonings
.flatMap { Seasonings.getFlavoursFromItemStack(it).entries }
.groupingBy { it.key }
.fold(0) { acc, entry -> acc + entry.value }
.mapNotNull { Seasonings.getFlavoursFromItemStack(it) }
.flatMap { it.entries }
.groupingBy { it.key }
.fold(0) { acc, entry -> acc + entry.value }
if (flavors.isEmpty()) return null
val maxFlavorValue = flavors.values.maxOrNull()
val dominantFlavors = flavors.filter { it.value == maxFlavorValue }.map { it.key }
@@ -1,24 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.cooking
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
data class MobCookingEffects(
val appliedEffects: List<SerializableMobEffectInstance> = emptyList()
) {
companion object {
val CODEC: Codec<MobCookingEffects> = RecordCodecBuilder.create { builder ->
builder.group(
SerializableMobEffectInstance.CODEC.listOf().optionalFieldOf("appliedEffects", emptyList()).forGetter { it.appliedEffects }
).apply(builder, ::MobCookingEffects)
}
}
}
@@ -52,21 +52,6 @@ object MobEffectUtils {
}
private fun mergeAmplifiers(amplifiers: List<Int>): Int {
if (amplifiers.isEmpty()) return 0
val sorted = amplifiers.sortedDescending()
var result = 0.0
for ((index, value) in sorted.withIndex()) {
val multiplier = when (index) {
0 -> 1.0
1 -> 0.75
2 -> 0.5
else -> 0.25
}
result += (value + 1) * multiplier
}
return ceil(result).toInt() - 1
return amplifiers.maxOrNull() ?: 0 // we do not want the power of the effects to combine, just take the highest one used of that type
}
}
@@ -21,6 +21,8 @@ import net.minecraft.network.codec.ByteBufCodecs
import net.minecraft.network.codec.StreamCodec
import net.minecraft.world.item.DyeColor
import net.minecraft.world.item.Item
import java.util.Optional
import kotlin.jvm.optionals.getOrNull
data class Seasoning(
val ingredient: RegistryLikeCondition<Item>,
@@ -29,33 +31,33 @@ data class Seasoning(
@SerializedName("colour", alternate = ["color"])
val colour: DyeColor,
@SerializedName("baitEffects")
val baitEffects: List<SpawnBait.Effect> = emptyList(),
val baitEffects: List<SpawnBait.Effect>? = null,
@SerializedName("food")
val food: Food? = null,
@SerializedName("mobEffects")
val mobEffects: MobCookingEffects? = null
val mobEffects: List<SerializableMobEffectInstance>? = null
) {
companion object {
val CODEC: Codec<Seasoning> = RecordCodecBuilder.create { builder ->
builder.group(
ITEM_REGISTRY_LIKE_CODEC.fieldOf("ingredient").forGetter { it.ingredient },
Codec.unboundedMap(Flavour.CODEC, Codec.INT).fieldOf("flavours").forGetter { it.flavours }, // Use map codec
Codec.unboundedMap(Flavour.CODEC, Codec.INT).optionalFieldOf("flavours").forGetter { Optional.ofNullable(it.flavours) },
DyeColor.CODEC.fieldOf("colour").forGetter { it.colour },
SpawnBait.Effect.CODEC.listOf().optionalFieldOf("baitEffects", emptyList()).forGetter { it.baitEffects },
Food.CODEC.optionalFieldOf("food", Food(0, 0f)).forGetter { it.food ?: Food(0, 0f) },
MobCookingEffects.CODEC.optionalFieldOf("mobEffects", MobCookingEffects()).forGetter { it.mobEffects ?: MobCookingEffects() }
).apply(builder, ::Seasoning)
SpawnBait.Effect.CODEC.listOf().optionalFieldOf("baitEffects").forGetter { Optional.ofNullable(it.baitEffects) },
Food.CODEC.optionalFieldOf("food").forGetter { Optional.ofNullable(it.food) },
SerializableMobEffectInstance.CODEC.listOf().optionalFieldOf("mobEffects").forGetter { Optional.ofNullable(it.mobEffects) }
).apply(builder) { item, flavours, colour, baitEffects, food, mobEffects ->
Seasoning(
item,
flavours.getOrNull(),
colour,
baitEffects.getOrNull(),
food.getOrNull(),
mobEffects.getOrNull()
)
}
}
val STREAM_CODEC: StreamCodec<ByteBuf, Seasoning> = ByteBufCodecs.fromCodec(CODEC)
val BLANK_SEASONING = Seasoning(
ingredient = RegistryLikeIdentifierCondition<Item>(cobblemonResource("blank")),
flavours = Flavour.entries.associate { it to 0 },
colour = DyeColor.WHITE,
baitEffects = emptyList(),
food = Food(),
mobEffects = MobCookingEffects()
)
}
}
@@ -14,7 +14,10 @@ import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.conditional.RegistryLikeIdentifierCondition
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.fishing.SpawnBait
import com.cobblemon.mod.common.api.fishing.SpawnBaitEffects
import com.cobblemon.mod.common.api.fishing.SpawnBaitUtils
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.item.components.FoodComponent
import com.cobblemon.mod.common.net.messages.client.cooking.SeasoningRegistrySyncPacket
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.adapters.ItemLikeConditionAdapter
@@ -43,14 +46,6 @@ object Seasonings : JsonDataRegistry<Seasoning> {
val seasonings = mutableListOf<Seasoning>()
val BLANK_SEASONING = Seasoning(
ingredient = RegistryLikeIdentifierCondition<Item>(cobblemonResource("blank")),
flavours = emptyMap(),
colour = DyeColor.WHITE,
food = Food(),
mobEffects = MobCookingEffects()
)
override fun sync(player: ServerPlayer) {
SeasoningRegistrySyncPacket(seasonings.toList()).sendToPlayer(player)
}
@@ -64,7 +59,7 @@ object Seasonings : JsonDataRegistry<Seasoning> {
colour = it.colour,
baitEffects = emptyList(),
food = Food(),
mobEffects = MobCookingEffects()
mobEffects = emptyList()
)
}.toMutableMap()
finalData.putAll(data)
@@ -76,19 +71,27 @@ object Seasonings : JsonDataRegistry<Seasoning> {
this.seasonings.addAll(seasonings)
}
fun getFlavoursFromItemStack(stack: ItemStack): Map<Flavour, Int> {
fun getFlavoursFromItemStack(stack: ItemStack): Map<Flavour, Int>? {
val holder = stack.itemHolder
val seasoning = seasonings.find { it.ingredient.fits(holder) } ?: BLANK_SEASONING
val inherentFlavours = stack.get(CobblemonItemComponents.FLAVOUR)?.flavours ?: emptyMap()
val seasoningFlavours = seasoning.flavours ?: emptyMap()
val seasoning = seasonings.find { it.ingredient.fits(holder) }
val inherentFlavours = stack.get(CobblemonItemComponents.FLAVOUR)?.flavours
val seasoningFlavours = seasoning?.flavours
return seasoningFlavours.mapValues { (flavour, value) ->
inherentFlavours.getOrDefault(flavour, 0) + value
if (seasoningFlavours.isNullOrEmpty() && inherentFlavours.isNullOrEmpty())
return null
return (seasoningFlavours ?: emptyMap()).mapValues { (flavour, value) ->
value + (inherentFlavours?.getOrDefault(flavour, 0) ?: 0)
}
}
fun getFoodFromItemStack(stack: ItemStack): Food? {
return getFromItemStack(stack)?.food
fun hasFlavors(stack: ItemStack): Boolean {
val flavors = getFlavoursFromItemStack(stack)
return !flavors.isNullOrEmpty() && flavors.any { it.value != 0 }
}
fun getFoodComponentFromItemStack(stack: ItemStack): FoodComponent? {
return getFromItemStack(stack)?.food?.toComponent()
}
fun hasFood(stack: ItemStack): Boolean {
@@ -96,17 +99,26 @@ object Seasonings : JsonDataRegistry<Seasoning> {
return effects.hunger > 0 || effects.saturation > 0f
}
fun getMobEffectFromItemStack(stack: ItemStack): MobCookingEffects? {
return getFromItemStack(stack)?.mobEffects
fun getMobEffectsFromItemStack(stack: ItemStack): List<SerializableMobEffectInstance> {
return getFromItemStack(stack)?.mobEffects ?: emptyList()
}
fun hasMobEffect(stack: ItemStack): Boolean {
val effects = getFromItemStack(stack)?.mobEffects ?: return false
return effects.appliedEffects.isNotEmpty()
return !getFromItemStack(stack)?.mobEffects.isNullOrEmpty()
}
fun getBaitEffectsFromItemStack(stack: ItemStack): List<SpawnBait.Effect> {
return getFromItemStack(stack)?.baitEffects ?: emptyList()
val primaryEffects = SpawnBaitEffects.getEffectsFromItemStack(stack)
return if (primaryEffects.isNotEmpty()) {
primaryEffects
} else {
getFromItemStack(stack)?.baitEffects ?: emptyList()
}
}
fun hasBaitEffects(stack: ItemStack): Boolean {
return SpawnBaitEffects.getEffectsFromItemStack(stack).isNotEmpty() ||
!(getFromItemStack(stack)?.baitEffects.isNullOrEmpty())
}
fun getFromItemStack(stack: ItemStack): Seasoning? {
@@ -72,6 +72,8 @@ object CobblemonEvents {
@JvmField
val FRIENDSHIP_UPDATED = EventObservable<FriendshipUpdatedEvent>()
@JvmField
val FULLNESS_UPDATED = EventObservable<FullnessUpdatedEvent>()
@JvmField
val POKEMON_FAINTED = EventObservable<PokemonFaintedEvent>()
@JvmField
val EVOLUTION_ACCEPTED = CancelableObservable<EvolutionAcceptedEvent>()
@@ -0,0 +1,25 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.pokemon
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.pokemon.Pokemon
/**
* Event that is fired when a player owned Pokémon has its fullness changed
*/
data class FullnessUpdatedEvent(
val pokemon: Pokemon,
val newFullnessInitial: Int
) {
var newFullness: Int = newFullnessInitial
set(value) {
field = value.coerceIn(0, pokemon.getMaxFullness())
}
}
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.api.fishing
import com.cobblemon.mod.common.CobblemonItemComponents
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.conditional.RegistryLikeIdentifierCondition
import com.cobblemon.mod.common.api.cooking.Seasonings
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.data.SpawnBaitRegistrySyncPacket
@@ -85,7 +86,7 @@ object SpawnBaitEffects : JsonDataRegistry<SpawnBait> {
(it.ingredient as RegistryLikeIdentifierCondition<Item>).identifier.path == identifier.path
}
if (seasoning != null && seasoning.baitEffects.isNotEmpty()) {
if (seasoning != null && !seasoning.baitEffects.isNullOrEmpty()) {
return SpawnBait(
item = seasoning.ingredient,
effects = seasoning.baitEffects
@@ -96,8 +97,9 @@ object SpawnBaitEffects : JsonDataRegistry<SpawnBait> {
return null
}
// if it has bait effects and is not listed as a seasoning then it is a bait
fun isFishingBait(stack: ItemStack): Boolean {
val holder = stack.itemHolder
return stack.has(CobblemonItemComponents.BAIT_EFFECTS) || effectsMap.values.any { it.item.fits(holder) }
return effectsMap.values.any { it.item.fits(holder) }
}
}
@@ -17,25 +17,20 @@ object SpawnBaitUtils {
return grouped.map { (key, groupEffects) ->
val (type, subcategory) = key
if (type == SpawnBait.Effects.RARITY_BUCKET) {
// we want to keep the highest rarity bucket effect instead of merging them
groupEffects.maxByOrNull { it.value }!!
} else {
val count = groupEffects.size
val totalChance = groupEffects.sumOf { it.chance }
val totalValue = groupEffects.sumOf { it.value }
val count = groupEffects.size
val totalChance = groupEffects.sumOf { it.chance }
val totalValue = groupEffects.sumOf { it.value }
val multiplier = when (count) {
2 -> 0.8
3 -> 0.6
else -> 1.0
}
val adjustedChance = (totalChance * multiplier).coerceAtMost(1.0)
val adjustedValue = ceil(totalValue * multiplier)
SpawnBait.Effect(type, subcategory, adjustedChance, adjustedValue)
val multiplier = when (count) {
2 -> 0.8
3 -> 0.6
else -> 1.0
}
val adjustedChance = (totalChance * multiplier).coerceAtMost(1.0)
val adjustedValue = ceil(totalValue * multiplier)
SpawnBait.Effect(type, subcategory, adjustedChance, adjustedValue)
}
}
}
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
import com.cobblemon.mod.common.api.callback.MoveSelectCallbacks
import com.cobblemon.mod.common.api.callback.PartyMoveSelectCallbacks
import com.cobblemon.mod.common.api.moves.Move
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.battles.BagItemActionResponse
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
@@ -92,7 +93,13 @@ interface PokemonAndMoveSelectingItem {
}
}
fun canUseOnPokemon(stack: ItemStack, pokemon: Pokemon): Boolean
fun canUseOnPokemon(stack: ItemStack, pokemon: Pokemon): Boolean {
if (stack.`is`(CobblemonItemTags.POKE_FOOD)) {
return !pokemon.isFull()
}
return true
}
fun canUseOnBattlePokemon(stack: ItemStack, battlePokemon: BattlePokemon): Boolean = bagItem!!.canUse(stack, battlePokemon.actor.battle, battlePokemon)
fun canUseOnMove(stack: ItemStack, pokemon: Pokemon, move: Move): Boolean = canUseOnMove(stack, move)
fun canUseOnMove(stack: ItemStack, move: Move): Boolean
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.advancement.CobblemonCriteria
import com.cobblemon.mod.common.advancement.criterion.PokemonInteractContext
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
import com.cobblemon.mod.common.api.callback.PartySelectCallbacks
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.battles.BagItemActionResponse
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
@@ -105,7 +106,13 @@ interface PokemonSelectingItem {
}
}
fun canUseOnPokemon(stack: ItemStack, pokemon: Pokemon): Boolean
fun canUseOnPokemon(stack: ItemStack, pokemon: Pokemon): Boolean {
if (stack.`is`(CobblemonItemTags.POKE_FOOD)) {
return !pokemon.isFull()
}
return true
}
fun canUseOnBattlePokemon(stack: ItemStack, battlePokemon: BattlePokemon): Boolean = bagItem!!.canUse(stack, battlePokemon.actor.battle, battlePokemon)
fun interactWithSpecificBattle(player: ServerPlayer, stack: ItemStack, battlePokemon: BattlePokemon): InteractionResultHolder<ItemStack> {
@@ -136,6 +143,7 @@ interface PokemonSelectingItem {
}
)
return InteractionResultHolder.success(stack)
}
@@ -22,6 +22,7 @@ import com.cobblemon.mod.common.CobblemonActivities
import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.CobblemonUnlockableWallpapers
import com.cobblemon.mod.common.Environment
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
@@ -45,6 +46,9 @@ import com.cobblemon.mod.common.api.pokedex.SeenPercent
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker
import com.cobblemon.mod.common.api.scheduling.Schedulable
import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
import com.cobblemon.mod.common.api.storage.PokemonStore
@@ -69,6 +73,7 @@ import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimati
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
import com.cobblemon.mod.common.net.messages.client.sound.UnvalidatedPlaySoundS2CPacket
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.Species
@@ -97,6 +102,7 @@ import net.minecraft.resources.ResourceLocation
import net.minecraft.server.MinecraftServer
import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer
import net.minecraft.sounds.SoundSource
import net.minecraft.tags.TagKey
import net.minecraft.world.damagesource.DamageSource
import net.minecraft.world.damagesource.DamageTypes
@@ -198,7 +204,15 @@ object MoLangFunctions {
runtime.environment.variable = params.environment.variable
runtime.environment.context = params.environment.context
val expression = params.getString(0).asExpressionLike()
runtime.resolve(expression)
val delayInSeconds = params.getDoubleOrNull(1)?.toFloat() ?: 0.0f
if (delayInSeconds > 0.0f) {
val tracker = if (Cobblemon.implementation.environment() == Environment.SERVER) ServerTaskTracker else ClientTaskTracker
tracker.after(delayInSeconds) {
runtime.resolve(expression)
}
} else {
runtime.resolve(expression)
}
},
"system_time_millis" to java.util.function.Function { _ ->
DoubleValue(System.currentTimeMillis())
@@ -345,6 +359,27 @@ object MoLangFunctions {
return@put DoubleValue.ZERO
}
}
map.put("play_sound_on_server") { params ->
val sound = params.getString(0).asResource()
val soundSource = params.getString(1).uppercase()
val x = params.getDouble(2)
val y = params.getDouble(3)
val z = params.getDouble(4)
val player = params.getOrNull<MoValue>(5)?.let {
if (it is StringValue) world.getPlayerByUUID(UUID.fromString(it.value))
else if (it is ObjectValue<*>) it.obj
else null
} as? ServerPlayer
val volume = params.getDoubleOrNull(6)?.toFloat() ?: 1.0f
val pitch = params.getDoubleOrNull(7)?.toFloat() ?: 1.0f
val packet = UnvalidatedPlaySoundS2CPacket(sound, SoundSource.valueOf(soundSource), x, y, z, volume, pitch)
if (player != null) {
packet.sendToPlayer(player)
} else {
packet.sendToPlayersAround(x, y, z, 16.0, world.dimension())
}
}
map.put("get_entities_around") { params ->
val x = params.getDouble(0)
val y = params.getDouble(1)
@@ -405,6 +440,15 @@ object MoLangFunctions {
val command = params.getString(0)
player.server.commands.performPrefixedCommand(player.createCommandSourceStack(), command)
}
map.put("play_sound_on_server") { params ->
val sound = params.getString(0).asResource()
val soundSource = SoundSource.valueOf(params.getString(1).uppercase())
val volume = params.getDoubleOrNull(2)?.toFloat() ?: 1.0f
val pitch = params.getDoubleOrNull(3)?.toFloat() ?: 1.0f
val packet = UnvalidatedPlaySoundS2CPacket(sound, soundSource, player.x, player.y, player.z, volume, pitch)
packet.sendToPlayer(player)
}
map.put("is_party_at_full_health") { _ ->
DoubleValue(player.party().none(Pokemon::canBeHealed)) }
map.put("can_heal_at_healer") { params ->
@@ -619,8 +663,9 @@ object MoLangFunctions {
map.put("biome") { _ -> entity.level().getBiome(entity.blockPosition()).asBiomeMoLangValue() }
map.put("is_passenger") { DoubleValue(entity.isPassenger) }
map.put("find_nearby_block") { params ->
val type = params.getString(0).asIdentifierDefaultingNamespace(namespace = "minecraft")
val isTag = type.path.startsWith("#")
val input = params.getString(0)
val isTag = input.contains("#")
val type = input.replace("#", "").asIdentifierDefaultingNamespace(namespace = "minecraft")
val range = params.getDoubleOrNull(1) ?: 10
val blockPos = entity.level().getBlockStatesWithPos(AABB.ofSize(entity.position(), range.toDouble(), range.toDouble(), range.toDouble()))
.filter { it.first.blockHolder.let { if (isTag) it.`is`(TagKey.create(Registries.BLOCK, type)) else it.`is`(type) } }
@@ -676,6 +721,17 @@ object MoLangFunctions {
}
}
}
if (entity is Schedulable) {
map.put("run_molang_after") { params ->
val expression = params.getString(0).asExpressionLike()
val delayInSeconds = params.getDouble(1).toFloat()
val runtime = MoLangRuntime()
runtime.environment.cloneFrom(params.environment)
entity.after(delayInSeconds) {
runtime.resolve(expression)
}
}
}
// q.entity.spawn_bedrock_particles(effect, locator, [player]) - sends to everyone nearby or just to the player if they're set. Locator is necessary even if unused on non-posables.
map.put("spawn_bedrock_particles") { params ->
val particle = params.getString(0).asResource()
@@ -0,0 +1,56 @@
/*
* 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.moves
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.pokemon.Pokemon
object HiddenPowerUtil {
val hiddenPowerTable = arrayOf(
ElementalTypes.FIGHTING,
ElementalTypes.FLYING,
ElementalTypes.POISON,
ElementalTypes.GROUND,
ElementalTypes.ROCK,
ElementalTypes.BUG,
ElementalTypes.GHOST,
ElementalTypes.STEEL,
ElementalTypes.FIRE,
ElementalTypes.WATER,
ElementalTypes.GRASS,
ElementalTypes.ELECTRIC,
ElementalTypes.PSYCHIC,
ElementalTypes.ICE,
ElementalTypes.DRAGON,
ElementalTypes.DARK
)
fun getHiddenPowerType(pokemon: Pokemon?): ElementalType {
if (pokemon == null) {
return ElementalTypes.NORMAL
}
val ivs = pokemon.ivs
val ivArray = arrayOf(
ivs[Stats.HP],
ivs[Stats.ATTACK],
ivs[Stats.DEFENCE],
ivs[Stats.SPEED],
ivs[Stats.SPECIAL_ATTACK],
ivs[Stats.SPECIAL_DEFENCE]
).map { it ?: return@getHiddenPowerType ElementalTypes.NORMAL }
var tableIndex = 0
ivArray.forEachIndexed { index, it ->
tableIndex += (it % 2) shl index
}
tableIndex = tableIndex * 15 / 63
return hiddenPowerTable[tableIndex.coerceAtMost(hiddenPowerTable.size - 1)]
}
}
@@ -15,7 +15,6 @@ import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.moves.animations.ActionEffectTimeline
import com.cobblemon.mod.common.api.moves.categories.DamageCategories
import com.cobblemon.mod.common.api.moves.categories.DamageCategory
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.battles.MoveTarget
@@ -100,24 +99,6 @@ open class MoveTemplate(
Moves::getByName,
MoveTemplate::name
) { id -> "No MoveTemplate for ID $id" }
val hiddenPowerTable = arrayOf(
ElementalTypes.FIGHTING,
ElementalTypes.FLYING,
ElementalTypes.POISON,
ElementalTypes.GROUND,
ElementalTypes.ROCK,
ElementalTypes.BUG,
ElementalTypes.GHOST,
ElementalTypes.STEEL,
ElementalTypes.FIRE,
ElementalTypes.WATER,
ElementalTypes.GRASS,
ElementalTypes.ELECTRIC,
ElementalTypes.PSYCHIC,
ElementalTypes.ICE,
ElementalTypes.DRAGON,
ElementalTypes.DARK
)
}
/**
@@ -146,21 +127,7 @@ open class MoveTemplate(
return this.elementalType
}
if (name == "hiddenpower") {
val ivs = pokemon.ivs
val ivArray = arrayOf(
ivs[Stats.HP],
ivs[Stats.ATTACK],
ivs[Stats.DEFENCE],
ivs[Stats.SPEED],
ivs[Stats.SPECIAL_ATTACK],
ivs[Stats.SPECIAL_DEFENCE]
).map { it ?: return@getEffectiveElementalType ElementalTypes.NORMAL }
var tableIndex = 0
ivArray.forEachIndexed { index, it ->
tableIndex += (it % 2) shl index
}
tableIndex = tableIndex * 15 / 63
return hiddenPowerTable[tableIndex.coerceAtMost(hiddenPowerTable.size - 1)]
return HiddenPowerUtil.getHiddenPowerType(pokemon)
}
// TODO: Handle ability suppression: clientactivebattlepokemon needs data about volatiles
// TODO: Handle Liquid Voice: need to know what moves have the sound flag
@@ -96,6 +96,8 @@ object CobblemonPermissions {
@JvmStatic
val PC = this.create("${COMMAND_PREFIX}pc", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@JvmStatic
val PC_SEARCH = this.create("${COMMAND_PREFIX}pcsearch", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@JvmStatic
val POKEBOX = this.create("${COMMAND_PREFIX}pokebox", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@JvmStatic
@@ -43,6 +43,7 @@ import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.mojang.brigadier.StringReader
import com.mojang.serialization.Codec
import com.mojang.serialization.JsonOps
import java.util.UUID
import kotlin.math.min
import kotlin.random.Random
@@ -51,6 +52,7 @@ import net.minecraft.commands.arguments.item.ItemParser
import net.minecraft.core.HolderLookup
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.nbt.NbtOps
import net.minecraft.nbt.StringTag
import net.minecraft.nbt.Tag
import net.minecraft.network.chat.Component
@@ -59,6 +61,7 @@ import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level
import kotlin.jvm.optionals.getOrNull
/**
* A grouping of typical, selectable properties for a Pokémon. This is serializable
@@ -402,6 +405,9 @@ open class PokemonProperties {
ivs.forEach { stat ->
pokemon.setIV(stat.key, stat.value)
}
ivs.hyperTrainedIVs.forEach { stat ->
pokemon.hyperTrainIV(stat.key, stat.value)
}
}
evs?.let { evs ->
evs.forEach { stat ->
@@ -633,8 +639,8 @@ open class PokemonProperties {
nature?.let { nbt.putString(DataKeys.POKEMON_NATURE, it) }
ability?.let { nbt.putString(DataKeys.POKEMON_ABILITY, it) }
status?.let { nbt.putString(DataKeys.POKEMON_STATUS_NAME, it) }
ivs?.let { nbt.put(DataKeys.POKEMON_IVS, it.saveToNBT(CompoundTag())) }
evs?.let { nbt.put(DataKeys.POKEMON_EVS, it.saveToNBT(CompoundTag())) }
ivs?.let { nbt.put(DataKeys.POKEMON_IVS, IVs.CODEC.encodeStart(NbtOps.INSTANCE, it).result().get()) }
evs?.let { nbt.put(DataKeys.POKEMON_EVS, EVs.CODEC.encodeStart(NbtOps.INSTANCE, it).result().get()) }
type?.let { nbt.putString(DataKeys.ELEMENTAL_TYPE, it) }
teraType?.let { nbt.putString(DataKeys.POKEMON_TERA_TYPE, it) }
dmaxLevel?.let { nbt.putInt(DataKeys.POKEMON_DMAX_LEVEL, it) }
@@ -664,8 +670,8 @@ open class PokemonProperties {
nature = if (tag.contains(DataKeys.POKEMON_NATURE)) tag.getString(DataKeys.POKEMON_NATURE) else null
ability = if (tag.contains(DataKeys.POKEMON_ABILITY)) tag.getString(DataKeys.POKEMON_ABILITY) else null
status = if (tag.contains(DataKeys.POKEMON_STATUS_NAME)) tag.getString(DataKeys.POKEMON_STATUS_NAME) else null
ivs = if (tag.contains(DataKeys.POKEMON_IVS)) IVs().loadFromNBT(tag.getCompound(DataKeys.POKEMON_IVS)) as IVs else null
evs = if (tag.contains(DataKeys.POKEMON_EVS)) EVs().loadFromNBT(tag.getCompound(DataKeys.POKEMON_EVS)) as EVs else null
ivs = if (tag.contains(DataKeys.POKEMON_IVS)) IVs.CODEC.decode(NbtOps.INSTANCE, tag.getCompound(DataKeys.POKEMON_IVS)).result().getOrNull()?.first else null
evs = if (tag.contains(DataKeys.POKEMON_EVS)) EVs.CODEC.decode(NbtOps.INSTANCE, tag.getCompound(DataKeys.POKEMON_EVS)).result().getOrNull()?.first else null
type = if (tag.contains(DataKeys.ELEMENTAL_TYPE)) tag.getString(DataKeys.ELEMENTAL_TYPE) else null
teraType = if (tag.contains(DataKeys.POKEMON_TERA_TYPE)) tag.getString(DataKeys.POKEMON_TERA_TYPE) else null
dmaxLevel = if (tag.contains(DataKeys.POKEMON_DMAX_LEVEL)) tag.getInt(DataKeys.POKEMON_DMAX_LEVEL) else null
@@ -697,8 +703,8 @@ open class PokemonProperties {
nature?.let { json.addProperty(DataKeys.POKEMON_NATURE, it) }
ability?.let { json.addProperty(DataKeys.POKEMON_ABILITY, it) }
status?.let { json.addProperty(DataKeys.POKEMON_STATUS_NAME, it) }
ivs?.let { json.add(DataKeys.POKEMON_IVS, it.saveToJSON(JsonObject())) }
evs?.let { json.add(DataKeys.POKEMON_EVS, it.saveToJSON(JsonObject())) }
ivs?.let { json.add(DataKeys.POKEMON_IVS, IVs.CODEC.encodeStart(JsonOps.INSTANCE, it).result().get()) }
evs?.let { json.add(DataKeys.POKEMON_EVS, EVs.CODEC.encodeStart(JsonOps.INSTANCE, it).result().get()) }
type?.let { json.addProperty(DataKeys.ELEMENTAL_TYPE, it) }
teraType?.let { json.addProperty(DataKeys.POKEMON_TERA_TYPE, it) }
dmaxLevel?.let { json.addProperty(DataKeys.POKEMON_DMAX_LEVEL, it) }
@@ -729,8 +735,8 @@ open class PokemonProperties {
nature = json.get(DataKeys.POKEMON_NATURE)?.asString
ability = json.get(DataKeys.POKEMON_ABILITY)?.asString
status = json.get(DataKeys.POKEMON_STATUS_NAME)?.asString
ivs = json.getAsJsonObject(DataKeys.POKEMON_IVS)?.let { IVs().loadFromJSON(it) } as? IVs
evs = json.getAsJsonObject(DataKeys.POKEMON_EVS)?.let { EVs().loadFromJSON(it) } as? EVs
ivs = json.getAsJsonObject(DataKeys.POKEMON_IVS)?.let { IVs.CODEC.decode(JsonOps.INSTANCE, it).result().getOrNull()?.first }
evs = json.getAsJsonObject(DataKeys.POKEMON_EVS)?.let { EVs.CODEC.decode(JsonOps.INSTANCE, it).result().getOrNull()?.first }
type = json.get(DataKeys.ELEMENTAL_TYPE)?.asString
teraType = json.get(DataKeys.POKEMON_TERA_TYPE)?.asString
dmaxLevel = json.get(DataKeys.POKEMON_DMAX_LEVEL)?.asInt
@@ -39,9 +39,9 @@ interface RidingBehaviour<Settings : RidingBehaviourSettings, State : RidingBeha
fun speed(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player): Float
fun clampPassengerRotation(settings: Settings, state: State, entity: PokemonEntity, driver: LivingEntity) {}
fun clampPassengerRotation(settings: Settings, state: State, vehicle: PokemonEntity, driver: LivingEntity) {}
fun updatePassengerRotation(settings: Settings, state: State, entity: PokemonEntity, driver: LivingEntity) {}
fun updatePassengerRotation(settings: Settings, state: State, vehicle: PokemonEntity, driver: LivingEntity) {}
fun rotation(settings: Settings, state: State, vehicle: PokemonEntity, driver: LivingEntity): Vec2
@@ -88,6 +88,8 @@ interface RidingBehaviour<Settings : RidingBehaviourSettings, State : RidingBeha
fun shouldRotatePlayerHead(settings: Settings, state: State, vehicle: PokemonEntity): Boolean
fun maxUpStep(settings: Settings, state: State, vehicle: PokemonEntity): Float? = null
fun createDefaultState(settings: Settings): State
}
@@ -23,8 +23,8 @@ import net.minecraft.world.phys.Vec3
* @author landonjw
*/
open class RidingBehaviourState {
open val rideVelocity: SidedRidingState<Vec3> = ridingState(Vec3.ZERO, Side.BOTH)
open val stamina: SidedRidingState<Float> = ridingState(1.0F, Side.BOTH)
open val rideVelocity: SidedRidingState<Vec3> = ridingState(Vec3.ZERO, Side.CLIENT)
open val stamina: SidedRidingState<Float> = ridingState(1.0F, Side.CLIENT)
open fun reset() {
rideVelocity.set(Vec3.ZERO, true)
@@ -8,23 +8,28 @@
package com.cobblemon.mod.common.api.riding.behaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.*
import com.cobblemon.mod.common.api.riding.behaviour.types.air.*
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.land.VehicleBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BoatBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BurstBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.DolphinBehaviour
import net.minecraft.resources.ResourceLocation
object RidingBehaviours {
val behaviours = mutableMapOf<ResourceLocation, RidingBehaviour<RidingBehaviourSettings, RidingBehaviourState>>()
init {
register(BirdAirBehaviour.KEY, BirdAirBehaviour())
register(BirdBehaviour.KEY, BirdBehaviour())
register(DolphinBehaviour.KEY, DolphinBehaviour())
register(GenericLandBehaviour.KEY, GenericLandBehaviour())
register(GenericSwimBehaviour.KEY, GenericSwimBehaviour())
register(GliderAirBehaviour.KEY, GliderAirBehaviour())
register(HorseBehaviour.KEY, HorseBehaviour())
register(BoatBehaviour.KEY, BoatBehaviour())
register(GliderBehaviour.KEY, GliderBehaviour())
register(HelicopterBehaviour.KEY, HelicopterBehaviour())
register(JetAirBehaviour.KEY, JetAirBehaviour())
register(SwimDashBehaviour.KEY, SwimDashBehaviour())
register(VehicleLandBehaviour.KEY, VehicleLandBehaviour())
register(JetBehaviour.KEY, JetBehaviour())
register(BurstBehaviour.KEY, BurstBehaviour())
register(VehicleBehaviour.KEY, VehicleBehaviour())
register(HoverBehaviour.KEY, HoverBehaviour())
register(CompositeBehaviour.KEY, CompositeBehaviour())
}
@@ -26,7 +26,7 @@ import net.minecraft.world.phys.Vec3
class RidingController<Settings : RidingBehaviourSettings, State : RidingBehaviourState>(val behaviour: RidingBehaviour<Settings, State>) : RidingBehaviour<Settings, State> {
override val key = behaviour.key
override fun getRidingStyle(settings: Settings, state: State): RidingStyle {
return behaviour.getRidingStyle(settings, state)
}
@@ -45,6 +45,16 @@ class RidingController<Settings : RidingBehaviourSettings, State : RidingBehavio
return behaviour.speed(settings, state, vehicle, driver)
}
override fun updatePassengerRotation(
settings: Settings,
state: State,
vehicle: PokemonEntity,
driver: LivingEntity
) {
if (!isActive(settings, state, vehicle)) return
return behaviour.updatePassengerRotation(settings, state, vehicle, driver)
}
override fun clampPassengerRotation(settings: Settings, state: State, vehicle: PokemonEntity, driver: LivingEntity) {
if (!isActive(settings, state, vehicle)) return
return behaviour.clampPassengerRotation(settings, state, vehicle, driver)
@@ -175,6 +185,11 @@ class RidingController<Settings : RidingBehaviourSettings, State : RidingBehavio
return behaviour.setRideBar(settings, state, vehicle, driver)
}
override fun maxUpStep(settings: Settings, state: State, vehicle: PokemonEntity): Float? {
if (!isActive(settings, state, vehicle)) return null
return behaviour.maxUpStep(settings, state, vehicle)
}
override fun createDefaultState(settings: Settings): State {
return behaviour.createDefaultState(settings)
}
@@ -1,267 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.riding.behaviour.types
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
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.posing.PoseOption
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.*
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
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
class GenericSwimBehaviour : RidingBehaviour<GenericSwimSettings, RidingBehaviourState> {
companion object {
val KEY = cobblemonResource("swim/generic")
}
override val key = KEY
override fun getRidingStyle(settings: GenericSwimSettings, state: RidingBehaviourState): RidingStyle {
return RidingStyle.LIQUID
}
val poseProvider = PoseProvider<GenericSwimSettings, RidingBehaviourState>(PoseType.FLOAT)
.with(PoseOption(PoseType.SWIM) { _, _, entity -> entity.isSwimming && entity.entityData.get(PokemonEntity.MOVING) })
override fun isActive(settings: GenericSwimSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
if (vehicle.isInWater || vehicle.isUnderWater) {
return@any true
}
val blockState = vehicle.level().getBlockState(it)
return@any !blockState.fluidState.isEmpty
}
}
override fun pose(settings: GenericSwimSettings, state: RidingBehaviourState, vehicle: PokemonEntity): PoseType {
return poseProvider.select(settings, state, vehicle)
}
override fun speed(settings: GenericSwimSettings, state: RidingBehaviourState, vehicle: PokemonEntity, driver: Player): Float {
return vehicle.runtime.resolveFloat(settings.speed)
}
override fun rotation(
settings: GenericSwimSettings,
state: RidingBehaviourState,
vehicle: PokemonEntity,
driver: LivingEntity
): Vec2 {
return Vec2(driver.xRot * 0.5f, driver.yRot)
}
override fun velocity(
settings: GenericSwimSettings,
state: RidingBehaviourState,
vehicle: PokemonEntity,
driver: Player,
input: Vec3
): Vec3 {
val runtime = vehicle.runtime
val driveFactor = runtime.resolveFloat(settings.driveFactor)
val strafeFactor = runtime.resolveFloat(settings.strafeFactor)
val f = driver.xxa * strafeFactor
var g = driver.zza * driveFactor
if (g <= 0.0f) {
g *= runtime.resolveFloat(settings.reverseDriveFactor)
}
val velocity = Vec3(f.toDouble(), 0.0, g.toDouble())
return velocity
}
override fun angRollVel(
settings: GenericSwimSettings,
state: RidingBehaviourState,
vehicle: PokemonEntity,
driver: Player,
deltaTime: Double
): Vec3 {
return Vec3.ZERO
}
override fun rotationOnMouseXY(
settings: GenericSwimSettings,
state: RidingBehaviourState,
vehicle: PokemonEntity,
driver: Player,
mouseY: Double,
mouseX: Double,
mouseYSmoother: SmoothDouble,
mouseXSmoother: SmoothDouble,
sensitivity: Double,
deltaTime: Double
): Vec3 {
if (driver !is OrientationControllable) return Vec3.ZERO
//Might need to add the smoothing here for default.
val invertRoll = if (Cobblemon.config.invertRoll) -1 else 1
val invertPitch = if (Cobblemon.config.invertPitch) -1 else 1
return Vec3(0.0, mouseY * invertPitch, mouseX * invertRoll)
}
override fun canJump(
settings: GenericSwimSettings,
state: RidingBehaviourState,
vehicle: PokemonEntity,
driver: Player
): Boolean {
return vehicle.runtime.resolveBoolean(settings.canJump)
}
override fun setRideBar(
settings: GenericSwimSettings,
state: RidingBehaviourState,
vehicle: PokemonEntity,
driver: Player
): Float {
return 0.0f
}
override fun jumpForce(
settings: GenericSwimSettings,
state: RidingBehaviourState,
vehicle: PokemonEntity,
driver: Player,
jumpStrength: Int
): Vec3 {
val runtime = vehicle.runtime
runtime.environment.query.addFunction("jump_strength") { DoubleValue(jumpStrength.toDouble()) }
val jumpVector = settings.jumpVector.map { runtime.resolveFloat(it) }
return Vec3(jumpVector[0].toDouble(), jumpVector[1].toDouble(), jumpVector[2].toDouble())
}
override fun gravity(
settings: GenericSwimSettings,
state: RidingBehaviourState,
vehicle: PokemonEntity,
regularGravity: Double
): Double {
return regularGravity
}
override fun rideFovMultiplier(
settings: GenericSwimSettings,
state: RidingBehaviourState,
vehicle: PokemonEntity,
driver: Player
): Float {
return 1.0f
}
override fun useAngVelSmoothing(settings: GenericSwimSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
return false
}
override fun useRidingAltPose(
settings: GenericSwimSettings,
state: RidingBehaviourState,
vehicle: PokemonEntity,
driver: Player
): ResourceLocation {
return cobblemonResource("no_pose")
}
override fun inertia(settings: GenericSwimSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Double {
return 0.5
}
override fun shouldRoll(settings: GenericSwimSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
return false
}
override fun turnOffOnGround(settings: GenericSwimSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
return false
}
override fun dismountOnShift(settings: GenericSwimSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
return false
}
override fun shouldRotatePokemonHead(
settings: GenericSwimSettings,
state: RidingBehaviourState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun shouldRotatePlayerHead(
settings: GenericSwimSettings,
state: RidingBehaviourState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun createDefaultState(settings: GenericSwimSettings) = RidingBehaviourState()
}
class GenericSwimSettings : RidingBehaviourSettings {
override val key = GenericSwimBehaviour.KEY
var canJump = "true".asExpression()
private set
var jumpVector = listOf("0".asExpression(), "0.3".asExpression(), "0".asExpression())
private set
var speed = "1.0".asExpression()
private set
var driveFactor = "1.0".asExpression()
private set
var reverseDriveFactor = "0.25".asExpression()
private set
var strafeFactor = "0.2".asExpression()
private set
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
buffer.writeExpression(canJump)
buffer.writeExpression(jumpVector[0])
buffer.writeExpression(jumpVector[1])
buffer.writeExpression(jumpVector[2])
buffer.writeExpression(speed)
buffer.writeExpression(driveFactor)
buffer.writeExpression(reverseDriveFactor)
buffer.writeExpression(strafeFactor)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
canJump = buffer.readExpression()
jumpVector = listOf(
buffer.readExpression(),
buffer.readExpression(),
buffer.readExpression()
)
speed = buffer.readExpression()
driveFactor = buffer.readExpression()
reverseDriveFactor = buffer.readExpression()
strafeFactor = buffer.readExpression()
}
}
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.riding.behaviour.types
package com.cobblemon.mod.common.api.riding.behaviour.types.air
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangMath.lerp
@@ -33,31 +33,31 @@ import net.minecraft.world.phys.Vec3
import org.joml.Vector3f
import kotlin.math.*
class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
companion object {
val KEY = cobblemonResource("air/bird")
}
override val key: ResourceLocation = KEY
override fun getRidingStyle(settings: BirdAirSettings, state: BirdAirState): RidingStyle {
override fun getRidingStyle(settings: BirdSettings, state: BirdState): RidingStyle {
return RidingStyle.AIR
}
val poseProvider = PoseProvider<BirdAirSettings, BirdAirState>(PoseType.HOVER)
val poseProvider = PoseProvider<BirdSettings, BirdState>(PoseType.HOVER)
.with(PoseOption(PoseType.FLY) { _, state, _ -> state.rideVelocity.get().z > 0.2 })
override fun isActive(settings: BirdAirSettings, state: BirdAirState, vehicle: PokemonEntity): Boolean {
override fun isActive(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity): Boolean {
return true
}
override fun pose(settings: BirdAirSettings, state: BirdAirState, vehicle: PokemonEntity): PoseType {
override fun pose(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity): PoseType {
return poseProvider.select(settings, state, vehicle)
}
override fun speed(
settings: BirdAirSettings,
state: BirdAirState,
settings: BirdSettings,
state: BirdState,
vehicle: PokemonEntity,
driver: Player
): Float {
@@ -65,8 +65,8 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
}
override fun rotation(
settings: BirdAirSettings,
state: BirdAirState,
settings: BirdSettings,
state: BirdState,
vehicle: PokemonEntity,
driver: LivingEntity
): Vec2 {
@@ -74,8 +74,8 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
}
override fun velocity(
settings: BirdAirSettings,
state: BirdAirState,
settings: BirdSettings,
state: BirdState,
vehicle: PokemonEntity,
driver: Player,
input: Vec3
@@ -122,7 +122,7 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
/*
* Calculates the change in the ride space vector due to player input and ride state
*/
fun calculateRideSpaceVel(settings: BirdAirSettings, state: BirdAirState, vehicle: PokemonEntity, driver: Player) {
fun calculateRideSpaceVel(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity, driver: Player) {
//retrieve stats
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val glideTopSpeed = vehicle.runtime.resolveDouble(settings.glidespeedExpr)
@@ -240,8 +240,8 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
}
override fun angRollVel(
settings: BirdAirSettings,
state: BirdAirState,
settings: BirdSettings,
state: BirdState,
vehicle: PokemonEntity,
driver: Player,
deltaTime: Double
@@ -282,8 +282,8 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
}
override fun rotationOnMouseXY(
settings: BirdAirSettings,
state: BirdAirState,
settings: BirdSettings,
state: BirdState,
vehicle: PokemonEntity,
driver: Player,
mouseY: Double,
@@ -367,8 +367,8 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
}
override fun canJump(
settings: BirdAirSettings,
state: BirdAirState,
settings: BirdSettings,
state: BirdState,
vehicle: PokemonEntity,
driver: Player
): Boolean {
@@ -376,8 +376,8 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
}
override fun setRideBar(
settings: BirdAirSettings,
state: BirdAirState,
settings: BirdSettings,
state: BirdState,
vehicle: PokemonEntity,
driver: Player
): Float {
@@ -385,8 +385,8 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
}
override fun jumpForce(
settings: BirdAirSettings,
state: BirdAirState,
settings: BirdSettings,
state: BirdState,
vehicle: PokemonEntity,
driver: Player,
jumpStrength: Int
@@ -395,8 +395,8 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
}
override fun gravity(
settings: BirdAirSettings,
state: BirdAirState,
settings: BirdSettings,
state: BirdState,
vehicle: PokemonEntity,
regularGravity: Double
): Double {
@@ -404,8 +404,8 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
}
override fun rideFovMultiplier(
settings: BirdAirSettings,
state: BirdAirState,
settings: BirdSettings,
state: BirdState,
vehicle: PokemonEntity,
driver: Player
): Float {
@@ -420,16 +420,16 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
}
override fun useAngVelSmoothing(
settings: BirdAirSettings,
state: BirdAirState,
settings: BirdSettings,
state: BirdState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun useRidingAltPose(
settings: BirdAirSettings,
state: BirdAirState,
settings: BirdSettings,
state: BirdState,
vehicle: PokemonEntity,
driver: Player
): ResourceLocation {
@@ -439,44 +439,44 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
return cobblemonResource("no_pose")
}
override fun inertia(settings: BirdAirSettings, state: BirdAirState, vehicle: PokemonEntity): Double {
override fun inertia(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity): Double {
return 0.5
}
override fun shouldRoll(settings: BirdAirSettings, state: BirdAirState, vehicle: PokemonEntity): Boolean {
override fun shouldRoll(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity): Boolean {
return true
}
override fun turnOffOnGround(settings: BirdAirSettings, state: BirdAirState, vehicle: PokemonEntity): Boolean {
override fun turnOffOnGround(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity): Boolean {
return false
}
override fun dismountOnShift(settings: BirdAirSettings, state: BirdAirState, vehicle: PokemonEntity): Boolean {
override fun dismountOnShift(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity): Boolean {
return false
}
override fun shouldRotatePokemonHead(
settings: BirdAirSettings,
state: BirdAirState,
settings: BirdSettings,
state: BirdState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun shouldRotatePlayerHead(
settings: BirdAirSettings,
state: BirdAirState,
settings: BirdSettings,
state: BirdState,
vehicle: PokemonEntity
): Boolean {
return true
}
override fun createDefaultState(settings: BirdAirSettings) = BirdAirState()
override fun createDefaultState(settings: BirdSettings) = BirdState()
}
class BirdAirSettings : RidingBehaviourSettings {
override val key = BirdAirBehaviour.KEY
class BirdSettings : RidingBehaviourSettings {
override val key = BirdBehaviour.KEY
var infiniteAltitude: Expression = "false".asExpression()
private set
@@ -518,7 +518,7 @@ class BirdAirSettings : RidingBehaviourSettings {
}
}
class BirdAirState : RidingBehaviourState() {
class BirdState : RidingBehaviourState() {
var gliding = ridingState(false, Side.CLIENT)
var lastGlide = ridingState(-100L, Side.CLIENT)
var currRollCorrectionForce = ridingState(0.0, Side.CLIENT)
@@ -542,10 +542,10 @@ class BirdAirState : RidingBehaviourState() {
}
override fun toString(): String {
return "BirdAirState(rideVelocity=${rideVelocity.get()}, stamina=${stamina.get()}, gliding=${gliding.get()})"
return "BirdState(rideVelocity=${rideVelocity.get()}, stamina=${stamina.get()}, gliding=${gliding.get()})"
}
override fun copy() = BirdAirState().also {
override fun copy() = BirdState().also {
it.rideVelocity.set(this.rideVelocity.get(), forced = true)
it.stamina.set(this.stamina.get(), forced = true)
it.gliding.set(this.gliding.get(), forced = true)
@@ -554,7 +554,7 @@ class BirdAirState : RidingBehaviourState() {
}
override fun shouldSync(previous: RidingBehaviourState): Boolean {
if (previous !is BirdAirState) return false
if (previous !is BirdState) return false
if (previous.gliding.get() != gliding.get()) return true
return super.shouldSync(previous)
}
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.riding.behaviour.types
package com.cobblemon.mod.common.api.riding.behaviour.types.air
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.Cobblemon
@@ -29,34 +29,34 @@ import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
class GliderAirBehaviour : RidingBehaviour<GliderAirSettings, RidingBehaviourState> {
class GliderBehaviour : RidingBehaviour<GliderSettings, RidingBehaviourState> {
companion object {
val KEY = cobblemonResource("air/glider")
}
override val key = KEY
override fun getRidingStyle(settings: GliderAirSettings, state: RidingBehaviourState): RidingStyle {
override fun getRidingStyle(settings: GliderSettings, state: RidingBehaviourState): RidingStyle {
return RidingStyle.AIR
}
val poseProvider = PoseProvider<GliderAirSettings, RidingBehaviourState>(PoseType.HOVER)
val poseProvider = PoseProvider<GliderSettings, RidingBehaviourState>(PoseType.HOVER)
.with(PoseOption(PoseType.FLY) { _, _, entity -> entity.entityData.get(PokemonEntity.MOVING) })
override fun isActive(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
override fun isActive(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
return true
}
override fun pose(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity): PoseType {
override fun pose(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity): PoseType {
return poseProvider.select(settings, state, vehicle)
}
override fun speed(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity, driver: Player): Float {
override fun speed(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity, driver: Player): Float {
return vehicle.runtime.resolveFloat(settings.speed)
}
override fun rotation(
settings: GliderAirSettings,
settings: GliderSettings,
state: RidingBehaviourState,
vehicle: PokemonEntity,
driver: LivingEntity
@@ -65,7 +65,7 @@ class GliderAirBehaviour : RidingBehaviour<GliderAirSettings, RidingBehaviourSta
}
override fun velocity(
settings: GliderAirSettings,
settings: GliderSettings,
state: RidingBehaviourState,
vehicle: PokemonEntity,
driver: Player,
@@ -80,7 +80,7 @@ class GliderAirBehaviour : RidingBehaviour<GliderAirSettings, RidingBehaviourSta
}
override fun angRollVel(
settings: GliderAirSettings,
settings: GliderSettings,
state: RidingBehaviourState,
vehicle: PokemonEntity,
driver: Player,
@@ -90,7 +90,7 @@ class GliderAirBehaviour : RidingBehaviour<GliderAirSettings, RidingBehaviourSta
}
override fun rotationOnMouseXY(
settings: GliderAirSettings,
settings: GliderSettings,
state: RidingBehaviourState,
vehicle: PokemonEntity,
driver: Player,
@@ -109,12 +109,12 @@ class GliderAirBehaviour : RidingBehaviour<GliderAirSettings, RidingBehaviourSta
return Vec3(0.0, mouseY * invertPitch, mouseX * invertRoll)
}
override fun canJump(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity, driver: Player): Boolean {
override fun canJump(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity, driver: Player): Boolean {
return false
}
override fun setRideBar(
settings: GliderAirSettings,
settings: GliderSettings,
state: RidingBehaviourState,
vehicle: PokemonEntity,
driver: Player
@@ -123,7 +123,7 @@ class GliderAirBehaviour : RidingBehaviour<GliderAirSettings, RidingBehaviourSta
}
override fun jumpForce(
settings: GliderAirSettings,
settings: GliderSettings,
state: RidingBehaviourState,
vehicle: PokemonEntity,
driver: Player,
@@ -133,7 +133,7 @@ class GliderAirBehaviour : RidingBehaviour<GliderAirSettings, RidingBehaviourSta
}
override fun gravity(
settings: GliderAirSettings,
settings: GliderSettings,
state: RidingBehaviourState,
vehicle: PokemonEntity,
regularGravity: Double
@@ -142,7 +142,7 @@ class GliderAirBehaviour : RidingBehaviour<GliderAirSettings, RidingBehaviourSta
}
override fun rideFovMultiplier(
settings: GliderAirSettings,
settings: GliderSettings,
state: RidingBehaviourState,
vehicle: PokemonEntity,
driver: Player
@@ -150,12 +150,12 @@ class GliderAirBehaviour : RidingBehaviour<GliderAirSettings, RidingBehaviourSta
return 1.0f
}
override fun useAngVelSmoothing(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
override fun useAngVelSmoothing(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
return false
}
override fun useRidingAltPose(
settings: GliderAirSettings,
settings: GliderSettings,
state: RidingBehaviourState,
vehicle: PokemonEntity,
driver: Player
@@ -163,35 +163,35 @@ class GliderAirBehaviour : RidingBehaviour<GliderAirSettings, RidingBehaviourSta
return cobblemonResource("no_pose")
}
override fun inertia(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Double {
override fun inertia(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Double {
return 0.5
}
override fun shouldRoll(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
override fun shouldRoll(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
return false
}
override fun turnOffOnGround(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
override fun turnOffOnGround(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
return false
}
override fun dismountOnShift(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
override fun dismountOnShift(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
return false
}
override fun shouldRotatePokemonHead(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
override fun shouldRotatePokemonHead(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
return false
}
override fun shouldRotatePlayerHead(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
override fun shouldRotatePlayerHead(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
return false
}
override fun createDefaultState(settings: GliderAirSettings) = RidingBehaviourState()
override fun createDefaultState(settings: GliderSettings) = RidingBehaviourState()
}
class GliderAirSettings : RidingBehaviourSettings {
override val key = GliderAirBehaviour.KEY
class GliderSettings : RidingBehaviourSettings {
override val key = GliderBehaviour.KEY
var glideSpeed: Expression = "0.1".asExpression()
private set
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.riding.behaviour.types
package com.cobblemon.mod.common.api.riding.behaviour.types.air
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.Cobblemon
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.riding.behaviour.types
package com.cobblemon.mod.common.api.riding.behaviour.types.air
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.api.riding.RidingStyle
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.riding.behaviour.types
package com.cobblemon.mod.common.api.riding.behaviour.types.air
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.Cobblemon
@@ -30,21 +30,21 @@ import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
import kotlin.math.*
class JetAirBehaviour : RidingBehaviour<JetAirSettings, JetAirState> {
class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
companion object {
val KEY = cobblemonResource("air/jet")
}
override val key = KEY
override fun getRidingStyle(settings: JetAirSettings, state: JetAirState): RidingStyle {
override fun getRidingStyle(settings: JetSettings, state: JetState): RidingStyle {
return RidingStyle.AIR
}
val poseProvider = PoseProvider<JetAirSettings, JetAirState>(PoseType.HOVER)
val poseProvider = PoseProvider<JetSettings, JetState>(PoseType.HOVER)
.with(PoseOption(PoseType.FLY) { _, state, _ -> state.rideVelocity.get().z > 0.1 })
override fun isActive(settings: JetAirSettings, state: JetAirState, vehicle: PokemonEntity): Boolean {
override fun isActive(settings: JetSettings, state: JetState, vehicle: PokemonEntity): Boolean {
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
//Need to check other fluids
if (vehicle.isInWater || vehicle.isUnderWater) {
@@ -59,11 +59,11 @@ class JetAirBehaviour : RidingBehaviour<JetAirSettings, JetAirState> {
}
}
override fun pose(settings: JetAirSettings, state: JetAirState, vehicle: PokemonEntity): PoseType {
override fun pose(settings: JetSettings, state: JetState, vehicle: PokemonEntity): PoseType {
return poseProvider.select(settings, state, vehicle)
}
override fun speed(settings: JetAirSettings, state: JetAirState, vehicle: PokemonEntity, driver: Player): Float {
override fun speed(settings: JetSettings, state: JetState, vehicle: PokemonEntity, driver: Player): Float {
//retrieve stats
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val staminaStat = vehicle.runtime.resolveDouble(settings.staminaExpr)
@@ -104,8 +104,8 @@ class JetAirBehaviour : RidingBehaviour<JetAirSettings, JetAirState> {
}
override fun rotation(
settings: JetAirSettings,
state: JetAirState,
settings: JetSettings,
state: JetState,
vehicle: PokemonEntity,
driver: LivingEntity
): Vec2 {
@@ -113,8 +113,8 @@ class JetAirBehaviour : RidingBehaviour<JetAirSettings, JetAirState> {
}
override fun velocity(
settings: JetAirSettings,
state: JetAirState,
settings: JetSettings,
state: JetState,
vehicle: PokemonEntity,
driver: Player,
input: Vec3
@@ -153,8 +153,8 @@ class JetAirBehaviour : RidingBehaviour<JetAirSettings, JetAirState> {
* Calculates the change in the ride space vector due to player input and ride state
*/
private fun calculateRideSpaceVel(
settings: JetAirSettings,
state: JetAirState,
settings: JetSettings,
state: JetState,
vehicle: PokemonEntity,
driver: Player
) {
@@ -186,8 +186,8 @@ class JetAirBehaviour : RidingBehaviour<JetAirSettings, JetAirState> {
}
override fun angRollVel(
settings: JetAirSettings,
state: JetAirState,
settings: JetSettings,
state: JetState,
vehicle: PokemonEntity,
driver: Player,
deltaTime: Double
@@ -211,8 +211,8 @@ class JetAirBehaviour : RidingBehaviour<JetAirSettings, JetAirState> {
}
override fun rotationOnMouseXY(
settings: JetAirSettings,
state: JetAirState,
settings: JetSettings,
state: JetState,
vehicle: PokemonEntity,
driver: Player,
mouseY: Double,
@@ -273,8 +273,8 @@ class JetAirBehaviour : RidingBehaviour<JetAirSettings, JetAirState> {
}
override fun canJump(
settings: JetAirSettings,
state: JetAirState,
settings: JetSettings,
state: JetState,
vehicle: PokemonEntity,
driver: Player
): Boolean {
@@ -282,8 +282,8 @@ class JetAirBehaviour : RidingBehaviour<JetAirSettings, JetAirState> {
}
override fun setRideBar(
settings: JetAirSettings,
state: JetAirState,
settings: JetSettings,
state: JetState,
vehicle: PokemonEntity,
driver: Player
): Float {
@@ -291,8 +291,8 @@ class JetAirBehaviour : RidingBehaviour<JetAirSettings, JetAirState> {
}
override fun jumpForce(
settings: JetAirSettings,
state: JetAirState,
settings: JetSettings,
state: JetState,
vehicle: PokemonEntity,
driver: Player,
jumpStrength: Int
@@ -301,8 +301,8 @@ class JetAirBehaviour : RidingBehaviour<JetAirSettings, JetAirState> {
}
override fun gravity(
settings: JetAirSettings,
state: JetAirState,
settings: JetSettings,
state: JetState,
vehicle: PokemonEntity,
regularGravity: Double
): Double {
@@ -310,8 +310,8 @@ class JetAirBehaviour : RidingBehaviour<JetAirSettings, JetAirState> {
}
override fun rideFovMultiplier(
settings: JetAirSettings,
state: JetAirState,
settings: JetSettings,
state: JetState,
vehicle: PokemonEntity,
driver: Player
): Float {
@@ -326,52 +326,52 @@ class JetAirBehaviour : RidingBehaviour<JetAirSettings, JetAirState> {
return 1.0f + normalizedSpeed.pow(2).toFloat() * 0.2f
}
override fun useAngVelSmoothing(settings: JetAirSettings, state: JetAirState, vehicle: PokemonEntity): Boolean {
override fun useAngVelSmoothing(settings: JetSettings, state: JetState, vehicle: PokemonEntity): Boolean {
return true
}
override fun useRidingAltPose(
settings: JetAirSettings,
state: JetAirState,
settings: JetSettings,
state: JetState,
vehicle: PokemonEntity,
driver: Player
): ResourceLocation {
return cobblemonResource("no_pose")
}
override fun inertia(settings: JetAirSettings, state: JetAirState, vehicle: PokemonEntity): Double {
override fun inertia(settings: JetSettings, state: JetState, vehicle: PokemonEntity): Double {
return 1.0
}
override fun shouldRoll(settings: JetAirSettings, state: JetAirState, vehicle: PokemonEntity): Boolean {
override fun shouldRoll(settings: JetSettings, state: JetState, vehicle: PokemonEntity): Boolean {
return true
}
override fun turnOffOnGround(settings: JetAirSettings, state: JetAirState, vehicle: PokemonEntity): Boolean {
override fun turnOffOnGround(settings: JetSettings, state: JetState, vehicle: PokemonEntity): Boolean {
return false
}
override fun dismountOnShift(settings: JetAirSettings, state: JetAirState, vehicle: PokemonEntity): Boolean {
override fun dismountOnShift(settings: JetSettings, state: JetState, vehicle: PokemonEntity): Boolean {
return false
}
override fun shouldRotatePokemonHead(
settings: JetAirSettings,
state: JetAirState,
settings: JetSettings,
state: JetState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun shouldRotatePlayerHead(settings: JetAirSettings, state: JetAirState, vehicle: PokemonEntity): Boolean {
override fun shouldRotatePlayerHead(settings: JetSettings, state: JetState, vehicle: PokemonEntity): Boolean {
return false
}
override fun createDefaultState(settings: JetAirSettings) = JetAirState()
override fun createDefaultState(settings: JetSettings) = JetState()
}
class JetAirSettings : RidingBehaviourSettings {
override val key = JetAirBehaviour.KEY
class JetSettings : RidingBehaviourSettings {
override val key = JetBehaviour.KEY
var gravity: Expression = "0".asExpression()
private set
@@ -428,7 +428,7 @@ class JetAirSettings : RidingBehaviourSettings {
}
}
class JetAirState : RidingBehaviourState() {
class JetState : RidingBehaviourState() {
var currSpeed = ridingState(0.0, Side.CLIENT)
var currMouseXForce = ridingState(0.0, Side.CLIENT)
var currMouseYForce = ridingState(0.0, Side.CLIENT)
@@ -451,7 +451,7 @@ class JetAirState : RidingBehaviourState() {
currMouseYForce.set(0.0, forced = true)
}
override fun copy() = JetAirState().also {
override fun copy() = JetState().also {
it.currSpeed.set(currSpeed.get(), forced = true)
it.stamina.set(stamina.get(), forced = true)
it.rideVelocity.set(rideVelocity.get(), forced = true)
@@ -460,7 +460,7 @@ class JetAirState : RidingBehaviourState() {
}
override fun shouldSync(previous: RidingBehaviourState): Boolean {
if (previous !is JetAirState) return false
if (previous !is JetState) return false
if (previous.currSpeed != currSpeed) return true
return super.shouldSync(previous)
}
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.riding.behaviour.types
package com.cobblemon.mod.common.api.riding.behaviour.types.land
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.api.riding.RidingStyle
@@ -30,25 +30,25 @@ import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
import kotlin.math.*
class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandState> {
class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
companion object {
val KEY = cobblemonResource("land/generic")
val KEY = cobblemonResource("land/horse")
}
override val key = KEY
override fun getRidingStyle(settings: GenericLandSettings, state: GenericLandState): RidingStyle {
override fun getRidingStyle(settings: HorseSettings, state: HorseState): RidingStyle {
return RidingStyle.LAND
}
val poseProvider = PoseProvider<GenericLandSettings, GenericLandState>(PoseType.STAND)
val poseProvider = PoseProvider<HorseSettings, HorseState>(PoseType.STAND)
.with(PoseOption(PoseType.WALK) { _, state, _ ->
return@PoseOption abs(state.rideVelocity.get().z) > 0.0
})
override fun isActive(
settings: GenericLandSettings,
state: GenericLandState,
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity
): Boolean {
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
@@ -66,16 +66,16 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
}
override fun pose(
settings: GenericLandSettings,
state: GenericLandState,
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity
): PoseType {
return this.poseProvider.select(settings, state, vehicle)
}
override fun speed(
settings: GenericLandSettings,
state: GenericLandState,
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity,
driver: Player
): Float {
@@ -102,8 +102,8 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
}
override fun updatePassengerRotation(
settings: GenericLandSettings,
state: GenericLandState,
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity,
driver: LivingEntity
) {
@@ -128,8 +128,8 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
}
override fun clampPassengerRotation(
settings: GenericLandSettings,
state: GenericLandState,
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity,
driver: LivingEntity
) {
@@ -143,8 +143,8 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
override fun rotation(
settings: GenericLandSettings,
state: GenericLandState,
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity,
driver: LivingEntity
): Vec2 {
@@ -161,8 +161,8 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
* without it being instant.
*/
fun calcRotAmount(
settings: GenericLandSettings,
state: GenericLandState,
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity,
driver: LivingEntity
): Float {
@@ -198,8 +198,8 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
override fun velocity(
settings: GenericLandSettings,
state: GenericLandState,
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity,
driver: Player,
input: Vec3
@@ -209,8 +209,8 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
}
private fun calculateRideSpaceVel(
settings: GenericLandSettings,
state: GenericLandState,
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity,
driver: Player
): Vec3 {
@@ -293,8 +293,8 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
}
override fun angRollVel(
settings: GenericLandSettings,
state: GenericLandState,
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity,
driver: Player,
deltaTime: Double
@@ -303,8 +303,8 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
}
override fun rotationOnMouseXY(
settings: GenericLandSettings,
state: GenericLandState,
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity,
driver: Player,
mouseY: Double,
@@ -324,8 +324,8 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
}
override fun canJump(
settings: GenericLandSettings,
state: GenericLandState,
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity,
driver: Player
): Boolean {
@@ -333,8 +333,8 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
}
override fun setRideBar(
settings: GenericLandSettings,
state: GenericLandState,
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity,
driver: Player
): Float {
@@ -343,8 +343,8 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
}
override fun jumpForce(
settings: GenericLandSettings,
state: GenericLandState,
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity,
driver: Player,
jumpStrength: Int
@@ -353,8 +353,8 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
}
override fun gravity(
settings: GenericLandSettings,
state: GenericLandState,
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity,
regularGravity: Double
): Double {
@@ -362,8 +362,8 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
}
override fun rideFovMultiplier(
settings: GenericLandSettings,
state: GenericLandState,
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity,
driver: Player
): Float {
@@ -371,16 +371,16 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
}
override fun useAngVelSmoothing(
settings: GenericLandSettings,
state: GenericLandState,
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun useRidingAltPose(
settings: GenericLandSettings,
state: GenericLandState,
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity,
driver: Player
): ResourceLocation {
@@ -392,58 +392,58 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
}
override fun inertia(
settings: GenericLandSettings,
state: GenericLandState,
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity
): Double {
return 1.0
}
override fun shouldRoll(
settings: GenericLandSettings,
state: GenericLandState,
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun turnOffOnGround(
settings: GenericLandSettings,
state: GenericLandState,
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun dismountOnShift(
settings: GenericLandSettings,
state: GenericLandState,
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun shouldRotatePokemonHead(
settings: GenericLandSettings,
state: GenericLandState,
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun shouldRotatePlayerHead(
settings: GenericLandSettings,
state: GenericLandState,
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity
): Boolean {
return true
}
override fun createDefaultState(settings: GenericLandSettings) = GenericLandState()
override fun createDefaultState(settings: HorseSettings) = HorseState()
}
class GenericLandSettings : RidingBehaviourSettings {
override val key = GenericLandBehaviour.KEY
class HorseSettings : RidingBehaviourSettings {
override val key = HorseBehaviour.KEY
var canJump = "true".asExpression()
private set
@@ -486,7 +486,7 @@ class GenericLandSettings : RidingBehaviourSettings {
}
class GenericLandState : RidingBehaviourState() {
class HorseState : RidingBehaviourState() {
var sprinting = ridingState(false, Side.CLIENT)
var inAir = ridingState(false, Side.CLIENT)
@@ -508,7 +508,7 @@ class GenericLandState : RidingBehaviourState() {
inAir.set(false, forced = true)
}
override fun copy() = GenericLandState().also {
override fun copy() = HorseState().also {
it.rideVelocity.set(this.rideVelocity.get(), forced = true)
it.stamina.set(this.stamina.get(), forced = true)
it.sprinting.set(this.sprinting.get(), forced = true)
@@ -516,7 +516,7 @@ class GenericLandState : RidingBehaviourState() {
}
override fun shouldSync(previous: RidingBehaviourState): Boolean {
if (previous !is GenericLandState) return false
if (previous !is HorseState) return false
if (previous.sprinting.get() != sprinting.get()) return true
if (previous.inAir.get() != inAir.get()) return true
return super.shouldSync(previous)
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.riding.behaviour.types
package com.cobblemon.mod.common.api.riding.behaviour.types.land
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Cobblemon
@@ -31,7 +31,7 @@ import kotlin.math.absoluteValue
import kotlin.math.max
import kotlin.math.min
class VehicleLandBehaviour : RidingBehaviour<VehicleLandSettings, VehicleLandState> {
class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
companion object {
val KEY = cobblemonResource("land/vehicle")
@@ -54,14 +54,14 @@ class VehicleLandBehaviour : RidingBehaviour<VehicleLandSettings, VehicleLandSta
override val key = KEY
override fun getRidingStyle(settings: VehicleLandSettings, state: VehicleLandState): RidingStyle {
override fun getRidingStyle(settings: VehicleSettings, state: VehicleState): RidingStyle {
return RidingStyle.LAND
}
val poseProvider = PoseProvider<VehicleLandSettings, VehicleLandState>(PoseType.STAND)
val poseProvider = PoseProvider<VehicleSettings, VehicleState>(PoseType.STAND)
.with(PoseOption(PoseType.WALK) { _, _, entity -> entity.entityData.get(PokemonEntity.MOVING) })
override fun isActive(settings: VehicleLandSettings, state: VehicleLandState, vehicle: PokemonEntity): Boolean {
override fun isActive(settings: VehicleSettings, state: VehicleState, vehicle: PokemonEntity): Boolean {
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
//Need to check other fluids
if (vehicle.isInWater || vehicle.isUnderWater) {
@@ -76,13 +76,13 @@ class VehicleLandBehaviour : RidingBehaviour<VehicleLandSettings, VehicleLandSta
}
}
override fun pose(settings: VehicleLandSettings, state: VehicleLandState, vehicle: PokemonEntity): PoseType {
override fun pose(settings: VehicleSettings, state: VehicleState, vehicle: PokemonEntity): PoseType {
return poseProvider.select(settings, state, vehicle)
}
override fun speed(
settings: VehicleLandSettings,
state: VehicleLandState,
settings: VehicleSettings,
state: VehicleState,
vehicle: PokemonEntity,
driver: Player
): Float {
@@ -101,8 +101,8 @@ class VehicleLandBehaviour : RidingBehaviour<VehicleLandSettings, VehicleLandSta
}
override fun rotation(
settings: VehicleLandSettings,
state: VehicleLandState,
settings: VehicleSettings,
state: VehicleState,
vehicle: PokemonEntity,
driver: LivingEntity
): Vec2 {
@@ -113,8 +113,8 @@ class VehicleLandBehaviour : RidingBehaviour<VehicleLandSettings, VehicleLandSta
}
override fun velocity(
settings: VehicleLandSettings,
state: VehicleLandState,
settings: VehicleSettings,
state: VehicleState,
vehicle: PokemonEntity,
driver: Player,
input: Vec3
@@ -134,8 +134,8 @@ class VehicleLandBehaviour : RidingBehaviour<VehicleLandSettings, VehicleLandSta
}
override fun angRollVel(
settings: VehicleLandSettings,
state: VehicleLandState,
settings: VehicleSettings,
state: VehicleState,
vehicle: PokemonEntity,
driver: Player,
deltaTime: Double
@@ -144,8 +144,8 @@ class VehicleLandBehaviour : RidingBehaviour<VehicleLandSettings, VehicleLandSta
}
override fun rotationOnMouseXY(
settings: VehicleLandSettings,
state: VehicleLandState,
settings: VehicleSettings,
state: VehicleState,
vehicle: PokemonEntity,
driver: Player,
mouseY: Double,
@@ -164,8 +164,8 @@ class VehicleLandBehaviour : RidingBehaviour<VehicleLandSettings, VehicleLandSta
}
override fun canJump(
settings: VehicleLandSettings,
state: VehicleLandState,
settings: VehicleSettings,
state: VehicleState,
vehicle: PokemonEntity,
driver: Player
): Boolean {
@@ -173,8 +173,8 @@ class VehicleLandBehaviour : RidingBehaviour<VehicleLandSettings, VehicleLandSta
}
override fun setRideBar(
settings: VehicleLandSettings,
state: VehicleLandState,
settings: VehicleSettings,
state: VehicleState,
vehicle: PokemonEntity,
driver: Player
): Float {
@@ -182,8 +182,8 @@ class VehicleLandBehaviour : RidingBehaviour<VehicleLandSettings, VehicleLandSta
}
override fun jumpForce(
settings: VehicleLandSettings,
state: VehicleLandState,
settings: VehicleSettings,
state: VehicleState,
vehicle: PokemonEntity,
driver: Player,
jumpStrength: Int
@@ -195,8 +195,8 @@ class VehicleLandBehaviour : RidingBehaviour<VehicleLandSettings, VehicleLandSta
}
override fun gravity(
settings: VehicleLandSettings,
state: VehicleLandState,
settings: VehicleSettings,
state: VehicleState,
vehicle: PokemonEntity,
regularGravity: Double
): Double {
@@ -204,8 +204,8 @@ class VehicleLandBehaviour : RidingBehaviour<VehicleLandSettings, VehicleLandSta
}
override fun rideFovMultiplier(
settings: VehicleLandSettings,
state: VehicleLandState,
settings: VehicleSettings,
state: VehicleState,
vehicle: PokemonEntity,
driver: Player
): Float {
@@ -213,67 +213,67 @@ class VehicleLandBehaviour : RidingBehaviour<VehicleLandSettings, VehicleLandSta
}
override fun useAngVelSmoothing(
settings: VehicleLandSettings,
state: VehicleLandState,
settings: VehicleSettings,
state: VehicleState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun useRidingAltPose(
settings: VehicleLandSettings,
state: VehicleLandState,
settings: VehicleSettings,
state: VehicleState,
vehicle: PokemonEntity,
driver: Player
): ResourceLocation {
return cobblemonResource("no_pose")
}
override fun inertia(settings: VehicleLandSettings, state: VehicleLandState, vehicle: PokemonEntity): Double {
override fun inertia(settings: VehicleSettings, state: VehicleState, vehicle: PokemonEntity): Double {
return 0.5
}
override fun shouldRoll(settings: VehicleLandSettings, state: VehicleLandState, vehicle: PokemonEntity): Boolean {
override fun shouldRoll(settings: VehicleSettings, state: VehicleState, vehicle: PokemonEntity): Boolean {
return false
}
override fun turnOffOnGround(
settings: VehicleLandSettings,
state: VehicleLandState,
settings: VehicleSettings,
state: VehicleState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun dismountOnShift(
settings: VehicleLandSettings,
state: VehicleLandState,
settings: VehicleSettings,
state: VehicleState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun shouldRotatePokemonHead(
settings: VehicleLandSettings,
state: VehicleLandState,
settings: VehicleSettings,
state: VehicleState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun shouldRotatePlayerHead(
settings: VehicleLandSettings,
state: VehicleLandState,
settings: VehicleSettings,
state: VehicleState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun createDefaultState(settings: VehicleLandSettings) = VehicleLandState()
override fun createDefaultState(settings: VehicleSettings) = VehicleState()
}
class VehicleLandSettings : RidingBehaviourSettings {
override val key = VehicleLandBehaviour.KEY
class VehicleSettings : RidingBehaviourSettings {
override val key = VehicleBehaviour.KEY
var canJump = "true".asExpression()
private set
@@ -329,7 +329,7 @@ class VehicleLandSettings : RidingBehaviourSettings {
}
}
class VehicleLandState : RidingBehaviourState() {
class VehicleState : RidingBehaviourState() {
var currSpeed = ridingState(0.0, Side.BOTH)
var deltaRotation = ridingState(Vec2.ZERO, Side.BOTH)
@@ -340,10 +340,10 @@ class VehicleLandState : RidingBehaviourState() {
}
override fun toString(): String {
return "VehicleLandState(currSpeed=${currSpeed.get()}, deltaRotation=${deltaRotation.get()})"
return "VehicleState(currSpeed=${currSpeed.get()}, deltaRotation=${deltaRotation.get()})"
}
override fun copy() = VehicleLandState().also {
override fun copy() = VehicleState().also {
it.rideVelocity.set(rideVelocity.get(), forced = true)
it.stamina.set(stamina.get(), forced = true)
it.currSpeed.set(currSpeed.get(), forced = true)
@@ -0,0 +1,383 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.riding.behaviour.types.liquid
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.posing.PoseOption
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
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.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth
import net.minecraft.util.SmoothDouble
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
companion object {
val KEY = cobblemonResource("liquid/boat")
val TOP_SPEED = 2.0
}
override val key = KEY
override fun getRidingStyle(settings: BoatSettings, state: BoatState): RidingStyle {
return RidingStyle.LIQUID
}
val poseProvider = PoseProvider<BoatSettings, BoatState>(PoseType.FLOAT)
.with(PoseOption(PoseType.SWIM) { _, _, entity -> entity.isSwimming && entity.entityData.get(PokemonEntity.MOVING) })
override fun isActive(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity): Boolean {
if (state.jumpBuffer.get() != -1) {
return true
}
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
if (vehicle.isInWater || vehicle.isUnderWater) {
return@any true
}
val blockState = vehicle.level().getBlockState(it)
return@any !blockState.fluidState.isEmpty
}
}
override fun tick(
settings: BoatSettings,
state: BoatState,
vehicle: PokemonEntity,
driver: Player,
input: Vec3
) {
if (state.jumpBuffer.get() > 5) {
if ((vehicle.isInWater || vehicle.isUnderWater || vehicle.onGround())) {
state.jumpBuffer.set(-1)
}
}
else if (state.jumpBuffer.get() > -1) {
state.jumpBuffer.set(state.jumpBuffer.get() + 1)
}
if (driver.zza == 0f && (vehicle.isInWater || vehicle.isUnderWater)) {
if (state.staminaBuffer.get() >= 20) {
val staminaIncrease = vehicle.getRideStat(RidingStat.STAMINA, RidingStyle.LIQUID, 1.0, 2.0)
state.stamina.set(min(1.0f, state.stamina.get() + 0.01f * staminaIncrease.toFloat()))
}
}
else if (abs(state.rideVelocity.get().z) > 0.1) {
consumeStamina(vehicle, state, 0.01f)
}
state.staminaBuffer.set(state.staminaBuffer.get() + 1)
super.tick(settings, state, vehicle, driver, input)
}
override fun pose(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity): PoseType {
return poseProvider.select(settings, state, vehicle)
}
override fun speed(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity, driver: Player): Float {
return state.rideVelocity.get().length().toFloat()
}
override fun rotation(
settings: BoatSettings,
state: BoatState,
vehicle: PokemonEntity,
driver: LivingEntity
): Vec2 {
state.deltaRotation.set(state.deltaRotation.get() * 0.8)
return Vec2(vehicle.xRot, vehicle.yRot + state.deltaRotation.get().toFloat())
}
override fun velocity(
settings: BoatSettings,
state: BoatState,
vehicle: PokemonEntity,
driver: Player,
input: Vec3
): Vec3 {
applyStrafeRotation(vehicle, driver, state)
var velocity = state.rideVelocity.get()
velocity = applyVelocityFromInput(velocity, vehicle, driver, state)
velocity = applyGravity(velocity, vehicle, settings, state)
velocity = applyJump(velocity, vehicle, driver, state)
state.rideVelocity.set(velocity)
return state.rideVelocity.get()
}
private fun applyVelocityFromInput(velocity: Vec3, vehicle: PokemonEntity, driver: Player, state: BoatState): Vec3 {
val speed = vehicle.getRideStat(RidingStat.SPEED, RidingStyle.LIQUID, 0.8, TOP_SPEED)
val acceleration = vehicle.getRideStat(RidingStat.ACCELERATION, RidingStyle.LIQUID, 0.05, 0.5)
if (state.jumpBuffer.get() != -1 || !(vehicle.isInWater || vehicle.isUnderWater)) {
return velocity
}
val forwardInput = driver.zza.toDouble()
val delta = when {
!hasStamina(state) || forwardInput == 0.0 -> -velocity.z * 0.04
forwardInput < 0.0 -> when {
velocity.z < 0.0 -> forwardInput * acceleration * 0.5
else -> min(forwardInput * acceleration * 0.5, -velocity.z * 0.04) // We never want it to be slower to reverse than no input
}
else -> forwardInput * acceleration
}
return Vec3(
velocity.x,
velocity.y,
Mth.clamp(velocity.z + delta, -speed * 0.25, speed)
)
}
private fun applyJump(velocity: Vec3, vehicle: PokemonEntity, driver: Player, state: BoatState): Vec3 {
if (!driver.jumping) return velocity // Not jumping
if (state.jumpBuffer.get() != -1) return velocity // Already jumped very recently
if (!hasStamina(state)) return velocity // Has no stamina
val jumpStrength = vehicle.getRideStat(RidingStat.JUMP, RidingStyle.LIQUID, 1.0, 2.5)
state.jumpBuffer.set(0)
consumeStamina(vehicle, state, 0.05f)
return Vec3(velocity.x, velocity.y + jumpStrength, velocity.z)
}
private fun applyGravity(velocity: Vec3, vehicle: PokemonEntity, settings: BoatSettings, state: BoatState): Vec3 {
if (state.jumpBuffer.get() == -1 && vehicle.isInWater || vehicle.isUnderWater) {
return Vec3(velocity.x, -0.05, velocity.z)
}
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 applyStrafeRotation(vehicle: PokemonEntity, driver: Player, state: BoatState) {
val skill = vehicle.getRideStat(RidingStat.SKILL, RidingStyle.LIQUID, 0.5, 1.5)
val strafe = driver.xxa
if (abs(strafe) > 0) {
val increase = if (strafe > 0) -1 else 1
state.deltaRotation.set(state.deltaRotation.get() + increase * skill)
}
}
private fun hasStamina(state: BoatState): Boolean {
return state.stamina.get() != 0f
}
private fun consumeStamina(vehicle: PokemonEntity, state: BoatState, drain: Float) {
val stamina = vehicle.getRideStat(RidingStat.STAMINA, RidingStyle.LIQUID, 1.0, 5.0)
state.stamina.set(max(0f, state.stamina.get() - drain / stamina.toFloat()))
state.staminaBuffer.set(0)
}
override fun updatePassengerRotation(
settings: BoatSettings,
state: BoatState,
vehicle: PokemonEntity,
driver: LivingEntity
) {
driver.yRot += state.deltaRotation.get().toFloat() * 0.887f
driver.yHeadRot += state.deltaRotation.get().toFloat() * 0.887f
clampPassengerRotation(settings, state, vehicle, driver)
}
override fun clampPassengerRotation(
settings: BoatSettings,
state: BoatState,
vehicle: PokemonEntity,
driver: LivingEntity
) {
driver.setYBodyRot(vehicle.yRot)
val f: Float = Mth.wrapDegrees(driver.yRot - vehicle.yRot)
val g = Mth.clamp(f, -105.0f, 105.0f)
driver.yRotO += g - f
driver.yRot = driver.yRot + g - f
driver.setYHeadRot(driver.yRot)
vehicle.setYHeadRot(driver.yRot)
}
override fun angRollVel(
settings: BoatSettings,
state: BoatState,
vehicle: PokemonEntity,
driver: Player,
deltaTime: Double
): Vec3 {
return Vec3.ZERO
}
override fun rotationOnMouseXY(
settings: BoatSettings,
state: BoatState,
vehicle: PokemonEntity,
driver: Player,
mouseY: Double,
mouseX: Double,
mouseYSmoother: SmoothDouble,
mouseXSmoother: SmoothDouble,
sensitivity: Double,
deltaTime: Double
): Vec3 {
if (driver !is OrientationControllable) return Vec3.ZERO
//Might need to add the smoothing here for default.
val invertRoll = if (Cobblemon.config.invertRoll) -1 else 1
val invertPitch = if (Cobblemon.config.invertPitch) -1 else 1
return Vec3(0.0, mouseY * invertPitch, mouseX * invertRoll)
}
override fun canJump(
settings: BoatSettings,
state: BoatState,
vehicle: PokemonEntity,
driver: Player
): Boolean {
return false
}
override fun setRideBar(
settings: BoatSettings,
state: BoatState,
vehicle: PokemonEntity,
driver: Player
): Float {
return (state.stamina.get() / 1.0f)
}
override fun jumpForce(
settings: BoatSettings,
state: BoatState,
vehicle: PokemonEntity,
driver: Player,
jumpStrength: Int
): Vec3 {
return Vec3.ZERO
}
override fun gravity(
settings: BoatSettings,
state: BoatState,
vehicle: PokemonEntity,
regularGravity: Double
): Double {
return 0.0
}
override fun rideFovMultiplier(
settings: BoatSettings,
state: BoatState,
vehicle: PokemonEntity,
driver: Player
): Float {
if (state.rideVelocity.get().z > TOP_SPEED * 0.8) {
return Mth.lerp((state.rideVelocity.get().z - (TOP_SPEED * 0.8)) / (TOP_SPEED * 0.2), 1.0, 1.2).toFloat()
}
return 1.0f
}
override fun useAngVelSmoothing(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity): Boolean {
return false
}
override fun useRidingAltPose(
settings: BoatSettings,
state: BoatState,
vehicle: PokemonEntity,
driver: Player
): ResourceLocation {
return cobblemonResource("no_pose")
}
override fun inertia(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity): Double {
return 0.5
}
override fun shouldRoll(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity): Boolean {
return false
}
override fun turnOffOnGround(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity): Boolean {
return false
}
override fun dismountOnShift(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity): Boolean {
return false
}
override fun shouldRotatePokemonHead(
settings: BoatSettings,
state: BoatState,
vehicle: PokemonEntity
): Boolean {
return vehicle.runtime.resolveBoolean(settings.rotatePokemonHead)
}
override fun shouldRotatePlayerHead(
settings: BoatSettings,
state: BoatState,
vehicle: PokemonEntity
): Boolean {
return true
}
override fun maxUpStep(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity) = 0f
override fun createDefaultState(settings: BoatSettings) = BoatState()
}
class BoatSettings : RidingBehaviourSettings {
override val key = BoatBehaviour.KEY
var terminalVelocity = "-2.0".asExpression()
private set
var rotatePokemonHead = "true".asExpression()
private set
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
buffer.writeExpression(terminalVelocity)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
terminalVelocity = buffer.readExpression()
}
}
class BoatState : RidingBehaviourState() {
val deltaRotation = ridingState(0.0, Side.CLIENT)
val jumpBuffer = ridingState(-1, Side.CLIENT)
val staminaBuffer = ridingState(0, Side.CLIENT)
override fun copy() = BoatState().also {
it.deltaRotation.set(deltaRotation.get(), true)
it.rideVelocity.set(rideVelocity.get(), true)
it.stamina.set(stamina.get(), true)
it.jumpBuffer.set(jumpBuffer.get(), true)
it.staminaBuffer.set(staminaBuffer.get(), true)
}
override fun shouldSync(previous: RidingBehaviourState): Boolean {
if (previous !is BoatState) return false
return super.shouldSync(previous)
}
}
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.riding.behaviour.types
package com.cobblemon.mod.common.api.riding.behaviour.types.liquid
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.OrientationControllable
@@ -27,22 +27,22 @@ import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
class SwimDashBehaviour : RidingBehaviour<SwimDashSettings, SwimDashState> {
class BurstBehaviour : RidingBehaviour<BurstSettings, BurstState> {
companion object {
val KEY = cobblemonResource("swim/dash")
val KEY = cobblemonResource("liquid/burst")
const val DASH_TICKS: Int = 60
}
override val key = KEY
override fun getRidingStyle(settings: SwimDashSettings, state: SwimDashState): RidingStyle {
override fun getRidingStyle(settings: BurstSettings, state: BurstState): RidingStyle {
return RidingStyle.LIQUID
}
val poseProvider = PoseProvider<SwimDashSettings, SwimDashState>(PoseType.FLOAT)
val poseProvider = PoseProvider<BurstSettings, BurstState>(PoseType.FLOAT)
.with(PoseOption(PoseType.SWIM) { _, _, entity -> entity.isSwimming && entity.entityData.get(PokemonEntity.MOVING) })
override fun isActive(settings: SwimDashSettings, state: SwimDashState, vehicle: PokemonEntity): Boolean {
override fun isActive(settings: BurstSettings, state: BurstState, vehicle: PokemonEntity): Boolean {
//This could be kinda weird... what if the top of the mon is in a fluid but the bottom isnt?
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
if (vehicle.isInWater || vehicle.isUnderWater) {
@@ -53,13 +53,13 @@ class SwimDashBehaviour : RidingBehaviour<SwimDashSettings, SwimDashState> {
}
}
override fun pose(settings: SwimDashSettings, state: SwimDashState, vehicle: PokemonEntity): PoseType {
override fun pose(settings: BurstSettings, state: BurstState, vehicle: PokemonEntity): PoseType {
return poseProvider.select(settings, state, vehicle)
}
override fun speed(
settings: SwimDashSettings,
state: SwimDashState,
settings: BurstSettings,
state: BurstState,
vehicle: PokemonEntity,
driver: Player
): Float {
@@ -77,8 +77,8 @@ class SwimDashBehaviour : RidingBehaviour<SwimDashSettings, SwimDashState> {
}
override fun rotation(
settings: SwimDashSettings,
state: SwimDashState,
settings: BurstSettings,
state: BurstState,
vehicle: PokemonEntity,
driver: LivingEntity
): Vec2 {
@@ -86,8 +86,8 @@ class SwimDashBehaviour : RidingBehaviour<SwimDashSettings, SwimDashState> {
}
override fun velocity(
settings: SwimDashSettings,
state: SwimDashState,
settings: BurstSettings,
state: BurstState,
vehicle: PokemonEntity,
driver: Player,
input: Vec3
@@ -102,8 +102,8 @@ class SwimDashBehaviour : RidingBehaviour<SwimDashSettings, SwimDashState> {
}
override fun angRollVel(
settings: SwimDashSettings,
state: SwimDashState,
settings: BurstSettings,
state: BurstState,
vehicle: PokemonEntity,
driver: Player,
deltaTime: Double
@@ -112,8 +112,8 @@ class SwimDashBehaviour : RidingBehaviour<SwimDashSettings, SwimDashState> {
}
override fun rotationOnMouseXY(
settings: SwimDashSettings,
state: SwimDashState,
settings: BurstSettings,
state: BurstState,
vehicle: PokemonEntity,
driver: Player,
mouseY: Double,
@@ -132,8 +132,8 @@ class SwimDashBehaviour : RidingBehaviour<SwimDashSettings, SwimDashState> {
}
override fun canJump(
settings: SwimDashSettings,
state: SwimDashState,
settings: BurstSettings,
state: BurstState,
vehicle: PokemonEntity,
driver: Player
): Boolean {
@@ -141,8 +141,8 @@ class SwimDashBehaviour : RidingBehaviour<SwimDashSettings, SwimDashState> {
}
override fun setRideBar(
settings: SwimDashSettings,
state: SwimDashState,
settings: BurstSettings,
state: BurstState,
vehicle: PokemonEntity,
driver: Player
): Float {
@@ -150,8 +150,8 @@ class SwimDashBehaviour : RidingBehaviour<SwimDashSettings, SwimDashState> {
}
override fun jumpForce(
settings: SwimDashSettings,
state: SwimDashState,
settings: BurstSettings,
state: BurstState,
vehicle: PokemonEntity,
driver: Player,
jumpStrength: Int
@@ -160,8 +160,8 @@ class SwimDashBehaviour : RidingBehaviour<SwimDashSettings, SwimDashState> {
}
override fun gravity(
settings: SwimDashSettings,
state: SwimDashState,
settings: BurstSettings,
state: BurstState,
vehicle: PokemonEntity,
regularGravity: Double
): Double {
@@ -169,64 +169,64 @@ class SwimDashBehaviour : RidingBehaviour<SwimDashSettings, SwimDashState> {
}
override fun rideFovMultiplier(
settings: SwimDashSettings,
state: SwimDashState,
settings: BurstSettings,
state: BurstState,
vehicle: PokemonEntity,
driver: Player
): Float {
return 1.0f
}
override fun useAngVelSmoothing(settings: SwimDashSettings, state: SwimDashState, vehicle: PokemonEntity): Boolean {
override fun useAngVelSmoothing(settings: BurstSettings, state: BurstState, vehicle: PokemonEntity): Boolean {
return false
}
override fun useRidingAltPose(
settings: SwimDashSettings,
state: SwimDashState,
settings: BurstSettings,
state: BurstState,
vehicle: PokemonEntity,
driver: Player
): ResourceLocation {
return cobblemonResource("no_pose")
}
override fun inertia(settings: SwimDashSettings, state: SwimDashState, vehicle: PokemonEntity): Double {
override fun inertia(settings: BurstSettings, state: BurstState, vehicle: PokemonEntity): Double {
return 0.5
}
override fun shouldRoll(settings: SwimDashSettings, state: SwimDashState, vehicle: PokemonEntity): Boolean {
override fun shouldRoll(settings: BurstSettings, state: BurstState, vehicle: PokemonEntity): Boolean {
return false
}
override fun turnOffOnGround(settings: SwimDashSettings, state: SwimDashState, vehicle: PokemonEntity): Boolean {
override fun turnOffOnGround(settings: BurstSettings, state: BurstState, vehicle: PokemonEntity): Boolean {
return false
}
override fun dismountOnShift(settings: SwimDashSettings, state: SwimDashState, vehicle: PokemonEntity): Boolean {
override fun dismountOnShift(settings: BurstSettings, state: BurstState, vehicle: PokemonEntity): Boolean {
return false
}
override fun shouldRotatePokemonHead(
settings: SwimDashSettings,
state: SwimDashState,
settings: BurstSettings,
state: BurstState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun shouldRotatePlayerHead(
settings: SwimDashSettings,
state: SwimDashState,
settings: BurstSettings,
state: BurstState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun createDefaultState(settings: SwimDashSettings) = SwimDashState()
override fun createDefaultState(settings: BurstSettings) = BurstState()
}
class SwimDashSettings : RidingBehaviourSettings {
override val key = SwimDashBehaviour.KEY
class BurstSettings : RidingBehaviourSettings {
override val key = BurstBehaviour.KEY
var dashSpeed = 1F
private set
@@ -241,7 +241,7 @@ class SwimDashSettings : RidingBehaviourSettings {
}
}
class SwimDashState : RidingBehaviourState() {
class BurstState : RidingBehaviourState() {
var dashing = ridingState(false, Side.BOTH)
var ticks = ridingState(0, Side.BOTH)
@@ -252,10 +252,10 @@ class SwimDashState : RidingBehaviourState() {
}
override fun toString(): String {
return "SwimDashState(dashing=${dashing.get()}, ticks=${ticks.get()})"
return "BurstState(dashing=${dashing.get()}, ticks=${ticks.get()})"
}
override fun copy() = SwimDashState().also {
override fun copy() = BurstState().also {
it.rideVelocity.set(rideVelocity.get(), forced = true)
it.stamina.set(stamina.get(), forced = true)
it.dashing.set(dashing.get(), forced = true)
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.riding.behaviour.types
package com.cobblemon.mod.common.api.riding.behaviour.types.liquid
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Cobblemon
@@ -32,7 +32,7 @@ import kotlin.math.sin
class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
companion object {
val KEY = cobblemonResource("swim/dolphin")
val KEY = cobblemonResource("liquid/dolphin")
}
override val key = KEY
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.api.spawning.detail
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.npc.NPCClass
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
@@ -16,6 +17,7 @@ import com.cobblemon.mod.common.api.spawning.selection.SpawnSelectionData
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.util.asExpression
import com.google.gson.annotations.SerializedName
import kotlin.math.ceil
/**
* A [SpawnDetail] describing an [NPCEntity] spawn.
@@ -37,6 +39,16 @@ class NPCSpawnDetail : SpawnDetail() {
val minLevel: Expression = "1".asExpression()
val maxLevel: Expression = "100".asExpression()
override fun autoLabel() {
val npcClass = this.npcClass
struct.addFunction("class") { StringValue(npcClass.resourceIdentifier.toString()) }
labels.add(npcClass.resourceIdentifier.toString())
super.autoLabel()
}
override fun createSpawnAction(
spawnablePosition: SpawnablePosition,
bucket: SpawnBucket,
@@ -151,6 +151,11 @@ open class PlayerPartyStore(
removeList.add(it)
}
removeList.forEach { pokemon.evolutionProxy.server().remove(it) }
// Metabolism for Fullness
if (pokemon.currentFullness > 0) {
pokemon.tickMetabolism()
}
}
// Friendship
// ToDo expand this down the line just a very basic implementation for the first releases
@@ -44,7 +44,17 @@ class Search(
"legendary" -> PokemonFilter { pokemon -> pokemon.isLegendary() }
"mythical" -> PokemonFilter { pokemon -> pokemon.isMythical() }
"ultrabeast", "ultra_beast" -> PokemonFilter { pokemon -> pokemon.isUltraBeast() }
else -> PokemonFilter { pokemon -> PokemonProperties.parse(filter).matches(pokemon) }
else -> {
val nameFilter = filter.lowercase(Locale.ROOT)
PokemonFilter { pokemon ->
// search by partial name
if (pokemon.species.resourceIdentifier.path.contains(nameFilter)) {
return@PokemonFilter true
}
// search by exactly match in different props
return@PokemonFilter PokemonProperties.parse(filter).matches(pokemon)
}
}
}
if (inverted) {
@@ -28,6 +28,7 @@ object CobblemonItemTags {
@JvmField val APRICORN_POKE_BALLS = create("apricorn_poke_balls")
@JvmField val APRICORN_SPROUTS = create("apricorn_sprouts")
@JvmField val APRICORNS = create("apricorns")
@JvmField val APRIJUICES = create("aprijuices")
/** This tag is only used for a Torterra aspect based easter egg evolution at the moment.
* It simply includes the 'minecraft:azalea' and 'minecraft:flowering_azalea' items by default. */
@JvmField val AZALEA_TREE = create("azalea_tree")
@@ -51,6 +52,7 @@ object CobblemonItemTags {
@JvmField val FIRE_STONE_ORES = create("fire_stone_ores")
@JvmField val FOSSIL_MACHINE_PARTS = create("fossil_machine_parts")
@JvmField val FOSSILS = create("fossils")
@JvmField val FULL_HEAL_INGREDIENTS = create("full_heal_ingredients")
@JvmField val GILDED_CHESTS = create("gilded_chests")
@JvmField val HANGING_SIGNS = create("hanging_signs")
@JvmField val HERBS = create("herbs")
@@ -60,6 +62,7 @@ object CobblemonItemTags {
@JvmField val MINT_LEAF = create("mint_leaf")
@JvmField val MINT_SEEDS = create("mint_seeds")
@JvmField val MINTS = create("mints")
@JvmField val MOCHIS = create("mochis")
@JvmField val MOON_STONE_ORES = create("moon_stone_ores")
@JvmField val MUTATED_BERRIES = create("mutated_berries")
@JvmField val PLANTS = create("plants")
@@ -67,6 +70,7 @@ object CobblemonItemTags {
@JvmField val POKE_RODS = create("poke_rods")
@JvmField val POKEDEX = create("pokedex")
@JvmField val POKEDEX_SCREEN = create("pokedex_screen")
@JvmField val POKE_FOOD = create("poke_food")
@JvmField val POTIONS = create("potions")
@JvmField val PROTEIN_INGREDIENTS = create("protein_ingredients")
/** See [COOKED_MEAT] */
@@ -74,14 +78,17 @@ object CobblemonItemTags {
@JvmField val RED_TUMBLESTONE_BRICKS = create("red_tumblestone_bricks")
@JvmField val RED_TUMBLESTONES = create("red_tumblestones")
@JvmField val REMEDIES = create("remedies")
@JvmField val REMEDY_BERRIES = create("remedy_berries")
@JvmField val RESTORES = create("restores")
@JvmField val REVIVES = create("revives")
@JvmField val SANDWICH_VEGGIE = create("sandwich_veggies")
@JvmField val SEEDS = create("seeds")
@JvmField val SHINY_STONE_ORES = create("shiny_stone_ores")
@JvmField val SIGNS = create("signs")
@JvmField val SKY_TUMBLESTONE_BRICKS = create("sky_tumblestone_bricks")
@JvmField val SKY_TUMBLESTONES = create("sky_tumblestones")
@JvmField val SUN_STONE_ORES = create("sun_stone_ores")
@JvmField val SUPER_POTION_INGREDIENTS = create("super_potion_ingredients")
@JvmField val SWEETS = create("sweets")
@JvmField val TEACUPS = create("teacups")
@JvmField val TEAPOTS = create("teapots")
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.battles.BattleStartedPostEvent
import com.cobblemon.mod.common.api.events.battles.BattleStartedPreEvent
import com.cobblemon.mod.common.api.moves.HiddenPowerUtil
import com.cobblemon.mod.common.api.pokemon.helditem.HeldItemProvider
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.pokemon.status.Statuses
@@ -96,7 +97,7 @@ object BattleRegistry {
// Gender
packedTeamBuilder.append("${pk.gender.showdownName}|")
// IVs
val ivsInOrder = Stats.PERMANENT.map { pk.ivs.getOrDefault(it) }.joinToString(separator = ",")
val ivsInOrder = Stats.PERMANENT.map { pk.ivs.getEffectiveBattleIV(it) }.joinToString(separator = ",")
packedTeamBuilder.append("$ivsInOrder|")
// Shiny
packedTeamBuilder.append("${if (pk.shiny) "S" else ""}|")
@@ -111,7 +112,9 @@ object BattleRegistry {
val pokeball = pokemon.effectedPokemon.caughtBall.name.path.replace("_", "")
packedTeamBuilder.append("$pokeball,")
// Hidden Power Type
packedTeamBuilder.append(",")
// This is empty if the pokemon is not Hyper Trained, and the provided type corresponds to base IVs and not battle IVs.
val hiddenPowerType = if (pk.ivs.hyperTrainedIVs.any()) HiddenPowerUtil.getHiddenPowerType(pk).name.replaceFirstChar { it.uppercase() } else ""
packedTeamBuilder.append("$hiddenPowerType,")
// Gigantamax
packedTeamBuilder.append("${if (pk.gmaxFactor) "G" else ""},")
// DynamaxLevel
@@ -28,8 +28,10 @@ class SideEndInstruction(val message: BattleMessage): InterpreterInstruction {
val side = if (message.argumentAt(0)?.get(1) == '1') battle.side1 else battle.side2
val effect = message.effectAt(1) ?: return@dispatchWaiting
battle.sides.forEach {
val subject = if (it == side) battleLang("side_subject.ally") else battleLang("side_subject.opponent")
val lang = battleLang("sideend.${effect.id}", subject)
val isAllySide = it == side
val positionSpecificKey = if (isAllySide) "sideend.ally.${effect.id}" else "sideend.opponent.${effect.id}"
val lang = battleLang(positionSpecificKey)
it.broadcastChatMessage(lang)
}
@@ -29,8 +29,10 @@ class SideStartInstruction(val message: BattleMessage): InterpreterInstruction {
val side = if (message.argumentAt(0)?.get(1) == '1') battle.side1 else battle.side2
val effect = message.effectAt(1) ?: return@dispatchWaiting
battle.sides.forEach {
val subject = if (it == side) battleLang("side_subject.ally") else battleLang("side_subject.opponent")
val lang = battleLang("sidestart.${effect.id}", subject)
val isAllySide = it == side
val positionSpecificKey = if (isAllySide) "sidestart.ally.${effect.id}" else "sidestart.opponent.${effect.id}"
val lang = battleLang(positionSpecificKey)
it.broadcastChatMessage(lang)
}
@@ -10,18 +10,12 @@ package com.cobblemon.mod.common.block
import com.mojang.serialization.MapCodec
import net.minecraft.core.BlockPos
import net.minecraft.world.InteractionHand
import net.minecraft.world.InteractionResult
import net.minecraft.world.entity.EquipmentSlot
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.phys.BlockHitResult
import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.Shapes
import net.minecraft.world.phys.shapes.VoxelShape
@@ -30,24 +24,6 @@ import net.minecraft.world.phys.shapes.VoxelShape
class BigRootBlock(settings: Properties) : RootBlock(settings) {
override fun getShape(state: BlockState, world: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape = AABB
override fun useWithoutItem(
state: BlockState,
world: Level,
pos: BlockPos,
player: Player,
blockHitResult: BlockHitResult
): InteractionResult {
val stack = player.getItemInHand(InteractionHand.MAIN_HAND)
if (stack.`is`(Items.SHEARS)) {
this.attemptShear(world, state, pos) {
player.onEquippedItemBroken(stack.item, EquipmentSlot.MAINHAND)
stack.hurtAndBreak(1, player, EquipmentSlot.MAINHAND)
}
return InteractionResult.sidedSuccess(world.isClientSide)
}
return super.useWithoutItem(state, world, pos, player, blockHitResult)
}
override fun shearedResultingState(): BlockState = Blocks.HANGING_ROOTS.defaultBlockState()
override fun shearedDrop(): ItemStack = Items.STRING.defaultInstance
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.block.entity.CakeBlockEntity
import com.cobblemon.mod.common.item.components.FoodColourComponent
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.DyeColor
@@ -20,33 +21,53 @@ import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.Level
import net.minecraft.world.level.LevelReader
import net.minecraft.world.level.block.BaseEntityBlock
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.block.Rotation
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.DirectionProperty
import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.VoxelShape
abstract class CakeBlock(settings: Properties): BaseEntityBlock(settings) {
companion object {
val FACING: DirectionProperty = DirectionProperty.create("facing", Direction.Plane.HORIZONTAL)
val SHAPE_BY_BITE = arrayOf(
box(1.0, 0.0, 1.0, 15.0, 9.0, 15.0),
box(3.0, 0.0, 1.0, 15.0, 9.0, 15.0),
box(5.0, 0.0, 1.0, 15.0, 9.0, 15.0),
box(7.0, 0.0, 1.0, 15.0, 9.0, 15.0),
box(9.0, 0.0, 1.0, 15.0, 9.0, 15.0),
box(11.0, 0.0, 1.0, 15.0, 9.0, 15.0),
box(13.0, 0.0, 1.0, 15.0, 9.0, 15.0)
box(1.0, 0.0, 1.0, 15.0, 9.0, 15.0),
box(3.0, 0.0, 1.0, 15.0, 9.0, 15.0),
box(5.0, 0.0, 1.0, 15.0, 9.0, 15.0),
box(7.0, 0.0, 1.0, 15.0, 9.0, 15.0),
box(9.0, 0.0, 1.0, 15.0, 9.0, 15.0),
box(11.0, 0.0, 1.0, 15.0, 9.0, 15.0),
box(13.0, 0.0, 1.0, 15.0, 9.0, 15.0)
)
enum class Rotation {
NONE,
CLOCKWISE_90,
CLOCKWISE_180,
COUNTERCLOCKWISE_90
}
}
override fun setPlacedBy(
level: Level,
pos: BlockPos,
state: BlockState,
placer: LivingEntity?,
stack: ItemStack
) {
init {
registerDefaultState(stateDefinition.any().setValue(FACING, Direction.NORTH))
}
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
super.createBlockStateDefinition(builder)
builder.add(FACING)
}
override fun setPlacedBy(level: Level, pos: BlockPos, state: BlockState, placer: LivingEntity?, stack: ItemStack) {
super.setPlacedBy(level, pos, state, placer, stack)
if (placer != null) {
level.setBlock(pos, state.setValue(FACING, placer.direction.opposite), 2)
}
val blockEntity = level.getBlockEntity(pos) as? CakeBlockEntity
blockEntity?.initializeFromItemStack(stack)
}
@@ -57,19 +78,60 @@ abstract class CakeBlock(settings: Properties): BaseEntityBlock(settings) {
}
override fun getShape(
state: BlockState,
level: BlockGetter,
pos: BlockPos,
context: CollisionContext
): VoxelShape? {
return SHAPE_BY_BITE[getBites(level, pos)]
state: BlockState,
level: BlockGetter,
pos: BlockPos,
context: CollisionContext
): VoxelShape {
val bites = getBites(level, pos)
val blockEntity = level.getBlockEntity(pos) as? CakeBlockEntity
val maxBites = blockEntity?.maxBites ?: 6
val visualStage = ((bites * SHAPE_BY_BITE.size) / (maxBites + 1))
.coerceIn(0, SHAPE_BY_BITE.lastIndex)
val baseShape = SHAPE_BY_BITE[visualStage]
return when (state.getValue(FACING)) {
Direction.NORTH -> baseShape
Direction.SOUTH -> rotateShape(baseShape, Rotation.CLOCKWISE_180)
Direction.WEST -> rotateShape(baseShape, Rotation.COUNTERCLOCKWISE_90)
Direction.EAST -> rotateShape(baseShape, Rotation.CLOCKWISE_90)
else -> baseShape
}
}
private fun rotateShape(shape: VoxelShape, rotation: Rotation): VoxelShape {
var newShape = net.minecraft.world.phys.shapes.Shapes.empty()
for (box in shape.toAabbs()) {
val minX = box.minX
val minY = box.minY
val minZ = box.minZ
val maxX = box.maxX
val maxY = box.maxY
val maxZ = box.maxZ
val (rMinX, rMinZ, rMaxX, rMaxZ) = when (rotation) {
Rotation.NONE -> arrayOf(minX, minZ, maxX, maxZ)
Rotation.CLOCKWISE_90 -> arrayOf(minZ, 1.0 - maxX, maxZ, 1.0 - minX)
Rotation.CLOCKWISE_180 -> arrayOf(1.0 - maxX, 1.0 - maxZ, 1.0 - minX, 1.0 - minZ)
Rotation.COUNTERCLOCKWISE_90 -> arrayOf(1.0 - maxZ, minX, 1.0 - minZ, maxX)
}
val rotatedVoxel = net.minecraft.world.phys.shapes.Shapes.box(rMinX, minY, rMinZ, rMaxX, maxY, rMaxZ)
newShape = net.minecraft.world.phys.shapes.Shapes.or(newShape, rotatedVoxel)
}
return newShape
}
override fun spawnDestroyParticles(
level: Level,
player: Player,
pos: BlockPos,
state: BlockState
level: Level,
player: Player,
pos: BlockPos,
state: BlockState
) {
val blockEntity = level.getBlockEntity(pos) as? CakeBlockEntity
val color = blockEntity?.foodColourComponent?.colours?.lastOrNull()
@@ -100,5 +162,7 @@ abstract class CakeBlock(settings: Properties): BaseEntityBlock(settings) {
}
fun getBites(level: BlockGetter, pos: BlockPos): Int = (level.getBlockEntity(pos) as? CakeBlockEntity)?.bites ?: 0
fun setBites(level: BlockGetter, pos: BlockPos, bites: Int) = (level.getBlockEntity(pos) as? CakeBlockEntity)?.bites = bites
fun setBites(level: BlockGetter, pos: BlockPos, bites: Int) {
(level.getBlockEntity(pos) as? CakeBlockEntity)?.bites = bites
}
}
@@ -86,15 +86,14 @@ class CandlePokeCakeBlock(settings: Properties) : CakeBlock(settings) {
)
}
override fun codec(): MapCodec<out BaseEntityBlock?>? {
override fun codec(): MapCodec<out BaseEntityBlock?> {
return simpleCodec(::CandlePokeCakeBlock)
}
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity? {
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity {
return CandlePokeCakeBlockEntity(pos, state)
}
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block?, BlockState?>) {
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
super.createBlockStateDefinition(builder)
builder.add(LIT, CANDLE_COLOR)
}
@@ -107,7 +106,7 @@ class CandlePokeCakeBlock(settings: Properties) : CakeBlock(settings) {
player: Player,
hand: InteractionHand,
hitResult: BlockHitResult
): ItemInteractionResult? {
): ItemInteractionResult {
return if (!stack.`is`(Items.FLINT_AND_STEEL) && !stack.`is`(Items.FIRE_CHARGE)) {
if (candleHit(hitResult) && stack.isEmpty && state.getValue(LIT) == true) {
extinguish(player, state, level, pos)
@@ -134,7 +133,7 @@ class CandlePokeCakeBlock(settings: Properties) : CakeBlock(settings) {
pos: BlockPos,
player: Player,
hitResult: BlockHitResult
): InteractionResult? {
): InteractionResult {
val oldCookingComponent = getFoodColourComponent(level, pos)
val candle = getCandleByMapColorId(state.getValue(CANDLE_COLOR))
@@ -175,5 +174,5 @@ class CandlePokeCakeBlock(settings: Properties) : CakeBlock(settings) {
level: BlockGetter,
pos: BlockPos,
context: CollisionContext
): VoxelShape? = SHAPE
): VoxelShape = SHAPE
}
@@ -0,0 +1,104 @@
/*
* 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
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.Level
import net.minecraft.world.level.LevelAccessor
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.block.RotatedPillarBlock
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BlockStateProperties
import net.minecraft.world.level.block.state.properties.EnumProperty
import net.minecraft.world.level.pathfinder.PathComputationType
import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.Shapes
import net.minecraft.world.phys.shapes.VoxelShape
class HeartyGrainBaleBlock(properties: Properties) : RotatedPillarBlock(properties) {
private var owner: LivingEntity? = null
init {
this.registerDefaultState(
this.stateDefinition.any()
.setValue(AXIS, Direction.Axis.Y)
)
}
override fun setPlacedBy(
level: Level,
pos: BlockPos,
state: BlockState,
placer: LivingEntity?,
itemStack: ItemStack
) {
owner = placer
super.setPlacedBy(level, pos, state, placer, itemStack)
}
override fun getShape(
state: BlockState,
level: BlockGetter,
pos: BlockPos,
context: CollisionContext
): VoxelShape {
return SHAPE
}
override fun getCollisionShape(
state: BlockState,
level: BlockGetter,
pos: BlockPos,
context: CollisionContext
): VoxelShape {
return super.getCollisionShape(state, level, pos, context)
}
override fun updateShape(
state: BlockState,
direction: Direction,
neighborState: BlockState,
level: LevelAccessor,
pos: BlockPos,
neighborPos: BlockPos
): BlockState {
return if (!state.canSurvive(level, pos)) {
Blocks.AIR.defaultBlockState()
} else {
super.updateShape(state, direction, neighborState, level, pos, neighborPos)
}
}
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
builder.add(AXIS)
}
override fun isPathfindable(
state: BlockState,
type: PathComputationType
): Boolean = false
companion object {
val AXIS: EnumProperty<Direction.Axis> = BlockStateProperties.AXIS
private val SHAPE: VoxelShape = Shapes.block()
}
override fun fallOn(level: Level, state: BlockState, pos: BlockPos, entity: Entity, fallDistance: Float) {
entity.causeFallDamage(fallDistance, 0.3f, level.damageSources().fall())
}
}
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.block
import com.cobblemon.mod.common.CobblemonBlocks
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
import com.mojang.serialization.MapCodec
@@ -16,9 +17,7 @@ import net.minecraft.core.Direction
import net.minecraft.server.level.ServerLevel
import net.minecraft.tags.FluidTags
import net.minecraft.util.RandomSource
import net.minecraft.util.StringRepresentable
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.ItemLike
import net.minecraft.world.level.Level
@@ -31,6 +30,7 @@ import net.minecraft.world.level.block.SimpleWaterloggedBlock
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BlockStateProperties
import net.minecraft.world.level.block.state.properties.DoubleBlockHalf
import net.minecraft.world.level.block.state.properties.EnumProperty
import net.minecraft.world.level.block.state.properties.IntegerProperty
import net.minecraft.world.level.material.FluidState
@@ -45,13 +45,13 @@ class HeartyGrainsBlock(settings: Properties) : CropBlock(settings), SimpleWater
override fun getMaxAge(): Int = MATURE_AGE
private val ageAfterHarvest = 3
private val ageAfterHarvest = 4
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
builder.add(AGE, PART, WATERLOGGED)
builder.add(AGE, HALF, WATERLOGGED)
}
override fun getBaseSeedId(): ItemLike = CobblemonItems.Hearty_Grains
override fun getBaseSeedId(): ItemLike = CobblemonItems.HEARTY_GRAINS
override fun getShape(state: BlockState, world: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
return AGE_TO_SHAPE[this.getAge(state)]
@@ -62,45 +62,39 @@ class HeartyGrainsBlock(settings: Properties) : CropBlock(settings), SimpleWater
}
override fun randomTick(state: BlockState, world: ServerLevel, pos: BlockPos, random: RandomSource) {
if (state.getValue(PART) == Part.UPPER || this.isMaxAge(state) || !canGrow(world, pos)) return
if (state.getValue(HALF) == DoubleBlockHalf.UPPER || !canGrow(world, pos, state)) return
val lightLevel = world.getMaxLocalRawBrightness(pos)
if (lightLevel >= 9 && random.nextInt(7) == 0) {
val newAge = state.getValue(AGE) + 1
world.setBlock(pos, state.setValue(AGE, newAge), UPDATE_CLIENTS)
if (newAge == MATURE_AGE) {
val abovePos = pos.above()
if (world.getBlockState(abovePos).isAir) {
world.setBlock(
abovePos,
this.defaultBlockState().setValue(AGE, newAge).setValue(PART, Part.UPPER),
UPDATE_CLIENTS
)
}
}
if (hasSufficientLight(world, pos) && random.nextInt(7) == 0) {
grow(world, pos, state, 1)
}
}
private fun canGrow(world: LevelReader, pos: BlockPos): Boolean {
return canSurvive(defaultBlockState().setValue(PART, Part.LOWER), world, pos)
private fun canGrow(world: LevelReader, pos: BlockPos, state: BlockState): Boolean {
return !this.isMaxAge(state) && canSurvive(state, world, pos) && (state.getValue(HALF) == DoubleBlockHalf.UPPER || canGrowInto(world, pos.above()))
}
private fun canGrowInto(world: LevelReader, pos: BlockPos): Boolean {
val blockState = world.getBlockState(pos)
return blockState.isAir || blockState.`is`(CobblemonBlocks.HEARTY_GRAINS)
}
override fun playerWillDestroy(world: Level, pos: BlockPos, state: BlockState, player: Player): BlockState {
if (state.getValue(PART) == Part.UPPER && state.block is HeartyGrainsBlock) {
val belowPos = pos.below()
val belowState = world.getBlockState(belowPos)
world.destroyBlock(pos, true) // Break the lower block without drops
world.setBlock(
belowPos,
belowState.setValue(AGE, ageAfterHarvest), // Reset age to simulate regrowth preparation
UPDATE_CLIENTS
)
} else if (state.getValue(PART) == Part.LOWER) {
val abovePos = pos.above()
val aboveState = world.getBlockState(abovePos)
if (aboveState.block is HeartyGrainsBlock && aboveState.getValue(PART) == Part.UPPER) {
world.destroyBlock(abovePos, true) // Break the upper block without drops
if (!world.isClientSide) {
if (state.getValue(HALF) == DoubleBlockHalf.UPPER) {
val belowPos = pos.below()
val belowState = world.getBlockState(belowPos)
world.destroyBlock(pos, true) // Break the lower block without drops
world.setBlock(
belowPos,
belowState.setValue(AGE, ageAfterHarvest), // Reset age to simulate regrowth preparation
UPDATE_CLIENTS
)
} else if (state.getValue(HALF) == DoubleBlockHalf.LOWER) {
val abovePos = pos.above()
val aboveState = world.getBlockState(abovePos)
if (aboveState.block is HeartyGrainsBlock && aboveState.getValue(HALF) == DoubleBlockHalf.UPPER) {
world.destroyBlock(abovePos, true) // Break the upper block without drops
}
}
}
return super.playerWillDestroy(world, pos, state, player)
@@ -127,22 +121,42 @@ class HeartyGrainsBlock(settings: Properties) : CropBlock(settings), SimpleWater
)
}
override fun isValidBonemealTarget(world: LevelReader, pos: BlockPos, state: BlockState): Boolean {
return !this.isMaxAge(state) && state.getValue(PART) == Part.LOWER
override fun isValidBonemealTarget(level: LevelReader, pos: BlockPos, state: BlockState): Boolean {
val posAndState = getLowerHalf(level, pos, state)
return if (posAndState == null) false else this.canGrow(
level,
posAndState.first,
posAndState.second
)
}
override fun growCrops(world: Level, pos: BlockPos, state: BlockState) {
if (!canGrow(world, pos)) return
override fun performBonemeal(level: ServerLevel, random: RandomSource, pos: BlockPos, state: BlockState) {
val posAndState = getLowerHalf(level, pos, state)
if (posAndState != null) {
grow(level, posAndState.first, posAndState.second, getBonemealAgeIncrease(level))
}
}
val newAge = (this.getAge(state) + this.getBonemealAgeIncrease(world)).coerceAtMost(this.maxAge)
fun grow(world: Level, pos: BlockPos, state: BlockState, increment: Int) {
if (!canGrow(world, pos, state)) return
val newAge = (this.getAge(state) + increment).coerceAtMost(this.maxAge)
world.setBlock(pos, state.setValue(this.ageProperty, newAge), UPDATE_CLIENTS)
if (newAge == MATURE_AGE && state.getValue(PART) == Part.LOWER) {
if (newAge >= 5) {
val abovePos = pos.above()
if (world.getBlockState(abovePos).isAir) {
val stateAbove = world.getBlockState(abovePos)
if (stateAbove.isAir) {
world.setBlock(
abovePos,
this.defaultBlockState().setValue(AGE, newAge).setValue(PART, Part.UPPER),
this.defaultBlockState().setValue(AGE, newAge).setValue(HALF, DoubleBlockHalf.UPPER),
UPDATE_CLIENTS
)
}
else if (stateAbove.getValue(HALF) == DoubleBlockHalf.UPPER) {
world.setBlock(
abovePos,
stateAbove.setValue(AGE, newAge),
UPDATE_CLIENTS
)
}
@@ -152,8 +166,8 @@ class HeartyGrainsBlock(settings: Properties) : CropBlock(settings), SimpleWater
override fun getBonemealAgeIncrease(world: Level): Int = 1
override fun canSurvive(state: BlockState, world: LevelReader, pos: BlockPos): Boolean {
return when (state.getValue(PART)) {
Part.LOWER -> {
return when (state.getValue(HALF)) {
DoubleBlockHalf.LOWER -> {
val floor = world.getBlockState(pos.below())
val fluidState = world.getFluidState(pos)
@@ -170,45 +184,57 @@ class HeartyGrainsBlock(settings: Properties) : CropBlock(settings), SimpleWater
// Check for valid soil or survival conditions
isSubmergedInWater || isWaterlogged || isNearWater || floor.`is`(CobblemonBlockTags.HEARTY_GRAINS_PLANTABLE) || floor.`is`(Blocks.MUD) || floor.`is`(Blocks.FARMLAND)
}
Part.UPPER -> {
val below = world.getBlockState(pos.below())
below.block is HeartyGrainsBlock && below.getValue(PART) == Part.LOWER
DoubleBlockHalf.UPPER -> {
getLowerHalf(world, pos, state) != null
// Upper part survives only if the lower part is valid
}
}
}
private fun getLowerHalf(level: LevelReader, pos: BlockPos, state: BlockState): Pair<BlockPos, BlockState>? {
if (isLower(state)) {
return Pair(pos, state)
} else {
val blockPos = pos.below()
val blockState = level.getBlockState(blockPos)
return if (isLower(blockState)) Pair(
blockPos,
blockState
) else null
}
}
private fun isLower(state: BlockState): Boolean =
state.`is`(CobblemonBlocks.HEARTY_GRAINS) && state.getValue(HALF) == DoubleBlockHalf.LOWER
override fun codec(): MapCodec<out CropBlock> = CODEC
companion object {
val CODEC = simpleCodec(::HeartyGrainsBlock)
const val MATURE_AGE = 4
const val MATURE_AGE = 7
val WATERLOGGED = BlockStateProperties.WATERLOGGED
val AGE: IntegerProperty = BlockStateProperties.AGE_4
val PART: EnumProperty<Part> = EnumProperty.create("part", Part::class.java)
val AGE: IntegerProperty = BlockStateProperties.AGE_7
val HALF: EnumProperty<DoubleBlockHalf> = BlockStateProperties.DOUBLE_BLOCK_HALF
val AGE_TO_SHAPE = arrayOf(
box(2.0, 0.0, 0.0, 16.0, 17.0, 16.0), // Stage 1
box(2.0, 0.0, 0.0, 16.0, 17.0, 16.0), // Stage 2
box(2.0, 0.0, 0.0, 16.0, 17.0, 16.0), // Stage 3
box(2.0, 0.0, 0.0, 16.0, 17.0, 16.0), // Stage 4 (Bottom)
box(2.0, 0.0, 0.0, 16.0, 17.0, 16.0) // Stage 5 (Bottom)
box(0.0, 0.0, 0.0, 16.0, 3.0, 16.0), // Stage 0
box(0.0, 0.0, 0.0, 16.0, 5.0, 16.0), // Stage 1
box(0.0, 0.0, 0.0, 16.0, 8.0, 16.0), // Stage 2
box(0.0, 0.0, 0.0, 16.0, 10.0, 16.0), // Stage 3
box(0.0, 0.0, 0.0, 16.0, 16.0, 16.0), // Stage 4
box(0.0, 0.0, 0.0, 16.0, 16.0, 16.0), // Stage 5
box(0.0, 0.0, 0.0, 16.0, 16.0, 16.0), // Stage 6
box(0.0, 0.0, 0.0, 16.0, 16.0, 16.0) // Stage 7
)
}
enum class Part : StringRepresentable {
LOWER, UPPER;
override fun getSerializedName(): String = name.lowercase()
}
init {
this.registerDefaultState(
this.stateDefinition.any()
.setValue(AGE, 0)
.setValue(PART, Part.LOWER)
.setValue(HALF, DoubleBlockHalf.LOWER)
.setValue(WATERLOGGED, false)
)
}
@@ -35,7 +35,7 @@ import net.minecraft.world.level.gameevent.GameEvent
import net.minecraft.world.phys.BlockHitResult
class PokeCakeBlock(settings: Properties): CakeBlock(settings) {
override fun codec(): MapCodec<out BaseEntityBlock?>? {
override fun codec(): MapCodec<out BaseEntityBlock?> {
return simpleCodec(::PokeCakeBlock)
}
@@ -62,7 +62,7 @@ class PokeCakeBlock(settings: Properties): CakeBlock(settings) {
pos: BlockPos,
player: Player,
hitResult: BlockHitResult
): InteractionResult? {
): InteractionResult {
if (level.isClientSide) {
if (eat(level, pos, player).consumesAction()) {
return InteractionResult.SUCCESS
@@ -84,7 +84,7 @@ class PokeCakeBlock(settings: Properties): CakeBlock(settings) {
player: Player,
hand: InteractionHand,
hitResult: BlockHitResult
): ItemInteractionResult? {
): ItemInteractionResult {
val item = stack.item
val block = state.block as? PokeCakeBlock
@@ -19,7 +19,12 @@ import net.minecraft.server.level.ServerLevel
import net.minecraft.sounds.SoundEvents
import net.minecraft.sounds.SoundSource
import net.minecraft.util.RandomSource
import net.minecraft.world.InteractionHand
import net.minecraft.world.InteractionResult
import net.minecraft.world.entity.EquipmentSlot
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.level.Level
import net.minecraft.world.level.LevelAccessor
import net.minecraft.world.level.LevelReader
@@ -29,6 +34,7 @@ import net.minecraft.world.level.block.BonemealableBlock
import net.minecraft.world.level.block.RenderShape
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.gameevent.GameEvent
import net.minecraft.world.phys.BlockHitResult
@Suppress("OVERRIDE_DEPRECATION", "UNUSED_PARAMETER", "MemberVisibilityCanBePrivate", "DEPRECATION")
abstract class RootBlock(settings: Properties) : Block(settings), BonemealableBlock, ShearableBlock {
@@ -42,6 +48,23 @@ abstract class RootBlock(settings: Properties) : Block(settings), BonemealableBl
}
}
override fun useWithoutItem(
state: BlockState,
world: Level,
pos: BlockPos,
player: Player,
blockHitResult: BlockHitResult
): InteractionResult {
val stack = player.getItemInHand(InteractionHand.MAIN_HAND)
if (stack.`is`(Items.SHEARS)) {
this.attemptShear(world, state, pos) {
stack.hurtAndBreak(1, player, EquipmentSlot.MAINHAND)
}
return InteractionResult.sidedSuccess(world.isClientSide)
}
return super.useWithoutItem(state, world, pos, player, blockHitResult)
}
fun hasReachedSpreadCap(world: Level, pos: BlockPos): Boolean {
var nearby = 0
val nearbyPositions = BlockPos.betweenClosed(pos.offset(-4, -1, -4), pos.offset(4, 1, 4)).iterator()
@@ -67,7 +90,8 @@ abstract class RootBlock(settings: Properties) : Block(settings), BonemealableBl
pos: BlockPos,
neighborPos: BlockPos
): BlockState {
return if (direction == Direction.UP && !this.canSurvive(state, world, pos)) Blocks.AIR.defaultBlockState() else super.updateShape(state, direction, neighborState, world, pos, neighborPos)
return if (direction == Direction.UP && !this.canSurvive(state, world, pos)) Blocks.AIR.defaultBlockState()
else super.updateShape(state, direction, neighborState, world, pos, neighborPos)
}
override fun isValidBonemealTarget(world: LevelReader, pos: BlockPos, state: BlockState) = this.canSpread(world, pos, state)
@@ -117,6 +141,7 @@ abstract class RootBlock(settings: Properties) : Block(settings), BonemealableBl
popResource(world, pos, shearedDrop)
}
world.gameEvent(null, GameEvent.SHEAR, pos)
successCallback.invoke()
return true
}
@@ -150,12 +175,12 @@ abstract class RootBlock(settings: Properties) : Block(settings), BonemealableBl
/**
* Picks the [BlockState] that results from a spread of this block.
*
* @param random The [Random] instance used during a spread attempt.
* @param random The [RandomSource] instance used during a spread attempt.
* @return The [BlockState] that will represent the spread root.
*/
protected fun spreadingRoot(random: RandomSource): BlockState =
if (random.nextFloat() < Cobblemon.config.energyRootChance) CobblemonBlocks.ENERGY_ROOT.defaultBlockState()
else this.defaultBlockState()
else CobblemonBlocks.BIG_ROOT.defaultBlockState()
/**
* Checks if the given coordinates allow for a root to be placed with some context.
@@ -169,7 +194,7 @@ abstract class RootBlock(settings: Properties) : Block(settings), BonemealableBl
protected fun canGoOn(state: BlockState, world: LevelReader, pos: BlockPos, ceilingValidator: (ceiling: BlockState) -> Boolean): Boolean {
val up = pos.above()
val upState = world.getBlockState(up)
return upState.isFaceSturdy(world, up, Direction.DOWN) && ceilingValidator(upState) // todo (techdaan): ensure this is the right mapping
return upState.isFaceSturdy(world, up, Direction.DOWN) && ceilingValidator(upState)
}
/**
@@ -0,0 +1,65 @@
/*
* 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
import com.mojang.serialization.MapCodec
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.world.item.context.BlockPlaceContext
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.Rotation
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BlockStateProperties.HORIZONTAL_FACING
import net.minecraft.world.level.pathfinder.PathComputationType
import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.Shapes
import net.minecraft.world.phys.shapes.VoxelShape
import net.minecraft.world.level.block.CarpetBlock
class TatamiBlock(settings: Properties) : Block(settings) {
init {
registerDefaultState(stateDefinition.any()
.setValue(HORIZONTAL_FACING, Direction.NORTH))
}
override fun getStateForPlacement(blockPlaceContext: BlockPlaceContext): BlockState {
return defaultBlockState().setValue(HORIZONTAL_FACING, blockPlaceContext.horizontalDirection)
}
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
builder.add(HORIZONTAL_FACING)
}
override fun getShape(
state: BlockState,
blockGetter: BlockGetter,
pos: BlockPos,
collisionContext: CollisionContext
): VoxelShape {
return HITBOX
}
@Deprecated("Deprecated in Java")
override fun rotate(state: BlockState, rotation: Rotation): BlockState {
return state.setValue(HORIZONTAL_FACING, rotation.rotate(state.getValue(HORIZONTAL_FACING)))
}
@Deprecated("Deprecated in Java")
override fun isPathfindable(state: BlockState, type: PathComputationType): Boolean {
return false
}
companion object {
val CODEC = simpleCodec(::TatamiBlock)
val HITBOX = Shapes.box(0.0, 0.0, 0.0, 1.0, 1.0, 1.0)
}
}
@@ -0,0 +1,65 @@
/*
* 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
import com.mojang.serialization.MapCodec
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.world.item.context.BlockPlaceContext
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.Rotation
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BlockStateProperties.HORIZONTAL_FACING
import net.minecraft.world.level.pathfinder.PathComputationType
import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.Shapes
import net.minecraft.world.phys.shapes.VoxelShape
import net.minecraft.world.level.block.CarpetBlock
class TatamiMatBlock(settings: Properties) : CarpetBlock(settings) {
init {
registerDefaultState(stateDefinition.any()
.setValue(HORIZONTAL_FACING, Direction.NORTH))
}
override fun getStateForPlacement(blockPlaceContext: BlockPlaceContext): BlockState {
return defaultBlockState().setValue(HORIZONTAL_FACING, blockPlaceContext.horizontalDirection)
}
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
builder.add(HORIZONTAL_FACING)
}
override fun getShape(
state: BlockState,
blockGetter: BlockGetter,
pos: BlockPos,
collisionContext: CollisionContext
): VoxelShape {
return HITBOX
}
@Deprecated("Deprecated in Java")
override fun rotate(state: BlockState, rotation: Rotation): BlockState {
return state.setValue(HORIZONTAL_FACING, rotation.rotate(state.getValue(HORIZONTAL_FACING)))
}
@Deprecated("Deprecated in Java")
override fun isPathfindable(state: BlockState, type: PathComputationType): Boolean {
return false
}
companion object {
val CODEC = simpleCodec(::TatamiMatBlock)
val HITBOX = Shapes.box(0.0, 0.0, 0.0, 1.0, 0.0625, 1.0)
}
}
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.CobblemonMenuType
import com.cobblemon.mod.common.CobblemonRecipeTypes
import com.cobblemon.mod.common.api.cooking.Seasonings
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity
import com.cobblemon.mod.common.client.tooltips.itemTakesSeasoningData
import com.cobblemon.mod.common.item.crafting.CookingPotRecipe
import com.cobblemon.mod.common.item.crafting.CookingPotRecipeBase
import net.minecraft.recipebook.ServerPlaceRecipe
@@ -109,11 +110,15 @@ class CookingPotMenu : RecipeBookMenu<CraftingInput, CookingPotRecipeBase>, Cont
}
override fun handlePlacement(placeAll: Boolean, recipe: RecipeHolder<*>, player: ServerPlayer) {
// Check if the recipe value implements CookingPotRecipeBase
val recipeValue = recipe.value()
if (recipeValue is CookingPotRecipeBase) {
@Suppress("UNCHECKED_CAST")
val castedRecipe = recipe as RecipeHolder<CookingPotRecipeBase>
// Save seasoning contents in case the next recipe absorbs seasoning data
val seasoningSlots = CampfireBlockEntity.SEASONING_SLOTS
val preservedSeasonings = seasoningSlots.map { container.getItem(it).copy() }
this.beginPlacingRecipe()
try {
val serverPlaceRecipe = ServerPlaceRecipe(this)
@@ -121,6 +126,15 @@ class CookingPotMenu : RecipeBookMenu<CraftingInput, CookingPotRecipeBase>, Cont
} finally {
this.finishPlacingRecipe(castedRecipe)
}
// Check if new result item supports seasoning
val result = castedRecipe.value().result
if (itemTakesSeasoningData(result)) {
seasoningSlots.forEachIndexed { index, slot ->
container.setItem(slot, preservedSeasonings[index])
}
}
} else {
throw IllegalArgumentException("Unsupported recipe type: ${recipeValue::class.java.name}")
}
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.block.campfirepot
import com.cobblemon.mod.common.CobblemonItemComponents
import com.cobblemon.mod.common.CobblemonSounds
import net.minecraft.sounds.SoundSource
import net.minecraft.world.entity.player.Player
@@ -23,8 +24,9 @@ class CookingPotResultSlot(
) : Slot(container, index, x, y) {
override fun onTake(player: Player, stack: ItemStack) {
val menu = player.containerMenu
stack.set(CobblemonItemComponents.CRAFTED, true)
val menu = player.containerMenu
if (menu is CookingPotMenu) {
menu.broadcastChanges()
player.playNotifySound(CobblemonSounds.CAMPFIRE_POT_USE, SoundSource.MASTER, 1.0f, 1.0f)
@@ -115,7 +115,7 @@ open class CakeBlockEntity(
return ClientboundBlockEntityDataPacket.create(this)
}
override fun getUpdateTag(registries: HolderLookup.Provider): CompoundTag? {
override fun getUpdateTag(registries: HolderLookup.Provider): CompoundTag {
val tag = CompoundTag()
saveAdditional(tag, registries)
return tag
@@ -16,6 +16,7 @@ import com.cobblemon.mod.common.advancement.CobblemonCriteria
import com.cobblemon.mod.common.api.pasture.PastureLinkManager
import com.cobblemon.mod.common.api.scheduling.afterOnServer
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.api.text.textClickHandlers
import com.cobblemon.mod.common.block.PastureBlock
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
@@ -96,6 +97,15 @@ class PokemonPastureBlockEntity(pos: BlockPos, state: BlockState) :
if (blockEntity.ticksUntilCheck <= 0) {
blockEntity.checkPokemon()
}
// for every tethered pokemon tick metabolism if fullness is above 0
val tetheredPokemonList = blockEntity.tetheredPokemon.filter { it.getPokemon() != null }
for (tetheredPokemon in tetheredPokemonList) {
val pokemon = tetheredPokemon.getPokemon()!!
if (pokemon.currentFullness > 0) {
pokemon.tickMetabolism()
}
}
blockEntity.togglePastureOn(blockEntity.getInRangeViewerCount(world, blockEntity.blockPos) > 0)
}
}
@@ -118,17 +118,17 @@ class RestorationTankBlockEntity(
}
override fun getSlotsForFace(side: Direction): IntArray {
return if (side == Direction.DOWN) intArrayOf(0, 1, 2, 3, 4, 5, 6, 7) else intArrayOf()
return intArrayOf(0, 1, 2, 3, 4, 5, 6, 7)
}
override fun canPlaceItemThroughFace(slot: Int, stack: ItemStack, direction: Direction?): Boolean {
if (tankEntity.multiblockStructure is FossilMultiblockStructure) {
if (direction != Direction.DOWN) {
val structure = tankEntity.multiblockStructure as FossilMultiblockStructure
val canUtilize = stack?.let { NaturalMaterials.isNaturalMaterial(it) } == true
val canUtilize = stack.let { NaturalMaterials.isNaturalMaterial(it) }
&& structure.organicMaterialInside < FossilMultiblockStructure.MATERIAL_TO_START
&& structure.hasCreatedPokemon
val returnItem = NaturalMaterials.getReturnItem(stack!!) ?: return canUtilize
&& !structure.hasCreatedPokemon
val returnItem = NaturalMaterials.getReturnItem(stack) ?: return canUtilize
if (canUtilize) {
// See if there's room
for (i in items.indices) {
@@ -170,13 +170,12 @@ object CobblemonClient {
private fun registerTooltipManagers() {
TooltipManager.registerTooltipGenerator(CobblemonTooltipGenerator)
TooltipManager.registerTooltipGenerator(RecipeSeasoningAbsorbtionTooltipGenerator)
TooltipManager.registerTooltipGenerator(FishingBaitTooltipGenerator)
TooltipManager.registerTooltipGenerator(FishingRodTooltipGenerator)
TooltipManager.registerTooltipGenerator(SeasoningTooltipGenerator)
TooltipManager.registerTooltipGenerator(FishingRodTooltipGenerator)
TooltipManager.registerTooltipGenerator(AprijuiceTooltipGenerator)
TooltipManager.registerTooltipGenerator(PokePuffTooltipGenerator)
TooltipManager.registerTooltipGenerator(FoodTooltipGenerator)
TooltipManager.registerTooltipGenerator(MobEffectTooltipGenerator)
}
fun registerFlywheelRenderers() {
@@ -219,6 +218,13 @@ object CobblemonClient {
CobblemonBlocks.RED_APRICORN_SAPLING,
CobblemonBlocks.WHITE_APRICORN_SAPLING,
CobblemonBlocks.YELLOW_APRICORN_SAPLING,
CobblemonBlocks.POTTED_BLACK_APRICORN_SAPLING,
CobblemonBlocks.POTTED_BLUE_APRICORN_SAPLING,
CobblemonBlocks.POTTED_GREEN_APRICORN_SAPLING,
CobblemonBlocks.POTTED_PINK_APRICORN_SAPLING,
CobblemonBlocks.POTTED_RED_APRICORN_SAPLING,
CobblemonBlocks.POTTED_WHITE_APRICORN_SAPLING,
CobblemonBlocks.POTTED_YELLOW_APRICORN_SAPLING,
CobblemonBlocks.BLACK_APRICORN,
CobblemonBlocks.BLUE_APRICORN,
CobblemonBlocks.GREEN_APRICORN,
@@ -268,6 +274,7 @@ object CobblemonClient {
CobblemonBlocks.SACCHARINE_HANGING_SIGN,
CobblemonBlocks.SACCHARINE_WALL_HANGING_SIGN,
CobblemonBlocks.SACCHARINE_SAPLING,
CobblemonBlocks.POTTED_SACCHARINE_SAPLING,
CobblemonBlocks.LURE_CAKE,
CobblemonBlocks.POKE_CAKE,
CobblemonBlocks.LECTERN,
@@ -15,6 +15,8 @@ import com.cobblemon.mod.common.api.pokemon.feature.SynchronizedSpeciesFeaturePr
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.api.pokemon.stats.Stats
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.widgets.SoundlessWidget
@@ -70,6 +72,7 @@ class StatWidget(
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 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")
@@ -87,6 +90,10 @@ class StatWidget(
private val speedLabel = lang("ui.stats.speed")
}
var effectiveBattleIVs: Map<Stat, Int> = Stats.PERMANENT.associateWith { stat ->
pokemon.ivs.getEffectiveBattleIV(stat)
}
var statTabIndex = tabIndex
val renderableFeatures = SpeciesFeatures
.getFeaturesFor(pokemon.species)
@@ -251,6 +258,79 @@ class StatWidget(
)
}
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)
}
blitk(
matrixStack = matrices,
texture = CobblemonResources.WHITE,
x = moduleX + 8,
y = moduleY + 18,
height = 8,
width = barWidth,
red = red,
green = green,
blue = blue
)
// Draw the overlay
blitk(
matrixStack = matrices,
texture = fullnessOverlayResource,
x = moduleX / SCALE,
y = (moduleY + 16) / SCALE,
height = 20,
width = 248,
scale = SCALE
)
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = "Fullness".text().bold(),
x = moduleX + 62,
y = moduleY + 2.5,
centered = true,
shadow = true
)
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
)
}
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
val renderChart = statOptions.get(statTabIndex) != OTHER
val matrices = context.pose()
@@ -277,7 +357,6 @@ class StatWidget(
scale = SCALE
)
}
when (statOptions.get(statTabIndex)) {
STATS -> drawStatHexagon(
mapOf(
@@ -292,7 +371,7 @@ class StatWidget(
maximum = 400
)
IV -> drawStatHexagon(
pokemon.ivs.associate { it.key to it.value },
effectiveBattleIVs,
colour = Vector3f(216F/255, 100F/255, 1F),
maximum = 31
)
@@ -391,6 +470,9 @@ class StatWidget(
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,
@@ -417,9 +499,9 @@ class StatWidget(
}
private fun getStatValueAsText(stat: Stat): MutableComponent {
val value = when(statOptions.get(statTabIndex)) {
val value = when (statOptions.get(statTabIndex)) {
STATS -> if (stat == Stats.HP) "${pokemon.currentHealth} / ${pokemon.maxHealth}" else pokemon.getStat(stat).toString()
IV -> pokemon.ivs.getOrDefault(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"
}
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.client.render.block
import com.cobblemon.mod.common.block.CakeBlock
import com.cobblemon.mod.common.block.entity.CakeBlockEntity
import com.cobblemon.mod.common.item.components.FoodColourComponent
import com.cobblemon.mod.common.util.cobblemonResource
@@ -21,6 +22,7 @@ import net.minecraft.client.renderer.RenderType
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
import net.minecraft.client.renderer.texture.TextureAtlasSprite
import net.minecraft.core.Direction
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.inventory.InventoryMenu.BLOCK_ATLAS
import org.joml.Vector3d
@@ -39,41 +41,41 @@ open class CakeBlockEntityRenderer(ctx: BlockEntityRendererProvider.Context) : B
object DefaultCakeLayout : CakeLayout {
override val topLayers: List<CakeTexture> = mutableListOf(
CakeTexture("poke_snack_top_layer2", LayerColor.SECONDARY),
CakeTexture("poke_snack_top_layer3", LayerColor.PRIMARY),
CakeTexture("poke_snack_top_layer2", LayerColor.SECONDARY),
CakeTexture("poke_snack_top_layer3", LayerColor.PRIMARY),
)
override val sideLayers: List<CakeTexture> = mutableListOf(
CakeTexture("poke_snack_side_layer1", LayerColor.TERTIARY),
CakeTexture("poke_snack_side_layer2", LayerColor.SECONDARY),
CakeTexture("poke_snack_side_layer3", LayerColor.PRIMARY),
CakeTexture("poke_snack_side_layer1", LayerColor.TERTIARY),
CakeTexture("poke_snack_side_layer2", LayerColor.SECONDARY),
CakeTexture("poke_snack_side_layer3", LayerColor.PRIMARY),
)
override val bottomLayers: List<CakeTexture> = mutableListOf(
CakeTexture("poke_snack_bottom_layer1", LayerColor.TERTIARY),
CakeTexture("poke_snack_bottom_layer1", LayerColor.TERTIARY),
)
override val innerLayers: List<CakeTexture> = mutableListOf(
CakeTexture("poke_snack_inner_layer1", LayerColor.TERTIARY),
CakeTexture("poke_snack_inner_layer2", LayerColor.SECONDARY),
CakeTexture("poke_snack_inner_layer3", LayerColor.PRIMARY),
CakeTexture("poke_snack_inner_layer1", LayerColor.TERTIARY),
CakeTexture("poke_snack_inner_layer2", LayerColor.SECONDARY),
CakeTexture("poke_snack_inner_layer3", LayerColor.PRIMARY),
)
}
object HeartCakeLayout : CakeLayout {
override val topLayers: List<CakeTexture> = mutableListOf(
CakeTexture("poke_snack_top_layer4", LayerColor.SECONDARY),
CakeTexture("poke_snack_top_layer5", LayerColor.PRIMARY),
CakeTexture("poke_snack_top_layer4", LayerColor.SECONDARY),
CakeTexture("poke_snack_top_layer5", LayerColor.PRIMARY),
)
override val sideLayers: List<CakeTexture> = mutableListOf(
CakeTexture("poke_snack_side_layer1", LayerColor.TERTIARY),
CakeTexture("poke_snack_side_layer2", LayerColor.SECONDARY),
CakeTexture("poke_snack_side_layer3", LayerColor.PRIMARY),
CakeTexture("poke_snack_side_layer1", LayerColor.TERTIARY),
CakeTexture("poke_snack_side_layer2", LayerColor.SECONDARY),
CakeTexture("poke_snack_side_layer3", LayerColor.PRIMARY),
)
override val bottomLayers: List<CakeTexture> = mutableListOf(
CakeTexture("poke_snack_bottom_layer1", LayerColor.TERTIARY),
CakeTexture("poke_snack_bottom_layer1", LayerColor.TERTIARY),
)
override val innerLayers: List<CakeTexture> = mutableListOf(
CakeTexture("poke_snack_inner_layer1", LayerColor.TERTIARY),
CakeTexture("poke_snack_inner_layer2", LayerColor.SECONDARY),
CakeTexture("poke_snack_inner_layer3", LayerColor.PRIMARY),
CakeTexture("poke_snack_inner_layer1", LayerColor.TERTIARY),
CakeTexture("poke_snack_inner_layer2", LayerColor.SECONDARY),
CakeTexture("poke_snack_inner_layer3", LayerColor.PRIMARY),
)
}
@@ -109,37 +111,37 @@ open class CakeBlockEntityRenderer(ctx: BlockEntityRendererProvider.Context) : B
}
override fun render(
blockEntity: CakeBlockEntity,
partialTick: Float,
poseStack: PoseStack,
bufferSource: MultiBufferSource,
packedLight: Int,
packedOverlay: Int
blockEntity: CakeBlockEntity,
partialTick: Float,
poseStack: PoseStack,
bufferSource: MultiBufferSource,
packedLight: Int,
packedOverlay: Int
) {
val foodColourComponent = blockEntity.foodColourComponent
renderCake(blockEntity, foodColourComponent, poseStack, bufferSource, packedLight, blockEntity.bites)
}
fun renderCake(
blockEntity: CakeBlockEntity,
foodColourComponent: FoodColourComponent?,
poseStack: PoseStack,
multiBufferSource: MultiBufferSource,
light: Int,
bites: Int,
blockEntity: CakeBlockEntity,
foodColourComponent: FoodColourComponent?,
poseStack: PoseStack,
multiBufferSource: MultiBufferSource,
light: Int,
bites: Int,
) {
val seedString = foodColourComponent?.colours?.joinToString()
val random = Random(seedString.hashCode())
val cakeLayout: CakeLayout = CAKE_LAYOUTS[random.nextInt(CAKE_LAYOUTS.size)]
val visualSegments = 7f
val segmentIndex = (bites.toFloat() / blockEntity.maxBites.toFloat()) * visualSegments
val segmentsRemaining = visualSegments - segmentIndex
val percentageToRender = segmentsRemaining / visualSegments
val clippedWidth = (ATLAS_END - ATLAS_START) * percentageToRender
val visualStages = 7f
val maxBites = blockEntity.maxBites.toFloat()
val bitesPerStage = ((maxBites + 1) / visualStages).coerceAtLeast(1f)
val visualStage = (bites / bitesPerStage).toInt().coerceIn(0, visualStages.toInt() - 1)
val biteOffset = (15f - 2f * visualStage) / 16f
val clippedWidth = (ATLAS_END - ATLAS_START) * biteOffset
val topLayer = cakeLayout.topLayers.first().texture
val uClipped = topLayer.u0 + (topLayer.u1 - topLayer.u0) * percentageToRender
val uClipped = topLayer.u0 + (topLayer.u1 - topLayer.u0) * biteOffset
val vertexConsumer = multiBufferSource.getBuffer(RenderType.cutout())
val primaryColor = foodColourComponent?.colours?.getOrNull(0)?.textureDiffuseColor ?: WHITE_OPAQUE_COLOR
@@ -148,33 +150,42 @@ open class CakeBlockEntityRenderer(ctx: BlockEntityRendererProvider.Context) : B
poseStack.pushPose()
poseStack.translate(0.5, 0.5, 0.5)
poseStack.mulPose(Axis.YP.rotationDegrees(180f))
val facing = blockEntity.blockState.getValue(CakeBlock.FACING)
val angle = when (facing) {
Direction.NORTH -> 180f
Direction.SOUTH -> 0f
Direction.WEST -> 90f
Direction.EAST -> -90f
else -> 0f
}
poseStack.mulPose(Axis.YP.rotationDegrees(angle))
poseStack.translate(-0.5, -0.5, -0.5)
poseStack.pushPose()
for (layer in cakeLayout.topLayers) {
val texture = layer.texture
drawQuad(
vertexConsumer, poseStack,
ATLAS_START, TOP_LAYER_HEIGHT, ATLAS_START, clippedWidth, TOP_LAYER_HEIGHT, ATLAS_END,
texture.u0, texture.v0, uClipped, texture.v1,
light, layer.getColor(primaryColor, secondaryColor, tertiaryColor)
vertexConsumer, poseStack,
ATLAS_START, TOP_LAYER_HEIGHT, ATLAS_START, clippedWidth, TOP_LAYER_HEIGHT, ATLAS_END,
texture.u0, texture.v0, uClipped, texture.v1,
light, layer.getColor(primaryColor, secondaryColor, tertiaryColor)
)
}
poseStack.popPose()
val translations = arrayOf(
Vector3d(-1.0, 1.0, 0.5 - PIXEL_SHIFT),
Vector3d(0.0, 1.0, 0.5 - PIXEL_SHIFT),
Vector3d(0.0, 1.0, -0.5 - PIXEL_SHIFT),
Vector3d(-1.0, 1.0, -0.5 - PIXEL_SHIFT),
Vector3d(-1.0, 1.0, 0.5 - PIXEL_SHIFT),
Vector3d(0.0, 1.0, 0.5 - PIXEL_SHIFT),
Vector3d(0.0, 1.0, -0.5 - PIXEL_SHIFT),
Vector3d(-1.0, 1.0, -0.5 - PIXEL_SHIFT),
)
val rotationsY = arrayOf(
Axis.YP.rotationDegrees(90f),
Axis.YP.rotationDegrees(0f),
Axis.YP.rotationDegrees(-90f),
Axis.YP.rotationDegrees(-180f)
Axis.YP.rotationDegrees(90f),
Axis.YP.rotationDegrees(0f),
Axis.YP.rotationDegrees(-90f),
Axis.YP.rotationDegrees(-180f)
)
val cakeIsFull = bites == 0
@@ -186,47 +197,47 @@ open class CakeBlockEntityRenderer(ctx: BlockEntityRendererProvider.Context) : B
poseStack.mulPose(Axis.XP.rotationDegrees(90f))
if (i == 0 && !cakeIsFull) {
poseStack.translate(0.0, -bites * PIXEL_SHIFT * 2, 0.0)
poseStack.translate(0.0, -visualStage * PIXEL_SHIFT * 2, 0.0)
for (layer in cakeLayout.innerLayers) {
val texture = layer.texture
drawQuad(
vertexConsumer, poseStack,
ATLAS_START, HEIGHT, ATLAS_START, ATLAS_END, HEIGHT, ATLAS_END,
texture.u0, texture.v0, texture.u1, texture.v1,
light, layer.getColor(primaryColor, secondaryColor, tertiaryColor)
vertexConsumer, poseStack,
ATLAS_START, HEIGHT, ATLAS_START, ATLAS_END, HEIGHT, ATLAS_END,
texture.u0, texture.v0, texture.u1, texture.v1,
light, layer.getColor(primaryColor, secondaryColor, tertiaryColor)
)
}
} else if (i == 0 || i == 1) {
for (layer in cakeLayout.sideLayers) {
val texture = layer.texture
drawQuad(
vertexConsumer, poseStack,
ATLAS_START, HEIGHT, ATLAS_START, clippedWidth, HEIGHT, ATLAS_END,
texture.u0, texture.v0, uClipped, texture.v1,
light, layer.getColor(primaryColor, secondaryColor, tertiaryColor)
vertexConsumer, poseStack,
ATLAS_START, HEIGHT, ATLAS_START, clippedWidth, HEIGHT, ATLAS_END,
texture.u0, texture.v0, uClipped, texture.v1,
light, layer.getColor(primaryColor, secondaryColor, tertiaryColor)
)
}
} else if (i == 2) {
for (layer in cakeLayout.sideLayers) {
val texture = layer.texture
drawQuad(
vertexConsumer, poseStack,
ATLAS_START, HEIGHT, ATLAS_START, ATLAS_END, HEIGHT, ATLAS_END,
texture.u0, texture.v0, texture.u1, texture.v1,
light, layer.getColor(primaryColor, secondaryColor, tertiaryColor)
vertexConsumer, poseStack,
ATLAS_START, HEIGHT, ATLAS_START, ATLAS_END, HEIGHT, ATLAS_END,
texture.u0, texture.v0, texture.u1, texture.v1,
light, layer.getColor(primaryColor, secondaryColor, tertiaryColor)
)
}
} else if (i == 3) {
val clippedStartX = ATLAS_START + (ATLAS_END - ATLAS_START) * (1 - percentageToRender)
val clippedStartX = ATLAS_START + (ATLAS_END - ATLAS_START) * (1 - biteOffset)
for (layer in cakeLayout.sideLayers) {
val texture = layer.texture
val clippedUStart = texture.u0 + (texture.u1 - texture.u0) * (1 - percentageToRender)
val clippedUStart = texture.u0 + (texture.u1 - texture.u0) * (1 - biteOffset)
drawQuad(
vertexConsumer, poseStack,
clippedStartX, HEIGHT, ATLAS_START, ATLAS_END, HEIGHT, ATLAS_END,
clippedUStart, texture.v0, texture.u1, texture.v1,
light, layer.getColor(primaryColor, secondaryColor, tertiaryColor)
vertexConsumer, poseStack,
clippedStartX, HEIGHT, ATLAS_START, ATLAS_END, HEIGHT, ATLAS_END,
clippedUStart, texture.v0, texture.u1, texture.v1,
light, layer.getColor(primaryColor, secondaryColor, tertiaryColor)
)
}
}
@@ -242,10 +253,10 @@ open class CakeBlockEntityRenderer(ctx: BlockEntityRendererProvider.Context) : B
for (layer in cakeLayout.bottomLayers) {
val texture = layer.texture
drawQuad(
vertexConsumer, poseStack,
ATLAS_START, BOTTOM_LAYER_HEIGHT, ATLAS_START, clippedWidth, BOTTOM_LAYER_HEIGHT, ATLAS_END,
texture.u0, texture.v0, uClipped, texture.v1,
light, layer.getColor(primaryColor, secondaryColor, tertiaryColor)
vertexConsumer, poseStack,
ATLAS_START, BOTTOM_LAYER_HEIGHT, ATLAS_START, clippedWidth, BOTTOM_LAYER_HEIGHT, ATLAS_END,
texture.u0, texture.v0, uClipped, texture.v1,
light, layer.getColor(primaryColor, secondaryColor, tertiaryColor)
)
}
@@ -255,12 +266,12 @@ open class CakeBlockEntityRenderer(ctx: BlockEntityRendererProvider.Context) : B
}
private fun drawQuad(
builder: VertexConsumer,
poseStack: PoseStack,
x0: Float, y0: Float, z0: Float,
x1: Float, y1: Float, z1: Float,
u0: Float, v0: Float, u1: Float, v1: Float,
packedLight: Int, color: Int,
builder: VertexConsumer,
poseStack: PoseStack,
x0: Float, y0: Float, z0: Float,
x1: Float, y1: Float, z1: Float,
u0: Float, v0: Float, u1: Float, v1: Float,
packedLight: Int, color: Int,
) {
drawVertex(builder, poseStack, x0, y0, z0, u0, v0, packedLight, color)
drawVertex(builder, poseStack, x0, y1, z1, u0, v1, packedLight, color)
@@ -269,16 +280,16 @@ open class CakeBlockEntityRenderer(ctx: BlockEntityRendererProvider.Context) : B
}
private fun drawVertex(
builder: VertexConsumer,
poseStack: PoseStack,
x: Float, y: Float, z: Float,
u: Float, v: Float,
packedLight: Int, color: Int
builder: VertexConsumer,
poseStack: PoseStack,
x: Float, y: Float, z: Float,
u: Float, v: Float,
packedLight: Int, color: Int
) {
builder.addVertex(poseStack.last().pose(), x, y, z)
.setColor(color)
.setUv(u, v)
.setLight(packedLight)
.setNormal(0f, 1f, 0f)
.setColor(color)
.setUv(u, v)
.setLight(packedLight)
.setNormal(0f, 1f, 0f)
}
}
@@ -102,4 +102,4 @@ class WailordModel(root: ModelPart) : PokemonPosableModel(root) {
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
}
}
@@ -58,8 +58,10 @@ object MountedPlayerRenderer {
val offset = Vector3f(0f, player.bbHeight / 2, 0f).mul(-1f)
matrix.rotate(locator.matrix.getRotation(AxisAngle4f()))
matrix.rotate(Axis.YP.rotationDegrees(180 + yBodyRot))
if (entity.beamMode == 0) {
matrix.rotate(locator.matrix.getRotation(AxisAngle4f()))
matrix.rotate(Axis.YP.rotationDegrees(180 + yBodyRot))
}
matrix.translate(offset)
matrix.translate(Vector3f(0f, 0.35f, 0f))
@@ -144,7 +144,7 @@ class PokemonRenderer(
if (entity.passengers.isNotEmpty()) {
renderRiding(entity, partialTicks, poseMatrix, buffer, packedLight)
renderRiding(entity, entityYaw, partialTicks, poseMatrix, buffer, packedLight)
}
else {
super.render(entity, entityYaw, partialTicks, poseMatrix, buffer, packedLight)
@@ -173,6 +173,7 @@ class PokemonRenderer(
fun renderRiding(
entity: PokemonEntity,
entityYaw: Float,
partialTicks: Float,
poseMatrix: PoseStack,
buffer: MultiBufferSource,
@@ -200,7 +201,7 @@ class PokemonRenderer(
matrix.mul(transformationMatrix)
}
super.render(entity, 0f, partialTicks, poseMatrix, buffer, packedLight)
super.render(entity, entityYaw, partialTicks, poseMatrix, buffer, packedLight)
poseMatrix.popPose()
}
@@ -9,17 +9,14 @@
package com.cobblemon.mod.common.client.tooltips
import com.cobblemon.mod.common.CobblemonItemComponents
import com.cobblemon.mod.common.api.text.blue
import com.cobblemon.mod.common.api.text.gray
import com.cobblemon.mod.common.item.AprijuiceItem
import com.cobblemon.mod.common.util.lang
import net.minecraft.network.chat.Component
import net.minecraft.world.item.ItemStack
object AprijuiceTooltipGenerator : TooltipGenerator() {
override fun generateCategoryTooltip(stack: ItemStack, lines: MutableList<Component>): MutableList<Component>? {
if (stack.item !is AprijuiceItem) return null
return mutableListOf(seasoningHeader)
}
override fun generateAdditionalTooltip(stack: ItemStack, lines: MutableList<Component>): MutableList<Component>? {
if (stack.item !is AprijuiceItem) return null
@@ -30,7 +27,7 @@ object AprijuiceTooltipGenerator : TooltipGenerator() {
val quality = flavourComponent.getQuality()
resultLines.add(lang("cooking.cooking_quality", quality.getLang()))
resultLines.addAll(generateAdditionalFlavorTooltip(flavourComponent.flavours))
SeasoningTooltipGenerator.generateAdditionalTooltip(stack, lines)
return resultLines
}
@@ -28,15 +28,8 @@ import net.minecraft.network.chat.Component
import net.minecraft.world.item.ItemStack
object FishingBaitTooltipGenerator : TooltipGenerator() {
private val fishingBaitHeader by lazy { lang("fishing_bait_effect_header").blue() }
private val fishingBaitItemClass by lazy { lang("item_class.fishing_bait").blue() }
private val Genders = mapOf<Gender, Component>(
Gender.MALE to lang("gender.male"),
Gender.FEMALE to lang("gender.female"),
Gender.GENDERLESS to lang("gender.genderless"),
)
override fun generateCategoryTooltip(stack: ItemStack, lines: MutableList<Component>): MutableList<Component>? {
if (stack.get(DataComponents.HIDE_ADDITIONAL_TOOLTIP) != null) {
return null
@@ -53,77 +46,7 @@ object FishingBaitTooltipGenerator : TooltipGenerator() {
}
val resultLines = mutableListOf<Component>()
// Determine the FishingBait or combined effects from poke_bait
val rawEffects = mutableListOf<SpawnBait.Effect>().apply {
if (stack.item is PokerodItem) {
addAll(SpawnBaitEffects.getEffectsFromRodItemStack(stack))
} else {
addAll(SpawnBaitEffects.getEffectsFromItemStack(stack))
}
// Check for Seasoning-based Bait effects
if (Seasonings.isSeasoning(stack)) {
val seasoningEffects = Seasonings.getBaitEffectsFromItemStack(stack)
if (seasoningEffects.isNotEmpty()) {
addAll(seasoningEffects)
}
}
}
val baitEffects = SpawnBaitUtils.mergeEffects(rawEffects)
if (baitEffects.isEmpty()) return null
resultLines.add(this.fishingBaitHeader)
val formatter = DecimalFormat("0.##")
baitEffects.forEach { effect ->
val effectType = effect.type.path.toString()
val effectSubcategory = effect.subcategory?.path
val effectChance = effect.chance * 100
var effectValue = when (effectType) {
"bite_time" -> (effect.value * 100).toInt()
else -> effect.value.toInt()
}
val subcategoryString: Component = if (effectSubcategory != null) {
when (effectType) {
"nature", "ev", "iv" -> com.cobblemon.mod.common.api.pokemon.stats.Stats.getStat(
effectSubcategory
).displayName
"gender_chance" -> Genders[Gender.valueOf(effectSubcategory.toUpperCase())]
"typing" -> ElementalTypes.get(effectSubcategory)?.displayName
"egg_group" -> {
val effectSubcategory = effect.subcategory.path
val eggGroup = effectSubcategory?.let { EggGroup.fromIdentifier(it) }
eggGroup?.let {
val langKey = "egg_group.${it.name.lowercase()}"
lang(langKey)
} ?: Component.literal(effectSubcategory ?: "Unknown").gold()
}
else -> Component.empty()
} ?: Component.literal("cursed").obfuscate()
} else Component.literal("cursed").obfuscate()
// Adjust shiny chance effectValue
if (effectType == "shiny_reroll") {
effectValue++
}
resultLines.add(
lang(
"fishing_bait_effects.$effectType.tooltip",
Component.literal(formatter.format(effectChance)).yellow(),
subcategoryString.copy().gold(),
Component.literal(formatter.format(effectValue)).green()
)
)
}
if (SpawnBaitEffects.isFishingBait(stack)) resultLines.add(this.fishingBaitItemClass)
return resultLines
}
@@ -1,36 +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.tooltips
import com.cobblemon.mod.common.api.cooking.Flavour
import com.cobblemon.mod.common.api.text.*
import com.cobblemon.mod.common.util.lang
import net.minecraft.network.chat.Component
val seasoningHeader by lazy { lang("item_class.seasoning").blue() }
private val flavorSubHeader by lazy { lang("seasoning_flavor_header").blue() }
fun generateAdditionalFlavorTooltip(flavours: Map<Flavour, Int>): MutableList<Component> {
val resultLines = mutableListOf<Component>()
resultLines.add(flavorSubHeader)
val combinedFlavorsLine = Component.literal("")
flavours.forEach { (flavour, value) ->
var flavourText = lang("seasoning_flavor.${flavour.name.lowercase()}").withStyle(flavour.chatFormatting)
if (combinedFlavorsLine.string.isNotEmpty()) {
combinedFlavorsLine.append(" ")
}
combinedFlavorsLine.append(flavourText).append(" $value")
}
resultLines.add(combinedFlavorsLine)
return resultLines
}
@@ -1,35 +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.tooltips
import com.cobblemon.mod.common.CobblemonItemComponents
import com.cobblemon.mod.common.api.text.blue
import com.cobblemon.mod.common.api.text.green
import com.cobblemon.mod.common.api.text.yellow
import com.cobblemon.mod.common.util.lang
import net.minecraft.network.chat.Component
import net.minecraft.world.item.ItemStack
object FoodTooltipGenerator : TooltipGenerator() {
private val foodHeader by lazy { lang("item_class.food").blue() }
override fun generateCategoryTooltip(stack: ItemStack, lines: MutableList<Component>): MutableList<Component>? {
val food = stack.get(CobblemonItemComponents.FOOD) ?: return null
return mutableListOf(foodHeader)
}
override fun generateAdditionalTooltip(stack: ItemStack, lines: MutableList<Component>): MutableList<Component>? {
val food = stack.get(CobblemonItemComponents.FOOD) ?: return null
return mutableListOf(
lang("tooltip.food.hunger", Component.literal("${food.hunger}").yellow()),
lang("tooltip.food.saturation", Component.literal("%.2f".format(food.saturation)).green())
)
}
}
@@ -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.tooltips
import com.cobblemon.mod.common.CobblemonItemComponents
import com.cobblemon.mod.common.api.text.blue
import com.cobblemon.mod.common.api.text.green
import com.cobblemon.mod.common.api.text.yellow
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.Minecraft
import net.minecraft.network.chat.Component
import net.minecraft.world.effect.MobEffectInstance
import net.minecraft.world.item.ItemStack
import net.minecraft.world.effect.MobEffectUtil
object MobEffectTooltipGenerator : TooltipGenerator() {
private val effectHeader by lazy { lang("item_class.mob_effect").blue() }
override fun generateCategoryTooltip(stack: ItemStack, lines: MutableList<Component>): MutableList<Component>? {
val effects = stack.get(CobblemonItemComponents.MOB_EFFECTS)?.mobEffects ?: return null
return if (effects.isNotEmpty()) mutableListOf(effectHeader) else null
}
override fun generateAdditionalTooltip(stack: ItemStack, lines: MutableList<Component>): MutableList<Component>? {
val effects = stack.get(CobblemonItemComponents.MOB_EFFECTS)?.mobEffects ?: return null
if (effects.isEmpty()) return null
val tickRate = Minecraft.getInstance().level?.tickRateManager()?.tickrate() ?: 20.0f
val result = mutableListOf<Component>()
// Always add the header FIRST, even for additional tooltip
result.add(effectHeader)
for (instance in effects) {
val name = instance.effect.value().descriptionId
val duration = MobEffectUtil.formatDuration(instance, 1.0f, tickRate) // Already a Component
val amplifierRoman = getRomanNumeral(instance.amplifier + 1)
result.add(
lang(
"tooltip.mob_effect_entry",
Component.translatable(name).yellow(),
Component.literal(amplifierRoman).green(),
duration.string.green()
)
)
}
return result
}
private fun getRomanNumeral(number: Int): String {
if (number <= 0) return number.toString() // no Roman numeral for zero or negatives
val numerals = listOf(
1000 to "M",
900 to "CM",
500 to "D",
400 to "CD",
100 to "C",
90 to "XC",
50 to "L",
40 to "XL",
10 to "X",
9 to "IX",
5 to "V",
4 to "IV",
1 to "I"
)
var remaining = number
val result = StringBuilder()
for ((value, numeral) in numerals) {
while (remaining >= value) {
result.append(numeral)
remaining -= value
}
}
return result.toString()
}
}
@@ -28,11 +28,8 @@ object PokePuffTooltipGenerator : TooltipGenerator() {
override fun generateAdditionalTooltip(stack: ItemStack, lines: MutableList<Component>): MutableList<Component>? {
if (stack.item !is PokePuffItem) return null
// val flavourComponent = stack.get(CobblemonItemComponents.FLAVOUR) ?: return null
val resultLines = mutableListOf<Component>()
// resultLines.addAll(generateAdditionalFlavorTooltip(flavourComponent.flavours))
val dominantFlavour = PokePuffUtils.getDominantFlavour(stack)
if (dominantFlavour != null) {
val friendshipLiked = PokePuffUtils.getFriendshipChangeLiked(stack)
@@ -0,0 +1,41 @@
/*
* 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.tooltips
import com.cobblemon.mod.common.CobblemonItemComponents
import com.cobblemon.mod.common.api.text.blue
import com.cobblemon.mod.common.api.text.gray
import com.cobblemon.mod.common.item.AprijuiceItem
import com.cobblemon.mod.common.util.lang
import net.minecraft.network.chat.Component
import net.minecraft.world.item.ItemStack
object RecipeSeasoningAbsorbtionTooltipGenerator : TooltipGenerator() {
override fun generateCategoryTooltip(stack: ItemStack, lines: MutableList<Component>): MutableList<Component>? {
// Skip if this is a crafting result preview. Vera wanted to be able to give extra context to things not fully crafted for added guidance in crafting UIs
if (stack.get(CobblemonItemComponents.CRAFTED) == true) {
return mutableListOf()
}
if (itemTakesSeasoningData(stack)) {
val accepted = mutableListOf<Component>()
if (recipeUsesFlavor(stack)) accepted.add(flavorHeader)
if (recipeUsesFood(stack)) accepted.add(foodHeader)
if (recipeUsesMobEffect(stack)) accepted.add(mobEffectHeader)
if (recipeUsesBaitEffect(stack)) accepted.add(baitEffectHeader)
if (accepted.isNotEmpty()) {
return mutableListOf(generateSeasoningAbsorbtionTooltip(accepted))
}
}
return null
}
}
@@ -8,21 +8,97 @@
package com.cobblemon.mod.common.client.tooltips
import com.cobblemon.mod.common.CobblemonItemComponents
import com.cobblemon.mod.common.api.cooking.Seasonings
import com.cobblemon.mod.common.api.text.blue
import com.cobblemon.mod.common.api.text.gray
import com.cobblemon.mod.common.util.lang
import net.minecraft.core.component.DataComponents
import net.minecraft.network.chat.Component
import net.minecraft.world.item.ItemStack
object SeasoningTooltipGenerator : TooltipGenerator() {
override fun generateCategoryTooltip(stack: ItemStack, lines: MutableList<Component>): MutableList<Component>? {
if (!Seasonings.isSeasoning(stack)) return null
return mutableListOf(seasoningHeader)
val isSeasoningIngredient = Seasonings.isSeasoning(stack)
val flavors = stack.get(CobblemonItemComponents.FLAVOUR)?.flavours
val food = stack.get(CobblemonItemComponents.FOOD)
val baitEffects = stack.get(CobblemonItemComponents.BAIT_EFFECTS)?.effects
val mobEffects = stack.get(CobblemonItemComponents.MOB_EFFECTS)?.mobEffects
val result = mutableListOf<Component>()
if (!isSeasoningIngredient && flavors == null && food == null && baitEffects == null && mobEffects == null) return null // exit early if there is nothing to show to save time
// This is where we label something as either an ingredient OR an item with Seasoning data. To help users know if something can be used IN the Pot or came FROM a pot at a glance
if (isSeasoningIngredient) {
// Flavor
if (Seasonings.hasFlavors(stack)) result.add(flavorSeasoningHeader)
// Food
if (Seasonings.hasFood(stack)) result.add(foodSeasoningHeader)
// Mob Effects
if (Seasonings.hasMobEffect(stack)) result.add(mobEffectSeasoningHeader)
// Bait Effects
if (Seasonings.hasBaitEffects(stack)) result.add(baitEffectSeasoningHeader)
} else { // these are output items that have Seasoning Component data on them
// Flavor
if (!flavors.isNullOrEmpty()) result.add(lang("seasoning_flavor_header").blue())
// Food
if (food != null) result.add(lang("seasoning_food_header").blue())
// Mob Effects
if (!mobEffects.isNullOrEmpty()) result.add(lang("seasoning_mob_effect_header").blue())
// Bait Effects
if (!baitEffects.isNullOrEmpty()) result.add(lang("seasoning_bait_effect_header").blue())
}
return if (result.isNotEmpty()) result else null
}
override fun generateAdditionalTooltip(stack: ItemStack, lines: MutableList<Component>): MutableList<Component>? {
val flavors = Seasonings.getFlavoursFromItemStack(stack)
if (flavors.isEmpty()) {
if (stack.get(DataComponents.HIDE_ADDITIONAL_TOOLTIP) != null) {
return null
}
return generateAdditionalFlavorTooltip(flavors)
val isSeasoningIngredient = Seasonings.isSeasoning(stack)
val flavors = stack.get(CobblemonItemComponents.FLAVOUR)?.flavours
val food = stack.get(CobblemonItemComponents.FOOD)
val baitEffects = stack.get(CobblemonItemComponents.BAIT_EFFECTS)?.effects
val mobEffects = stack.get(CobblemonItemComponents.MOB_EFFECTS)?.mobEffects
if (!isSeasoningIngredient && flavors == null && food == null && baitEffects == null && mobEffects == null) return null // exit early if there is nothing to show to save time
val result = mutableListOf<Component>()
// this is where we want to replace the headers with the info and data for each seasoning category we want to show
// Flavor
val flavorData = Seasonings.getFlavoursFromItemStack(stack).takeIf { !it.isNullOrEmpty() } ?: flavors
if (flavorData != null && flavorData.any { it.value != 0 }) {
result.addAll(generateAdditionalFlavorTooltip(flavorData))
}
// Food
if (Seasonings.hasFood(stack) || food != null) {
result.addAll(generateAdditionalFoodTooltip(stack))
}
// Mob Effects
if (Seasonings.hasMobEffect(stack) || mobEffects != null) {
result.addAll(generateAdditionalMobEffectTooltip(stack))
}
// Bait Effects
if (Seasonings.hasBaitEffects(stack) || baitEffects != null) {
result.addAll(generateAdditionalBaitEffectTooltip(stack))
}
return if (result.isNotEmpty()) result else null
}
}
@@ -0,0 +1,301 @@
/*
* 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.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
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.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() }
val seasoningsHeader by lazy { lang("item_class.seasonings").gray() }
val flavorSeasoningHeader by lazy { lang("item_class.flavor_seasoning").blue() }
val flavorHeader by lazy { lang("seasoning_flavor_header").gray() }
val flavorInfoSubHeader by lazy { lang("seasoning_flavor_info_header").blue() }
val baitEffectSeasoningHeader by lazy { lang("item_class.bait_effect_seasoning").blue() }
val baitEffectHeader by lazy { lang("seasoning_bait_effect_header").gray() }
val baitEffectInfoSubHeader by lazy { lang("seasoning_bait_effect_info_header").blue() }
val foodSeasoningHeader by lazy { lang("item_class.food_seasoning").blue() }
val foodHeader by lazy { lang("seasoning_food_header").gray() }
val foodInfoSubHeader by lazy { lang("seasoning_food_info_header").blue() }
val mobEffectSeasoningHeader by lazy { lang("item_class.mob_effect_seasoning").blue() }
val mobEffectHeader by lazy { lang("seasoning_mob_effect_header").gray() }
val mobEffectInfoSubHeader by lazy { lang("seasoning_mob_effect_info_header").blue() }
private fun recipeUsesProcessor(stack: ItemStack, processorType: String): Boolean {
val level = Minecraft.getInstance().level ?: return false
val recipeManager = level.recipeManager
val allCookingRecipes = recipeManager.getAllRecipesFor(CobblemonRecipeTypes.COOKING_POT_COOKING) +
recipeManager.getAllRecipesFor(CobblemonRecipeTypes.COOKING_POT_SHAPELESS)
return allCookingRecipes
.any { recipe ->
stack.item == recipe.value.result.item &&
recipe.value.seasoningProcessors.any { it.type == processorType }
}
}
fun itemTakesSeasoningData(stack: ItemStack): Boolean {
return recipeUsesFlavor(stack) || recipeUsesFood(stack) || recipeUsesMobEffect(stack) || recipeUsesBaitEffect(stack)
}
fun recipeUsesFlavor(stack: ItemStack): Boolean {
return recipeUsesProcessor(stack, "flavour")
}
fun recipeUsesFood(stack: ItemStack): Boolean {
return recipeUsesProcessor(stack, "food")
}
fun recipeUsesMobEffect(stack: ItemStack): Boolean {
return recipeUsesProcessor(stack, "mob_effects")
}
fun recipeUsesBaitEffect(stack: ItemStack): Boolean {
return recipeUsesProcessor(stack, "spawn_bait")
}
fun generateSeasoningAbsorbtionTooltip(accepted: List<Component>): Component {
if (accepted.isEmpty()) return Component.empty()
if (accepted.size == 1) return lang("item_class.cooking_pot_recipe_absorbs")
.append(" ").append(accepted[0]).append(" ")
.append(seasoningsHeader).gray()
val result = Component.literal("")
result.append(lang("item_class.cooking_pot_recipe_absorbs")).append(" ").gray()
for ((index, component) in accepted.withIndex()) {
when (index) {
accepted.lastIndex -> {
result.append("and ").append(component)
}
accepted.lastIndex - 1 -> {
result.append(component).append(", ")
}
else -> {
result.append(component).append(", ")
}
}
if (index == accepted.lastIndex) {
result.append(" ").append(seasoningsHeader).gray()
}
}
return result
}
fun generateAdditionalFlavorTooltip(flavours: Map<Flavour, Int>): MutableList<Component> {
val resultLines = mutableListOf<Component>()
resultLines.add(flavorInfoSubHeader)
val combinedFlavorsLine = Component.literal("")
flavours.filter { it.key != Flavour.MILD }.forEach { (flavour, value) ->
var flavourText = lang("seasoning_flavor.${flavour.name.lowercase()}").withStyle(flavour.chatFormatting)
if (combinedFlavorsLine.string.isNotEmpty()) {
combinedFlavorsLine.append(" ")
}
combinedFlavorsLine.append(flavourText).append(" $value")
}
resultLines.add(combinedFlavorsLine)
return resultLines
}
fun generateAdditionalMobEffectTooltip(stack: ItemStack): MutableList<Component> {
val effects: List<MobEffectInstance> =
Seasonings.getMobEffectsFromItemStack(stack)
.takeIf { it.isNotEmpty() }
?.map { it.toInstance() }
?: stack.get(CobblemonItemComponents.MOB_EFFECTS)?.mobEffects
?: return mutableListOf()
val tickRate = Minecraft.getInstance().level?.tickRateManager()?.tickrate() ?: 20.0f
val resultLines = mutableListOf<Component>()
resultLines.add(mobEffectInfoSubHeader)
for (instance in effects) {
val effect = instance.effect.value()
val name = Component.translatable(effect.descriptionId)
val color = if (effect.isBeneficial) ChatFormatting.AQUA else ChatFormatting.RED
val duration = MobEffectUtil.formatDuration(instance, 1.0f, tickRate)
val amplifierRoman = getRomanNumeral(instance.amplifier + 1)
resultLines.add(
lang(
"tooltip.mob_effect_entry",
name.copy().withStyle(color),
Component.literal(amplifierRoman).withStyle(color),
duration.string.green()
)
)
}
return resultLines
}
fun generateAdditionalFoodTooltip(stack: ItemStack): MutableList<Component> {
val food = Seasonings.getFoodComponentFromItemStack(stack) ?: stack.get(CobblemonItemComponents.FOOD)
val resultLines = mutableListOf<Component>()
if (food != null) {
resultLines.add(foodInfoSubHeader)
resultLines.add(
lang("tooltip.food.hunger", Component.literal("${food.hunger}").yellow())
)
resultLines.add(
lang("tooltip.food.saturation", Component.literal("%.2f".format(food.saturation)).green())
)
}
return resultLines
}
fun generateAdditionalBaitEffectTooltip(stack: ItemStack): MutableList<Component> {
val resultLines = mutableListOf<Component>()
val rawEffects = mutableListOf<SpawnBait.Effect>().apply {
if (stack.item is PokerodItem) {
addAll(SpawnBaitEffects.getEffectsFromRodItemStack(stack))
} else {
addAll(SpawnBaitEffects.getEffectsFromItemStack(stack))
}
if (Seasonings.isSeasoning(stack)) {
val seasoningEffects = Seasonings.getBaitEffectsFromItemStack(stack)
if (seasoningEffects.isNotEmpty()) {
addAll(seasoningEffects)
}
}
}
val baitEffects = SpawnBaitUtils.mergeEffects(rawEffects)
if (baitEffects.isNotEmpty()) {
resultLines.add(baitEffectInfoSubHeader)
val formatter = DecimalFormat("0.##")
val genders = mapOf(
Gender.MALE to lang("gender.male"),
Gender.FEMALE to lang("gender.female"),
Gender.GENDERLESS to lang("gender.genderless")
)
for (effect in baitEffects) {
val effectType = effect.type.path
val effectSubcategory = effect.subcategory?.path
val effectChance = effect.chance * 100
var effectValue = when (effectType) {
"bite_time" -> (effect.value * 100).toInt()
else -> effect.value.toInt()
}
val subcategoryString: Component = if (effectSubcategory != null) {
when (effectType) {
"nature", "ev", "iv" ->
com.cobblemon.mod.common.api.pokemon.stats.Stats.getStat(effectSubcategory).displayName
"gender_chance" ->
genders[Gender.valueOf(effectSubcategory.uppercase())]
"typing" ->
ElementalTypes.get(effectSubcategory)?.displayName
"egg_group" -> {
val eggGroup = EggGroup.fromIdentifier(effectSubcategory)
eggGroup?.let {
lang("egg_group.${it.name.lowercase()}")
} ?: Component.literal(effectSubcategory).gold()
}
else -> Component.empty()
} ?: Component.literal("cursed").obfuscate()
} else Component.literal("cursed").obfuscate()
if (effectType == "shiny_reroll") {
effectValue++
}
resultLines.add(
lang(
"fishing_bait_effects.$effectType.tooltip",
Component.literal(formatter.format(effectChance)).yellow(),
subcategoryString.copy().gold(),
Component.literal(formatter.format(effectValue)).green()
)
)
}
}
return resultLines
}
private fun getRomanNumeral(number: Int): String {
if (number <= 0) return number.toString()
val numerals = listOf(
1000 to "M",
900 to "CM",
500 to "D",
400 to "CD",
100 to "C",
90 to "XC",
50 to "L",
40 to "XL",
10 to "X",
9 to "IX",
5 to "V",
4 to "IV",
1 to "I"
)
var remaining = number
val result = StringBuilder()
for ((value, numeral) in numerals) {
while (remaining >= value) {
result.append(numeral)
remaining -= value
}
}
return result.toString()
}
@@ -0,0 +1,120 @@
/*
* 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.command
import com.cobblemon.mod.common.api.permission.CobblemonPermissions
import com.cobblemon.mod.common.api.text.*
import com.cobblemon.mod.common.command.argument.PokemonPropertiesArgumentType
import com.cobblemon.mod.common.util.*
import com.mojang.brigadier.Command
import com.mojang.brigadier.CommandDispatcher
import com.mojang.brigadier.context.CommandContext
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType
import net.minecraft.commands.CommandSourceStack
import net.minecraft.commands.Commands.literal
import net.minecraft.commands.Commands.argument
import net.minecraft.commands.arguments.EntityArgument
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
import net.minecraft.server.level.ServerPlayer
import com.cobblemon.mod.common.api.pokemon.stats.Stats
object PcSearchCommand {
private const val NAME = "pcsearch"
private const val PROPERTIES = "properties"
private const val OTHER = "other"
private val IN_BATTLE_EXCEPTION = SimpleCommandExceptionType(lang("pc.inbattle").red())
fun register(dispatcher: CommandDispatcher<CommandSourceStack>) {
dispatcher.register(
literal(NAME)
.permission(CobblemonPermissions.PC_SEARCH)
.then(
argument(PROPERTIES, PokemonPropertiesArgumentType.properties())
.executes { execute(it, it.source.playerOrException) }
)
.then(
literal("other")
.then(
argument(OTHER, EntityArgument.player())
.then(
argument(PROPERTIES, PokemonPropertiesArgumentType.properties())
.executes { execute(it, EntityArgument.getPlayer(it, OTHER)) }
)
)
)
)
}
private fun formatField(label: MutableComponent, value: MutableComponent): MutableComponent {
return label.add(": ").aqua().add(value.yellow())
}
private fun execute(context: CommandContext<CommandSourceStack>, player: ServerPlayer): Int {
if (player.isInBattle()) {
throw IN_BATTLE_EXCEPTION.create()
}
val pokemonProperties = PokemonPropertiesArgumentType.getPokemonProperties(context, PROPERTIES)
val pc = player.pc()
val matchingPokemon = mutableListOf<MutableComponent>()
pc.boxes.forEachIndexed { boxIndex, box ->
val nonEmptySlots = box.getNonEmptySlots()
for ((slotIndex, pokemon) in nonEmptySlots) {
if (pokemonProperties.matches(pokemon)) {
val boxNumber = boxIndex + 1
val displayName = pokemon.species.translatedName
val level = pokemon.level
val baseText = commandLang("pcsearch.entry", displayName, lang("label.lv", level)).yellow()
val hoverText = Component.literal("")
.add(formatField(lang("ui.info.species"), displayName))
.add(" ").add(lang("label.lv", level))
.add("\n").add(formatField(lang("ui.info.gender"), lang("gender.${pokemon.gender.name.lowercase()}")))
.add("\n").add(formatField(lang("ui.info.nature"), pokemon.nature.displayName.asTranslated()))
.add("\n").add(formatField(lang("ui.info.ability"), pokemon.ability.displayName.asTranslated()))
.add("\n").add(lang("ui.stats.ivs").add(": ").aqua().add(
"${pokemon.ivs[Stats.HP]}/${pokemon.ivs[Stats.ATTACK]}/${pokemon.ivs[Stats.DEFENCE]}/${pokemon.ivs[Stats.SPEED]}/${pokemon.ivs[Stats.SPECIAL_ATTACK]}/${pokemon.ivs[Stats.SPECIAL_DEFENCE]}".yellow()
))
.add("\n").add(lang("ui.stats.evs").add(": ").aqua().add(
"${pokemon.evs[Stats.HP]}/${pokemon.evs[Stats.ATTACK]}/${pokemon.evs[Stats.DEFENCE]}/${pokemon.evs[Stats.SPEED]}/${pokemon.evs[Stats.SPECIAL_ATTACK]}/${pokemon.evs[Stats.SPECIAL_DEFENCE]}".yellow()
))
if (pokemon.shiny) {
hoverText.add("\n").add(commandLang("pcsearch.tooltip.shiny").gold())
}
baseText.onHover(hoverText)
val locationText = commandLang("pcsearch.location", boxNumber, slotIndex + 1).green()
.onHover(commandLang("pcsearch.takepokemon"))
.suggest("/pctake ${player.name.string} $boxNumber ${slotIndex + 1}")
matchingPokemon.add(baseText.add(locationText))
}
}
}
if (matchingPokemon.isEmpty()) {
context.source.sendSystemMessage(commandLang("pcsearch.nomatch", pokemonProperties.originalString, player.name.string))
} else {
context.source.sendSystemMessage(commandLang("pcsearch.found", player.name.string))
matchingPokemon.forEach { result ->
context.source.sendSystemMessage(result)
}
}
return Command.SINGLE_SUCCESS
}
}
@@ -0,0 +1,98 @@
/*
* 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.command
import com.cobblemon.mod.common.api.permission.CobblemonPermissions
import com.cobblemon.mod.common.api.storage.pc.PCPosition
import com.cobblemon.mod.common.api.storage.pc.POKEMON_PER_BOX
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.util.*
import com.mojang.brigadier.Command
import com.mojang.brigadier.CommandDispatcher
import com.mojang.brigadier.arguments.IntegerArgumentType
import com.mojang.brigadier.context.CommandContext
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType
import net.minecraft.commands.CommandSourceStack
import net.minecraft.commands.Commands
import net.minecraft.commands.arguments.EntityArgument
import net.minecraft.server.level.ServerPlayer
object PcTakeCommand {
private val IN_BATTLE_EXCEPTION = SimpleCommandExceptionType(lang("pc.inbattle").red())
fun register(dispatcher : CommandDispatcher<CommandSourceStack>) {
val command = Commands.literal("pctake")
.permission(CobblemonPermissions.TAKE_POKEMON)
.then(
Commands.argument("player", EntityArgument.player())
.then(
Commands.argument("box", IntegerArgumentType.integer(1))
.then(
Commands.argument("slot", IntegerArgumentType.integer(1, 30))
.executes(::execute)
)
)
)
dispatcher.register(command)
}
private fun execute(context: CommandContext<CommandSourceStack>) : Int {
val executor = context.source.entity as? ServerPlayer
val target = EntityArgument.getPlayer(context, "player")
return executeWithTarget(context, target, executor)
}
private fun executeWithTarget(context: CommandContext<CommandSourceStack>, target: ServerPlayer, executor: ServerPlayer?) : Int {
try {
if (target.isInBattle()) {
throw IN_BATTLE_EXCEPTION.create()
}
val box = IntegerArgumentType.getInteger(context, "box")
val slot = IntegerArgumentType.getInteger(context, "slot")
val pc = target.pc()
if (box > POKEMON_PER_BOX) {
context.source.sendFailure(commandLang("pctake.too_many_boxes", POKEMON_PER_BOX))
return 0
}
val boxIndex = box - 1
val slotIndex = slot - 1
if (slotIndex < 0 || slotIndex >= POKEMON_PER_BOX) {
context.source.sendFailure(commandLang("pctake.invalid_slot"))
return 0
}
val pokemon = pc.boxes[boxIndex][slotIndex]
if (pokemon == null) {
context.source.sendFailure(commandLang("pctake.no_pokemon", box, slot))
return 0
}
pc.remove(PCPosition(boxIndex, slotIndex))
if (executor != null && executor != target) {
val toParty = executor.party()
toParty.add(pokemon)
context.source.sendSuccess({ commandLang("pctake.taken_other", pokemon.species.name, target.name.string) }, true)
return Command.SINGLE_SUCCESS
}
context.source.sendSuccess({ commandLang("pctake.removed", pokemon.species.name, box, slot) }, true)
return Command.SINGLE_SUCCESS
} catch (e: Exception) {
e.printStackTrace()
}
return Command.SINGLE_SUCCESS
}
}
@@ -16,6 +16,7 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder
import com.mojang.datafixers.DSL.TypeReference
import com.mojang.datafixers.DataFixer
import com.mojang.datafixers.DataFixerBuilder
import com.mojang.datafixers.schemas.Schema
import com.mojang.datafixers.util.Pair
import com.mojang.serialization.Codec
import com.mojang.serialization.DataResult
@@ -26,10 +27,10 @@ import java.util.concurrent.Executors
@Suppress("MemberVisibilityCanBePrivate", "unused")
object CobblemonSchemas {
private val SAME: (Int, Schema) -> Schema = ::Schema
private val RESULT: DataFixerBuilder.Result = this.create()
const val DATA_VERSION = 1
const val DATA_VERSION = 2
/**
* The Cobblemon [DataFixer].
@@ -82,6 +83,8 @@ object CobblemonSchemas {
builder.addFixer(ShoulderStateJsonFix(schema1))
builder.addFixer(NicknameFix(schema1))
builder.addFixer(RaisedPPStagesFix(schema1))
val schema2 = builder.addSchema(2, SAME)
builder.addFixer(HypertrainIVFix(schema2))
}
private class CobblemonDataFixerCodec<R>(private val baseCodec: Codec<R>, private val typeReference: TypeReference) : Codec<R> {
@@ -0,0 +1,27 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.datafixer.fix
import com.cobblemon.mod.common.util.DataKeys
import com.mojang.datafixers.schemas.Schema
import com.mojang.serialization.Dynamic
import kotlin.jvm.optionals.getOrNull
class HypertrainIVFix(outputSchema: Schema) : PokemonFix(outputSchema) {
override fun fixPokemonData(dynamic: Dynamic<*>): Dynamic<*> {
var baseDynamic = dynamic
val oldIVs = baseDynamic.get(DataKeys.POKEMON_IVS).get().result().getOrNull() ?: return baseDynamic
baseDynamic = baseDynamic.remove(DataKeys.POKEMON_IVS)
val newIVs = baseDynamic.createMap(mapOf(baseDynamic.createString(DataKeys.POKEMON_IVS_BASE) to oldIVs,
baseDynamic.createString(DataKeys.POKEMON_IVS_HYPERTRAINED) to baseDynamic.createMap(emptyMap())))
baseDynamic = baseDynamic.set(DataKeys.POKEMON_IVS, newIVs)
return baseDynamic
}
}
@@ -158,6 +158,7 @@ import net.minecraft.world.item.ItemUtils
import net.minecraft.world.item.Items
import net.minecraft.world.level.Level
import net.minecraft.world.level.LightLayer
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.gameevent.GameEvent
import net.minecraft.world.level.material.FluidState
import net.minecraft.world.level.pathfinder.PathType
@@ -784,7 +785,10 @@ open class PokemonEntity(
pokemon = this.createSidedPokemon()
health = 0F
}
} else {
} else if(pokemon.storeCoordinates.get() == null) {
// when the vanilla /data merge command is used, it will also run through this load method
// and if we are not careful here, the pokemon instance will get rebuilt from scratch
// this will fuck with storages, as they are tied to these very pokemon instances and their observables
val ops = registryAccess().createSerializationContext(NbtOps.INSTANCE)
pokemon = try {
this.sidedCodec().decode(ops, nbt.getCompound(DataKeys.POKEMON)).orThrow.first
@@ -2087,6 +2091,13 @@ open class PokemonEntity(
}
}
override fun maxUpStep(): Float {
val upStep = ifRidingAvailableSupply(fallback = null) { behaviour, settings, state ->
behaviour.maxUpStep(settings, state, this)
}
return upStep ?: super.maxUpStep()
}
override fun getRiddenSpeed(controller: Player): Float {
return ifRidingAvailableSupply(fallback = 0.05f) { behaviour, settings, state ->
behaviour.speed(settings, state,this, controller)
@@ -83,7 +83,8 @@ class AprijuiceItem(val type: Apricorn): CobblemonItem(Properties().stacksTo(16)
}
override fun canUseOnPokemon(stack: ItemStack, pokemon: Pokemon): Boolean {
return getBoosts(stack, pokemon).any { pokemon.canAddRideBoost(it.key, it.value) }
val boosts = getBoosts(stack, pokemon)
return boosts.isNotEmpty() && boosts.any { pokemon.canAddRideBoost(it.key, it.value) } && super.canUseOnPokemon(stack, pokemon)
}
fun getBoosts(stack: ItemStack, pokemon: Pokemon): Map<RidingStat, Float> {
@@ -101,20 +102,17 @@ class AprijuiceItem(val type: Apricorn): CobblemonItem(Properties().stacksTo(16)
stack: ItemStack,
pokemon: Pokemon
): InteractionResultHolder<ItemStack>? {
val boosts = getBoosts(stack, pokemon)
if (boosts.isEmpty()) {
return InteractionResultHolder.fail(stack)
}
if (!canUseOnPokemon(stack, pokemon)) {
return InteractionResultHolder.fail(stack)
}
val boosts = getBoosts(stack, pokemon)
// Feed the Pokémon 1 fullness point
pokemon.feedPokemon(1)
boosts.forEach { (stat, value) ->
pokemon.addRideBoost(stat, value)
}
pokemon.entity?.playSound(CobblemonSounds.BERRY_EAT, 1F, 1F)
if (!player.isCreative) {
stack.shrink(1)
}
@@ -100,6 +100,13 @@ class PokePuffItem : Item(Properties().stacksTo(16)), PokemonSelectingItem {
}
override fun applyToPokemon(player: ServerPlayer, stack: ItemStack, pokemon: Pokemon): InteractionResultHolder<ItemStack> {
if (!canUseOnPokemon(stack, pokemon)) {
return InteractionResultHolder.fail(stack)
}
// Feed the Pokémon 4 fullness points
pokemon.feedPokemon(4)
val friendshipChange = PokePuffUtils.calculateFriendshipChange(stack, pokemon.nature)
if (friendshipChange != 0) {
@@ -163,7 +170,7 @@ class PokePuffItem : Item(Properties().stacksTo(16)), PokemonSelectingItem {
override fun canUseOnPokemon(stack: ItemStack, pokemon: Pokemon): Boolean {
return getFriendshipDelta(stack, pokemon) != 0
return getFriendshipDelta(stack, pokemon) != 0 && super.canUseOnPokemon(stack, pokemon)
}
private fun getFriendshipDelta(stack: ItemStack, pokemon: Pokemon): Int {
@@ -0,0 +1,89 @@
/*
* 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.item
import com.cobblemon.mod.common.CobblemonItemComponents
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.apricorn.Apricorn
import com.cobblemon.mod.common.api.conditional.RegistryLikeIdentifierCondition
import com.cobblemon.mod.common.api.cooking.Flavour
import com.cobblemon.mod.common.api.cooking.PokePuffUtils
import com.cobblemon.mod.common.api.cooking.Seasonings
import com.cobblemon.mod.common.api.item.PokemonSelectingItem
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.Nature
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.network.chat.Component
import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer
import net.minecraft.sounds.SoundEvent
import net.minecraft.sounds.SoundEvents
import net.minecraft.world.InteractionHand
import net.minecraft.world.InteractionResult
import net.minecraft.world.InteractionResultHolder
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.UseAnim
import net.minecraft.world.level.Level
class RegionalFoodItem(properties: Properties) : Item(properties), PokemonSelectingItem {
override val bagItem = null
override fun use(world: Level, player: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> {
val stack = player.getItemInHand(hand)
// Allow eating normally if player needs food
if (player.foodData.needsFood()) {
player.startUsingItem(hand)
return InteractionResultHolder.consume(stack)
}
return InteractionResultHolder.fail(stack)
}
override fun interactLivingEntity(stack: ItemStack, player: Player, target: LivingEntity, hand: InteractionHand): InteractionResult {
if (player.level().isClientSide) return InteractionResult.PASS
if (player is ServerPlayer && target is PokemonEntity) {
val pokemon = target.pokemon
if (pokemon.status != null) {
return InteractionResult.SUCCESS
}
}
return InteractionResult.PASS
}
override fun applyToPokemon(player: ServerPlayer, stack: ItemStack, pokemon: Pokemon): InteractionResultHolder<ItemStack> {
if (pokemon.status != null) {
pokemon.status = null
pokemon.entity?.playSound(SoundEvents.GENERIC_EAT, 1F, 1F)
if (!player.isCreative) {
stack.shrink(1)
}
return InteractionResultHolder.success(stack)
}
return InteractionResultHolder.fail(stack)
}
override fun getUseAnimation(stack: ItemStack): UseAnim = UseAnim.EAT
override fun getUseDuration(stack: ItemStack, entity: LivingEntity): Int = 32
override fun getEatingSound(): SoundEvent = SoundEvents.GENERIC_EAT
override fun canUseOnPokemon(stack: ItemStack, pokemon: Pokemon): Boolean {
return pokemon.status != null && pokemon.currentHealth > 0
}
}
@@ -48,7 +48,7 @@ class RevivalHerbItem(block: RevivalHerbBlock) : ItemNameBlockItem(block, Proper
override val returnItem = Items.AIR
override fun canUse(stack: ItemStack, battle: PokemonBattle, target: BattlePokemon) = target.health <= 0
override fun getShowdownInput(actor: BattleActor, battlePokemon: BattlePokemon, data: String?): String {
battlePokemon.effectedPokemon.decrementFriendship(CobblemonMechanics.remedies.getFriendshipDrop(runtime))
battlePokemon.effectedPokemon.decrementFriendship(CobblemonMechanics.remedies.getFriendshipDrop("revival_herb", runtime))
return "revive 0.25"
}
}
@@ -77,7 +77,7 @@ class RevivalHerbItem(block: RevivalHerbBlock) : ItemNameBlockItem(block, Proper
pokemon.entity?.playSound(CobblemonSounds.MEDICINE_HERB_USE, 1F, 1F)
pokemon.currentHealth = amount
pokemon.decrementFriendship(CobblemonMechanics.remedies.getFriendshipDrop(runtime))
pokemon.decrementFriendship(CobblemonMechanics.remedies.getFriendshipDrop("revival_herb", runtime))
if (!player.isCreative) {
stack.shrink(1)
}
@@ -35,12 +35,19 @@ import net.minecraft.world.level.Level
*/
class FriendshipRaisingBerryItem(block: BerryBlock, val stat: Stat) : BerryItem(block), PokemonSelectingItem {
override val bagItem = null
override fun canUseOnPokemon(stack: ItemStack, pokemon: Pokemon) = pokemon.evs.getOrDefault(stat) > 0 || pokemon.friendship < Cobblemon.config.maxPokemonFriendship
override fun canUseOnPokemon(stack: ItemStack, pokemon: Pokemon) = (pokemon.evs.getOrDefault(stat) > 0 || pokemon.friendship < Cobblemon.config.maxPokemonFriendship)
&& super.canUseOnPokemon(stack, pokemon)
override fun applyToPokemon(
player: ServerPlayer,
stack: ItemStack,
pokemon: Pokemon
): InteractionResultHolder<ItemStack> {
if (!canUseOnPokemon(stack, pokemon)) {
return InteractionResultHolder.fail(stack)
}
val friendshipRaiseAmount = genericRuntime.resolveInt(CobblemonMechanics.berries.friendshipRaiseAmount, pokemon)
val increasedFriendship = pokemon.incrementFriendship(friendshipRaiseAmount)
@@ -49,7 +56,8 @@ class FriendshipRaisingBerryItem(block: BerryBlock, val stat: Stat) : BerryItem(
val decreasedEVs = pokemon.evs.add(stat, -evLowerAmount, ItemEvSource(player, stack, pokemon)) != 0
return if (increasedFriendship || decreasedEVs) {
pokemon.entity?.playSound(CobblemonSounds.BERRY_EAT, 1F, 1F)
pokemon.feedPokemon(1)
if (!player.isCreative) {
stack.shrink(1)
}
@@ -45,17 +45,19 @@ class HealingBerryItem(block: BerryBlock, val amount: () -> ExpressionLike): Ber
}
override fun canUseOnPokemon(stack: ItemStack, pokemon: Pokemon) = !pokemon.isFainted() && !pokemon.isFullHealth()
&& super.canUseOnPokemon(stack, pokemon)
override fun applyToPokemon(
player: ServerPlayer,
stack: ItemStack,
pokemon: Pokemon
): InteractionResultHolder<ItemStack>? {
if (pokemon.isFullHealth() || pokemon.isFainted()) {
if (!canUseOnPokemon(stack, pokemon)) {
return InteractionResultHolder.fail(stack)
}
pokemon.feedPokemon(1)
pokemon.currentHealth = Integer.min(pokemon.currentHealth + genericRuntime.resolveInt(amount(), pokemon), pokemon.maxHealth)
pokemon.entity?.playSound(CobblemonSounds.BERRY_EAT, 1F, 1F)
if (!player.isCreative) {
stack.shrink(1)
}
@@ -64,7 +66,7 @@ class HealingBerryItem(block: BerryBlock, val amount: () -> ExpressionLike): Ber
override fun applyToBattlePokemon(player: ServerPlayer, stack: ItemStack, battlePokemon: BattlePokemon) {
super.applyToBattlePokemon(player, stack, battlePokemon)
battlePokemon.entity?.playSound(CobblemonSounds.BERRY_EAT, 1F, 1F)
battlePokemon.originalPokemon.feedPokemon(1)
}
override fun use(world: Level, user: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> {
@@ -48,20 +48,26 @@ class PPRestoringBerryItem(block: BerryBlock, val amount: () -> ExpressionLike):
override fun canUseOnMove(stack: ItemStack, move: Move) = move.currentPp < move.maxPp
override fun canUseOnPokemon(stack: ItemStack, pokemon: Pokemon) = pokemon.moveSet.any { canUseOnMove(stack, it) }
&& super.canUseOnPokemon(stack, pokemon)
override fun applyToPokemon(player: ServerPlayer, stack: ItemStack, pokemon: Pokemon, move: Move) {
val moveToRecover = pokemon.moveSet.find { it.template == move.template }
if (moveToRecover != null && moveToRecover.currentPp < moveToRecover.maxPp) {
moveToRecover.currentPp = min(moveToRecover.maxPp, moveToRecover.currentPp + genericRuntime.resolveInt(amount(), pokemon))
player.playSound(CobblemonSounds.BERRY_EAT, 1F, 1F)
if (!player.isCreative) {
stack.shrink(1)
if (canUseOnPokemon(stack, pokemon) && canUseOnMove(stack, move)) {
pokemon.feedPokemon(1)
val moveToRecover = pokemon.moveSet.find { it.template == move.template }
if (moveToRecover != null && moveToRecover.currentPp < moveToRecover.maxPp) {
moveToRecover.currentPp =
min(moveToRecover.maxPp, moveToRecover.currentPp + genericRuntime.resolveInt(amount(), pokemon))
if (!player.isCreative) {
stack.shrink(1)
}
}
}
}
override fun applyToBattlePokemon(player: ServerPlayer, stack: ItemStack, battlePokemon: BattlePokemon, move: Move) {
super.applyToBattlePokemon(player, stack, battlePokemon, move)
player.playSound(CobblemonSounds.BERRY_EAT, 1F, 1F)
battlePokemon.originalPokemon.feedPokemon(1)
}
override fun use(world: Level, user: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> {
@@ -48,20 +48,26 @@ class PortionHealingBerryItem(block: BerryBlock, val canCauseConfusion: Boolean,
}
override fun canUseOnPokemon(stack: ItemStack, pokemon: Pokemon) = !pokemon.isFainted() && !pokemon.isFullHealth()
&& super.canUseOnPokemon(stack, pokemon)
override fun applyToPokemon(
player: ServerPlayer,
stack: ItemStack,
pokemon: Pokemon
): InteractionResultHolder<ItemStack>? {
if (pokemon.isFullHealth() || pokemon.isFainted()) {
if (!canUseOnPokemon(stack, pokemon)) {
return InteractionResultHolder.fail(stack)
}
// count these berries as multiple feedings due to the amount they heal
pokemon.feedPokemon(5)
var amount = Integer.min(pokemon.currentHealth + (genericRuntime.resolveFloat(portion(), pokemon) * pokemon.maxHealth).toInt(), pokemon.maxHealth)
CobblemonEvents.POKEMON_HEALED.postThen(PokemonHealedEvent(pokemon, amount, this), { cancelledEvent -> return InteractionResultHolder.fail(stack)}) { event ->
amount = event.amount
}
pokemon.currentHealth = amount
player.playSound(CobblemonSounds.BERRY_EAT, 1F, 1F)
if (!player.isCreative) {
stack.shrink(1)
}
@@ -70,7 +76,7 @@ class PortionHealingBerryItem(block: BerryBlock, val canCauseConfusion: Boolean,
override fun applyToBattlePokemon(player: ServerPlayer, stack: ItemStack, battlePokemon: BattlePokemon) {
super.applyToBattlePokemon(player, stack, battlePokemon)
player.playSound(CobblemonSounds.BERRY_EAT, 1F, 1F)
battlePokemon.originalPokemon.feedPokemon(5)
}
override fun use(world: Level, user: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> {
@@ -39,15 +39,19 @@ class StatusCuringBerryItem(block: BerryBlock, vararg val status: Status): Berry
override fun getShowdownInput(actor: BattleActor, battlePokemon: BattlePokemon, data: String?) = "cure_status${status.takeIf { it.isNotEmpty() }?.let { " ${it.joinToString(separator = " ") { it.showdownName } }" } ?: "" }"
}
override fun canUseOnPokemon(stack: ItemStack, pokemon: Pokemon) = pokemon.status?.let { it.status in status || status.isEmpty() } == true && pokemon.currentHealth > 0
override fun canUseOnPokemon(stack: ItemStack, pokemon: Pokemon) = pokemon.status?.let { it.status in status || status.isEmpty() } == true &&
pokemon.currentHealth > 0 &&
super.canUseOnPokemon(stack, pokemon)
override fun applyToPokemon(
player: ServerPlayer,
stack: ItemStack,
pokemon: Pokemon
): InteractionResultHolder<ItemStack>? {
val currentStatus = pokemon.status?.status
return if (currentStatus != null && (status.isEmpty() || currentStatus in status)) {
return if (canUseOnPokemon(stack, pokemon)) {
pokemon.feedPokemon(1)
pokemon.status = null
pokemon.entity?.playSound(CobblemonSounds.BERRY_EAT, 1F, 1F)
if (!player.isCreative) {
stack.shrink(1)
@@ -60,7 +64,7 @@ class StatusCuringBerryItem(block: BerryBlock, vararg val status: Status): Berry
override fun applyToBattlePokemon(player: ServerPlayer, stack: ItemStack, battlePokemon: BattlePokemon) {
super.applyToBattlePokemon(player, stack, battlePokemon)
battlePokemon.entity?.playSound(CobblemonSounds.BERRY_EAT, 1F, 1F)
battlePokemon.originalPokemon.feedPokemon(1)
}
override fun use(world: Level, user: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> {
@@ -25,8 +25,8 @@ object BaitSeasoningProcessor : SeasoningProcessor {
// From seasonings with baitEffects
val seasoningData = Seasonings.getFromItemStack(seasoningStack)
if (seasoningData != null && seasoningData.baitEffects.isNotEmpty()) {
val itemId = seasoningStack.item.builtInRegistryHolder().key().location()
if (seasoningData != null && !seasoningData.baitEffects.isNullOrEmpty()) {
val itemId = seasoningStack.itemHolder.unwrapKey().get().location()
val id = ResourceLocation("seasonings", itemId.path)
baitIdentifiers.add(id)
}
@@ -23,7 +23,7 @@ enum class CookingPotBookCategory(
) : StringRepresentable {
FOODS("foods", 0),
MEDICINES("medicines", 1),
BAITS("baits", 2),
COMPLEX_DISHES("complex_dishes", 2),
MISC("misc", 3);
override fun getSerializedName() = categoryName
@@ -37,22 +37,20 @@ class CookingPotShapelessRecipe(
override fun canCraftInDimensions(width: Int, height: Int) = width * height >= ingredients.size
override fun matches(input: CraftingInput, level: Level): Boolean {
// Match ingredients in any order
val matchedIngredients = mutableListOf<Ingredient>()
val remaining = ingredients.toMutableList()
for (item in input.items()) {
if (item.isEmpty) continue
// Find matching ingredient
val matchingIngredient = ingredients.find { it.test(item) && it !in matchedIngredients }
if (matchingIngredient != null) {
matchedIngredients.add(matchingIngredient)
val matchIndex = remaining.indexOfFirst { it.test(item) }
if (matchIndex != -1) {
remaining.removeAt(matchIndex)
} else {
return false
}
}
val matches = matchedIngredients.size == ingredients.size
return matches
return remaining.isEmpty()
}
class Serializer : RecipeSerializer<CookingPotShapelessRecipe> {
@@ -21,8 +21,7 @@ object MobEffectSeasoningProcessor : SeasoningProcessor {
override fun apply(result: ItemStack, seasoning: List<ItemStack>) {
val allEffects: List<SerializableMobEffectInstance> = seasoning
.mapNotNull { Seasonings.getFromItemStack(it)?.mobEffects }
.flatMap { it.appliedEffects }
.flatMap { Seasonings.getMobEffectsFromItemStack(it) }
val mergedEffects = MobEffectUtils.mergeEffects(allEffects).map { it.toInstance() }

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