forked from mirrors/cobblemon
Compare commits
293
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e1694cc8d | ||
|
|
bb6e5607f1 | ||
|
|
4603f5ac7a | ||
|
|
c683294227 | ||
|
|
f29c2343e6 | ||
|
|
3a2da5a87d | ||
|
|
1c13ab24e8 | ||
|
|
380cf0ea71 | ||
|
|
8ded7334bb | ||
|
|
0ac34bf1a4 | ||
|
|
9716ea0806 | ||
|
|
ffea94f3b2 | ||
|
|
3061ba69e2 | ||
|
|
741e38d606 | ||
|
|
02f2c072c9 | ||
|
|
4722defec2 | ||
|
|
c54849f415 | ||
|
|
d44e625e9d | ||
|
|
6a74d5982a | ||
|
|
03905c77d2 | ||
|
|
da9558f5f9 | ||
|
|
5c65dffac8 | ||
|
|
b67b37c99e | ||
|
|
fbb7ee31c5 | ||
|
|
d3ef26976b | ||
|
|
dac01a3730 | ||
|
|
ddb05768e4 | ||
|
|
5374940d24 | ||
|
|
1fd6a660dd | ||
|
|
fd45934775 | ||
|
|
5e0ffd8bd3 | ||
|
|
b4b315360d | ||
|
|
a44c70648b | ||
|
|
c41b0cdca2 | ||
|
|
88f743ee48 | ||
|
|
15b9afacf8 | ||
|
|
babcf7430b | ||
|
|
3439ca2011 | ||
|
|
6539f3dea1 | ||
|
|
1677598ec0 | ||
|
|
fcde1eb171 | ||
|
|
a30a5644df | ||
|
|
a5672a38b9 | ||
|
|
3b14ea319b | ||
|
|
57e2765f90 | ||
|
|
a090c90831 | ||
|
|
5dcc9ba46d | ||
|
|
f8e4ea70df | ||
|
|
5dc1c749f3 | ||
|
|
3accfa29b7 | ||
|
|
1af1569f63 | ||
|
|
dbd2f5d340 | ||
|
|
86bf413f00 | ||
|
|
f13e7d03a1 | ||
|
|
4a025ac65e | ||
|
|
8a0c98c9ab | ||
|
|
36c5fe2419 | ||
|
|
0c79d102cf | ||
|
|
b14b25ae19 | ||
|
|
bd8f644b9a | ||
|
|
7c84cdb05d | ||
|
|
0293c0e811 | ||
|
|
125975e25f | ||
|
|
c7223ae898 | ||
|
|
baf766298a | ||
|
|
83580e7e8b | ||
|
|
38f5c12dce | ||
|
|
e6a9a27692 | ||
|
|
7ff413c401 | ||
|
|
90335ccb2d | ||
|
|
603f99e5f3 | ||
|
|
1b53a127af | ||
|
|
9679929694 | ||
|
|
fd6fb5db47 | ||
|
|
a80ada8590 | ||
|
|
c4a4b9cb86 | ||
|
|
5af96e75eb | ||
|
|
023a01befd | ||
|
|
4912a89e6e | ||
|
|
f2e10d1b01 | ||
|
|
25eb197491 | ||
|
|
8de233b572 | ||
|
|
83972ca9e6 | ||
|
|
35e2190cae | ||
|
|
b966cd2db0 | ||
|
|
3e492541cc | ||
|
|
cc2022ed59 | ||
|
|
298fdf45d5 | ||
|
|
4e9541d7f5 | ||
|
|
1dab5b8c7c | ||
|
|
2875e9efa9 | ||
|
|
2b42fff66b | ||
|
|
643a33a643 | ||
|
|
e348c48eff | ||
|
|
473f58cab9 | ||
|
|
987b8619a3 | ||
|
|
87632941e8 | ||
|
|
041acab7ca | ||
|
|
b3de7d1c1a | ||
|
|
afcb2dd77c | ||
|
|
a7bd6d2bf4 | ||
|
|
b096cb60a3 | ||
|
|
7f1c0c3d4c | ||
|
|
a4f0bf9d2f | ||
|
|
f46c748e9f | ||
|
|
065479c58c | ||
|
|
eee3d46a2d | ||
|
|
d7b43fcbfc | ||
|
|
c82e011f73 | ||
|
|
bf1b996962 | ||
|
|
fc46cdcda0 | ||
|
|
4e7b029afb | ||
|
|
5430f02770 | ||
|
|
6a51abe3ee | ||
|
|
008652b442 | ||
|
|
c1e088eb33 | ||
|
|
1669a2f428 | ||
|
|
2ba63f4641 | ||
|
|
41b9e9001d | ||
|
|
679920b786 | ||
|
|
f058d6e635 | ||
|
|
f9377b7489 | ||
|
|
e59b5094ad | ||
|
|
c8ec4e6f1f | ||
|
|
a2fef9f1be | ||
|
|
3d4df2c098 | ||
|
|
7557cab03d | ||
|
|
f1018b1da9 | ||
|
|
cbcc6abe40 | ||
|
|
5af4e3bddb | ||
|
|
64c9d47b0f | ||
|
|
1f519cae50 | ||
|
|
c0d2803f61 | ||
|
|
e249d5238f | ||
|
|
e32e505e95 | ||
|
|
7642a84108 | ||
|
|
b6e20d69cc | ||
|
|
9d04e20a30 | ||
|
|
d2624813f7 | ||
|
|
a2789e70ed | ||
|
|
f6f607904e | ||
|
|
22ea0296c0 | ||
|
|
24ae2f5324 | ||
|
|
9284497cec | ||
|
|
36cb12952a | ||
|
|
2700dc7f39 | ||
|
|
c9d27d46b6 | ||
|
|
b68756378c | ||
|
|
1ff1880951 | ||
|
|
fea2d32de8 | ||
|
|
e3e6d4452c | ||
|
|
f687e8e658 | ||
|
|
2413981f6f | ||
|
|
4ac244003a | ||
|
|
9ac92a084f | ||
|
|
aacbcb1f28 | ||
|
|
39ff7e418c | ||
|
|
58498a091d | ||
|
|
a806f77aff | ||
|
|
60ae9e3a55 | ||
|
|
03ddc35d36 | ||
|
|
1b8e9b9b36 | ||
|
|
e52ac956f0 | ||
|
|
4bfe147730 | ||
|
|
493664bef2 | ||
|
|
981e3e5efc | ||
|
|
963614f5fa | ||
|
|
7fdb70e3c3 | ||
|
|
37e8f0eab6 | ||
|
|
a6d17659f8 | ||
|
|
05edafa5c4 | ||
|
|
997a6de23b | ||
|
|
c15350e647 | ||
|
|
f9e5784b10 | ||
|
|
cea600617e | ||
|
|
c948b6d885 | ||
|
|
5508980384 | ||
|
|
e39dee01e5 | ||
|
|
a81412e176 | ||
|
|
09f9dd91aa | ||
|
|
2efc3c72ac | ||
|
|
a92fb03093 | ||
|
|
ad5847983f | ||
|
|
bb25300513 | ||
|
|
de33804eb4 | ||
|
|
de042f71dd | ||
|
|
5a1e85bbf4 | ||
|
|
3489e0df74 | ||
|
|
afe8eec343 | ||
|
|
2f4b861f98 | ||
|
|
a7022268c1 | ||
|
|
3082600e11 | ||
|
|
a348cfd338 | ||
|
|
35bfc7bf6c | ||
|
|
ae87c3dd84 | ||
|
|
d944803571 | ||
|
|
2c3b474c01 | ||
|
|
c2538560e1 | ||
|
|
d10c058c74 | ||
|
|
d1b6d95d57 | ||
|
|
977387a184 | ||
|
|
69bbc09d6a | ||
|
|
46b04ad462 | ||
|
|
a1b6fd3d1a | ||
|
|
fb9ed28b74 | ||
|
|
4bfc2a97dd | ||
|
|
a2cf66fbc1 | ||
|
|
5bda81a5c4 | ||
|
|
32ccd15121 | ||
|
|
a51cda998b | ||
|
|
9dc14eff7c | ||
|
|
4b1690c62e | ||
|
|
d02251b67d | ||
|
|
54c8cbfbe8 | ||
|
|
2d8ce63dcd | ||
|
|
1a92af2cd6 | ||
|
|
7e088bdeec | ||
|
|
6142049d51 | ||
|
|
d031f339c6 | ||
|
|
3ddbc9b313 | ||
|
|
dd2c1ddbc6 | ||
|
|
badd7e8c2e | ||
|
|
d7c4380cc2 | ||
|
|
7fc26ab939 | ||
|
|
37f430ddf7 | ||
|
|
f0a2182149 | ||
|
|
9605775f1e | ||
|
|
f251bc20c1 | ||
|
|
a02b3d74b8 | ||
|
|
7f3a4c20b3 | ||
|
|
e76999c611 | ||
|
|
c2771bcb95 | ||
|
|
5b858b5982 | ||
|
|
7082220e29 | ||
|
|
1a87830a52 | ||
|
|
9eff6dcced | ||
|
|
52690c3aa6 | ||
|
|
8f0780c5f6 | ||
|
|
91864b13d0 | ||
|
|
465a7fbe73 | ||
|
|
6b5abfa319 | ||
|
|
39197adebb | ||
|
|
4b1e192705 | ||
|
|
a3fa3d4b82 | ||
|
|
c5ad7a0fb7 | ||
|
|
d17f94c0d6 | ||
|
|
adc8e9921a | ||
|
|
5e5473acae | ||
|
|
3509944f1a | ||
|
|
398bff4c15 | ||
|
|
e0655df5f0 | ||
|
|
8a5489f805 | ||
|
|
9174aac64c | ||
|
|
3cdc4c7d97 | ||
|
|
9ce6acc44d | ||
|
|
f170ed726b | ||
|
|
979d6f02a6 | ||
|
|
3fbe80cb1a | ||
|
|
8fdb32273d | ||
|
|
d5c9d8d5b1 | ||
|
|
752c9c234e | ||
|
|
b7b6185ada | ||
|
|
211b886bf3 | ||
|
|
36d1008f29 | ||
|
|
1441625a6b | ||
|
|
3859eb574e | ||
|
|
b4a29214a7 | ||
|
|
6ca16a0161 | ||
|
|
2f31db2232 | ||
|
|
ed0e0fc65a | ||
|
|
c5dcfc57ea | ||
|
|
9997a5c4b4 | ||
|
|
2b70e5614c | ||
|
|
3b7d56ff45 | ||
|
|
589c5381e2 | ||
|
|
4539703497 | ||
|
|
0152bc2e32 | ||
|
|
e175c44118 | ||
|
|
50c567d264 | ||
|
|
d4964e41d3 | ||
|
|
41b1c169c3 | ||
|
|
49e2001004 | ||
|
|
66393f9293 | ||
|
|
7b3b6146a1 | ||
|
|
e1452244c2 | ||
|
|
8913d3df11 | ||
|
|
3a55965704 | ||
|
|
5358414a0c | ||
|
|
d691373919 | ||
|
|
038f13e6fa | ||
|
|
dd288a3b0c | ||
|
|
7112c74bf2 | ||
|
|
faa1906290 |
@@ -0,0 +1,12 @@
|
||||
### Additions
|
||||
- Added functionality for marks.
|
||||
- Added data for all marks and all title ribbons from Pokémon.
|
||||
- Added marks screen within the summary UI.
|
||||
- Added `/givemark <player> <partySlot> <mark>` command to give a mark to a Pokémon.
|
||||
- Added `/takemark <player> <partySlot> <mark>` command to remove a mark from a Pokémon.
|
||||
- Added `/giveallmarks <player> <partySlot>` command to give all available marks to a Pokémon.
|
||||
|
||||
### MoLang & Datapacks
|
||||
- Added Flows for `bobber_spawn_pokemon_post`, `pokemon_entity_spawn`, and `fossil_revived`.
|
||||
- Added `marks` datapack folder.
|
||||
- Added MoLang functions `entity.is_in_rain`, `pokemon.add_marks(Mark...)`, `pokemon.add_potential_marks(Mark...)`, `pokemon.add_marks_with_chance(Mark...)`, `pokemon.apply_potential_marks`, and `world.is_snowing_at(x, y, z)`.
|
||||
+17
-12
@@ -1,16 +1,21 @@
|
||||
### Additions
|
||||
|
||||
- Added ability to rename PC Boxes by clicking on the name of a box
|
||||
- Added ability to set wallpapers per PC Box by clicking the button right to the box name
|
||||
- Added sorting by Name, Level and type by clicking the button left of the box name (shift reverses order)
|
||||
- Added filtering/search functionality in PC UI, which also supports pokemon properties (e.g. shiny=yes shows all shinies)
|
||||
- Added /changewallpaper <player> <boxNumber> <wallpaper> command to change a wallpaper through commands
|
||||
- Added /renamebox <player> <boxNumber> <name> command to rename a PC box through commands
|
||||
- Added ability to rename PC Boxes by clicking on the name of a box.
|
||||
- Added box options buttons for boxes, toggleable by clicking the right icon button in the bottom bar.
|
||||
- The button on the right allows for changing the box wallpaper.
|
||||
- The option buttons on the left side allow for sorting the box by name, level, Pokédex number, gender, and type. Shift clicking allows for sorting in reverse order.
|
||||
- Added filter functionality in PC UI, which supports `PokemonProperties` (e.g. `shiny=yes` shows all shiny Pokémon).
|
||||
- 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.
|
||||
|
||||
### Developer
|
||||
- Added `RenamePCBoxEvent.Pre` and `RenamePCBoxEvent.Post` events to prevent players from renaming a box or changing their input.
|
||||
- Added `ChangePCBoxWallpaperEvent.Pre` and `ChangePCBoxWallpaperEvent.Post` to prevent players from changing wallpapers or changing their selection.
|
||||
- Added `WallpaperCollectionEvent` which gets called when clients connect to a server, allowing the server to handle which of the client-found wallpapers it's allowed to move (collected wallpapers can be removed for example to make it "vanish" client-side).
|
||||
- Added `WallpaperUnlockedEvent`.
|
||||
- Renamed `SetPCBoxPokemonPacket` and the respective handler to `SetPCBoxPacket`.
|
||||
|
||||
- Added RenamePCBoxEvent.Pre/Post events to prevent players from renaming a box or change their input
|
||||
- Added ChangePCBoxWallpaperEvent.Pre/Post to prevent players from changing to a wallpaper or change their selection
|
||||
- Added WallpaperCollectionEvent which gets called when clients connect to a server, allowing the server which of the clients found wallpapers it's allowed to move (collected wallpapers can be removed for example to make it "vanish" client-side)
|
||||
- Wallpapers are loaded from `assets/<namespace>/textures/gui/pc/wallpaper/` and all wallpapers in this folder are available to the client to choose by default
|
||||
- Renamed `SetPCBoxPokemonPacket` and the respective handler to `SetPCBoxPacket`
|
||||
### Addons
|
||||
- Added Flows for `player_tick_pre`, `player_tick_post`, `advancement_earned`, `right_clicked_entity`, `right_clicked_block`, `player_died`, `wallpaper_unlocked`.
|
||||
- Flows now have `q.` for any context properties in addition to the `c.` properties, making it a bit more convenient when calling other scripts from Flows.
|
||||
- Added `unlockable_pc_box_wallpapers` datapack folder for wallpapers that need to be unlocked before they're usable.
|
||||
- Wallpapers are loaded from `assets/<namespace>/textures/gui/pc/wallpaper/` and all wallpapers in this folder are available to the client to choose by default.
|
||||
|
||||
+2
-1
@@ -1,3 +1,4 @@
|
||||
### Additions
|
||||
|
||||
- Added the ability to ride Pokémon by shift-right clicking and selecting the ride option.
|
||||
- Added the ability to ride Pokémon by shift-right clicking and selecting the ride option.
|
||||
- Added /calculateseatpositions command for approximating hitbox locations for riding configurations.
|
||||
+123
-3
@@ -2,6 +2,7 @@
|
||||
## [1.7.0 (Month xth, 2025)](#1-7-0)
|
||||
|
||||
### Additions
|
||||
- Added `/spectateBattle <player>` command to spectate battles without having to manually walk up to the target.
|
||||
- Added an in-game configuration screen, allowing all settings from `main.json` to be edited directly in-game.
|
||||
- Added `/cobblemonconfig reload` command to reload `main.json` configuration. **Note:** Some settings require a server restart to take effect; use this command cautiously.
|
||||
- Added `blacklisted_items_to_hold` and `whitelisted_items_to_hold` tags to allow for controlling which items players can give to their Pokémon. If the whitelist is empty, it will consider all item as allowed (unless they are in the blacklist).
|
||||
@@ -10,9 +11,104 @@
|
||||
- Added cosmetic item functionality for Pokémon. Certain cosmetic items can be given to applicable Pokémon via the interact menu.
|
||||
- Added the various log blocks as cosmetic items for Timburr and Komala.
|
||||
- Added `visibility/hidden`, `visibility/hat` and `visibility/face` tags to control where and how certain items are rendered.
|
||||
- Added Pokémon markings, toggleable within the summary.
|
||||
- Added `/boxcount` command to change PC boxes amount
|
||||
- Added cosmetics for Gurdurr, Conkeldurr, Squirtle Line, Sneasler, Sandile line, Treecko line, Braixen, Delphox, and Dragonite.
|
||||
- Added cosmetics for Gurdurr, Conkeldurr, Squirtle Line, Sneasler, Sandle line, Treecko line, Braixen, Delphox, and Dragonite.
|
||||
- Added `/transformmodelpart (position|rotation|scale) <modelPart> <transform: x y z>` command that can add transformations to a pokemon's model part.
|
||||
- The player executing the command must be facing the target pokemon entity. Transformations are not persistent and will revert when resources are reloaded.
|
||||
|
||||
### Pokémon Added
|
||||
|
||||
#### Gen 2
|
||||
- Dunsparce
|
||||
|
||||
#### Gen 3
|
||||
- Spoink
|
||||
- Grumpig
|
||||
- Snorunt
|
||||
- Glalie
|
||||
|
||||
#### Gen 4
|
||||
- Bronzor
|
||||
- Bronzong
|
||||
- Croagunk
|
||||
- Froslass
|
||||
|
||||
#### Gen 5
|
||||
- Pansage
|
||||
- Simisage
|
||||
- Pansear
|
||||
- Simisear
|
||||
- Panpour
|
||||
- Simipour
|
||||
- Munna
|
||||
- Musharna
|
||||
- Blitzle
|
||||
- Zebstrika
|
||||
- Trubbish
|
||||
- Garbodor
|
||||
|
||||
#### Gen 6
|
||||
- Noibat
|
||||
- Noivern
|
||||
|
||||
#### Gen 7
|
||||
- Drampa
|
||||
|
||||
#### Gen 8
|
||||
- Silicobra
|
||||
- Sandaconda
|
||||
|
||||
#### Gen 9
|
||||
- Smoliv
|
||||
- Dolliv
|
||||
- Arboliva
|
||||
- Orthworm
|
||||
- Dudunsparce
|
||||
|
||||
### Animation updates for the following Pokémon
|
||||
- Garchomp
|
||||
- Tropius
|
||||
- Torpius
|
||||
- Nosepass
|
||||
- Probopass
|
||||
- Sneasler
|
||||
- Braixen
|
||||
- Delphox
|
||||
|
||||
### Model updates for the following Pokémon
|
||||
- Gyarados
|
||||
- Dragonite
|
||||
- Eevee
|
||||
- Vaporeon
|
||||
- Jolteon
|
||||
- Flareon
|
||||
- Espeon
|
||||
- Umbreon
|
||||
- Leafeon
|
||||
- Glaceon
|
||||
- Sylveon
|
||||
- Treecko
|
||||
- Grovyle
|
||||
- Sceptile
|
||||
- Honchkrow
|
||||
- Garchomp
|
||||
- Pidgeot
|
||||
- Nosepass
|
||||
- Probopass
|
||||
- Magnezone
|
||||
- Metagross
|
||||
|
||||
### Changes
|
||||
- Renamed `chargeGainedPerTick` config to `secondsToChargeHealingMachine`.
|
||||
- Made Blocks of Gold count as Big Nuggets when held by a Pokémon (for Fling functionality)
|
||||
- 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.
|
||||
- Improved the performance of saving Pokédex and player data.
|
||||
- Pokémon hitbox now scales with entity attribute `generic.scale`.
|
||||
- Removed Shulker aspect and replaced it with cosmetic_item-shulker_shell.
|
||||
- Shulker shell Forretress is now a cosmetic rather than a special evo and thus all shulker Forretress will revert back to normal until a shulker shell is put in their cosmetic slot.
|
||||
|
||||
### Fixes
|
||||
- Fixed Particles sometimes facing the wrong direction (looking at you, Swords Dance)
|
||||
@@ -34,6 +130,13 @@
|
||||
- Fixed owned Pokémon sometimes being un-interactable after the player relogs fast
|
||||
- Fixed field name in evolution requirements for Spewpa Pokeball.
|
||||
- Fixed LevelUpCriterion logic to correctly check that the Pokémon is a preEvo.
|
||||
- Fixed `hide_additional_tooltip` vanilla flag not properly hiding tooltips on pokerod and bait items
|
||||
- Removed a number of scenarios in which a Pokémon battle may send out a Pokémon into collision geometry.
|
||||
- Fixed NPCs using Pokémon outside of their pool when a Pokémon name had a typo.
|
||||
- Fixed an issue with datapacked species features not being applied properly when relogging.
|
||||
- Fixed Pokémon marked as silent still playing shiny sounds and effects.
|
||||
- Fixed an issue with newer versions of Fabric API where underground Pokémon were spawning in The End.
|
||||
- Fixed spawning not working well when you're at high points surrounded by lower altitude spawning areas, such as flying.
|
||||
|
||||
### Developer
|
||||
- A finished battle now has winners and losers set inside of `PokemonBattle` instead of them always being empty.
|
||||
@@ -44,9 +147,14 @@
|
||||
- Removed the NbtItemPredicate class, all the mod usages now use the vanilla item predicate solution, this causes breaking changes on Fossil, HeldItemRequirement & ItemInteractionEvolution
|
||||
- Renamed Cobblemon's creative tabs to start with "Cobblemon: " to distinguish Cobblemon's tabs from tabs for other mods.
|
||||
- Various items now have a rarity value.
|
||||
|
||||
### Changes
|
||||
- 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.
|
||||
- Reworked observable handling in `Pokemon.kt` to cut down on RAM usage and clarify the file.
|
||||
- Note: This will break mods that used our observable functionality there or in MoveSet, IVs, EVs, or BenchedMoves.
|
||||
- Using `Pokemon#onChange()` is now the way to mark a Pokémon as needing a save.
|
||||
- Using `[Pokemon].changeObservable` is now the way to get an `Observable` for any save-worthy changes.
|
||||
- Updated NPCEntity beam positioning to properly account for the baseScale property.
|
||||
- Updated NPCEntity pokeball throw positioning to properly account for the baseScale property.
|
||||
- Fixed `[Pokemon].copyFrom` error causing forms, IVs, and EVs to not be applied properly when using `[Pokemon].loadFromJSON` or `[Pokemon].loadFromNBT`
|
||||
- Added new item class, `WearableItem`. Instances of this class should have a corresponding 3D model. These models render when the items display context is `HEAD`.
|
||||
|
||||
### 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)
|
||||
@@ -54,6 +162,17 @@
|
||||
- The `itemCondition` for a held item evolution requirement
|
||||
- The `fossils` for a fossil entry
|
||||
- Added MoLang flows for `poke_ball_capture_calculated`, `evolution_tested`, `evolution_accepted`, `evolution_completed`
|
||||
- Added `interpolate` boolean property to animated textures to allow gradual colour changes between frames.
|
||||
- Fixed species additions not being capable of changing implemented status.
|
||||
- Added support for action effects that are triggered by `|-activate|` Showdown instructions. `activate_{effect_id}` is the syntax.
|
||||
- Added MoLang functions for rendering items `render_item(item_id, locator_name)` and `clear_items()`.
|
||||
- Fixed location spawn filter components causing crashes
|
||||
- Added `pokemon` as an available MoLang function for the `battleActor` functions.
|
||||
- Added `spawn_pokemon` as an available MoLang function for the `worldHolder` functions.
|
||||
- Added `attempt_wild_battle` as an available MoLang function.
|
||||
- Added `pokemon` as an available Molang function for the `battleActor` functions.
|
||||
- Fixed `heldItem` property inside spawn files not working and causing crashes
|
||||
- Fixed `spawn_bedrock_particles` MoLang causing crashes when used in a server environment
|
||||
|
||||
## [1.6.1 (January 26th, 2025)](#1-6-1)
|
||||
|
||||
@@ -159,6 +278,7 @@
|
||||
- Geodude
|
||||
- Graveler
|
||||
- Golem
|
||||
- Magnezone
|
||||
|
||||
### Cry updates for the following Pokémon
|
||||
- Sceptile
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common;
|
||||
|
||||
import com.cobblemon.mod.common.api.orientation.OrientationController;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.util.SmoothDouble;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public interface OrientationControllable {
|
||||
OrientationController getOrientationController();
|
||||
}
|
||||
@@ -1,124 +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;
|
||||
|
||||
import net.minecraft.util.Mth;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public interface Rollable {
|
||||
|
||||
// From Minecraft's Camera
|
||||
Vector3f FORWARDS = new Vector3f(0.0F, 0.0F, -1.0F);
|
||||
Vector3f UP = new Vector3f(0.0F, 1.0F, 0.0F);
|
||||
Vector3f LEFT = new Vector3f(-1.0F, 0.0F, 0.0F);
|
||||
|
||||
@Nullable Matrix3f getOrientation();
|
||||
|
||||
Rollable updateOrientation(Function<Matrix3f, Matrix3f> update);
|
||||
|
||||
/**
|
||||
* @return The forward vector (0, 0, -1) after the rotation matrix is applied.
|
||||
*/
|
||||
default Vector3f getForwardVector() {
|
||||
if (getOrientation() == null) return new Vector3f(FORWARDS);
|
||||
return getOrientation().transform(FORWARDS, new Vector3f());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The left vector (-1, 0, 0) after the rotation matrix is applied.
|
||||
*/
|
||||
default Vector3f getLeftVector() {
|
||||
if (getOrientation() == null) return new Vector3f(LEFT);
|
||||
return getOrientation().transform(LEFT, new Vector3f());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The up vector (0, 1, 0) after the rotation matrix is applied.
|
||||
*/
|
||||
default Vector3f getUpVector() {
|
||||
if (getOrientation() == null) return new Vector3f(UP);
|
||||
return getOrientation().transform(UP, new Vector3f());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param yaw change in degrees
|
||||
* @return The Rollable object this method was called on
|
||||
*/
|
||||
|
||||
default Rollable rotateYaw(float yaw) {
|
||||
return updateOrientation(prev -> prev.rotateY((float) Math.toRadians(-yaw)));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param pitch change in degrees
|
||||
* @return The Rollable object this method was called on
|
||||
*/
|
||||
|
||||
default Rollable rotatePitch(float pitch) {
|
||||
return updateOrientation(prev -> prev.rotateX((float) Math.toRadians(-pitch)));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param roll change in degrees
|
||||
* @return The Rollable object this method was called on
|
||||
*/
|
||||
default Rollable rotateRoll(float roll) {
|
||||
return updateOrientation(prev -> prev.rotateZ((float) Math.toRadians(-roll)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Order matters when rotating an object. If you need a different order, individually call rotateYaw, rotatePitch, and rotateRoll
|
||||
*
|
||||
* @param yaw change in degrees
|
||||
* @param pitch change in degrees
|
||||
* @param roll change in degrees
|
||||
* @return The Rollable object this method was called on
|
||||
*/
|
||||
default Rollable rotate(float yaw, float pitch, float roll) {
|
||||
return rotateYaw(yaw).rotatePitch(pitch).rotateRoll(roll);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The pitch angle in degrees as a float.
|
||||
* Value is between [-180, 180] with 0 being South, 90 being West, 180/-180 being North, and -90 being East
|
||||
*/
|
||||
default float getYaw() {
|
||||
return (float) Mth.wrapDegrees(-Math.toDegrees(FORWARDS.angleSigned(getForwardVector(), UP)) + 180);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The yaw angle in degrees as a float.
|
||||
* Value is between [-90, 90] with 0 being straight forward, 90 being straight down, and -90 being straight up
|
||||
*/
|
||||
default float getPitch() {
|
||||
return (float) Mth.wrapDegrees(-Math.toDegrees(Math.asin(getForwardVector().y)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The roll angle in degrees as a float.
|
||||
* Value is between [-180, 180] with 0 being no roll (normal), 90 being 90 degrees CW, 180/-180 being 180 degrees CW/CCW, and -90 being 90 degrees CCW
|
||||
*/
|
||||
default float getRoll() {
|
||||
//Cobblemon.LOGGER.info("{}", (float) Math.toDegrees(getUpVector().angleSigned(UP, getForwardVector())));
|
||||
return (float) Mth.wrapDegrees(-Math.toDegrees(getUpVector().angleSigned(UP, getForwardVector())));
|
||||
}
|
||||
|
||||
boolean shouldRoll();
|
||||
|
||||
void clearRotation();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(LocalPlayer.class)
|
||||
public abstract class LocalPlayerMixin {
|
||||
@Shadow
|
||||
private float jumpRidingScale;
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.platform.events.PlatformEvents;
|
||||
import com.cobblemon.mod.common.platform.events.ServerPlayerEvent;
|
||||
import net.minecraft.advancements.AdvancementHolder;
|
||||
import net.minecraft.server.PlayerAdvancements;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(PlayerAdvancements.class)
|
||||
public class PlayerAdvancementsMixin {
|
||||
@Shadow private ServerPlayer player;
|
||||
|
||||
@Inject(method = "award", at = @At(value = "INVOKE", target = "Lnet/minecraft/advancements/AdvancementRewards;grant(Lnet/minecraft/server/level/ServerPlayer;)V"))
|
||||
private void cobblemon$award(AdvancementHolder advancement, String criterionKey, CallbackInfoReturnable<Boolean> cir) {
|
||||
PlatformEvents.SERVER_PLAYER_ADVANCEMENT_EARNED.emit(new ServerPlayerEvent.AdvancementEarned(player, advancement));
|
||||
}
|
||||
}
|
||||
@@ -10,14 +10,17 @@ package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon;
|
||||
import com.cobblemon.mod.common.CobblemonItems;
|
||||
import com.cobblemon.mod.common.Rollable;
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents;
|
||||
import com.cobblemon.mod.common.api.events.item.LeftoversCreatedEvent;
|
||||
import com.cobblemon.mod.common.api.orientation.OrientationController;
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies;
|
||||
import com.cobblemon.mod.common.api.riding.Rideable;
|
||||
import com.cobblemon.mod.common.api.riding.Seat;
|
||||
import com.cobblemon.mod.common.api.storage.party.PartyStore;
|
||||
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore;
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags;
|
||||
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate;
|
||||
import com.cobblemon.mod.common.client.render.MatrixWrapper;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.cobblemon.mod.common.pokedex.scanner.PokedexEntityData;
|
||||
import com.cobblemon.mod.common.pokedex.scanner.ScannableEntity;
|
||||
@@ -31,25 +34,26 @@ import com.cobblemon.mod.common.util.DataKeys;
|
||||
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.*;
|
||||
import net.minecraft.world.entity.item.ItemEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.food.FoodProperties;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
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.Nullable;
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Vector3f;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
@@ -58,15 +62,11 @@ 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.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Mixin(Player.class)
|
||||
public abstract class PlayerMixin extends LivingEntity implements Rollable, ScannableEntity {
|
||||
public abstract class PlayerMixin extends LivingEntity implements ScannableEntity, OrientationControllable {
|
||||
|
||||
@Shadow public abstract CompoundTag getShoulderEntityLeft();
|
||||
|
||||
@@ -84,7 +84,10 @@ public abstract class PlayerMixin extends LivingEntity implements Rollable, Scan
|
||||
|
||||
@Shadow public abstract void displayClientMessage(Component message, boolean overlay);
|
||||
|
||||
@Unique private Matrix3f orientation = null;
|
||||
@Shadow @Final private static Map<Pose, EntityDimensions> POSES;
|
||||
@Shadow @Final public static EntityDimensions STANDING_DIMENSIONS;
|
||||
@Unique
|
||||
private final OrientationController cobblemon$orientationController = new OrientationController(this);
|
||||
|
||||
protected PlayerMixin(EntityType<? extends LivingEntity> p_20966_, Level p_20967_) {
|
||||
super(p_20966_, p_20967_);
|
||||
@@ -112,6 +115,19 @@ public abstract class PlayerMixin extends LivingEntity implements Rollable, Scan
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "tick", at = @At("HEAD"))
|
||||
private void cobblemon$updateRenderOrientation(CallbackInfo ci) {
|
||||
this.cobblemon$orientationController.tick();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityDimensions getDefaultDimensions(Pose pose) {
|
||||
if (this.getVehicle() instanceof PokemonEntity) {
|
||||
return STANDING_DIMENSIONS;
|
||||
}
|
||||
return POSES.getOrDefault(pose, STANDING_DIMENSIONS);
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "removeEntitiesOnShoulder",
|
||||
at = @At(
|
||||
@@ -238,7 +254,7 @@ public abstract class PlayerMixin extends LivingEntity implements Rollable, Scan
|
||||
|
||||
@Override
|
||||
public void absMoveTo(double x, double y, double z, float yaw, float pitch) {
|
||||
if (shouldRoll()) {
|
||||
if (cobblemon$orientationController.getActive()) {
|
||||
this.absMoveTo(x, y, z);
|
||||
this.setYRot(yaw % 360.0f);
|
||||
this.setXRot(pitch % 360.0f);
|
||||
@@ -251,65 +267,58 @@ public abstract class PlayerMixin extends LivingEntity implements Rollable, Scan
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "tick", at = @At("HEAD"))
|
||||
public void tick(CallbackInfo ci) {
|
||||
if (this.shouldRoll()) {
|
||||
// Proof of concept for yaw movement
|
||||
if (this.getMainHandItem().is(Items.STICK)) {
|
||||
this.rotateYaw(2);
|
||||
}
|
||||
else if (this.getMainHandItem().is(Items.BLAZE_ROD)) {
|
||||
this.rotateYaw(-2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Matrix3f getOrientation() {
|
||||
return orientation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rollable updateOrientation(Function<Matrix3f, Matrix3f> update) {
|
||||
if (!shouldRoll()) return this;
|
||||
|
||||
if (this.orientation == null) {
|
||||
this.orientation = new Matrix3f();
|
||||
this.rotate(getYRot() - 180, getXRot(), 0);
|
||||
}
|
||||
this.orientation = update.apply(this.orientation);
|
||||
setYRot(this.getYaw());
|
||||
setXRot(this.getPitch());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRoll() {
|
||||
LocalPlayer player = Minecraft.getInstance().player;
|
||||
if (!this.equals(player)) return false;
|
||||
if (player.isHolding(Items.DIAMOND)) return true;
|
||||
|
||||
var playerVehicle = player.getVehicle();
|
||||
if (playerVehicle == null) return false;
|
||||
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return false;
|
||||
|
||||
return pokemonEntity.getRiding().shouldRoll(pokemonEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearRotation() {
|
||||
this.orientation = null;
|
||||
}
|
||||
|
||||
@WrapOperation(
|
||||
method = "rideTick()V",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;wantsToStopRiding()Z")
|
||||
)
|
||||
public boolean delegateDismountToController(Player instance, Operation<Boolean> original) {
|
||||
if (this.getVehicle() instanceof Rideable vehicle) {
|
||||
return vehicle.getRiding().dismountOnShift() && original.call(instance);
|
||||
if (this.getVehicle() instanceof PokemonEntity pokemonEntity) {
|
||||
return pokemonEntity.ifRidingAvailableSupply(false, (behaviour, settings, state) -> {
|
||||
return behaviour.dismountOnShift(settings, state, pokemonEntity) && original.call(instance);
|
||||
});
|
||||
}
|
||||
return original.call(instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OrientationController getOrientationController() {
|
||||
return cobblemon$orientationController;
|
||||
}
|
||||
|
||||
@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();
|
||||
Matrix3f orientation = controller.isActive() && controller.getOrientation() != null ? controller.getOrientation() : new Matrix3f();
|
||||
Vec3 rotatedEyeHeight = new Vec3(orientation.transform(new Vector3f(0f, currEyeHeight - (this.getBbHeight() / 2), 0f)));
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.CobblemonNetwork;
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket;
|
||||
import com.cobblemon.mod.common.api.orientation.OrientationController;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.cobblemon.mod.common.mixin.accessor.ChunkMapAccessor;
|
||||
import com.cobblemon.mod.common.mixin.accessor.TrackedEntityAccessor;
|
||||
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateOrientationPacket;
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.ClientboundUpdateRidingStatePacket;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ServerEntity;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.network.ServerPlayerConnection;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.joml.Matrix3f;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Mixin(ServerEntity.class)
|
||||
public abstract class ServerEntityMixin {
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private Consumer<Packet<?>> broadcast;
|
||||
@Shadow
|
||||
@Final
|
||||
private Entity entity;
|
||||
@Unique
|
||||
private Matrix3f cobblemon$lastSentOrientation;
|
||||
@Unique
|
||||
private boolean cobblemon$lastSentActive;
|
||||
|
||||
@Inject(method = "sendChanges", at = @At("TAIL"))
|
||||
private void cobblemon$sendChanges(CallbackInfo ci) {
|
||||
cobblemon$sendOrientationChanges();
|
||||
cobblemon$sendRidingStateChanges();
|
||||
}
|
||||
|
||||
private void cobblemon$sendRidingStateChanges() {
|
||||
if (!(this.entity instanceof PokemonEntity pokemonEntity)) return;
|
||||
if (pokemonEntity.getRidingState() == null) return;
|
||||
if (pokemonEntity.getRiding() == null) return;
|
||||
var ridingBehaviour = pokemonEntity.getRiding();
|
||||
var ridingState = pokemonEntity.getRidingState();
|
||||
var previousRidingState = pokemonEntity.getPreviousRidingState();
|
||||
if (previousRidingState != null && !ridingState.shouldSync(previousRidingState)) return;
|
||||
cobblemon$broadcast(new ClientboundUpdateRidingStatePacket(pokemonEntity.getId(), ridingBehaviour.getKey(), pokemonEntity.getRidingState(), null));
|
||||
}
|
||||
|
||||
private void cobblemon$sendOrientationChanges() {
|
||||
if (!(this.entity instanceof OrientationControllable controllable)) return;
|
||||
OrientationController controller = controllable.getOrientationController();
|
||||
Matrix3f currOrientation = controller.getOrientation();
|
||||
boolean currActive = controller.getActive();
|
||||
|
||||
if (currOrientation == null || currOrientation.equals(cobblemon$lastSentOrientation) && (currActive == cobblemon$lastSentActive))
|
||||
return;
|
||||
cobblemon$lastSentOrientation = new Matrix3f(currOrientation);
|
||||
cobblemon$lastSentActive = currActive;
|
||||
|
||||
cobblemon$broadcast(new ClientboundUpdateOrientationPacket(currOrientation, currActive, entity.getId()));
|
||||
}
|
||||
|
||||
private void cobblemon$broadcast(NetworkPacket<?> packet) {
|
||||
if (!(entity.level() instanceof ServerLevel level)) return;
|
||||
ChunkMap chunkMap = level.getChunkSource().chunkMap;
|
||||
Int2ObjectMap<?> entityMap = ((ChunkMapAccessor) chunkMap).getEntityMap();
|
||||
Object tracked = entityMap.get(entity.getId());
|
||||
if (!(tracked instanceof TrackedEntityAccessor tracker)) return;
|
||||
|
||||
Set<ServerPlayerConnection> seenBy = tracker.getSeenBy();
|
||||
for (ServerPlayerConnection conn : seenBy) {
|
||||
ServerPlayer player = conn.getPlayer();
|
||||
if (player == entity) continue;
|
||||
CobblemonNetwork.INSTANCE.sendPacketToPlayer(player, packet);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
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.CallbackInfo;
|
||||
|
||||
@Mixin(ServerPlayer.class)
|
||||
public abstract class ServerPlayerMixin {
|
||||
|
||||
@Inject(method = "rideTick", at = @At("HEAD"))
|
||||
private void cobblemon$updateOrientationControllerRideTick(CallbackInfo ci) {
|
||||
if (!(this instanceof OrientationControllable controllable)) return;
|
||||
var shouldUseCustomOrientation = cobblemon$shouldUseCustomOrientation((ServerPlayer)(Object)this);
|
||||
controllable.getOrientationController().setActive(shouldUseCustomOrientation);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private boolean cobblemon$shouldUseCustomOrientation(ServerPlayer player) {
|
||||
var playerVehicle = player.getVehicle();
|
||||
if (playerVehicle == null) return false;
|
||||
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return false;
|
||||
return pokemonEntity.ifRidingAvailableSupply(false, (behaviour, settings, state) -> {
|
||||
return behaviour.shouldRoll(settings, state, pokemonEntity);
|
||||
});
|
||||
}
|
||||
|
||||
@Inject(method = "stopRiding", at = @At("HEAD"))
|
||||
public void cobblemon$resetOrientationOnDismount(CallbackInfo ci) {
|
||||
if (!(this instanceof OrientationControllable controllable)) return;
|
||||
controllable.getOrientationController().setActive(false);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin.accessor;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(ChunkMap.class)
|
||||
public interface ChunkMapAccessor {
|
||||
@Accessor("entityMap")
|
||||
Int2ObjectMap<?> getEntityMap();
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin.accessor;
|
||||
|
||||
import net.minecraft.server.network.ServerPlayerConnection;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@Mixin(targets = "net.minecraft.server.level.ChunkMap$TrackedEntity")
|
||||
public interface TrackedEntityAccessor {
|
||||
@Accessor("seenBy")
|
||||
Set<ServerPlayerConnection> getSeenBy();
|
||||
}
|
||||
+10
@@ -9,8 +9,10 @@
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.client.CobblemonClient;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.cobblemon.mod.common.pokedex.scanner.PokedexUsageContext;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
@@ -30,5 +32,13 @@ public class AbstractClientPlayerMixin {
|
||||
if (usageContext.getScanningGuiOpen()) {
|
||||
cir.setReturnValue(usageContext.getFovMultiplier());
|
||||
}
|
||||
|
||||
LocalPlayer player = (LocalPlayer) (Object) this;
|
||||
if (player != null && player.isPassenger() && player.getVehicle() instanceof PokemonEntity) {
|
||||
PokemonEntity ride = (PokemonEntity) player.getVehicle();
|
||||
|
||||
//Return custom fov mult
|
||||
cir.setReturnValue(ride.rideFovMult());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,18 +8,28 @@
|
||||
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.Rollable;
|
||||
import com.cobblemon.mod.common.Cobblemon;
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.api.orientation.OrientationController;
|
||||
import com.cobblemon.mod.common.api.riding.Rideable;
|
||||
import com.cobblemon.mod.common.api.riding.Seat;
|
||||
import com.cobblemon.mod.common.client.MountedPokemonAnimationRenderController;
|
||||
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.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import net.minecraft.world.level.ClipContext;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Quaternionf;
|
||||
@@ -32,10 +42,10 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(Camera.class)
|
||||
public abstract class CameraMixin {
|
||||
@Shadow @Final private static float DEFAULT_CAMERA_DISTANCE;
|
||||
|
||||
@Shadow private Entity entity;
|
||||
@Shadow @Final private Quaternionf rotation;
|
||||
@Shadow @Final private Vector3f forwards;
|
||||
@@ -44,13 +54,19 @@ public abstract class CameraMixin {
|
||||
@Shadow private float xRot;
|
||||
@Shadow private float yRot;
|
||||
|
||||
@Shadow public abstract float getXRot();
|
||||
@Shadow public abstract float getYRot();
|
||||
|
||||
@Shadow private Vec3 position;
|
||||
@Shadow private float eyeHeight;
|
||||
@Shadow private float eyeHeightOld;
|
||||
|
||||
@Shadow protected abstract void setPosition(Vec3 pos);
|
||||
|
||||
@Shadow
|
||||
private BlockGetter level;
|
||||
|
||||
@Shadow private float partialTickTime;
|
||||
|
||||
@Shadow public abstract void tick();
|
||||
|
||||
@Shadow private boolean detached;
|
||||
@Unique private float returnTimer = 0;
|
||||
@Unique private float rollAngleStart = 0;
|
||||
@Unique Minecraft minecraft = Minecraft.getInstance();
|
||||
@@ -58,94 +74,230 @@ public abstract class CameraMixin {
|
||||
@Unique boolean disableRollableCameraDebug = false;
|
||||
|
||||
@Inject(method = "setRotation", at = @At("HEAD"), cancellable = true)
|
||||
public void open_camera$setRotation(float f, float g, CallbackInfo ci) {
|
||||
if (!(this.entity instanceof Rollable rollable) || disableRollableCameraDebug) return;
|
||||
if (!rollable.shouldRoll() && rollable.getOrientation() != null) {
|
||||
public void cobblemon$setRotation(float f, float g, CallbackInfo ci) {
|
||||
if (!(this.entity instanceof OrientationControllable controllable) || disableRollableCameraDebug) return;
|
||||
var controller = controllable.getOrientationController();
|
||||
if (!controller.isActive() && controller.getOrientation() != null) {
|
||||
if(this.returnTimer < 1) {
|
||||
//Rotation is taken from entity since we no longer handle mouse ourselves
|
||||
//Stops a period of time when you can't input anything.
|
||||
rollable.getOrientation().set(
|
||||
controller.getOrientation().set(
|
||||
new Matrix3f()
|
||||
.rotateY((float) Math.toRadians(180 - this.entity.getYRot()))
|
||||
.rotateX((float) Math.toRadians(-this.entity.getXRot()))
|
||||
);
|
||||
if(rollAngleStart == 0){
|
||||
this.returnTimer = 1;
|
||||
rollable.clearRotation();
|
||||
controller.reset();
|
||||
return;
|
||||
}
|
||||
rollable.getOrientation().rotateZ((float) Math.toRadians(-rollAngleStart*(1-returnTimer)));
|
||||
applyRotation();
|
||||
controller.getOrientation().rotateZ((float) Math.toRadians(-rollAngleStart*(1-returnTimer)));
|
||||
cobblemon$applyRotation();
|
||||
this.returnTimer += .05F;
|
||||
ci.cancel();
|
||||
} else {
|
||||
cobblemon$applyRotation();
|
||||
this.returnTimer = 1;
|
||||
rollable.clearRotation();
|
||||
controller.reset();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (rollable.getOrientation() == null) return;
|
||||
applyRotation();
|
||||
cobblemon$applyRotation();
|
||||
|
||||
this.returnTimer = 0;
|
||||
this.rollAngleStart = rollable.getRoll();
|
||||
if (controller.getOrientation() != null) {
|
||||
this.rollAngleStart = controller.getRoll();
|
||||
}
|
||||
|
||||
if (!(entity.getVehicle() instanceof PokemonEntity)) return;
|
||||
ci.cancel();
|
||||
}
|
||||
|
||||
@Inject(method = "setup", at = @At("HEAD"))
|
||||
private void cobblemon$prepAnimation(BlockGetter level, Entity entity, boolean detached, boolean thirdPersonReverse, float partialTick, CallbackInfo ci) {
|
||||
Entity vehicle = entity.getVehicle();
|
||||
MountedPokemonAnimationRenderController.INSTANCE.reset();
|
||||
|
||||
if (vehicle instanceof PokemonEntity pokemon) {
|
||||
MountedPokemonAnimationRenderController.INSTANCE.setup(pokemon, partialTick);
|
||||
}
|
||||
}
|
||||
|
||||
//If you want to move this to a delagate you need an AW for position
|
||||
@WrapOperation(method = "setup", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;setPosition(DDD)V"))
|
||||
public void positionCamera(Camera instance, double x, double y, double z, Operation<Void> original){
|
||||
public void cobblemon$positionCamera(Camera instance, double x, double y, double z, Operation<Void> original, @Local(ordinal = 1, argsOnly = true) boolean thirdPersonReverse) {
|
||||
Entity entity = instance.getEntity();
|
||||
Entity vehicle = entity.getVehicle();
|
||||
|
||||
if(vehicle instanceof Rideable){
|
||||
//RidingCameraDelagate.INSTANCE.positionCamera(instance, x, y, z, original);
|
||||
if(!(vehicle instanceof PokemonEntity)){
|
||||
if (!(vehicle instanceof PokemonEntity pokemon)) {
|
||||
original.call(instance, x, y, z);
|
||||
return;
|
||||
}
|
||||
PokemonEntity pokemon = (PokemonEntity) vehicle;
|
||||
|
||||
int seatIndex = pokemon.getPassengers().indexOf(entity);
|
||||
Seat seat = pokemon.getSeats().get(seatIndex);
|
||||
PokemonClientDelegate delegate = (PokemonClientDelegate) pokemon.getDelegate();
|
||||
MatrixWrapper locator = delegate.getLocatorStates().get(seat.getLocator());
|
||||
if(locator == null){
|
||||
original.call(instance, x, y, z);
|
||||
return;
|
||||
}
|
||||
|
||||
Vec3 locatorOffset = new Vec3(locator.getMatrix().getTranslation(new Vector3f()));
|
||||
|
||||
Vec3 entityPos = new Vec3(
|
||||
Mth.lerp(instance.getPartialTickTime(), pokemon.xOld, pokemon.getX()),
|
||||
Mth.lerp(instance.getPartialTickTime(), pokemon.yOld, pokemon.getY()),
|
||||
Mth.lerp(instance.getPartialTickTime(), pokemon.zOld, pokemon.getZ())
|
||||
);
|
||||
int seatIndex = pokemon.getPassengers().indexOf(entity);
|
||||
Seat seat = pokemon.getSeats().get(seatIndex);
|
||||
|
||||
Rollable rollable = (Rollable) entity;
|
||||
float currEyeHeight = Mth.lerp(instance.getPartialTickTime(), eyeHeightOld, eyeHeight);
|
||||
Matrix3f orientation = rollable.shouldRoll() && rollable.getOrientation() != null ? rollable.getOrientation() : new Matrix3f();
|
||||
Vec3 rotatedEyeHeight = new Vec3(orientation.transform(new Vector3f(0f, currEyeHeight, 0f)));
|
||||
OrientationControllable rollable = (OrientationControllable) entity;
|
||||
OrientationController controller = rollable.getOrientationController();
|
||||
|
||||
position = locatorOffset.add(entityPos).add(rotatedEyeHeight);
|
||||
if (!instance.isDetached() || Cobblemon.config.getThirdPartyViewBobbing()) {
|
||||
MatrixWrapper locator = delegate.getLocatorStates().get(seat.getLocator());
|
||||
|
||||
if (locator == null) {
|
||||
original.call(instance, x, y, z);
|
||||
return;
|
||||
}
|
||||
|
||||
Vec3 locatorOffset = new Vec3(locator.getMatrix().getTranslation(new Vector3f()));
|
||||
|
||||
float currEyeHeight = Mth.lerp(instance.getPartialTickTime(), eyeHeightOld, eyeHeight);
|
||||
var rotatedEyeHeight = new Vector3f(0f, currEyeHeight - (entity.getBbHeight() / 2), 0f);
|
||||
if (controller.isActive()) {
|
||||
rotatedEyeHeight = controller.getRenderOrientation(partialTickTime).transform(rotatedEyeHeight);
|
||||
}
|
||||
|
||||
Vec3 position = locatorOffset.add(entityPos).add(new Vec3(rotatedEyeHeight));
|
||||
setPosition(position);
|
||||
} else {
|
||||
Vector3f pos = entityPos.add(new Vec3(0, pokemon.getBbHeight() / 2, 0)).toVector3f();
|
||||
|
||||
PosableModel model = VaryingModelRepository.INSTANCE.getPoser(pokemon.getPokemon().getSpecies().getResourceIdentifier(), new FloatingState());
|
||||
Map<String, Vec3> cameraOffsets = model.getSeatToCameraOffset();
|
||||
|
||||
Vec3 cameraOffset;
|
||||
if (thirdPersonReverse && cameraOffsets.containsKey(seat.getLocator() + "_reverse")) {
|
||||
cameraOffset = cameraOffsets.get(seat.getLocator() + "_reverse");
|
||||
} else if (cameraOffsets.containsKey(seat.getLocator())) {
|
||||
cameraOffset = cameraOffsets.get(seat.getLocator());
|
||||
} else {
|
||||
cameraOffset = new Vec3(0f, 2f, 4f);
|
||||
}
|
||||
|
||||
Vector3f offset = cameraOffset.toVector3f();
|
||||
|
||||
if (thirdPersonReverse) offset.z *= -1;
|
||||
float xRot = (float) (-1 * Math.toRadians(instance.getXRot()));
|
||||
|
||||
Matrix3f orientation = controller.isActive() && controller.getOrientation() != null ? controller.getOrientation() : new Matrix3f().rotateY((float) Math.toRadians(180f - instance.getYRot())).rotateX(xRot);
|
||||
Vector3f rotatedOffset = orientation.transform(offset);
|
||||
float offsetDistance = rotatedOffset.length();
|
||||
Vector3f offsetDirection = rotatedOffset.mul(1 / offsetDistance);
|
||||
|
||||
float maxZoom = cobblemon$getMaxZoom(offsetDistance, offsetDirection, pos);
|
||||
|
||||
setPosition(new Vec3(offsetDirection.mul(maxZoom).add(pos)));
|
||||
}
|
||||
} else {
|
||||
original.call(instance, x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void applyRotation(){
|
||||
if (!(this.entity instanceof Rollable rollable) || rollable.getOrientation() == null) return;
|
||||
var newRotation = rollable.getOrientation().normal(new Matrix3f()).getNormalizedRotation(new Quaternionf());
|
||||
private void cobblemon$applyRotation(){
|
||||
if (!(this.entity instanceof OrientationControllable controllable) || controllable.getOrientationController().getOrientation() == null || !controllable.getOrientationController().isActive()) {
|
||||
Quaternionf newRotation = null;
|
||||
var vehicle = entity.getVehicle();
|
||||
if (vehicle instanceof PokemonEntity pokemon) {
|
||||
var delegate = (PokemonClientDelegate) pokemon.getDelegate();
|
||||
int seatIndex = pokemon.getPassengers().indexOf(entity);
|
||||
Seat seat = pokemon.getSeats().get(seatIndex);
|
||||
MatrixWrapper locator = delegate.getLocatorStates().get(seat.getLocator());
|
||||
if (locator != null && !detached) {
|
||||
var scale = locator.getMatrix().getScale(new Vector3f());
|
||||
if (scale.length() > 0.1) {
|
||||
newRotation = locator.getMatrix().getUnnormalizedRotation(new Quaternionf()).normalize();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (newRotation != null) {
|
||||
if (this.minecraft.options.getCameraType().isMirrored()) {
|
||||
newRotation.rotateY((float)Math.toRadians(180));
|
||||
}
|
||||
|
||||
this.rotation.set(newRotation);
|
||||
|
||||
var forwardVector = newRotation.transform(OrientationController.Companion.getFORWARDS(), new Vector3f());
|
||||
var upVector = newRotation.transform(OrientationController.Companion.getUP(), new Vector3f());
|
||||
var leftVector = newRotation.transform(OrientationController.Companion.getLEFT(), new Vector3f());
|
||||
this.xRot = (float)Mth.wrapDegrees(-Math.toDegrees(Math.asin(forwardVector.y)));
|
||||
this.yRot = Mth.wrapDegrees((float)-Math.toDegrees(OrientationController.Companion.getFORWARDS().angleSigned(forwardVector, OrientationController.Companion.getUP())) + 180);
|
||||
this.forwards.set(forwardVector);
|
||||
this.up.set(upVector);
|
||||
this.left.set(leftVector);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var controller = controllable.getOrientationController();
|
||||
var newRotation = controller.getOrientation().normal(new Matrix3f()).getNormalizedRotation(new Quaternionf());
|
||||
|
||||
var vehicle = entity.getVehicle();
|
||||
if (vehicle instanceof PokemonEntity pokemon) {
|
||||
var delegate = (PokemonClientDelegate) pokemon.getDelegate();
|
||||
int seatIndex = pokemon.getPassengers().indexOf(entity);
|
||||
Seat seat = pokemon.getSeats().get(seatIndex);
|
||||
MatrixWrapper locator = delegate.getLocatorStates().get(seat.getLocator());
|
||||
if (locator != null && !detached) {
|
||||
var scale = locator.getMatrix().getScale(new Vector3f());
|
||||
if (scale.length() > 0.1) {
|
||||
newRotation = locator.getMatrix().getUnnormalizedRotation(new Quaternionf()).normalize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.minecraft.options.getCameraType().isMirrored()) {
|
||||
newRotation.rotateY((float)Math.toRadians(180));
|
||||
}
|
||||
|
||||
this.rotation.set(newRotation);
|
||||
this.xRot = rollable.getPitch();
|
||||
this.yRot = rollable.getYaw();
|
||||
this.forwards.set(rollable.getForwardVector());
|
||||
this.up.set(rollable.getUpVector());
|
||||
this.left.set(rollable.getLeftVector());
|
||||
|
||||
var forwardVector = newRotation.transform(OrientationController.Companion.getFORWARDS(), new Vector3f());
|
||||
var upVector = newRotation.transform(OrientationController.Companion.getUP(), new Vector3f());
|
||||
var leftVector = newRotation.transform(OrientationController.Companion.getLEFT(), new Vector3f());
|
||||
this.xRot = (float)Mth.wrapDegrees(-Math.toDegrees(Math.asin(forwardVector.y)));
|
||||
this.yRot = Mth.wrapDegrees((float)-Math.toDegrees(OrientationController.Companion.getFORWARDS().angleSigned(forwardVector, OrientationController.Companion.getUP())) + 180);
|
||||
this.forwards.set(forwardVector);
|
||||
this.up.set(upVector);
|
||||
this.left.set(leftVector);
|
||||
}
|
||||
|
||||
@WrapOperation(method = "setup", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;move(FFF)V", ordinal = 0))
|
||||
public void stopMoveIfRolling(Camera instance, float zoom, float dy, float dx, Operation<Void> original) {
|
||||
Entity entity = instance.getEntity();
|
||||
Entity vehicle = entity.getVehicle();
|
||||
|
||||
if (!(vehicle instanceof PokemonEntity) || Cobblemon.config.getThirdPartyViewBobbing()) {
|
||||
original.call(instance, zoom, dy, dx);
|
||||
}
|
||||
}
|
||||
|
||||
//Modified vanilla code
|
||||
@Unique
|
||||
private float cobblemon$getMaxZoom(float maxZoom, Vector3f directionVector, Vector3f positionVector) {
|
||||
Vec3 pos = new Vec3(positionVector);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
float g = (float) ((i & 1) * 2 - 1);
|
||||
float h = (float) ((i >> 1 & 1) * 2 - 1);
|
||||
float j = (float) ((i >> 2 & 1) * 2 - 1);
|
||||
Vec3 vec3 = pos.add(g * 0.1F, h * 0.1F, j * 0.1F);
|
||||
Vec3 vec32 = vec3.add(new Vec3(directionVector).scale(maxZoom));
|
||||
HitResult hitResult = this.level.clip(new ClipContext(vec3, vec32, ClipContext.Block.VISUAL, ClipContext.Fluid.NONE, this.entity));
|
||||
if (hitResult.getType() != HitResult.Type.MISS) {
|
||||
float k = (float) hitResult.getLocation().distanceToSqr(pos);
|
||||
if (k < Mth.square(maxZoom)) {
|
||||
maxZoom = Mth.sqrt(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return maxZoom;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,8 +59,21 @@ public class HumanoidModelMixin {
|
||||
}
|
||||
|
||||
Entity vehicle = entity.getVehicle();
|
||||
if (vehicle instanceof PokemonEntity && entity instanceof AbstractClientPlayer) {
|
||||
MountedPlayerRenderer.INSTANCE.animate((PokemonEntity) vehicle, (AbstractClientPlayer) entity, relevantPartsByName, netHeadYaw, headPitch, ageInTicks, limbSwing, limbSwingAmount);
|
||||
if (vehicle instanceof PokemonEntity pokemonEntity && entity instanceof AbstractClientPlayer) {
|
||||
var shouldRotatePlayerHead = pokemonEntity.ifRidingAvailableSupply(false, (behaviour, settings, state) -> {
|
||||
return behaviour.shouldRotatePlayerHead(settings, state, pokemonEntity);
|
||||
});
|
||||
|
||||
if (!shouldRotatePlayerHead) {
|
||||
netHeadYaw = 0f;
|
||||
headPitch = 0f;
|
||||
if (this.head != null) {
|
||||
this.head.yRot = 0f;
|
||||
this.head.xRot = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
MountedPlayerRenderer.INSTANCE.animate(pokemonEntity, (AbstractClientPlayer) entity, relevantPartsByName, netHeadYaw, headPitch, ageInTicks, limbSwing, limbSwingAmount);
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -8,7 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.Rollable;
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.item.PokedexItem;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.client.Minecraft;
|
||||
@@ -31,14 +31,14 @@ public class ItemInHandRendererMixin {
|
||||
if (abstractClientPlayer.isUsingItem() && abstractClientPlayer.getUseItem().getItem() instanceof PokedexItem) {
|
||||
ci.cancel();
|
||||
}
|
||||
else if (abstractClientPlayer instanceof Rollable rollable && rollable.shouldRoll()) {
|
||||
else if (abstractClientPlayer instanceof OrientationControllable controllable && controllable.getOrientationController().isActive()) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Redirect(method = "renderHandsWithItems", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/vertex/PoseStack;mulPose(Lorg/joml/Quaternionf;)V", ordinal = 1))
|
||||
private void cobblemon$renderHandswithItems(PoseStack instance, Quaternionf quaternion) {
|
||||
if (!(Minecraft.getInstance().player instanceof Rollable rollable && rollable.shouldRoll())) {
|
||||
if (!(Minecraft.getInstance().player instanceof OrientationControllable controllable && controllable.getOrientationController().isActive())) {
|
||||
instance.mulPose(quaternion);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.Cobblemon;
|
||||
import com.cobblemon.mod.common.ModAPI;
|
||||
import com.cobblemon.mod.common.item.PokeBallItem;
|
||||
import com.cobblemon.mod.common.item.PokedexItem;
|
||||
import com.cobblemon.mod.common.item.WearableItem;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.renderer.ItemModelShaper;
|
||||
@@ -54,17 +55,18 @@ public abstract class ItemRendererMixin {
|
||||
)
|
||||
private void cobblemon$overrideItemModel(ItemStack stack, ItemDisplayContext renderMode, boolean leftHanded, PoseStack matrices, MultiBufferSource multiBufferSource, int light, int overlay, BakedModel model, CallbackInfo ci) {
|
||||
boolean shouldBe2d = renderMode == ItemDisplayContext.GUI || renderMode == ItemDisplayContext.FIXED;
|
||||
ResourceLocation resourceLocation = null;
|
||||
if (shouldBe2d) {
|
||||
ResourceLocation resourceLocation = null;
|
||||
if (stack.getItem() instanceof PokeBallItem pokeBallItem) resourceLocation = pokeBallItem.getPokeBall().getModel2d();
|
||||
else if (stack.getItem() instanceof PokedexItem pokedexItem) resourceLocation = pokedexItem.getType().getItemSpritePath();
|
||||
}
|
||||
if (renderMode != ItemDisplayContext.HEAD && stack.getItem() instanceof WearableItem wearableItem) resourceLocation = wearableItem.getModel2d();
|
||||
|
||||
if (resourceLocation != null) {
|
||||
BakedModel replacementModel = this.itemModelShaper.getModelManager().getModel(new ModelResourceLocation(resourceLocation, "inventory"));
|
||||
if (!cobblemon$isSameModel(model, replacementModel)) {
|
||||
ci.cancel();
|
||||
render(stack, renderMode, leftHanded, matrices, multiBufferSource, light, overlay, replacementModel);
|
||||
}
|
||||
if (resourceLocation != null) {
|
||||
BakedModel replacementModel = this.itemModelShaper.getModelManager().getModel(new ModelResourceLocation(resourceLocation, "inventory"));
|
||||
if (!cobblemon$isSameModel(model, replacementModel)) {
|
||||
ci.cancel();
|
||||
render(stack, renderMode, leftHanded, matrices, multiBufferSource, light, overlay, replacementModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,6 +80,7 @@ public abstract class ItemRendererMixin {
|
||||
boolean canOpenScreen = entity != null && ((entity.getOffhandItem() == stack && !(entity.getMainHandItem().getItem() instanceof PokedexItem)) || entity.getMainHandItem() == stack);
|
||||
resourceLocation = pokedexItem.getType().getItemModelPath(isScanModel ? "scanning" : (canOpenScreen ? null : "off"));
|
||||
}
|
||||
else if (stack.getItem() instanceof WearableItem wearableItem) resourceLocation = wearableItem.getModel3d();
|
||||
|
||||
if (resourceLocation != null) {
|
||||
BakedModel model = this.itemModelShaper.getModelManager().getModel(new ModelResourceLocation(resourceLocation, MODEL_PATH));
|
||||
|
||||
@@ -9,9 +9,14 @@
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonNetwork;
|
||||
import com.cobblemon.mod.common.Rollable;
|
||||
import com.cobblemon.mod.common.net.messages.server.orientation.C2SUpdateOrientationPacket;
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.cobblemon.mod.common.net.messages.server.orientation.ServerboundUpdateOrientationPacket;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.joml.Matrix3f;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
@@ -20,24 +25,55 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(LocalPlayer.class)
|
||||
public class LocalPlayerMixin {
|
||||
public abstract class LocalPlayerMixin extends LivingEntity {
|
||||
|
||||
@Unique Matrix3f cobblemon$lastOrientation;
|
||||
|
||||
@Inject(method = "sendPosition", at = @At("TAIL"))
|
||||
private void cobblemon_updateRotationMatrix(CallbackInfo ci) {
|
||||
if (!(this instanceof Rollable rollable)) return;
|
||||
if (rollable.getOrientation() == cobblemon$lastOrientation) return;
|
||||
cobblemon$lastOrientation = rollable.getOrientation() != null ? new Matrix3f(rollable.getOrientation()) : null;
|
||||
CobblemonNetwork.INSTANCE.sendToServer(new C2SUpdateOrientationPacket(rollable.getOrientation()));
|
||||
protected LocalPlayerMixin(EntityType<? extends LivingEntity> entityType, Level level) {
|
||||
super(entityType, level);
|
||||
}
|
||||
|
||||
// @Inject(method = "sendPosition", at = @At("TAIL"))
|
||||
// private void cobblemon$updateRotationMatrix(CallbackInfo ci) {
|
||||
// if (!(this instanceof OrientationControllable controllable)) return;
|
||||
// var controller = controllable.getOrientationController();
|
||||
// if (!controller.isActive() || controller.getOrientation() == cobblemon$lastOrientation) return;
|
||||
// cobblemon$lastOrientation = controller.getOrientation() != null ? new Matrix3f(controller.getOrientation()) : null;
|
||||
// CobblemonNetwork.INSTANCE.sendToServer(new ServerboundUpdateOrientationPacket(controller.getOrientation()));
|
||||
// }
|
||||
|
||||
@Inject(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientPacketListener;send(Lnet/minecraft/network/protocol/Packet;)V"))
|
||||
private void cobblemon_updateRotationMatrixPassenger(CallbackInfo ci) {
|
||||
if (!(this instanceof Rollable rollable)) return;
|
||||
if (rollable.getOrientation() == cobblemon$lastOrientation) return;
|
||||
cobblemon$lastOrientation = rollable.getOrientation() != null ? new Matrix3f(rollable.getOrientation()) : null;
|
||||
CobblemonNetwork.INSTANCE.sendToServer(new C2SUpdateOrientationPacket(rollable.getOrientation()));
|
||||
private void cobblemon$updateRotationMatrixPassenger(CallbackInfo ci) {
|
||||
if (!(this instanceof OrientationControllable controllable)) return;
|
||||
var controller = controllable.getOrientationController();
|
||||
if (!controller.isActive() || controller.getOrientation() == cobblemon$lastOrientation) return;
|
||||
cobblemon$lastOrientation = controller.getOrientation() != null ? new Matrix3f(controller.getOrientation()) : null;
|
||||
CobblemonNetwork.INSTANCE.sendToServer(new ServerboundUpdateOrientationPacket(controller.getOrientation()));
|
||||
}
|
||||
|
||||
@Inject(method = "rideTick", at = @At("HEAD"))
|
||||
private void cobblemon$updateOrientationControllerRideTick(CallbackInfo ci) {
|
||||
if (Minecraft.getInstance().player != (Object)this) return;
|
||||
if (!(this instanceof OrientationControllable controllable)) return;
|
||||
var shouldUseCustomOrientation = cobblemon$shouldUseCustomOrientation((LocalPlayer)(Object)this);
|
||||
controllable.getOrientationController().setActive(shouldUseCustomOrientation);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private boolean cobblemon$shouldUseCustomOrientation(LocalPlayer player) {
|
||||
var playerVehicle = player.getVehicle();
|
||||
if (playerVehicle == null) return false;
|
||||
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return false;
|
||||
return pokemonEntity.ifRidingAvailableSupply(false, (behaviour, settings, state) -> {
|
||||
return behaviour.shouldRoll(settings, state, pokemonEntity);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopRiding() {
|
||||
super.stopRiding();
|
||||
if (Minecraft.getInstance().player != (Object)this) return;
|
||||
if (!(this instanceof OrientationControllable controllable)) return;
|
||||
controllable.getOrientationController().setActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
+130
-17
@@ -8,17 +8,20 @@
|
||||
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.Rollable;
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.client.CobblemonClient;
|
||||
import com.cobblemon.mod.common.client.keybind.keybinds.PartySendBinding;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.cobblemon.mod.common.pokedex.scanner.PokedexUsageContext;
|
||||
import com.llamalad7.mixinextras.injector.v2.WrapWithCondition;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import com.mojang.blaze3d.Blaze3D;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.MouseHandler;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.util.SmoothDouble;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
@@ -27,14 +30,18 @@ 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.LocalCapture;
|
||||
|
||||
@Mixin(MouseHandler.class)
|
||||
public class MouseHandlerMixin {
|
||||
public abstract class MouseHandlerMixin {
|
||||
@Shadow private double accumulatedScrollY;
|
||||
@Shadow @Final private Minecraft minecraft;
|
||||
|
||||
@Unique SmoothDouble xMouseSmoother = new SmoothDouble();
|
||||
@Unique SmoothDouble yMouseSmoother = new SmoothDouble();
|
||||
@Unique SmoothDouble pitchSmoother = new SmoothDouble();
|
||||
@Unique SmoothDouble rollSmoother = new SmoothDouble();
|
||||
@Unique SmoothDouble yawSmoother = new SmoothDouble();
|
||||
|
||||
@Shadow @Final private SmoothDouble smoothTurnY;
|
||||
|
||||
@@ -44,6 +51,14 @@ public class MouseHandlerMixin {
|
||||
|
||||
@Shadow private double accumulatedDY;
|
||||
|
||||
@Shadow private double lastHandleMovementTime;
|
||||
|
||||
@Shadow public abstract boolean isMouseGrabbed();
|
||||
|
||||
@Shadow protected abstract void turnPlayer(double movementTime);
|
||||
|
||||
@Unique private double cobblemon$timeDelta;
|
||||
|
||||
@Inject(
|
||||
method = "onScroll",
|
||||
at = @At(
|
||||
@@ -95,7 +110,13 @@ public class MouseHandlerMixin {
|
||||
target = "Lnet/minecraft/client/player/LocalPlayer;turn(DD)V"
|
||||
)
|
||||
)
|
||||
public boolean cobblemon$modifyRotation(LocalPlayer player, double cursorDeltaX, double cursorDeltaY, @Local(argsOnly = true) double d) {
|
||||
public boolean cobblemon$modifyRotation(
|
||||
LocalPlayer player,
|
||||
double cursorDeltaX,
|
||||
double cursorDeltaY,
|
||||
@Local(argsOnly = true) double d,
|
||||
@Local(argsOnly = true) double movementTime
|
||||
) {
|
||||
PokedexUsageContext usageContext = CobblemonClient.INSTANCE.getPokedexUsageContext();
|
||||
if (usageContext.getScanningGuiOpen()) {
|
||||
this.smoothTurnY.reset();
|
||||
@@ -108,29 +129,121 @@ public class MouseHandlerMixin {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(player instanceof Rollable rollable)) return true;
|
||||
if (!rollable.shouldRoll()) {
|
||||
if (!(player instanceof OrientationControllable controllable)) return true;
|
||||
|
||||
if (!controllable.getOrientationController().isActive()) {
|
||||
xMouseSmoother.reset();
|
||||
yMouseSmoother.reset();
|
||||
pitchSmoother.reset();
|
||||
rollSmoother.reset();
|
||||
yawSmoother.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
var pitch = pitchSmoother.getNewDeltaValue(cursorDeltaY * 0.15f, d);
|
||||
var roll = rollSmoother.getNewDeltaValue(cursorDeltaX * 0.15f, d);
|
||||
rollable.rotate(0.0F, (float)pitch, (float)roll);
|
||||
//Send mouse input to be interpreted into rotation
|
||||
//deltas by the ride controller
|
||||
Vec3 angVecMouse = cobblemon$getRideMouseRotation(cursorDeltaX, cursorDeltaY, movementTime);
|
||||
|
||||
//Perform Rotation using mouse influenced rotation deltas.
|
||||
controllable.getOrientationController().rotate(
|
||||
(float) angVecMouse.x,
|
||||
(float) angVecMouse.y,
|
||||
(float) angVecMouse.z
|
||||
);
|
||||
|
||||
//Gather and apply the current rotation deltas
|
||||
var angRot = cobblemon$getAngularVelocity(movementTime);
|
||||
|
||||
//Apply smoothing if requested by the controller.
|
||||
//This Might be best if done by the controller itself?
|
||||
if(cobblemon$shouldUseAngVelSmoothing())
|
||||
{
|
||||
var yaw = yawSmoother.getNewDeltaValue(angRot.x * 0.5f, d);
|
||||
var pitch = pitchSmoother.getNewDeltaValue(angRot.y * 0.5f, d);
|
||||
var roll = rollSmoother.getNewDeltaValue(angRot.z * 0.5f, d);
|
||||
controllable.getOrientationController().rotate((float) yaw, (float) pitch, (float) roll);
|
||||
}
|
||||
//Otherwise simply apply the smoothing
|
||||
else
|
||||
{
|
||||
controllable.getOrientationController().rotate((float) (angRot.x * 10 * d), (float) (angRot.y * 10 * d), (float) (angRot.z * 10 * d));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Inject(method = "handleAccumulatedMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MouseHandler;isMouseGrabbed()Z", ordinal = 0))
|
||||
private void cobblemon$maintainMovementWhenInScreens(CallbackInfo ci, @Local(ordinal = 1) double e) {
|
||||
if (minecraft.player == null) return;
|
||||
if (!(minecraft.player instanceof Rollable rollable)) return;
|
||||
if (!rollable.shouldRoll()) return;
|
||||
if (minecraft.isPaused()) return;
|
||||
@Inject(method = "handleAccumulatedMovement", at = @At("HEAD"))
|
||||
private void cobblemon$maintainMovementWhenInScreens2(CallbackInfo ci) {
|
||||
double time = Blaze3D.getTime();
|
||||
cobblemon$timeDelta = time - this.lastHandleMovementTime;
|
||||
}
|
||||
|
||||
var pitch = pitchSmoother.getNewDeltaValue(0, e);
|
||||
var roll = rollSmoother.getNewDeltaValue(0, e);
|
||||
rollable.rotate(0.0F, (float)pitch, (float)roll);
|
||||
@Inject(method = "handleAccumulatedMovement", at = @At("TAIL"))
|
||||
private void cobblemon$maintainMovementWhenInScreens(CallbackInfo ci) {
|
||||
if (minecraft.player == null) return;
|
||||
if (!(minecraft.player instanceof OrientationControllable controllable)) return;
|
||||
if (!controllable.getOrientationController().isActive()) return;
|
||||
if (minecraft.isPaused()) return;
|
||||
if (isMouseGrabbed()) return;
|
||||
|
||||
this.turnPlayer(cobblemon$timeDelta);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private Vec3 cobblemon$getAngularVelocity(double deltaTime) {
|
||||
var player = minecraft.player;
|
||||
if (player == null) return Vec3.ZERO;
|
||||
if (!(player instanceof OrientationControllable)) return Vec3.ZERO;
|
||||
|
||||
var playerVehicle = player.getVehicle();
|
||||
if (playerVehicle == null) return Vec3.ZERO;
|
||||
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return Vec3.ZERO;
|
||||
return pokemonEntity.ifRidingAvailableSupply(Vec3.ZERO, (behaviour, settings, state) -> {
|
||||
return behaviour.angRollVel(settings, state, pokemonEntity, player, deltaTime);
|
||||
});
|
||||
}
|
||||
|
||||
@Unique
|
||||
private Vec3 cobblemon$getRideMouseRotation(double mouseX, double mouseY, double deltaTime) {
|
||||
var player = minecraft.player;
|
||||
if (player == null) return Vec3.ZERO;
|
||||
var vehicle = player.getVehicle();
|
||||
if (vehicle == null) return Vec3.ZERO;
|
||||
if (!(vehicle instanceof PokemonEntity pokemonEntity)) return Vec3.ZERO;
|
||||
|
||||
var sensitivity = cobblemon$getRidingSensitivity();
|
||||
return pokemonEntity.ifRidingAvailableSupply(Vec3.ZERO, (behaviour, settings, state) -> {
|
||||
return behaviour.rotationOnMouseXY(
|
||||
settings,
|
||||
state,
|
||||
pokemonEntity,
|
||||
player,
|
||||
mouseY,
|
||||
mouseX,
|
||||
yMouseSmoother,
|
||||
xMouseSmoother,
|
||||
sensitivity,
|
||||
deltaTime
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@Unique
|
||||
private double cobblemon$getRidingSensitivity() {
|
||||
var sensitivity = this.minecraft.options.sensitivity().get() * 0.6000000238418579 + 0.20000000298023224;
|
||||
return Math.pow(sensitivity, 3);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private boolean cobblemon$shouldUseAngVelSmoothing() {
|
||||
var player = minecraft.player;
|
||||
if (player == null) return true;
|
||||
|
||||
var playerVehicle = player.getVehicle();
|
||||
if (playerVehicle == null) return true;
|
||||
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return true;
|
||||
return pokemonEntity.ifRidingAvailableSupply(true, (behaviour, settings, state) -> {
|
||||
return behaviour.useAngVelSmoothing(settings, state, pokemonEntity);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.mixin.client;
|
||||
import com.cobblemon.mod.common.api.riding.Rideable;
|
||||
import com.cobblemon.mod.common.api.riding.RidingManager;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import net.minecraft.client.model.PlayerModel;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(PlayerModel.class)
|
||||
public class PlayerModelMixin {
|
||||
@Inject(method = "setupAnim(Lnet/minecraft/world/entity/LivingEntity;FFFFF)V", at = @At(value = "HEAD"))
|
||||
private void cobblemon$setHeadRotation(LivingEntity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch, CallbackInfo ci){
|
||||
if (!(entity instanceof Player)) return;
|
||||
Entity vehicle = entity.getVehicle();
|
||||
|
||||
if (!(vehicle instanceof PokemonEntity)) return;
|
||||
RidingManager ridingManager = ((Rideable) vehicle).getRiding();
|
||||
if (ridingManager.shouldRotatePlayerHead((PokemonEntity) vehicle)) return;
|
||||
|
||||
netHeadYaw = 0f;
|
||||
headPitch = 0f;
|
||||
}
|
||||
}
|
||||
+28
-1
@@ -11,9 +11,15 @@ package com.cobblemon.mod.common.mixin.client;
|
||||
import com.cobblemon.mod.common.client.render.ClientPlayerIcon;
|
||||
import com.cobblemon.mod.common.client.render.player.MountedPlayerRenderer;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.client.model.EntityModel;
|
||||
import net.minecraft.client.model.PlayerModel;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
|
||||
import net.minecraft.client.renderer.entity.player.PlayerRenderer;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
@@ -23,12 +29,33 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(PlayerRenderer.class)
|
||||
public class PlayerRendererMixin {
|
||||
public abstract class PlayerRendererMixin extends LivingEntityRenderer<AbstractClientPlayer, PlayerModel<AbstractClientPlayer>> {
|
||||
|
||||
public PlayerRendererMixin(EntityRendererProvider.Context context, PlayerModel<AbstractClientPlayer> model, float shadowRadius) {
|
||||
super(context, model, shadowRadius);
|
||||
}
|
||||
|
||||
@Inject(method = "render(Lnet/minecraft/world/entity/LivingEntity;FFLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;I)V", at = @At(value = "TAIL"))
|
||||
public void cobblemon$clientPlayerIcon(LivingEntity entity, float entityYaw, float partialTicks, PoseStack poseStack, MultiBufferSource buffer, int packedLight, CallbackInfo ci) {
|
||||
ClientPlayerIcon.Companion.onRenderPlayer((Player) entity);
|
||||
}
|
||||
|
||||
@WrapOperation(method = "getRenderOffset(Lnet/minecraft/client/player/AbstractClientPlayer;F)Lnet/minecraft/world/phys/Vec3;", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/AbstractClientPlayer;isCrouching()Z"))
|
||||
private boolean cobblemon$disableCrouchOffset(AbstractClientPlayer instance, Operation<Boolean> original) {
|
||||
if (instance.isPassenger() && instance.getVehicle() instanceof PokemonEntity) {
|
||||
return false;
|
||||
}
|
||||
return original.call(instance);
|
||||
}
|
||||
|
||||
@Inject(method = "setModelProperties", at = @At("TAIL"))
|
||||
private void cobblemon$setModelProperties(AbstractClientPlayer player, CallbackInfo ci) {
|
||||
if (player.isPassenger() && player.getVehicle() instanceof PokemonEntity) {
|
||||
PlayerModel<AbstractClientPlayer> playerModel = this.getModel();
|
||||
playerModel.crouching = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "setupRotations(Lnet/minecraft/client/player/AbstractClientPlayer;Lcom/mojang/blaze3d/vertex/PoseStack;FFFF)V",
|
||||
at = @At("HEAD")
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.cobblemon.mod.common.api.drop.ItemDropEntry
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.DATA_SYNCHRONIZED
|
||||
import com.cobblemon.mod.common.api.interaction.RequestManager
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.permission.PermissionValidator
|
||||
import com.cobblemon.mod.common.api.pokeball.catching.calculators.CaptureCalculator
|
||||
import com.cobblemon.mod.common.api.pokeball.catching.calculators.CaptureCalculators
|
||||
@@ -62,7 +63,11 @@ import com.cobblemon.mod.common.api.storage.player.adapter.PlayerDataMongoBacken
|
||||
import com.cobblemon.mod.common.api.storage.player.factory.CachedPlayerDataStoreFactory
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonEntityTypeTags
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
import com.cobblemon.mod.common.battles.*
|
||||
import com.cobblemon.mod.common.battles.BagItems
|
||||
import com.cobblemon.mod.common.battles.BattleFormat
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
import com.cobblemon.mod.common.battles.BattleSide
|
||||
import com.cobblemon.mod.common.battles.ShowdownThread
|
||||
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.client.CobblemonPack
|
||||
@@ -86,11 +91,23 @@ import com.cobblemon.mod.common.pokemon.aspects.SHINY_ASPECT
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.BlockClickEvolution
|
||||
import com.cobblemon.mod.common.pokemon.feature.TagSeasonResolver
|
||||
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
|
||||
import com.cobblemon.mod.common.pokemon.properties.*
|
||||
import com.cobblemon.mod.common.pokemon.properties.AspectPropertyType
|
||||
import com.cobblemon.mod.common.pokemon.properties.BattleCloneProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.FreezeFrameProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.HiddenAbilityPropertyType
|
||||
import com.cobblemon.mod.common.pokemon.properties.NoAIProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.UnaspectPropertyType
|
||||
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.tags.PokemonFlagProperty
|
||||
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
|
||||
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.ifDedicatedServer
|
||||
import com.cobblemon.mod.common.util.isLaterVersion
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import com.cobblemon.mod.common.util.requestWallpapers
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import com.cobblemon.mod.common.world.feature.CobblemonPlacedFeatures
|
||||
import com.cobblemon.mod.common.world.feature.ore.CobblemonOrePlacedFeatures
|
||||
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
|
||||
@@ -98,27 +115,29 @@ import com.mongodb.ConnectionString
|
||||
import com.mongodb.MongoClientSettings
|
||||
import com.mongodb.client.MongoClient
|
||||
import com.mongodb.client.MongoClients
|
||||
import java.io.File
|
||||
import java.io.FileReader
|
||||
import java.io.FileWriter
|
||||
import java.io.PrintWriter
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.properties.Delegates
|
||||
import kotlin.reflect.full.memberProperties
|
||||
import kotlin.reflect.jvm.isAccessible
|
||||
import kotlin.reflect.jvm.javaField
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.commands.synchronization.SingletonArgumentInfo
|
||||
import net.minecraft.resources.ResourceKey
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.NameTagItem
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.storage.LevelResource
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import org.apache.logging.log4j.Logger
|
||||
import java.io.File
|
||||
import java.io.FileReader
|
||||
import java.io.FileWriter
|
||||
import java.io.PrintWriter
|
||||
import java.util.*
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.properties.Delegates
|
||||
import kotlin.reflect.full.memberProperties
|
||||
import kotlin.reflect.jvm.isAccessible
|
||||
import kotlin.reflect.jvm.javaField
|
||||
|
||||
object Cobblemon {
|
||||
const val MODID = CobblemonBuildDetails.MOD_ID
|
||||
@@ -152,7 +171,9 @@ object Cobblemon {
|
||||
var permissionValidator: PermissionValidator by Delegates.observable(LaxPermissionValidator().also { it.initialize() }) { _, _, newValue -> newValue.initialize() }
|
||||
var statProvider: StatProvider = CobblemonStatProvider
|
||||
var seasonResolver: SeasonResolver = TagSeasonResolver
|
||||
var wallpapers = mapOf<UUID, List<ResourceLocation>>()
|
||||
var wallpapers = mutableMapOf<UUID, Set<ResourceLocation>>()
|
||||
|
||||
val serverPlayerStructs = mutableMapOf<UUID, ObjectValue<Player>>()
|
||||
|
||||
@JvmStatic
|
||||
val builtinPacks = listOf<CobblemonPack>(
|
||||
@@ -218,6 +239,7 @@ object Cobblemon {
|
||||
storage.onPlayerDisconnect(it.player)
|
||||
playerDataManager.onPlayerDisconnect(it.player)
|
||||
RequestManager.onLogoff(it.player)
|
||||
serverPlayerStructs.remove(it.player.uuid)
|
||||
}
|
||||
PlatformEvents.PLAYER_DEATH.subscribe {
|
||||
PCLinkManager.removeLink(it.player.uuid)
|
||||
@@ -387,9 +409,11 @@ object Cobblemon {
|
||||
PlatformEvents.SERVER_STOPPED.subscribe {
|
||||
storage.unregisterAll(it.server.registryAccess())
|
||||
playerDataManager.saveAllStores()
|
||||
playerDataManager.saveExecutor.shutdown()
|
||||
playerDataManager.saveExecutor.awaitTermination(30L, TimeUnit.SECONDS)
|
||||
}
|
||||
PlatformEvents.SERVER_STARTED.subscribe {
|
||||
bestSpawner.onServerStarted()
|
||||
PlatformEvents.SERVER_STARTED.subscribe { event ->
|
||||
bestSpawner.onServerStarted(event.server)
|
||||
battleRegistry.onServerStarted()
|
||||
}
|
||||
PlatformEvents.SERVER_TICK_POST.subscribe { ServerTickHandler.onTick(it.server) }
|
||||
@@ -537,6 +561,10 @@ object Cobblemon {
|
||||
this.implementation.registerCommandArgument(cobblemonResource("form"), FormArgumentType::class, SingletonArgumentInfo.contextFree(FormArgumentType::form))
|
||||
this.implementation.registerCommandArgument(cobblemonResource("dex"), DexArgumentType::class, SingletonArgumentInfo.contextFree (DexArgumentType::dex))
|
||||
this.implementation.registerCommandArgument(cobblemonResource("npc_class"), NPCClassArgumentType::class, SingletonArgumentInfo.contextFree(NPCClassArgumentType::npcClass))
|
||||
this.implementation.registerCommandArgument(cobblemonResource("wallpaper"), UnlockablePCBoxWallpaperArgumentType::class, SingletonArgumentInfo.contextFree(UnlockablePCBoxWallpaperArgumentType::wallpaper))
|
||||
this.implementation.registerCommandArgument(cobblemonResource("mark"), MarkArgumentType::class, SingletonArgumentInfo.contextFree(MarkArgumentType::mark))
|
||||
this.implementation.registerCommandArgument(cobblemonResource("transform_type"), TransformTypeArgumentType::class, SingletonArgumentInfo.contextFree(TransformTypeArgumentType::transformType))
|
||||
this.implementation.registerCommandArgument(cobblemonResource("model_part"), ModelPartArgumentType::class, SingletonArgumentInfo.contextFree(ModelPartArgumentType::modelPart))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ object CobblemonCommands {
|
||||
ChangeEyeHeight.register(dispatcher)
|
||||
ChangeScaleAndSize.register(dispatcher)
|
||||
ChangeWalkSpeed.register(dispatcher)
|
||||
ChangeJointScaleCommand.register(dispatcher)
|
||||
TransformModelPartCommand.register(dispatcher)
|
||||
TestCommand.register(dispatcher)
|
||||
ReloadShowdownCommand.register(dispatcher)
|
||||
ClickTextCommand.register(dispatcher)
|
||||
@@ -60,10 +60,17 @@ object CobblemonCommands {
|
||||
RenameBoxCommand.register(dispatcher)
|
||||
ChangeBoxWallpaperCommand.register(dispatcher)
|
||||
RunMolangScriptCommand.register(dispatcher)
|
||||
SpectateBattleCommand.register(dispatcher)
|
||||
CobblemonConfigCommand.register(dispatcher)
|
||||
UnlockPCBoxWallpaperCommand.register(dispatcher)
|
||||
ChangePCBoxesCommand.register(dispatcher)
|
||||
MarkGiveCommand.register(dispatcher)
|
||||
MarkTakeCommand.register(dispatcher)
|
||||
MarkGiveAllCommand.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
|
||||
CobblemonInfoCommand.register(dispatcher)
|
||||
CalculateSeatPositionCommand.register(dispatcher)
|
||||
}
|
||||
}
|
||||
@@ -99,6 +99,9 @@ object CobblemonFlows : DataRegistry {
|
||||
runtime.environment.query.addFunction("cancel") { cancelable }
|
||||
}
|
||||
|
||||
// Most places in the mod use query structs so it's probably nicer to present the things as query values.
|
||||
context.forEach { (key, value) -> runtime.environment.query.addFunction(key) { return@addFunction value } }
|
||||
|
||||
functions.forEach { (name, function) -> runtime.environment.query.addFunction(name, function) }
|
||||
|
||||
flows[eventResourceLocation]?.forEach {
|
||||
|
||||
@@ -281,7 +281,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
// Evolution items
|
||||
@JvmField val LINK_CABLE = create("link_cable", LinkCableItem())
|
||||
@JvmField val DRAGON_SCALE = noSettingsItem("dragon_scale")
|
||||
@JvmField val KINGS_ROCK = noSettingsItem("kings_rock")
|
||||
@JvmField val METAL_COAT = noSettingsItem("metal_coat")
|
||||
@JvmField val UPGRADE = noSettingsItem("upgrade")
|
||||
@JvmField val DUBIOUS_DISC = noSettingsItem("dubious_disc")
|
||||
@@ -660,6 +659,19 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val WATER_STONE = noSettingsItem("water_stone")
|
||||
|
||||
val wearables = mutableListOf<WearableItem>()
|
||||
//Wearable items (these items should have a corresponding 3D model)
|
||||
@JvmField val BLACK_GLASSES = wearableItem("black_glasses")
|
||||
@JvmField val CHOICE_BAND = wearableItem("choice_band")
|
||||
@JvmField val CHOICE_SPECS = wearableItem("choice_specs")
|
||||
@JvmField val EXP_SHARE = wearableItem("exp_share")
|
||||
@JvmField val FOCUS_BAND = wearableItem("focus_band")
|
||||
@JvmField val KINGS_ROCK = wearableItem("kings_rock")
|
||||
@JvmField val MUSCLE_BAND = wearableItem("muscle_band")
|
||||
@JvmField val ROCKY_HELMET = wearableItem("rocky_helmet")
|
||||
@JvmField val SAFETY_GOGGLES = wearableItem("safety_goggles")
|
||||
@JvmField val WISE_GLASSES = wearableItem("wise_glasses")
|
||||
|
||||
// Held Items
|
||||
@JvmField
|
||||
val ABILITY_SHIELD = heldItem("ability_shield")
|
||||
@@ -676,8 +688,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val BLACK_BELT = heldItem("black_belt")
|
||||
@JvmField
|
||||
val BLACK_GLASSES = heldItem("black_glasses")
|
||||
@JvmField
|
||||
val BLACK_SLUDGE = heldItem("black_sludge")
|
||||
@JvmField
|
||||
val BLUNDER_POLICY = heldItem("blunder_policy")
|
||||
@@ -686,12 +696,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val CHARCOAL = heldItem("charcoal_stick", remappedName = "charcoal")
|
||||
@JvmField
|
||||
val CHOICE_BAND = heldItem("choice_band")
|
||||
@JvmField
|
||||
val CHOICE_SCARF = heldItem("choice_scarf")
|
||||
@JvmField
|
||||
val CHOICE_SPECS = heldItem("choice_specs")
|
||||
@JvmField
|
||||
val CLEANSE_TAG = heldItem("cleanse_tag")
|
||||
// @JvmField
|
||||
// val CLEAR_AMULET = heldItem("clear_amulet")
|
||||
@@ -712,8 +718,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val EVIOLITE = heldItem("eviolite")
|
||||
@JvmField
|
||||
val EXP_SHARE = heldItem("exp_share")
|
||||
@JvmField
|
||||
val EXPERT_BELT = heldItem("expert_belt")
|
||||
@JvmField
|
||||
val FAIRY_FEATHER = heldItem("fairy_feather")
|
||||
@@ -722,8 +726,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val FLOAT_STONE = heldItem("float_stone")
|
||||
@JvmField
|
||||
val FOCUS_BAND = heldItem("focus_band")
|
||||
@JvmField
|
||||
val FOCUS_SASH = heldItem("focus_sash")
|
||||
@JvmField
|
||||
val GRASSY_SEED = heldItem("grassy_seed")
|
||||
@@ -758,8 +760,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val MIRACLE_SEED = heldItem("miracle_seed")
|
||||
@JvmField
|
||||
val MUSCLE_BAND = heldItem("muscle_band")
|
||||
@JvmField
|
||||
val MYSTIC_WATER = heldItem("mystic_water")
|
||||
@JvmField
|
||||
val NEVER_MELT_ICE = heldItem("never_melt_ice")
|
||||
@@ -790,12 +790,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val RING_TARGET = heldItem("ring_target")
|
||||
@JvmField
|
||||
val ROCKY_HELMET = heldItem("rocky_helmet")
|
||||
@JvmField
|
||||
val ROOM_SERVICE = heldItem("room_service")
|
||||
@JvmField
|
||||
val SAFETY_GOGGLES = heldItem("safety_goggles")
|
||||
@JvmField
|
||||
val SCOPE_LENS = heldItem("scope_lens")
|
||||
@JvmField
|
||||
val SHARP_BEAK = heldItem("sharp_beak")
|
||||
@@ -832,8 +828,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val WIDE_LENS = heldItem("wide_lens")
|
||||
@JvmField
|
||||
val WISE_GLASSES = heldItem("wise_glasses")
|
||||
@JvmField
|
||||
val ZOOM_LENS = heldItem("zoom_lens")
|
||||
@JvmField
|
||||
val MENTAL_HERB = compostableHeldItem("mental_herb", null, 1F)
|
||||
@@ -1263,6 +1257,16 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
return item
|
||||
}
|
||||
|
||||
private fun wearableItem(name: String, heldItemRemappedName: String? = null): CobblemonItem = create(
|
||||
name,
|
||||
WearableItem(name).also {
|
||||
wearables.add(it)
|
||||
if (heldItemRemappedName != null) {
|
||||
CobblemonHeldItemManager.registerRemap(it, heldItemRemappedName)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
private fun heldItem(name: String, remappedName: String? = null): CobblemonItem = create(
|
||||
name,
|
||||
CobblemonItem(Item.Properties()).also {
|
||||
@@ -1278,6 +1282,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
CobblemonHeldItemManager.registerRemap(it, remappedName)
|
||||
CobblemonHeldItemManager.registerRemap(Items.BONE, "thickclub")
|
||||
CobblemonHeldItemManager.registerRemap(Items.SNOWBALL, "snowball")
|
||||
CobblemonHeldItemManager.registerRemap(Items.GOLD_BLOCK, "bignugget")
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.client.net.CalculateSeatPositionsHandler
|
||||
import com.cobblemon.mod.common.client.net.PlayerInteractOptionsHandler
|
||||
import com.cobblemon.mod.common.client.net.SetClientPlayerDataHandler
|
||||
import com.cobblemon.mod.common.client.net.animation.PlayPosableAnimationHandler
|
||||
@@ -28,11 +29,13 @@ import com.cobblemon.mod.common.client.net.gui.PokedexUIPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.SummaryUIPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.npc.CloseNPCEditorHandler
|
||||
import com.cobblemon.mod.common.client.net.npc.OpenNPCEditorHandler
|
||||
import com.cobblemon.mod.common.client.net.orientation.S2CUpdateOrientationHandler
|
||||
import com.cobblemon.mod.common.client.net.pasture.ClosePastureHandler
|
||||
import com.cobblemon.mod.common.client.net.pasture.OpenPastureHandler
|
||||
import com.cobblemon.mod.common.client.net.pasture.PokemonPasturedHandler
|
||||
import com.cobblemon.mod.common.client.net.pasture.PokemonUnpasturedHandler
|
||||
import com.cobblemon.mod.common.client.net.pokedex.ServerConfirmedRegisterHandler
|
||||
import com.cobblemon.mod.common.client.net.pokemon.update.ClientboundUpdateRidingStateHandler
|
||||
import com.cobblemon.mod.common.client.net.pokemon.update.PokemonUpdatePacketHandler
|
||||
import com.cobblemon.mod.common.client.net.settings.OpenCobblemonConfigEditorHandler
|
||||
import com.cobblemon.mod.common.client.net.settings.ServerSettingsPacketHandler
|
||||
@@ -56,6 +59,7 @@ import com.cobblemon.mod.common.client.net.trade.TradeProcessStartedHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeStartedHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeUpdatedHandler
|
||||
import com.cobblemon.mod.common.net.PacketRegisterInfo
|
||||
import com.cobblemon.mod.common.net.messages.client.CalculateSeatPositionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.PlayerInteractOptionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
|
||||
@@ -71,6 +75,7 @@ import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityP
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.npc.CloseNPCEditorPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.npc.OpenNPCEditorPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateOrientationPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pasture.ClosePasturePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pasture.OpenPasturePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pasture.PokemonPasturedPacket
|
||||
@@ -104,6 +109,7 @@ import com.cobblemon.mod.common.net.messages.client.storage.pc.SetPCPokemonPacke
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.ChangePCBoxWallpaperPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.RequestPCBoxWallpapersPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.SetPCBoxWallpapersPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.UnlockPCBoxWallpaperPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.toast.ToastPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeAcceptanceChangedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeCancelledPacket
|
||||
@@ -139,7 +145,7 @@ import com.cobblemon.mod.common.net.messages.server.callback.partymove.PartyPoke
|
||||
import com.cobblemon.mod.common.net.messages.server.dialogue.EscapeDialoguePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.dialogue.InputToDialoguePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.npc.SaveNPCPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.orientation.C2SUpdateOrientationPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.orientation.ServerboundUpdateOrientationPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pasture.PasturePokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pasture.UnpastureAllPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pasture.UnpasturePokemonPacket
|
||||
@@ -148,6 +154,9 @@ import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.StartScannin
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.interact.InteractPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetItemHiddenPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetNicknamePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetActiveMarkPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetMarkingsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.ServerboundUpdateRidingStatePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.evolution.AcceptEvolutionPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.starter.RequestStarterScreenPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.SwapPCPartyPokemonPacket
|
||||
@@ -190,6 +199,9 @@ import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.StartScanning
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.interact.InteractPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetItemHiddenHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetNicknameHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetActiveMarkHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetMarkingsHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.ServerboundUpdateRidingStateHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.starter.RequestStarterScreenHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.starter.SelectStarterPacketHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.BenchMoveHandler
|
||||
@@ -262,6 +274,13 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(DmaxLevelUpdatePacket.ID, DmaxLevelUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(GmaxFactorUpdatePacket.ID, GmaxFactorUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(CosmeticItemUpdatePacket.ID, CosmeticItemUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(ActiveMarkUpdatePacket.ID, ActiveMarkUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(MarkAddUpdatePacket.ID, MarkAddUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(MarkRemoveUpdatePacket.ID, MarkRemoveUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(MarkPotentialAddUpdatePacket.ID, MarkPotentialAddUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(MarksUpdatePacket.ID, MarksUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(MarksPotentialUpdatePacket.ID, MarksPotentialUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(MarkingsUpdatePacket.ID, MarkingsUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
|
||||
// Evolution start
|
||||
list.add(PacketRegisterInfo(AddEvolutionPacket.ID, AddEvolutionPacket::decode, PokemonUpdatePacketHandler()))
|
||||
@@ -287,6 +306,7 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(RequestPCBoxWallpapersPacket.ID, RequestPCBoxWallpapersPacket::decode, RequestPCBoxWallpapersHandler))
|
||||
list.add(PacketRegisterInfo(SetPCBoxWallpapersPacket.ID, SetPCBoxWallpapersPacket::decode, SetPCBoxWallpapersHandler))
|
||||
list.add(PacketRegisterInfo(ChangePCBoxWallpaperPacket.ID, ChangePCBoxWallpaperPacket::decode, ChangePCBoxWallpaperHandler))
|
||||
list.add(PacketRegisterInfo(UnlockPCBoxWallpaperPacket.ID, UnlockPCBoxWallpaperPacket::decode, UnlockPCBoxWallpaperHandler))
|
||||
|
||||
// UI Packets
|
||||
list.add(PacketRegisterInfo(SummaryUIPacket.ID, SummaryUIPacket::decode, SummaryUIPacketHandler))
|
||||
@@ -353,6 +373,7 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(FishingBaitRegistrySyncPacket.ID, FishingBaitRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(FlowRegistrySyncPacket.ID, FlowRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(CosmeticItemAssignmentSyncPacket.ID, CosmeticItemAssignmentSyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(MarkRegistrySyncPacket.ID, MarkRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
|
||||
// Effects
|
||||
list.add(PacketRegisterInfo(SpawnSnowstormParticlePacket.ID, SpawnSnowstormParticlePacket::decode, SpawnSnowstormParticleHandler))
|
||||
@@ -383,6 +404,9 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(PokemonPasturedPacket.ID, PokemonPasturedPacket::decode, PokemonPasturedHandler))
|
||||
list.add(PacketRegisterInfo(PokemonUnpasturedPacket.ID, PokemonUnpasturedPacket::decode, PokemonUnpasturedHandler))
|
||||
|
||||
// Orientation
|
||||
list.add(PacketRegisterInfo(ClientboundUpdateOrientationPacket.ID, ClientboundUpdateOrientationPacket::decode, S2CUpdateOrientationHandler))
|
||||
|
||||
// Behaviours
|
||||
list.add(PacketRegisterInfo(PlayPosableAnimationPacket.ID, PlayPosableAnimationPacket::decode, PlayPosableAnimationHandler))
|
||||
|
||||
@@ -405,6 +429,13 @@ object CobblemonNetwork {
|
||||
|
||||
// Pokédex scanning
|
||||
list.add(PacketRegisterInfo(ServerConfirmedRegisterPacket.ID, ServerConfirmedRegisterPacket::decode, ServerConfirmedRegisterHandler))
|
||||
|
||||
// Debug / cheats
|
||||
list.add(PacketRegisterInfo(CalculateSeatPositionsPacket.ID, CalculateSeatPositionsPacket::decode, CalculateSeatPositionsHandler))
|
||||
|
||||
// Riding
|
||||
list.add(PacketRegisterInfo(ClientboundUpdateRidingStatePacket.ID, ClientboundUpdateRidingStatePacket::decode, ClientboundUpdateRidingStateHandler))
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
@@ -413,6 +444,8 @@ object CobblemonNetwork {
|
||||
// Pokemon Update Packets
|
||||
list.add(PacketRegisterInfo(SetNicknamePacket.ID, SetNicknamePacket::decode, SetNicknameHandler))
|
||||
list.add(PacketRegisterInfo(SetItemHiddenPacket.ID, SetItemHiddenPacket::decode, SetItemHiddenHandler))
|
||||
list.add(PacketRegisterInfo(SetActiveMarkPacket.ID, SetActiveMarkPacket::decode, SetActiveMarkHandler))
|
||||
list.add(PacketRegisterInfo(SetMarkingsPacket.ID, SetMarkingsPacket::decode, SetMarkingsHandler))
|
||||
|
||||
// Evolution Packets
|
||||
list.add(PacketRegisterInfo(AcceptEvolutionPacket.ID, AcceptEvolutionPacket::decode, AcceptEvolutionHandler))
|
||||
@@ -440,6 +473,7 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(RequestRenamePCBoxPacket.Companion.ID, RequestRenamePCBoxPacket.Companion::decode, RequestRenamePCBoxHandler))
|
||||
list.add(PacketRegisterInfo(PCBoxWallpapersPacket.ID, PCBoxWallpapersPacket::decode, PCBoxWallpapersHandler))
|
||||
list.add(PacketRegisterInfo(RequestChangePCBoxWallpaperPacket.ID, RequestChangePCBoxWallpaperPacket::decode, RequestChangePCBoxWallpaperHandler))
|
||||
list.add(PacketRegisterInfo(MarkPCBoxWallpapersSeenPacket.ID, MarkPCBoxWallpapersSeenPacket::decode, MarkPCBoxWallpapersSeenHandler))
|
||||
|
||||
// Starter packets
|
||||
list.add(PacketRegisterInfo(SelectStarterPacket.ID, SelectStarterPacket::decode, SelectStarterPacketHandler))
|
||||
@@ -499,8 +533,9 @@ object CobblemonNetwork {
|
||||
// NPC packets
|
||||
list.add(PacketRegisterInfo(SaveNPCPacket.ID, SaveNPCPacket::decode, SaveNPCHandler))
|
||||
|
||||
// Orientation packet(s)
|
||||
list.add(PacketRegisterInfo(C2SUpdateOrientationPacket.ID, C2SUpdateOrientationPacket::decode, OrientationPacketHandler))
|
||||
// Riding packet(s)
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateOrientationPacket.ID, ServerboundUpdateOrientationPacket::decode, OrientationPacketHandler))
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateRidingStatePacket.ID, ServerboundUpdateRidingStatePacket::decode, ServerboundUpdateRidingStateHandler))
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
@@ -40,6 +40,8 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
val PC_RELEASE = this.create("pc.release")
|
||||
@JvmField
|
||||
val PC_CLICK = this.create("pc.click")
|
||||
@JvmField
|
||||
val PC_WALLPAPER_UNLOCK = this.create("pc.wallpaper.unlock")
|
||||
|
||||
@JvmField
|
||||
val VILLAGER_WORK_NURSE = this.create("entity.villager.work_nurse")
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.storage.pc.UnlockablePCWallpaper
|
||||
import com.cobblemon.mod.common.util.adapters.ExpressionAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.ExpressionLikeAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.TextAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
|
||||
/**
|
||||
* A data registry for unlockable wallpapers that can be applied to a PC box. This is different to a resource packed
|
||||
* wallpaper, as those are purely client-side and don't require any unlocking.
|
||||
*
|
||||
* See docs/pc_wallpapers.md
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 9th, 2025
|
||||
*/
|
||||
object CobblemonUnlockableWallpapers : JsonDataRegistry<UnlockablePCWallpaper> {
|
||||
override val gson: Gson = GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
|
||||
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.registerTypeAdapter(Component::class.java, TextAdapter)
|
||||
.create()
|
||||
|
||||
override val typeToken = TypeToken.get(UnlockablePCWallpaper::class.java)
|
||||
override val resourcePath = "unlockable_pc_box_wallpapers"
|
||||
override val id: ResourceLocation = cobblemonResource("unlockable_pc_box_wallpapers")
|
||||
override val type = PackType.SERVER_DATA
|
||||
override val observable = SimpleObservable<CobblemonUnlockableWallpapers>()
|
||||
|
||||
val unlockableWallpapers = mutableMapOf<ResourceLocation, UnlockablePCWallpaper>()
|
||||
|
||||
override fun sync(player: ServerPlayer) { /* These don't sync, they're applied to a PC instance from which they're hooked into RequestChangePCBoxWallpaperHandler. */ }
|
||||
|
||||
override fun reload(data: Map<ResourceLocation, UnlockablePCWallpaper>) {
|
||||
unlockableWallpapers.clear()
|
||||
data.forEach { (id, value) -> value.id = id }
|
||||
unlockableWallpapers.putAll(data)
|
||||
observable.emit(this)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -28,7 +28,7 @@ class BattleCountableCriterion(
|
||||
val CODEC: Codec<BattleCountableCriterion> = RecordCodecBuilder.create { it.group(
|
||||
ContextAwarePredicate.CODEC.optionalFieldOf("player").forGetter(BattleCountableCriterion::playerCtx),
|
||||
Codec.STRING.listOf().optionalFieldOf("battle_types", listOf("any")).forGetter(BattleCountableCriterion::battleTypes),
|
||||
Codec.INT.optionalFieldOf( "count", 0).forGetter(BattleCountableCriterion::count)
|
||||
Codec.INT.optionalFieldOf("count", 0).forGetter(BattleCountableCriterion::count)
|
||||
).apply(it, ::BattleCountableCriterion) }
|
||||
}
|
||||
|
||||
|
||||
+7
-1
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.conditional
|
||||
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.core.Registry
|
||||
|
||||
/**
|
||||
@@ -17,5 +18,10 @@ import net.minecraft.core.Registry
|
||||
* @since July 16th, 2022
|
||||
*/
|
||||
interface RegistryLikeCondition<T> {
|
||||
fun fits(t: T, registry: Registry<T>): Boolean
|
||||
fun fits(t: T, registry: Registry<T>): Boolean {
|
||||
val holder = registry.wrapAsHolder(t)
|
||||
return holder != null && fits(holder)
|
||||
}
|
||||
|
||||
fun fits(holder: Holder<T>): Boolean
|
||||
}
|
||||
+3
-2
@@ -9,7 +9,7 @@
|
||||
package com.cobblemon.mod.common.api.conditional
|
||||
|
||||
import com.google.gson.JsonElement
|
||||
import net.minecraft.core.Registry
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
@@ -24,5 +24,6 @@ open class RegistryLikeIdentifierCondition<T : Any>(val identifier: ResourceLoca
|
||||
constructor: (ResourceLocation) -> RegistryLikeIdentifierCondition<T>
|
||||
): (JsonElement) -> RegistryLikeIdentifierCondition<T>? = { constructor(ResourceLocation.parse(it.asString)) }
|
||||
}
|
||||
override fun fits(t: T, registry: Registry<T>) = registry.getKey(t) == identifier
|
||||
|
||||
override fun fits(t: Holder<T>) = t.`is`(identifier)
|
||||
}
|
||||
+2
-12
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.api.conditional
|
||||
|
||||
import com.google.gson.JsonElement
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.core.Registry
|
||||
import net.minecraft.resources.ResourceKey
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
@@ -39,16 +40,5 @@ open class RegistryLikeTagCondition<T : Any>(val tag: TagKey<T>) : RegistryLikeC
|
||||
}
|
||||
}
|
||||
|
||||
override fun fits(t: T, registry: Registry<T>): Boolean {
|
||||
// val registryHasTag = registry.containsTag(tag)
|
||||
// if (!registryHasTag) {
|
||||
//// LOGGER.warn("No tag in registry: ${tag.id}")
|
||||
// return false
|
||||
// }
|
||||
|
||||
return registry.getResourceKey(t)
|
||||
.flatMap(registry::getHolder)
|
||||
.map { entry -> entry.`is`(tag) }
|
||||
.orElse(false)
|
||||
}
|
||||
override fun fits(t: Holder<T>) = t.`is`(tag)
|
||||
}
|
||||
@@ -20,4 +20,4 @@ interface EntitySideDelegate<T : Entity> {
|
||||
fun tick(entity: T) {}
|
||||
fun onSyncedDataUpdated(data: EntityDataAccessor<*>) {}
|
||||
fun addToStruct(struct: QueryStruct) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,4 +21,6 @@ interface PokemonSideDelegate : EntitySideDelegate<PokemonEntity> {
|
||||
fun updatePostDeath() {}
|
||||
fun handleStatus(status: Byte) {}
|
||||
fun spawnShinyParticle(player: Player) {}
|
||||
}
|
||||
fun positionRider(passenger: Entity, positionUpdater: MoveFunction)
|
||||
fun updateAge(age: Int) {}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ import com.cobblemon.mod.common.api.events.storage.ChangePCBoxWallpaperEvent
|
||||
import com.cobblemon.mod.common.api.events.storage.ReleasePokemonEvent
|
||||
import com.cobblemon.mod.common.api.events.storage.RenamePCBoxEvent
|
||||
import com.cobblemon.mod.common.api.events.storage.WallpaperCollectionEvent
|
||||
import com.cobblemon.mod.common.api.events.storage.WallpaperUnlockedEvent
|
||||
import com.cobblemon.mod.common.api.events.world.BigRootPropagatedEvent
|
||||
import com.cobblemon.mod.common.api.reactive.CancelableObservable
|
||||
import com.cobblemon.mod.common.api.reactive.EventObservable
|
||||
@@ -179,6 +180,8 @@ object CobblemonEvents {
|
||||
|
||||
@JvmField
|
||||
val WALLPAPER_COLLECTION_EVENT = EventObservable<WallpaperCollectionEvent>()
|
||||
@JvmField
|
||||
val WALLPAPER_UNLOCKED_EVENT = CancelableObservable<WallpaperUnlockedEvent>()
|
||||
|
||||
@JvmField
|
||||
val CHANGE_PC_BOX_WALLPAPER_EVENT_PRE = CancelableObservable<ChangePCBoxWallpaperEvent.Pre>()
|
||||
|
||||
+13
-1
@@ -8,10 +8,16 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.fishing
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.entity.fishing.PokeRodFishingBobberEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.itemRegistry
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
/**
|
||||
@@ -55,5 +61,11 @@ interface BobberSpawnPokemonEvent {
|
||||
val chosenBucket: SpawnBucket,
|
||||
val bait: ItemStack,
|
||||
val pokemon: PokemonEntity
|
||||
) : BobberSpawnPokemonEvent
|
||||
) : BobberSpawnPokemonEvent {
|
||||
val context = mutableMapOf<String, MoValue>(
|
||||
"chosen_bucket" to ObjectValue(chosenBucket, { it.name }),
|
||||
"bait" to pokemon.level().itemRegistry.wrapAsHolder(bait.item).asMoLangValue(Registries.ITEM),
|
||||
"pokemon_entity" to pokemon.asMoLangValue()
|
||||
)
|
||||
}
|
||||
}
|
||||
+9
-1
@@ -8,10 +8,18 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokemon
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
data class FossilRevivedEvent (
|
||||
val pokemon: Pokemon,
|
||||
val player: ServerPlayer?
|
||||
)
|
||||
) {
|
||||
val context = mutableMapOf<String, MoValue>(
|
||||
"pokemon" to pokemon.struct,
|
||||
"player" to (player?.asMoLangValue() ?: DoubleValue.ZERO)
|
||||
)
|
||||
}
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ data class PokemonInteractionGUICreationEvent(
|
||||
|
||||
private fun getNextFreeOrientation(): Orientation {
|
||||
var largest = Orientation.TOP_LEFT
|
||||
for(orientation in Orientation.values()) {
|
||||
for (orientation in Orientation.entries) {
|
||||
if(!options.containsKey(orientation)) {
|
||||
return orientation
|
||||
} else {
|
||||
|
||||
+1
-1
@@ -22,5 +22,5 @@ import net.minecraft.server.level.ServerPlayer
|
||||
*/
|
||||
data class WallpaperCollectionEvent(
|
||||
val player: ServerPlayer,
|
||||
val wallpapers: MutableList<ResourceLocation>
|
||||
val wallpapers: MutableSet<ResourceLocation>
|
||||
)
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.events.storage
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.cobblemon.mod.common.api.storage.pc.PCStore
|
||||
import com.cobblemon.mod.common.api.storage.pc.UnlockablePCWallpaper
|
||||
import com.cobblemon.mod.common.util.asArrayValue
|
||||
import com.cobblemon.mod.common.util.getBoolean
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
/**
|
||||
* Event fired when a wallpaper is unlocked for a PC. Typically, this will have a single player, but it is not
|
||||
* technically guaranteed to due to shared PCs and offline support.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 10th, 2025
|
||||
*/
|
||||
data class WallpaperUnlockedEvent(
|
||||
val pc: PCStore,
|
||||
val wallpaper: UnlockablePCWallpaper,
|
||||
var shouldNotify: Boolean
|
||||
): Cancelable() {
|
||||
/** For shared PCs with multiple players online, and for offline PC access, the player will be null. */
|
||||
val player: ServerPlayer?
|
||||
get() = pc.getObservingPlayers().takeIf { it.size == 1 }?.first()
|
||||
|
||||
/** All online, observing players for the PC. This would typically have a single player in it. */
|
||||
val players = pc.getObservingPlayers()
|
||||
|
||||
val context: MutableMap<String, MoValue>
|
||||
get() = mutableMapOf(
|
||||
"pc" to pc.struct,
|
||||
"wallpaper" to wallpaper.struct,
|
||||
"player" to (player?.asMoLangValue() ?: DoubleValue.ZERO),
|
||||
"players" to players.asArrayValue { it.asMoLangValue() },
|
||||
"should_notify" to DoubleValue(shouldNotify)
|
||||
)
|
||||
|
||||
val functions = moLangFunctionMap(
|
||||
cancelFunc,
|
||||
"set_should_notify" to { params ->
|
||||
shouldNotify = params.getBoolean(0)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -37,6 +37,8 @@ import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.FormattedCharSequence
|
||||
import org.joml.Matrix4f
|
||||
import org.joml.Vector3f
|
||||
import org.lwjgl.opengl.GL30
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
@JvmOverloads
|
||||
fun blitk(
|
||||
@@ -364,4 +366,26 @@ fun renderSprite(matrixStack: PoseStack, sprite: ResourceLocation) {
|
||||
buffer.addVertex(matrix, 2f, 2f, 0.0f).setUv(1f, 1f)
|
||||
|
||||
BufferUploader.drawWithShader(buffer.buildOrThrow())
|
||||
}
|
||||
}
|
||||
|
||||
fun getPixelRGB(x: Int, y: Int): Triple<Int, Int, Int> {
|
||||
val window = Minecraft.getInstance().window
|
||||
val scale = window.guiScale
|
||||
val buffer = ByteBuffer.allocateDirect(4)
|
||||
|
||||
RenderSystem.readPixels(
|
||||
(x * scale).toInt(),
|
||||
(window.height - y * scale - scale).toInt(),
|
||||
1,
|
||||
1,
|
||||
GL30.GL_RGBA,
|
||||
GL30.GL_UNSIGNED_BYTE,
|
||||
buffer
|
||||
)
|
||||
|
||||
return Triple(
|
||||
buffer[0].toInt() and 0xFF,
|
||||
buffer[1].toInt() and 0xFF,
|
||||
buffer[2].toInt() and 0xFF
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.mark
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.StringRepresentable
|
||||
|
||||
class Mark(
|
||||
identifier: ResourceLocation,
|
||||
val name: String,
|
||||
val description: String,
|
||||
val title: String?,
|
||||
@SerializedName(value = "titleColor", alternate = ["titleColour"])
|
||||
val titleColour: String?,
|
||||
val texture: ResourceLocation,
|
||||
val replace: List<ResourceLocation>?,
|
||||
val group: String?,
|
||||
val chance: Float = 0F,
|
||||
val indexNumber: Int?
|
||||
): StringRepresentable {
|
||||
|
||||
companion object {
|
||||
internal fun decode(buffer: RegistryFriendlyByteBuf): Mark {
|
||||
return Mark(
|
||||
buffer.readIdentifier(),
|
||||
buffer.readString(),
|
||||
buffer.readString(),
|
||||
buffer.readNullable { buffer.readString() },
|
||||
buffer.readNullable { buffer.readString() },
|
||||
buffer.readResourceLocation(),
|
||||
buffer.readNullable { buffer.readList { buffer.readResourceLocation() } },
|
||||
buffer.readNullable { buffer.readString() },
|
||||
buffer.readFloat(),
|
||||
buffer.readNullable { buffer.readInt() }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Transient
|
||||
var identifier: ResourceLocation = identifier
|
||||
internal set
|
||||
|
||||
override fun getSerializedName(): String = identifier.toString()
|
||||
|
||||
fun getName(): MutableComponent = name.asTranslated()
|
||||
|
||||
fun getDescription(): MutableComponent = description.asTranslated()
|
||||
|
||||
fun getTitle(name: MutableComponent): MutableComponent {
|
||||
title?.let {
|
||||
try {
|
||||
return if (titleColour != null) it.asTranslated(name.withColor(-1)).withColor(titleColour.toInt(16))
|
||||
else it.asTranslated(name)
|
||||
} catch (e: Exception) {
|
||||
Cobblemon.LOGGER.error("Error parsing titleColor hexadecimal color code for {}", identifier, e)
|
||||
return it.asTranslated(name)
|
||||
}
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
fun getChanceGroup(): Pair<String, Float> = Pair(group ?: chance.toString(), chance)
|
||||
|
||||
internal fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeIdentifier(identifier)
|
||||
buffer.writeString(name)
|
||||
buffer.writeString(description)
|
||||
buffer.writeNullable(title) { _, v -> buffer.writeString(v) }
|
||||
buffer.writeNullable(titleColour) { _, v -> buffer.writeString(v) }
|
||||
buffer.writeResourceLocation(texture)
|
||||
buffer.writeNullable(replace) { _, v -> buffer.writeCollection(v) { _, resource -> buffer.writeResourceLocation(resource) } }
|
||||
buffer.writeNullable(group) { _, v -> buffer.writeString(v) }
|
||||
buffer.writeFloat(chance)
|
||||
buffer.writeNullable(indexNumber) { _, v -> buffer.writeInt(v) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.mark
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.net.messages.client.data.MarkRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
|
||||
object Marks: JsonDataRegistry<Mark> {
|
||||
|
||||
override val id: ResourceLocation = cobblemonResource("marks")
|
||||
override val type: PackType = PackType.SERVER_DATA
|
||||
override val observable = SimpleObservable<Marks>()
|
||||
override val typeToken: TypeToken<Mark> = TypeToken.get(Mark::class.java)
|
||||
override val resourcePath: String = "marks"
|
||||
|
||||
override val gson = GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.create()
|
||||
|
||||
private val marks = hashMapOf<ResourceLocation, Mark>()
|
||||
|
||||
override fun reload(data: Map<ResourceLocation, Mark>) {
|
||||
this.marks.clear()
|
||||
data.forEach { (identifier, mark) ->
|
||||
try {
|
||||
mark.identifier = identifier
|
||||
this.marks[identifier] = mark
|
||||
} catch (e: Exception) {
|
||||
Cobblemon.LOGGER.error("Skipped loading the {} mark", identifier, e)
|
||||
}
|
||||
}
|
||||
Cobblemon.LOGGER.info("Loaded {} marks", this.marks.size)
|
||||
this.observable.emit(this)
|
||||
}
|
||||
|
||||
override fun sync(player: ServerPlayer) {
|
||||
MarkRegistrySyncPacket(this.all()).sendToPlayer(player)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all loaded [Mark]s.
|
||||
*/
|
||||
fun all() = this.marks.values.toList()
|
||||
fun identifiers(): Collection<String> = marks.keys.toSet().map { it.toString() }
|
||||
|
||||
/**
|
||||
* Gets a [Mark] by its [ResourceLocation].
|
||||
* @param identifier The identifier of the mark.
|
||||
* @return The [Mark] if loaded, otherwise null.
|
||||
*/
|
||||
fun getByIdentifier(identifier: ResourceLocation): Mark? = this.marks[identifier]
|
||||
}
|
||||
@@ -17,25 +17,17 @@ import com.bedrockk.molang.runtime.struct.VariableStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonActivities
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.*
|
||||
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
|
||||
import com.cobblemon.mod.common.api.dialogue.PlayerDialogueFaceProvider
|
||||
import com.cobblemon.mod.common.api.dialogue.ReferenceDialogueFaceProvider
|
||||
import com.cobblemon.mod.common.api.mark.Marks
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
|
||||
import com.cobblemon.mod.common.api.moves.animations.NPCProvider
|
||||
import com.cobblemon.mod.common.api.pokedex.AbstractPokedexManager
|
||||
import com.cobblemon.mod.common.api.pokedex.CaughtCount
|
||||
import com.cobblemon.mod.common.api.pokedex.CaughtPercent
|
||||
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
|
||||
import com.cobblemon.mod.common.api.pokedex.PokedexManager
|
||||
import com.cobblemon.mod.common.api.pokedex.SeenCount
|
||||
import com.cobblemon.mod.common.api.pokedex.SeenPercent
|
||||
import com.cobblemon.mod.common.api.pokedex.*
|
||||
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
|
||||
@@ -46,11 +38,11 @@ import com.cobblemon.mod.common.api.storage.party.PartyStore
|
||||
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore
|
||||
import com.cobblemon.mod.common.api.storage.pc.PCPosition
|
||||
import com.cobblemon.mod.common.api.storage.pc.PCStore
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
|
||||
import com.cobblemon.mod.common.client.particle.BedrockParticleOptionsRepository
|
||||
import com.cobblemon.mod.common.client.particle.ParticleStorm
|
||||
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.WaveFunctions
|
||||
import com.cobblemon.mod.common.entity.PosableEntity
|
||||
import com.cobblemon.mod.common.entity.npc.NPCBattleActor
|
||||
@@ -70,20 +62,14 @@ import com.cobblemon.mod.common.pokemon.evolution.variants.LevelUpEvolution
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.TradeEvolution
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.mojang.datafixers.util.Either
|
||||
import java.util.UUID
|
||||
import kotlin.math.sqrt
|
||||
import net.minecraft.client.multiplayer.ClientLevel
|
||||
import net.minecraft.commands.arguments.EntityAnchorArgument
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.core.Registry
|
||||
import net.minecraft.core.RegistryAccess
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.DoubleTag
|
||||
import net.minecraft.nbt.ListTag
|
||||
import net.minecraft.nbt.StringTag
|
||||
import net.minecraft.nbt.Tag
|
||||
import net.minecraft.nbt.*
|
||||
import net.minecraft.resources.ResourceKey
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.MinecraftServer
|
||||
@@ -92,22 +78,24 @@ import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.tags.TagKey
|
||||
import net.minecraft.world.damagesource.DamageSource
|
||||
import net.minecraft.world.damagesource.DamageTypes
|
||||
import net.minecraft.world.entity.EntityType
|
||||
import net.minecraft.world.entity.LightningBolt
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.*
|
||||
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.MemoryStatus
|
||||
import net.minecraft.world.entity.ai.memory.WalkTarget
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.Level.ExplosionInteraction
|
||||
import net.minecraft.world.level.biome.Biome
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.dimension.DimensionType
|
||||
import net.minecraft.world.level.levelgen.Heightmap
|
||||
import net.minecraft.world.phys.AABB
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import java.util.*
|
||||
import kotlin.math.sqrt
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
* Holds a bunch of useful MoLang trickery that can be used or extended in API
|
||||
@@ -228,6 +216,22 @@ object MoLangFunctions {
|
||||
val z = params.getInt(2)
|
||||
return@put DoubleValue(world.isRainingAt(BlockPos(x, y, z)))
|
||||
}
|
||||
map.put("is_snowing_at") { params ->
|
||||
val x = params.getInt(0)
|
||||
val y = params.getInt(1)
|
||||
val z = params.getInt(2)
|
||||
val blockPos = BlockPos(x, y, z)
|
||||
if (!world.isRaining()) {
|
||||
return@put DoubleValue.ZERO
|
||||
} else if (!world.canSeeSky(blockPos)) {
|
||||
return@put DoubleValue.ZERO
|
||||
} else if (world.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, blockPos).getY() > blockPos.getY()) {
|
||||
return@put DoubleValue.ZERO
|
||||
} else {
|
||||
val biome = world.getBiome(blockPos).value() as Biome
|
||||
return@put DoubleValue(biome.getPrecipitationAt(blockPos) == Biome.Precipitation.SNOW)
|
||||
}
|
||||
}
|
||||
map.put("is_chunk_loaded_at") { params ->
|
||||
val x = params.getInt(0)
|
||||
val y = params.getInt(1)
|
||||
@@ -283,16 +287,32 @@ object MoLangFunctions {
|
||||
} as? ServerPlayer
|
||||
val pos = Vec3(x, y, z)
|
||||
|
||||
if (world !is ClientLevel) {
|
||||
val packet = SpawnSnowstormParticlePacket(particle, pos)
|
||||
if (player != null) {
|
||||
packet.sendToPlayer(player)
|
||||
} else {
|
||||
packet.sendToPlayersAround(x, y, z, 64.0, world.dimension())
|
||||
}
|
||||
val packet = SpawnSnowstormParticlePacket(particle, pos)
|
||||
if (player != null) {
|
||||
packet.sendToPlayer(player)
|
||||
} else {
|
||||
val effect = BedrockParticleOptionsRepository.getEffect(particle) ?: return@put DoubleValue.ZERO
|
||||
ParticleStorm.createAtPosition(world, effect, pos).spawn()
|
||||
packet.sendToPlayersAround(x, y, z, 64.0, world.dimension())
|
||||
}
|
||||
}
|
||||
map.put("spawn_pokemon") { params ->
|
||||
val x = params.getInt(0)
|
||||
val y = params.getInt(1)
|
||||
val z = params.getInt(2)
|
||||
val props = params.getString(3).toProperties()
|
||||
|
||||
val pos = BlockPos(x, y, z)
|
||||
|
||||
if (!Level.isInSpawnableBounds(pos)) {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
|
||||
val pokemon = props.createEntity(world)
|
||||
pokemon.moveTo(pos, pokemon.yRot, pokemon.xRot)
|
||||
|
||||
if (world.addFreshEntity(pokemon)) {
|
||||
return@put pokemon.struct
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("get_entities_around") { params ->
|
||||
@@ -306,6 +326,11 @@ object MoLangFunctions {
|
||||
.map { it.asMostSpecificMoLangValue() }
|
||||
.asArrayValue()
|
||||
}
|
||||
map.put("is_healer_in_use") { params ->
|
||||
val pos = params.get<ArrayStruct>(0).asBlockPos()
|
||||
val healer = world.getBlockEntity(pos, CobblemonBlockEntities.HEALING_MACHINE).orElse(null) ?: return@put DoubleValue.ONE
|
||||
return@put DoubleValue(healer.isInUse)
|
||||
}
|
||||
|
||||
return@mutableListOf map
|
||||
}
|
||||
@@ -317,8 +342,8 @@ object MoLangFunctions {
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("username") { _ -> StringValue(player.gameProfile.name) }
|
||||
map.put("uuid") { _ -> StringValue(player.gameProfile.id.toString()) }
|
||||
map.put("main_held_item") { _ -> player.level().itemRegistry.wrapAsHolder(player.mainHandItem.item).asMoLangValue(Registries.ITEM) }
|
||||
map.put("off_held_item") { _ -> player.level().itemRegistry.wrapAsHolder(player.offhandItem.item).asMoLangValue(Registries.ITEM) }
|
||||
map.put("main_held_item") { _ -> player.mainHandItem.asMoLangValue(player.registryAccess()) }
|
||||
map.put("off_held_item") { _ -> player.offhandItem.asMoLangValue(player.registryAccess()) }
|
||||
map.put("face") { params -> ObjectValue(PlayerDialogueFaceProvider(player.uuid, params.getBooleanOrNull(0) != false)) }
|
||||
map.put("swing_hand") { _ -> player.swing(player.usedItemHand) }
|
||||
map.put("food_level") { _ -> DoubleValue(player.foodData.foodLevel) }
|
||||
@@ -412,11 +437,39 @@ object MoLangFunctions {
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("pokedex") { player.pokedex().struct }
|
||||
map.put("has_advancement") { params ->
|
||||
val requiredAdvancement = ResourceLocation.parse(params.getString(0))
|
||||
for (entry in player.advancements.progress) {
|
||||
if (entry.key.id == requiredAdvancement && entry.value.isDone) {
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
}
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map
|
||||
}
|
||||
)
|
||||
val entityFunctions: MutableList<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
val itemStackFunctions = mutableListOf<(ItemStack, RegistryAccess) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ stack, registryAccess ->
|
||||
val itemRegistry = registryAccess.registryOrThrow(Registries.ITEM)
|
||||
val holder = itemRegistry.wrapAsHolder(stack.item)
|
||||
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("item") { _ -> holder.asMoLangValue(Registries.ITEM) }
|
||||
map.put("count") { _ -> DoubleValue(stack.count.toDouble()) }
|
||||
map.put("damage_value") { _ -> DoubleValue(stack.damageValue) }
|
||||
map.put("max_damage") { _ -> DoubleValue(stack.maxDamage) }
|
||||
map.put("is_empty") { _ -> DoubleValue(stack.isEmpty) }
|
||||
map.put("shrink") { params -> stack.shrink(params.getInt(0)) }
|
||||
map.put("grow") { params -> stack.grow(params.getInt(0)) }
|
||||
map.put("is_of") { params -> DoubleValue(holder.`is`(params.getString(0).asIdentifierDefaultingNamespace())) }
|
||||
map.put("is_in") { params -> DoubleValue(holder.`is`(TagKey.create(Registries.ITEM, params.getString(0).replace("#", "").asIdentifierDefaultingNamespace()))) }
|
||||
return@mutableListOf map
|
||||
}
|
||||
)
|
||||
|
||||
val entityFunctions: MutableList<(Entity) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
|
||||
{ entity ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("uuid") { _ -> StringValue(entity.uuid.toString()) }
|
||||
@@ -425,48 +478,23 @@ object MoLangFunctions {
|
||||
val source = DamageSource(entity.level().registryAccess().registryOrThrow(Registries.DAMAGE_TYPE).getHolder(DamageTypes.GENERIC).get())
|
||||
entity.hurt(source, amount.toFloat())
|
||||
}
|
||||
map.put("walk_to") { params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
val speedMultiplier = params.getDoubleOrNull(3) ?: 0.35
|
||||
if (entity is PathfinderMob) {
|
||||
if (entity.brain.checkMemory(MemoryModuleType.WALK_TARGET, MemoryStatus.REGISTERED)) {
|
||||
entity.brain.setMemory(MemoryModuleType.WALK_TARGET, WalkTarget(Vec3(x, y, z), speedMultiplier.toFloat(), 1))
|
||||
entity.brain.setMemory(MemoryModuleType.LOOK_TARGET, BlockPosTracker(Vec3(x, y + entity.eyeHeight, z)))
|
||||
} else {
|
||||
entity.navigation.moveTo(x, y, z, speedMultiplier)
|
||||
entity.lookControl.setLookAt(Vec3(x, y + entity.eyeHeight, z))
|
||||
}
|
||||
}
|
||||
}
|
||||
map.put("has_walk_target") { _ ->
|
||||
if (entity is PathfinderMob) {
|
||||
DoubleValue(entity.brain.getMemory(MemoryModuleType.WALK_TARGET).isPresent || entity.isPathFinding)
|
||||
} else {
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("is_sneaking") { _ -> DoubleValue(entity.isShiftKeyDown) }
|
||||
map.put("is_sprinting") { _ -> DoubleValue(entity.isSprinting) }
|
||||
map.put("is_flying") { _ -> DoubleValue(entity.isFallFlying) }
|
||||
map.put("is_in_water") { _ -> DoubleValue(entity.isUnderWater) }
|
||||
map.put("is_in_rain") { _ -> DoubleValue(entity.isInWaterOrRain && !entity.isInWater) }
|
||||
map.put("is_touching_water_or_rain") { _ -> DoubleValue(entity.isInWaterRainOrBubble) }
|
||||
map.put("is_touching_water") { _ -> DoubleValue(entity.isInWater) }
|
||||
map.put("is_underwater") { DoubleValue(entity.getIsSubmerged()) }
|
||||
map.put("is_in_lava") { _ -> DoubleValue(entity.isInLava) }
|
||||
map.put("is_on_fire") { _ -> DoubleValue(entity.isOnFire) }
|
||||
map.put("is_invisible") { _ -> DoubleValue(entity.isInvisible) }
|
||||
map.put("is_sleeping") { _ -> DoubleValue(entity.isSleeping) }
|
||||
map.put("is_riding") { _ -> DoubleValue(entity.isPassenger) }
|
||||
map.put("health") { _ -> DoubleValue(entity.health) }
|
||||
map.put("distance_to_pos") { params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
return@put DoubleValue(sqrt(entity.distanceToSqr(Vec3(x, y, z))))
|
||||
}
|
||||
map.put("max_health") { _ -> DoubleValue(entity.maxHealth) }
|
||||
map.put("name") { _ -> StringValue(entity.effectiveName().string) }
|
||||
map.put("type") { _ ->
|
||||
entity.registryAccess().registry(Registries.ENTITY_TYPE).get().getKey(entity.type)?.toString()?.let {
|
||||
@@ -493,11 +521,6 @@ object MoLangFunctions {
|
||||
map.put("world") { _ -> entity.level().worldRegistry.wrapAsHolder(entity.level()).asWorldMoLangValue() }
|
||||
map.put("biome") { _ -> entity.level().getBiome(entity.blockPosition()).asBiomeMoLangValue() }
|
||||
map.put("is_passenger") { DoubleValue(entity.isPassenger) }
|
||||
map.put("is_healer_in_use") { params ->
|
||||
val pos = params.get<ArrayStruct>(0).asBlockPos()
|
||||
val healer = entity.level().getBlockEntity(pos, CobblemonBlockEntities.HEALING_MACHINE).orElse(null) ?: return@put DoubleValue.ONE
|
||||
return@put DoubleValue(healer.isInUse)
|
||||
}
|
||||
map.put("find_nearby_block") { params ->
|
||||
val type = params.getString(0).asIdentifierDefaultingNamespace(namespace = "minecraft")
|
||||
val isTag = type.path.startsWith("#")
|
||||
@@ -516,7 +539,7 @@ object MoLangFunctions {
|
||||
val distance = params.getDouble(0)
|
||||
val entities = entity.level().getEntities(entity, AABB.ofSize(entity.position(), distance, distance, distance))
|
||||
return@put entities
|
||||
.filterIsInstance<LivingEntity>()
|
||||
.filterIsInstance<Entity>()
|
||||
.map { it.asMostSpecificMoLangValue() }
|
||||
.asArrayValue()
|
||||
}
|
||||
@@ -573,6 +596,45 @@ object MoLangFunctions {
|
||||
packet.sendToPlayer(player)
|
||||
}
|
||||
}
|
||||
return@mutableListOf map
|
||||
}
|
||||
)
|
||||
|
||||
val livingEntityFunctions: MutableList<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ entity ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("is_living_entity") { DoubleValue.ONE }
|
||||
map.put("is_flying") { _ -> DoubleValue(entity.isFallFlying) }
|
||||
map.put("is_sleeping") { _ -> DoubleValue(entity.isSleeping) }
|
||||
map.put("health") { _ -> DoubleValue(entity.health) }
|
||||
map.put("max_health") { _ -> DoubleValue(entity.maxHealth) }
|
||||
|
||||
if (entity is PathfinderMob) {
|
||||
map.put("walk_to") { params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
val speedMultiplier = params.getDoubleOrNull(3) ?: 0.35
|
||||
|
||||
if (entity.brain.checkMemory(MemoryModuleType.WALK_TARGET, MemoryStatus.REGISTERED)) {
|
||||
entity.brain.setMemory(
|
||||
MemoryModuleType.WALK_TARGET,
|
||||
WalkTarget(Vec3(x, y, z), speedMultiplier.toFloat(), 1)
|
||||
)
|
||||
entity.brain.setMemory(
|
||||
MemoryModuleType.LOOK_TARGET,
|
||||
BlockPosTracker(Vec3(x, y + entity.eyeHeight, z))
|
||||
)
|
||||
} else {
|
||||
entity.navigation.moveTo(x, y, z, speedMultiplier)
|
||||
entity.lookControl.setLookAt(Vec3(x, y + entity.eyeHeight, z))
|
||||
}
|
||||
}
|
||||
map.put("has_walk_target") { _ ->
|
||||
DoubleValue(entity.brain.getMemory(MemoryModuleType.WALK_TARGET).isPresent || entity.isPathFinding)
|
||||
}
|
||||
}
|
||||
|
||||
map
|
||||
}
|
||||
)
|
||||
@@ -691,6 +753,8 @@ object MoLangFunctions {
|
||||
map.put("npc") { battleActor.entity.struct }
|
||||
} else if (battleActor is PlayerBattleActor) {
|
||||
map.put("player") { battleActor.entity?.asMoLangValue() ?: DoubleValue.ZERO }
|
||||
} else if (battleActor is PokemonBattleActor) {
|
||||
map.put("pokemon") { battleActor.entity?.asMoLangValue() ?: DoubleValue.ZERO }
|
||||
}
|
||||
return@mutableListOf map
|
||||
}
|
||||
@@ -729,6 +793,34 @@ object MoLangFunctions {
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map.put("owner") { pokemon.getOwnerPlayer()?.asMoLangValue() ?: DoubleValue.ZERO }
|
||||
map.put("add_marks") { params ->
|
||||
for (param in params.params) {
|
||||
val identifier = param.asString().asIdentifierDefaultingNamespace()
|
||||
val mark = Marks.getByIdentifier(identifier)
|
||||
if (mark != null) pokemon.exchangeMark(mark, true)
|
||||
}
|
||||
}
|
||||
map.put("add_marks_with_chance") { params ->
|
||||
for (param in params.params) {
|
||||
val identifier = param.asString().asIdentifierDefaultingNamespace()
|
||||
val mark = Marks.getByIdentifier(identifier)
|
||||
mark?.let {
|
||||
val probability = it.chance.coerceIn(0F, 1F) * 100
|
||||
val randomValue = Random.nextDouble(0.0, 100.0)
|
||||
if (randomValue < probability) pokemon.exchangeMark(it, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
map.put("add_potential_marks") { params ->
|
||||
for (param in params.params) {
|
||||
val identifier = param.asString().asIdentifierDefaultingNamespace()
|
||||
val mark = Marks.getByIdentifier(identifier)
|
||||
if (mark != null) pokemon.addPotentialMark(mark)
|
||||
}
|
||||
}
|
||||
map.put("apply_potential_marks") {
|
||||
return@put DoubleValue(pokemon.applyPotentialMarks())
|
||||
}
|
||||
map
|
||||
}
|
||||
)
|
||||
@@ -739,8 +831,15 @@ object MoLangFunctions {
|
||||
map.put("in_battle") { DoubleValue(pokemonEntity.isBattling) }
|
||||
map.put("is_moving") { DoubleValue((pokemonEntity.moveControl as? PokemonMoveControl)?.hasWanted() == true) }
|
||||
map.put("is_flying") { DoubleValue(pokemonEntity.getBehaviourFlag(PokemonBehaviourFlag.FLYING)) }
|
||||
map.put("is_gliding") { DoubleValue(pokemonEntity.useRidingAltPose()) }
|
||||
map.put("is_ridden") { DoubleValue(pokemonEntity.hasControllingPassenger()) }
|
||||
map.put("has_aspect") { DoubleValue(it.getString(0) in pokemonEntity.aspects) }
|
||||
map.put("is_pokemon") { DoubleValue.ONE }
|
||||
map.put("is_holding_item") { DoubleValue(!pokemonEntity.entityData.get(PokemonEntity.SHOWN_HELD_ITEM).let {
|
||||
it.isEmpty || it.`is`(CobblemonItemTags.WEARABLE_HAT_ITEMS) || it.`is`(CobblemonItemTags.WEARABLE_FACE_ITEMS)
|
||||
}) }
|
||||
map.put("is_wearing_hat") { DoubleValue(pokemonEntity.entityData.get(PokemonEntity.SHOWN_HELD_ITEM).`is`(CobblemonItemTags.WEARABLE_HAT_ITEMS)) }
|
||||
map.put("is_wearing_face") { DoubleValue(pokemonEntity.entityData.get(PokemonEntity.SHOWN_HELD_ITEM).`is`(CobblemonItemTags.WEARABLE_FACE_ITEMS)) }
|
||||
map
|
||||
}
|
||||
)
|
||||
@@ -748,6 +847,7 @@ object MoLangFunctions {
|
||||
val pokemonStoreFunctions = mutableListOf<(PokemonStore<*>) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ store ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("uuid") { StringValue(store.uuid.toString()) }
|
||||
map.put("add") { params ->
|
||||
val pokemon = params.get<ObjectValue<Pokemon>>(0)
|
||||
return@put DoubleValue(store.add(pokemon.obj))
|
||||
@@ -848,6 +948,17 @@ object MoLangFunctions {
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("get_box_count") { _ -> DoubleValue(pc.boxes.size.toDouble()) }
|
||||
map.put("has_unlocked_wallpaper") { params ->
|
||||
val wallpaper = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
return@put DoubleValue(pc.unlockedWallpapers.contains(wallpaper))
|
||||
}
|
||||
map.put("get_unlocked_wallpapers") { pc.unlockedWallpapers.asArrayValue { StringValue(it.toString()) } }
|
||||
map.put("unlock_wallpaper") {
|
||||
val wallpaper = it.getString(0).asIdentifierDefaultingNamespace()
|
||||
val playSound = it.getBooleanOrNull(1) != false
|
||||
CobblemonUnlockableWallpapers.unlockableWallpapers[wallpaper] ?: return@put DoubleValue.ZERO
|
||||
return@put DoubleValue(pc.unlockWallpaper(wallpaper, playSound))
|
||||
}
|
||||
return@mutableListOf map
|
||||
}
|
||||
)
|
||||
@@ -856,7 +967,7 @@ object MoLangFunctions {
|
||||
{ spawningContext ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
val worldValue = spawningContext.world.registryAccess().registryOrThrow(Registries.DIMENSION).wrapAsHolder(spawningContext.world).asWorldMoLangValue()
|
||||
val biomeValue = spawningContext.biomeRegistry.wrapAsHolder(spawningContext.biome).asBiomeMoLangValue()
|
||||
val biomeValue = spawningContext.biomeHolder.asBiomeMoLangValue()
|
||||
map.put("biome") { _ -> biomeValue }
|
||||
map.put("world") { _ -> worldValue }
|
||||
map.put("light") { _ -> DoubleValue(spawningContext.light.toDouble()) }
|
||||
@@ -1084,18 +1195,35 @@ object MoLangFunctions {
|
||||
}
|
||||
)
|
||||
|
||||
fun ItemStack.asMoLangValue(registryAccess: RegistryAccess) = ObjectValue(this).addStandardFunctions().addFunctions(itemStackFunctions.flatMap { it(this, registryAccess).entries.map { it.key to it.value } }.toMap())
|
||||
fun Holder<Biome>.asBiomeMoLangValue() = asMoLangValue(Registries.BIOME).addFunctions(biomeFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
fun Holder<Level>.asWorldMoLangValue() = asMoLangValue(Registries.DIMENSION).addFunctions(worldFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
fun Holder<Block>.asBlockMoLangValue() = asMoLangValue(Registries.BLOCK).addFunctions(blockFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
fun MinecraftServer.asMoLangValue() = ObjectValue(this).addStandardFunctions().addFunctions(serverFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
fun Holder<DimensionType>.asDimensionTypeMoLangValue() = asMoLangValue(Registries.DIMENSION_TYPE).addFunctions(dimensionTypeFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
fun Player.asMoLangValue(): ObjectValue<Player> {
|
||||
if (this is ServerPlayer && uuid in Cobblemon.serverPlayerStructs) {
|
||||
val existing = Cobblemon.serverPlayerStructs[uuid]!!
|
||||
if (existing.obj == this) {
|
||||
return existing
|
||||
} else {
|
||||
Cobblemon.serverPlayerStructs.remove(uuid)
|
||||
}
|
||||
}
|
||||
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.effectiveName().string }
|
||||
)
|
||||
|
||||
value.addFunctions(entityFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
value.addFunctions(livingEntityFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
value.addFunctions(playerFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
|
||||
if (this is ServerPlayer) {
|
||||
Cobblemon.serverPlayerStructs[uuid] = value
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
@@ -1150,6 +1278,7 @@ object MoLangFunctions {
|
||||
stringify = { it.name.string }
|
||||
)
|
||||
value.addFunctions(entityFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
value.addFunctions(livingEntityFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
value.addFunctions(npcFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
return value
|
||||
}
|
||||
@@ -1160,6 +1289,7 @@ object MoLangFunctions {
|
||||
stringify = { it.pokemon.uuid.toString() }
|
||||
)
|
||||
value.addFunctions(entityFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
value.addFunctions(livingEntityFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
value.addFunctions(pokemonFunctions.flatMap { it(this.pokemon).entries.map { it.key to it.value } }.toMap()) // Convenience
|
||||
value.addFunctions(pokemonEntityFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
return value
|
||||
@@ -1195,12 +1325,18 @@ object MoLangFunctions {
|
||||
/**
|
||||
* Different functions exist depending on
|
||||
*/
|
||||
fun LivingEntity.asMostSpecificMoLangValue(): ObjectValue<out LivingEntity> {
|
||||
fun Entity.asMostSpecificMoLangValue(): ObjectValue<out Entity> {
|
||||
return when (this) {
|
||||
is Player -> asMoLangValue()
|
||||
is PokemonEntity -> struct
|
||||
is NPCEntity -> struct
|
||||
else -> ObjectValue(this).also { it.addStandardFunctions().addFunctions(entityFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap()) }
|
||||
else -> ObjectValue(this).also {
|
||||
it.addStandardFunctions()
|
||||
it.addFunctions(entityFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
if (this is LivingEntity) {
|
||||
it.addFunctions(livingEntityFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1225,7 +1361,7 @@ object MoLangFunctions {
|
||||
return this
|
||||
}
|
||||
|
||||
fun QueryStruct.addEntityFunctions(entity: LivingEntity): QueryStruct {
|
||||
fun QueryStruct.addEntityFunctions(entity: Entity): QueryStruct {
|
||||
val addedFunctions = entityFunctions
|
||||
.flatMap { it.invoke(entity).entries }
|
||||
.associate { it.key to it.value }
|
||||
@@ -1233,6 +1369,14 @@ object MoLangFunctions {
|
||||
return this
|
||||
}
|
||||
|
||||
fun QueryStruct.addLivingEntityFunctions(entity: LivingEntity): QueryStruct {
|
||||
val addedFunctions = livingEntityFunctions
|
||||
.flatMap { it.invoke(entity).entries }
|
||||
.associate { it.key to it.value }
|
||||
functions.putAll(addedFunctions)
|
||||
return this
|
||||
}
|
||||
|
||||
fun QueryStruct.addPokedexFunctions(pokedexManager: AbstractPokedexManager): QueryStruct {
|
||||
val addedFunctions = pokedexFunctions
|
||||
.flatMap { it.invoke(pokedexManager).entries }
|
||||
@@ -1342,4 +1486,4 @@ object MoLangFunctions {
|
||||
}
|
||||
}
|
||||
|
||||
fun Either<ResourceLocation, ExpressionLike>.runScript(runtime: MoLangRuntime) = map({ CobblemonScripts.run(it, runtime) }, { it.resolve(runtime) })
|
||||
fun Either<ResourceLocation, ExpressionLike>.runScript(runtime: MoLangRuntime) = map({ CobblemonScripts.run(it, runtime) }, { it.resolve(runtime) })
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.moves
|
||||
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.net.IntSize
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.readSizedInt
|
||||
@@ -24,7 +23,7 @@ import net.minecraft.nbt.ListTag
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
class BenchedMoves : Iterable<BenchedMove> {
|
||||
val observable = SimpleObservable<BenchedMoves>()
|
||||
var changeFunction: ((BenchedMoves) -> Unit) = {}
|
||||
private var emit = true
|
||||
private val benchedMoves = mutableListOf<BenchedMove>()
|
||||
|
||||
@@ -50,7 +49,7 @@ class BenchedMoves : Iterable<BenchedMove> {
|
||||
|
||||
fun update() {
|
||||
if (emit) {
|
||||
observable.emit(this)
|
||||
changeFunction(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.api.moves
|
||||
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.net.IntSize
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.readSizedInt
|
||||
@@ -24,7 +23,7 @@ import com.mojang.serialization.Codec
|
||||
import kotlin.math.min
|
||||
|
||||
class MoveSet : Iterable<Move> {
|
||||
val observable = SimpleObservable<MoveSet>()
|
||||
var changeFunction: (MoveSet) -> Unit = {}
|
||||
private var emit = true
|
||||
|
||||
private val moves = arrayOfNulls<Move>(MOVE_COUNT)
|
||||
@@ -153,7 +152,7 @@ class MoveSet : Iterable<Move> {
|
||||
|
||||
fun update() {
|
||||
if (emit) {
|
||||
observable.emit(this)
|
||||
changeFunction(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -166,7 +166,7 @@ open class MoveTemplate(
|
||||
// TODO: Handle Liquid Voice: need to know what moves have the sound flag
|
||||
// TODO: Handle weatherball, naturalgift, judgement, technoblast, terrainpulse, and terrablast
|
||||
if (this.elementalType == ElementalTypes.NORMAL) {
|
||||
if( this.damageCategory != DamageCategories.STATUS) {
|
||||
if (this.damageCategory != DamageCategories.STATUS) {
|
||||
return when (pokemon.ability.name) {
|
||||
"pixilate" -> ElementalTypes.FAIRY
|
||||
"aerilate" -> ElementalTypes.FLYING
|
||||
|
||||
@@ -19,4 +19,4 @@ interface Decodable {
|
||||
*/
|
||||
fun decode(buffer: RegistryFriendlyByteBuf)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,4 +25,4 @@ interface Encodable {
|
||||
*/
|
||||
fun encode(buffer: RegistryFriendlyByteBuf)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* 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.net.serializers
|
||||
|
||||
object RidingStateSerializer {
|
||||
|
||||
|
||||
|
||||
}
|
||||
+35
-18
@@ -7,27 +7,31 @@
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.npc.partyproviders
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
import com.cobblemon.mod.common.api.npc.NPCPartyProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.storage.party.NPCPartyStore
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.util.asArrayValue
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.resolveFloat
|
||||
import com.cobblemon.mod.common.util.resolveInt
|
||||
import com.cobblemon.mod.common.util.weightedSelection
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonPrimitive
|
||||
import kotlin.random.Random
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import kotlin.collections.List
|
||||
import kotlin.collections.any
|
||||
import kotlin.collections.filter
|
||||
import kotlin.collections.first
|
||||
import kotlin.collections.mutableListOf
|
||||
import kotlin.collections.mutableMapOf
|
||||
import kotlin.collections.randomOrNull
|
||||
import kotlin.collections.set
|
||||
import kotlin.collections.toMutableList
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
* A provider of a party for battling the NPC. It generates a party using a more complex process
|
||||
@@ -60,9 +64,9 @@ class PoolPartyProvider : NPCPartyProvider {
|
||||
json as JsonObject
|
||||
this.minPokemon = json.getAsJsonPrimitive("minPokemon").asString?.asExpression() ?: "1".asExpression()
|
||||
this.maxPokemon = json.getAsJsonPrimitive("maxPokemon").asString?.asExpression() ?: "6".asExpression()
|
||||
this.pool = json.getAsJsonArray("pool").map {
|
||||
this.pool = json.getAsJsonArray("pool").mapNotNull {
|
||||
if (it is JsonPrimitive) {
|
||||
return@map DynamicPokemon(
|
||||
return@mapNotNull DynamicPokemon(
|
||||
PokemonProperties.parse(it.asString),
|
||||
"0".asExpression(),
|
||||
null,
|
||||
@@ -73,17 +77,26 @@ class PoolPartyProvider : NPCPartyProvider {
|
||||
}
|
||||
|
||||
it as JsonObject
|
||||
DynamicPokemon(
|
||||
PokemonProperties.parse(it.get("pokemon").asString),
|
||||
it.get("levelVariation")?.asString?.asExpression() ?: "0".asExpression(),
|
||||
it.get("level")?.asString?.asExpression(),
|
||||
it.get("npcLevels")?.asString?.split("-")?.let { it[0].toInt()..it[1].toInt() } ?: 1..100,
|
||||
it.get("selectableTimes")?.asString?.asExpression() ?: "1".asExpression(),
|
||||
it.get("weight")?.asString?.asExpression() ?: "1".asExpression()
|
||||
)
|
||||
val properties = PokemonProperties.parse(it.get("pokemon").asString)
|
||||
// Checks for errors on Pokémon name, if not a valid species ignores it to avoid random Pokémon in the Pool.
|
||||
if (properties.species == null) {
|
||||
LOGGER.warn("Error making NPC pokemon pool. ${it.get("pokemon").asString.substringBefore(" ")} is not a valid species, ignoring")
|
||||
null
|
||||
}
|
||||
else {
|
||||
DynamicPokemon(
|
||||
properties,
|
||||
it.get("levelVariation")?.asString?.asExpression() ?: "0".asExpression(),
|
||||
it.get("level")?.asString?.asExpression(),
|
||||
it.get("npcLevels")?.asString?.split("-")?.let { it[0].toInt()..it[1].toInt() } ?: 1..100,
|
||||
it.get("selectableTimes")?.asString?.asExpression() ?: "1".asExpression(),
|
||||
it.get("weight")?.asString?.asExpression() ?: "1".asExpression()
|
||||
)
|
||||
}
|
||||
}.toMutableList()
|
||||
isStatic = json.get("isStatic").asBoolean
|
||||
json.get("useFixedRandom")?.asBoolean?.let { useFixedRandom = it }
|
||||
|
||||
}
|
||||
|
||||
class DynamicPokemon(
|
||||
@@ -137,6 +150,10 @@ class PoolPartyProvider : NPCPartyProvider {
|
||||
val instance = selected.pokemon.copy().also { it.level = it.level ?: dictatedLevel ?: randomLevel }.create()
|
||||
party.add(instance)
|
||||
}
|
||||
if (party.none()) {
|
||||
LOGGER.error("${npc} has no Pokemon on Party")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun provide(npc: NPCEntity, level: Int, players: List<ServerPlayer>): NPCPartyStore {
|
||||
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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.orientation
|
||||
|
||||
import com.cobblemon.mod.common.util.math.geometry.toDegrees
|
||||
import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import org.joml.Matrix3f
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
import kotlin.math.asin
|
||||
|
||||
open class OrientationController(val entity: LivingEntity) {
|
||||
|
||||
companion object {
|
||||
val FORWARDS: Vector3f = Vector3f(0.0f, 0.0f, -1.0f)
|
||||
val UP: Vector3f = Vector3f(0.0f, 1.0f, 0.0f)
|
||||
val LEFT: Vector3f = Vector3f(-1.0f, 0.0f, 0.0f)
|
||||
}
|
||||
|
||||
open var active: Boolean = false
|
||||
|
||||
var orientation: Matrix3f? = null
|
||||
private set
|
||||
|
||||
private var renderOrientationO: Matrix3f? = null
|
||||
private var renderOrientation: Matrix3f? = null
|
||||
|
||||
/** Adding this simply because it irritates me seeing `getActive` when most of this is Java. */
|
||||
fun isActive() = active
|
||||
|
||||
fun updateOrientation(fn: (Matrix3f) -> Matrix3f?) {
|
||||
if (!active) return
|
||||
if (orientation == null) {
|
||||
orientation = Matrix3f()
|
||||
rotate(entity.yRot - 180, entity.xRot, 0f)
|
||||
}
|
||||
orientation = fn(orientation!!)
|
||||
entity.yRot = yaw
|
||||
entity.xRot = pitch
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
orientation = null
|
||||
renderOrientationO = null
|
||||
renderOrientation = null
|
||||
active = false
|
||||
}
|
||||
|
||||
fun rotate(yaw: Float, pitch: Float, roll: Float) {
|
||||
rotateYaw(yaw)
|
||||
rotatePitch(pitch)
|
||||
rotateRoll(roll)
|
||||
}
|
||||
|
||||
fun getRenderOrientation(delta: Float): Quaternionf {
|
||||
val old = renderOrientationO ?: renderOrientation ?: orientation
|
||||
val new = renderOrientation ?: old
|
||||
val oldQuat = Quaternionf().setFromUnnormalized(old)
|
||||
val newQuat = Quaternionf().setFromUnnormalized(new)
|
||||
oldQuat.slerp(newQuat, delta)
|
||||
return oldQuat
|
||||
}
|
||||
|
||||
fun tick() {
|
||||
if (!active || orientation == null) return
|
||||
renderOrientationO = renderOrientation
|
||||
val current = orientation
|
||||
val renderMatrix = this.renderOrientation ?: current
|
||||
val renderQuat = Quaternionf().setFromUnnormalized(renderMatrix)
|
||||
val targetQuat = Quaternionf().setFromUnnormalized(current)
|
||||
val dampingFactor = 0.66f // We can change this factor for faster transitions
|
||||
renderQuat.slerp(targetQuat, dampingFactor)
|
||||
|
||||
val newRenderOrientation = Matrix3f()
|
||||
renderQuat.get(newRenderOrientation)
|
||||
renderOrientation = newRenderOrientation
|
||||
}
|
||||
|
||||
val forwardVector: Vector3f
|
||||
get() = orientation?.transform(FORWARDS, Vector3f()) ?: Vector3f(FORWARDS)
|
||||
|
||||
val leftVector: Vector3f
|
||||
get() = orientation?.transform(LEFT, Vector3f()) ?: Vector3f(LEFT)
|
||||
|
||||
val upVector : Vector3f
|
||||
get() = orientation?.transform(UP, Vector3f()) ?: Vector3f(UP)
|
||||
|
||||
fun rotateYaw(yaw: Float) = updateOrientation { it.rotateY(-yaw.toRadians()) }
|
||||
fun rotatePitch(pitch: Float) = updateOrientation { it.rotateX(-pitch.toRadians()) }
|
||||
fun rotateRoll(roll: Float) = updateOrientation { it.rotateZ(-roll.toRadians()) }
|
||||
|
||||
val yaw: Float
|
||||
get() = Mth.wrapDegrees(-FORWARDS.angleSigned(forwardVector, UP).toDegrees() + 180)
|
||||
|
||||
val pitch: Float
|
||||
get() = Mth.wrapDegrees(-asin(forwardVector.y).toDegrees())
|
||||
|
||||
val roll: Float
|
||||
get() = Mth.wrapDegrees(-upVector.angleSigned(UP, forwardVector).toDegrees())
|
||||
|
||||
fun applyGlobalYaw(deltaYawDegrees: Float) = updateOrientation { original ->
|
||||
val euler = Vector3f()
|
||||
original.getEulerAnglesXYZ(euler)
|
||||
val pitch0 = euler.x
|
||||
val roll0 = euler.z
|
||||
|
||||
val unroll = Matrix3f().rotateZ(-roll0)
|
||||
val unpitch = Matrix3f().rotateX(-pitch0)
|
||||
val flattened = Matrix3f(original).mul(unroll).mul(unpitch)
|
||||
|
||||
//Flip to reflect current acceptance of what direction is pos for yaw
|
||||
val globalYaw = Matrix3f().rotationY(-1.0f * deltaYawDegrees.toRadians())
|
||||
val rePitch = Matrix3f().rotateX(pitch0)
|
||||
val reRoll = Matrix3f().rotateZ(roll0)
|
||||
|
||||
return@updateOrientation Matrix3f(globalYaw).mul(flattened).mul(rePitch).mul(reRoll)
|
||||
}
|
||||
|
||||
}
|
||||
+62
-12
@@ -13,80 +13,130 @@ object CobblemonPermissions {
|
||||
private const val COMMAND_PREFIX = "command."
|
||||
private val permissions = arrayListOf<Permission>()
|
||||
|
||||
@JvmStatic
|
||||
val CHANGE_EYE_HEIGHT = this.create("${COMMAND_PREFIX}changeeyeheight", PermissionLevel.ALL_COMMANDS)
|
||||
@JvmStatic
|
||||
val CHANGE_SCALE_AND_SIZE = this.create("${COMMAND_PREFIX}changescaleandsize", PermissionLevel.ALL_COMMANDS)
|
||||
@JvmStatic
|
||||
val CHANGE_WALK_SPEED = this.create("${COMMAND_PREFIX}changewalkspeed", PermissionLevel.ALL_COMMANDS)
|
||||
val CHANGE_JOINT_SCALE = this.create("${COMMAND_PREFIX}changejointscale", PermissionLevel.ALL_COMMANDS)
|
||||
@JvmStatic
|
||||
val TRANSFORM_MODEL_PART = this.create("${COMMAND_PREFIX}transformmodelpart", PermissionLevel.ALL_COMMANDS)
|
||||
@JvmStatic
|
||||
val CHECKSPAWNS = this.create("${COMMAND_PREFIX}checkspawns", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
@JvmStatic
|
||||
val GET_NBT = this.create("${COMMAND_PREFIX}getnbt", PermissionLevel.ALL_COMMANDS)
|
||||
|
||||
private const val GIVE_POKEMON_BASE = "${COMMAND_PREFIX}givepokemon"
|
||||
@JvmStatic
|
||||
val GIVE_POKEMON_SELF = this.create("${GIVE_POKEMON_BASE}.self", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
@JvmStatic
|
||||
val GIVE_POKEMON_OTHER = this.create("${GIVE_POKEMON_BASE}.other", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
private const val HEAL_POKEMON_BASE = "${COMMAND_PREFIX}healpokemon"
|
||||
@JvmStatic
|
||||
val HEAL_POKEMON_SELF = this.create("$HEAL_POKEMON_BASE.self", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
@JvmStatic
|
||||
val HEAL_POKEMON_OTHER = this.create("$HEAL_POKEMON_BASE.other", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
|
||||
private const val LEVEL_UP_BASE = "${COMMAND_PREFIX}levelup"
|
||||
@JvmStatic
|
||||
val LEVEL_UP_SELF = this.create("$LEVEL_UP_BASE.self", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
@JvmStatic
|
||||
val LEVEL_UP_OTHER = this.create("$LEVEL_UP_BASE.other", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
@JvmStatic
|
||||
val OPEN_STARTER_SCREEN = this.create("${COMMAND_PREFIX}openstarterscreen", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
@JvmStatic
|
||||
val BEDROCK_PARTICLE = this.create("${COMMAND_PREFIX}bedrockparticle", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
@JvmStatic
|
||||
val OPEN_DIALOGUE = this.create("${COMMAND_PREFIX}opendialogue", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
@JvmStatic
|
||||
val UNLOCK_PC_BOX_WALLPAPER = this.create("${COMMAND_PREFIX}unlockpcboxwallpaper", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
private const val POKEMON_EDIT_BASE = "${COMMAND_PREFIX}pokemonedit"
|
||||
@JvmStatic
|
||||
val POKEMON_EDIT_SELF = this.create("$POKEMON_EDIT_BASE.self", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
@JvmStatic
|
||||
val POKEMON_EDIT_OTHER = this.create("$POKEMON_EDIT_BASE.other", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
@JvmStatic
|
||||
val SPAWN_ALL_POKEMON = this.create("${COMMAND_PREFIX}spawnallpokemon", PermissionLevel.ALL_COMMANDS)
|
||||
@JvmStatic
|
||||
val GIVE_ALL_POKEMON = this.create("${COMMAND_PREFIX}giveallpokemon", PermissionLevel.ALL_COMMANDS)
|
||||
|
||||
@JvmStatic
|
||||
val SPAWN_POKEMON = this.create("${COMMAND_PREFIX}spawnpokemon", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
@JvmStatic
|
||||
val SPAWN_NPC = this.create("${COMMAND_PREFIX}spawnnpc", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
|
||||
@JvmStatic
|
||||
val STOP_BATTLE = this.create("${COMMAND_PREFIX}stopbattle", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
|
||||
@JvmStatic
|
||||
val TAKE_POKEMON = this.create("${COMMAND_PREFIX}takepokemon", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
|
||||
@JvmStatic
|
||||
val TEACH = this.create("${COMMAND_PREFIX}teach.base", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
@JvmStatic
|
||||
val TEACH_BYPASS_LEARNSET = this.create("${COMMAND_PREFIX}teach.bypass", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
|
||||
@JvmStatic
|
||||
val FRIENDSHIP = this.create("${COMMAND_PREFIX}friendship", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
|
||||
@JvmStatic
|
||||
val HELD_ITEM = this.create("${COMMAND_PREFIX}helditem", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
val PC = this.create("${COMMAND_PREFIX}pc", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
@JvmStatic
|
||||
val CHANGE_MARK = this.create("${COMMAND_PREFIX}changemark", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
@JvmStatic
|
||||
val PC = this.create("${COMMAND_PREFIX}pc", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
@JvmStatic
|
||||
val POKEBOX = this.create("${COMMAND_PREFIX}pokebox", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
@JvmStatic
|
||||
val RENAMEBOX = this.create("${COMMAND_PREFIX}renamebox", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
@JvmStatic
|
||||
val CHANGE_WALLPAPER = this.create("${COMMAND_PREFIX}changewallpaper", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
@JvmStatic
|
||||
val TEST_STORE = this.create("${COMMAND_PREFIX}teststore", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
@JvmStatic
|
||||
val QUERY_LEARNSET = this.create("${COMMAND_PREFIX}querylearnset", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
@JvmStatic
|
||||
val TEST_PC_SLOT = this.create("${COMMAND_PREFIX}testpcslot", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
@JvmStatic
|
||||
val TEST_PARTY_SLOT = this.create("${COMMAND_PREFIX}testpartyslot", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
@JvmStatic
|
||||
val CLEAR_PARTY = this.create("${COMMAND_PREFIX}clearparty", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
@JvmStatic
|
||||
val CLEAR_PC = this.create("${COMMAND_PREFIX}clearpc", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
@JvmStatic
|
||||
val POKEDEX = this.create("${COMMAND_PREFIX}pokedex", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
@JvmStatic
|
||||
val NPC_EDIT = this.create("${COMMAND_PREFIX}npcedit", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
@JvmStatic
|
||||
val NPC_DELETE = this.create("${COMMAND_PREFIX}npcdelete", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
@JvmStatic
|
||||
val FREEZE_POKEMON = this.create("${COMMAND_PREFIX}freezepokemon", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
@JvmStatic
|
||||
val APPLY_PLAYER_TEXTURE = this.create("${COMMAND_PREFIX}applyplayertexture", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
@JvmStatic
|
||||
val ABANDON_MULTITEAM = this.create("${COMMAND_PREFIX}abandonmultiteam", PermissionLevel.NONE)
|
||||
|
||||
@JvmStatic
|
||||
val RUN_MOLANG_SCRIPT = this.create("${COMMAND_PREFIX}runmolangscript", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
@JvmStatic
|
||||
val COBBLEMON_CONFIG_RELOAD = this.create("${COMMAND_PREFIX}cobblemonconfig.reload", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
@JvmStatic
|
||||
val CALCULATE_SEAT_POSITIONS = this.create("${COMMAND_PREFIX}calculateseatpositions", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
@JvmStatic
|
||||
val CHANGE_BOX_COUNT = this.create("${COMMAND_PREFIX}boxcount", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
@JvmStatic
|
||||
val SPECTATE_BATTLE = this.create("${COMMAND_PREFIX}spectatebattle", PermissionLevel.ALL_COMMANDS)
|
||||
|
||||
fun all(): Iterable<Permission> = this.permissions
|
||||
|
||||
|
||||
@@ -722,8 +722,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().loadFromJSON(it) } as? IVs
|
||||
evs = json.getAsJsonObject(DataKeys.POKEMON_EVS)?.let { EVs().loadFromJSON(it) } as? EVs
|
||||
type = json.get(DataKeys.ELEMENTAL_TYPE)?.asString
|
||||
teraType = json.get(DataKeys.POKEMON_TERA_TYPE)?.asString
|
||||
dmaxLevel = json.get(DataKeys.POKEMON_DMAX_LEVEL)?.asInt
|
||||
|
||||
@@ -11,9 +11,11 @@ package com.cobblemon.mod.common.api.pokemon
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
|
||||
enum class PokemonSortMode(val comparator: Comparator<Pokemon?>, val reverseComparator: Comparator<Pokemon?>) {
|
||||
NAME( { it?.getDisplayName()?.string } ),
|
||||
LEVEL( { it?.level } ),
|
||||
TYPE( { it?.primaryType?.name } );
|
||||
NAME({ it?.getDisplayName()?.string }),
|
||||
LEVEL({ it?.level }),
|
||||
TYPE({ it?.primaryType?.name }),
|
||||
POKEDEX_NUMBER({ it?.species?.nationalPokedexNumber }),
|
||||
GENDER({ it?.gender });
|
||||
|
||||
constructor(mapper: (Pokemon?) -> Comparable<*>?) : this(compareBy({ it == null }, mapper),
|
||||
compareBy<Pokemon?> { it == null }.thenComparing(compareBy(mapper).reversed()))
|
||||
|
||||
@@ -33,7 +33,7 @@ import com.cobblemon.mod.common.api.pokemon.moves.Learnset
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stat
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.riding.controller.RideController
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStatDefinition
|
||||
import com.cobblemon.mod.common.api.spawning.TimeRange
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
@@ -48,8 +48,9 @@ import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonRequirementA
|
||||
import com.cobblemon.mod.common.pokemon.evolution.adapters.LegacyItemConditionWrapperAdapter
|
||||
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
|
||||
import com.cobblemon.mod.common.util.adapters.*
|
||||
import com.cobblemon.mod.common.util.adapters.RideControllerAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.RidingBehaviourSettingsAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.ifClient
|
||||
import com.google.common.collect.HashBasedTable
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
@@ -106,7 +107,7 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
.registerTypeAdapter(EggGroup::class.java, EggGroupAdapter)
|
||||
.registerTypeAdapter(MobEffect::class.java, RegistryElementAdapter<MobEffect>(BuiltInRegistries::MOB_EFFECT))
|
||||
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter)
|
||||
.registerTypeAdapter(RideController::class.java, RideControllerAdapter)
|
||||
.registerTypeAdapter(RidingBehaviourSettings::class.java, RidingBehaviourSettingsAdapter)
|
||||
.registerTypeAdapter(RidingStatDefinition::class.java, RidingStatDefinitionAdapter)
|
||||
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
|
||||
.disableHtmlEscaping()
|
||||
@@ -128,6 +129,11 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
init {
|
||||
SpeciesAdditions.observable.subscribe {
|
||||
this.species.forEach(Species::initialize)
|
||||
this.species.forEach {
|
||||
if (it.implemented) {
|
||||
this.implemented.add(it)
|
||||
}
|
||||
}
|
||||
this.species.forEach(Species::resolveEvolutionMoves)
|
||||
Cobblemon.showdownThread.queue {
|
||||
it.registerSpecies()
|
||||
@@ -206,8 +212,10 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
this.speciesByDex.remove(old.resourceIdentifier.namespace, old.nationalPokedexNumber)
|
||||
}
|
||||
this.speciesByDex.put(species.resourceIdentifier.namespace, species.nationalPokedexNumber, species)
|
||||
if (species.implemented) {
|
||||
this.implemented.add(species)
|
||||
ifClient {
|
||||
if (species.implemented) {
|
||||
this.implemented.add(species)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -283,4 +291,4 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
return "${species.resourceIdentifier.namespace}:${species.name}"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +176,7 @@ interface Evolution : EvolutionLike {
|
||||
pokemonEntity.after(11.2F) {
|
||||
evolutionMethod(pokemon)
|
||||
}
|
||||
pokemonEntity.after( seconds = 12F ) {
|
||||
pokemonEntity.after(seconds = 12F) {
|
||||
cryAnimation(pokemonEntity)
|
||||
pokemonEntity.entityData.set(PokemonEntity.EVOLUTION_STARTED, false)
|
||||
pokemon.getOwnerPlayer()?.sendSystemMessage(lang("ui.evolve.into", preEvoName, pokemon.species.translatedName))
|
||||
|
||||
+2
-2
@@ -68,8 +68,8 @@ object SpeciesFeatures : JsonDataRegistry<SpeciesFeatureProvider<*>> {
|
||||
data.forEach(this::registerFromAssets)
|
||||
}
|
||||
|
||||
fun getCodeFeature(name: String) = resourceFeatures[name]
|
||||
fun getResourceFeature(name: String) = codeFeatures[name]
|
||||
fun getCodeFeature(name: String) = codeFeatures[name]
|
||||
fun getResourceFeature(name: String) = resourceFeatures[name]
|
||||
fun getFeature(name: String) = getCodeFeature(name) ?: getResourceFeature(name)
|
||||
fun loadOnClient(entries: Collection<Map.Entry<String, SpeciesFeatureProvider<*>>>) {
|
||||
codeFeatures.putAll(entries.map { it.toPair() })
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.riding
|
||||
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
|
||||
import net.minecraft.world.entity.PlayerRideableJumping
|
||||
|
||||
/**
|
||||
@@ -20,6 +23,6 @@ interface Rideable : PlayerRideableJumping {
|
||||
/**
|
||||
* Denotes the manager responsible for handling any instance of riding taking place on an entity
|
||||
*/
|
||||
val riding: RidingManager
|
||||
val riding: RidingBehaviour<RidingBehaviourSettings, RidingBehaviourState>?
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.controller
|
||||
package com.cobblemon.mod.common.api.riding
|
||||
|
||||
import com.cobblemon.mod.common.util.readString
|
||||
import com.cobblemon.mod.common.util.writeString
|
||||
@@ -29,4 +29,4 @@ class RidingAnimation(
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,128 +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
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
import com.cobblemon.mod.common.api.riding.controller.RideController
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
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
|
||||
|
||||
data class RidingManager(val entity: PokemonEntity) {
|
||||
var lastSpeed = 0F
|
||||
var states: MutableMap<ResourceLocation, RidingState> = mutableMapOf()
|
||||
var deltaRotation = Vec2.ZERO
|
||||
|
||||
val runtime: MoLangRuntime by lazy {
|
||||
MoLangRuntime()
|
||||
.setup()
|
||||
.withQueryValue("entity", entity.struct)
|
||||
.also {
|
||||
it.environment.query.addFunction("passenger_count") { DoubleValue(entity.passengers.size.toDouble()) }
|
||||
}
|
||||
}
|
||||
|
||||
fun <T : RidingState> getState(id: ResourceLocation, constructor: (PokemonEntity) -> T): T {
|
||||
val storedState = states[id]
|
||||
if (storedState == null) {
|
||||
val newState = constructor(entity)
|
||||
states[id] = newState
|
||||
return newState
|
||||
}
|
||||
|
||||
return storedState as T
|
||||
}
|
||||
|
||||
fun getController(entity: PokemonEntity): RideController? {
|
||||
return entity.pokemon.riding.controller?.takeIf { it.condition.invoke(entity) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for handling riding conditions and transitions amongst controllers. This will tick
|
||||
* whenever the entity receives a tickControlled interaction.
|
||||
*/
|
||||
fun tick(entity: PokemonEntity, driver: Player, input: Vec3) {
|
||||
val controller = getController(entity) ?: return
|
||||
|
||||
val pose = controller.pose(entity)
|
||||
entity.entityData.set(PokemonEntity.POSE_TYPE, pose)
|
||||
|
||||
// driver.displayClientMessage(Component.literal("Speed: ").withStyle { it.withColor(ChatFormatting.GREEN) }.append(Component.literal("$lastSpeed b/t")), true)
|
||||
}
|
||||
|
||||
fun speed(entity: PokemonEntity, driver: Player): Float {
|
||||
val controller = getController(entity) ?: return 0.05F
|
||||
this.lastSpeed = controller.speed(entity, driver)
|
||||
return this.lastSpeed
|
||||
}
|
||||
|
||||
fun controlledRotation(entity: PokemonEntity, driver: Player): Vec2 {
|
||||
val controller = getController(entity) ?: return entity.rotationVector
|
||||
val previousRotation = entity.rotationVector
|
||||
val rotation = controller.rotation(entity, driver)
|
||||
this.deltaRotation = Vec2(rotation.x - previousRotation.x, rotation.y - previousRotation.y)
|
||||
return rotation
|
||||
}
|
||||
|
||||
fun clampPassengerRotation(entity: PokemonEntity, driver: LivingEntity) {
|
||||
val controller = getController(entity) ?: return
|
||||
return controller.clampPassengerRotation(entity, driver)
|
||||
}
|
||||
|
||||
fun updatePassengerRotation(entity: PokemonEntity, driver: LivingEntity) {
|
||||
val controller = getController(entity) ?: return
|
||||
return controller.updatePassengerRotation(entity, driver)
|
||||
}
|
||||
|
||||
fun velocity(entity: PokemonEntity, driver: Player, input: Vec3): Vec3 {
|
||||
val controller = getController(entity) ?: return Vec3.ZERO
|
||||
return controller.velocity(entity, driver, input)
|
||||
}
|
||||
|
||||
fun canJump(entity: PokemonEntity, driver: Player): Boolean {
|
||||
val controller = getController(entity) ?: return false
|
||||
return controller.canJump(entity, driver)
|
||||
}
|
||||
|
||||
fun jumpVelocity(entity: PokemonEntity, driver: Player, jumpStrength: Int): Vec3 {
|
||||
val controller = getController(entity) ?: return Vec3.ZERO
|
||||
return controller.jumpForce(entity, driver, jumpStrength)
|
||||
}
|
||||
|
||||
fun gravity(entity: PokemonEntity, regularGravity: Double): Double? {
|
||||
val controller = getController(entity) ?: return null
|
||||
return controller.gravity(entity, regularGravity)
|
||||
}
|
||||
|
||||
fun shouldRoll(entity: PokemonEntity): Boolean {
|
||||
val controller = getController(entity) ?: return false
|
||||
return controller.shouldRoll(entity)
|
||||
}
|
||||
|
||||
fun shouldRotatePlayerHead(entity: PokemonEntity): Boolean {
|
||||
val controller = getController(entity) ?: return false
|
||||
return controller.shouldRotatePlayerHead()
|
||||
}
|
||||
|
||||
fun shouldRotatePokemonHead(entity: PokemonEntity): Boolean {
|
||||
val controller = getController(entity) ?: return false
|
||||
return controller.shouldRotatePokemonHead()
|
||||
}
|
||||
|
||||
//FIXME: Make this not just be false for all controllers lol
|
||||
fun dismountOnShift(): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -9,45 +9,38 @@
|
||||
package com.cobblemon.mod.common.api.riding
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.api.riding.controller.RideController
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStatDefinition
|
||||
import com.cobblemon.mod.common.util.adapters.RideControllerAdapter
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.getString
|
||||
import com.cobblemon.mod.common.util.readIdentifier
|
||||
import com.cobblemon.mod.common.util.readString
|
||||
import com.cobblemon.mod.common.util.writeString
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.util.adapters.RidingBehaviourSettingsAdapter
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
class RidingProperties(
|
||||
val stats: Map<RidingStat, RidingStatDefinition> = mapOf(),
|
||||
val seats: List<Seat> = listOf(),
|
||||
val conditions: List<Expression> = listOf(),
|
||||
val controller: RideController? = null
|
||||
val behaviour: RidingBehaviourSettings? = null
|
||||
) {
|
||||
companion object {
|
||||
fun decode(buffer: RegistryFriendlyByteBuf): RidingProperties {
|
||||
val stats: Map<RidingStat, RidingStatDefinition> = buffer.readMap(
|
||||
{ buffer.readEnum<RidingStat>(RidingStat::class.java) },
|
||||
{ buffer.readEnum(RidingStat::class.java) },
|
||||
{ RidingStatDefinition.decode(buffer) }
|
||||
)
|
||||
val seats: List<Seat> = buffer.readList { _ -> Seat.decode(buffer) }
|
||||
val conditions = buffer.readList { buffer.readString().asExpression() }
|
||||
val controller = buffer.readNullable { _ ->
|
||||
val key = buffer.readIdentifier()
|
||||
val controller = RideControllerAdapter.types[key]?.getConstructor()?.newInstance() ?: error("Unknown controller key: $key")
|
||||
controller.decode(buffer)
|
||||
return@readNullable controller
|
||||
val behaviour = buffer.readNullable { _ ->
|
||||
val key = buffer.readResourceLocation()
|
||||
val settings = RidingBehaviourSettingsAdapter.types[key]?.getConstructor()?.newInstance() ?: error("Unknown controller key: $key")
|
||||
settings.decode(buffer)
|
||||
return@readNullable settings
|
||||
}
|
||||
|
||||
return RidingProperties(stats = stats, seats = seats, conditions = conditions, controller = controller)
|
||||
return RidingProperties(stats = stats, seats = seats, conditions = conditions, behaviour = behaviour)
|
||||
}
|
||||
}
|
||||
|
||||
val canRide: Boolean
|
||||
get() = seats.isNotEmpty() && controller != null
|
||||
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeMap(
|
||||
stats,
|
||||
@@ -56,7 +49,9 @@ class RidingProperties(
|
||||
)
|
||||
buffer.writeCollection(seats) { _, seat -> seat.encode(buffer) }
|
||||
buffer.writeCollection(conditions) { _, condition -> buffer.writeString(condition.getString()) }
|
||||
buffer.writeNullable(controller) { _, controller -> controller.encode(buffer) }
|
||||
buffer.writeNullable(behaviour) { _, behaviour ->
|
||||
behaviour.encode(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
fun calculate(stat: RidingStat, style: RidingStyle, boosts: Int): Float {
|
||||
|
||||
@@ -1,18 +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
|
||||
|
||||
/**
|
||||
* Honestly having a base interface is probably unnecessary I'm just considering whether there's going to be some
|
||||
* kind of shared state later.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since October 6th, 2024
|
||||
*/
|
||||
interface RidingState
|
||||
@@ -18,5 +18,6 @@ package com.cobblemon.mod.common.api.riding
|
||||
enum class RidingStyle {
|
||||
LAND,
|
||||
LIQUID,
|
||||
AIR
|
||||
}
|
||||
AIR,
|
||||
COMPOSITE
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
package com.cobblemon.mod.common.api.riding
|
||||
|
||||
import com.cobblemon.mod.common.api.net.Encodable
|
||||
import com.cobblemon.mod.common.api.riding.controller.RidingAnimation
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.util.readString
|
||||
import com.cobblemon.mod.common.util.writeString
|
||||
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
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
|
||||
|
||||
/**
|
||||
* Represents the behaviour of a Pokemon when being ridden.
|
||||
* This is intended to contain the logic for how a Pokemon should behave when being ridden.
|
||||
*
|
||||
* This class should remain stateless as it is intended to be shared between multiple instances of Pokemon.
|
||||
*
|
||||
* @author landonjw
|
||||
*/
|
||||
interface RidingBehaviour<Settings : RidingBehaviourSettings, State : RidingBehaviourState> {
|
||||
val key: ResourceLocation
|
||||
val style: RidingStyle
|
||||
|
||||
fun isActive(settings: Settings, state: State, vehicle: PokemonEntity): Boolean
|
||||
|
||||
fun tick(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player, input: Vec3) {}
|
||||
|
||||
fun pose(settings: Settings, state: State, vehicle: PokemonEntity): PoseType
|
||||
|
||||
fun speed(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player): Float
|
||||
|
||||
fun clampPassengerRotation(settings: Settings, state: State, entity: PokemonEntity, driver: LivingEntity) {}
|
||||
|
||||
fun updatePassengerRotation(settings: Settings, state: State, entity: PokemonEntity, driver: LivingEntity) {}
|
||||
|
||||
fun rotation(settings: Settings, state: State, vehicle: PokemonEntity, driver: LivingEntity): Vec2
|
||||
|
||||
fun velocity(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player, input: Vec3): Vec3
|
||||
|
||||
fun angRollVel(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player, deltaTime: Double): Vec3
|
||||
|
||||
fun rotationOnMouseXY(
|
||||
settings: Settings,
|
||||
state: State,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
mouseX: Double,
|
||||
mouseYSmoother: SmoothDouble,
|
||||
mouseXSmoother: SmoothDouble,
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3
|
||||
|
||||
fun canJump(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player): Boolean
|
||||
|
||||
fun setRideBar(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player): Float
|
||||
|
||||
fun jumpForce(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player, jumpStrength: Int): Vec3
|
||||
|
||||
fun gravity(settings: Settings, state: State, vehicle: PokemonEntity, regularGravity: Double): Double
|
||||
|
||||
fun rideFovMultiplier(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player): Float
|
||||
|
||||
fun useAngVelSmoothing(settings: Settings, state: State, vehicle: PokemonEntity): Boolean
|
||||
|
||||
fun useRidingAltPose(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player): Boolean
|
||||
|
||||
fun inertia(settings: Settings, state: State, vehicle: PokemonEntity): Double
|
||||
|
||||
fun shouldRoll(settings: Settings, state: State, vehicle: PokemonEntity): Boolean
|
||||
|
||||
fun turnOffOnGround(settings: Settings, state: State, vehicle: PokemonEntity): Boolean
|
||||
|
||||
fun dismountOnShift(settings: Settings, state: State, vehicle: PokemonEntity): Boolean
|
||||
|
||||
fun shouldRotatePokemonHead(settings: Settings, state: State, vehicle: PokemonEntity): Boolean
|
||||
|
||||
fun shouldRotatePlayerHead(settings: Settings, state: State, vehicle: PokemonEntity): Boolean
|
||||
|
||||
fun createDefaultState(settings: Settings): State
|
||||
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour
|
||||
|
||||
import com.cobblemon.mod.common.api.net.Decodable
|
||||
import com.cobblemon.mod.common.api.net.Encodable
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
/**
|
||||
* Represents static settings of a riding behaviour.
|
||||
* Values in this class are intended to be constant and not change during the riding process.
|
||||
* Typically this will be initialized for each pokemon form during deserialization
|
||||
* to determine how they should ride.
|
||||
*
|
||||
* @author landonjw
|
||||
*/
|
||||
interface RidingBehaviourSettings: Encodable, Decodable {
|
||||
val key: ResourceLocation
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import com.cobblemon.mod.common.util.ifClient
|
||||
import com.cobblemon.mod.common.util.ifServer
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
/**
|
||||
* Represents the state of a Pokemon when being ridden.
|
||||
* This is intended to contain mutable state that is passed between the client and server
|
||||
* or temporary state that is used during the riding process.
|
||||
*
|
||||
* This will be destroyed when the a user dismounts the Pokemon.
|
||||
*
|
||||
* @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 fun reset() {
|
||||
rideVelocity.set(Vec3.ZERO, true)
|
||||
stamina.set(1.0F, true)
|
||||
}
|
||||
|
||||
open fun copy(): RidingBehaviourState {
|
||||
val copy = RidingBehaviourState()
|
||||
copy.rideVelocity.set(rideVelocity.get(), true)
|
||||
copy.stamina.set(stamina.get(), true)
|
||||
return copy
|
||||
}
|
||||
|
||||
open fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
if (previous.rideVelocity.get() != rideVelocity.get()) return true
|
||||
if (previous.stamina.get() != stamina.get()) return true
|
||||
return false
|
||||
}
|
||||
|
||||
open fun encode(buffer: FriendlyByteBuf) {
|
||||
buffer.writeVec3(rideVelocity.get())
|
||||
buffer.writeFloat(stamina.get())
|
||||
}
|
||||
|
||||
open fun decode(buffer: FriendlyByteBuf) {
|
||||
rideVelocity.set(buffer.readVec3(), true)
|
||||
stamina.set(buffer.readFloat(), true)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun <T> ridingState(value: T, side: Side) = SidedRidingState(value, side)
|
||||
|
||||
class SidedRidingState<T>(private var value: T, val side: Side) {
|
||||
fun get() = value
|
||||
|
||||
fun set(value: T, forced: Boolean = false) {
|
||||
if (forced) {
|
||||
this.value = value
|
||||
}
|
||||
else {
|
||||
when (side) {
|
||||
Side.BOTH -> { this.value = value }
|
||||
Side.CLIENT -> { ifClient { this.value = value } }
|
||||
Side.SERVER -> { ifServer { this.value = value } }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other is SidedRidingState<*>) {
|
||||
return value == other.value
|
||||
}
|
||||
return super.equals(other)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return value.hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
enum class Side {
|
||||
SERVER, CLIENT, BOTH
|
||||
}
|
||||
|
||||
enum class DriverSide {
|
||||
LOCAL, REMOTE, BOTH
|
||||
}
|
||||
+41
@@ -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.api.riding.behaviour
|
||||
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.*
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeBehaviour
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
object RidingBehaviours {
|
||||
val behaviours = mutableMapOf<ResourceLocation, RidingBehaviour<RidingBehaviourSettings, RidingBehaviourState>>()
|
||||
|
||||
init {
|
||||
register(BirdAirBehaviour.KEY, BirdAirBehaviour())
|
||||
register(DolphinBehaviour.KEY, DolphinBehaviour())
|
||||
register(GenericLandBehaviour.KEY, GenericLandBehaviour())
|
||||
register(GenericSwimBehaviour.KEY, GenericSwimBehaviour())
|
||||
register(GliderAirBehaviour.KEY, GliderAirBehaviour())
|
||||
register(HelicopterBehaviour.KEY, HelicopterBehaviour())
|
||||
register(JetAirBehaviour.KEY, JetAirBehaviour())
|
||||
register(SwimDashBehaviour.KEY, SwimDashBehaviour())
|
||||
register(VehicleLandBehaviour.KEY, VehicleLandBehaviour())
|
||||
register(HoverBehaviour.KEY, HoverBehaviour())
|
||||
register(CompositeBehaviour.KEY, CompositeBehaviour())
|
||||
}
|
||||
|
||||
fun register(key: ResourceLocation, behaviour: RidingBehaviour<out RidingBehaviourSettings, out RidingBehaviourState>) {
|
||||
if (behaviours.contains(key)) error("Behaviour already registered to key $key")
|
||||
behaviours[key] = RidingController(behaviour) as RidingBehaviour<RidingBehaviourSettings, RidingBehaviourState>
|
||||
}
|
||||
|
||||
fun get(key: ResourceLocation): RidingBehaviour<RidingBehaviourSettings, RidingBehaviourState> {
|
||||
if (!behaviours.contains(key)) error("Behaviour not registered to key $key")
|
||||
return behaviours[key]!!
|
||||
}
|
||||
}
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
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
|
||||
|
||||
/**
|
||||
* Small wrapper around a RidingBehaviour to provide sane defaults in the event that the behaviour is not active.
|
||||
*
|
||||
* @author landonjw
|
||||
*/
|
||||
class RidingController<Settings : RidingBehaviourSettings, State : RidingBehaviourState>(val behaviour: RidingBehaviour<Settings, State>) : RidingBehaviour<Settings, State> {
|
||||
|
||||
override val key = behaviour.key
|
||||
override val style = behaviour.style
|
||||
|
||||
override fun isActive(settings: Settings, state: State, vehicle: PokemonEntity) =
|
||||
behaviour.isActive(settings, state, vehicle)
|
||||
|
||||
override fun tick(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player, input: Vec3) =
|
||||
behaviour.tick(settings, state, vehicle, driver, input)
|
||||
|
||||
override fun pose(settings: Settings, state: State, vehicle: PokemonEntity) =
|
||||
behaviour.pose(settings, state, vehicle)
|
||||
|
||||
override fun speed(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player): Float {
|
||||
if (!isActive(settings, state, vehicle)) return 0.0F
|
||||
return behaviour.speed(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)
|
||||
}
|
||||
|
||||
override fun rotation(settings: Settings, state: State, vehicle: PokemonEntity, driver: LivingEntity): Vec2 {
|
||||
if (!isActive(settings, state, vehicle)) return driver.rotationVector
|
||||
return behaviour.rotation(settings, state, vehicle, driver)
|
||||
}
|
||||
|
||||
override fun velocity(
|
||||
settings: Settings,
|
||||
state: State,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
if (!isActive(settings, state, vehicle)) return Vec3.ZERO
|
||||
return behaviour.velocity(settings, state, vehicle, driver, input)
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: Settings,
|
||||
state: State,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (!isActive(settings, state, vehicle)) return Vec3.ZERO
|
||||
return behaviour.angRollVel(settings, state, vehicle, driver, deltaTime)
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: Settings,
|
||||
state: State,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
mouseX: Double,
|
||||
mouseYSmoother: SmoothDouble,
|
||||
mouseXSmoother: SmoothDouble,
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (!isActive(settings, state, vehicle)) return Vec3.ZERO
|
||||
return behaviour.rotationOnMouseXY(
|
||||
settings,
|
||||
state,
|
||||
vehicle,
|
||||
driver,
|
||||
mouseY,
|
||||
mouseX,
|
||||
mouseYSmoother,
|
||||
mouseXSmoother,
|
||||
sensitivity,
|
||||
deltaTime
|
||||
)
|
||||
}
|
||||
|
||||
override fun canJump(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player): Boolean {
|
||||
if (!isActive(settings, state, vehicle)) return false
|
||||
return behaviour.canJump(settings, state, vehicle, driver)
|
||||
}
|
||||
|
||||
override fun shouldRotatePlayerHead(settings: Settings, state: State, vehicle: PokemonEntity): Boolean {
|
||||
if (!isActive(settings, state, vehicle)) return false
|
||||
return behaviour.shouldRotatePlayerHead(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun shouldRotatePokemonHead(settings: Settings, state: State, vehicle: PokemonEntity): Boolean {
|
||||
if (!isActive(settings, state, vehicle)) return false
|
||||
return behaviour.shouldRotatePokemonHead(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun dismountOnShift(settings: Settings, state: State, vehicle: PokemonEntity): Boolean {
|
||||
if (!isActive(settings, state, vehicle)) return false
|
||||
return behaviour.dismountOnShift(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun turnOffOnGround(settings: Settings, state: State, vehicle: PokemonEntity): Boolean {
|
||||
if (!isActive(settings, state, vehicle)) return false
|
||||
return behaviour.turnOffOnGround(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun shouldRoll(settings: Settings, state: State, vehicle: PokemonEntity): Boolean {
|
||||
if (!isActive(settings, state, vehicle)) return false
|
||||
return behaviour.shouldRoll(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun inertia(settings: Settings, state: State, vehicle: PokemonEntity): Double {
|
||||
if (!isActive(settings, state, vehicle)) return 0.5
|
||||
return behaviour.inertia(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun useRidingAltPose(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player): Boolean {
|
||||
if (!isActive(settings, state, vehicle)) return false
|
||||
return behaviour.useRidingAltPose(settings, state, vehicle, driver)
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(settings: Settings, state: State, vehicle: PokemonEntity): Boolean {
|
||||
if (!isActive(settings, state, vehicle)) return true
|
||||
return behaviour.useAngVelSmoothing(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player): Float {
|
||||
if (!isActive(settings, state, vehicle)) return 1.0F
|
||||
return behaviour.rideFovMultiplier(settings, state, vehicle, driver)
|
||||
}
|
||||
|
||||
override fun gravity(settings: Settings, state: State, vehicle: PokemonEntity, regularGravity: Double): Double {
|
||||
if (!isActive(settings, state, vehicle)) return regularGravity
|
||||
return behaviour.gravity(settings, state, vehicle, regularGravity)
|
||||
}
|
||||
|
||||
override fun jumpForce(
|
||||
settings: Settings,
|
||||
state: State,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
jumpStrength: Int
|
||||
): Vec3 {
|
||||
if (!isActive(settings, state, vehicle)) return Vec3.ZERO
|
||||
return behaviour.jumpForce(settings, state, vehicle, driver, jumpStrength)
|
||||
}
|
||||
|
||||
override fun setRideBar(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player): Float {
|
||||
if (!isActive(settings, state, vehicle)) return 0.0F
|
||||
return behaviour.setRideBar(settings, state, vehicle, driver)
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: Settings): State {
|
||||
return behaviour.createDefaultState(settings)
|
||||
}
|
||||
|
||||
}
|
||||
+567
@@ -0,0 +1,567 @@
|
||||
/*
|
||||
* 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.Expression
|
||||
import com.bedrockk.molang.runtime.MoLangMath.lerp
|
||||
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.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import 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 org.joml.Matrix3f
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
import kotlin.math.*
|
||||
|
||||
class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("air/bird")
|
||||
}
|
||||
|
||||
override val key: ResourceLocation = KEY
|
||||
override val style = RidingStyle.AIR
|
||||
|
||||
val poseProvider = PoseProvider<BirdAirSettings, BirdAirState>(PoseType.HOVER)
|
||||
.with(PoseOption(PoseType.FLY) { _, state, _ -> state.rideVelocity.get().z > 0.2 })
|
||||
|
||||
override fun isActive(settings: BirdAirSettings, state: BirdAirState, vehicle: PokemonEntity): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun pose(settings: BirdAirSettings, state: BirdAirState, vehicle: PokemonEntity): PoseType {
|
||||
return poseProvider.select(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun speed(
|
||||
settings: BirdAirSettings,
|
||||
state: BirdAirState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
settings: BirdAirSettings,
|
||||
state: BirdAirState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Vec2 {
|
||||
return Vec2(driver.xRot * 0.5f, driver.yRot)
|
||||
}
|
||||
|
||||
override fun velocity(
|
||||
settings: BirdAirSettings,
|
||||
state: BirdAirState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
var yVel = 0.0
|
||||
var leftForce = 0.0
|
||||
var upForce = 0.0
|
||||
var forwardForce = 0.0
|
||||
|
||||
//Perform ride velocity update
|
||||
calculateRideSpaceVel(settings, state, vehicle, driver)
|
||||
|
||||
//Translate ride space velocity to world space velocity.
|
||||
val controller = (driver as? OrientationControllable)?.orientationController
|
||||
if (controller != null) {
|
||||
//Need to deadzone this when straight up or down
|
||||
|
||||
upForce += -1.0 * sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().z
|
||||
forwardForce += cos(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().z
|
||||
|
||||
upForce += cos(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().y
|
||||
forwardForce += sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().y
|
||||
}
|
||||
|
||||
|
||||
//Bring the ride out of the sky when stamina is depleted.
|
||||
if (state.stamina.get() <= 0.0) {
|
||||
upForce -= 0.3
|
||||
}
|
||||
|
||||
val altitudeLimit = vehicle.runtime.resolveDouble(settings.jumpExpr)
|
||||
|
||||
//Only limit altitude if altitude is not infinite
|
||||
if (!vehicle.runtime.resolveBoolean(settings.infiniteAltitude)) {
|
||||
//Provide a hard limit on altitude
|
||||
upForce = if (vehicle.y >= altitudeLimit && upForce > 0) 0.0 else upForce
|
||||
}
|
||||
|
||||
val velocity = Vec3(state.rideVelocity.get().x , upForce, forwardForce)
|
||||
return velocity
|
||||
}
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
//retrieve stats
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val glideTopSpeed = vehicle.runtime.resolveDouble(settings.glidespeedExpr)
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
|
||||
val staminaStat = vehicle.runtime.resolveDouble(settings.staminaExpr)
|
||||
|
||||
var glideSpeedChange = 0.0
|
||||
|
||||
val currSpeed = state.rideVelocity.get().length()
|
||||
|
||||
//Flag for determining if player is actively inputting
|
||||
var activeInput = false
|
||||
|
||||
var newVelocity = Vec3(state.rideVelocity.get().x, state.rideVelocity.get().y, state.rideVelocity.get().z)
|
||||
|
||||
//speed up and slow down based on input
|
||||
if (driver.zza != 0.0f && state.stamina.get() > 0.0) {
|
||||
//make sure it can't exceed top speed
|
||||
val forwardInput = when {
|
||||
driver.zza > 0 && newVelocity.z > topSpeed -> 0.0
|
||||
driver.zza < 0 && newVelocity.z < (-topSpeed / 3.0) -> 0.0
|
||||
else -> driver.zza.sign
|
||||
}
|
||||
|
||||
newVelocity = Vec3(
|
||||
newVelocity.x,
|
||||
newVelocity.y,
|
||||
(newVelocity.z + (accel * forwardInput.toDouble())))
|
||||
|
||||
activeInput = true
|
||||
}
|
||||
|
||||
val controller = (driver as? OrientationControllable)?.orientationController
|
||||
if (controller != null) {
|
||||
//Base glide speed change on current pitch of the ride.
|
||||
glideSpeedChange = sin(Math.toRadians(controller.pitch.toDouble()))
|
||||
glideSpeedChange = glideSpeedChange.pow(3) * 0.5
|
||||
|
||||
//TODO: Possibly create a deadzone around parallel where glide doesn't affect speed?
|
||||
if (glideSpeedChange <= 0.0) {
|
||||
//Ensures that a propelling force is still able to be applied when
|
||||
//climbing in height
|
||||
if (driver.zza <= 0) {
|
||||
//speed decrease should be 2x speed increase?
|
||||
//state.currSpeed = max(state.currSpeed + (0.0166) * glideSpeedChange, 0.0 )
|
||||
newVelocity = Vec3(
|
||||
newVelocity.x,
|
||||
newVelocity.y,
|
||||
lerp( newVelocity.z, 0.0,glideSpeedChange * -0.0166 * 2 )
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// only add to the speed if it hasn't exceeded the current
|
||||
//glide angles maximum amount of speed that it can give.
|
||||
//state.currSpeed = min(state.currSpeed + ((0.0166 * 2) * glideSpeedChange), maxGlideSpeed)
|
||||
newVelocity = Vec3(
|
||||
newVelocity.x,
|
||||
newVelocity.y,
|
||||
min(newVelocity.z + ((0.0166 * 2) * glideSpeedChange), glideTopSpeed)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
//Lateral movement based on driver input.
|
||||
// val latTopSpeed = topSpeed / 2.0
|
||||
// if (driver.xxa != 0.0f && state.stamina.get() > 0.0) {
|
||||
// newVelocity = Vec3(
|
||||
// (newVelocity.x + (accel * driver.xxa)).coerceIn(-latTopSpeed, latTopSpeed),
|
||||
// newVelocity.y,
|
||||
// newVelocity.z)
|
||||
// activeInput = true
|
||||
// }
|
||||
// else {
|
||||
// newVelocity = Vec3(
|
||||
// lerp(newVelocity.x, 0.0, latTopSpeed / 20.0),
|
||||
// newVelocity.y,
|
||||
// newVelocity.z)
|
||||
// }
|
||||
|
||||
//Vertical movement based on driver input.
|
||||
val vertTopSpeed = topSpeed / 2.0
|
||||
val vertInput = when {
|
||||
driver.jumping && state.rideVelocity.get().length() < 0.3 -> 1.0
|
||||
driver.isShiftKeyDown && state.rideVelocity.get().length() < 0.3 -> -1.0
|
||||
else -> 0.0
|
||||
}
|
||||
|
||||
if (vertInput != 0.0 && state.stamina.get() > 0.0) {
|
||||
newVelocity = Vec3(
|
||||
newVelocity.x,
|
||||
(newVelocity.y + (accel * vertInput)).coerceIn(-vertTopSpeed, vertTopSpeed),
|
||||
newVelocity.z)
|
||||
activeInput = true
|
||||
}
|
||||
else {
|
||||
newVelocity = Vec3(
|
||||
newVelocity.x,
|
||||
lerp(newVelocity.y, 0.0, vertTopSpeed / 20.0),
|
||||
newVelocity.z)
|
||||
}
|
||||
|
||||
//Check if the ride should be gliding
|
||||
if (driver.isLocalPlayer) {
|
||||
if (activeInput && state.stamina.get() > 0.0) {
|
||||
state.gliding.set(false)
|
||||
} else {
|
||||
state.gliding.set(true)
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Reintroduce stamina drain once stats start to be polished and tweaked
|
||||
// //Only perform stamina logic if the ride does not have infinite stamina
|
||||
// if (!vehicle.runtime.resolveBoolean(settings.infiniteStamina)) {
|
||||
// if (activeInput) {
|
||||
// state.stamina.set(state.stamina.get() - (0.05 / staminaStat).toFloat())
|
||||
// }
|
||||
//
|
||||
// //Lose a base amount of stamina just for being airborne
|
||||
// state.stamina.set(state.stamina.get() - (0.01 / staminaStat).toFloat())
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// state.stamina.set(1.0f)
|
||||
// }
|
||||
|
||||
//air resistance
|
||||
if( abs(newVelocity.z) > 0) {
|
||||
newVelocity = newVelocity.subtract(0.0, 0.0, min(0.0015 * newVelocity.z.sign, newVelocity.z))
|
||||
}
|
||||
state.rideVelocity.set(newVelocity)
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: BirdAirSettings,
|
||||
state: BirdAirState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
|
||||
//limit rolling based on handling and current speed.
|
||||
//modulated by speed so that when flapping idle in air you are ont wobbling around to look around
|
||||
//TODO: Tie handling into yaw rate
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
//This rotation limit will be used to know if the ride has exceeded it and its needs correcting.
|
||||
val rotMin = 15.0
|
||||
val rotLimit = max(handling * sqrt(normalizeVal(state.rideVelocity.get().length(), 0.0, topSpeed)), rotMin)
|
||||
|
||||
|
||||
val rotationChangeRate = 100.0
|
||||
|
||||
var yawForce = rotationChangeRate * sin(Math.toRadians(controller.roll.toDouble()))
|
||||
//for a bit of correction on the rolls limit it to a quarter the amount
|
||||
var pitchForce = -0.35 * rotationChangeRate * abs(sin(Math.toRadians(controller.roll.toDouble())))
|
||||
|
||||
//limit rotation modulation when pitched up heavily or pitched down heavily
|
||||
yawForce *= abs(cos(Math.toRadians(controller.pitch.toDouble())))
|
||||
pitchForce *= abs(cos(Math.toRadians(controller.pitch.toDouble()))) * 1.5
|
||||
|
||||
var yawDeltaDeg = deltaTime * rotationChangeRate * sin(Math.toRadians(controller.roll.toDouble())) //sin(Math.toRadians(controller.roll.toDouble())) * abs(cos(Math.toRadians(controller.pitch.toDouble())))
|
||||
val trueYawDelt = yawDeltaDeg * abs(cos(Math.toRadians(controller.pitch.toDouble())))
|
||||
//val pitchedYawDelt = yawDeltaDeg - trueYawDelt
|
||||
|
||||
controller.applyGlobalYaw(trueYawDelt.toFloat())
|
||||
|
||||
val correctionRate = 5.0
|
||||
//Calculate correcting roll force.
|
||||
if(abs(controller.roll) > rotLimit) {
|
||||
state.currRollCorrectionForce.set(
|
||||
state.currRollCorrectionForce.get() + ((abs(controller.roll) - rotLimit) / 180 - rotLimit) * controller.roll.sign * correctionRate * deltaTime * 0.01
|
||||
)
|
||||
} else {
|
||||
state.currRollCorrectionForce.set(
|
||||
//reduce rollCorrection gradually and make sure to
|
||||
//state.currRollCorrectionForce.get() - max(correctionRate * deltaTime * 0.04, abs(state.currRollCorrectionForce.get())) * state.currRollCorrectionForce.get().sign
|
||||
lerp(state.currRollCorrectionForce.get(), 0.0, deltaTime * 0.98)
|
||||
)
|
||||
}
|
||||
|
||||
//yaw, pitch, roll
|
||||
return Vec3(0.0, 0.0, state.currRollCorrectionForce.get())
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: BirdAirSettings,
|
||||
state: BirdAirState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
mouseX: Double,
|
||||
mouseYSmoother: SmoothDouble,
|
||||
mouseXSmoother: SmoothDouble,
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
|
||||
//Smooth out mouse input.
|
||||
val smoothingSpeed = 4.0
|
||||
val invertRoll = if (Cobblemon.config.invertRoll) -1 else 1
|
||||
val invertPitch = if (Cobblemon.config.invertPitch) -1 else 1
|
||||
val mouseXc = (mouseX).coerceIn(-60.0, 60.0)
|
||||
val mouseYc = (mouseY).coerceIn(-60.0, 60.0)
|
||||
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1 * invertRoll, deltaTime * smoothingSpeed);
|
||||
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1 * invertPitch, deltaTime * smoothingSpeed);
|
||||
|
||||
//limit rolling based on handling and current speed.
|
||||
//modulated by speed so that when flapping idle in air you are ont wobbling around to look around
|
||||
val rotMin = 15.0
|
||||
var rollForce = xInput
|
||||
val rotLimit = max(handling * sqrt(normalizeVal(state.rideVelocity.get().length(), 0.0, topSpeed)), rotMin)
|
||||
|
||||
//Limit roll by non linearly decreasing inputs towards
|
||||
// a rotation limit based on the current distance from
|
||||
// that rotation limit
|
||||
if (abs(controller.roll + rollForce) < rotLimit) {
|
||||
if (sign(rollForce) == sign(controller.roll).toDouble()) {
|
||||
//Grab how far the current roll is away from the roll limit
|
||||
val d = abs(abs(controller.roll) - rotLimit)
|
||||
rollForce *= (d.pow(2)) / (rotLimit.pow(2))
|
||||
}
|
||||
} else if (sign(rollForce) == sign(controller.roll).toDouble()) {
|
||||
rollForce = 0.0
|
||||
}
|
||||
|
||||
//Give the ability to yaw with x mouse input when at low speeds.
|
||||
val yawForce = xInput * ( 1.0 - sqrt(normalizeVal(state.rideVelocity.get().length(), 0.0, topSpeed)))
|
||||
|
||||
//Yaw locally a bit when up or down so that its more intuitive to make it out of a dive or a straight vertical
|
||||
//climb
|
||||
val yawForcePitched = xInput * sin(Math.toRadians(abs(controller.pitch.toDouble()))) * 0.25
|
||||
|
||||
|
||||
//Apply yaw globally as we don't want roll or pitch changes due to local yaw when looking up or down.
|
||||
controller.applyGlobalYaw(yawForce.toFloat())
|
||||
|
||||
//yaw, pitch, roll
|
||||
return Vec3(yawForcePitched, yInput, rollForce)
|
||||
}
|
||||
|
||||
/*
|
||||
* Normalizes the given value between a min and a max.
|
||||
* The result is clamped between 0.0 and 1.0, where 0.0 represents x is at or below min
|
||||
* and 1.0 represents x is at or above it.
|
||||
*/
|
||||
private fun normalizeVal(x: Double, min: Double, max: Double): Double {
|
||||
require(max > min) { "max must be greater than min" }
|
||||
return ((x - min) / (max - min)).coerceIn(0.0, 1.0)
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
settings: BirdAirSettings,
|
||||
state: BirdAirState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun setRideBar(
|
||||
settings: BirdAirSettings,
|
||||
state: BirdAirState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return (state.stamina.get() / 1.0f)
|
||||
}
|
||||
|
||||
override fun jumpForce(
|
||||
settings: BirdAirSettings,
|
||||
state: BirdAirState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
jumpStrength: Int
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun gravity(
|
||||
settings: BirdAirSettings,
|
||||
state: BirdAirState,
|
||||
vehicle: PokemonEntity,
|
||||
regularGravity: Double
|
||||
): Double {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(
|
||||
settings: BirdAirSettings,
|
||||
state: BirdAirState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val glideTopSpeed = vehicle.runtime.resolveDouble(settings.glidespeedExpr)
|
||||
|
||||
//Must I ensure that topspeed is greater than minimum?
|
||||
val normalizedGlideSpeed = normalizeVal(state.rideVelocity.get().length(), topSpeed, glideTopSpeed)
|
||||
|
||||
//Only ever want the fov change to be a max of 0.2 and for it to have non linear scaling.
|
||||
return 1.0f + normalizedGlideSpeed.pow(2).toFloat() * 0.2f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(
|
||||
settings: BirdAirSettings,
|
||||
state: BirdAirState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun useRidingAltPose(
|
||||
settings: BirdAirSettings,
|
||||
state: BirdAirState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return state.gliding.get()
|
||||
}
|
||||
|
||||
override fun inertia(settings: BirdAirSettings, state: BirdAirState, vehicle: PokemonEntity): Double {
|
||||
return 0.5
|
||||
}
|
||||
|
||||
override fun shouldRoll(settings: BirdAirSettings, state: BirdAirState, vehicle: PokemonEntity): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun turnOffOnGround(settings: BirdAirSettings, state: BirdAirState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun dismountOnShift(settings: BirdAirSettings, state: BirdAirState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePokemonHead(
|
||||
settings: BirdAirSettings,
|
||||
state: BirdAirState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePlayerHead(
|
||||
settings: BirdAirSettings,
|
||||
state: BirdAirState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: BirdAirSettings) = BirdAirState()
|
||||
|
||||
}
|
||||
|
||||
class BirdAirSettings : RidingBehaviourSettings {
|
||||
override val key = BirdAirBehaviour.KEY
|
||||
|
||||
var infiniteAltitude: Expression = "false".asExpression()
|
||||
private set
|
||||
|
||||
var infiniteStamina: Expression = "false".asExpression()
|
||||
private set
|
||||
|
||||
//max y level for the ride
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 200.0, 128.0)".asExpression()
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 135.0, 45.0)".asExpression()
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 1.0, 0.35)".asExpression()
|
||||
var accelerationExpr: Expression = "q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 3.0)), (1.0 / (20.0 * 8.0)))".asExpression()
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 120.0, 20.0)".asExpression()
|
||||
|
||||
var glidespeedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 2.0, 1.0)".asExpression()
|
||||
private set
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeExpression(infiniteAltitude)
|
||||
buffer.writeExpression(infiniteStamina)
|
||||
buffer.writeExpression(glidespeedExpr)
|
||||
buffer.writeExpression(jumpExpr)
|
||||
buffer.writeExpression(handlingExpr)
|
||||
buffer.writeExpression(speedExpr)
|
||||
buffer.writeExpression(accelerationExpr)
|
||||
buffer.writeExpression(staminaExpr)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
infiniteAltitude = buffer.readExpression()
|
||||
infiniteStamina = buffer.readExpression()
|
||||
glidespeedExpr = buffer.readExpression()
|
||||
jumpExpr = buffer.readExpression()
|
||||
handlingExpr = buffer.readExpression()
|
||||
speedExpr = buffer.readExpression()
|
||||
accelerationExpr = buffer.readExpression()
|
||||
staminaExpr = buffer.readExpression()
|
||||
}
|
||||
}
|
||||
|
||||
class BirdAirState : RidingBehaviourState() {
|
||||
var gliding = ridingState(false, Side.CLIENT)
|
||||
var lastGlide = ridingState(-100L, Side.CLIENT)
|
||||
var currRollCorrectionForce = ridingState(0.0, Side.CLIENT)
|
||||
|
||||
override fun encode(buffer: FriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
buffer.writeFloat(stamina.get())
|
||||
buffer.writeBoolean(gliding.get())
|
||||
}
|
||||
|
||||
override fun decode(buffer: FriendlyByteBuf) {
|
||||
super.decode(buffer)
|
||||
gliding.set(buffer.readBoolean(), forced = true)
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
super.reset()
|
||||
gliding.set(false, forced = true)
|
||||
lastGlide.set(-100L, forced = true)
|
||||
currRollCorrectionForce.set(0.0, forced = true)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "BirdAirState(rideVelocity=${rideVelocity.get()}, stamina=${stamina.get()}, gliding=${gliding.get()})"
|
||||
}
|
||||
|
||||
override fun copy() = BirdAirState().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)
|
||||
it.lastGlide.set(this.lastGlide.get(), forced = true)
|
||||
it.currRollCorrectionForce.set(this.currRollCorrectionForce.get(), forced = true)
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
if (previous !is BirdAirState) return false
|
||||
if (previous.gliding.get() != gliding.get()) return true
|
||||
return super.shouldSync(previous)
|
||||
}
|
||||
}
|
||||
+328
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
* 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.*
|
||||
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.util.SmoothDouble
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
|
||||
class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("swim/dolphin")
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
override val style = RidingStyle.LIQUID
|
||||
|
||||
val poseProvider = PoseProvider<DolphinSettings, DolphinState>(PoseType.FLOAT)
|
||||
.with(PoseOption(PoseType.SWIM) { _, _, entity -> entity.entityData.get(PokemonEntity.MOVING) })
|
||||
|
||||
override fun isActive(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity): Boolean {
|
||||
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
|
||||
if (it.y.toDouble() == (vehicle.position().y)) {
|
||||
val blockState = vehicle.level().getBlockState(it.below())
|
||||
return@any (blockState.isAir || !blockState.fluidState.isEmpty ) ||
|
||||
(vehicle.isInWater || vehicle.isUnderWater)
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun pose(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity): PoseType {
|
||||
return poseProvider.select(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun speed(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity, driver: Player): Float {
|
||||
return vehicle.runtime.resolveFloat(settings.speed)
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
settings: DolphinSettings,
|
||||
state: DolphinState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Vec2 {
|
||||
return Vec2(driver.xRot * 0.5f, driver.yRot)
|
||||
}
|
||||
|
||||
override fun velocity(
|
||||
settings: DolphinSettings,
|
||||
state: DolphinState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
val runtime = vehicle.runtime
|
||||
|
||||
if (!vehicle.isInWater && !vehicle.isUnderWater)
|
||||
{
|
||||
state.lastVelocity.set(Vec3(state.lastVelocity.get().x, state.lastVelocity.get().y - 0.035, state.lastVelocity.get().z))
|
||||
return state.lastVelocity.get()
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
var yComp = 0.0
|
||||
|
||||
//Get roll to add a left and right strafe during roll
|
||||
//Not sure if this is desired? Will need to mess around with this
|
||||
//a bit more
|
||||
var zComp = 0.0
|
||||
val controller = (driver as? OrientationControllable)?.orientationController
|
||||
if (controller != null)
|
||||
{
|
||||
//Can be used again if I better figure out how to deadzone it near the tops?
|
||||
zComp = -1.0 * g.toDouble() * sin(Math.toRadians(controller.roll.toDouble()))
|
||||
yComp = -1.0 * g.toDouble() * sin(Math.toRadians(controller.pitch.toDouble()))
|
||||
}
|
||||
|
||||
val currVelocity = Vec3(0.0, yComp , g.toDouble())
|
||||
state.lastVelocity.set(currVelocity)
|
||||
return currVelocity
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: DolphinSettings,
|
||||
state: DolphinState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (!vehicle.isInWater && !vehicle.isUnderWater)
|
||||
{
|
||||
return Vec3(0.0, 0.0, 0.0)
|
||||
}
|
||||
|
||||
val controller = (driver as? OrientationControllable)?.orientationController
|
||||
|
||||
//this should be changed to be speed maybe?
|
||||
val movingForce = driver.zza
|
||||
if (controller != null) {
|
||||
var yawAngVel = 3 * sin(Math.toRadians(controller.roll.toDouble())).toFloat()
|
||||
var pitchAngVel = -1 * Math.abs(sin(Math.toRadians(controller.roll.toDouble())).toFloat())
|
||||
|
||||
//limit rotation modulation when pitched up heavily or pitched down heavily
|
||||
yawAngVel *= (abs(cos(Math.toRadians(controller.pitch.toDouble())))).toFloat()
|
||||
pitchAngVel *= (abs(cos(Math.toRadians(controller.pitch.toDouble())))).toFloat()
|
||||
//if you are not pressing forward then don't turn
|
||||
//yawAngVel *= movingForce
|
||||
//Ignore x,y,z its angular velocity:
|
||||
//yaw, pitch, roll
|
||||
return Vec3(yawAngVel.toDouble(), pitchAngVel.toDouble(), 0.0)
|
||||
}
|
||||
|
||||
return Vec3(0.0, 0.0, 0.0)
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: DolphinSettings,
|
||||
state: DolphinState,
|
||||
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: DolphinSettings,
|
||||
state: DolphinState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return vehicle.runtime.resolveBoolean(settings.canJump)
|
||||
}
|
||||
|
||||
override fun setRideBar(
|
||||
settings: DolphinSettings,
|
||||
state: DolphinState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return 0.0f
|
||||
}
|
||||
|
||||
override fun jumpForce(
|
||||
settings: DolphinSettings,
|
||||
state: DolphinState,
|
||||
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: DolphinSettings,
|
||||
state: DolphinState,
|
||||
vehicle: PokemonEntity,
|
||||
regularGravity: Double
|
||||
): Double {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(
|
||||
settings: DolphinSettings,
|
||||
state: DolphinState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return 1.0f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun useRidingAltPose(
|
||||
settings: DolphinSettings,
|
||||
state: DolphinState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun inertia(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity): Double {
|
||||
return 0.05
|
||||
}
|
||||
|
||||
override fun shouldRoll(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun turnOffOnGround(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun dismountOnShift(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePokemonHead(
|
||||
settings: DolphinSettings,
|
||||
state: DolphinState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePlayerHead(
|
||||
settings: DolphinSettings,
|
||||
state: DolphinState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: DolphinSettings) = DolphinState()
|
||||
|
||||
}
|
||||
|
||||
class DolphinSettings : RidingBehaviourSettings {
|
||||
override val key = DolphinBehaviour.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()
|
||||
}
|
||||
}
|
||||
|
||||
class DolphinState : RidingBehaviourState() {
|
||||
var lastVelocity = ridingState(Vec3.ZERO, Side.BOTH)
|
||||
|
||||
override fun reset() {
|
||||
super.reset()
|
||||
lastVelocity.set(Vec3.ZERO, forced = true)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "DolphinState(lastVelocity=${lastVelocity.get()})"
|
||||
}
|
||||
|
||||
override fun copy() = DolphinState().also {
|
||||
it.stamina.set(stamina.get(), forced = true)
|
||||
it.rideVelocity.set(rideVelocity.get(), forced = true)
|
||||
it.lastVelocity.set(lastVelocity.get(), forced = true)
|
||||
}
|
||||
}
|
||||
+474
@@ -0,0 +1,474 @@
|
||||
/*
|
||||
* 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.Expression
|
||||
import com.bedrockk.molang.runtime.MoLangMath.lerp
|
||||
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.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import kotlin.math.*
|
||||
|
||||
class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandState> {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("land/generic")
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
override val style = RidingStyle.LAND
|
||||
|
||||
val poseProvider = PoseProvider<GenericLandSettings, GenericLandState>(PoseType.STAND)
|
||||
.with(PoseOption(PoseType.WALK) { _, state, _ ->
|
||||
return@PoseOption abs(state.rideVelocity.get().z) > 0.2
|
||||
})
|
||||
|
||||
override fun isActive(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
|
||||
//Need to check other fluids
|
||||
if (vehicle.isInWater || vehicle.isUnderWater) {
|
||||
return@any false
|
||||
}
|
||||
//This might not actually work, depending on what the yPos actually is. yPos of the middle of the entity? the feet?
|
||||
if (it.y.toDouble() == (vehicle.position().y)) {
|
||||
val blockState = vehicle.level().getBlockState(it.below())
|
||||
return@any !blockState.isAir && blockState.fluidState.isEmpty
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun pose(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
vehicle: PokemonEntity
|
||||
): PoseType {
|
||||
return this.poseProvider.select(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun speed(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
|
||||
override fun updatePassengerRotation(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
) {
|
||||
|
||||
//Take the inverse so that it cancels out how
|
||||
//much the entity rotates.
|
||||
val turnAmount = calcRotAmount(settings, state, vehicle, driver)
|
||||
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val maxYawDiff = 90.0f
|
||||
|
||||
//Normalize the current rotation diff
|
||||
val rotMod = Mth.wrapDegrees(driver.yRot - vehicle.yRot) / maxYawDiff
|
||||
|
||||
val rotAmount = 10.0f
|
||||
|
||||
//Take the inverse so that you turn more at higher speeds
|
||||
val normSpeed = 1.0f - 0.5f*normalizeVal(state.rideVelocity.get().length(), 0.0, topSpeed).toFloat()
|
||||
|
||||
//driver.yRot += (entity.riding.deltaRotation.y - turnAmount)
|
||||
//driver.setYHeadRot(driver.yHeadRot + (entity.riding.deltaRotation.y) - turnAmount)
|
||||
}
|
||||
|
||||
override fun clampPassengerRotation(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
) {
|
||||
val f = Mth.wrapDegrees(driver.yRot - vehicle.yRot)
|
||||
val lookYawLimit = 90.0f
|
||||
val g = Mth.clamp(f, -lookYawLimit, lookYawLimit)
|
||||
driver.yRotO += g - f
|
||||
driver.yRot = driver.yRot + g - f
|
||||
driver.setYHeadRot(driver.yRot)
|
||||
}
|
||||
|
||||
|
||||
override fun rotation(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Vec2 {
|
||||
val turnAmount = calcRotAmount(settings, state, vehicle, driver)
|
||||
|
||||
return Vec2(vehicle.xRot, vehicle.yRot + turnAmount )
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculates the rotation amount given the difference between
|
||||
* the current player y rot and entity y rot. This gives the
|
||||
* affect of influencing a rides rotation through the mouse
|
||||
* without it being instant.
|
||||
*/
|
||||
fun calcRotAmount(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Float {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
//In degrees per second
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
|
||||
val maxYawDiff = 90.0f
|
||||
|
||||
//Normalize the current rotation diff
|
||||
val rotDiff = Mth.wrapDegrees(driver.yRot - vehicle.yRot)
|
||||
val rotDiffNorm = rotDiff / maxYawDiff
|
||||
|
||||
//Take the square root so that the ride levels out quicker when at lower differences between entity
|
||||
//y and driver y
|
||||
//This influences the speed of the turn based on how far in one direction you're looking
|
||||
val rotDiffMod = (sqrt(abs(rotDiffNorm)) * rotDiffNorm.sign)
|
||||
|
||||
//Take the inverse so that you turn less at higher speeds
|
||||
val normSpeed = 1.0f // = 1.0f - 0.5f*normalizeVal(state.rideVelocityocity.length(), 0.0, topSpeed).toFloat()
|
||||
|
||||
// TODO: This needs to be tweaked with in game results and testing
|
||||
val turnRate = (handling.toFloat() / 20.0f) * 2
|
||||
|
||||
//Ensure you only ever rotate as much difference as there is between the angles.
|
||||
val turnSpeed = turnRate * rotDiffMod * normSpeed
|
||||
val rotAmount = turnSpeed.coerceIn(-abs(rotDiff), abs(rotDiff))
|
||||
|
||||
return rotAmount
|
||||
}
|
||||
|
||||
|
||||
override fun velocity(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
//Limit horizontal velocity that may have accumulated through alternate behaviour
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val currVel = state.rideVelocity.get()
|
||||
// state.rideVelocity.set(Vec3(
|
||||
// max(currVel.x, topSpeed),
|
||||
// currVel.y,
|
||||
// max(currVel.z, topSpeed)
|
||||
// ))
|
||||
|
||||
state.rideVelocity.set(calculateRideSpaceVel(settings, state, vehicle, driver))
|
||||
|
||||
//This is cheap.
|
||||
//Also make it stop quicker the slower it is.
|
||||
val maxSpeed = 1.0
|
||||
//state.currVel = state.currVel.lerp( Vec3(0.0, state.currVel.y, 0.0), 1.0/20.0 )
|
||||
|
||||
|
||||
//entity.deltaMovement = entity.deltaMovement.lerp(velocity, 1.0)
|
||||
//state.currVel = state.currVel.add( f.toDouble() / 20.0, gravity / 20.0 , g.toDouble() / 20.0)
|
||||
return state.rideVelocity.get()
|
||||
}
|
||||
|
||||
private fun calculateRideSpaceVel(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Vec3 {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
|
||||
val speed = state.rideVelocity.get().length()
|
||||
|
||||
//Flag for determining if player is actively inputting
|
||||
var activeInput = false
|
||||
|
||||
var newVelocity = Vec3(state.rideVelocity.get().x, state.rideVelocity.get().y, state.rideVelocity.get().z)
|
||||
|
||||
|
||||
//speed up and slow down based on input
|
||||
if (driver.zza != 0.0f && state.stamina.get() > 0.0) {
|
||||
//make sure it can't exceed top speed
|
||||
val forwardInput = when {
|
||||
driver.zza > 0 && newVelocity.z > topSpeed -> 0.0
|
||||
driver.zza < 0 && newVelocity.z < (-topSpeed / 3.0) -> 0.0
|
||||
else -> driver.zza.sign
|
||||
}
|
||||
|
||||
newVelocity = Vec3(
|
||||
newVelocity.x,
|
||||
newVelocity.y,
|
||||
(newVelocity.z + (accel * forwardInput.toDouble())))
|
||||
|
||||
activeInput = true
|
||||
}
|
||||
|
||||
//Gravity logic
|
||||
if (vehicle.onGround()) {
|
||||
newVelocity = Vec3(newVelocity.x, 0.0, newVelocity.z)
|
||||
} else {
|
||||
val gravity = (9.8 / ( 20.0)) * 0.2
|
||||
val terminalVel = 2.0
|
||||
|
||||
val fallingForce = gravity - ( newVelocity.z.sign *gravity *(abs(newVelocity.z) / 2.0))
|
||||
newVelocity = Vec3(newVelocity.x, max(newVelocity.y - fallingForce, -terminalVel), newVelocity.z)
|
||||
}
|
||||
|
||||
//ground Friction
|
||||
if( abs(newVelocity.z) > 0 && vehicle.onGround() && !activeInput) {
|
||||
newVelocity = newVelocity.subtract(0.0, 0.0, min(0.03 * newVelocity.z.sign, newVelocity.z))
|
||||
}
|
||||
|
||||
//Jump the thang!
|
||||
if (driver.jumping && vehicle.onGround()) {
|
||||
val jumpVel = 1.0
|
||||
newVelocity = newVelocity.add(0.0, 1.0, 0.0)
|
||||
|
||||
//Ensure this doesn't add unwanted forward velocity
|
||||
val mag = if(newVelocity.length() < topSpeed) newVelocity.length() else topSpeed
|
||||
newVelocity = newVelocity.normalize().scale(mag)
|
||||
}
|
||||
|
||||
return newVelocity
|
||||
}
|
||||
|
||||
/*
|
||||
* Normalizes the current speed between minSpeed and maxSpeed.
|
||||
* The result is clamped between 0.0 and 1.0, where 0.0 represents minSpeed and 1.0 represents maxSpeed.
|
||||
*/
|
||||
private fun normalizeVal(currSpeed: Double, minSpeed: Double, maxSpeed: Double): Double {
|
||||
require(maxSpeed > minSpeed) { "maxSpeed must be greater than minSpeed" }
|
||||
return ((currSpeed - minSpeed) / (maxSpeed - minSpeed)).coerceIn(0.0, 1.0)
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
mouseX: Double,
|
||||
mouseYSmoother: SmoothDouble,
|
||||
mouseXSmoother: SmoothDouble,
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
//Smooth out mouse input.
|
||||
val smoothingSpeed = 4
|
||||
val xInput = mouseXSmoother.getNewDeltaValue(mouseX * 0.1, deltaTime * smoothingSpeed);
|
||||
val yInput = mouseYSmoother.getNewDeltaValue(mouseY * 0.1, deltaTime * smoothingSpeed);
|
||||
|
||||
//yaw, pitch, roll
|
||||
return Vec3(0.0, yInput, xInput)
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun setRideBar(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
//Retrieve stamina from state and tick up at a rate of 0.1 a second
|
||||
return (state.stamina.get() / 1.0f)
|
||||
}
|
||||
|
||||
override fun jumpForce(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
jumpStrength: Int
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun gravity(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
vehicle: PokemonEntity,
|
||||
regularGravity: Double
|
||||
): Double {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return 1.0f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun useRidingAltPose(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun inertia(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
vehicle: PokemonEntity
|
||||
): Double {
|
||||
return 0.5
|
||||
}
|
||||
|
||||
override fun shouldRoll(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun turnOffOnGround(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun dismountOnShift(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePokemonHead(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePlayerHead(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: GenericLandSettings) = GenericLandState()
|
||||
}
|
||||
|
||||
class GenericLandSettings : RidingBehaviourSettings {
|
||||
override val key = GenericLandBehaviour.KEY
|
||||
|
||||
var canJump = "true".asExpression()
|
||||
private set
|
||||
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'LAND', 1.0, 0.3)".asExpression()
|
||||
private set
|
||||
|
||||
// Max accel is a whole 1.0 in 1 second. The conversion in the function below is to convert seconds to ticks
|
||||
var accelerationExpr: Expression =
|
||||
"q.get_ride_stats('ACCELERATION', 'LAND', (1.0 / (20.0 * 1.5)), (1.0 / (20.0 * 5.0)))".asExpression()
|
||||
private set
|
||||
|
||||
// Between 30 seconds and 10 seconds at the lowest when at full speed.
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'LAND', 30.0, 10.0)".asExpression()
|
||||
private set
|
||||
|
||||
//Between a one block jump and a ten block jump
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'LAND', 10.0, 1.0)".asExpression()
|
||||
private set
|
||||
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'LAND', 140.0, 20.0)".asExpression()
|
||||
private set
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeExpression(speedExpr)
|
||||
buffer.writeExpression(accelerationExpr)
|
||||
buffer.writeExpression(staminaExpr)
|
||||
buffer.writeExpression(jumpExpr)
|
||||
buffer.writeExpression(handlingExpr)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
speedExpr = buffer.readExpression()
|
||||
accelerationExpr = buffer.readExpression()
|
||||
staminaExpr = buffer.readExpression()
|
||||
jumpExpr = buffer.readExpression()
|
||||
handlingExpr = buffer.readExpression()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class GenericLandState : RidingBehaviourState() {
|
||||
var currSpeed = ridingState(0.0, Side.BOTH)
|
||||
|
||||
override fun copy() = GenericLandState().also {
|
||||
it.rideVelocity.set(this.rideVelocity.get(), forced = true)
|
||||
it.stamina.set(this.stamina.get(), forced = true)
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
if (previous !is GenericLandState) return false
|
||||
return super.shouldSync(previous)
|
||||
}
|
||||
}
|
||||
+264
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
* 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 val style = 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
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* 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.Expression
|
||||
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.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.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
|
||||
|
||||
class GliderAirBehaviour : RidingBehaviour<GliderAirSettings, RidingBehaviourState> {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("air/glider")
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
override val style = RidingStyle.AIR
|
||||
|
||||
val poseProvider = PoseProvider<GliderAirSettings, RidingBehaviourState>(PoseType.HOVER)
|
||||
.with(PoseOption(PoseType.FLY) { _, _, entity -> entity.entityData.get(PokemonEntity.MOVING) })
|
||||
|
||||
override fun isActive(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun pose(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity): PoseType {
|
||||
return poseProvider.select(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun speed(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity, driver: Player): Float {
|
||||
return vehicle.runtime.resolveFloat(settings.speed)
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
settings: GliderAirSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Vec2 {
|
||||
return Vec2(driver.xRot, driver.yRot)
|
||||
}
|
||||
|
||||
override fun velocity(
|
||||
settings: GliderAirSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
val xVector = if (vehicle.runtime.resolveBoolean(settings.canStrafe)) driver.xxa.toDouble() else 0.0
|
||||
val yVector = -vehicle.runtime.resolveDouble(settings.glideSpeed)
|
||||
val zVector = driver.zza.toDouble()
|
||||
val statSpeed = vehicle.rideProp.calculate(RidingStat.SPEED, RidingStyle.AIR, 0 )
|
||||
|
||||
return Vec3(xVector, yVector, zVector)
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: GliderAirSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: GliderAirSettings,
|
||||
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: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity, driver: Player): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun setRideBar(
|
||||
settings: GliderAirSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return 0.0f
|
||||
}
|
||||
|
||||
override fun jumpForce(
|
||||
settings: GliderAirSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
jumpStrength: Int
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun gravity(
|
||||
settings: GliderAirSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
regularGravity: Double
|
||||
): Double {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(
|
||||
settings: GliderAirSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return 1.0f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun useRidingAltPose(
|
||||
settings: GliderAirSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun inertia(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Double {
|
||||
return 0.5
|
||||
}
|
||||
|
||||
override fun shouldRoll(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun turnOffOnGround(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun dismountOnShift(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePokemonHead(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePlayerHead(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: GliderAirSettings) = RidingBehaviourState()
|
||||
}
|
||||
|
||||
class GliderAirSettings : RidingBehaviourSettings {
|
||||
override val key = GliderAirBehaviour.KEY
|
||||
|
||||
var glideSpeed: Expression = "0.1".asExpression()
|
||||
private set
|
||||
|
||||
var speed: Expression = "1.0".asExpression()
|
||||
private set
|
||||
|
||||
var canStrafe: Expression = "false".asExpression()
|
||||
private set
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeExpression(glideSpeed)
|
||||
buffer.writeExpression(speed)
|
||||
buffer.writeExpression(canStrafe)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
glideSpeed = buffer.readExpression()
|
||||
speed = buffer.readExpression()
|
||||
canStrafe = buffer.readExpression()
|
||||
}
|
||||
}
|
||||
+305
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
* 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.Expression
|
||||
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
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sin
|
||||
|
||||
class HelicopterBehaviour : RidingBehaviour<HelicopterSettings, RidingBehaviourState> {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("air/helicopter")
|
||||
val ROTATION_LIMIT = 30.0f
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
override val style = RidingStyle.AIR
|
||||
|
||||
val poseProvider = PoseProvider<HelicopterSettings, RidingBehaviourState>(PoseType.HOVER)
|
||||
.with(PoseOption(PoseType.FLY) { _, _, entity -> entity.entityData.get(PokemonEntity.MOVING) })
|
||||
|
||||
override fun isActive(settings: HelicopterSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
//If there are only fluid blocks or air block below the ride
|
||||
//then activate the controller. If it is in water the ride will
|
||||
//dismount accordingly
|
||||
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
|
||||
if (it.y.toDouble() == (vehicle.position().y)) {
|
||||
val blockState = vehicle.level().getBlockState(it.below())
|
||||
return@any (blockState.isAir || !blockState.fluidState.isEmpty)
|
||||
}
|
||||
true
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
override fun pose(settings: HelicopterSettings, state: RidingBehaviourState, vehicle: PokemonEntity): PoseType {
|
||||
return poseProvider.select(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun speed(settings: HelicopterSettings, state: RidingBehaviourState, vehicle: PokemonEntity, driver: Player): Float {
|
||||
//Increased max speed to exaggerate movement.
|
||||
//This likely just needs to be a static number if it really is just
|
||||
//the scalar for the velocity vector
|
||||
return 2.0f
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
settings: HelicopterSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Vec2 {
|
||||
return Vec2(driver.xRot * 0.5f, driver.yRot)
|
||||
}
|
||||
|
||||
override fun velocity(
|
||||
settings: HelicopterSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
val runtime = vehicle.runtime
|
||||
|
||||
//If the player is not modulating height then hover
|
||||
var yVel = if (driver.jumping) runtime.resolveDouble(settings.verticalVelocity)
|
||||
else if (driver.isShiftKeyDown) -runtime.resolveDouble(settings.verticalVelocity)
|
||||
else (0.0)
|
||||
|
||||
yVel *= 0.25
|
||||
var xVel = 0.0
|
||||
var zVel = 0.0
|
||||
|
||||
val controller = (driver as? OrientationControllable)?.orientationController
|
||||
|
||||
//Horizontal velocity is based on pitch and roll
|
||||
if (controller != null) {
|
||||
xVel = -1.0 * sin(Math.toRadians(controller.roll.toDouble()))
|
||||
zVel = sin(Math.toRadians(controller.pitch.toDouble()))
|
||||
}
|
||||
|
||||
return Vec3(xVel, yVel, zVel)
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: HelicopterSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
val controller = (driver as? OrientationControllable)?.orientationController
|
||||
|
||||
//In degrees per second? It was supposed to be I think but
|
||||
//I have messed something up
|
||||
val rotationChangeRate = 2.0
|
||||
val rotLimit = 30.0
|
||||
|
||||
var pitchForce = driver.zza * rotationChangeRate
|
||||
var rollForce = -1.0 * driver.xxa * rotationChangeRate
|
||||
|
||||
|
||||
//TODO: Fix accumulated movement causing pitch and roll out of set bounds
|
||||
if (controller != null) {
|
||||
//If the roll or pitch have exceeded the limit then do not modulate that
|
||||
//rotation in that direction further
|
||||
|
||||
//modulate the forces so that they trend towards 0 as they approach rotation limit.
|
||||
//The current function has it drop off very quickly which may not be ideal.
|
||||
//Prev function:
|
||||
// pitchForce = pitchForce * max(rotLim - abs(rollable.pitch.toDouble()), 0.0)
|
||||
|
||||
//The three denotes that the force will be 1/3 what it would have been at
|
||||
//the rotation limit
|
||||
rollForce *= 2.0.pow(-1.0 * (abs(controller.roll.toDouble()) / ROTATION_LIMIT))
|
||||
|
||||
pitchForce *= 2.0.pow(-1.0 * (abs(controller.pitch.toDouble()) / ROTATION_LIMIT))
|
||||
|
||||
if (controller.roll >= ROTATION_LIMIT && rollForce > 0.0) {
|
||||
rollForce = 0.0;
|
||||
}
|
||||
if (controller.roll <= -ROTATION_LIMIT && rollForce < 0.0) {
|
||||
rollForce = 0.0;
|
||||
}
|
||||
if (controller.pitch >= ROTATION_LIMIT && pitchForce > 0.0) {
|
||||
pitchForce = 0.0;
|
||||
}
|
||||
if (controller.pitch <= -ROTATION_LIMIT && pitchForce < 0.0) {
|
||||
pitchForce = 0.0;
|
||||
}
|
||||
|
||||
//Ignore x,y,z its angular velocity:
|
||||
//yaw, pitch, roll
|
||||
return Vec3(0.0, pitchForce.toDouble(), rollForce)
|
||||
}
|
||||
|
||||
return Vec3(0.0, 0.0, 0.0)
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: HelicopterSettings,
|
||||
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
|
||||
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
val invertYaw = if (Cobblemon.config.invertYaw) -1 else 1
|
||||
//yaw, pitch, roll
|
||||
return Vec3(mouseX * invertYaw, 0.0, 0.0)
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
settings: HelicopterSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun setRideBar(
|
||||
settings: HelicopterSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return 0.0f
|
||||
}
|
||||
|
||||
override fun jumpForce(
|
||||
settings: HelicopterSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
jumpStrength: Int
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun gravity(
|
||||
settings: HelicopterSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
regularGravity: Double
|
||||
): Double {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(
|
||||
settings: HelicopterSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return 1.0f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(settings: HelicopterSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun useRidingAltPose(
|
||||
settings: HelicopterSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun inertia(settings: HelicopterSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Double {
|
||||
return 0.1
|
||||
}
|
||||
|
||||
override fun shouldRoll(settings: HelicopterSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun turnOffOnGround(settings: HelicopterSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun dismountOnShift(settings: HelicopterSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePokemonHead(
|
||||
settings: HelicopterSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePlayerHead(settings: HelicopterSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: HelicopterSettings) = RidingBehaviourState()
|
||||
}
|
||||
|
||||
|
||||
class HelicopterSettings : RidingBehaviourSettings {
|
||||
override val key = HelicopterBehaviour.KEY
|
||||
|
||||
var gravity: Expression = "1.0".asExpression()
|
||||
private set
|
||||
|
||||
var horizontalAcceleration: Expression = "0.1".asExpression()
|
||||
private set
|
||||
|
||||
var verticalVelocity: Expression = "1.0".asExpression()
|
||||
private set
|
||||
|
||||
var speed: Expression = "1.0".asExpression()
|
||||
private set
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeExpression(gravity)
|
||||
buffer.writeExpression(horizontalAcceleration)
|
||||
buffer.writeExpression(verticalVelocity)
|
||||
buffer.writeExpression(speed)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
gravity = buffer.readExpression()
|
||||
horizontalAcceleration = buffer.readExpression()
|
||||
verticalVelocity = buffer.readExpression()
|
||||
speed = buffer.readExpression()
|
||||
}
|
||||
}
|
||||
+479
@@ -0,0 +1,479 @@
|
||||
/*
|
||||
* 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.Expression
|
||||
import com.bedrockk.molang.runtime.MoLangMath.lerp
|
||||
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.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import kotlin.math.*
|
||||
|
||||
class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("air/hover")
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
override val style = RidingStyle.AIR
|
||||
|
||||
val poseProvider = PoseProvider<HoverSettings, HoverState>(PoseType.STAND)
|
||||
.with(PoseOption(PoseType.WALK) { _, state, _ ->
|
||||
return@PoseOption abs(state.rideVelocity.get().z) > 0.2
|
||||
})
|
||||
|
||||
override fun isActive(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
|
||||
//Need to check other fluids
|
||||
if (vehicle.isInWater || vehicle.isUnderWater) {
|
||||
return@any false
|
||||
}
|
||||
//This might not actually work, depending on what the yPos actually is. yPos of the middle of the entity? the feet?
|
||||
if (it.y.toDouble() == (vehicle.position().y)) {
|
||||
val blockState = vehicle.level().getBlockState(it.below())
|
||||
return@any !blockState.isAir && blockState.fluidState.isEmpty
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun pose(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity
|
||||
): PoseType {
|
||||
return this.poseProvider.select(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun speed(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
|
||||
override fun updatePassengerRotation(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
) {
|
||||
|
||||
//Take the inverse so that it cancels out how
|
||||
//much the entity rotates.
|
||||
val turnAmount = calcRotAmount(settings, state, vehicle, driver)
|
||||
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val maxYawDiff = 90.0f
|
||||
|
||||
//Normalize the current rotation diff
|
||||
val rotMod = Mth.wrapDegrees(driver.yRot - vehicle.yRot) / maxYawDiff
|
||||
|
||||
val rotAmount = 10.0f
|
||||
|
||||
//Take the inverse so that you turn more at higher speeds
|
||||
val normSpeed = 1.0f - 0.5f*normalizeVal(state.rideVelocity.get().length(), 0.0, topSpeed).toFloat()
|
||||
|
||||
//driver.yRot += (entity.riding.deltaRotation.y - turnAmount)
|
||||
//driver.setYHeadRot(driver.yHeadRot + (entity.riding.deltaRotation.y) - turnAmount)
|
||||
}
|
||||
|
||||
override fun clampPassengerRotation(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
) {
|
||||
val f = Mth.wrapDegrees(driver.yRot - vehicle.yRot)
|
||||
val lookYawLimit = 90.0f
|
||||
val g = Mth.clamp(f, -lookYawLimit, lookYawLimit)
|
||||
driver.yRotO += g - f
|
||||
driver.yRot = driver.yRot + g - f
|
||||
driver.setYHeadRot(driver.yRot)
|
||||
}
|
||||
|
||||
|
||||
override fun rotation(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Vec2 {
|
||||
val turnAmount = calcRotAmount(settings, state, vehicle, driver)
|
||||
|
||||
return Vec2(driver.xRot, vehicle.yRot + turnAmount )
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculates the rotation amount given the difference between
|
||||
* the current player y rot and entity y rot. This gives the
|
||||
* affect of influencing a rides rotation through the mouse
|
||||
* without it being instant.
|
||||
*/
|
||||
fun calcRotAmount(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Float {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
//In degrees per second
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
|
||||
val maxYawDiff = 90.0f
|
||||
|
||||
//Normalize the current rotation diff
|
||||
val rotDiff = Mth.wrapDegrees(driver.yRot - vehicle.yRot)
|
||||
val rotDiffNorm = rotDiff / maxYawDiff
|
||||
|
||||
//Take the square root so that the ride levels out quicker when at lower differences between entity
|
||||
//y and driver y
|
||||
//This influences the speed of the turn based on how far in one direction you're looking
|
||||
val rotDiffMod = (sqrt(abs(rotDiffNorm)) * rotDiffNorm.sign)
|
||||
|
||||
//Take the inverse so that you turn less at higher speeds
|
||||
val normSpeed = 1.0f // = 1.0f - 0.5f*normalizeVal(state.rideVelocityocity.length(), 0.0, topSpeed).toFloat()
|
||||
|
||||
// TODO: This needs to be tweaked with in game results and testing
|
||||
val turnRate = (handling.toFloat() / 20.0f) * 2
|
||||
|
||||
//Ensure you only ever rotate as much difference as there is between the angles.
|
||||
val turnSpeed = turnRate * rotDiffMod * normSpeed
|
||||
val rotAmount = turnSpeed.coerceIn(-abs(rotDiff), abs(rotDiff))
|
||||
|
||||
return rotAmount
|
||||
}
|
||||
|
||||
|
||||
override fun velocity(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
|
||||
val strafeFactor = 0.2
|
||||
val f = driver.xxa * strafeFactor
|
||||
var g = driver.zza
|
||||
if (g <= 0.0f) {
|
||||
g *= -1.0f
|
||||
}
|
||||
|
||||
return calculateRideSpaceVel(settings, state, vehicle, driver).scale(10.0)
|
||||
}
|
||||
|
||||
private fun calculateRideSpaceVel(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Vec3 {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
|
||||
val speed = state.rideVelocity.get().length()
|
||||
|
||||
//Flag for determining if player is actively inputting
|
||||
var activeInput = false
|
||||
|
||||
val minSpeed = 0.0
|
||||
|
||||
var newVelocity = Vec3(state.rideVelocity.get().x, state.rideVelocity.get().y, state.rideVelocity.get().z)
|
||||
|
||||
var jumpVec = Vec3.ZERO
|
||||
|
||||
|
||||
//speed up and slow down based on input
|
||||
if (driver.zza != 0.0f && state.stamina.get() > 0.0) {
|
||||
//make sure it can't exceed top speed
|
||||
val forwardInput = when {
|
||||
driver.zza > 0 && newVelocity.z > topSpeed -> 0.0
|
||||
driver.zza < 0 && newVelocity.z < (-topSpeed / 3.0) -> 0.0
|
||||
else -> driver.zza.sign
|
||||
}
|
||||
|
||||
newVelocity = Vec3(
|
||||
newVelocity.x,
|
||||
newVelocity.y,
|
||||
(newVelocity.z + (accel * forwardInput.toDouble())))
|
||||
|
||||
activeInput = true
|
||||
jumpVec = jumpVec.add(0.0, 0.0, forwardInput.toDouble())
|
||||
}
|
||||
|
||||
//speed up and slow down based on input
|
||||
if (driver.xxa != 0.0f && state.stamina.get() > 0.0) {
|
||||
//make sure it can't exceed top speed
|
||||
val forwardInput = when {
|
||||
driver.xxa > 0 && newVelocity.x > topSpeed -> 0.0
|
||||
driver.xxa < 0 && newVelocity.x < (-topSpeed / 3.0) -> 0.0
|
||||
else -> driver.xxa.sign
|
||||
}
|
||||
|
||||
newVelocity = Vec3(
|
||||
newVelocity.x + (accel * forwardInput.toDouble()),
|
||||
newVelocity.y,
|
||||
(newVelocity.z ))
|
||||
|
||||
activeInput = true
|
||||
}
|
||||
|
||||
//Vertical movement based on driver input.
|
||||
val vertTopSpeed = topSpeed / 2.0
|
||||
val vertInput = when {
|
||||
driver.jumping -> 1.0
|
||||
driver.isShiftKeyDown -> -1.0
|
||||
else -> 0.0
|
||||
}
|
||||
|
||||
if (vertInput != 0.0 && state.stamina.get() > 0.0) {
|
||||
newVelocity = Vec3(
|
||||
newVelocity.x,
|
||||
(newVelocity.y + (accel * vertInput)).coerceIn(-vertTopSpeed, vertTopSpeed),
|
||||
newVelocity.z)
|
||||
activeInput = true
|
||||
}
|
||||
|
||||
|
||||
//air Friction
|
||||
|
||||
|
||||
|
||||
return newVelocity
|
||||
}
|
||||
|
||||
/*
|
||||
* Normalizes the current speed between minSpeed and maxSpeed.
|
||||
* The result is clamped between 0.0 and 1.0, where 0.0 represents minSpeed and 1.0 represents maxSpeed.
|
||||
*/
|
||||
private fun normalizeVal(currSpeed: Double, minSpeed: Double, maxSpeed: Double): Double {
|
||||
require(maxSpeed > minSpeed) { "maxSpeed must be greater than minSpeed" }
|
||||
return ((currSpeed - minSpeed) / (maxSpeed - minSpeed)).coerceIn(0.0, 1.0)
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
mouseX: Double,
|
||||
mouseYSmoother: SmoothDouble,
|
||||
mouseXSmoother: SmoothDouble,
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
//Smooth out mouse input.
|
||||
val smoothingSpeed = 4
|
||||
val xInput = mouseXSmoother.getNewDeltaValue(mouseX * 0.1, deltaTime * smoothingSpeed);
|
||||
val yInput = mouseYSmoother.getNewDeltaValue(mouseY * 0.1, deltaTime * smoothingSpeed);
|
||||
|
||||
//yaw, pitch, roll
|
||||
return Vec3(0.0, yInput, xInput)
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun setRideBar(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
//Retrieve stamina from state and use it to set the "stamina bar"
|
||||
return (state.stamina.get() / 1.0f)
|
||||
}
|
||||
|
||||
override fun jumpForce(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
jumpStrength: Int
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun gravity(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity,
|
||||
regularGravity: Double
|
||||
): Double {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return 1.0f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun useRidingAltPose(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun inertia(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity
|
||||
): Double {
|
||||
return 0.05
|
||||
}
|
||||
|
||||
override fun shouldRoll(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun turnOffOnGround(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun dismountOnShift(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePokemonHead(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePlayerHead(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: HoverSettings) = HoverState()
|
||||
}
|
||||
|
||||
class HoverSettings : RidingBehaviourSettings {
|
||||
override val key = HoverBehaviour.KEY
|
||||
|
||||
var canJump = "true".asExpression()
|
||||
private set
|
||||
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 1.0, 0.3)".asExpression()
|
||||
private set
|
||||
|
||||
// Max accel is a whole 1.0 in 1 second. The conversion in the function below is to convert seconds to ticks
|
||||
var accelerationExpr: Expression =
|
||||
"q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 0.5)), (1.0 / (20.0 * 5.0)))".asExpression()
|
||||
private set
|
||||
|
||||
// Between 30 seconds and 10 seconds at the lowest when at full speed.
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 30.0, 10.0)".asExpression()
|
||||
private set
|
||||
|
||||
//Between a one block jump and a ten block jump
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 10.0, 1.0)".asExpression()
|
||||
private set
|
||||
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 140.0, 20.0)".asExpression()
|
||||
private set
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeExpression(speedExpr)
|
||||
buffer.writeExpression(accelerationExpr)
|
||||
buffer.writeExpression(staminaExpr)
|
||||
buffer.writeExpression(jumpExpr)
|
||||
buffer.writeExpression(handlingExpr)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
speedExpr = buffer.readExpression()
|
||||
accelerationExpr = buffer.readExpression()
|
||||
staminaExpr = buffer.readExpression()
|
||||
jumpExpr = buffer.readExpression()
|
||||
handlingExpr = buffer.readExpression()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class HoverState : RidingBehaviourState() {
|
||||
|
||||
override fun copy() = HoverState().also {
|
||||
it.rideVelocity.set(this.rideVelocity.get(), forced = true)
|
||||
it.stamina.set(this.stamina.get(), forced = true)
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
if (previous !is HoverState) return false
|
||||
return super.shouldSync(previous)
|
||||
}
|
||||
}
|
||||
+456
@@ -0,0 +1,456 @@
|
||||
/*
|
||||
* 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.Expression
|
||||
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.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import 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
|
||||
import kotlin.math.*
|
||||
|
||||
class JetAirBehaviour : RidingBehaviour<JetAirSettings, JetAirState> {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("air/jet")
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
override val style = RidingStyle.AIR
|
||||
|
||||
val poseProvider = PoseProvider<JetAirSettings, JetAirState>(PoseType.HOVER)
|
||||
.with(PoseOption(PoseType.FLY) { _, state, _ -> state.rideVelocity.get().z > 0.1 })
|
||||
|
||||
override fun isActive(settings: JetAirSettings, state: JetAirState, vehicle: PokemonEntity): Boolean {
|
||||
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
|
||||
//Need to check other fluids
|
||||
if (vehicle.isInWater || vehicle.isUnderWater) {
|
||||
return@any false
|
||||
}
|
||||
//This might not actually work, depending on what the yPos actually is. yPos of the middle of the entity? the feet?
|
||||
if (it.y.toDouble() == (vehicle.position().y)) {
|
||||
val blockState = vehicle.level().getBlockState(it.below())
|
||||
return@any !(!blockState.isAir && blockState.fluidState.isEmpty)
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun pose(settings: JetAirSettings, state: JetAirState, vehicle: PokemonEntity): PoseType {
|
||||
return poseProvider.select(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun speed(settings: JetAirSettings, state: JetAirState, vehicle: PokemonEntity, driver: Player): Float {
|
||||
//retrieve stats
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val staminaStat = vehicle.runtime.resolveDouble(settings.staminaExpr)
|
||||
|
||||
//retrieve minSpeed
|
||||
val minSpeed = vehicle.runtime.resolveDouble(settings.minSpeed)
|
||||
|
||||
//TODO: Reintroduce stamina drain once stats start to be polished and tweaked
|
||||
state.stamina.set(1.0f)
|
||||
// //Reduce stamina unless stamina is infinite
|
||||
// if (!vehicle.runtime.resolveBoolean(settings.infiniteStamina)) {
|
||||
// //Calculate stamina loss due to speed
|
||||
// //At max speed it will tick down 0.1 a second so the stamina will last ten seconds
|
||||
// //There has got to be a better way to express this equation. It interpolates between 0.5 and 1.0
|
||||
// var staminaRate = (normalizeSpeed(state.rideVelocity.get().length(), minSpeed, topSpeed))
|
||||
//
|
||||
// //interpolate between 0.25 and 1.0 so that you always have at least a min of 0.25 stam loss
|
||||
// staminaRate = 0.25 + (0.75 * staminaRate.pow(3))
|
||||
//
|
||||
// //Calculate stamina loss in seconds achievable at top speed
|
||||
// val staminaLoss = staminaRate * (1.0 / (20.0 * staminaStat))
|
||||
// state.stamina.set(max(state.stamina.get() - staminaLoss, 0.0).toFloat())
|
||||
// } else {
|
||||
// state.stamina.set(1.0f)
|
||||
// }
|
||||
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
|
||||
//TODO: Move these functions to a riding util class.
|
||||
/*
|
||||
* Normalizes the current speed between minSpeed and maxSpeed.
|
||||
* The result is clamped between 0.0 and 1.0, where 0.0 represents minSpeed and 1.0 represents maxSpeed.
|
||||
*/
|
||||
private fun normalizeSpeed(currSpeed: Double, minSpeed: Double, maxSpeed: Double): Double {
|
||||
require(maxSpeed > minSpeed) { "maxSpeed must be greater than minSpeed" }
|
||||
return ((currSpeed - minSpeed) / (maxSpeed - minSpeed)).coerceIn(0.0, 1.0)
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
settings: JetAirSettings,
|
||||
state: JetAirState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Vec2 {
|
||||
return Vec2(driver.xRot * 0.5f, driver.yRot)
|
||||
}
|
||||
|
||||
override fun velocity(
|
||||
settings: JetAirSettings,
|
||||
state: JetAirState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
var upForce = 0.0
|
||||
var forwardForce = 0.0
|
||||
|
||||
val controller = (driver as? OrientationControllable)?.orientationController
|
||||
|
||||
//Calculate ride space velocity
|
||||
calculateRideSpaceVel(settings, state, vehicle, driver)
|
||||
|
||||
//Translate ride space velocity to world space velocity.
|
||||
if (controller != null) {
|
||||
upForce = -1.0 * sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().z
|
||||
forwardForce = cos(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().z
|
||||
}
|
||||
|
||||
//If stamina has run out then initiate forced glide down.
|
||||
upForce = if (state.stamina.get() > 0.0) upForce else -0.7
|
||||
|
||||
val altitudeLimit = vehicle.runtime.resolveDouble(settings.jumpExpr)
|
||||
|
||||
//Only limit altitude if altitude is not infinite
|
||||
if (!vehicle.runtime.resolveBoolean(settings.infiniteAltitude)) {
|
||||
//Provide a hard limit on altitude
|
||||
upForce = if (vehicle.y >= altitudeLimit && upForce > 0) 0.0 else upForce
|
||||
}
|
||||
|
||||
val velocity = Vec3(0.0, upForce, forwardForce)
|
||||
|
||||
return velocity
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculates the change in the ride space vector due to player input and ride state
|
||||
*/
|
||||
private fun calculateRideSpaceVel(
|
||||
settings: JetAirSettings,
|
||||
state: JetAirState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
) {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
|
||||
val altitudeLimit = vehicle.runtime.resolveDouble(settings.jumpExpr)
|
||||
val minSpeed = vehicle.runtime.resolveDouble(settings.minSpeed)
|
||||
val speed = state.rideVelocity.get().length()
|
||||
|
||||
//Give no altitude limit if at max jump stat.
|
||||
val pushingHeightLimit = if (vehicle.runtime.resolveBoolean(settings.infiniteStamina)) false
|
||||
else (vehicle.y >= altitudeLimit && vehicle.xRot <= 0)
|
||||
|
||||
|
||||
//speed up and slow down based on input
|
||||
if (driver.zza > 0.0 && speed < topSpeed && state.stamina.get() > 0.0f && !pushingHeightLimit) {
|
||||
//modify acceleration to be slower when at closer speeds to top speed
|
||||
val accelMod = max(-(normalizeSpeed(speed, minSpeed, topSpeed)) + 1, 0.0)
|
||||
state.rideVelocity.set(Vec3(state.rideVelocity.get().x, state.rideVelocity.get().y, min(state.rideVelocity.get().z + (accel * accelMod), topSpeed)))
|
||||
} else if (driver.zza >= 0.0 && (state.stamina.get() == 0.0f || pushingHeightLimit)) {
|
||||
state.rideVelocity.set(Vec3(state.rideVelocity.get().x, state.rideVelocity.get().y, max(state.rideVelocity.get().z - ((accel) / 4), minSpeed)))
|
||||
} else if (driver.zza < 0.0 && speed > minSpeed) {
|
||||
//modify deccel to be slower when at closer speeds to minimum speed
|
||||
val deccelMod = max((normalizeSpeed(speed, minSpeed, topSpeed) - 1).pow(2) * 8, 0.1)
|
||||
|
||||
//Decelerate currently always a constant half of max acceleration.
|
||||
state.rideVelocity.set(Vec3(state.rideVelocity.get().x, state.rideVelocity.get().y, max(state.rideVelocity.get().z - ((accel * deccelMod) / 2), minSpeed)))
|
||||
}
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: JetAirSettings,
|
||||
state: JetAirState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
//Cap at a rate of 5fps so frame skips dont lead to huge jumps
|
||||
val cappedDeltaTime = min(deltaTime, 0.2)
|
||||
|
||||
//Get handling in degrees per second
|
||||
val yawRotRate = vehicle.runtime.resolveDouble(settings.handlingYawExpr)
|
||||
|
||||
//Base the change off of deltatime.
|
||||
var handlingYaw = yawRotRate * (cappedDeltaTime)
|
||||
|
||||
//apply stamina debuff if applicable
|
||||
handlingYaw *= if (state.stamina.get() > 0.0) 1.0 else 0.5
|
||||
|
||||
//A+D to yaw
|
||||
val yawForce = driver.xxa * handlingYaw * -1
|
||||
|
||||
return Vec3(yawForce, 0.0, 0.0)
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: JetAirSettings,
|
||||
state: JetAirState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
mouseX: Double,
|
||||
mouseYSmoother: SmoothDouble,
|
||||
mouseXSmoother: SmoothDouble,
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
//TODO: figure out a cleaner solution to this issue of large jumps when skipping frames or lagging
|
||||
//Cap at a rate of 5fps so frame skips dont lead to huge jumps
|
||||
val cappedDeltaTime = min(deltaTime, 0.2)
|
||||
|
||||
val invertRoll = if (Cobblemon.config.invertRoll) -1 else 1
|
||||
val invertPitch = if (Cobblemon.config.invertPitch) -1 else 1
|
||||
|
||||
// Smooth out and accumulate roll input
|
||||
val smoothingSpeed = 4.0
|
||||
val mouseXc = (mouseX).coerceIn(-60.0, 60.0)
|
||||
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1 * invertRoll, deltaTime * smoothingSpeed);
|
||||
|
||||
// Accumulate the y mouse input for pitch
|
||||
//state.currMouseXForce.set((state.currMouseXForce.get() + (0.0015 * mouseX * invertRoll)).coerceIn(-1.0, 1.0))
|
||||
state.currMouseYForce.set((state.currMouseYForce.get() + (0.0015 * mouseY * invertPitch)).coerceIn(-1.0, 1.0))
|
||||
|
||||
//Get handling in degrees per second
|
||||
var handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
|
||||
|
||||
//convert it to delta time
|
||||
handling *= (cappedDeltaTime)
|
||||
|
||||
//apply stamina debuff if applicable
|
||||
handling *= if (state.stamina.get() > 0.0) 1.0 else 0.5
|
||||
|
||||
val poke = driver.vehicle as? PokemonEntity
|
||||
|
||||
//TODO: reevaluate if deadzones are needed and if they are still causing issues.
|
||||
//create deadzones for the constant input values.
|
||||
//val xInput = remapWithDeadzone(state.currMouseXForce, 0.025, 1.0)
|
||||
//val yInput = remapWithDeadzone(state.currMouseYForce, 0.025, 1.0)
|
||||
|
||||
val pitchRot = handling * state.currMouseYForce.get()
|
||||
|
||||
//Roll is 1.5 times as fast as pitch
|
||||
|
||||
//val rollRot = handling * 1.5 * state.currMouseXForce.get()
|
||||
val rollRot = handling * 1.5 * xInput.coerceIn(-1.0,1.0)
|
||||
//yaw, pitch, roll
|
||||
return Vec3(0.0, pitchRot, rollRot)
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
settings: JetAirSettings,
|
||||
state: JetAirState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun setRideBar(
|
||||
settings: JetAirSettings,
|
||||
state: JetAirState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return (state.stamina.get() / 1.0f)
|
||||
}
|
||||
|
||||
override fun jumpForce(
|
||||
settings: JetAirSettings,
|
||||
state: JetAirState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
jumpStrength: Int
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun gravity(
|
||||
settings: JetAirSettings,
|
||||
state: JetAirState,
|
||||
vehicle: PokemonEntity,
|
||||
regularGravity: Double
|
||||
): Double {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(
|
||||
settings: JetAirSettings,
|
||||
state: JetAirState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val minSpeed = vehicle.runtime.resolveDouble(settings.minSpeed)
|
||||
|
||||
//Must I ensure that topspeed is greater than minimum?
|
||||
val normalizedSpeed = normalizeSpeed(state.rideVelocity.get().length(), minSpeed, topSpeed)
|
||||
|
||||
//TODO: Determine if this should be based on max possible speed instead of top speed.
|
||||
//Only ever want the fov change to be a max of 0.2 and for it to have non linear scaling.
|
||||
return 1.0f + normalizedSpeed.pow(2).toFloat() * 0.2f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(settings: JetAirSettings, state: JetAirState, vehicle: PokemonEntity): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun useRidingAltPose(
|
||||
settings: JetAirSettings,
|
||||
state: JetAirState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun inertia(settings: JetAirSettings, state: JetAirState, vehicle: PokemonEntity): Double {
|
||||
return 0.5
|
||||
}
|
||||
|
||||
override fun shouldRoll(settings: JetAirSettings, state: JetAirState, vehicle: PokemonEntity): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun turnOffOnGround(settings: JetAirSettings, state: JetAirState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun dismountOnShift(settings: JetAirSettings, state: JetAirState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePokemonHead(
|
||||
settings: JetAirSettings,
|
||||
state: JetAirState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePlayerHead(settings: JetAirSettings, state: JetAirState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: JetAirSettings) = JetAirState()
|
||||
}
|
||||
|
||||
class JetAirSettings : RidingBehaviourSettings {
|
||||
override val key = JetAirBehaviour.KEY
|
||||
|
||||
var gravity: Expression = "0".asExpression()
|
||||
private set
|
||||
|
||||
var minSpeed: Expression = "0.8".asExpression()
|
||||
private set
|
||||
|
||||
var handlingYawExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 50.0, 25.0)".asExpression()
|
||||
private set
|
||||
|
||||
// Make configurable by json
|
||||
var infiniteStamina: Expression = "false".asExpression()
|
||||
private set
|
||||
|
||||
var infiniteAltitude: Expression = "false".asExpression()
|
||||
private set
|
||||
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 300.0, 128.0)".asExpression()
|
||||
private set
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 140.0, 20.0)".asExpression()
|
||||
private set
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 1.8, 1.0)".asExpression()
|
||||
private set
|
||||
var accelerationExpr: Expression = "q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 1.0)), (1.0 / (20.0 * 5.0)))".asExpression()
|
||||
private set
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 60.0, 10.0)".asExpression()
|
||||
private set
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeExpression(gravity)
|
||||
buffer.writeExpression(minSpeed)
|
||||
buffer.writeExpression(handlingYawExpr)
|
||||
buffer.writeExpression(infiniteStamina)
|
||||
buffer.writeExpression(infiniteAltitude)
|
||||
buffer.writeExpression(jumpExpr)
|
||||
buffer.writeExpression(handlingExpr)
|
||||
buffer.writeExpression(speedExpr)
|
||||
buffer.writeExpression(accelerationExpr)
|
||||
buffer.writeExpression(staminaExpr)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
gravity = buffer.readExpression()
|
||||
minSpeed = buffer.readExpression()
|
||||
handlingYawExpr = buffer.readExpression()
|
||||
infiniteStamina = buffer.readExpression()
|
||||
infiniteAltitude = buffer.readExpression()
|
||||
jumpExpr = buffer.readExpression()
|
||||
handlingExpr = buffer.readExpression()
|
||||
speedExpr = buffer.readExpression()
|
||||
accelerationExpr = buffer.readExpression()
|
||||
staminaExpr = buffer.readExpression()
|
||||
}
|
||||
}
|
||||
|
||||
class JetAirState : RidingBehaviourState() {
|
||||
var currSpeed = ridingState(0.0, Side.CLIENT)
|
||||
var currMouseXForce = ridingState(0.0, Side.CLIENT)
|
||||
var currMouseYForce = ridingState(0.0, Side.CLIENT)
|
||||
|
||||
override fun encode(buffer: FriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
buffer.writeDouble(currSpeed.get())
|
||||
buffer.writeFloat(stamina.get())
|
||||
}
|
||||
|
||||
override fun decode(buffer: FriendlyByteBuf) {
|
||||
super.decode(buffer)
|
||||
currSpeed.set(buffer.readDouble(), forced = true)
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
super.reset()
|
||||
currSpeed.set(0.0, forced = true)
|
||||
currMouseXForce.set(0.0, forced = true)
|
||||
currMouseYForce.set(0.0, forced = true)
|
||||
}
|
||||
|
||||
override fun copy() = JetAirState().also {
|
||||
it.currSpeed.set(currSpeed.get(), forced = true)
|
||||
it.stamina.set(stamina.get(), forced = true)
|
||||
it.rideVelocity.set(rideVelocity.get(), forced = true)
|
||||
// it.currMouseXForce.set(currMouseXForce.get(), forced = true)
|
||||
// it.currMouseYForce.set(currMouseYForce.get(), forced = true)
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
if (previous !is JetAirState) return false
|
||||
if (previous.currSpeed != currSpeed) return true
|
||||
return super.shouldSync(previous)
|
||||
}
|
||||
}
|
||||
+261
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* 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.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.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import 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 SwimDashBehaviour : RidingBehaviour<SwimDashSettings, SwimDashState> {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("swim/dash")
|
||||
const val DASH_TICKS: Int = 60
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
override val style = RidingStyle.LIQUID
|
||||
|
||||
val poseProvider = PoseProvider<SwimDashSettings, SwimDashState>(PoseType.FLOAT)
|
||||
.with(PoseOption(PoseType.SWIM) { _, _, entity -> entity.isSwimming && entity.entityData.get(PokemonEntity.MOVING) })
|
||||
|
||||
override fun isActive(settings: SwimDashSettings, state: SwimDashState, 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) {
|
||||
return@any true
|
||||
}
|
||||
val blockState = vehicle.level().getBlockState(it)
|
||||
return@any !blockState.fluidState.isEmpty
|
||||
}
|
||||
}
|
||||
|
||||
override fun pose(settings: SwimDashSettings, state: SwimDashState, vehicle: PokemonEntity): PoseType {
|
||||
return poseProvider.select(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun speed(
|
||||
settings: SwimDashSettings,
|
||||
state: SwimDashState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
if(state.dashing.get()) {
|
||||
state.ticks.set(state.ticks.get() + 1)
|
||||
if(state.ticks.get() >= DASH_TICKS) {
|
||||
state.dashing.set(false)
|
||||
}
|
||||
|
||||
return 0.0F
|
||||
}
|
||||
|
||||
state.dashing.set(true)
|
||||
return settings.dashSpeed
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
settings: SwimDashSettings,
|
||||
state: SwimDashState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Vec2 {
|
||||
return Vec2(driver.xRot * 0.5f, driver.yRot)
|
||||
}
|
||||
|
||||
override fun velocity(
|
||||
settings: SwimDashSettings,
|
||||
state: SwimDashState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
val f = driver.xxa * 0.05f
|
||||
var g = driver.zza * 0.6f
|
||||
if (g <= 0.0f) {
|
||||
g *= 0.25f
|
||||
}
|
||||
|
||||
return Vec3(f.toDouble(), 0.0, g.toDouble())
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: SwimDashSettings,
|
||||
state: SwimDashState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: SwimDashSettings,
|
||||
state: SwimDashState,
|
||||
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, mouseX * invertPitch, mouseY * invertRoll)
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
settings: SwimDashSettings,
|
||||
state: SwimDashState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun setRideBar(
|
||||
settings: SwimDashSettings,
|
||||
state: SwimDashState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return 0.0f
|
||||
}
|
||||
|
||||
override fun jumpForce(
|
||||
settings: SwimDashSettings,
|
||||
state: SwimDashState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
jumpStrength: Int
|
||||
): Vec3 {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun gravity(
|
||||
settings: SwimDashSettings,
|
||||
state: SwimDashState,
|
||||
vehicle: PokemonEntity,
|
||||
regularGravity: Double
|
||||
): Double {
|
||||
return regularGravity
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(
|
||||
settings: SwimDashSettings,
|
||||
state: SwimDashState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return 1.0f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(settings: SwimDashSettings, state: SwimDashState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun useRidingAltPose(
|
||||
settings: SwimDashSettings,
|
||||
state: SwimDashState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun inertia(settings: SwimDashSettings, state: SwimDashState, vehicle: PokemonEntity): Double {
|
||||
return 0.5
|
||||
}
|
||||
|
||||
override fun shouldRoll(settings: SwimDashSettings, state: SwimDashState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun turnOffOnGround(settings: SwimDashSettings, state: SwimDashState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun dismountOnShift(settings: SwimDashSettings, state: SwimDashState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePokemonHead(
|
||||
settings: SwimDashSettings,
|
||||
state: SwimDashState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePlayerHead(
|
||||
settings: SwimDashSettings,
|
||||
state: SwimDashState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: SwimDashSettings) = SwimDashState()
|
||||
}
|
||||
|
||||
class SwimDashSettings : RidingBehaviourSettings {
|
||||
override val key = SwimDashBehaviour.KEY
|
||||
|
||||
var dashSpeed = 1F
|
||||
private set
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeFloat(dashSpeed)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
dashSpeed = buffer.readFloat()
|
||||
}
|
||||
}
|
||||
|
||||
class SwimDashState : RidingBehaviourState() {
|
||||
var dashing = ridingState(false, Side.BOTH)
|
||||
var ticks = ridingState(0, Side.BOTH)
|
||||
|
||||
override fun reset() {
|
||||
super.reset()
|
||||
dashing.set(false, forced = true)
|
||||
ticks.set(0, forced = true)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "SwimDashState(dashing=${dashing.get()}, ticks=${ticks.get()})"
|
||||
}
|
||||
|
||||
override fun copy() = SwimDashState().also {
|
||||
it.rideVelocity.set(rideVelocity.get(), forced = true)
|
||||
it.stamina.set(stamina.get(), forced = true)
|
||||
it.dashing.set(dashing.get(), forced = true)
|
||||
it.ticks.set(ticks. get(), forced = true)
|
||||
}
|
||||
}
|
||||
+349
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
* 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.*
|
||||
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.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.absoluteValue
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
class VehicleLandBehaviour : RidingBehaviour<VehicleLandSettings, VehicleLandState> {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("land/vehicle")
|
||||
|
||||
val MAX_TOP_SPEED = 1.0 // 20 bl/s
|
||||
val MIN_TOP_SPEED = 0.35 // 7 bl/s
|
||||
//val MIN_SPEED = 0.25 // 5 bl/s
|
||||
|
||||
//Accel will lie between 1.0 second and 5.0 seconds
|
||||
val MAX_ACCEL = (MAX_TOP_SPEED) / (20*3) //3.0 second to max speed
|
||||
val MIN_ACCEL = (MAX_TOP_SPEED) / (20*8) // 8 seconds to max speed
|
||||
|
||||
//Can rotate 90 degrees
|
||||
val MAX_HANDLING = 90.0
|
||||
val MIN_HANDLING = 30.0
|
||||
|
||||
|
||||
val MAX_YAW_HANDLING = 16.0
|
||||
val MIN_YAW_HANDLING = 8.0
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
override val style = RidingStyle.LAND
|
||||
|
||||
val poseProvider = PoseProvider<VehicleLandSettings, VehicleLandState>(PoseType.STAND)
|
||||
.with(PoseOption(PoseType.WALK) { _, _, entity -> entity.entityData.get(PokemonEntity.MOVING) })
|
||||
|
||||
override fun isActive(settings: VehicleLandSettings, state: VehicleLandState, vehicle: PokemonEntity): Boolean {
|
||||
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
|
||||
//Need to check other fluids
|
||||
if (vehicle.isInWater || vehicle.isUnderWater) {
|
||||
return@any false
|
||||
}
|
||||
//This might not actually work, depending on what the yPos actually is. yPos of the middle of the entity? the feet?
|
||||
if (it.y.toDouble() == (vehicle.position().y)) {
|
||||
val blockState = vehicle.level().getBlockState(it.below())
|
||||
return@any !(!blockState.isAir && blockState.fluidState.isEmpty)
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun pose(settings: VehicleLandSettings, state: VehicleLandState, vehicle: PokemonEntity): PoseType {
|
||||
return poseProvider.select(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun speed(
|
||||
settings: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
val topSpeed = vehicle.getRideStat(RidingStat.SPEED, RidingStyle.AIR, MIN_TOP_SPEED, MAX_TOP_SPEED)
|
||||
val accel = vehicle.getRideStat(RidingStat.ACCELERATION, RidingStyle.AIR, MIN_ACCEL, MAX_ACCEL)
|
||||
|
||||
//speed up and slow down based on input
|
||||
if (driver.zza > 0.0 && state.currSpeed.get() < topSpeed) {
|
||||
state.currSpeed.set(min(state.currSpeed.get() + accel , topSpeed))
|
||||
} else if (driver.zza < 0.0 && state.currSpeed.get() > 0.0) {
|
||||
//Decelerate is now always a constant half of max acceleration.
|
||||
state.currSpeed.set(max(state.currSpeed.get() - (MAX_ACCEL / 2), 0.0))
|
||||
}
|
||||
|
||||
return state.currSpeed.get().toFloat()
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
settings: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Vec2 {
|
||||
val rotationDegrees = driver.xxa * vehicle.runtime.resolveFloat(settings.rotationSpeed)
|
||||
val rotation = Vec2(driver.xRot, vehicle.yRot - rotationDegrees)
|
||||
state.deltaRotation.set(Vec2(rotation.x - driver.rotationVector.x, rotation.y - vehicle.rotationVector.y))
|
||||
return rotation
|
||||
}
|
||||
|
||||
override fun velocity(
|
||||
settings: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
val runtime = vehicle.runtime
|
||||
val driveFactor = runtime.resolveFloat(settings.driveFactor)
|
||||
var g = driver.zza * driveFactor
|
||||
if (g <= 0.0f) {
|
||||
g *= runtime.resolveFloat(settings.reverseDriveFactor)
|
||||
}
|
||||
if (driver.xxa != 0F && g.absoluteValue < runtime.resolveFloat(settings.minimumSpeedToTurn)) {
|
||||
driver.xxa = 0F
|
||||
}
|
||||
val velocity = Vec3(0.0 /* Maybe do drifting here later */, 0.0, g.toDouble() * state.currSpeed.get())
|
||||
|
||||
return velocity
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
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: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return vehicle.runtime.resolveBoolean(settings.canJump)
|
||||
}
|
||||
|
||||
override fun setRideBar(
|
||||
settings: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return 0.0f
|
||||
}
|
||||
|
||||
override fun jumpForce(
|
||||
settings: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
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: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
vehicle: PokemonEntity,
|
||||
regularGravity: Double
|
||||
): Double {
|
||||
return regularGravity
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(
|
||||
settings: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return 1.0f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(
|
||||
settings: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun useRidingAltPose(
|
||||
settings: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun inertia(settings: VehicleLandSettings, state: VehicleLandState, vehicle: PokemonEntity): Double {
|
||||
return 0.5
|
||||
}
|
||||
|
||||
override fun shouldRoll(settings: VehicleLandSettings, state: VehicleLandState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun turnOffOnGround(
|
||||
settings: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun dismountOnShift(
|
||||
settings: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePokemonHead(
|
||||
settings: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePlayerHead(
|
||||
settings: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: VehicleLandSettings) = VehicleLandState()
|
||||
}
|
||||
|
||||
class VehicleLandSettings : RidingBehaviourSettings {
|
||||
override val key = VehicleLandBehaviour.KEY
|
||||
|
||||
var canJump = "true".asExpression()
|
||||
private set
|
||||
|
||||
var jumpVector = listOf("0".asExpression(), "0.3".asExpression(), "0".asExpression())
|
||||
private set
|
||||
|
||||
var speed = "0.3".asExpression()
|
||||
private set
|
||||
|
||||
var driveFactor = "1.0".asExpression()
|
||||
private set
|
||||
|
||||
var reverseDriveFactor = "0.25".asExpression()
|
||||
private set
|
||||
|
||||
var minimumSpeedToTurn = "0.1".asExpression()
|
||||
private set
|
||||
|
||||
var rotationSpeed = "45/20".asExpression()
|
||||
private set
|
||||
|
||||
var lookYawLimit = "101".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(minimumSpeedToTurn)
|
||||
buffer.writeExpression(rotationSpeed)
|
||||
buffer.writeExpression(lookYawLimit)
|
||||
}
|
||||
|
||||
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()
|
||||
minimumSpeedToTurn = buffer.readExpression()
|
||||
rotationSpeed = buffer.readExpression()
|
||||
lookYawLimit = buffer.readExpression()
|
||||
}
|
||||
}
|
||||
|
||||
class VehicleLandState : RidingBehaviourState() {
|
||||
var currSpeed = ridingState(0.0, Side.BOTH)
|
||||
var deltaRotation = ridingState(Vec2.ZERO, Side.BOTH)
|
||||
|
||||
override fun reset() {
|
||||
super.reset()
|
||||
currSpeed.set(0.0, forced = true)
|
||||
deltaRotation.set(Vec2.ZERO, forced = true)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "VehicleLandState(currSpeed=${currSpeed.get()}, deltaRotation=${deltaRotation.get()})"
|
||||
}
|
||||
|
||||
override fun copy() = VehicleLandState().also {
|
||||
it.rideVelocity.set(rideVelocity.get(), forced = true)
|
||||
it.stamina.set(stamina.get(), forced = true)
|
||||
it.currSpeed.set(currSpeed.get(), forced = true)
|
||||
it.deltaRotation.set(deltaRotation.get(), forced = true)
|
||||
}
|
||||
}
|
||||
+426
@@ -0,0 +1,426 @@
|
||||
/*
|
||||
* 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.composite
|
||||
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.strategies.CompositeRidingStrategies
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.adapters.RidingBehaviourSettingsAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class CompositeBehaviour : RidingBehaviour<CompositeSettings, CompositeState> {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("composite")
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
override val style = RidingStyle.COMPOSITE
|
||||
|
||||
override fun createDefaultState(settings: CompositeSettings): CompositeState {
|
||||
val defaultBehaviour = RidingBehaviours.get(settings.defaultBehaviour.key)
|
||||
val defaultState = defaultBehaviour.createDefaultState(settings.defaultBehaviour)
|
||||
val alternateBehaviour = RidingBehaviours.get(settings.alternateBehaviour.key)
|
||||
val alternativeState = alternateBehaviour.createDefaultState(settings.alternateBehaviour)
|
||||
return CompositeState(
|
||||
defaultBehaviour = settings.defaultBehaviour.key,
|
||||
defaultBehaviourState = defaultState,
|
||||
alternateBehaviourState = alternativeState
|
||||
)
|
||||
}
|
||||
|
||||
fun <T> chooseBehaviour(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
action: (behaviour: RidingBehaviour<RidingBehaviourSettings, RidingBehaviourState>, settings: RidingBehaviourSettings, state: RidingBehaviourState) -> T
|
||||
): T {
|
||||
val defaultBehaviour = RidingBehaviours.get(settings.defaultBehaviour.key)
|
||||
val alternativeBehaviour = RidingBehaviours.get(settings.alternateBehaviour.key)
|
||||
return when (state.activeController.get()) {
|
||||
settings.defaultBehaviour.key -> action(
|
||||
defaultBehaviour,
|
||||
settings.defaultBehaviour,
|
||||
state.defaultBehaviourState
|
||||
)
|
||||
settings.alternateBehaviour.key -> action(
|
||||
alternativeBehaviour,
|
||||
settings.alternateBehaviour,
|
||||
state.alternateBehaviourState
|
||||
)
|
||||
|
||||
else -> error("Invalid controller: ${state.activeController.get()}")
|
||||
}
|
||||
}
|
||||
|
||||
override fun tick(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
) {
|
||||
val strategy = CompositeRidingStrategies.get(settings.transitionStrategy)
|
||||
strategy.tick(settings, state, state.defaultBehaviourState, state.alternateBehaviourState, vehicle, driver, input)
|
||||
}
|
||||
|
||||
override fun clampPassengerRotation(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
entity: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
) {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.clampPassengerRotation(behaviourSettings, behaviourState, entity, driver)
|
||||
}
|
||||
}
|
||||
|
||||
override fun updatePassengerRotation(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
entity: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
) {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.updatePassengerRotation(behaviourSettings, behaviourState, entity, driver)
|
||||
}
|
||||
}
|
||||
|
||||
override fun shouldRotatePlayerHead(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.shouldRotatePlayerHead(behaviourSettings, behaviourState, vehicle)
|
||||
}
|
||||
}
|
||||
|
||||
override fun shouldRotatePokemonHead(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.shouldRotatePokemonHead(behaviourSettings, behaviourState, vehicle)
|
||||
}
|
||||
}
|
||||
|
||||
override fun dismountOnShift(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.dismountOnShift(behaviourSettings, behaviourState, vehicle)
|
||||
}
|
||||
}
|
||||
|
||||
override fun turnOffOnGround(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.turnOffOnGround(behaviourSettings, behaviourState, vehicle)
|
||||
}
|
||||
}
|
||||
|
||||
override fun shouldRoll(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.shouldRoll(behaviourSettings, behaviourState, vehicle)
|
||||
}
|
||||
}
|
||||
|
||||
override fun inertia(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity
|
||||
): Double {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.inertia(behaviourSettings, behaviourState, vehicle)
|
||||
}
|
||||
}
|
||||
|
||||
override fun useRidingAltPose(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.useRidingAltPose(behaviourSettings, behaviourState, vehicle, driver)
|
||||
}
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.useAngVelSmoothing(behaviourSettings, behaviourState, vehicle)
|
||||
}
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.rideFovMultiplier(behaviourSettings, behaviourState, vehicle, driver)
|
||||
}
|
||||
}
|
||||
|
||||
override fun gravity(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity,
|
||||
regularGravity: Double
|
||||
): Double {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.gravity(behaviourSettings, behaviourState, vehicle, regularGravity)
|
||||
}
|
||||
}
|
||||
|
||||
override fun jumpForce(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
jumpStrength: Int
|
||||
): Vec3 {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.jumpForce(behaviourSettings, behaviourState, vehicle, driver, jumpStrength)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setRideBar(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.setRideBar(behaviourSettings, behaviourState, vehicle, driver)
|
||||
}
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.canJump(behaviourSettings, behaviourState, vehicle, driver)
|
||||
}
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
mouseX: Double,
|
||||
mouseYSmoother: SmoothDouble,
|
||||
mouseXSmoother: SmoothDouble,
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.rotationOnMouseXY(
|
||||
behaviourSettings,
|
||||
behaviourState,
|
||||
vehicle,
|
||||
driver,
|
||||
mouseY,
|
||||
mouseX,
|
||||
mouseYSmoother,
|
||||
mouseXSmoother,
|
||||
sensitivity,
|
||||
deltaTime
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.angRollVel(behaviourSettings, behaviourState, vehicle, driver, deltaTime)
|
||||
}
|
||||
}
|
||||
|
||||
override fun velocity(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.velocity(behaviourSettings, behaviourState, vehicle, driver, input)
|
||||
}
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Vec2 {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.rotation(behaviourSettings, behaviourState, vehicle, driver)
|
||||
}
|
||||
}
|
||||
|
||||
override fun speed(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.speed(behaviourSettings, behaviourState, vehicle, driver)
|
||||
}
|
||||
}
|
||||
|
||||
override fun pose(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity
|
||||
): PoseType {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.pose(behaviourSettings, behaviourState, vehicle)
|
||||
}
|
||||
}
|
||||
|
||||
override fun isActive(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.isActive(behaviourSettings, behaviourState, vehicle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open class CompositeSettings : RidingBehaviourSettings {
|
||||
lateinit var transitionStrategy: ResourceLocation
|
||||
private set
|
||||
lateinit var defaultBehaviour: RidingBehaviourSettings
|
||||
private set
|
||||
lateinit var alternateBehaviour: RidingBehaviourSettings
|
||||
private set
|
||||
|
||||
override val key: ResourceLocation = CompositeBehaviour.KEY
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeResourceLocation(transitionStrategy)
|
||||
defaultBehaviour.encode(buffer)
|
||||
alternateBehaviour.encode(buffer)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
transitionStrategy = buffer.readResourceLocation()
|
||||
val defaultBehaviourKey = buffer.readResourceLocation()
|
||||
defaultBehaviour = RidingBehaviourSettingsAdapter.types[defaultBehaviourKey]?.getConstructor()?.newInstance()
|
||||
?: error("Unknown controller key: $key")
|
||||
defaultBehaviour.decode(buffer)
|
||||
val alternativeBehaviourKey = buffer.readResourceLocation()
|
||||
alternateBehaviour =
|
||||
RidingBehaviourSettingsAdapter.types[alternativeBehaviourKey]?.getConstructor()?.newInstance()
|
||||
?: error("Unknown controller key: $key")
|
||||
alternateBehaviour.decode(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
class CompositeState(
|
||||
private val defaultBehaviour: ResourceLocation,
|
||||
val defaultBehaviourState: RidingBehaviourState,
|
||||
val alternateBehaviourState: RidingBehaviourState
|
||||
) : RidingBehaviourState() {
|
||||
var activeController = ridingState(defaultBehaviour, Side.CLIENT)
|
||||
var lastTransition = ridingState(-100L, Side.BOTH)
|
||||
|
||||
override val rideVelocity: SidedRidingState<Vec3>
|
||||
get() = when (activeController.get()) {
|
||||
defaultBehaviour -> defaultBehaviourState.rideVelocity
|
||||
else -> alternateBehaviourState.rideVelocity
|
||||
}
|
||||
|
||||
override val stamina: SidedRidingState<Float>
|
||||
get() = when (activeController.get()) {
|
||||
defaultBehaviour -> defaultBehaviourState.stamina
|
||||
else -> alternateBehaviourState.stamina
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
super.reset()
|
||||
activeController.set(defaultBehaviour, forced = true)
|
||||
lastTransition.set(-100L, forced = true)
|
||||
defaultBehaviourState.reset()
|
||||
alternateBehaviourState.reset()
|
||||
}
|
||||
|
||||
override fun copy(): CompositeState {
|
||||
val state =
|
||||
CompositeState(defaultBehaviour, defaultBehaviourState.copy(), alternateBehaviourState.copy())
|
||||
state.activeController.set(activeController.get(), forced = true)
|
||||
state.lastTransition.set(lastTransition.get(), forced = true)
|
||||
state.rideVelocity.set(rideVelocity.get(), forced = true)
|
||||
state.stamina.set(stamina.get(), forced = true)
|
||||
return state
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
if (previous !is CompositeState) return false
|
||||
if (previous.activeController.get() != activeController.get()) return true
|
||||
if (defaultBehaviourState.shouldSync(previous.defaultBehaviourState)) return true
|
||||
if (alternateBehaviourState.shouldSync(previous.alternateBehaviourState)) return true
|
||||
return super.shouldSync(previous)
|
||||
}
|
||||
|
||||
override fun encode(buffer: FriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
buffer.writeResourceLocation(activeController.get())
|
||||
defaultBehaviourState.encode(buffer)
|
||||
alternateBehaviourState.encode(buffer)
|
||||
}
|
||||
|
||||
override fun decode(buffer: FriendlyByteBuf) {
|
||||
super.decode(buffer)
|
||||
activeController.set(buffer.readResourceLocation(), forced = true)
|
||||
defaultBehaviourState.decode(buffer)
|
||||
alternateBehaviourState.decode(buffer)
|
||||
}
|
||||
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.composite.strategies
|
||||
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeSettings
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
object CompositeRidingStrategies {
|
||||
val strategies = mutableMapOf<ResourceLocation, CompositeRidingStrategy<out CompositeSettings>>()
|
||||
|
||||
init {
|
||||
register(RunStrategy.key, RunStrategy)
|
||||
register(JumpStrategy.key, JumpStrategy)
|
||||
register(FallStrategy.key, FallStrategy)
|
||||
}
|
||||
|
||||
fun register(key: ResourceLocation, strategy: CompositeRidingStrategy<out CompositeSettings>) {
|
||||
if (strategies.contains(key)) error("Strategy already registered to key $key")
|
||||
strategies[key] = strategy
|
||||
}
|
||||
|
||||
fun get(key: ResourceLocation): CompositeRidingStrategy<CompositeSettings> {
|
||||
if (!strategies.contains(key)) error("Strategy not registered to key $key")
|
||||
return strategies[key]!! as CompositeRidingStrategy<CompositeSettings>
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types.composite.strategies
|
||||
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviours
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeState
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonBehaviourFlag
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
interface CompositeRidingStrategy<T : CompositeSettings> {
|
||||
val key: ResourceLocation
|
||||
|
||||
fun tick(
|
||||
settings: T,
|
||||
state: CompositeState,
|
||||
defaultState: RidingBehaviourState,
|
||||
alternateState: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
)
|
||||
|
||||
fun transition(
|
||||
vehicle: PokemonEntity,
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
fromState: RidingBehaviourState,
|
||||
toState: RidingBehaviourState,
|
||||
toSettings: RidingBehaviourSettings
|
||||
) {
|
||||
toState.stamina.set(fromState.stamina.get())
|
||||
toState.rideVelocity.set(fromState.rideVelocity.get())
|
||||
fromState.reset()
|
||||
state.activeController.set(toSettings.key)
|
||||
state.lastTransition.set(vehicle.level().gameTime)
|
||||
val behaviour = RidingBehaviours.get(toSettings.key)
|
||||
vehicle.setBehaviourFlag(PokemonBehaviourFlag.FLYING, behaviour.style == RidingStyle.AIR)
|
||||
}
|
||||
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.composite.strategies
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviours
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeState
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonBehaviourFlag
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
object FallStrategy : CompositeRidingStrategy<FallCompositeSettings> {
|
||||
|
||||
override val key = cobblemonResource("strategy/fall")
|
||||
|
||||
override fun tick(
|
||||
settings: FallCompositeSettings,
|
||||
state: CompositeState,
|
||||
defaultState: RidingBehaviourState,
|
||||
alternateState: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
) {
|
||||
if (shouldTransitionToDefault(state, settings.defaultBehaviour, vehicle)) {
|
||||
transition(vehicle, settings, state, alternateState, defaultState, settings.defaultBehaviour)
|
||||
} else if (shouldTransitionToAlternate(settings, state, settings.alternateBehaviour, vehicle, driver)) {
|
||||
transition(vehicle, settings, state, defaultState, alternateState, settings.alternateBehaviour)
|
||||
}
|
||||
}
|
||||
|
||||
private fun shouldTransitionToDefault(
|
||||
state: CompositeState,
|
||||
defaultSettings: RidingBehaviourSettings,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
if (state.activeController.get() == defaultSettings.key) return false
|
||||
if (state.lastTransition.get() + 20 >= vehicle.level().gameTime) return false
|
||||
if (!vehicle.onGround()) return false
|
||||
val defaultBehaviour = RidingBehaviours.get(defaultSettings.key)
|
||||
return defaultBehaviour.isActive(defaultSettings, state.defaultBehaviourState, vehicle)
|
||||
}
|
||||
|
||||
private fun shouldTransitionToAlternate(
|
||||
settings: FallCompositeSettings,
|
||||
state: CompositeState,
|
||||
alternateSettings: RidingBehaviourSettings,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
if (state.activeController.get() == alternateSettings.key) return false
|
||||
if (!driver.jumping) return false
|
||||
if (state.lastTransition.get() + 20 >= vehicle.level().gameTime) return false
|
||||
|
||||
val runtime = vehicle.runtime
|
||||
val minFallingSpeed = runtime.resolveFloat(settings.minimumFallSpeed)
|
||||
val minForwardSpeed = runtime.resolveFloat(settings.minimumForwardSpeed)
|
||||
if (vehicle.deltaMovement.y < -minFallingSpeed) return false
|
||||
if (vehicle.deltaMovement.horizontalDistance() < minForwardSpeed) return false
|
||||
val alternativeBehaviour = RidingBehaviours.get(alternateSettings.key)
|
||||
return alternativeBehaviour.isActive(alternateSettings, state.alternateBehaviourState, vehicle)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class FallCompositeSettings : CompositeSettings() {
|
||||
override val key = FallStrategy.key
|
||||
|
||||
var minimumForwardSpeed: Expression = "0.0".asExpression()
|
||||
private set
|
||||
|
||||
var minimumFallSpeed: Expression = "0.5".asExpression()
|
||||
private set
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
buffer.writeExpression(minimumForwardSpeed)
|
||||
buffer.writeExpression(minimumFallSpeed)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
super.decode(buffer)
|
||||
minimumForwardSpeed = buffer.readExpression()
|
||||
minimumFallSpeed = buffer.readExpression()
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types.composite.strategies
|
||||
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviours
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeState
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonBehaviourFlag
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
object JumpStrategy : CompositeRidingStrategy<CompositeSettings> {
|
||||
|
||||
override val key = cobblemonResource("strategy/jump")
|
||||
|
||||
override fun tick(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
defaultState: RidingBehaviourState,
|
||||
alternateState: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
) {
|
||||
if (shouldTransitionToDefault(state, settings.defaultBehaviour, vehicle)) {
|
||||
transition(vehicle, settings, state, alternateState, defaultState, settings.defaultBehaviour)
|
||||
} else if (shouldTransitionToAlternative(state, settings.alternateBehaviour, vehicle, driver)) {
|
||||
transition(vehicle, settings, state, defaultState, alternateState, settings.alternateBehaviour)
|
||||
}
|
||||
}
|
||||
|
||||
private fun shouldTransitionToDefault(
|
||||
state: CompositeState,
|
||||
defaultSettings: RidingBehaviourSettings,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
if (state.activeController.get() == defaultSettings.key) return false
|
||||
if (!vehicle.onGround()) return false
|
||||
if (state.lastTransition.get() + 20 >= vehicle.level().gameTime) return false
|
||||
val defaultBehaviour = RidingBehaviours.get(defaultSettings.key)
|
||||
return defaultBehaviour.isActive(defaultSettings, state.defaultBehaviourState, vehicle)
|
||||
}
|
||||
|
||||
private fun shouldTransitionToAlternative(
|
||||
state: CompositeState,
|
||||
alternativeSettings: RidingBehaviourSettings,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
if (state.activeController.get() == alternativeSettings.key) return false
|
||||
if (!driver.jumping) return false
|
||||
if (state.lastTransition.get() + 20 >= vehicle.level().gameTime) return false
|
||||
val alternativeBehaviour = RidingBehaviours.get(alternativeSettings.key)
|
||||
return alternativeBehaviour.isActive(alternativeSettings, state.alternateBehaviourState, vehicle)
|
||||
}
|
||||
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.composite.strategies
|
||||
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviours
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeState
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
object RunStrategy : CompositeRidingStrategy<CompositeSettings> {
|
||||
|
||||
override val key = cobblemonResource("strategy/run")
|
||||
|
||||
override fun tick(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
defaultState: RidingBehaviourState,
|
||||
alternateState: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
) {
|
||||
if (shouldTransitionToDefault(state, settings.defaultBehaviour, vehicle, driver)) {
|
||||
transition(vehicle, settings, state, alternateState, defaultState, settings.defaultBehaviour)
|
||||
}
|
||||
else if (shouldTransitionToAlternate(state, settings.alternateBehaviour, vehicle, driver, input)) {
|
||||
transition(vehicle, settings, state, defaultState, alternateState, settings.alternateBehaviour)
|
||||
}
|
||||
}
|
||||
|
||||
private fun shouldTransitionToDefault(
|
||||
state: CompositeState,
|
||||
defaultSettings: RidingBehaviourSettings,
|
||||
entity: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
if (state.activeController.get() == defaultSettings.key) return false
|
||||
if (!entity.onGround()) return false
|
||||
if (state.lastTransition.get() + 20 >= entity.level().gameTime) return false
|
||||
val defaultBehaviour = RidingBehaviours.get(defaultSettings.key)
|
||||
val ret = defaultBehaviour.isActive(defaultSettings, state.defaultBehaviourState, entity)
|
||||
return ret
|
||||
}
|
||||
|
||||
private fun shouldTransitionToAlternate(
|
||||
state: CompositeState,
|
||||
alternativeSettings: RidingBehaviourSettings,
|
||||
entity: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Boolean {
|
||||
if (state.activeController.get() == alternativeSettings.key) return false
|
||||
if (state.lastTransition.get() + 20 >= entity.level().gameTime) return false
|
||||
val alternativeBehaviour = RidingBehaviours.get(alternativeSettings.key)
|
||||
return driver.isSprinting && input.z > 0.5 && alternativeBehaviour.isActive(alternativeSettings, state.alternateBehaviourState, entity)
|
||||
}
|
||||
|
||||
}
|
||||
-106
@@ -1,106 +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.controller
|
||||
|
||||
import com.cobblemon.mod.common.api.net.Decodable
|
||||
import com.cobblemon.mod.common.api.net.Encodable
|
||||
import com.cobblemon.mod.common.api.riding.RidingState
|
||||
import com.cobblemon.mod.common.api.riding.controller.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.writeIdentifier
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
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
|
||||
|
||||
/**
|
||||
* A riding controller is the internal control mechanism for determining how a pokemon
|
||||
* handles during riding. Controllers come into play once the pokemon being ridden has
|
||||
* established the path it's capable of. For instance, a land based controller could have
|
||||
* the pokemon experience drifting after movement, or generic movement. It's up to the
|
||||
* controller to decide how the pokemon behaves.
|
||||
*
|
||||
* @since 1.7.0
|
||||
*/
|
||||
interface RideController : Encodable, Decodable {
|
||||
|
||||
/** A reference key used to denote the individual controller */
|
||||
val key: ResourceLocation
|
||||
|
||||
/**
|
||||
* Specifies the provider of poses which helps control updating the current pose on the pokemon
|
||||
* being ridden. Controllers are expected to define the pose types responsible for posing the entity
|
||||
* through all expected conditions. It should define a default pose, that will be used in all cases
|
||||
* where a pose does not meet the current conditions for the current mount. Typically, this will be
|
||||
* an idle pose.
|
||||
*/
|
||||
val poseProvider: PoseProvider
|
||||
|
||||
/**
|
||||
* Represents a condition that must be met for this controller to be active. For instance, this can be used
|
||||
* to evaluate things such as current velocity or block state of the entity.
|
||||
*/
|
||||
val condition: (PokemonEntity) -> Boolean
|
||||
|
||||
/**
|
||||
* Specifies the pose the ridden mount should be positioned in. This queries the pose provider using
|
||||
* the entity as a means of determining the conditions the entity is currently under. This allows for
|
||||
* dynamic pose updates.
|
||||
*/
|
||||
fun pose(entity: PokemonEntity) : PoseType {
|
||||
return this.poseProvider.select(entity)
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the current speed of the mount.
|
||||
*/
|
||||
fun speed(entity: PokemonEntity, driver: Player) : Float
|
||||
|
||||
/**
|
||||
* Sets the rotation of the mount. This is typically based on the controlling driver and is manipulated as
|
||||
* necessary.
|
||||
*/
|
||||
fun rotation(entity: PokemonEntity, driver: LivingEntity) : Vec2
|
||||
|
||||
fun clampPassengerRotation(entity: PokemonEntity, driver: LivingEntity) {}
|
||||
|
||||
fun updatePassengerRotation(entity: PokemonEntity, driver: LivingEntity) {
|
||||
driver.yRot = entity.yRot
|
||||
}
|
||||
|
||||
/**
|
||||
* Manipulates the movement input as necessary by the controller. This is primarily used to apply limits
|
||||
* to the types of movements. For instance, we can apply limits to any sort of sideways movement input, so that
|
||||
* it would otherwise be slower than normal forward movement.
|
||||
*/
|
||||
fun velocity(entity: PokemonEntity, driver: Player, input: Vec3) : Vec3
|
||||
|
||||
fun canJump(entity: PokemonEntity, driver: Player) : Boolean
|
||||
fun jumpForce(entity: PokemonEntity, driver: Player, jumpStrength: Int) : Vec3
|
||||
|
||||
fun gravity(entity: PokemonEntity, regularGravity: Double) : Double? = null
|
||||
|
||||
fun shouldRoll(entity: PokemonEntity): Boolean = false
|
||||
|
||||
fun getRuntime(entity: PokemonEntity) = entity.riding.runtime
|
||||
|
||||
fun shouldRotatePokemonHead(): Boolean = false
|
||||
fun shouldRotatePlayerHead(): Boolean = false
|
||||
|
||||
fun <T : RidingState> getState(entity: PokemonEntity, constructor: (PokemonEntity) -> T): T {
|
||||
return entity.riding.getState(key, constructor)
|
||||
}
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeIdentifier(this.key)
|
||||
}
|
||||
}
|
||||
-15
@@ -1,15 +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.controller.posing
|
||||
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import java.util.function.Predicate
|
||||
|
||||
data class PoseOption(val pose: PoseType, val condition: Predicate<PokemonEntity>)
|
||||
-31
@@ -1,31 +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.controller.posing
|
||||
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
|
||||
class PoseProvider(private val fallback: PoseType) {
|
||||
|
||||
private val options = mutableListOf<PoseOption>()
|
||||
|
||||
fun select(entity: PokemonEntity) : PoseType {
|
||||
return this.options.stream()
|
||||
.filter { it.condition.test(entity) }
|
||||
.map { it.pose }
|
||||
.findFirst()
|
||||
.orElse(this.fallback)
|
||||
}
|
||||
|
||||
fun with(option: PoseOption) : PoseProvider {
|
||||
this.options.add(option)
|
||||
return this
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.posing
|
||||
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
|
||||
data class PoseOption<Settings : RidingBehaviourSettings, State : RidingBehaviourState>(
|
||||
val pose: PoseType,
|
||||
val condition: (Settings, State, PokemonEntity) -> Boolean
|
||||
)
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.posing
|
||||
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
|
||||
class PoseProvider<Settings : RidingBehaviourSettings, State : RidingBehaviourState>(private val fallback: PoseType) {
|
||||
|
||||
private val options = mutableListOf<PoseOption<Settings, State>>()
|
||||
|
||||
fun select(settings: Settings, state: State, entity: PokemonEntity) : PoseType {
|
||||
return this.options.stream()
|
||||
.filter { it.condition(settings, state, entity) }
|
||||
.map { it.pose }
|
||||
.findFirst()
|
||||
.orElse(this.fallback)
|
||||
}
|
||||
|
||||
fun with(option: PoseOption<Settings, State>) : PoseProvider<Settings, State> {
|
||||
this.options.add(option)
|
||||
return this
|
||||
}
|
||||
|
||||
}
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.util
|
||||
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import org.joml.Matrix3f
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
|
||||
/**
|
||||
* Class to handle the updating and containment of ride data used in animation.
|
||||
*
|
||||
* @author Jackowes
|
||||
* @since April 7th, 2025
|
||||
*/
|
||||
|
||||
|
||||
class RidingAnimationData
|
||||
{
|
||||
var prevOrientation: Matrix3f = Matrix3f()
|
||||
var prevRot: Vec3 = Vec3.ZERO
|
||||
var velocitySpring: Vec3Spring = Vec3Spring()
|
||||
var rotSpring: Vec3Spring = Vec3Spring()
|
||||
var rotDeltaSpring: Vec3Spring = Vec3Spring()
|
||||
var localVelocitySpring: Vec3Spring = Vec3Spring()
|
||||
|
||||
fun update(entity: PokemonEntity) {
|
||||
|
||||
var currRot = Vec3.ZERO
|
||||
var angDelta = Vec3.ZERO
|
||||
|
||||
// val stiffness = 90.0
|
||||
// val damping = 18.0
|
||||
val stiffness = 16.0
|
||||
val damping = 8.0
|
||||
|
||||
// Update velocity first
|
||||
val currentVelocity = Vec3(
|
||||
entity.x - entity.xOld,
|
||||
entity.y - entity.yOld,
|
||||
entity.z - entity.zOld
|
||||
)
|
||||
|
||||
val r = entity.ifRidingAvailableSupply(false) { behaviour, settings, state ->
|
||||
behaviour.shouldRoll(settings, state, entity);
|
||||
}
|
||||
if( r ) {
|
||||
//TODO: should this return?
|
||||
val controller = (entity.firstPassenger as? OrientationControllable)?.orientationController ?: return
|
||||
val orientation = controller.orientation ?: Matrix3f()
|
||||
|
||||
val currPitch = controller.pitch
|
||||
val currYaw = controller.yaw
|
||||
val currRoll = controller.roll
|
||||
currRot = Vec3(currPitch.toDouble(), currYaw.toDouble(), currRoll.toDouble())
|
||||
|
||||
val currOrientation = orientation
|
||||
|
||||
/******************************************************
|
||||
* Calculate current 3d euler rotation deltas in the
|
||||
* entities local space
|
||||
*****************************************************/
|
||||
|
||||
// Get current and previous orientation as quaternions
|
||||
val currQuat = Quaternionf().setFromUnnormalized(currOrientation)
|
||||
val prevQuat = Quaternionf().setFromUnnormalized(prevOrientation)
|
||||
|
||||
// Compute deltaQuat = current * inverse(previous)
|
||||
val deltaQuat = Quaternionf(currQuat).mul(Quaternionf(prevQuat).invert())
|
||||
|
||||
// Transform delta into the local space of the current orientation
|
||||
val localDeltaQuat = Quaternionf(currQuat).invert().mul(deltaQuat).mul(currQuat)
|
||||
|
||||
// Convert delta rotation to Euler angles
|
||||
val euler = Vector3f()
|
||||
localDeltaQuat.getEulerAnglesXYZ(euler) // Angles are in radians
|
||||
|
||||
// Convert to degrees
|
||||
val pitchDegrees = Math.toDegrees(euler.x.toDouble()) // Rotation around X axis = pitch
|
||||
val yawDegrees = Math.toDegrees(euler.y.toDouble()) // Rotation around Y axis = yaw
|
||||
val rollDegrees = Math.toDegrees(euler.z.toDouble()) // Rotation around Z axis = roll
|
||||
//Scale rotation delta by 20 to represent degree per second instead of tick
|
||||
angDelta = Vec3(pitchDegrees, yawDegrees, rollDegrees).scale(20.0)
|
||||
|
||||
/******************************************************
|
||||
* Calculate ride local velocity
|
||||
*****************************************************/
|
||||
val velocityWorldF = Vector3f(currentVelocity.x.toFloat(), currentVelocity.y.toFloat(), currentVelocity.z.toFloat())
|
||||
|
||||
val localVelocityF = Matrix3f(orientation).transpose().transform(velocityWorldF)
|
||||
val localVelocity = Vec3(localVelocityF.x.toDouble(), localVelocityF.y.toDouble(), localVelocityF.z.toDouble())
|
||||
|
||||
localVelocitySpring.update(localVelocity, stiffness, damping)
|
||||
|
||||
// Update previous values
|
||||
prevOrientation.set(currOrientation)
|
||||
} else {
|
||||
val currPitch = entity.xRot
|
||||
val currYaw = entity.yRot
|
||||
val currRoll = 0.0f
|
||||
currRot = Vec3(currPitch.toDouble(), currYaw.toDouble(), currRoll.toDouble())
|
||||
|
||||
//Scale rotation delta by 20 to represent degree per second instead of tick
|
||||
angDelta = Vec3(
|
||||
(currRot.x - prevRot.x),
|
||||
Mth.degreesDifference(currRot.y.toFloat(), prevRot.y.toFloat()).toDouble(),
|
||||
Mth.degreesDifference(currRot.z.toFloat(), prevRot.z.toFloat()).toDouble()
|
||||
).scale(20.0)
|
||||
|
||||
/******************************************************
|
||||
* Calculate ride local velocity
|
||||
*****************************************************/
|
||||
val yaw360 = -1.0 * (entity.yRot + 360.0f) % 360.0f
|
||||
val orientationNonRollable = Matrix3f().rotateY(yaw360.toRadians())
|
||||
|
||||
//Convert to Vector3f
|
||||
var velocityWorldF = Vector3f(currentVelocity.x.toFloat(), currentVelocity.y.toFloat(), currentVelocity.z.toFloat())
|
||||
|
||||
val localVelocityF = Matrix3f(orientationNonRollable).transpose().transform(velocityWorldF)
|
||||
val localVelocity = Vec3(localVelocityF.x.toDouble(), localVelocityF.y.toDouble(), localVelocityF.z.toDouble())
|
||||
|
||||
localVelocitySpring.update(localVelocity, stiffness, damping)
|
||||
}
|
||||
|
||||
/******************************************************
|
||||
* Update springs
|
||||
*****************************************************/
|
||||
rotDeltaSpring.update(angDelta, stiffness, damping)
|
||||
velocitySpring.update(currentVelocity, stiffness, damping)
|
||||
rotSpring.update(currRot, stiffness, damping)
|
||||
prevRot = currRot
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.util
|
||||
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* A spring-based smoother for Vec3 values, designed for applying damped spring motion to riding data.
|
||||
* Stores the previous and current values, along with a history buffer for lookback-based interpolation.
|
||||
*
|
||||
* @author Jackowes
|
||||
* @since March 27th, 2025
|
||||
*/
|
||||
|
||||
|
||||
class Vec3Spring(
|
||||
var value: Vec3 = Vec3.ZERO,
|
||||
var velocity: Vec3 = Vec3.ZERO
|
||||
) {
|
||||
val history = ArrayDeque<Vec3>()
|
||||
//Hold up to a seconds worth of values
|
||||
val lookBackSize = 20
|
||||
|
||||
fun update(target: Vec3, stiffness: Double, damping: Double) {
|
||||
|
||||
//Hooke's Law f = kx where:
|
||||
//f is force
|
||||
//k is the spring constant or stiffness of this spring
|
||||
//x is the distance from rest and in this case the difference between the most recent
|
||||
//rotation delta ( the target ) and the springs current value for delta rot
|
||||
val force = target.subtract(value).scale(stiffness)
|
||||
|
||||
//Now calculate acceleration and apply damping
|
||||
val accel = force.subtract(velocity.scale(damping))
|
||||
|
||||
velocity = velocity.add(accel.scale(1.0/20.0))
|
||||
value = value.add(velocity.scale(1.0/20.0))
|
||||
|
||||
history.addFirst(value)
|
||||
//Adding one since if we want to check max look back point it needs something to interp with.
|
||||
if( history.size > lookBackSize + 1) {
|
||||
history.removeLast()
|
||||
}
|
||||
}
|
||||
|
||||
fun getInterpolated(partialTick: Double, lookBackTick: Int): Vec3 {
|
||||
if (history.size < 2) {
|
||||
return value // or Vec3.ZERO if you prefer
|
||||
}
|
||||
|
||||
val tick = min(lookBackTick, history.size - 2)
|
||||
var ret = Vec3.ZERO
|
||||
for( i in 0..tick) {
|
||||
val older = history.get(i + 1).scale(1.0)
|
||||
val newer = history.get(i).scale(1.0)
|
||||
ret = ret.add(older.add(newer.subtract(older).scale(partialTick)))
|
||||
}
|
||||
|
||||
return ret.scale(1.0/(tick+1))
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,14 @@ package com.cobblemon.mod.common.api.spawning
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.api.entity.Despawner
|
||||
import com.cobblemon.mod.common.api.spawning.condition.*
|
||||
import com.cobblemon.mod.common.api.spawning.condition.AreaSpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.BasicSpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.FishingSpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.GroundedSpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SeafloorSpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SubmergedSpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SurfaceSpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.context.AreaContextResolver
|
||||
import com.cobblemon.mod.common.api.spawning.context.FishingSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.context.GroundedSpawningContext
|
||||
@@ -44,6 +51,7 @@ import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.TickingSpawner
|
||||
import com.cobblemon.mod.common.entity.pokemon.CobblemonAgingDespawner
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import net.minecraft.server.MinecraftServer
|
||||
|
||||
/**
|
||||
* A grouping of all the overarching behaviours of the Best Spawner system. This is a convenient accessor to
|
||||
@@ -130,7 +138,8 @@ object BestSpawner {
|
||||
spawnerManagers.forEach(SpawnerManager::onConfigReload)
|
||||
}
|
||||
|
||||
fun onServerStarted() {
|
||||
fun onServerStarted(server: MinecraftServer) {
|
||||
CobblemonSpawnPools.onServerLoad(server)
|
||||
spawnerManagers.forEach(SpawnerManager::onServerStarted)
|
||||
fishingSpawner = FishingSpawner()
|
||||
}
|
||||
|
||||
+16
-1
@@ -8,10 +8,13 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.spawning.condition.BiomePrecalculation
|
||||
import com.cobblemon.mod.common.api.spawning.condition.BucketPrecalculation
|
||||
import com.cobblemon.mod.common.api.spawning.condition.ContextPrecalculation
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
|
||||
import com.cobblemon.mod.common.data.CobblemonDataProvider
|
||||
import net.minecraft.server.MinecraftServer
|
||||
|
||||
/**
|
||||
* A collection of all of Cobblemon's general-purpose [SpawnPool]s. These
|
||||
@@ -26,6 +29,18 @@ object CobblemonSpawnPools {
|
||||
lateinit var WORLD_SPAWN_POOL: SpawnPool
|
||||
|
||||
fun load() {
|
||||
WORLD_SPAWN_POOL = CobblemonDataProvider.register(SpawnPool("world").addPrecalculators(ContextPrecalculation, BucketPrecalculation))
|
||||
WORLD_SPAWN_POOL = CobblemonDataProvider.register(SpawnPool("world")
|
||||
.addPrecalculators(
|
||||
ContextPrecalculation,
|
||||
BucketPrecalculation,
|
||||
BiomePrecalculation
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun onServerLoad(server: MinecraftServer) {
|
||||
Cobblemon.LOGGER.info("Optimizing spawn pools...")
|
||||
WORLD_SPAWN_POOL.forEach { it.onServerLoad(server) }
|
||||
WORLD_SPAWN_POOL.precalculate()
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -32,7 +32,7 @@ abstract class AreaTypeSpawningCondition<T : AreaSpawningContext> : SpawningCond
|
||||
return false
|
||||
} else if (maxHeight != null && ctx.height > maxHeight!!) {
|
||||
return false
|
||||
} else if (neededNearbyBlocks != null && neededNearbyBlocks!!.none { cond -> ctx.nearbyBlockTypes.any { cond.fits(it, ctx.blockRegistry) } }) {
|
||||
} else if (neededNearbyBlocks != null && neededNearbyBlocks!!.none { cond -> ctx.nearbyBlockHolders.any { cond.fits(it) } }) {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
|
||||
+11
@@ -10,6 +10,8 @@ package com.cobblemon.mod.common.api.spawning.condition
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.world.level.biome.Biome
|
||||
|
||||
/**
|
||||
* A spawning condition that is composed of a list of conditions and anticonditions.
|
||||
@@ -30,6 +32,15 @@ class CompositeSpawningCondition {
|
||||
var conditions = mutableListOf<SpawningCondition<*>>()
|
||||
var anticonditions = mutableListOf<SpawningCondition<*>>()
|
||||
|
||||
fun isBiomeValid(holder: Holder<Biome>): Boolean {
|
||||
if (conditions.isEmpty() || conditions.any { it.biomes == null || it.biomes!!.isEmpty() || it.biomes!!.any { it.fits(holder) } }) {
|
||||
if (anticonditions.isEmpty() || anticonditions.none { it.biomes != null && it.biomes!!.any { it.fits(holder) } }) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun satisfiedBy(ctx: SpawningContext): Boolean {
|
||||
return if (conditions.isNotEmpty() && conditions.none { it.isSatisfiedBy(ctx) }) {
|
||||
false
|
||||
|
||||
+5
-1
@@ -30,7 +30,11 @@ abstract class GroundedTypeSpawningCondition<T : GroundedSpawningContext> : Area
|
||||
return false
|
||||
} else if (maxHeight != null && ctx.height > maxHeight!!) {
|
||||
return false
|
||||
} else !(neededBaseBlocks != null && neededBaseBlocks!!.none { it.fits(ctx.baseBlock.block, ctx.blockRegistry) })
|
||||
} else if (neededBaseBlocks != null && neededBaseBlocks!!.none { it.fits(ctx.baseBlockHolder) }) {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
override fun copyFrom(other: SpawningCondition<*>, merger: Merger) {
|
||||
|
||||
+8
-4
@@ -30,10 +30,14 @@ abstract class SeafloorTypeSpawningCondition<T : SeafloorSpawningContext> : Area
|
||||
return if (!super.fits(ctx)) {
|
||||
false
|
||||
} else if (minHeight != null && ctx.height < minHeight!!) {
|
||||
return false
|
||||
false
|
||||
} else if (maxHeight != null && ctx.height > maxHeight!!) {
|
||||
return false
|
||||
} else !(neededBaseBlocks != null && neededBaseBlocks!!.none { it.fits(ctx.baseBlock.block, ctx.blockRegistry) })
|
||||
false
|
||||
} else if (neededBaseBlocks != null && neededBaseBlocks!!.none { it.fits(ctx.baseBlockHolder) }) {
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun copyFrom(other: SpawningCondition<*>, merger: Merger) {
|
||||
@@ -44,7 +48,7 @@ abstract class SeafloorTypeSpawningCondition<T : SeafloorSpawningContext> : Area
|
||||
}
|
||||
|
||||
override fun isValid(): Boolean {
|
||||
val containsNullValues = neededBaseBlocks != null && neededBaseBlocks!!.any {it == null}
|
||||
val containsNullValues = neededBaseBlocks != null && neededBaseBlocks!!.any { it == null }
|
||||
return super.isValid() && !containsNullValues
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user