Compare commits

...
Author SHA1 Message Date
Apion d0cbc78b31 Configurable dance distance 2025-04-26 15:29:06 -04:00
Apion f2a03e40ed Make dancing pose driven instead of a molang condition 2025-04-26 15:08:39 -04:00
Landon Wills 87071bedbe Move dance behaviour to delegates 2025-04-15 07:59:40 -04:00
Landon ebe33e200c Remove debug code 2025-04-15 11:36:50 +00:00
Landon 15f30721b4 Rename dance pose 2025-04-15 06:12:03 +00:00
Landon Wills c3c4d6685e :catJAM: 2025-04-14 23:34:38 -04:00
Landon bb6e5607f1 Merge branch 'stop-head-pitch' into 'main'
Make head moving actually stop this time.

See merge request cable-mc/cobblemon!1596
2025-04-13 04:55:00 +00:00
Landon 4603f5ac7a Merge branch 'riding-animation-01' into 'main'
Riding dynamic animation, riding fixes, and controller tweaks

See merge request cable-mc/cobblemon!1599
2025-04-12 23:59:31 +00:00
Jackson BowesandLandon c683294227 Riding dynamic animation, riding fixes, and controller tweaks 2025-04-12 23:59:31 +00:00
JPAK f29c2343e6 Merge remote-tracking branch 'origin/stop-head-pitch' into stop-head-pitch 2025-04-12 16:41:40 -05:00
JPAK 3a2da5a87d Make head moving actually stop this time. 2025-04-12 16:41:13 -05:00
Landon 1c13ab24e8 Merge branch 'even-more-riding-tweaks' into 'main'
Even more riding tweaks

See merge request cable-mc/cobblemon!1597
2025-04-12 03:07:36 +00:00
Paul 380cf0ea71 You? You're meant to be dead! 2025-04-12 12:29:44 +10:00
Paul 8ded7334bb Refactor forcedTick handling in PosableEntityModel 2025-04-12 12:23:17 +10:00
Polymeta 0ac34bf1a4 Merge branch 'locator-rotation-player-rendering' into 'main'
Lock player model to seat locator's rotation when riding

See merge request cable-mc/cobblemon!1598
2025-04-11 19:41:47 +00:00
Landon Wills 9716ea0806 Fix rotation of player when using custom camera 2025-04-11 06:13:47 -04:00
Landon Wills ffea94f3b2 Rotate player depending on seat locator's rotation 2025-04-11 03:39:49 -04:00
Landon Wills 3061ba69e2 Riding momentum is maintained when window is not active 2025-04-11 03:17:26 -04:00
Landon Wills 741e38d606 Fix issue where same-species pokemon will sync animations on multiplayer environments 2025-04-11 02:01:28 -04:00
Landon Wills 02f2c072c9 Fix issues with pre-animation logic 2025-04-11 01:29:49 -04:00
JPAK 4722defec2 Make head moving actually stop this time. 2025-04-10 13:14:32 -05:00
Polymeta c54849f415 Merge branch 'implement_pokemon' into 'main'
Few mons added for testing

See merge request cable-mc/cobblemon!1595
2025-04-10 13:46:35 +00:00
Frank The FarmerandPolymeta d44e625e9d Few mons added for testing 2025-04-10 13:46:35 +00:00
Polymeta 6a74d5982a Merge branch 'more-riding-tweaks' into 'main'
Riding tweaks / fixes

See merge request cable-mc/cobblemon!1594
2025-04-10 09:13:32 +00:00
Landon Wills 03905c77d2 Add license 2025-04-10 03:12:53 -04:00
Landon Wills da9558f5f9 Fix camera riding momentum not occurring when in chat or screens 2025-04-10 01:40:44 -04:00
Landon Wills 5c65dffac8 Improve rendering of ridden pokemon substantially on low or inconsistent FPS 2025-04-09 23:36:21 -04:00
Landon Wills b67b37c99e Fix some issues with merge 2025-04-09 22:54:23 -04:00
Landon Wills fbb7ee31c5 Merge branch 'refs/heads/main' into water-mount-behaviours 2025-04-09 22:38:45 -04:00
Polymeta d3ef26976b Merge branch 'composite-behaviour-refactor' into 'main'
Composite behaviour refactor

See merge request cable-mc/cobblemon!1584
2025-04-09 15:58:55 +00:00
Landon Wills dac01a3730 Fix issue with crashing on packet decode before player is initialized 2025-04-09 03:30:55 -04:00
Landon Wills ddb05768e4 Remove redundant riding behaviour, rename ride style option from misc to composite 2025-04-09 03:17:31 -04:00
Polymeta 5374940d24 Merge branch 'spicy-molang' into 'main'
Added `spawn_pokemon` and `attempt_wild_battle` as available MoLang functions

Closes #1237

See merge request cable-mc/cobblemon!1586
2025-04-09 07:13:00 +00:00
SpicyandPolymeta 1fd6a660dd Added spawn_pokemon and attempt_wild_battle as available MoLang functions 2025-04-09 07:13:00 +00:00
Landon Wills fd45934775 Fix issue with canRide condition, remove duplicate riding config in aerodactyl 2025-04-09 03:09:04 -04:00
Polymeta 5e0ffd8bd3 Merge branch 'josh/item-molang-fixes' into 'main'
Small fixes for molang functions related to item rendering.

See merge request cable-mc/cobblemon!1590
2025-04-08 16:02:48 +00:00
JoshandPolymeta b4b315360d Small fixes for molang functions related to item rendering. 2025-04-08 16:02:48 +00:00
Polymeta a44c70648b actually return held item to player again
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-04-08 16:07:20 +01:00
Polymeta c41b0cdca2 Merge branch 'negative-config-values' into 'main'
Allow negative numbers in config GUI

See merge request cable-mc/cobblemon!1588
2025-04-08 12:01:45 +00:00
lionandPolymeta 88f743ee48 Allow negative numbers in config GUI 2025-04-08 12:01:44 +00:00
Polymeta 15b9afacf8 remove client logic from common code MoLang functions
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-04-08 12:59:36 +01:00
Polymeta babcf7430b don't error when heldItem nbt is not present
Closes #1505

Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-04-08 11:38:05 +01:00
Polymeta 3439ca2011 [skip-ci] fix tag docs formatting
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-04-08 10:55:48 +01:00
Polymeta 6539f3dea1 Merge branch 'josh/3d-wearable-items' into 'main'
Start of wearable item implementation.

See merge request cable-mc/cobblemon!1579
2025-04-08 09:16:24 +00:00
Landon Wills 1677598ec0 Merge branch 'refs/heads/main' into water-mount-behaviours
# Conflicts:
#	common/src/main/java/com/cobblemon/mod/common/mixin/client/CameraMixin.java
2025-04-08 01:36:01 -04:00
Landon fcde1eb171 Merge branch 'hitbox-and-hit' into 'main'
Position hitbox on client to be accurate to the locator and make looking at...

See merge request cable-mc/cobblemon!1589
2025-04-08 03:26:26 +00:00
Josh a30a5644df Okay, this is it this time for real 2025-04-07 23:03:30 -04:00
JPAK a5672a38b9 Switch to using delegates to position riders. 2025-04-07 21:37:42 -05:00
JPAK 3b14ea319b Position hitbox on client to be accurate to the locator and make looking at stuff align to the camera. 2025-04-07 21:12:01 -05:00
Landon Wills 57e2765f90 Clean up some method signatures in CameraMixin 2025-04-07 21:57:20 -04:00
Josh a090c90831 Merge branch 'main' into josh/3d-wearable-items 2025-04-07 20:55:41 -04:00
Josh 5dcc9ba46d Edit CHANGELOG.md 2025-04-07 21:21:05 +00:00
Josh f8e4ea70df Removed debug models. 2025-04-07 17:09:15 -04:00
Josh 5dc1c749f3 Replaced WearableItemModel enum with WearableItem class. Instances of this class should have a corresponding 3D model in models/item/wearable/ 2025-04-07 16:35:46 -04:00
Polymeta 3accfa29b7 Merge branch 'josh/joint-transform-command' into 'main'
Changed command "changejointscale" into "transformmodelpart". Allows you to easily add model transformations in-game.

See merge request cable-mc/cobblemon!1587
2025-04-07 17:18:56 +00:00
Josh 1af1569f63 Updated CHANGELOG.md 2025-04-07 11:13:27 -04:00
Polymeta dbd2f5d340 Merge branch 'implement_pokemon' into 'main'
Implemented many pokemon

See merge request cable-mc/cobblemon!1585
2025-04-07 09:35:47 +00:00
Frank The FarmerandPolymeta 86bf413f00 Implemented many pokemon 2025-04-07 09:35:46 +00:00
Landon f13e7d03a1 Merge branch 'camera-changes-third-person' into 'main'
Riding Third Person Camera

See merge request cable-mc/cobblemon!1582
2025-04-07 02:51:07 +00:00
JordanandLandon 4a025ac65e Riding Third Person Camera 2025-04-07 02:51:07 +00:00
Josh 8a0c98c9ab Renamed command to transformmodelpart 2025-04-06 18:58:13 -04:00
Josh 36c5fe2419 Properly display name of model part. 2025-04-06 18:52:15 -04:00
Josh 0c79d102cf Changed changejointscale command into transformpartcommand with can cahnge the position rotation and scale of any joint on a model. 2025-04-06 18:31:39 -04:00
Polymeta b14b25ae19 Merge branch 'SaferSendouts' into 'main'
Safer sendouts

See merge request cable-mc/cobblemon!1458
2025-04-06 20:58:09 +00:00
Josh bd8f644b9a Add scaling as an option when creating transformedParts 2025-04-06 10:56:10 -04:00
Josh 7c84cdb05d transformedParts must add to the model part rather than replacing it with set() 2025-04-06 10:54:00 -04:00
Landon Wills 0293c0e811 Update licenses 2025-04-06 04:02:10 -04:00
Landon 125975e25f Merge branch 'item-with-components-swap' into 'main'
Fixed not being able to swap held item of the same type, but have different components.

See merge request cable-mc/cobblemon!1580
2025-04-06 05:42:42 +00:00
Landon Wills c7223ae898 Fix some issues with the new composite system, move assets to new system 2025-04-06 01:35:46 -04:00
Landon baf766298a Merge branch 'riding-ground-render-revert' into 'main'
revert broken ground rendering code. Correctly set stale yrot value on return...

See merge request cable-mc/cobblemon!1583
2025-04-06 05:11:30 +00:00
Jackson BowesandLandon 83580e7e8b revert broken ground rendering code. Correctly set stale yrot value on return... 2025-04-06 05:11:30 +00:00
Landon Wills 38f5c12dce Remove some redundant casting 2025-04-05 23:59:47 -04:00
Landon Wills e6a9a27692 Forgot to remove some code I refactored 2025-04-05 23:59:21 -04:00
Landon Wills 7ff413c401 Add support for custom behaviour settings for different composite strategies 2025-04-05 23:57:30 -04:00
Test 90335ccb2d hide mark scroll when unused 2025-04-05 17:26:09 -04:00
Landon Wills 603f99e5f3 Refactor composite behaviours 2025-04-05 04:24:19 -04:00
Landon Wills 1b53a127af Add lapras assets 2025-04-05 01:39:02 -04:00
Josh 9679929694 Merge branch 'main' into 'item-with-components-swap'
# Conflicts:
#   common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonEntity.kt
2025-04-05 02:01:27 +00:00
Landon fd6fb5db47 Merge branch 'riding-friction-change' into 'main'
Decouple minecraft friction logic for better feeling controllers. Limit speed...

See merge request cable-mc/cobblemon!1581
2025-04-05 00:21:27 +00:00
Jackson BowesandLandon a80ada8590 Decouple minecraft friction logic for better feeling controllers. Limit speed... 2025-04-05 00:21:27 +00:00
Landon c4a4b9cb86 Merge branch 'multiplayer-riding' into 'main'
Multiplayer support for riding

See merge request cable-mc/cobblemon!1578
2025-04-04 21:15:55 +00:00
Josh 5af96e75eb Fixed message when swapping items.
Used `displayName` rather than `hoverName` for more dynamic text in message.
2025-04-04 13:35:09 -04:00
Josh 023a01befd Can now swap pokemon's held items if the components are different. 2025-04-04 13:03:49 -04:00
Polymeta 4912a89e6e pokemon are allowed to turn their heads when they are not ridden
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-04-04 13:12:16 +01:00
Polymeta f2e10d1b01 Merge branch 'refs/heads/main' into multiplayer-riding
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonNetwork.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonEntity.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/net/messages/client/spawn/SpawnPokemonPacket.kt
2025-04-04 12:54:10 +01:00
Hiroku 25eb197491 Merge branch 'ribs' into 'main'
🎀

See merge request cable-mc/cobblemon!1566
2025-04-04 10:13:58 +00:00
TestandHiroku 8de233b572 🎀 2025-04-04 10:13:58 +00:00
Landon Wills 83972ca9e6 Hook up a couple more pokemon for bird controller 2025-04-04 03:50:34 -04:00
Landon Wills 35e2190cae Reset orientation properly on landing 2025-04-04 02:33:23 -04:00
Landon Wills b966cd2db0 Fix issue with orientation tracking for rendering 2025-04-04 02:06:06 -04:00
Landon Wills 3e492541cc Fix issue with registry sync on riding properties 2025-04-04 01:40:27 -04:00
JazzMcNade cc2022ed59 Merge branch 'main' into SaferSendouts 2025-04-03 20:30:24 -05:00
Josh 298fdf45d5 Adjusted default position and scale values for wearables. Any additional transformations for a specific pokemon should be done in its posers. 2025-04-03 20:47:09 -04:00
Polymeta 4e9541d7f5 licenses
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-04-03 23:24:11 +01:00
Josh 1dab5b8c7c Added debug models to help with aligning wearable items on pokemon. 2025-04-03 17:03:26 -04:00
Landon Wills 2875e9efa9 Temporary fix to crashing issue due to pokemon sticking around when they shouldnt be 2025-04-03 16:27:44 -04:00
Landon Wills 2b42fff66b Fix issue with players sometimes launching at odd angles when riding 2025-04-03 16:26:54 -04:00
Landon Wills 643a33a643 Prevent crouching moving the player when riding pokemon 2025-04-03 16:26:34 -04:00
Landon Wills e348c48eff Fix reversed third person camera 2025-04-03 14:25:03 -04:00
Landon Wills 473f58cab9 No more random render flickers yippee 2025-04-03 13:49:18 -04:00
Landon Wills 987b8619a3 Specify side for riding state management, add multiplayer wing flap for charizard 2025-04-03 05:11:33 -04:00
Landon Wills 87632941e8 Fix issue with player not rotating on mount 2025-04-03 02:25:11 -04:00
Landon Wills 041acab7ca Optimize change detection on riding sync 2025-04-03 02:06:23 -04:00
Landon Wills b3de7d1c1a Synchronize riding state to clients 2025-04-03 01:25:30 -04:00
Landon Wills afcb2dd77c Use render orientation instead of raw orientation for local players 2025-04-03 00:12:36 -04:00
Landon Wills a7bd6d2bf4 Update Garchomp, change translation in mounted player rendering 2025-04-02 23:54:22 -04:00
Landon Wills b096cb60a3 Use more precise orientation for posable model if player is local 2025-04-02 23:16:34 -04:00
JPAK 7f1c0c3d4c Fixed culling. It really was that easy xd 2025-04-02 07:23:26 -05:00
Landon Wills a4f0bf9d2f Fix issue with the world ocasionally playing hide and seek when rolling in first person 2025-04-02 01:57:40 -04:00
JacksonB f46c748e9f Add forgotten bird logic 2025-04-02 00:07:37 -05:00
Landon Wills 065479c58c Make rendering smooth for rolling players & entities 2025-04-02 00:16:05 -04:00
JacksonB eee3d46a2d Keep a render orientation that interpolates behind the last received orientation and damp the slerp by a static amount frame to frame. 2025-04-01 21:07:21 -05:00
Hiroku d7b43fcbfc Merge branch 'spicy-molang' into 'main'
Added `pokemon` as an available Molang function for the `battleActor` functions

See merge request cable-mc/cobblemon!1577
2025-04-01 20:21:18 +00:00
Landon Wills c82e011f73 Pull together riding branches, trying to fix render stuttering 2025-04-01 13:18:50 -04:00
Spicy bf1b996962 Added pokemon as an available Molang function for the battleActor functions 2025-04-01 09:12:16 -05:00
Landon Wills fc46cdcda0 Merge remote-tracking branch 'refs/remotes/origin/camera-changes' into test
# Conflicts:
#	common/src/main/java/com/cobblemon/mod/common/mixin/client/CameraMixin.java
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/player/MountedPlayerRenderer.kt
2025-04-01 09:24:11 -04:00
Landon Wills 4e7b029afb Merge remote-tracking branch 'refs/remotes/origin/multiplayer-roll-sync' into test 2025-04-01 09:22:19 -04:00
Landon Wills 5430f02770 Add much more context to pose provider, optimize velocity tracking, add sync to server 2025-04-01 09:20:51 -04:00
Landon Wills 6a51abe3ee Axe more of the old system, fix some issues with deserialization 2025-04-01 06:48:21 -04:00
Landon Wills 008652b442 Forgot a change 2025-04-01 06:32:11 -04:00
Landon Wills c1e088eb33 Hook new riding infrastructure up to serialization, axe the old controllers 2025-04-01 06:31:33 -04:00
Landon Wills 1669a2f428 Make all riding behaviour settings encodable 2025-04-01 05:44:18 -04:00
Landon Wills 2ba63f4641 Redirect PokemonEntity and mixins to new riding system 2025-04-01 05:29:17 -04:00
Landon Wills 41b9e9001d Refactor riding system 2025-04-01 03:48:45 -04:00
JacksonB 679920b786 Rolling is now displayed on remote clients while riding. 2025-03-31 23:15:44 -05:00
Landon Wills f058d6e635 More work on controllers. Poses borked again 2025-03-31 20:46:11 -04:00
Landon Wills f9377b7489 Some progress on data transfer for ride controllers (currently not functional) 2025-03-31 17:55:59 -04:00
Polymeta e59b5094ad correctly warp world as holder inside spawnfilters
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-03-31 11:50:54 +01:00
JacksonB c8ec4e6f1f commit to not lose anything because paranoid the computer explodes 2025-03-31 00:20:21 -05:00
Josh a2fef9f1be Moved WearableItemModel functions out of HeldItemRenderer.kt 2025-03-30 21:24:13 -04:00
Josh 3d4df2c098 Fixed item rendering not falling back to the item locator if other locators are not present. 2025-03-30 21:11:05 -04:00
Josh 7557cab03d Fixed item visibility button not showing the right icon when opening summary or switching pokemon. 2025-03-30 20:55:28 -04:00
Josh f1018b1da9 Simplified HeldItemRenderer to use only one render function that renders an Item on a PosableModel. And fixed the item lighting in the summary menu. 2025-03-30 20:03:07 -04:00
Landon Wills cbcc6abe40 Fix issue with poses not changing properly in multiplayer riding 2025-03-30 17:04:36 -04:00
Josh 5af4e3bddb Added placeholder models for wearable items and logic to register the models. 2025-03-30 02:03:44 -04:00
Landon Wills 64c9d47b0f Fix multiplayer issue where pokemon other player's were riding would not rotate 2025-03-29 21:10:42 -04:00
Josh 1f519cae50 Fixed rotation of items rending in PokemonOnShoulderRenderer and ModelWidget 2025-03-29 20:01:04 -04:00
Landon Wills c0d2803f61 Fix multiplayer issues with clients not recognizing ridden pokemon on login 2025-03-29 15:55:34 -04:00
Landon Wills e249d5238f Fix issue with orientation not getting reset on ride dismount 2025-03-29 02:34:00 -04:00
Landon Wills e32e505e95 Fix issue with sprint not getting recognized on RunUpToJetFlightCompositeController 2025-03-29 02:14:40 -04:00
Landon 7642a84108 Merge branch 'ride-controller-server-crash-fix' into 'multiplayer-riding'
Ride controller server crash fix

See merge request cable-mc/cobblemon!1575
2025-03-29 04:58:25 +00:00
JacksonB b6e20d69cc fix to crouch but sprint during riding is still broken 2025-03-28 15:18:10 -05:00
JacksonB 9d04e20a30 Remove client only code causing server crash and replace with proper code. 2025-03-28 14:51:33 -05:00
Landon Wills d2624813f7 Merge branch 'refs/heads/bug-fixes' into multiplayer-riding 2025-03-28 13:36:22 -04:00
Landon Wills a2789e70ed Use readString in readExpression to be a bit more clear on it's function 2025-03-28 13:35:05 -04:00
Landon Wills f6f607904e Merge branch 'refs/heads/bug-fixes' into multiplayer-riding 2025-03-28 01:53:19 -04:00
Landon Wills 22ea0296c0 Fix issues with riding controller packet decoding 2025-03-28 01:52:03 -04:00
Landon Wills 24ae2f5324 Fix issue with registering species on servers 2025-03-28 01:51:38 -04:00
Landon Wills 9284497cec Refactor Rollable interface, fix issue with mixins crossing sides, causing server to fail to load 2025-03-28 00:21:26 -04:00
Landon Wills 36cb12952a Move more functions out of Rollable to mouse handling 2025-03-27 21:55:29 -04:00
Landon Wills 2700dc7f39 Refactor function to get angular velocity out of Rollable 2025-03-27 21:23:12 -04:00
Polymeta c9d27d46b6 license header
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-03-26 11:39:18 +00:00
Polymeta b68756378c Merge branch 'Box-count-command' into 'main'
Added /boxcount

See merge request cable-mc/cobblemon!1565
2025-03-25 17:01:22 +00:00
Polymeta 1ff1880951 Merge branch 'main' into 'Box-count-command'
# Conflicts:
#   common/src/main/kotlin/com/cobblemon/mod/common/api/permission/CobblemonPermissions.kt
2025-03-25 16:59:25 +00:00
Polymeta fea2d32de8 Merge branch 'spectate-command' into 'main'
Added a spectate battle command.

See merge request cable-mc/cobblemon!1264
2025-03-25 16:56:49 +00:00
Conan e3e6d4452c # 2025-03-25 14:12:49 +01:00
Conan f687e8e658 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into spectate-command
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonCommands.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/permission/CobblemonPermissions.kt
#	common/src/main/resources/assets/cobblemon/lang/en_us.json
2025-03-25 14:07:49 +01:00
Hiroku 2413981f6f Merge branch 'charizardmarketing' into 'main'
Updated Charizard with the latest model and animations to be used for marketing on the main branch.

See merge request cable-mc/cobblemon!1572
2025-03-24 01:23:40 +00:00
Tyzillion 4ac244003a -6+ ?????? 2025-03-23 21:22:47 -04:00
Tyzillion 9ac92a084f Updated Charizard with new locators. 2025-03-23 21:18:31 -04:00
Apion aacbcb1f28 Merge branch 'jackowes/concrete_bird' into 'main'
Overhaul of bird and fix of jet velocity bug

See merge request cable-mc/cobblemon!1563
2025-03-23 23:37:47 +00:00
Jackson BowesandApion 39ff7e418c Overhaul of bird and fix of jet velocity bug 2025-03-23 23:37:47 +00:00
Hiroku 58498a091d Merge branch 'josh/npc-item-rendering' into 'main'
Molang for rendering items during animations.

See merge request cable-mc/cobblemon!1570
2025-03-23 23:12:06 +00:00
Hiroku a806f77aff Merge branch 'entity-scale' into 'main'
Pokemon hitboxes scale with attribute generic.scale

Closes #1410

See merge request cable-mc/cobblemon!1529
2025-03-23 22:05:38 +00:00
JordanandHiroku 60ae9e3a55 Pokemon hitboxes scale with attribute generic.scale 2025-03-23 22:05:37 +00:00
Hiroku 03ddc35d36 Merge branch 'Fix-pokemon-loadFrom' into 'main'
Fixed Pokemon.loadFrom[JSON/NBT]

Closes #1507

See merge request cable-mc/cobblemon!1571
2025-03-23 03:22:17 +00:00
ShiroFenixandHiroku 1b8e9b9b36 Fixed Pokemon.loadFrom[JSON/NBT] 2025-03-23 03:22:17 +00:00
Paul e52ac956f0 Added q.entity.is_holding_item for pokemon posers to use 2025-03-23 12:40:59 +10:00
Josh 4bfe147730 Merge branch 'main' into josh/npc-item-rendering 2025-03-22 12:41:31 -04:00
Hiroku 493664bef2 Merge branch 'mealam/pokeballlocation' into 'main'
fix: Adjust `NPCEntity` pokeball throw positioning to account for `baseScale`

See merge request cable-mc/cobblemon!1568
2025-03-21 23:50:33 +00:00
AramandHiroku 981e3e5efc fix: Adjust NPCEntity pokeball throw positioning to account for baseScale 2025-03-21 23:50:33 +00:00
Josh 963614f5fa Updated CHANGELOG.md 2025-03-21 11:51:18 -04:00
Josh 7fdb70e3c3 Updated trainer npcs for testing 2025-03-21 11:20:24 -04:00
Josh 37e8f0eab6 multiple items can render on a model per locator and the render_item() molang function now supports items with components 2025-03-20 16:14:16 -04:00
ShiroFenixlm a6d17659f8 Added removeListOfBoxes to PCStore 2025-03-19 23:31:43 -04:00
Josh 05edafa5c4 NPC Trainers send out/return animation are able to show the correct ball. 2025-03-19 22:35:00 -04:00
ShiroFenixlm 997a6de23b Remove and Set now only delete empty boxes unless forced 2025-03-19 21:52:08 -04:00
Hiroku c15350e647 Merge branch 'mealam/1499' into 'main'
fix: Adjust `NPCEntity` beam positioning to account for `baseScale`

Closes #1499

See merge request cable-mc/cobblemon!1567
2025-03-20 00:36:12 +00:00
AramandHiroku f9e5784b10 fix: Adjust NPCEntity beam positioning to account for baseScale 2025-03-20 00:36:12 +00:00
Josh cea600617e Added render_item() molang function for PosableState. Allows rendering dummy items for animations. 2025-03-19 18:47:11 -04:00
ShiroFenixlm c948b6d885 Added force remove button 2025-03-18 19:40:53 -04:00
ShiroFenixlm 5508980384 Final Cleanup, i hope 2025-03-17 15:24:02 -04:00
ShiroFenixlm e39dee01e5 Cleanup 2025-03-17 15:16:06 -04:00
ShiroFenixlm a81412e176 Merge remote-tracking branch 'origin/Box-count-command' into Box-count-command
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/command/ChangePCBoxesCommand.kt
2025-03-17 15:15:01 -04:00
ShiroFenixlm 09f9dd91aa Fixed some issues and added error handling when removing too many boxes 2025-03-17 15:08:07 -04:00
ShiroFenixlm 2efc3c72ac Fixed some issues and added error handling when removing too many boxes 2025-03-17 15:02:57 -04:00
ShiroFenix a92fb03093 Updated command logic to improve legibility 2025-03-17 12:56:15 -04:00
ShiroFenix ad5847983f Updated command logic to improve legibility 2025-03-17 12:55:59 -04:00
WhiteFenix19 bb25300513 Updated command logic to improve legibility 2025-03-17 12:32:46 -04:00
WhiteFenix19 de33804eb4 Updated command logic to improve legibility 2025-03-17 12:19:47 -04:00
WhiteFenix19 de042f71dd Updated command logic to improve legibility 2025-03-17 12:18:28 -04:00
WhiteFenix19 5a1e85bbf4 Added /boxcount command 2025-03-17 00:47:00 -04:00
ShiroFenix 3489e0df74 Revert "Added /boxcount"
This reverts commit afe8eec343
2025-03-17 03:59:02 +00:00
ShiroFenix afe8eec343 Added /boxcount 2025-03-17 03:43:32 +00:00
Hiroku 2f4b861f98 Merge branch 'implement_pokemon' into 'main'
Dragonite and Gyarados models with locators for testing on main

See merge request cable-mc/cobblemon!1564
2025-03-16 00:20:05 +00:00
Frank The FarmerandHiroku a7022268c1 Dragonite and Gyarados models with locators for testing on main 2025-03-16 00:20:05 +00:00
Hiroku 3082600e11 Merge branch 'meowscarada-fix' into 'main'
Fixed Meowscarada's resolver having the wrong name

See merge request cable-mc/cobblemon!1562
2025-03-15 08:48:41 +00:00
gruxor a348cfd338 Fixed Meowscarada's resolver having the wrong name 2025-03-15 04:19:38 -04:00
Paul 35bfc7bf6c Merge remote-tracking branch 'origin/main' 2025-03-15 13:22:28 +10:00
Paul ae87c3dd84 Fixed downward correction of spawning, closes #1498 2025-03-15 13:22:04 +10:00
Hiroku d944803571 Merge branch 'jackowes/concrete_jet/01' into 'main'
Full Base Jet Controller

See merge request cable-mc/cobblemon!1560
2025-03-15 01:14:00 +00:00
Jackson BowesandHiroku 2c3b474c01 Full Base Jet Controller 2025-03-15 01:13:59 +00:00
Paul c2538560e1 Added check for activate_ action effects, implemented protect blocking effect 2025-03-14 10:23:50 +10:00
Paul d10c058c74 Added properties map to posers and look for item display contexts from there as a first try for item rendering 2025-03-14 09:55:31 +10:00
Paul d1b6d95d57 Fixed interpolation being applied to full model transformed parts 2025-03-14 09:17:34 +10:00
Paul 977387a184 Merge remote-tracking branch 'origin/main' 2025-03-13 20:26:35 +10:00
Paul 69bbc09d6a Updated fabric version, fixed our is_overworld including end biomes 2025-03-13 20:26:23 +10:00
Hiroku 46b04ad462 Merge branch 'async-player-data-saving' into 'main'
Moved player data saving to do the heavier work on a worker thread,...

See merge request cable-mc/cobblemon!1559
2025-03-13 05:03:28 +00:00
Paul a1b6fd3d1a Fixed silent pokemon still playing shiny sounds and particles, closes #1316 2025-03-13 11:19:10 +10:00
Paul fb9ed28b74 Moved player data saving to do the heavier work on a worker thread, potentially fixing storages not saving in race conditions 2025-03-13 11:01:51 +10:00
Paul 4bfc2a97dd Fixed base entity functions not being added to pokemon structs, messing with a lot of things 2025-03-13 09:47:12 +10:00
Nick a2cf66fbc1 Merge branch 'seat-generator' into 'main'
Seat generator

See merge request cable-mc/cobblemon!1558
2025-03-12 16:10:03 -07:00
HirokuandNick 5bda81a5c4 Seat generator 2025-03-12 16:10:03 -07:00
Test 32ccd15121 biome test bg 2025-03-12 03:56:25 +00:00
Hiroku a51cda998b Merge branch 'Adainish-main-patch-50806' into 'main'
Fixes a deserialisation issue due to IVS/EVS being required

See merge request cable-mc/cobblemon!1557
2025-03-12 02:25:20 +00:00
Adenydd Stuut || Winglet 9dc14eff7c Edit PokemonProperties.kt 2025-03-12 01:48:15 +00:00
Hiroku 4b1690c62e Merge branch 'observe-less' into 'main'
Removed heaps of observables from Pokemon because they weren't needed and it added heaps to the RAM

See merge request cable-mc/cobblemon!1555
2025-03-11 22:57:49 +00:00
Hiroku d02251b67d Edit CHANGELOG.md 2025-03-11 11:57:07 +00:00
Hiroku 54c8cbfbe8 Merge branch 'unlockable-wallpapers' into 'main'
Added unlockable PC box wallpapers

See merge request cable-mc/cobblemon!1525
2025-03-11 09:49:11 +00:00
Paul 2d8ce63dcd Changelog 2025-03-11 15:53:02 +10:00
Paul 1a92af2cd6 Removed heaps of observables from Pokemon because they weren't needed and it added heaps to the RAM 2025-03-11 15:51:45 +10:00
Paul 7e088bdeec @JvmStatic on all the permissions so the interop isn't whack 2025-03-11 13:30:01 +10:00
Paul 6142049d51 Added uuid function to pokemonStore molang 2025-03-11 12:57:38 +10:00
Hiroku d031f339c6 Merge branch 'spawning-improvements' into 'main'
Spawning performance improvements

See merge request cable-mc/cobblemon!1553
2025-03-10 11:33:39 +00:00
Paul 3ddbc9b313 Unmark wallpapers as new once you try them 2025-03-10 20:59:42 +10:00
Paul dd2c1ddbc6 Regenerate the validBiomes lists when data is reloaded (yknow, for when that works) 2025-03-10 20:34:31 +10:00
Paul badd7e8c2e Merge remote-tracking branch 'origin/main' into spawning-improvements 2025-03-10 20:27:48 +10:00
Paul d7c4380cc2 oop 2025-03-09 20:09:00 +10:00
Paul 7fc26ab939 Fixed held items being given with "Air" as the name oops 2025-03-09 14:50:08 +10:00
Paul 37f430ddf7 Fixed issues with unseen wallpapers not communicating properly to the client 2025-03-09 14:42:29 +10:00
Hiroku f0a2182149 Merge branch 'riding-settings' into 'main'
Added riding config options and implemented the invert mouse options on current controllers.

See merge request cable-mc/cobblemon!1550
2025-03-09 04:14:47 +00:00
Paul 9605775f1e Merge remote-tracking branch 'origin/main' into spawning-improvements 2025-03-09 11:00:56 +10:00
Paul f251bc20c1 Fixed species feature lookups not targeting code features first because of a funnybug 2025-03-09 11:00:29 +10:00
Paul a02b3d74b8 Fixed species additions not being able to add implemented, closes #779 2025-03-09 10:58:18 +10:00
Paul 7f3a4c20b3 Added biome precalculation to the spawning system, improved RegistryLikeCondition performance 2025-03-09 10:39:10 +10:00
Hiroku e76999c611 Merge branch 'Fix-NPCs-using-pokemon-outside-Pool' into 'main'
Fixing NPCs using pokemon outside of their Pool

Closes #1482

See merge request cable-mc/cobblemon!1552
2025-03-07 23:30:04 +00:00
WhiteFenix19 c2771bcb95 Fixed Formatting 2025-03-07 19:27:51 -04:00
WhiteFenix19 5b858b5982 Merge remote-tracking branch 'origin/Fix-NPCs-using-pokemon-outside-Pool' into Fix-NPCs-using-pokemon-outside-Pool 2025-03-07 17:09:29 -04:00
WhiteFenix19 7082220e29 Fixed NPCs using Pokémon outside of their pool when a Pokémon name had a typo. (Fixed indentations) 2025-03-07 17:08:52 -04:00
WhiteFenix19 1a87830a52 Fixed NPCs using Pokémon outside of their pool when a Pokémon name had a typo. 2025-03-07 17:02:54 -04:00
WhiteFenix19 9eff6dcced Fixed NPCs using Pokémon outside of their pool when a Pokémon name had a typo. 2025-03-07 15:05:48 -04:00
WhiteFenix19 52690c3aa6 Added check to avoid random Pokémon on Pool, added warning if species wrong or party empty 2025-03-07 13:59:40 -04:00
Paul 8f0780c5f6 I'm autistic 2025-03-07 12:34:59 +10:00
JazzMcNade 91864b13d0 Moved GetSendOutPosition into ActiveBattlePokemon.kt 2025-03-06 01:45:58 -06:00
Test 465a7fbe73 update changelog, alt icons for descending sort 2025-03-05 17:47:42 -05:00
Hiroku 6b5abfa319 Merge branch 'fix-interpolation' into 'main'
Fixed interpolation breaking with non emissive textures

See merge request cable-mc/cobblemon!1551
2025-03-05 11:31:37 +00:00
Paul 39197adebb Why'd I format that like that 2025-03-05 18:45:00 +10:00
BuildTools 4b1e192705 Fixed interpolation breaking with non emmisive textures 2025-03-05 10:32:54 +02:00
Test a3fa3d4b82 Merge branch 'unlockable-wallpapers' of https://gitlab.com/cable-mc/cobblemon into unlockable-wallpapers 2025-03-05 02:44:32 -05:00
Test c5ad7a0fb7 refactor pc updates 2025-03-05 02:44:11 -05:00
Paul d17f94c0d6 Reworked way pokemon are blocked from spawning on rails to not be as intensive 2025-03-05 14:27:02 +10:00
JazzMcNade adc8e9921a Updated Changelog 2025-03-04 21:54:23 -06:00
JazzMcNade 5e5473acae Muted timing console logging 2025-03-04 21:50:28 -06:00
JazzMcNade 3509944f1a Comment, fixed indentation 2025-03-03 20:53:42 -06:00
JPAK 398bff4c15 Fix some formatting. 2025-03-03 20:48:45 -06:00
JPAK e0655df5f0 Merge remote-tracking branch 'origin/riding-settings' into riding-settings
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/riding/controller/RideController.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/riding/controllers/BirdAirController.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/riding/controllers/HelicopterAirController.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/riding/controllers/JetAirController.kt
2025-03-03 20:44:58 -06:00
JazzMcNade 8a5489f805 Merge branch 'main' into SaferSendouts 2025-03-03 20:44:26 -06:00
JPAK 9174aac64c Added Config options and implemented the invert mouse options on current controllers. 2025-03-03 20:43:35 -06:00
JPAK 3cdc4c7d97 Added Config options and implemented the invert mouse options on current controllers. 2025-03-03 17:35:32 -06:00
JPAK 922ee95fd2 Camera changes for riding. Need to find a better system for being "unlocked" (in third person only) to make the animations feel better. 2025-03-03 16:22:13 -06:00
Polymeta 9ce6acc44d Merge branch 'fix-hide-additional-tooltip' into 'main'
hide_additional_tooltip working for Pokerod and Bait

See merge request cable-mc/cobblemon!1547
2025-03-03 08:44:49 +00:00
Polymeta f170ed726b Merge branch 'jackowes/riding/02' into 'main'
Fix: Whole json object being passed to deserialize rather than the single JsonElement.

See merge request cable-mc/cobblemon!1549
2025-03-02 18:11:14 +00:00
JacksonB 979d6f02a6 Fix: Whole json object being passed to deserialize rather than the single JsonElement. 2025-03-02 11:32:38 -06:00
BuildTools 3fbe80cb1a Changelog 2025-03-02 17:02:07 +02:00
Polymeta 8fdb32273d Merge branch 'nuggets-of-nuggets' into 'main'
Fling

See merge request cable-mc/cobblemon!1548
2025-03-02 13:11:56 +00:00
dhi_holo d5c9d8d5b1 Fling
Blocks of Gold now can be flung for 150 BP
2025-03-02 20:57:37 +10:00
BuildTools 752c9c234e hide_additional_tooltip working for Pokerod and Bait 2025-03-02 12:10:28 +02:00
Hiroku b7b6185ada Merge branch 'jackowes/ride_cleanup' into 'main'
WIP jet, bird, and helicopter controller

See merge request cable-mc/cobblemon!1543
2025-03-02 09:31:46 +00:00
Jackson BowesandHiroku 211b886bf3 WIP jet, bird, and helicopter controller 2025-03-02 09:31:45 +00:00
Paul 36d1008f29 Minor code cleanup and added interpolate property to changelog 2025-03-02 15:39:02 +10:00
Hiroku 1441625a6b Merge branch 'animated-texures-interpolation' into 'main'
Animated Texture Interpolation for Cobblemon

See merge request cable-mc/cobblemon!1545
2025-03-02 05:28:00 +00:00
Paul 3859eb574e Another tweak for Polymeta 2025-03-02 15:25:12 +10:00
Paul b4a29214a7 Minor changes to unlockable wallpapers code for Poly's happiness 2025-03-02 15:24:05 +10:00
Apion 0fb5d4f69b Merge branch 'riding-sheet-scraper' into 'main'
Added scraper for riding sheet

See merge request cable-mc/cobblemon!1546
2025-03-01 08:34:14 +00:00
Gertimoshka 6ca16a0161 Resolve Conflicts 2025-03-01 03:46:18 +00:00
Paul e4346687fc Added scraper for riding sheet 2025-03-01 10:51:04 +10:00
BuildTools 2f31db2232 Insane Person Presents: INTERPOLATION 2025-02-27 18:04:48 +02:00
Polymeta e0f26923b7 Merge branch 'moe-eats-bugs' into 'main'
Fix logic for LevelUpCriterion

See merge request cable-mc/cobblemon!1544
2025-02-26 13:08:30 +00:00
MoeBoy76andPolymeta 52cff9472a Fix logic for LevelUpCriterion 2025-02-26 13:08:30 +00:00
Test ed0e0fc65a Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into unlockable-wallpapers 2025-02-23 11:04:13 -05:00
Polymeta c4935c3128 Merge branch 'feat/animated-party-portraits' into 'main'
Add support for animated portraits in the party overlay

See merge request cable-mc/cobblemon!1302
2025-02-23 09:26:59 +00:00
Hiroku 7831421399 Merge branch 'feature/cosmetic-slot' into 'main'
Cosmetic item

See merge request cable-mc/cobblemon!1538
2025-02-23 09:11:15 +00:00
TestandHiroku a059979220 Cosmetic item 2025-02-23 09:11:15 +00:00
Hiroku 09bab5b2aa Merge branch 'means-of-transportation' into 'main'
Rhydon the Pokemon

See merge request cable-mc/cobblemon!1534
2025-02-21 22:45:15 +00:00
Paul 358d2604fb imports 2025-02-22 08:34:17 +10:00
Paul bbd0415c92 Removed unused packet 2025-02-22 08:33:47 +10:00
Paul 9f553fc6c1 Remove blank mixin 2025-02-22 08:22:25 +10:00
Polymeta 51f8bb6ad5 Merge branch 'fix-pokeball-spewpa-evo' into 'main'
Fixed field name in evolution requirements for Spewpa Pokeball.

See merge request cable-mc/cobblemon!1541
2025-02-21 11:40:23 +00:00
Deltric cf93c0d544 Update changelog 2025-02-21 05:25:12 -06:00
Paul 2e6bef608b Merge remote-tracking branch 'origin/main' 2025-02-21 20:22:54 +10:00
Paul dcba7a5ab7 Removed unnecessary berry validation now that resultant yields are capped 2025-02-21 20:22:27 +10:00
Hiroku 4e3b1eb641 Merge branch 'feature/fix-ghost-pokemon' into 'main'
remove owned pokemon immediately when player stops seeing them, instead of queuing them

Closes #1127

See merge request cable-mc/cobblemon!1528
2025-02-21 10:21:43 +00:00
Paul 6f949ce858 Fixed no sounds playing for status curing berries being eaten or battle-interacted healing berries being eaten 2025-02-21 20:20:38 +10:00
Paul a04cd3162a Merge remote-tracking branch 'origin/main' into means-of-transportation
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/pokemon/PokemonRenderer.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/Pokemon.kt
2025-02-21 17:48:24 +10:00
Paul 8b551159a6 Removed things that shouldn't have been there, client-packaged various client mixins 2025-02-21 17:46:46 +10:00
Hiroku 05b8621a81 Merge branch 'gradle/reposilite' into 'main'
Reposilite maven publication endpoint

See merge request cable-mc/cobblemon!1537
2025-02-21 03:14:39 +00:00
Paul 1068d3e078 Fixed berry yield exceeding growth points, closes #1435 2025-02-21 12:51:20 +10:00
Hiroku c1d6a9563c Merge branch 'feature/modfile-versions' into 'main'
Modfile tweaks and small mixin fixes

Closes #1460 and #1456

See merge request cable-mc/cobblemon!1539
2025-02-21 00:28:37 +00:00
PolymetaandHiroku f426b16f1d Modfile tweaks and small mixin fixes 2025-02-21 00:28:37 +00:00
Hiroku c944fc1ac6 Merge branch 'fix/form-change-illegal-moves' into 'main'
fix: Pokemon will now properly update their moveset after form changes

See merge request cable-mc/cobblemon!1540
2025-02-21 00:27:48 +00:00
Paul c5dcfc57ea Added toast for unlocking PC wallpapers, fixed some molang issues for living entities 2025-02-21 10:26:13 +10:00
Deltric c68df830a7 Fixed field name in evolution requirements for Spewpa Pokeball. 2025-02-19 19:37:44 -06:00
Ina 0432441868 fix: Pokemon will now properly update their moveset after form changes 2025-02-19 22:52:23 +00:00
Ina dcd0959600 chore: MR renamed feedback 2025-02-19 20:47:12 +00:00
Nick 7f631174d7 Use native snapshot extension 2025-02-17 14:27:05 -08:00
Nick 379344036f Reposilite maven publication endpoint 2025-02-17 14:24:20 -08:00
Paul 9997a5c4b4 Touch-ups to wallpaper unlocked flow and the right click platform event flows 2025-02-17 20:29:43 +10:00
Paul 2b70e5614c Merge remote-tracking branch 'origin/main' into unlockable-wallpapers
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonFlows.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/molang/MoLangFunctions.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/events/FlowHandler.kt
2025-02-17 20:18:29 +10:00
Paul 759f0800cd Added MoLang flows for poke_ball_capture_calculated, evolution_tested, evolution_accepted, evolution_completed, cleaned up a collection of MoLang hooks from PokemonProperties and spawning 2025-02-17 20:08:49 +10:00
Paul 0ed0270d18 Merge remote-tracking branch 'origin/main' 2025-02-17 16:42:05 +10:00
Hiroku 478e714a2d Merge branch 'held-item-rending-fix' into 'main'
Visual Held Items Rending Fix

See merge request cable-mc/cobblemon!1536
2025-02-17 03:13:38 +00:00
JazzMcNade 3b7d56ff45 Merge branch 'main' into SaferSendouts 2025-02-16 20:53:08 -06:00
Josh 6005c04e64 There was an issue with rendering items in the original visual held items branch that wasn't properly reverted. If multiple pokemon where being rendered with different item display contexts it would prioritize one, ignoring the other. This fixes that. 2025-02-16 21:16:48 -05:00
Polymeta 75f76ae228 Merge branch 'fix/chestboat-inventory-persistence' into 'main'
Fix 1444 (Apricorn boat with chest voiding inventory)

Closes #1444

See merge request cable-mc/cobblemon!1533
2025-02-16 01:09:56 +00:00
JustAHuman-xDandPolymeta 31a955fed1 Fix 1444 (Apricorn boat with chest voiding inventory) 2025-02-16 01:09:55 +00:00
Paul 4f1517e336 Adding molang context and functions to poke ball capture event 2025-02-16 10:07:14 +10:00
Polymeta 6772d95d25 fix example json [skip-ci] 2025-02-15 23:47:51 +00:00
Hiroku a29f9d7bce Merge branch 'visually-held-items' into 'main'
Visible Held Items Implementation

See merge request cable-mc/cobblemon!1531
2025-02-15 23:28:56 +00:00
Josh 2ff6559b5e Merge remote-tracking branch 'origin/visually-held-items' into visually-held-items 2025-02-15 16:49:51 -05:00
Josh fc6739f640 Revert "Updated HeldItemRender to only update modifiers once when the item changes, rather than every frame"
This reverts commit 6ff4eef7dc.
2025-02-15 16:48:12 -05:00
Josh 6ff4eef7dc Updated HeldItemRender to only update modifiers once when the item changes, rather than every frame 2025-02-15 16:40:11 -05:00
Josh 82151c3944 Updated HeldItemRender to only update modifiers once when the item changes, rather than every frame 2025-02-15 16:38:50 -05:00
Hiroku 0af4588a14 README change [skip-ci] 2025-02-15 20:26:29 +00:00
Test 589c5381e2 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into unlockable-wallpapers 2025-02-15 15:14:40 -05:00
Josh 3c07ca973b Updated CHANGELOG.md 2025-02-15 15:14:33 -05:00
Test 4539703497 prevent filenotfoundexception; update colors 2025-02-15 15:14:23 -05:00
Josh 60f3393f3f HeldItemRenderer is no longer instantiated every frame 2025-02-15 02:16:25 -05:00
Josh c6a1518e0d A HeldItemRenderer is no longer instantiated every frame, oops 2025-02-14 22:16:44 -05:00
Josh e816e41df1 Removed unused/unnecessary methods 2025-02-14 01:34:20 -05:00
Josh 96a9844fa4 Changed heldItemVisible to optional 2025-02-13 22:02:35 -05:00
Josh 8febe77edb Made item visibility value savable 2025-02-13 21:21:29 -05:00
Josh ef89d8fcd8 renamed isItemHidden to heldItemVisible and reversed the bool in places where needed 2025-02-13 21:20:40 -05:00
Josh 0253a4dd7a Items with components no longer break rendering 2025-02-13 19:46:07 -05:00
Josh 5259ea0455 Merge branch 'main' into visually-held-items 2025-02-13 17:53:08 -05:00
Josh f637768e3d Rendering items for shoulder pokemon 2025-02-13 17:38:02 -05:00
Josh ef867a02d5 Refactors and renamed item_glasses to item_face 2025-02-13 17:37:45 -05:00
Josh e0456939a9 ModelWidget can render held items 2025-02-12 19:04:59 -05:00
Ina 7210063026 feat: Add config support for this system 2025-02-12 20:10:16 +00:00
Ina 94e52f6f82 Merge branch 'main' into feat/animated-party-portraits
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/gui/PartyOverlay.kt
2025-02-12 18:49:15 +00:00
Josh cba0c138b0 Merge branch 'main' into visually-held-items 2025-02-12 11:17:13 -05:00
Josh b748e39877 Removed unused comment 2025-02-12 11:15:27 -05:00
Polymeta 0d311593cc remove owned pokemon immediately when player stops seeing them, instead of queuing them
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-02-11 11:12:13 +00:00
Josh a1031f3dc9 Logic to decide when held items or hand items are shown. 2025-02-11 00:13:35 -05:00
Josh e753e05c67 Created new C2S packet for changing item visibility 2025-02-11 00:07:51 -05:00
Josh ed292bcaea Changed item tag names 2025-02-10 23:05:59 -05:00
Paul 0152bc2e32 Added event for unlocking wallpapers, added flows for advancement_earned, right_clicked_entity, right_clicked_block, player_died, wallpaper_unlocked 2025-02-10 18:13:27 +10:00
Paul e175c44118 Added unlockable PC box wallpapers 2025-02-10 15:36:46 +10:00
Josh c2f1176aca Merge branch 'main' into visually-held-items 2025-02-08 19:18:00 -05:00
Josh 637ad2e510 Created a Renderer Class for held items. Using Null objects to modify scale and displayContext for now, but this is planned to change 2025-02-08 17:17:13 -05:00
Josh e08ddc4fc4 Started implementing renderable held items. Only working client side at the moment. 2025-02-08 00:02:25 -05:00
JazzMcNade 50c567d264 Clean up, scale look radius with size 2025-01-04 18:35:14 -06:00
JazzMcNade d4964e41d3 Clean up, fixed fluid handling 2025-01-03 22:04:23 -06:00
JazzMcNade 41b1c169c3 Merge branch 'main' into SaferSendouts 2025-01-03 20:49:13 -06:00
JazzMcNade 49e2001004 clean up 2025-01-03 20:48:28 -06:00
JazzMcNade 66393f9293 Merge branch 'main' into SaferSendouts 2025-01-02 21:11:07 -06:00
JazzMcNade 7b3b6146a1 Single raycast to avoid spawning in walls, setPositionSafely can no longer path into a wall 2024-12-30 20:44:32 -06:00
JazzMcNade e1452244c2 Merge branch 'main' into SaferSendouts 2024-12-30 12:50:24 -06:00
Conan 8913d3df11 Reworked SpectateBattleHandler.kt 2024-12-30 18:23:58 +01:00
Conan 3a55965704 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into spectate-command
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonCommands.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/permission/CobblemonPermissions.kt
2024-12-29 12:30:54 +01:00
JazzMcNade 5358414a0c Merge branch 'main' into SaferSendouts 2024-12-24 15:27:25 -06:00
JazzMcNade d691373919 Capped collision box size. Added trace to help prevent attempts to send outs going into walls 2024-12-15 20:14:30 -06:00
JazzMcNade 038f13e6fa Merge branch 'main' into SaferSendouts 2024-12-15 00:52:31 -06:00
JazzMcNade dd288a3b0c SetPositionSafely now implements BFS WIP 2024-12-08 21:39:17 -06:00
Ina 0da8243f05 fix: Missing return 2024-11-17 19:38:03 +00:00
Ina 2774360d4c feat: Add system to draw animated (or not) portraits based on graphics settings. 2024-11-17 19:34:49 +00:00
Conan 7112c74bf2 Reworked spectate command. 2024-11-09 22:37:11 +01:00
Conan faa1906290 Added a spectate battle command. 2024-11-09 17:30:01 +01:00
1471 changed files with 100844 additions and 17966 deletions
+12
View File
@@ -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
View File
@@ -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
View File
@@ -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.
+138 -3
View File
@@ -2,13 +2,113 @@
## [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).
- Pokémon now follow the mouse cursor on the Summary screen, with an option to disable this in the settings.
- Pokémon's held items can now be rendered, with a visibility toggle in the Summary screen.
- Added cosmetic item functionality for Pokémon. Certain cosmetic items can be given to applicable Pokémon via the interact menu.
- Added the various log blocks as cosmetic items for Timburr and Komala.
- Added `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)
@@ -21,6 +121,22 @@
- Fixed fling not using Item Names for minecraft held items that substitute Pokémon items
- Fixed evolutions sometimes preventing players from logging in to servers
- Fixed rendering of shoulder-mounted Pokémon desyncing between clients
- Fixed apricorn chest boats forgetting their inventories when being unloaded
- Fixed moves not updating correctly between form changes resulting in illegal movesets
- Fixed crash sometimes occurring with the "Oritech" mod
- Fixed crashes due to an incorrect Java version handing out an obscure crash.
- Fixed some berries being able to rarely get too many berries and cause a crash.
- Fixed status curing berries not playing the berry eating sound, same for healing berries used mid-battle.
- 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.
@@ -31,14 +147,32 @@
- 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.
- 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`.
### 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.
### 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)
- The `requiredContext` for an item interaction evolution
- 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)
@@ -144,6 +278,7 @@
- Geodude
- Graveler
- Golem
- Magnezone
### Cry updates for the following Pokémon
- Sceptile
@@ -2159,4 +2294,4 @@ Thank you so much to all of our community translators that bring the mod to the
- Added species, ability, and move translations for `ko_ko`, `jp_jp`, `fr_fr`, `es_es`, `it_it`, and `zh_cn`.
## [1.0.0 (November 12th, 2022)](#1-0-0)
- Initial release.
- Initial release.
+1 -1
View File
@@ -1,6 +1,6 @@
# Cobblemon
An open-source Pokémon mod for Minecraft Java Edition, written in Kotlin for Fabric and Forge.
An open-source Pokémon mod for Minecraft Java Edition, written in Kotlin for Fabric and NeoForge.
## Getting started
@@ -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();
}
@@ -1,97 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.Rollable;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.world.entity.Entity;
import org.joml.Matrix3f;
import org.joml.Quaternionf;
import org.joml.Vector3f;
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;
@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;
@Shadow @Final private Vector3f up;
@Shadow @Final private Vector3f left;
@Shadow private float xRot;
@Shadow private float yRot;
@Shadow public abstract float getXRot();
@Shadow public abstract float getYRot();
@Unique private float returnTimer = 0;
@Unique private float rollAngleStart = 0;
@Unique Minecraft minecraft = Minecraft.getInstance();
@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) {
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(
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();
return;
}
rollable.getOrientation().rotateZ((float) Math.toRadians(-rollAngleStart*(1-returnTimer)));
applyRotation();
this.returnTimer += .05F;
ci.cancel();
} else {
this.returnTimer = 1;
rollable.clearRotation();
}
return;
}
if (rollable.getOrientation() == null) return;
applyRotation();
this.returnTimer = 0;
this.rollAngleStart = rollable.getRoll();
ci.cancel();
}
@Unique
private void applyRotation(){
if (!(this.entity instanceof Rollable rollable) || rollable.getOrientation() == null) return;
var newRotation = rollable.getOrientation().normal(new Matrix3f()).getNormalizedRotation(new Quaternionf());
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());
}
}
@@ -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);
}
}
}
}
@@ -1,30 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import net.minecraft.server.ServerInterface;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(ServerLevel.class)
public abstract class MinecraftDedicatedServerMixin implements ServerInterface {
@Inject(method = "shouldDiscardEntity", at = @At(value = "HEAD"), cancellable = true)
public void cobblemon$allowPokemonSpawns(Entity entity, CallbackInfoReturnable<Boolean> callback) {
if (entity instanceof PokemonEntity) {
callback.setReturnValue(false);
}
}
}
@@ -1,136 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.Rollable;
import com.cobblemon.mod.common.client.CobblemonClient;
import com.cobblemon.mod.common.client.keybind.keybinds.PartySendBinding;
import com.cobblemon.mod.common.pokedex.scanner.PokedexUsageContext;
import com.llamalad7.mixinextras.injector.v2.WrapWithCondition;
import com.llamalad7.mixinextras.sugar.Local;
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 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;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(MouseHandler.class)
public class MouseHandlerMixin {
@Shadow private double accumulatedScrollY;
@Shadow @Final private Minecraft minecraft;
@Unique SmoothDouble pitchSmoother = new SmoothDouble();
@Unique SmoothDouble rollSmoother = new SmoothDouble();
@Shadow @Final private SmoothDouble smoothTurnY;
@Shadow @Final private SmoothDouble smoothTurnX;
@Shadow private double accumulatedDX;
@Shadow private double accumulatedDY;
@Inject(
method = "onScroll",
at = @At(
value = "FIELD",
target="Lnet/minecraft/client/MouseHandler;accumulatedScrollY:D",
opcode = Opcodes.PUTFIELD,
ordinal = 2,
shift = At.Shift.BEFORE
),
cancellable = true
)
public void cobblemon$scrollParty(long window, double horizontal, double vertical, CallbackInfo ci) {
if (PartySendBinding.INSTANCE.getWasDown()) {
int i = (int) accumulatedScrollY;
if (i > 0) {
accumulatedScrollY -= i;
CobblemonClient.INSTANCE.getStorage().shiftSelected(false);
ci.cancel();
PartySendBinding.INSTANCE.actioned();
} else if (i < 0) {
accumulatedScrollY -= i;
CobblemonClient.INSTANCE.getStorage().shiftSelected(true);
ci.cancel();
PartySendBinding.INSTANCE.actioned();
}
}
}
@Inject(
method = "onScroll",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/entity/player/Inventory;swapPaint(D)V"
),
cancellable = true
)
public void cobblemon$doPokedexZoom(long window, double horizontal, double vertical, CallbackInfo ci) {
PokedexUsageContext usageContext = CobblemonClient.INSTANCE.getPokedexUsageContext();
if (usageContext.getScanningGuiOpen()) {
usageContext.adjustZoom(vertical);
ci.cancel();
}
}
@WrapWithCondition(
method = "turnPlayer",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/client/player/LocalPlayer;turn(DD)V"
)
)
public boolean cobblemon$modifyRotation(LocalPlayer player, double cursorDeltaX, double cursorDeltaY, @Local(argsOnly = true) double d) {
PokedexUsageContext usageContext = CobblemonClient.INSTANCE.getPokedexUsageContext();
if (usageContext.getScanningGuiOpen()) {
this.smoothTurnY.reset();
this.smoothTurnX.reset();
var defaultSensitivity = this.minecraft.options.sensitivity().get() * 0.6000000238418579 + 0.20000000298023224;
var spyglassSensitivity = Math.pow(defaultSensitivity, 3);
var lookSensitivity = spyglassSensitivity * 8.0;
var sensitivity = Mth.lerp(usageContext.getFovMultiplier(), spyglassSensitivity, lookSensitivity);
player.turn(this.accumulatedDX * sensitivity, (this.accumulatedDY * sensitivity));
return false;
}
if (!(player instanceof Rollable rollable)) return true;
if (!rollable.shouldRoll()) {
pitchSmoother.reset();
rollSmoother.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);
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;
var pitch = pitchSmoother.getNewDeltaValue(0, e);
var roll = rollSmoother.getNewDeltaValue(0, e);
rollable.rotate(0.0F, (float)pitch, (float)roll);
}
}
@@ -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(
@@ -178,8 +194,7 @@ public abstract class PlayerMixin extends LivingEntity implements Rollable, Scan
@Inject(method = "isInvulnerableTo", at = @At("HEAD"), cancellable = true)
public void isInvulnerableTo(DamageSource source, CallbackInfoReturnable<Boolean> ci) {
if (!level().isClientSide) {
ServerPlayer player = (ServerPlayer)(Object)this;
if (!level().isClientSide && (Object) this instanceof ServerPlayer player) {
boolean invulnerableInBattle = this.level().getGameRules().getBoolean(CobblemonGameRules.BATTLE_INVULNERABILITY);
boolean inBattle = Cobblemon.INSTANCE.getBattleRegistry().getBattleByParticipatingPlayer(player) != null;
if (invulnerableInBattle && inBattle) {
@@ -239,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);
@@ -252,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,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.mixin;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.entity.pokemon.PokemonServerDelegate;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.block.LevelEvent;
import net.minecraft.world.phys.AABB;
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;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(ServerLevel.class)
public abstract class ServerLevelMixin {
@Inject(method = "shouldDiscardEntity", at = @At(value = "HEAD"), cancellable = true)
public void cobblemon$allowPokemonSpawns(Entity entity, CallbackInfoReturnable<Boolean> callback) {
if (entity instanceof PokemonEntity) {
callback.setReturnValue(false);
}
}
@Inject(method = "levelEvent(Lnet/minecraft/world/entity/player/Player;ILnet/minecraft/core/BlockPos;I)V", at = @At(value = "HEAD"))
public void cobblemon$setPokemonDancing(Player player, int type, BlockPos pos, int data, CallbackInfo ci) {
if (type == LevelEvent.SOUND_PLAY_JUKEBOX_SONG || type == LevelEvent.SOUND_STOP_JUKEBOX_SONG) {
cobblemon$notifyNearbyEntities((ServerLevel) ((Object) this), pos, type == LevelEvent.SOUND_PLAY_JUKEBOX_SONG);
}
}
@Unique
private void cobblemon$notifyNearbyEntities(ServerLevel level, BlockPos pos, boolean playing) {
for (PokemonEntity pokemonEntity : level.getEntitiesOfClass(PokemonEntity.class, new AABB(pos).inflate(Cobblemon.config.getPokemonDanceDistance()))) {
PokemonServerDelegate delegate = (PokemonServerDelegate) pokemonEntity.getDelegate();
delegate.setDancing(playing);
delegate.setJukeboxPos(pos);
pokemonEntity.setRecordPlayingNearby(pos, playing);
}
}
}
@@ -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();
}
@@ -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();
}
@@ -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());
}
}
}
@@ -0,0 +1,234 @@
/*
* 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.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;
import org.joml.Vector3f;
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.Map;
@Mixin(Camera.class)
public abstract class CameraMixin {
@Shadow private Entity entity;
@Shadow @Final private Quaternionf rotation;
@Shadow @Final private Vector3f forwards;
@Shadow @Final private Vector3f up;
@Shadow @Final private Vector3f left;
@Shadow private float xRot;
@Shadow private float yRot;
@Shadow private float eyeHeight;
@Shadow private float eyeHeightOld;
@Shadow protected abstract void setPosition(Vec3 pos);
@Shadow
private BlockGetter level;
@Shadow private float partialTickTime;
@Unique private float returnTimer = 0;
@Unique private float rollAngleStart = 0;
@Unique Minecraft minecraft = Minecraft.getInstance();
@Unique boolean disableRollableCameraDebug = false;
@Inject(method = "setRotation", at = @At("HEAD"), cancellable = true)
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.
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;
controller.reset();
return;
}
controller.getOrientation().rotateZ((float) Math.toRadians(-rollAngleStart*(1-returnTimer)));
cobblemon$applyRotation();
this.returnTimer += .05F;
ci.cancel();
} else {
this.returnTimer = 1;
controller.reset();
}
return;
}
if (controller.getOrientation() == null) return;
cobblemon$applyRotation();
this.returnTimer = 0;
this.rollAngleStart = controller.getRoll();
ci.cancel();
}
//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 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();
MountedPokemonAnimationRenderController.INSTANCE.reset();
if(vehicle instanceof Rideable){
//RidingCameraDelagate.INSTANCE.positionCamera(instance, x, y, z, original);
if (!(vehicle instanceof PokemonEntity pokemon)) {
original.call(instance, x, y, z);
return;
}
PokemonClientDelegate delegate = (PokemonClientDelegate) pokemon.getDelegate();
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())
);
MountedPokemonAnimationRenderController.INSTANCE.setup(pokemon, partialTickTime);
int seatIndex = pokemon.getPassengers().indexOf(entity);
Seat seat = pokemon.getSeats().get(seatIndex);
OrientationControllable rollable = (OrientationControllable) entity;
OrientationController controller = rollable.getOrientationController();
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 cobblemon$applyRotation(){
if (!(this.entity instanceof OrientationControllable controllable) || controllable.getOrientationController().getOrientation() == null) return;
var controller = controllable.getOrientationController();
var newRotation = controller.getOrientation().normal(new Matrix3f()).getNormalizedRotation(new Quaternionf());
if (this.minecraft.options.getCameraType().isMirrored()) {
newRotation.rotateY((float)Math.toRadians(180));
}
this.rotation.set(newRotation);
this.xRot = controller.getPitch();
this.yRot = controller.getYaw();
this.forwards.set(controller.getForwardVector());
this.up.set(controller.getUpVector());
this.left.set(controller.getLeftVector());
}
@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;
}
}
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker;
import net.minecraft.client.DeltaTracker;
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.client.render.player.MountedPlayerRenderer;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
@@ -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);
}
}
}
@@ -6,12 +6,11 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
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 com.mojang.math.Axis;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.renderer.ItemInHandRenderer;
@@ -32,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);
}
}
@@ -6,12 +6,13 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
package com.cobblemon.mod.common.mixin.client;
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;
@@ -40,7 +41,8 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
*/
@Mixin(ItemRenderer.class)
public abstract class ItemRendererMixin {
@Unique private final String MODEL_PATH = Cobblemon.implementation.getModAPI() == ModAPI.FABRIC ? "fabric_resource" : "standalone";
// ignore the hint saying implmentation is never null, if something crashes then it is null and causes red herrings
@Unique private final String MODEL_PATH = (Cobblemon.implementation != null && Cobblemon.implementation.getModAPI() == ModAPI.FABRIC) ? "fabric_resource" : "standalone";
@Shadow @Final private ItemModelShaper itemModelShaper;
@@ -53,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);
}
}
}
@@ -77,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);
}
}
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.client.CobblemonBakingOverrides;
import java.util.List;
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Bone;
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext;
@@ -0,0 +1,249 @@
/*
* 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.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;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
@Mixin(MouseHandler.class)
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;
@Shadow @Final private SmoothDouble smoothTurnX;
@Shadow private double accumulatedDX;
@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(
value = "FIELD",
target="Lnet/minecraft/client/MouseHandler;accumulatedScrollY:D",
opcode = Opcodes.PUTFIELD,
ordinal = 2,
shift = At.Shift.BEFORE
),
cancellable = true
)
public void cobblemon$scrollParty(long window, double horizontal, double vertical, CallbackInfo ci) {
if (PartySendBinding.INSTANCE.getWasDown()) {
int i = (int) accumulatedScrollY;
if (i > 0) {
accumulatedScrollY -= i;
CobblemonClient.INSTANCE.getStorage().shiftSelected(false);
ci.cancel();
PartySendBinding.INSTANCE.actioned();
} else if (i < 0) {
accumulatedScrollY -= i;
CobblemonClient.INSTANCE.getStorage().shiftSelected(true);
ci.cancel();
PartySendBinding.INSTANCE.actioned();
}
}
}
@Inject(
method = "onScroll",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/entity/player/Inventory;swapPaint(D)V"
),
cancellable = true
)
public void cobblemon$doPokedexZoom(long window, double horizontal, double vertical, CallbackInfo ci) {
PokedexUsageContext usageContext = CobblemonClient.INSTANCE.getPokedexUsageContext();
if (usageContext.getScanningGuiOpen()) {
usageContext.adjustZoom(vertical);
ci.cancel();
}
}
@WrapWithCondition(
method = "turnPlayer",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/client/player/LocalPlayer;turn(DD)V"
)
)
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();
this.smoothTurnX.reset();
var defaultSensitivity = this.minecraft.options.sensitivity().get() * 0.6000000238418579 + 0.20000000298023224;
var spyglassSensitivity = Math.pow(defaultSensitivity, 3);
var lookSensitivity = spyglassSensitivity * 8.0;
var sensitivity = Mth.lerp(usageContext.getFovMultiplier(), spyglassSensitivity, lookSensitivity);
player.turn(this.accumulatedDX * sensitivity, (this.accumulatedDY * sensitivity));
return false;
}
if (!(player instanceof OrientationControllable controllable)) return true;
if (!controllable.getOrientationController().isActive()) {
xMouseSmoother.reset();
yMouseSmoother.reset();
pitchSmoother.reset();
rollSmoother.reset();
yawSmoother.reset();
return true;
}
//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("HEAD"))
private void cobblemon$maintainMovementWhenInScreens2(CallbackInfo ci) {
double time = Blaze3D.getTime();
cobblemon$timeDelta = time - this.lastHandleMovementTime;
}
@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;
}
}
@@ -6,14 +6,20 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
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")
@@ -1,45 +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.renderer;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
/**
* @author landonjw
*/
@Mixin(LivingEntityRenderer.class)
public abstract class LivingEntityRendererMixin {
// @Redirect(
// method = "render(Lnet/minecraft/entity/LivingEntity;FFLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;I)V",
// at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/LivingEntity;hasVehicle()Z"),
// slice = @Slice(
// from = @At(value = "INVOKE", target = "Lnet/minecraft/util/math/MathHelper;lerpAngleDegrees(FFF)F"),
// to = @At(value = "TAIL")
// )
// )
// private boolean dodgeRenderer(LivingEntity instance) {
// return false;
// }
/*@WrapOperation(
method = "render(Lnet/minecraft/world/entity/LivingEntity;FFLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;I)V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Mth;wrapDegrees(F)F")
)
private float replaceWrapValue(float angle, Operation<Float> original){
if(origin){
return 0f;
} else {
return original.call(angle);
}
}*/
}
@@ -12,14 +12,6 @@ object CobblemonBuildDetails {
const val BRANCH = "{{ branch | default("Not Specified") }}"
fun smallCommitHash(): String {
if(this.GIT_COMMIT.equals("Not Specified")) {
return "Unknown"
}
return this.GIT_COMMIT.substring(0, 7)
}
fun version(): String {
return this.VERSION + if(this.SNAPSHOT) "+${this.smallCommitHash()}" else ""
}
}
@@ -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
@@ -80,16 +85,29 @@ import com.cobblemon.mod.common.net.messages.client.settings.ServerSettingsPacke
import com.cobblemon.mod.common.permission.LaxPermissionValidator
import com.cobblemon.mod.common.platform.events.PlatformEvents
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.aspects.COSMETIC_SLOT_ASPECT
import com.cobblemon.mod.common.pokemon.aspects.GENDER_ASPECT
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
@@ -97,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
@@ -151,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>(
@@ -217,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)
@@ -278,6 +301,7 @@ object Cobblemon {
SHINY_ASPECT.register()
GENDER_ASPECT.register()
COSMETIC_SLOT_ASPECT.register()
SpeciesFeatures.types["choice"] = ChoiceSpeciesFeatureProvider::class.java
SpeciesFeatures.types["flag"] = FlagSpeciesFeatureProvider::class.java
@@ -385,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) }
@@ -535,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)
}
}
@@ -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.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.data.CosmeticItemAssignmentSyncPacket
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.cosmetic.CosmeticItemAssignment
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.adapters.ItemLikeConditionAdapter
import com.cobblemon.mod.common.util.adapters.PokemonPropertiesAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.core.RegistryAccess
import net.minecraft.core.registries.Registries
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
object CobblemonCosmeticItems : JsonDataRegistry<CosmeticItemAssignment> {
override val id = cobblemonResource("cosmetic_items")
override val type = PackType.SERVER_DATA
override val observable = SimpleObservable<CobblemonCosmeticItems>()
override val typeToken = TypeToken.get(CosmeticItemAssignment::class.java)
override val resourcePath = "cosmetic_items"
override val gson = GsonBuilder()
.setPrettyPrinting()
.registerTypeAdapter(PokemonProperties::class.java, PokemonPropertiesAdapter(saveLong = false))
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, ItemLikeConditionAdapter)
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
.create()
val cosmeticItems = mutableListOf<CosmeticItemAssignment>()
override fun reload(data: Map<ResourceLocation, CosmeticItemAssignment>) {
cosmeticItems.clear()
data.entries.forEach { (id, value) -> value.id = id }
cosmeticItems.addAll(data.values)
}
override fun sync(player: ServerPlayer) {
CosmeticItemAssignmentSyncPacket(cosmeticItems).sendToPlayer(player)
}
fun findValidForPokemon(pokemon: Pokemon) = cosmeticItems.filter { it.pokemon.any { it.matches(pokemon) } }
fun findValidCosmeticForPokemonAndItem(registryAccess: RegistryAccess, pokemon: Pokemon, itemStack: ItemStack) = cosmeticItems
.filter { it.pokemon.any { it.matches(pokemon) } }
.flatMap { it.cosmeticItems }
.firstOrNull { it.consumedItem.fits(itemStack.item, registryAccess.registryOrThrow(Registries.ITEM)) }
}
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.MoParams
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.data.DataRegistry
@@ -86,13 +87,23 @@ object CobblemonFlows : DataRegistry {
observable.emit(this)
}
fun run(eventResourceLocation: ResourceLocation, context: Map<String, MoValue>, cancelable: Cancelable? = null) {
fun run(
eventResourceLocation: ResourceLocation,
context: Map<String, MoValue>,
functions: Map<String, (MoParams) -> Any> = emptyMap(),
cancelable: Cancelable? = null
) {
if (cancelable == null) {
runtime.environment.query.functions.remove("cancel")
} else {
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 {
if (cancelable != null && cancelable.isCanceled) {
return
@@ -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,14 +145,18 @@ 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
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.FinishScanningPacket
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.StartScanningPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.interact.InteractPokemonPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetItemHiddenPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetNicknamePacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetActiveMarkPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetMarkingsPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.ServerboundUpdateRidingStatePacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.evolution.AcceptEvolutionPacket
import com.cobblemon.mod.common.net.messages.server.starter.RequestStarterScreenPacket
import com.cobblemon.mod.common.net.messages.server.storage.SwapPCPartyPokemonPacket
@@ -187,7 +197,11 @@ import com.cobblemon.mod.common.net.serverhandling.pasture.UnpasturePokemonHandl
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.FinishScanningHandler
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.StartScanningHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.interact.InteractPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetItemHiddenHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetNicknameHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetActiveMarkHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetMarkingsHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.ServerboundUpdateRidingStateHandler
import com.cobblemon.mod.common.net.serverhandling.starter.RequestStarterScreenHandler
import com.cobblemon.mod.common.net.serverhandling.starter.SelectStarterPacketHandler
import com.cobblemon.mod.common.net.serverhandling.storage.BenchMoveHandler
@@ -259,6 +273,14 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(TeraTypeUpdatePacket.ID, TeraTypeUpdatePacket::decode, PokemonUpdatePacketHandler()))
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()))
@@ -284,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))
@@ -349,6 +372,8 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(DexEntrySyncPacket.ID, DexEntrySyncPacket::decode, DataRegistrySyncPacketHandler()))
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))
@@ -379,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))
@@ -401,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
}
@@ -408,6 +443,9 @@ object CobblemonNetwork {
val list = mutableListOf<PacketRegisterInfo<*>>()
// 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))
@@ -435,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))
@@ -494,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)
}
}
@@ -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) }
}
@@ -33,7 +33,7 @@ class LevelUpCriterion(
}
override fun matches(player: ServerPlayer, context: LevelUpContext): Boolean {
val preEvo = context.pokemon.preEvolution == null
val preEvo = context.pokemon.preEvolution != null
val hasEvolution = !context.pokemon.evolutions.none()
var evolutionCheck = true
if (preEvo || hasEvolution) {
@@ -43,27 +43,3 @@ class LevelUpCriterion(
}
}
/*class LevelUpCriterionCondition(id: Identifier, entity: LootContextPredicate) : SimpleCriterionCondition<LevelUpContext>(id, entity) {
var level = 0
var evolved = true
override fun toJson(json: JsonObject) {
json.addProperty("level", level)
json.addProperty("has_evolved", evolved)
}
override fun fromJson(json: JsonObject) {
level = json.get("level")?.asInt ?: 0
evolved = json.get("has_evolved")?.asBoolean ?: true
}
override fun matches(player: ServerPlayer, context: LevelUpContext): Boolean {
val preEvo = context.pokemon.preEvolution == null
val hasEvolution = !context.pokemon.evolutions.none()
var evolutionCheck = true
if (preEvo || hasEvolution) {
evolutionCheck = !(preEvo == hasEvolution)
}
return level == context.level && evolutionCheck == evolved
}
}*/
@@ -8,6 +8,9 @@
package com.cobblemon.mod.common.api.battles.interpreter
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.queryStructOf
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
/**
@@ -30,6 +33,13 @@ interface BattleContext {
/** The [BattlePokemon] that directly or indirectly caused this [BattleContext]. */
val origin: BattlePokemon?
fun getStruct() = queryStructOf(
"type" to { StringValue(type.name) },
"origin" to { origin?.struct ?: DoubleValue(0.0) },
"id" to { StringValue(id) },
"turn_created" to { DoubleValue(turn.toDouble()) }
)
/**
* The type of [BattleContext].
*
@@ -99,7 +99,6 @@ class Berry(
val weight: Float,
val boneMealChance: Float
) {
@Transient
var identifier: ResourceLocation = identifier
internal set
@@ -159,7 +158,7 @@ class Berry(
* Calculates the yield for a berry tree being planted or replanted after a life cycle.
* Triggers [BerryYieldCalculationEvent].
*
* @param world The [World] the tree is present in.
* @param world The [Level] the tree is present in.
* @param state The [BlockState] of the tree.
* @param pos The [BlockPos] of the tree.
* @param placer The [LivingEntity] tending to the tree, if any.
@@ -173,9 +172,10 @@ class Berry(
if (BerryBlock.getMulch(treeEntity) == MulchVariant.RICH) {
//I hope the upper bound isnt exclusive, especially when there's a method called nextBetweenExclusive
// update: it was meant to be exclusive, which caused #1124. I've lowered the upper bound from 3 to 2.
yield += world.random.nextIntBetweenInclusive(1, 2)
yield += world.random.nextIntBetweenInclusive(RICH_MULCH_MIN, RICH_MULCH_MAX)
treeEntity.decrementMulchDuration(world, pos, state)
}
yield = yield.coerceAtMost(growthPoints.size)
val event = BerryYieldCalculationEvent(this, world, state, pos, placer, yield, bonus.second)
CobblemonEvents.BERRY_YIELD.post(event) { yield = it.yield }
return yield
@@ -193,7 +193,7 @@ class Berry(
*
* @return The maximum possible yield.
*/
fun maxYield() = this.baseYield.last + this.growthFactors.sumOf { it.maxYield() }
fun maxYield() = this.baseYield.last + this.growthFactors.sumOf { it.maxYield() } + RICH_MULCH_MAX
/**
* Checks if this berry can mutate with the given [partner].
@@ -231,6 +231,7 @@ class Berry(
}
// A cheat since gson doesn't invoke init block
// well I actually suspect it does but
internal fun validate() {
if (this.baseYield.first < 0 || this.baseYield.last < 0) {
throw IllegalArgumentException("A berry base yield must be a positive range")
@@ -247,10 +248,6 @@ class Berry(
}
*/
this.growthFactors.forEach { it.validateArguments() }
val maxYield = this.maxYield()
if (this.growthPoints.size < maxYield) {
throw IllegalArgumentException("Anchor points must have enough elements for the max possible yield of $maxYield you've provided ${this.growthPoints.size} points")
}
this.shapedFlower = hashMapOf()
this.shapedFruit = hashMapOf()
this.sproutShape = this.createAndUniteShapes(this.sproutShapeBoxes)
@@ -339,6 +336,8 @@ class Berry(
}
companion object {
const val RICH_MULCH_MIN = 1
const val RICH_MULCH_MAX = 2
internal fun decode(buffer: RegistryFriendlyByteBuf): Berry {
val identifier = buffer.readIdentifier()
@@ -0,0 +1,39 @@
/*
* 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.conditional
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.codec.CodecUtils
import com.mojang.serialization.Codec
import net.minecraft.core.Registry
import net.minecraft.core.registries.Registries
import net.minecraft.resources.ResourceKey
import net.minecraft.tags.TagKey
val ITEM_REGISTRY_LIKE_CODEC = makeCodecForRegistryLikeCondition("item", Registries.ITEM)
fun <T : Any> makeCodecForRegistryLikeCondition(name: String, registryKey: ResourceKey<Registry<T>>): Codec<RegistryLikeCondition<T>> {
return CodecUtils.createByStringCodec(
from = { str ->
if (str.startsWith("#")) {
return@createByStringCodec RegistryLikeTagCondition<T>(TagKey.create(registryKey, str.substring(1).asIdentifierDefaultingNamespace()))
} else {
return@createByStringCodec RegistryLikeIdentifierCondition<T>(str.asIdentifierDefaultingNamespace())
}
},
to = { condition ->
if (condition is RegistryLikeTagCondition) {
"#${condition.tag.location}"
} else {
(condition as RegistryLikeIdentifierCondition).identifier.toString()
}
},
errorSupplier = { "Unknown $name condition: $it. Should be # followed by a tag, or $name resource location e.g. #minecraft:ladders or minecraft:grass_block." }
)
}
@@ -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
}
@@ -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)
}
@@ -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
@@ -26,29 +27,18 @@ open class RegistryLikeTagCondition<T : Any>(val tag: TagKey<T>) : RegistryLikeC
companion object {
const val PREFIX = "#"
fun <T : Any> resolver(
ResourceKey: ResourceKey<Registry<T>>,
resourceKey: ResourceKey<Registry<T>>,
constructor: (TagKey<T>) -> RegistryLikeTagCondition<T>
): (JsonElement) -> RegistryLikeTagCondition<T>? = {
val firstSymbol = it.asString.substring(0, 1)
if (firstSymbol == PREFIX) {
val identifier = ResourceLocation.parse(it.asString.substring(1))
constructor(TagKey.create(ResourceKey, identifier))
constructor(TagKey.create(resourceKey, identifier))
} else {
null
}
}
}
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) {}
}
@@ -8,6 +8,10 @@
package com.cobblemon.mod.common.api.events
import com.bedrockk.molang.runtime.MoParams
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.MoValue
/**
* Something that can be canceled. This is a highly complex class and should only be read by professional engineers.
*
@@ -21,4 +25,9 @@ abstract class Cancelable {
fun cancel() {
isCanceled = true
}
val cancelFunc: Pair<String, (MoParams) -> MoValue> = "cancel" to {
cancel()
DoubleValue.ONE
}
}
@@ -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>()
@@ -212,6 +215,10 @@ object CobblemonEvents {
val HELD_ITEM_PRE = CancelableObservable<HeldItemEvent.Pre>()
@JvmField
val HELD_ITEM_POST = EventObservable<HeldItemEvent.Post>()
@JvmField
val COSMETIC_ITEM_PRE = CancelableObservable<HeldItemEvent.Pre>()
@JvmField
val COSMETIC_ITEM_POST = EventObservable<HeldItemEvent.Post>()
@JvmField
val POKEMON_GAINED = EventObservable<PokemonGainedEvent>()
@@ -8,16 +8,12 @@
package com.cobblemon.mod.common.api.events.battles
import com.bedrockk.molang.runtime.struct.QueryStruct
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.api.battles.interpreter.BattleContext
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.util.asArrayValue
import java.util.function.Function
/**
* Event fired when a [BattlePokemon] faints. Exposes the [BattlePokemon] that fainted and the [BattleContext]
@@ -37,13 +33,6 @@ data class BattleFaintedEvent(
"npcs" to battle.actors.filter { it.type == ActorType.NPC }.asArrayValue { it.struct },
"wild_pokemon" to battle.actors.filter { it.type == ActorType.WILD }.asArrayValue { it.struct },
"pokemon" to killed.struct,
"context" to QueryStruct(
hashMapOf(
"type" to Function { StringValue(context.type.name) },
"origin" to Function { context.origin?.struct ?: DoubleValue(0.0) },
"id" to Function { StringValue(context.id) },
"turn_created" to Function { DoubleValue(context.turn.toDouble()) },
)
)
"context" to context.getStruct()
)
}
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.api.events.battles
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.util.asArrayValue
import net.minecraft.network.chat.MutableComponent
@@ -30,6 +31,8 @@ data class BattleStartedPreEvent (override val battle: PokemonBattle, var reason
"npcs" to battle.actors.filter { it.type == ActorType.NPC }.asArrayValue { it.struct },
"wild_pokemon" to battle.actors.filter { it.type == ActorType.WILD }.asArrayValue { it.struct }
)
val functions = moLangFunctionMap(cancelFunc)
}
/**
@@ -26,7 +26,7 @@ data class TerastallizationEvent(
val battle: PokemonBattle,
val pokemon: BattlePokemon,
val teraType: TeraType
) {
) {
val context = mutableMapOf<String, MoValue>(
"battle" to battle.struct,
"players" to battle.actors.filter { it.type == ActorType.PLAYER }.asArrayValue { it.struct },
@@ -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()
)
}
}
@@ -8,9 +8,15 @@
package com.cobblemon.mod.common.api.events.pokeball
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.api.pokeball.catching.CaptureContext
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.getBooleanOrNull
import com.cobblemon.mod.common.util.getIntOrNull
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.LivingEntity
@@ -32,4 +38,34 @@ class PokeBallCaptureCalculatedEvent(
action(this, thrower)
}
}
val context = mutableMapOf<String, MoValue>(
"thrower" to thrower.asMostSpecificMoLangValue(),
"pokemon" to pokemonEntity.struct,
"poke_ball" to pokeBallEntity.struct,
"is_successful_capture" to DoubleValue(captureResult.isSuccessfulCapture),
"is_critical_capture" to DoubleValue(captureResult.isCriticalCapture),
)
val functions = moLangFunctionMap(
"set_shakes" to { params ->
val numShakes = params.getInt(0)
val successful = params.getBooleanOrNull(1) ?: (numShakes == 4)
captureResult = CaptureContext(
numberOfShakes = numShakes,
isSuccessfulCapture = successful,
isCriticalCapture = false
)
DoubleValue.ONE
},
"set_critical_capture" to { params ->
val numShakes = params.getIntOrNull(0) ?: 1
captureResult = CaptureContext(
numberOfShakes = numShakes,
isSuccessfulCapture = true,
isCriticalCapture = true
)
DoubleValue.ONE
}
)
}
@@ -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)
)
}
@@ -20,8 +20,7 @@ import com.cobblemon.mod.common.pokemon.Pokemon
data class FriendshipUpdatedEvent(
val pokemon: Pokemon,
val newFriendshipInitial: Int
)
{
) {
var newFriendship: Int = newFriendshipInitial
set(value) {
field = value.coerceIn(0, Cobblemon.config.maxPokemonFriendship)
@@ -9,13 +9,11 @@
package com.cobblemon.mod.common.api.events.pokemon
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.events.pokemon.HeldItemEvent.Post
import com.cobblemon.mod.common.api.events.pokemon.HeldItemEvent.Pre
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.world.item.ItemStack
/**
* The base for all the events related to held items.
* The base for all the events related to held items and cosmetic items.
*
* @see [Pre]
* @see [Post]
@@ -28,7 +26,7 @@ interface HeldItemEvent {
val pokemon: Pokemon
/**
* Fired at the start of [Pokemon.swapHeldItem].
* Fired at the start of [Pokemon.swapHeldItem] and [Pokemon.swapCosmeticItem].
*
* This event should be used to mutate the results of this transaction.
*
@@ -44,7 +42,7 @@ interface HeldItemEvent {
data class Pre(override val pokemon: Pokemon, var receiving: ItemStack, var returning: ItemStack, var decrement: Boolean) : HeldItemEvent, Cancelable()
/**
* Fired at the end of [Pokemon.swapHeldItem].
* Fired at the end of [Pokemon.swapHeldItem] and [Pokemon.swapCosmeticItem].
*
* @property pokemon The [Pokemon] triggering this event.
* @property received The [ItemStack] considered as received and set to the [pokemon]. This is a copy and mutation will not be taken into account.
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.api.events.pokemon
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.pokemon.Pokemon
/**
@@ -23,4 +24,11 @@ class LevelUpEvent(val pokemon: Pokemon, val oldLevel: Int, var newLevel: Int) {
"old_level" to DoubleValue(oldLevel.toDouble()),
"new_level" to DoubleValue(newLevel.toDouble())
)
val functions = moLangFunctionMap(
"set_new_level" to {
newLevel = it.getInt(0)
DoubleValue.ONE
}
)
}
@@ -13,6 +13,7 @@ import com.bedrockk.molang.runtime.value.MoValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.api.pokedex.AbstractPokedexManager
import com.cobblemon.mod.common.api.pokedex.FormDexRecord
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
@@ -47,7 +48,9 @@ sealed interface PokedexDataChangedEvent {
override val knowledge: PokedexEntryProgress,
override val playerUUID: UUID,
override val record: FormDexRecord
) : PokedexDataChangedEvent, Cancelable()
) : PokedexDataChangedEvent, Cancelable() {
val functions = moLangFunctionMap(cancelFunc)
}
class Post(
override val dataSource: PokedexEntityData,
@@ -8,8 +8,12 @@
package com.cobblemon.mod.common.api.events.pokemon
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.server
import java.util.UUID
data class PokemonGainedEvent(
@@ -20,4 +24,8 @@ data class PokemonGainedEvent(
"player_id" to StringValue(playerId.toString()),
"pokemon" to pokemon.struct
)
val functions = moLangFunctionMap(
"player" to { server()?.playerList?.getPlayer(playerId)?.asMoLangValue() ?: DoubleValue.ZERO },
)
}
@@ -8,7 +8,10 @@
package com.cobblemon.mod.common.api.events.pokemon.evolution
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.pokemon.evolution.Evolution
import com.cobblemon.mod.common.pokemon.Pokemon
@@ -25,4 +28,11 @@ import com.cobblemon.mod.common.pokemon.Pokemon
data class EvolutionAcceptedEvent(
override val pokemon: Pokemon,
override val evolution: Evolution
) : Cancelable(), EvolutionEvent
) : Cancelable(), EvolutionEvent {
val context = mutableMapOf<String, MoValue>(
"pokemon" to pokemon.struct,
"evolution" to evolution.asMoLangValue()
)
val functions = moLangFunctionMap(cancelFunc)
}
@@ -8,6 +8,8 @@
package com.cobblemon.mod.common.api.events.pokemon.evolution
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.pokemon.Pokemon
@@ -23,4 +25,9 @@ import com.cobblemon.mod.common.pokemon.Pokemon
data class EvolutionCompleteEvent(
override val pokemon: Pokemon,
override val evolution: Evolution
) : EvolutionEvent
) : EvolutionEvent {
val context = mutableMapOf<String, MoValue>(
"pokemon" to pokemon.struct,
"evolution" to evolution.asMoLangValue()
)
}
@@ -8,8 +8,13 @@
package com.cobblemon.mod.common.api.events.pokemon.evolution
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.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.getBoolean
/**
* An event fired when [Evolution.test] has been performed.
@@ -25,4 +30,18 @@ data class EvolutionTestedEvent(
override val evolution: Evolution,
val originalResult: Boolean,
var result: Boolean
) : EvolutionEvent
) : EvolutionEvent {
val context = mutableMapOf<String, MoValue>(
"pokemon" to pokemon.struct,
"evolution" to evolution.asMoLangValue(),
"original_result" to DoubleValue(originalResult),
)
val functions = moLangFunctionMap(
"result" to { DoubleValue(result) },
"set_result" to {
result = it.getBoolean(0)
DoubleValue.ONE
}
)
}
@@ -16,6 +16,9 @@ import java.util.UUID
data class PokemonInteractionGUICreationEvent(
val pokemonID: UUID,
val mountShoulder: Boolean,
val giveHeld: Boolean,
val giveCosmetic: Boolean,
val canRide: Boolean,
val options: Multimap<Orientation, InteractWheelOption>
) {
fun addFillingOption(option: InteractWheelOption) {
@@ -28,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 {
@@ -22,5 +22,5 @@ import net.minecraft.server.level.ServerPlayer
*/
data class WallpaperCollectionEvent(
val player: ServerPlayer,
val wallpapers: MutableList<ResourceLocation>
val wallpapers: MutableSet<ResourceLocation>
)
@@ -0,0 +1,57 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.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]
}
@@ -21,11 +21,13 @@ 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.CobblemonUnlockableWallpapers
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
@@ -36,6 +38,8 @@ 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.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
@@ -44,11 +48,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
@@ -60,17 +64,47 @@ import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimati
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.Species
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolution
import com.cobblemon.mod.common.pokemon.evolution.variants.LevelUpEvolution
import com.cobblemon.mod.common.pokemon.evolution.variants.TradeEvolution
import com.cobblemon.mod.common.util.asArrayValue
import com.cobblemon.mod.common.util.asBlockPos
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.asResource
import com.cobblemon.mod.common.util.asUUID
import com.cobblemon.mod.common.util.blockRegistry
import com.cobblemon.mod.common.util.cloneFrom
import com.cobblemon.mod.common.util.effectiveName
import com.cobblemon.mod.common.util.getBattleState
import com.cobblemon.mod.common.util.getBlockStatesWithPos
import com.cobblemon.mod.common.util.getBooleanOrNull
import com.cobblemon.mod.common.util.getDoubleOrNull
import com.cobblemon.mod.common.util.getIntOrNull
import com.cobblemon.mod.common.util.getIsSubmerged
import com.cobblemon.mod.common.util.getOrNull
import com.cobblemon.mod.common.util.getPlayer
import com.cobblemon.mod.common.util.getStringOrNull
import com.cobblemon.mod.common.util.isInBattle
import com.cobblemon.mod.common.util.isInt
import com.cobblemon.mod.common.util.isStandingOn
import com.cobblemon.mod.common.util.party
import com.cobblemon.mod.common.util.pc
import com.cobblemon.mod.common.util.pokedex
import com.cobblemon.mod.common.util.resolve
import com.cobblemon.mod.common.util.server
import com.cobblemon.mod.common.util.toProperties
import com.cobblemon.mod.common.util.withNPCValue
import com.cobblemon.mod.common.util.worldRegistry
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
@@ -86,6 +120,7 @@ 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.Entity
import net.minecraft.world.entity.EntityType
import net.minecraft.world.entity.LightningBolt
import net.minecraft.world.entity.LivingEntity
@@ -95,13 +130,56 @@ 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.UUID
import kotlin.collections.HashMap
import kotlin.collections.Map
import kotlin.collections.MutableList
import kotlin.collections.MutableSet
import kotlin.collections.any
import kotlin.collections.associate
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.collections.contains
import kotlin.collections.count
import kotlin.collections.emptyList
import kotlin.collections.filter
import kotlin.collections.filterIsInstance
import kotlin.collections.find
import kotlin.collections.flatMap
import kotlin.collections.forEach
import kotlin.collections.forEachIndexed
import kotlin.collections.getOrPut
import kotlin.collections.hashMapOf
import kotlin.collections.isNotEmpty
import kotlin.collections.iterator
import kotlin.collections.listOf
import kotlin.collections.map
import kotlin.collections.mapIndexed
import kotlin.collections.mapNotNull
import kotlin.collections.mapOf
import kotlin.collections.maxOfOrNull
import kotlin.collections.minByOrNull
import kotlin.collections.minOfOrNull
import kotlin.collections.mutableListOf
import kotlin.collections.mutableSetOf
import kotlin.collections.none
import kotlin.collections.random
import kotlin.collections.set
import kotlin.collections.setOf
import kotlin.collections.toList
import kotlin.collections.toMap
import kotlin.collections.toTypedArray
import kotlin.math.sqrt
import kotlin.random.Random
/**
* Holds a bunch of useful MoLang trickery that can be used or extended in API
@@ -222,6 +300,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)
@@ -277,16 +371,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 ->
@@ -300,6 +410,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
}
@@ -311,8 +426,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) }
@@ -406,11 +521,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()) }
@@ -419,48 +562,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 {
@@ -487,11 +605,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("#")
@@ -510,7 +623,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()
}
@@ -567,6 +680,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
}
)
@@ -685,6 +837,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
}
@@ -723,6 +877,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
}
)
@@ -733,8 +915,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
}
)
@@ -742,6 +931,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))
@@ -842,6 +1032,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
}
)
@@ -850,7 +1051,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()) }
@@ -1002,18 +1203,111 @@ object MoLangFunctions {
}
)
val pokemonPropertiesFunctions: MutableList<(PokemonProperties) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
{ props ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("level") { DoubleValue(props.level?.toDouble() ?: 0) }
map.put("set_level") { params ->
props.level = params.getIntOrNull(0)
return@put DoubleValue.ONE
}
map.put("shiny") { DoubleValue(props.shiny) }
map.put("set_shiny") { params ->
props.shiny = params.getBooleanOrNull(0)
return@put DoubleValue.ONE
}
map.put("species") { props.species?.let { StringValue(it) } ?: DoubleValue.ZERO }
map.put("set_species") { params ->
props.species = params.getStringOrNull(0)
return@put DoubleValue.ONE
}
map.put("gender") { props.gender?.let { StringValue(it.name) } ?: DoubleValue.ZERO }
map.put("set_gender") { params ->
props.gender = params.getStringOrNull(0)?.let { Gender.valueOf(it) }
return@put DoubleValue.ONE
}
map.put("form") { props.form?.let { StringValue(it) } ?: DoubleValue.ZERO }
map.put("ivs") {
val ivs = props.ivs
if (ivs == null) {
return@put DoubleValue.ZERO
} else {
return@put ivs.struct
}
}
map.put("evs") {
val evs = props.evs
if (evs == null) {
return@put DoubleValue.ZERO
} else {
return@put evs.struct
}
}
map.put("friendship") { DoubleValue(props.friendship?.toDouble() ?: DoubleValue.ZERO) }
map.put("set_friendship") { params ->
props.friendship = params.getIntOrNull(0)
return@put DoubleValue.ONE
}
map.put("create") { params ->
val pokemon = props.create()
return@put pokemon.struct
}
map
}
)
val evolutionFunctions: MutableList<(Evolution) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
{ evolution ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("result") { evolution.result.asMoLangValue() }
if (evolution is LevelUpEvolution) {
map.put("is_level_up") { DoubleValue.ONE }
} else if (evolution is TradeEvolution) {
map.put("is_trade") { DoubleValue.ONE }
} else if (evolution is ItemInteractionEvolution) {
map.put("is_item") { DoubleValue.ONE }
}
map.put("is_optional") { DoubleValue(evolution.optional) }
map.put("consumes_held_item") { DoubleValue(evolution.consumeHeldItem) }
map
}
)
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
}
@@ -1034,6 +1328,24 @@ object MoLangFunctions {
return value
}
fun PokemonProperties.asMoLangValue(): ObjectValue<PokemonProperties> {
val value = ObjectValue(
obj = this,
stringify = { it.asString() }
)
value.addFunctions(pokemonPropertiesFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
return value
}
fun Evolution.asMoLangValue(): ObjectValue<Evolution> {
val value = ObjectValue(
obj = this,
stringify = { it.toString() }
)
value.addFunctions(evolutionFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
return value
}
fun PCStore.asMoLangValue(): ObjectValue<PCStore> {
val value = ObjectValue(
obj = this,
@@ -1050,6 +1362,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
}
@@ -1060,6 +1373,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
@@ -1095,12 +1409,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())
}
}
}
}
@@ -1125,7 +1445,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 }
@@ -1133,6 +1453,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 }
@@ -1170,6 +1498,24 @@ object MoLangFunctions {
return this
}
fun moLangFunctionMap(
vararg functions: Pair<String, (MoParams) -> MoValue>
): Map<String, (MoParams) -> MoValue> {
return functions.toMap()
}
fun queryStructOf(
vararg functions: Pair<String, (MoParams) -> MoValue>
): QueryStruct {
return QueryStruct(
hashMapOf<String, java.util.function.Function<MoParams, Any>>(
*functions.map { (name, func) ->
name to java.util.function.Function<MoParams, Any> { params -> func(params) }
}.toTypedArray()
)
)
}
fun MoLangRuntime.setup(): MoLangRuntime {
environment.query.addStandardFunctions()
return this
@@ -1224,4 +1570,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)
}
}
@@ -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 {
}
@@ -320,7 +320,7 @@ a single battle challenge and this is a dynamic party, there will also be a `q.p
"pokemon": "weedle",
"weight": "10",
"selectableTimes": "2",
"levelVariation": "2"
"levelVariation": "2",
"npcLevels": "1-8"
},
{
@@ -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 {
@@ -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)
}
}
@@ -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
@@ -8,9 +8,6 @@
package com.cobblemon.mod.common.api.pokemon
import com.bedrockk.molang.runtime.struct.VariableStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.abilities.Abilities
import com.cobblemon.mod.common.api.abilities.Ability
@@ -725,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
@@ -775,17 +772,6 @@ open class PokemonProperties {
return pieces.joinToString(separator)
}
fun asStruct(): VariableStruct {
val struct = VariableStruct()
species?.let { struct.setDirectly("species", StringValue(it)) }
level?.let { struct.setDirectly("level", DoubleValue(it)) }
shiny?.let { struct.setDirectly("shiny", DoubleValue(it)) }
// add more of the optional properties to the struct as doubles or strings
gender?.let { struct.setDirectly("gender", StringValue(it.name)) }
friendship?.let { struct.setDirectly("friendship", DoubleValue(it)) }
return struct
}
fun updateAspects() {
val aspects = mutableSetOf<String>()
AspectProvider.providers.forEach { aspects.addAll(it.provide(this)) }
@@ -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,8 @@ 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
import com.cobblemon.mod.common.api.types.adapters.ElementalTypeAdapter
@@ -47,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.riding.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
@@ -105,7 +107,8 @@ 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()
.enableComplexMapKeySerialization()
@@ -126,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()
@@ -204,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)
}
}
}
}
@@ -281,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))
@@ -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,18 +8,21 @@
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
/**
* Represents an entity that supports riding.
*
* @since 1.5.0
* @since 1.7.0
*/
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>?
}
}
@@ -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.RidingStatDefinitions
import com.cobblemon.mod.common.util.adapters.riding.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.api.riding.stats.RidingStatDefinition
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, RidingStatDefinitions> = mapOf(),
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, RidingStatDefinitions> = buffer.readMap(
{ buffer.readEnum<RidingStat>(RidingStat::class.java) },
{ RidingStatDefinitions.decode(buffer) }
val stats: Map<RidingStat, RidingStatDefinition> = buffer.readMap(
{ 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
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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]!!
}
}
@@ -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)
}
}

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