Compare commits

..
Author SHA1 Message Date
JPAKx4 8c1d30c039 Charizard third person is now centered 2025-11-11 13:38:46 -06:00
Nick be466d1610 Merge branch 'aprijuice-neoforge-crash-fix' into 'main'
Fix Aprijuices crashing on Neoforge

See merge request cable-mc/cobblemon!1957
2025-11-11 11:18:42 -08:00
lionandNick d75faae1a0 Fix Aprijuices crashing on Neoforge 2025-11-11 11:18:42 -08:00
Polymeta 202bee5690 Merge branch 'do-you-get-the-joke' into 'main'
🍆

See merge request cable-mc/cobblemon!1777
2025-11-11 17:03:40 +00:00
Landon 730dc88ede Merge branch 'small-rides-sprint-issue' into 'main'
fix the stuttery walk on large mons when ridden

See merge request cable-mc/cobblemon!1953
2025-11-11 15:17:21 +00:00
Landon 4f0a667f23 Merge branch 'riding-air-transition-improvement' into 'main'
Improve air transition so you can consistently get off the ground

See merge request cable-mc/cobblemon!1954
2025-11-11 15:17:01 +00:00
Landon f1186f469c Merge branch 'motion-sick-camera-changes' into 'main'
Motion Sick Cam

See merge request cable-mc/cobblemon!1948
2025-11-11 15:11:45 +00:00
Jackson BowesandLandon 91eeecf6e4 Motion Sick Cam 2025-11-11 15:11:45 +00:00
Polymeta f6cae9d52e Edit eggantberry.js 2025-11-11 15:08:04 +00:00
Polymeta ed94d76e98 Merge branch '1.7-spawns' into 'main'
Added and updated spawns and tags. Edited berry data scraper.

See merge request cable-mc/cobblemon!1937
2025-11-11 15:04:46 +00:00
Polymeta 6e2b76d6a1 Merge branch 'its-not-bait' into 'main'
Bait Seasoning Correction

See merge request cable-mc/cobblemon!1955
2025-11-11 15:03:28 +00:00
Landon 2b70b222df Merge branch 'non-rolling-block-placement-fix' into 'main'
Fix non rollables arrow spawning and block picking. (building should be much better now)

See merge request cable-mc/cobblemon!1943
2025-11-11 14:51:18 +00:00
Landon e9f0ef6497 Merge branch 'camera-fixes' into 'main'
Camera fixes

See merge request cable-mc/cobblemon!1944
2025-11-11 14:41:24 +00:00
dhi_holo a4dd34235a Bait Seasonings are now also Baits
Added Apple, Gapple, EGapples, Golden Carrots, and Glistering Melon Slices to the bait types
2025-11-11 15:37:08 +10:00
dhi_holo 8501fd3b18 Delete nether_star.json
Dev tester or something?
2025-11-11 14:36:19 +10:00
dhi_holo 52896b208e Bait Seasoning Correction
You can no longer cook Nether Stars into food, but you can Apples!
2025-11-11 14:28:43 +10:00
jackowes 6ad6487dbe Gave a bit more time to double jump and now boost the y velocity to a minimum of 0.4 on transition. 2025-11-10 21:30:37 -06:00
jackowes 703ccb9d8e revert uneeded stuff 2025-11-10 19:34:53 -06:00
jackowes c33d4b591e fix the stuttery walk on large mons when ridden. The ridevelocity keeps going to zero at some point and I have no idea why. 2025-11-10 18:32:55 -06:00
Landon e31f2a2598 Merge branch 'small-rides-sprint-issue' into 'main'
Fix small mounts erratically stopping their sprints

See merge request cable-mc/cobblemon!1952
2025-11-10 23:37:03 +00:00
jackowes 513c958da1 Move sprint logic to custom tracking rather than using player sprint flag (odd interactions with short mounts) 2025-11-10 15:47:39 -06:00
Polymeta 9e1fde04ef Merge branch 'mopping-up-tags' into 'main'
Tags and Seasonings Improvement

Closes #1765 and #1764

See merge request cable-mc/cobblemon!1949
2025-11-10 09:30:10 +00:00
dhi_holo 6e4565d49b Update eggant_sprout.json
Missed a spot that didn't show up properly
2025-11-10 16:10:13 +10:00
dhi_holo 91f1523ef5 Update eggant_sprout.json
apparently the model didn't get updated properly
2025-11-10 15:59:09 +10:00
dhi_holo e949626f06 Update eggant_sprout.png
Mipmap Fix Compat
2025-11-10 15:26:32 +10:00
dhi_holo 9678fcc009 Merge branch 'main' into do-you-get-the-joke 2025-11-10 15:24:38 +10:00
dhi_holo 4fbe3e406d Merge branch 'main' into mopping-up-tags 2025-11-10 15:22:21 +10:00
dhi_holo 00a1e77a9f Update pitcher_plant.json
Buffed it
2025-11-10 15:20:28 +10:00
dhi_holo e055dede73 Number fix
Forgot it's in ticks not seconds.
2025-11-10 15:14:14 +10:00
Test 98cfa0ac91 Merge branch 'mip-map-fix' into 'main'
Mip map fix

See merge request cable-mc/cobblemon!1707
2025-11-10 03:47:48 +00:00
DeltricandTest f2441e7b27 Mip map fix 2025-11-10 03:47:48 +00:00
dhi_holo 46c247c742 Merge branch 'main' into mopping-up-tags 2025-11-10 13:28:49 +10:00
Paul 3d99db64b9 Double the spawning speed for poke snack blocks 2025-11-10 12:39:10 +10:00
Paul 4f07746355 Fix poor failure handling in behaviours on NPCs. Closes #1772 2025-11-10 11:58:00 +10:00
Hiroku 2f0a201cf1 Merge branch 'aprijuice-color-crash' into 'main'
Aprijuice Color Crash

See merge request cable-mc/cobblemon!1946
2025-11-09 22:46:23 +00:00
Gertimoshka 778f24acf1 Fixed a bug where if there is no flavours in aprijuice, it would crash when rendered 2025-11-09 22:32:07 +02:00
jackowes aa9c7b4f55 Fix non rollables arrow spawning and block picking. (building should be much better now) 2025-11-09 10:39:13 -06:00
JPAKx4 96e2e4a6fe Merge remote-tracking branch 'origin/camera-fixes' into pivot
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/camera/MountedCameraRenderer.kt
2025-11-09 06:58:34 -06:00
JPAKx4 20d903cffe Fix some camera code that got yeeted in the cleanup. 2025-11-09 06:57:33 -06:00
JPAKx4 d4383fa317 Fix some camera code that got yeeted in the cleanup. 2025-11-09 06:47:41 -06:00
Paul 1687fa12db Changelog [skip-ci] 2025-11-09 22:11:19 +10:00
Hiroku cf599de339 Merge branch 'YetMorePathingImprovement' into 'main'
YetMorePathingImprovements

Closes #1754

See merge request cable-mc/cobblemon!1942
2025-11-09 12:09:38 +00:00
Hiroku c5f92c1ad4 Merge branch 'party-ui-overhaul' into 'main'
Implementation of Party Overlay UI Animations for different Events

See merge request cable-mc/cobblemon!1939
2025-11-09 12:08:38 +00:00
Paul 1d99da78e0 Updated the behaviour scraper 2025-11-09 21:35:33 +10:00
dhi_holo 050a2a6a4b Seasoning Fixes
Resolves #1765
Partially Addresses #1766
2025-11-09 15:25:02 +10:00
dhi_holo c80e7f4a51 Update leaves.json
Corrects IDs
Resolves #1764
2025-11-09 15:11:51 +10:00
dhi_holo f4fbdf8735 Block Tag Improvements
Added some overlooked pieces to certain tags
2025-11-09 15:03:43 +10:00
dhi_holo f2aa4141c0 Merge branch 'main' into do-you-get-the-joke 2025-11-09 14:04:39 +10:00
Landon aeded69b25 Merge branch 'implement_pokemon' into 'main'
Merge species updates

See merge request cable-mc/cobblemon!1940
2025-11-09 02:59:41 +00:00
dhi_holo 16b25a2e6e Tag Updates
Now matches to main
2025-11-09 12:57:52 +10:00
dhi_holo e66ebfb7a8 Merge branch 'main' into do-you-get-the-joke 2025-11-09 12:56:25 +10:00
JazzMcNade 70e79e6fe4 Fix a case in which trimpath would result in a mon pathing into a solid block. Fixed NPCs getting stuck on specific upward diagonal formations 2025-11-08 20:20:47 -06:00
Hiroku 797e0704e9 Merge branch 'new-aprijuice-point-formula' into 'main'
Aprijuice formula rework and don't allow poke bait to stack otherwise fishing combines baits and that's bad

See merge request cable-mc/cobblemon!1938
2025-11-09 00:24:56 +00:00
Carmendar 0b184a722b Cutiefly and Ribombee scale adjustments. 2025-11-08 18:17:35 -06:00
Carmendar 1a34c2942f Cutiefly line remodels and Charizard zfighting fix. 2025-11-08 17:29:16 -06:00
Carmendar 2a20398b42 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-11-08 15:25:42 -06:00
Carmendar 3931295086 Merge branch 'main' into implement_pokemon 2025-11-08 14:18:46 -06:00
violet 550e579190 e 2025-11-08 06:03:40 -05:00
violet e7d710e0a3 implemented poltchageist & sinistcha remodels (+ tweaks to polteageist) 2025-11-08 06:02:08 -05:00
violet acb86b1935 implemented dratini, dragonair, and dragonite remodels 2025-11-08 05:50:42 -05:00
Carmendar fd8e9b49b5 Adjusted some ride stats. 2025-11-08 03:07:34 -06:00
Landon Wills 1fdbee91df Run scraper 2025-11-08 03:10:01 -05:00
Gertimoshka bfab1d7070 Actually Fix the Merge Conflicts 2025-11-07 16:41:39 +02:00
Gertimoshka e61e1390cb Fix Merge Conflicts 2025-11-07 14:26:09 +00:00
Gertimoshka 18151ff2f0 Implemented Levelup Sounds 2025-11-07 16:04:17 +02:00
Paul d5b1dfa9da no wait don't do that 2025-11-07 22:34:26 +10:00
Paul 9f0ddf4dd3 Put the bucket multiplication back onto fishing since I think I forgor to put this back on 2025-11-07 22:07:41 +10:00
Paul ef3ea62ae0 Aprijuice formula rework and don't allow poke bait to stack otherwise fishing combines baits and that's bad 2025-11-07 21:46:20 +10:00
Polymeta 6b43bf9f48 Merge branch 'main' into '1.7-spawns'
# Conflicts:
#   common/src/main/resources/data/cobblemon/spawn_pool_world/0194_wooper.json
#   common/src/main/resources/data/cobblemon/tags/worldgen/biome/is_mirage_island.json
#   utilityscripts/cobblemon_spawn_csv_to_json.py
2025-11-07 11:13:38 +00:00
Veraxiel a4012895f3 Added and updated spawns and tags. Edited berry data scraper. 2025-11-07 04:32:20 -05:00
FrankTheFarmer 7f6a3e8e87 Retweaked Alolan Golem's profile because the profile's respect aspects again. Yippee 2025-11-07 19:06:56 +10:00
FrankTheFarmer 7a84ea723f Merge branch 'main' into implement_pokemon 2025-11-07 18:53:53 +10:00
FrankTheFarmer 2aad33ded5 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-11-07 18:45:53 +10:00
Carmendar 254c95fa15 Rocket Expression changes. 2025-11-07 02:14:07 -06:00
Carmendar 5aab7f67e1 Ride anims for Lapras and Glide/Dive anims for Salamence. - surfacewater poser tweaks for some pokemon. - Tyrantrum crash fix. 2025-11-07 02:12:46 -06:00
Polymeta ae4decacc7 Merge branch 'bottle-o-knowledge' into 'main'
Added Unlock Condition

See merge request cable-mc/cobblemon!1936
2025-11-07 07:01:59 +00:00
dhi_holo ae5a9be626 Added Unlock Condition
Elixirs, Ethers, Potions, Super Potions, and Hyper Potions now unlock from having the berry or Medicinal Brew, rather than just the berry
2025-11-07 16:11:11 +10:00
Landon 05faea4ec8 Merge branch 'no-riding-suffocation' into 'main'
Prevent suffocation when riding a pokemon

See merge request cable-mc/cobblemon!1935
2025-11-07 05:09:05 +00:00
Landon Wills 05edd34db6 Prevent suffocation when riding a pokemon 2025-11-06 10:54:39 -05:00
Landon c198f8fccd Merge branch 'implement_pokemon' into 'main'
A more polished world...

See merge request cable-mc/cobblemon!1930
2025-11-06 14:37:53 +00:00
Landon Wills 521f9aaafa Scrape files 2025-11-06 08:52:29 -05:00
Hiroku 8d38505993 Merge branch 'SaccTreeFixes' into 'main'
SaccTreeFixes

See merge request cable-mc/cobblemon!1932
2025-11-06 12:20:44 +00:00
Carmendar 7c5fab02a8 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-11-06 01:53:24 -06:00
Carmendar 7e5a267c89 Dolphins & Submarines ready for business. - Mime Jr shoulder mount anims. - Various seats and poser tweaks. 2025-11-06 01:52:51 -06:00
Landon 5b23b0cfbe Merge branch 'pondering-my-orb-and-not-crashing' into 'main'
Fix a crash with Ponder and likely others in CameraMixin

See merge request cable-mc/cobblemon!1933
2025-11-05 13:52:17 +00:00
Nadja Reitzenstein 778ceff183 Fix a crash with Ponder and likely others in CameraMixin
Our setRotation mixin assumes the camera to be attached to an entity. It's not always, and it's not for Ponder Scenes specifically.
2025-11-05 13:57:36 +01:00
Paul f32050c8b5 Added setter to player spawner mixin and opened PlayerSpawner 2025-11-05 21:40:09 +10:00
Hiroku 3522bac72a Merge branch 'smeargle-paint-me-like-one-of-your-french-mooshtanks' into 'main'
Smeargle paint me like one of your french mooshtanks

See merge request cable-mc/cobblemon!1746
2025-11-05 11:24:27 +00:00
Paul ecf037666f Merge remote-tracking branch 'weblate/lang-updates' 2025-11-05 21:13:23 +10:00
Watuneni iteiteandWeblate 618d062ca9 Translated using Weblate (Japanese)
Currently translated at 93.5% (8334 of 8908 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-11-05 11:13:04 +00:00
Paul 49841b40ec Merge remote-tracking branch 'weblate/lang-updates' 2025-11-05 21:10:49 +10:00
Paul 87141e1137 Variable naming cleanup 2025-11-05 21:09:18 +10:00
Paul 1f962d664f Removed inaccurate comment 2025-11-05 21:08:50 +10:00
Paul 09797f25b1 Added characteristic_rainbow molang function to pokemon behaviour object 2025-11-05 21:08:31 +10:00
Paul 3911413c69 Removed unused lang function 2025-11-05 21:08:09 +10:00
violet e075e60231 tea time 2025-11-05 05:35:57 -05:00
violet acdd98573b Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-11-05 05:27:35 -05:00
Paul 12f3e7b78b Fix party renders not respecting aspects 2025-11-05 19:01:24 +10:00
violet a462fe5efc added raboot.json poser 2025-11-05 03:43:26 -05:00
Carmendar 2390662fc8 A few submarines and poser/anim tweaks for Dusknoir, Bastiodon and Magnezone. Added surface water ride sounds for Aqua Tauros. 2025-11-05 02:33:26 -06:00
Carmendar 1bd5d2372f Merge branch 'refs/heads/main' into implement_pokemon
# Conflicts:
#	common/src/main/resources/data/cobblemon/species/generation4/garchomp.json
2025-11-05 01:20:44 -06:00
Landon Wills 12f6bb55c2 Fix goof 2025-11-05 02:05:32 -05:00
Landon fdb37ba5e8 Merge branch 'dolphin-sub-noPuke' into 'main'
submarine, dolphin rework, projectile support, view vector fix, and motion sick cam

See merge request cable-mc/cobblemon!1929
2025-11-05 06:56:07 +00:00
Jackson BowesandLandon 7a99845d0f submarine, dolphin rework, projectile support, view vector fix, and motion sick cam 2025-11-05 06:56:06 +00:00
JazzMcNade 54d183c8d3 Merge branch 'main' into SaccTreeFixes 2025-11-04 23:08:44 -06:00
JazzMcNade 29713bbe1f Update SaccharineTreeFeature.kt
natural sacc trees have 20% chance to generate a hive.
post generation sacc trees with nearby flowers have 5% chance to generate a hive.
Hives now always generate adjacent to a log block.
No longer records flower positions.
2025-11-04 22:56:43 -06:00
Landon 247052ee67 Merge branch 'moonrise-compat' into 'main'
Retain moonrise compatibility for Cobblemon 1.7

See merge request cable-mc/cobblemon!1926
2025-11-05 03:11:05 +00:00
FrankTheFarmer 14b7e93965 Tweaked Geodude's look range + poser tweaks 2025-11-05 09:24:32 +10:00
FrankTheFarmer 85a4793a1e formatting and cry fix for Graveler and Golem posers 2025-11-05 09:23:32 +10:00
Veraxiel 29f6524c49 Fix to formatting in TimeRange.kt 2025-11-04 17:50:24 -05:00
Veraxiel 1e617a684b Minor edit to a time range in TimeRange.kt 2025-11-04 17:49:42 -05:00
Paul c9a0352a7c Nerfed aprijuice stat point gain, tweak saccharine tree top leaf gen 2025-11-05 06:40:51 +10:00
Gertimoshka 1a5808e5a6 Update Licenses 2025-11-04 21:57:53 +02:00
Gertimoshka f14d242907 Fix a crash in code for party overlay 2025-11-04 21:52:50 +02:00
Sterrezo 4998ee2c5b Added levelup sounds 2025-11-04 20:25:19 +01:00
violet 015402d54b Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-11-04 05:24:23 -05:00
violet 6f697d2b9a solosis line poser tweaks 2025-11-04 05:23:29 -05:00
violet f18752d484 haxorus model + poser tweaks 2025-11-04 04:53:53 -05:00
violet 5876c02d44 unown animations 2025-11-04 04:28:56 -05:00
violet c890f6910c a lot of poser alterations 2025-11-04 04:27:24 -05:00
Paul d157536e1e party pokemon walk again woops 2025-11-04 18:54:29 +10:00
Carmendar dac2d9e96e Mimikyu animations & ride style changes. - Updated changelog 2025-11-04 01:59:01 -06:00
Landon Wills 5c71680db9 Run scraper 2025-11-04 00:26:13 -05:00
Carmendar 5f5c036f8d Merge branch 'main' into implement_pokemon 2025-11-03 23:14:21 -06:00
Carmendar 1f16c2d542 Updated dewgong anim file. 2025-11-03 23:12:50 -06:00
JazzMcNade 2da9dc50f7 Fixed creative duped beehives creating combees with duplicate UUIDs 2025-11-03 21:30:57 -06:00
Carmendar 092f9ef9f9 Updated Gyarados fly pose. 2025-11-03 20:03:18 -06:00
Landon Wills 2f12b05d43 Run scraper 2025-11-03 19:48:32 -05:00
Sterrezo 4299aff1bf Added water ride loop sounds and updated the wind loop 2025-11-03 16:57:05 +01:00
Paul fb90ccb86e Fixed dismount not working for non-owner passengers if they have no Pokemon 2025-11-03 22:08:12 +10:00
Paul c112f9615c Merge remote-tracking branch 'origin/main' 2025-11-03 21:42:57 +10:00
Paul fc882190fe /ride no longer works on seatless pokemon, closes #1750 2025-11-03 21:39:48 +10:00
Paul 15d7cd815d Only handle mount transition camera logic for the actual client player. Probably doesn't fix anything? 2025-11-03 21:23:35 +10:00
violet 6b5969fff6 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-11-03 03:33:13 -05:00
Carmendar 5053c633b7 Grafaiai paint/saliva colors 2025-11-03 02:22:33 -06:00
violet c4294697e6 implemented remodels for scorbunny, raboot, and cinderace 2025-11-03 03:16:37 -05:00
Carmendar dfa1c5e369 Orthworm sand_idle fix 2025-11-03 01:59:46 -06:00
Carmendar 51f3b4357d Implemented Remodels for Steelix and Shroodle line. 2025-11-03 01:59:08 -06:00
Carmendar 220a9741ae Implemented Mime Jr animations. 2025-11-03 01:58:15 -06:00
Carmendar 48a9446f73 Implemented the Vanillite line. 2025-11-03 01:56:52 -06:00
violet f1edfc453e minor adjustments to shuppet's poser 2025-11-03 01:18:40 -05:00
FrankTheFarmer 8d612ec395 Changelog of hatred and anguish 2025-11-03 15:01:03 +10:00
FrankTheFarmer 6d9daf3cf6 Stupid fish's mama 🔴⚪ 2025-11-03 15:00:26 +10:00
FrankTheFarmer c7ecfc74de Stupid fish 🔴🔵⚪ 2025-11-03 14:26:02 +10:00
Hiroku fefdfd877b Merge branch 'let-me-/ride' into 'main'
Fix /ride

Closes #1751

See merge request cable-mc/cobblemon!1927
2025-11-03 02:35:18 +00:00
Landon Wills 35fb170225 Fix /ride 2025-11-02 20:50:39 -05:00
Paul d2e405131f Fixed speculative cast in pokedex FOV multiplier. Closes #1756 2025-11-03 11:37:00 +10:00
Paul b6ec0205ef Fixed NPC gibber sounds playing even when text was skipped. Closes #1755 2025-11-03 11:36:20 +10:00
Paul e57a42e755 Fixed immobile pokemon walking to follow the player. Closes #1757 2025-11-03 11:25:46 +10:00
Carmendar f34a03c8d6 Implemented Carvanha and Sharpedo animations - removed surfacewater swimming from Charizard and Arcanine. 2025-11-02 17:28:22 -06:00
Carmendar 8b1599705f Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-11-02 13:35:26 -06:00
Carmendar e7f6fc32c4 Implemented Arcanine Animations! 2025-11-02 13:34:39 -06:00
Landon Wills 5119617f00 Check for moonrise and disable conflicting mixin if loaded 2025-11-02 09:39:33 -05:00
Paul 6e84efb53c No Packed Up backpacks either. Your move, Monocle 2025-11-02 22:41:11 +10:00
Paul de3191b768 Some english improvements on new NPC Behaviours 2025-11-02 22:06:27 +10:00
Paul 76ec4b4d5a Merge remote-tracking branch 'origin/main' 2025-11-02 21:34:42 +10:00
Hiroku 25702ade61 Merge branch 'main' into 'main'
dialogue selection interaction and name tag display - done properly this time

See merge request cable-mc/cobblemon!1925
2025-11-02 11:34:19 +00:00
Paul c6da88d0b2 Improvements to GUI pokemon animation config options, pokebait effects, setup some berry gen stuff for vera 2025-11-02 21:32:18 +10:00
violet c4acc09fbd Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-11-02 03:49:32 -05:00
violet 53b434906b altered various starter pokemon portraits, profiles, and scales 2025-11-02 03:49:22 -05:00
Jolihan b22a31909d Update file en_us.json 2025-11-02 08:09:19 +00:00
Jolihan cbbbacc215 Edit name_tag_display.json 2025-11-02 07:01:40 +00:00
Jolihan566 d22d81209d dialogue selection interaction and name tag display - done properly this time 2025-11-02 16:59:21 +10:00
Carmendar 89b989c46c Updated Changelog 2025-11-02 01:42:05 -05:00
Carmendar 714d51b26e Implemented Tadbulb and Bellibolt animations 2025-11-02 01:35:27 -05:00
Hiroku 8571d78894 Merge branch 'FixGreedyHoneyEaters' into 'main'
Fixed Greedy Honey Eaters

Closes #1725

See merge request cable-mc/cobblemon!1920
2025-11-02 05:54:22 +00:00
JazzMcNade 72c57fde39 Sacc tree spawned beenests are now populated with either Minecraft bees or combees 2025-11-01 21:58:15 -05:00
Carmendar fc5c479f31 two small fixes 2025-11-01 21:48:58 -05:00
Carmendar d3c54924be no more blinky bronzong 2025-11-01 20:57:25 -05:00
Paul 65c5e95ddf Fixed possible crash from bees putting honey in saccarine leaves 2025-11-02 11:56:27 +10:00
Carmendar fc1dec6b4b Shellos & Gastrodon cries implemented 2025-11-01 20:53:45 -05:00
Carmendar 7604af9308 Shellos & Gastrodon cries 2025-11-01 20:48:16 -05:00
Carmendar ab1e138ace Koffing and Weezing animations. 2025-11-01 19:25:38 -05:00
Carmendar 62ee94c71d Bronzong and Camerupt animations - More poser tweaks. 2025-11-01 19:06:26 -05:00
Nadja Reitzenstein 0fae95aabc Reorder smeargle's tail colours to allow handing them cosmetic dyes
Our aspect system has the limitation that you can not easily have two separate systems add mutually exclusive aspects correctly. This means that for cosmetics to work without much duplication, the characteristic rainbow has to set a different aspect with some (but much more limited) duplication in the assignment of textures.
2025-11-01 19:55:55 +01:00
Nadja Reitzenstein bbfd864b33 Clean up Rainbow Aspect provider 2025-11-01 18:43:31 +01:00
Nadja Reitzenstein b107d76762 Cosmetic Item dye affects smeargle tails now 2025-11-01 17:37:04 +01:00
Nadja Reitzenstein 8d4f10349c Use the easier to understand maxBy call 2025-11-01 17:25:23 +01:00
Nadja Reitzenstein f36787e67f Clean up Characteristic calculation
Also ensures characteristics are recalculated on IV updates.
2025-11-01 16:42:59 +01:00
Nadja Reitzenstein fc198c599e Update CHANGELOG.md 2025-11-01 16:23:12 +01:00
Nadja Reitzenstein 26f753880c Implement rotating a feature on lightning hit more sensibly 2025-11-01 16:23:12 +01:00
Nadja Reitzenstein a9ebb0b6f2 Merge thunderHit method for Pokemon entity 2025-11-01 16:23:12 +01:00
Nadja Reitzenstein a7158ee57a Rainbow aspected Smeargle 2025-11-01 16:23:12 +01:00
Nadja Reitzenstein 06c3fd3f09 Make lightning damage only not apply if there was an actual aspect change. 2025-11-01 16:23:12 +01:00
Nadja Reitzenstein 4e196e8a29 Generalise switching aspect behaviour on lightning hit 2025-11-01 16:23:12 +01:00
Nadja Reitzenstein 871f33f442 Make mooshtank switch colour on lightning hit 2025-11-01 16:23:12 +01:00
Polymeta 21c1f912a7 Merge branch 'typo' into 'main'
Update crops.json

See merge request cable-mc/cobblemon!1924
2025-11-01 10:16:37 +00:00
Carmendar a0d9ce12a6 Implemented animations for Mr. Mime and Relicanth - poser/anim tweaks for Dragonite. 2025-11-01 03:59:05 -05:00
Carmendar c07173fc0f Merge branch 'main' into implement_pokemon 2025-11-01 01:54:43 -05:00
violet 3faf8ac302 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-11-01 01:21:23 -04:00
violet e85b3f3cc5 implemented shuppet and banette 2025-11-01 01:19:52 -04:00
RedGenesectNinja 251a09f78c Implemented Stunfisk and Galarian Stunfisk 2025-10-31 23:43:46 -05:00
dhi_holo 776fb5413f Update crops.json
Apparently missed this file while updating stuff? I swear I didn't but it must not've commited
2025-11-01 14:36:45 +10:00
Paul f645fd9b60 Added support for species-specific move action effects 2025-11-01 11:44:05 +10:00
Landon ac161ae5b4 Merge branch 'orientation-refactor' into 'main'
Refactor orientation to better allow for passengers on rolling rides.

See merge request cable-mc/cobblemon!1910
2025-11-01 01:19:29 +00:00
Jackson BowesandLandon fc3542e3eb Refactor orientation to better allow for passengers on rolling rides. 2025-11-01 01:19:28 +00:00
Gertimoshka 7c44dd204e Remove Old Notifications of Level Up, EXP Gained, and Moves Learned 2025-10-31 15:13:53 +02:00
Carmendar eca14f61ac Various mount style changes - prep work for Submarine, and removed mountability from 5 pokemon. 2025-10-31 02:35:15 -05:00
Carmendar 9ccf2bb9ad Heracross anims, poser fixes, removed land mount style from select pokemon. 2025-10-31 02:33:30 -05:00
Kenji_64 eacb0fe587 Forgot the changelog 2025-10-30 13:27:23 +01:00
Kenji_64 1752588cbc New scales for Dreepy and Drakloak 2025-10-30 13:22:48 +01:00
Kenji_64 9e09ac52fc Added animations + ride stats to Dragapult! 2025-10-30 13:22:25 +01:00
Kenji_64 845ade1aeb Added cries to Alolan Vulpix and Ninetales 2025-10-30 11:28:53 +01:00
Carmendar f6bd04f016 Implemented Darmanitan, Gastrodon, Orthworm & Salamence anims - Switched Tentacruel to json poser - fixed minor camera and poser issues. 2025-10-30 02:41:42 -05:00
Landon Wills afa1d9af8b Update riding scraper, scrape pokemon 2025-10-30 03:15:02 -04:00
Landon Wills bf6db8b745 Merge branch 'main' into implement_pokemon 2025-10-30 00:58:03 -04:00
Gertimoshka 9aef7b9c3b Party Notifications Will Override and Stack if triggered too fast (feeding exp candy as an example) and now pokemon level text will change during animation instead instantly 2025-10-29 16:28:00 +02:00
Carmendar c5bebb1abb Hydreigon geo fix
(I goofed up)
2025-10-29 03:03:12 -05:00
Carmendar 990fed2fe0 The great Camera polishing event of '25. 2025-10-29 02:51:02 -05:00
Carmendar fe77a3cd30 Implemented new Charizard, Staraptor, Dusknoir & Forretress animations, fixed Altaria not using some anims, adjusted floating seat locators.
Also fixed Will O' Wisp particle.
2025-10-29 02:34:02 -05:00
Gertimoshka de9e1949fe Do actually implement all of the animations for party overlay level up 2025-10-29 08:06:14 +02:00
Nico MerenqandWeblate f75e3d11da Translated using Weblate (Russian)
Currently translated at 63.6% (5669 of 8908 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-10-28 08:50:20 +00:00
Nico MerenqandWeblate 3439aeff1e Translated using Weblate (Russian)
Currently translated at 63.5% (5662 of 8908 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-10-27 21:50:57 +00:00
Nico MerenqandWeblate 2ada7e0b68 Translated using Weblate (Russian)
Currently translated at 63.5% (5659 of 8908 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-10-26 06:57:00 +00:00
JazzMcNade 7f6197ab8b Honey slathered logs no longer activated multiple time on the same pokemon, and ignore pokemon that already have their hidden ability. 2025-10-25 14:52:51 -05:00
Carmendar 63143832e0 Chinchilla implementation. 2025-10-25 02:53:59 -05:00
Carmendar 29215f5c0c Merge branch 'refs/heads/main' into implement_pokemon 2025-10-25 02:48:22 -05:00
dhi_holo 70bf3e7805 Merge branch 'main' into do-you-get-the-joke 2025-10-25 17:12:40 +10:00
Gertimoshka b4d9d1b963 First working itteration of Popups and EXP Bar animations for level up, new moves and new evos 2025-10-25 09:42:26 +03:00
Carmendar cf5e1bf142 ride stat adjustments and Blastoise anim implementation. 2025-10-24 02:20:43 -05:00
Nico MerenqandWeblate 4b8196685a Translated using Weblate (Russian)
Currently translated at 63.4% (5653 of 8908 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-10-24 04:12:32 +00:00
Carmendar d1053468bf Adjusted first person mount view for the paldean tauros variants. 2025-10-23 19:22:54 -05:00
Carmendar f0c41865db Adjusted first person mount view for most mons. 2025-10-23 19:19:36 -05:00
Carmendar 2b2264bdca Updated Boat expressions 2: Electric Boogaloo 2025-10-22 19:13:09 -05:00
Carmendar 1e32f7b80a Updated Boat expressions 2025-10-22 17:19:52 -05:00
Carmendar cfc8edfa9e Ride stats. 2025-10-22 01:40:12 -05:00
Carmendar 417b49beda Hover speed expression increase and updated Metagross Poser. 2025-10-22 01:21:41 -05:00
FrankTheFarmer a2f6a35227 Changelog 2025-10-22 11:11:02 +10:00
FrankTheFarmer b9f1cc5883 Added handmade placeholders for ❤Corphish💖 and 🤷‍♂️Crawdaunt🦞 2025-10-22 11:07:02 +10:00
FrankTheFarmer ad132fe729 Made Corphish and Crawdaunt sink and walk in water 2025-10-22 11:03:29 +10:00
FrankTheFarmer f33892fcd1 Merge branch 'main' into implement_pokemon 2025-10-22 11:01:54 +10:00
Carmendar 2a56709395 Merge branch 'main' into implement_pokemon 2025-10-21 17:57:11 -05:00
TeggyandWeblate 08c2a0d07b Translated using Weblate (Spanish)
Currently translated at 85.0% (7578 of 8908 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-10-20 20:24:26 +00:00
Nico MerenqandWeblate 1f68fdac8c Translated using Weblate (Russian)
Currently translated at 63.4% (5649 of 8908 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-10-20 17:26:47 +00:00
TeggyandWeblate e8d4f0fcf4 Translated using Weblate (Spanish)
Currently translated at 84.7% (7546 of 8908 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-10-20 17:26:45 +00:00
阿鱼andWeblate 5d522bd608 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (8908 of 8908 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-10-20 05:04:02 +00:00
Carmendar 35436c1d09 Dewott no longer sleeps in the void. 2025-10-19 23:26:07 -05:00
Carmendar d66793d186 Merge branch 'main' into implement_pokemon 2025-10-18 16:03:03 -05:00
Carmendar cc00962752 Swadloon do be schmoovin (properly this time) 2025-10-18 15:58:41 -05:00
Watuneni iteiteandWeblate 3894a99bc4 Translated using Weblate (Japanese)
Currently translated at 93.5% (8334 of 8908 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-10-17 18:40:49 +00:00
Carmendar 3a83ed424f Swadloon do be schmoovin. 2025-10-17 01:46:39 -05:00
Carmendar cbccc612a4 Spewpa learned how to walk. 2025-10-16 02:07:51 -05:00
Carmendar 63fd5d90c1 Merge branch 'main' into implement_pokemon 2025-10-15 23:42:59 -05:00
Carmendar 92336d6107 Implemented Aerodactyl remodel. 2025-10-15 23:37:00 -05:00
Carmendar f3f29ca0ea sky protector remodel. 2025-10-15 02:02:01 -05:00
Carmendar 2544cbc252 Cosmetics for Alola-bias Pichu and Pikachu, and cosmetics for Alolan Raichu. 2025-10-14 23:53:32 -05:00
阿鱼andWeblate 9036a2f317 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (8908 of 8908 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-10-13 12:31:47 +00:00
阿鱼andWeblate c75ab28002 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 99.3% (8853 of 8908 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-10-13 06:00:42 +00:00
Nico MerenqandWeblate 62a8320068 Translated using Weblate (Russian)
Currently translated at 63.2% (5635 of 8908 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-10-11 20:53:16 +00:00
Watuneni iteiteandWeblate f21b38f66a Translated using Weblate (Japanese)
Currently translated at 93.1% (8300 of 8908 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-10-11 20:53:15 +00:00
阿鱼andWeblate 596765bb52 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 98.6% (8791 of 8908 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-10-11 06:00:06 +00:00
Watuneni iteiteandWeblate 297aafba45 Translated using Weblate (Japanese)
Currently translated at 92.9% (8282 of 8908 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-10-11 06:00:04 +00:00
Hiroku 28887d9116 Merge branch 'do-you-get-the-joke' into 'do-you-get-the-joke'
Eggant Effect Data Pack

See merge request cable-mc/cobblemon!1893
2025-10-08 13:02:28 +00:00
KyozennoandHiroku 6ef1bac1d7 Eggant Effect Data Pack 2025-10-08 13:02:27 +00:00
Watuneni iteiteandWeblate a0a682e224 Translated using Weblate (Japanese)
Currently translated at 92.3% (8227 of 8908 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-10-06 12:15:07 +00:00
Nico MerenqandWeblate d161b11892 Translated using Weblate (Russian)
Currently translated at 63.1% (5627 of 8908 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-10-04 22:39:35 +00:00
YasitoandWeblate d70bb243b2 Translated using Weblate (French)
Currently translated at 100.0% (8908 of 8908 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-10-02 18:49:12 +00:00
YasitoandWeblate 301a3efb7c Translated using Weblate (French (Canada))
Currently translated at 100.0% (8908 of 8908 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-10-02 18:49:11 +00:00
YasitoandWeblate 05b7cbe20a Translated using Weblate (French)
Currently translated at 100.0% (8908 of 8908 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-10-02 18:38:58 +00:00
YasitoandWeblate 48cbbc1bef Translated using Weblate (French (Canada))
Currently translated at 100.0% (8908 of 8908 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-10-02 18:38:57 +00:00
YasitoandWeblate f8f7c28274 Translated using Weblate (French)
Currently translated at 99.8% (8895 of 8908 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-10-02 17:13:57 +00:00
YasitoandWeblate 0ebb53888a Translated using Weblate (French (Canada))
Currently translated at 99.8% (8891 of 8908 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-10-02 17:13:56 +00:00
YasitoandWeblate c0071c1525 Translated using Weblate (French)
Currently translated at 99.5% (8868 of 8908 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-10-02 14:53:46 +00:00
YasitoandWeblate 71f7b3b3d8 Translated using Weblate (French (Canada))
Currently translated at 99.2% (8841 of 8908 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2025-10-02 14:53:45 +00:00
Watuneni iteiteandWeblate bd6d7b7149 Translated using Weblate (Japanese)
Currently translated at 91.1% (8116 of 8908 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-10-01 00:25:05 +00:00
Alexis SalgueiroandWeblate 63de8fe44a Translated using Weblate (French)
Currently translated at 99.5% (8868 of 8908 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-09-29 18:00:45 +00:00
dhi_holo 95399a32a8 Tweaks
Fixed the advancements breaking (whoops) and lowered the berry in soil
2025-08-16 18:10:11 +10:00
dhi_holo 7089ff68d4 🍆
That's it, that's the addition

Things that need finishing:
- Bait/Cooking Effects
- Battle Effects (Eggant Berry doesn't exist within our Showdown repo, which it seems to need)
2025-08-16 17:09:02 +10:00
1120 changed files with 177485 additions and 34937 deletions
+62 -2
View File
@@ -49,11 +49,15 @@
- Berries can now be smelted into their respective dyes.
- Added Syrupy Apples.
- Added `/runmolang <molang> [<npc>|<player>|<pokemon>]` command that executes a MoLang expression with the provided options as environment variables, as well as the entity (as `q.entity`) that executed the command.
- Added new 69th berry, Eggant.
- Added bubble quirk to Kingler; like Krabby, Kingler will blows bubbles during dusk.
- Added `.Pre` and `.Post` to the following events:
- `PokemonRecallEvent`
- `TradeEvent`
- Added a new gamerule, 'healerHealsPC', when set to true a successful use of a healer will also heal all of the Pokemon in that player's PC.
- Mooshtanks will switch between their red and brown variant when hit by lightning
- Smeargle spawns with differing tail colour depending on its [Characteristic](https://bulbapedia.bulbagarden.net/wiki/Characteristic) stat
- Added smooth level-up animations to the party overlay to replace chat messages.
### Pokémon Added
@@ -85,6 +89,8 @@
- Altaria
- Snorunt
- Glalie
- Shuppet
- Banette
- Latias
- Latios
@@ -133,6 +139,11 @@
- Swadloon
- Leavanny
- Druddigon
- Minccino
- Cinccino
- Vanillite
- Vanillish
- Vanilluxe
#### Gen 6
- Skiddo
@@ -295,6 +306,23 @@
- Tauros - Paldea Combat
- Dewgong
- Exeggutor - Kanto
- Corphish (updated placeholders for crustaceous tripod gait legs)
- Crawdaunt (updated placeholders for crustaceous bipedal legs)
- Dragapult
- Camerupt
- Relicanth
- Heracross
- Salamence
- Staraptor
- Dusknoir
- Arcanine
- Carvanha
- Sharpedo
- Mimikyu
- Dewgong
- Mime Jr
- Gyarados
- Lapras
### Model updates for the following Pokémon
- Cleffa
@@ -322,6 +350,8 @@
- Nosepass
- Probopass
- Kangaskhan
- Scorbunny
- Raboot
- Cinderace
- Magnemite
- Magneton
@@ -408,6 +438,18 @@
- Litwick
- Lampent
- Chandelure
- Rayquaza
- Aerodactyl
- Basculin
- Basculegion
- Shroodle
- Grafaiai
- Steelix
- Dratini
- Dragonair
- Dragonite
- Cutiefly
- Ribombee
### Changes
- Changed pokemon caught and seen count to update based on the current pokedex being looked
@@ -474,7 +516,7 @@
- Updated `HatchEggEvent.Post` to include the Pokemon that hatched.
- Roseli Berry Trees now naturally generate in their preferred biomes.
- Refactored dynamic lights compat to be more future proof. The dynamic light support is only tested with LambDynamicLights, on NeoForged use that mod in combination with Sinytra Connector
- Shulker Boxes and Traveler's Backpacks can no longer be held by Pokémon. Thanks Monocle ;) You could re-enable this with a datapack but you'd be crazy.
- Shulker Boxes and Traveler's Backpacks AND Packed Up backpacks can no longer be held by Pokémon. Thanks Monocle ;) You could re-enable this with our datapack (but you'd be crazy).
- Moomoo Milk now clears Pokémon stat changes when used in battle.
### Fixes
@@ -561,7 +603,9 @@
- Fixed species additions not being able to properly mark a species as implemented.
- Fixed Pokémon item models not showing a glint when enchanted.
- Fixed some specific bag items not being dropped when used in battle.
- The Corphish line will now sink in water.
- Fixed the "use all berry bait" achievement not being progressed
- Fixed bobber hook and berry sprouts texture sizes causing mipmap issues.
### Developer
- A finished battle now has winners and losers set inside of `PokemonBattle` instead of them always being empty.
@@ -599,10 +643,25 @@
- Added a `hoverText` option to PartySelectCallback, to display a tooltip on hovering over a Pokémon in the selection screen.
- `PokemonEntity` instances spawned into the world now appropriately finalize the spawn for mod compatibility.
- Added PokedexManager.obtain as a replacement for .catch which is not a friendly function name in Java.
- Added `Pokemon#hyperTrainIV()` and `IVs#setHyperTrainedIV(Stat, Int)`
- `ElementalType` now implements `ShowdownIdentifiable` to ensure the communcation with showdown stays consistent (also in regards to TeraTypes)
- Pokemon no longer have a change observable
- Pokémon can now have a behaviour changing the value of a species feature on lightning hit:
```JSON
{
"behaviour": {
"lightningHit": {
"rotateFeatures": [
{
"key": "mooshtank",
"chain": ["red", "brown"]
}
]
}
}
}
```
This will change the value of `mooshtank` from `red` to `brown` and vice versa when a lightning strikes this Pokémon. Only chains of which a value is the current value will be considered, so multiple entries for the same key with different chains can be added.
### 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)
@@ -709,6 +768,7 @@
- Fixed `cobblemon:reel_in_pokemon` criteria not working when used together with a `baitId`
- Also changed the default from `cobblemon:empty_bait` to `any`
- The previous default is still available by using the above as baitId
- Added support for species-specific move action effects, using the format `{move_id}_{species}.json`.
### Particles
Added new/updated particles for the following moves:
@@ -17,6 +17,7 @@ import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawner;
* @author Hiroku
* @since October 27th, 2025
*/
public interface PlayerSpawnerGetter {
public interface PlayerSpawnerAccessor {
PlayerSpawner getPlayerSpawner();
void setPlayerSpawner(PlayerSpawner spawner);
}
@@ -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.duck;
import net.minecraft.world.phys.Vec3;
public interface RidePassenger {
void cobblemon$setRideXRot(float rideXRot);
float cobblemon$getRideXRot();
void cobblemon$setRideYRot(float rideYRot);
float cobblemon$getRideYRot();
void cobblemon$setRideEyePos(Vec3 rideEyePos);
Vec3 cobblemon$getRideEyePos();
}
@@ -0,0 +1,51 @@
/*
* 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.duck.RidePassenger;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.projectile.AbstractArrow;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.Nullable;
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(AbstractArrow.class)
public abstract class AbstractArrowMixin {
@Inject(
method = "<init>(Lnet/minecraft/world/entity/EntityType;Lnet/minecraft/world/entity/LivingEntity;Lnet/minecraft/world/level/Level;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemStack;)V",
at = @At("TAIL")
)
private void cobblemon$adjustSpawnPosition(
EntityType<? extends AbstractArrow> entityType,
LivingEntity owner,
Level level,
ItemStack pickupItemStack,
@Nullable ItemStack firedFromWeapon,
CallbackInfo ci
) {
AbstractArrow arrow = (AbstractArrow)(Object)this;
if(!(owner.getVehicle() instanceof OrientationControllable vehicle)) return;
if(!(vehicle instanceof PokemonEntity)) return;
var vehicleController = vehicle.getOrientationController();
// If this is a pokemon ride passenger then use the rider eye position to set the arrow spawn position.
if (owner instanceof RidePassenger ridePassenger && vehicleController != null ) {
var eyePos = ridePassenger.cobblemon$getRideEyePos(); //.add(0.0, -2.0, 0.0);
arrow.setPos(eyePos);
}
}
}
@@ -28,6 +28,10 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@Mixin(BeehiveBlockEntity.Occupant.class)
public abstract class BeeOccupantMixin {
@@ -88,8 +92,11 @@ public abstract class BeeOccupantMixin {
@Inject(method = "of", at = @At("HEAD"), cancellable = true)
private static void cobblemon$of(Entity entity, CallbackInfoReturnable<BeehiveBlockEntity.Occupant> cir) {
if (entity instanceof PokemonEntity pokemonEntity) {
List<String> IGNORED_BEE_TAGS = Arrays.asList("Air", "ArmorDropChances", "ArmorItems", "Brain", "CanPickUpLoot", "DeathTime", "FallDistance", "FallFlying", "Fire", "HandDropChances", "HandItems", "HurtByTimestamp", "HurtTime", "LeftHanded", "Motion", "NoGravity", "OnGround", "PortalCooldown", "Pos", "Rotation", "Passengers", "leash", "UUID");
CompoundTag compoundTag = new CompoundTag();
entity.save(compoundTag);
Objects.requireNonNull(compoundTag);
IGNORED_BEE_TAGS.forEach(compoundTag::remove);
Boolean hasNectar = pokemonEntity.getBrain().getMemory(CobblemonMemories.INSTANCE.getHAS_NECTAR()).orElse(false);
cir.setReturnValue(new BeehiveBlockEntity.Occupant(CustomData.of(compoundTag), 0, hasNectar ? 2400 : 600));
cir.cancel();
@@ -8,18 +8,25 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.CobblemonNetwork;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.api.orientation.OrientationController;
import com.cobblemon.mod.common.api.pokemon.effect.ShoulderEffectRegistry;
import com.cobblemon.mod.common.api.riding.RidingStyle;
import com.cobblemon.mod.common.api.riding.Seat;
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.SubmarineBehaviour;
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.net.messages.server.orientation.ServerboundUpdateOrientationPacket;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.HitResult;
@@ -32,6 +39,7 @@ 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(LivingEntity.class)
public abstract class LivingEntityMixin extends Entity implements OrientationControllable {
@@ -55,6 +63,56 @@ public abstract class LivingEntityMixin extends Entity implements OrientationCon
}
}
@WrapOperation(method = "baseTick", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;isInWall()Z"))
public boolean cobblemon$preventMountSuffocation(LivingEntity instance, Operation<Boolean> original) {
if (instance.getVehicle() instanceof PokemonEntity) {
return false;
}
return original.call(instance);
}
@Override
public @NotNull HitResult pick(double hitDistance, float partialTicks, boolean hitFluids) {
Entity vehicle = this.getVehicle();
if (vehicle instanceof PokemonEntity pokemonEntity && pokemonEntity.getDelegate() instanceof PokemonClientDelegate delegate) {
int seatIndex = pokemonEntity.getPassengers().indexOf(this);
Seat seat = pokemonEntity.getSeats().get(seatIndex);
MatrixWrapper locator = delegate.getLocatorStates().get(seat.getLocator());
Vec3 locatorOffset = new Vec3(locator.getMatrix().getTranslation(new Vector3f()));
Vec3 eyePosition = cobblemon$getEyePosition(partialTicks, pokemonEntity, locatorOffset);
Vec3 viewVector = this.getViewVector(partialTicks);
Vec3 viewDistanceVector = eyePosition.add(viewVector.x * hitDistance, viewVector.y * hitDistance, viewVector.z * hitDistance);
return this.level()
.clip(
new ClipContext(
eyePosition, viewDistanceVector, ClipContext.Block.OUTLINE, hitFluids ? net.minecraft.world.level.ClipContext.Fluid.ANY : net.minecraft.world.level.ClipContext.Fluid.NONE, this
)
);
}
return super.pick(hitDistance, partialTicks, hitFluids);
}
@Unique
private Vec3 cobblemon$getEyePosition(float partialTicks, PokemonEntity pokemonEntity, Vec3 locatorOffset) {
OrientationController controller = this.getOrientationController();
float currEyeHeight = this.getEyeHeight();
Vec3 offset = locatorOffset.add(pokemonEntity.position());
if (controller.isActive() && controller.getOrientation() != null) {
Quaternionf orientation = controller.getRenderOrientation(partialTicks);
Vec3 rotatedEyeHeight = new Vec3(orientation.transform(new Vector3f(0f, currEyeHeight - (this.getBbHeight() / 2), 0f)));
offset.add(rotatedEyeHeight);
}
return offset;
}
@Inject(method = "tick", at = @At("HEAD"))
private void cobblemon$updateRenderOrientation(CallbackInfo ci) {
this.cobblemon$orientationController.tick();
@@ -74,4 +132,31 @@ public abstract class LivingEntityMixin extends Entity implements OrientationCon
}
}
@Inject(method = "decreaseAirSupply", at = @At("HEAD"), cancellable = true)
private void cobblemon$conditionalWaterBreathing(
int currentAir,
CallbackInfoReturnable<Integer> cir
) {
var entity = (LivingEntity) (Object) this;
if (!(entity instanceof Player player)) return;
if (!(player.getVehicle() instanceof PokemonEntity vehicle)) return;
var ridingController = vehicle.getRidingController();
if (ridingController == null) return;
var activeContext = ridingController.getContext();
if (activeContext == null) return;
// If the rider is currently on a submarine and the ride still has stamina.
var stamina = activeContext.getState().getStamina().get();
if (
activeContext.getStyle() == RidingStyle.LIQUID &&
activeContext.getBehaviour().equals(SubmarineBehaviour.Companion.getKEY()) &&
stamina > 0.0f
) {
cir.setReturnValue(currentAir);
cir.cancel();
}
}
}
@@ -19,6 +19,7 @@ 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.duck.PlayerDuck;
import com.cobblemon.mod.common.duck.RidePassenger;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.pokedex.scanner.PokedexEntityData;
import com.cobblemon.mod.common.pokedex.scanner.ScannableEntity;
@@ -47,6 +48,7 @@ import net.minecraft.world.entity.player.Player;
import net.minecraft.world.food.FoodProperties;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import org.joml.Vector3f;
import org.objectweb.asm.Opcodes;
@@ -63,7 +65,7 @@ import java.util.*;
import java.util.stream.Collectors;
@Mixin(Player.class)
public abstract class PlayerMixin extends LivingEntity implements ScannableEntity, OrientationControllable, PlayerDuck {
public abstract class PlayerMixin extends LivingEntity implements ScannableEntity, OrientationControllable, PlayerDuck, RidePassenger {
@Unique
private Vector3f cobblemon$driverInput;
@@ -91,6 +93,12 @@ public abstract class PlayerMixin extends LivingEntity implements ScannableEntit
@Shadow protected int jumpTriggerTime;
@Unique private final OrientationController cobblemon$orientationController = new OrientationController(this);
@Unique private float cobblemon$rideXRot = 0.0f;
@Unique private float cobblemon$rideYRot = 0.0f;
@Unique private Vec3 cobblemon$rideEyePos = Vec3.ZERO;
protected PlayerMixin(EntityType<? extends LivingEntity> p_20966_, Level p_20967_) {
super(p_20966_, p_20967_);
}
@@ -117,11 +125,6 @@ public abstract class PlayerMixin extends LivingEntity implements ScannableEntit
}
}
@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) {
@@ -307,4 +310,34 @@ public abstract class PlayerMixin extends LivingEntity implements ScannableEntit
return cobblemon$lastSentDriverInput;
}
@Override
public float cobblemon$getRideXRot() {
return this.cobblemon$rideXRot;
}
@Override
public void cobblemon$setRideXRot(float rideXRot) {
this.cobblemon$rideXRot = rideXRot;
}
@Override
public float cobblemon$getRideYRot() {
return this.cobblemon$rideYRot;
}
@Override
public void cobblemon$setRideYRot(float rideYRot) {
this.cobblemon$rideYRot = rideYRot;
}
@Override
public Vec3 cobblemon$getRideEyePos() {
return this.cobblemon$rideEyePos;
}
@Override
public void cobblemon$setRideEyePos(Vec3 rideEyePos) {
this.cobblemon$rideEyePos = rideEyePos;
}
}
@@ -10,7 +10,7 @@ package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.PlayerSpawnerGetter;
import com.cobblemon.mod.common.PlayerSpawnerAccessor;
import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawner;
import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawnerFactory;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
@@ -28,7 +28,7 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ServerPlayer.class)
public abstract class ServerPlayerMixin implements PlayerSpawnerGetter {
public abstract class ServerPlayerMixin implements PlayerSpawnerAccessor {
@Shadow
public abstract ServerLevel serverLevel();
@@ -44,6 +44,11 @@ public abstract class ServerPlayerMixin implements PlayerSpawnerGetter {
return cobblemon$spawner;
}
@Override
public void setPlayerSpawner(PlayerSpawner spawner) {
this.cobblemon$spawner = spawner;
}
@Inject(method = "tick", at = @At("TAIL"))
private void cobblemon$tickSpawner(CallbackInfo ci) {
if (!Cobblemon.config.getEnableSpawning()) {
@@ -56,16 +61,17 @@ public abstract class ServerPlayerMixin implements PlayerSpawnerGetter {
@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);
ServerPlayer player = (ServerPlayer)(Object)this;
Entity vehicle = player.getVehicle();
if (!(vehicle instanceof OrientationControllable controllableVehicle)) return;
var shouldUseCustomOrientation = cobblemon$shouldUseCustomOrientation(vehicle);
controllableVehicle.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;
private boolean cobblemon$shouldUseCustomOrientation(Entity entity) {
if (entity == null) return false;
if (!(entity instanceof PokemonEntity pokemonEntity)) return false;
return pokemonEntity.ifRidingAvailableSupply(false, (behaviour, settings, state) -> {
return behaviour.shouldRoll(settings, state, pokemonEntity);
});
@@ -73,8 +79,9 @@ public abstract class ServerPlayerMixin implements PlayerSpawnerGetter {
@Inject(method = "stopRiding", at = @At("HEAD"))
public void cobblemon$resetOrientationOnDismount(CallbackInfo ci) {
if (!(this instanceof OrientationControllable controllable)) return;
controllable.getOrientationController().setActive(false);
ServerPlayer player = (ServerPlayer)(Object)this;
if (!(player.getVehicle() instanceof OrientationControllable controllableVehicle)) return;
controllableVehicle.getOrientationController().setActive(false);
}
//TODO: Switch to sending out on load instead of preventing saving
@@ -34,7 +34,8 @@ public class AbstractClientPlayerMixin {
} else {
// Only modify fov through riding if the pokedex is not open
LocalPlayer player = (LocalPlayer) (Object) this;
if (player != null && player.isPassenger() && player.getVehicle() instanceof PokemonEntity) {
// Only modify fov for the driver and not the passengers
if (player != null && player.isPassenger() && player.getVehicle() instanceof PokemonEntity && player.getVehicle().getControllingPassenger() == player) {
PokemonEntity ride = (PokemonEntity) player.getVehicle();
// Return custom fov mult for riding
@@ -14,14 +14,18 @@ import com.cobblemon.mod.common.api.orientation.OrientationController;
import com.cobblemon.mod.common.api.riding.Rideable;
import com.cobblemon.mod.common.client.MountedPokemonAnimationRenderController;
import com.cobblemon.mod.common.client.render.camera.MountedCameraRenderer;
import com.cobblemon.mod.common.duck.RidePassenger;
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 com.mojang.blaze3d.Blaze3D;
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.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix3f;
import org.joml.Quaternionf;
import org.joml.Vector3f;
@@ -48,45 +52,51 @@ public abstract class CameraMixin {
@Shadow protected abstract void setPosition(Vec3 pos);
@Unique private float returnTimer = 0;
@Unique private float rollAngleStart = 0;
@Unique Minecraft minecraft = Minecraft.getInstance();
@Shadow private float partialTickTime;
// Used to help in camera rotation rendering when transitioning
// from rolled to unrolled.
@Unique private float cobblemon$returnTimer = 0;
@Unique private float cobblemon$rollAngleStart = 0;
@Unique private double cobblemon$lastHandledRotationTime = Double.MIN_VALUE;
@Unique private double cobblemon$frameTime = 0.0;
// Used to as the render rotation for the motion sick camera.
@Unique @Nullable private Quaternionf cobblemon$smoothRotation = null;
@Inject(method = "setRotation", at = @At("HEAD"), cancellable = true)
public void cobblemon$setRotation(float f, float g, CallbackInfo ci) {
if (!(this.entity instanceof OrientationControllable controllable)) 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();
// Calculate the current frametime
double d = Blaze3D.getTime();
this.cobblemon$frameTime = (this.cobblemon$lastHandledRotationTime != Double.MIN_VALUE) ? d - this.cobblemon$lastHandledRotationTime : 0.0;
this.cobblemon$lastHandledRotationTime = d;
this.returnTimer = 0;
this.rollAngleStart = controller.getRoll();
ci.cancel();
// Do no camera rotations if the game is paused
if(Minecraft.getInstance().isPaused()) return;
// Don't assume the camera to be attached to an entity. Ponder Scenes from create et.al. for example aren't.
if (this.entity == null) return;
var vehicle = this.entity.getVehicle();
if (!(vehicle instanceof OrientationControllable controllableVehicle)) return;
var instance = (Camera)(Object)this;
// If the current vehicle has no orientation then return and perform
// vanilla camera rotations
var vehicleController = controllableVehicle.getOrientationController();
if (vehicleController.getOrientation() == null) return;
// If the controller has been deactivated but the orientation isn't null yet
// then perform transition from rolled ride/camera to vanilla
if (!vehicleController.isActive() && vehicleController.getOrientation() != null) {
cobblemon$applyTransitionRotation(vehicleController, ci);
} else { // Otherwise perform cobblemon specific camera rotation and set transition helper variables
cobblemon$applyRotation();
this.cobblemon$returnTimer = 0;
this.cobblemon$rollAngleStart = instance.rotation().getEulerAnglesYXZ(new Vector3f()).z();
ci.cancel();
}
}
@WrapOperation(method = "setup", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;setPosition(DDD)V"))
@@ -114,25 +124,153 @@ public abstract class CameraMixin {
}
}
// reset the rotation values if it is now not being ridden.
cobblemon$smoothRotation = null;
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()) {
var driver = this.entity;
if (!(driver.getVehicle() instanceof OrientationControllable controllableVehicle) ||
controllableVehicle.getOrientationController().getOrientation() == null ||
!controllableVehicle.getOrientationController().getActive()
) return;
// Grab the driver and vehicle orientation controllers.
var vehicleController = controllableVehicle.getOrientationController();
var isDriving = driver.getControlledVehicle() != null;
// Grab the ride orientation
var newRotation = new Quaternionf();
// If the player is driving then use the current orientation since the driver sets it.
// If not then use the smoothed render orientation for passengers otherwise the vehicle rotation will be jerky
if (isDriving) {
newRotation = vehicleController.getOrientation().normal(new Matrix3f()).getNormalizedRotation(new Quaternionf());
} else {
newRotation = vehicleController.getRenderOrientation(this.partialTickTime);
}
// Unroll the current rotation if no roll is requested and not currently freelooking.
if (Cobblemon.config.getDisableRoll()) {
// Init the smooth rotation if it has not been set yet.
if (cobblemon$smoothRotation == null) {
cobblemon$smoothRotation = new Quaternionf().set(this.rotation);
}
var rideAngs = newRotation.getEulerAnglesYXZ(new Vector3f());
var cameraAngs = cobblemon$smoothRotation.getEulerAnglesYXZ(new Vector3f());
// var vehicle = (PokemonEntity)driver.getVehicle();
// var vel = vehicle.getRidingAnimationData().getVelocitySpring().getInterpolated(this.partialTickTime, 0);
// // Grab the target yaw as the current rides yaw. If the horizontal velocity is high enough to be used to
// // derive a yaw then use it.
// var targetYaw = (double) rideAngs.y();
// if ( vel.horizontalDistance() > 0.01) {
// // lerp the new rotation towards this y rot at a rate proportional to the horizontal movement speed?
// targetYaw = Mth.wrapDegrees(Math.toDegrees(Mth.atan2(vel.x(), vel.z())) + 180.0f);
// }
var degDiff = Mth.degreesDifference((float)Math.toDegrees(cameraAngs.y()), (float)Math.toDegrees(rideAngs.y));
var pitchDeadzoneDeg = 15.0; // x degrees of deadzone about +-90 degrees of pitch.
var pitchDeg = vehicleController.getPitch();
// ConsiderPitch to be already 90 if within the deadzone around it.
var cutoffPitch = Math.min(1.0,(Mth.abs(pitchDeg) / (90.0f - pitchDeadzoneDeg))) * 90.0f * Math.signum(pitchDeg);
var lerpRateMod = Mth.cos((float)Math.toRadians(cutoffPitch));
// Apply smoothing from the cameras current yaw to the yaw of the velocity vector of the ride.
var k = 10.0f; // Some smoothing constant k
var smoothingFactor = lerpRateMod * cobblemon$frameTime * k;
var newYaw = cameraAngs.y() + (Math.toRadians(degDiff) * smoothingFactor);
var newPitch = (float)Mth.lerp(cobblemon$frameTime * k, cameraAngs.x(), rideAngs.x());
//var newYaw = (float)Mth.lerp(smoothingFactor, cameraAngs.y(), rideAngs.y());
var maxRoll = Math.toRadians(15.0f);
var newRoll = (float)Mth.lerp(cobblemon$frameTime * k / 2.0, cameraAngs.z(), Math.abs(lerpRateMod) * Math.sin(rideAngs.z()) * maxRoll);
// Set rotations
var smoothedRot = new Quaternionf().rotateYXZ((float)newYaw, newPitch, newRoll);
cobblemon$smoothRotation.set(smoothedRot);
newRotation.set(smoothedRot);
}
var rotationOffset = 0;
if (Minecraft.getInstance().options.getCameraType().isMirrored()) {
newRotation.rotateY((float)Math.toRadians(180));
rotationOffset = 180;
}
// Get the drivers local look angle in relation to the ride and apply it.
if (vehicleController.getActive()) {
var vehicleMatrix = new Matrix3f().set(newRotation);
var driverMatrix = new Matrix3f();// Base the driver
var playerRotater = (RidePassenger)driver;
newRotation = vehicleMatrix.mul(
driverMatrix.rotateYXZ(
-Mth.DEG_TO_RAD * (playerRotater.cobblemon$getRideYRot()),
-Mth.DEG_TO_RAD * playerRotater.cobblemon$getRideXRot(),
0.0f)
).normal(new Matrix3f()).getNormalizedRotation(new Quaternionf());
}
this.rotation.set(newRotation);
this.xRot = controller.getPitch();
this.yRot = controller.getYaw();
var eulerAngs = newRotation.getEulerAnglesXYZ(new Vector3f());
this.xRot = eulerAngs.x() * Mth.RAD_TO_DEG;
this.yRot = eulerAngs.y() * Mth.RAD_TO_DEG;
// Set the drivers rotations to match
var driverEulerAngs = new Quaternionf(this.rotation).getEulerAnglesYXZ(new Vector3f());
driver.setXRot(-driverEulerAngs.x() * Mth.RAD_TO_DEG + rotationOffset);
driver.setYRot(180.0f - (driverEulerAngs.y() * Mth.RAD_TO_DEG));
OrientationController.Companion.getFORWARDS().rotate(this.rotation, this.forwards);
OrientationController.Companion.getUP().rotate(this.rotation, this.up);
OrientationController.Companion.getLEFT().rotate(this.rotation, this.left);
}
@Unique
private void cobblemon$applyTransitionRotation(
OrientationController vehicleController,
CallbackInfo ci
) {
// If the transition has just started then
if (this.cobblemon$returnTimer == 0.0) {
MountedCameraRenderer.INSTANCE.resetDriverRotations((Camera)(Object)this, this.entity);
}
if(this.cobblemon$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.
float interpolatedRoll = Mth.lerp(this.cobblemon$returnTimer, this.cobblemon$rollAngleStart, 0.0f);
float pitch = (float) Math.toRadians(-this.entity.getXRot());
float yaw = (float) Math.toRadians(180 - this.entity.getYRot());
Quaternionf interRot = new Quaternionf();
interRot.rotationYXZ(yaw, pitch, interpolatedRoll);
this.rotation.set(interRot);
var eulerAngs = interRot.getEulerAnglesXYZ(new Vector3f());
this.xRot = eulerAngs.x() * Mth.RAD_TO_DEG;
this.yRot = eulerAngs.y() * Mth.RAD_TO_DEG;
OrientationController.Companion.getFORWARDS().rotate(this.rotation, this.forwards);
OrientationController.Companion.getUP().rotate(this.rotation, this.up);
OrientationController.Companion.getLEFT().rotate(this.rotation, this.left);
if(cobblemon$rollAngleStart == 0){
this.cobblemon$returnTimer = 1;
vehicleController.reset();
MountedCameraRenderer.INSTANCE.resetDriverRotations((Camera)(Object)this, this.entity);
return;
}
this.cobblemon$returnTimer += (partialTickTime * (1.0f/20.0f));
ci.cancel();
} else {
this.cobblemon$returnTimer = 1;
vehicleController.reset();
MountedCameraRenderer.INSTANCE.resetDriverRotations((Camera)(Object)this, this.entity);
}
}
@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();
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.client.render.player.MountedPlayerRenderer;
import com.cobblemon.mod.common.duck.RidePassenger;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import net.minecraft.client.model.HumanoidModel;
import net.minecraft.client.model.geom.ModelPart;
@@ -68,6 +69,13 @@ public class HumanoidModelMixin {
behaviour.shouldRotateRiderHead(settings, state, pokemonEntity)
);
if (entity instanceof RidePassenger playerRotater) {
//this.head.yRot = playerRotater.cobblemon$getRideYRot();
this.head.xRot = playerRotater.cobblemon$getRideXRot();
headPitch = playerRotater.cobblemon$getRideXRot();
}
if (!shouldRotatePlayerHead) {
netHeadYaw = 0f;
headPitch = 0f;
@@ -19,9 +19,11 @@ 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.duck.PlayerDuck;
import com.cobblemon.mod.common.duck.RidePassenger;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.net.messages.server.orientation.ServerboundUpdateOrientationPacket;
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateDriverInputPacket;
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateRiderRotationPacket;
import com.mojang.authlib.GameProfile;
import net.minecraft.client.CameraType;
import net.minecraft.client.Minecraft;
@@ -29,12 +31,10 @@ import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.player.Input;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.NotNull;
@@ -55,6 +55,10 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
@Unique Matrix3f cobblemon$lastOrientation;
@Unique float cobblemon$lastRideXRot;
@Unique float cobblemon$lastRideYRot;
@Unique Vec3 cobblemon$lastRideEyePos;
@Unique boolean cobblemon$isDoubleJumping = false;
@Shadow
@@ -72,30 +76,35 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
super(clientLevel, gameProfile);
}
// @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 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()));
if (!(this.getVehicle() instanceof OrientationControllable controllableVehicle)) return;
var vehicleController = controllableVehicle.getOrientationController();
if (!vehicleController.isActive() || vehicleController.getOrientation() == cobblemon$lastOrientation) return;
cobblemon$lastOrientation = vehicleController.getOrientation() != null ? new Matrix3f(vehicleController.getOrientation()) : null;
CobblemonNetwork.INSTANCE.sendToServer(new ServerboundUpdateOrientationPacket(this.getVehicle().getId(), vehicleController.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 driver = (LocalPlayer)(Object)this;
var vehicle = driver.getVehicle();
if (!(this.getVehicle() instanceof OrientationControllable controllableVehicle)) return;
var shouldUseCustomOrientation = cobblemon$shouldUseCustomOrientation((LocalPlayer)(Object)this);
controllable.getOrientationController().setActive(shouldUseCustomOrientation);
var vehicleController = controllableVehicle.getOrientationController();
// If the player has just switched to riding a custom orientation ride then set their
// x and y rots local to the vehicle rots(if the controller wasn't active and will now be active)
// This ensures that on transition your camera stays in the same spot
if (!vehicleController.isActive() && shouldUseCustomOrientation) {
// Set local to the vehicle x and yrot so
var playerRotater = (RidePassenger)driver;
playerRotater.cobblemon$setRideXRot(Mth.wrapDegrees(driver.getXRot() - vehicle.getXRot()));
playerRotater.cobblemon$setRideYRot(Mth.wrapDegrees(driver.getYRot() - vehicle.getYRot()));
}
vehicleController.setActive(shouldUseCustomOrientation);
}
@Inject(method = "rideTick", at = @At("HEAD"))
@@ -115,6 +124,36 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
CobblemonNetwork.INSTANCE.sendToServer(new ServerboundUpdateDriverInputPacket(driverInput));
}
@Inject(method = "rideTick", at = @At("HEAD"))
private void cobblemon$updateRiderRotationsRideTick(CallbackInfo ci) {
if (Minecraft.getInstance().player != (Object)this) return;
if (!(this.getVehicle() instanceof PokemonEntity pokemonEntity)) return;
var passenger = (LocalPlayer)(Object)this;
var playerRotater = (RidePassenger)passenger;
// Gather Rider Rotation info
var rideXRot = playerRotater.cobblemon$getRideXRot();
var rideYRot = playerRotater.cobblemon$getRideYRot();
var rideEyePos = playerRotater.cobblemon$getRideEyePos();
// Check for a change in the values before sending them.
if (cobblemon$lastRideXRot != rideXRot ||
cobblemon$lastRideYRot != rideYRot ||
cobblemon$lastRideEyePos != rideEyePos
) {
// Update 'last' values
cobblemon$lastRideXRot = rideXRot;
cobblemon$lastRideYRot = rideYRot;
cobblemon$lastRideEyePos = rideEyePos;
CobblemonNetwork.INSTANCE.sendToServer(new ServerboundUpdateRiderRotationPacket(rideXRot, rideYRot, rideEyePos));
}
}
@Unique
private boolean cobblemon$shouldUseCustomOrientation(LocalPlayer player) {
var playerVehicle = player.getVehicle();
@@ -129,8 +168,8 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
public void stopRiding() {
super.stopRiding();
if (Minecraft.getInstance().player != (Object)this) return;
if (!(this instanceof OrientationControllable controllable)) return;
controllable.getOrientationController().setActive(false);
if (!(this instanceof OrientationControllable controllableVehicle)) return;
controllableVehicle.getOrientationController().setActive(false);
}
@Inject(method = "getJumpRidingScale", at = @At("HEAD"), cancellable = true)
@@ -161,7 +200,7 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
this.cobblemon$survivalJumpTriggerTime = 0;
}
else {
this.cobblemon$survivalJumpTriggerTime = 7;
this.cobblemon$survivalJumpTriggerTime = 12;
}
}
else if (this.cobblemon$survivalJumpTriggerTime > 0) {
@@ -188,33 +227,17 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
public HitResult pick(double hitDistance, float partialTicks, boolean hitFluids) {
Entity vehicle = this.getVehicle();
if (vehicle instanceof PokemonEntity pokemonEntity) {
PokemonClientDelegate delegate = (PokemonClientDelegate) pokemonEntity.getDelegate();
String locatorName = delegate.getSeatLocator(this);
MatrixWrapper locator = delegate.getLocatorStates().get(locatorName);
if (locator == null) {
return super.pick(hitDistance, partialTicks, hitFluids);
}
// Get player object
var passenger = (LocalPlayer)(Object)this;
var playerRotater = (RidePassenger)passenger;
Vec3 locatorOffset = locator != null ? new Vec3(locator.getMatrix().getTranslation(new Vector3f())) : Vec3.ZERO;
OrientationController controller = this.getOrientationController();
float currEyeHeight = this.getEyeHeight();
Vector3f offset = new Vector3f(new Vector3f(0f, currEyeHeight - (this.getBbHeight() / 2), 0f));
if (Minecraft.getInstance().options.getCameraType() == CameraType.FIRST_PERSON) {
PosableModel model = VaryingModelRepository.INSTANCE.getPoser(pokemonEntity.getPokemon().getSpecies().getResourceIdentifier(), new FloatingState());
offset.add(cobblemon$getFirstPersonOffset(model, locatorName));
}
Matrix3f orientation = controller.isActive() && controller.getOrientation() != null ? controller.getOrientation() : new Matrix3f();
Vec3 rotatedEyeHeight = new Vec3(orientation.transform(offset));
Vec3 eyePosition = locatorOffset.add(pokemonEntity.position()).add(rotatedEyeHeight);
// Gather eye position from stored 1st person camera position calculation
var eyePosition = playerRotater.cobblemon$getRideEyePos();
Vec3 viewVector = this.getViewVector(partialTicks);
Vec3 viewDistanceVector = eyePosition.add(viewVector.x * hitDistance, viewVector.y * hitDistance, viewVector.z * hitDistance);
return this.level()
.clip(
new ClipContext(
@@ -10,13 +10,16 @@ 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.client.CobblemonClient;
import com.cobblemon.mod.common.client.keybind.keybinds.PartySendBinding;
import com.cobblemon.mod.common.duck.RidePassenger;
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 kotlin.Pair;
import net.minecraft.client.Minecraft;
import net.minecraft.client.MouseHandler;
import net.minecraft.client.player.LocalPlayer;
@@ -32,6 +35,9 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import static com.google.common.primitives.Floats.min;
import static net.minecraft.util.Mth.lerp;
@Mixin(MouseHandler.class)
public abstract class MouseHandlerMixin {
@Shadow private double accumulatedScrollY;
@@ -117,8 +123,6 @@ public abstract class MouseHandlerMixin {
@Local(argsOnly = true) double d,
@Local(argsOnly = true) double movementTime
) {
boolean returnValue = true;
PokedexUsageContext usageContext = CobblemonClient.INSTANCE.getPokedexUsageContext();
if (usageContext.getScanningGuiOpen()) {
this.smoothTurnY.reset();
@@ -126,34 +130,78 @@ public abstract class MouseHandlerMixin {
var defaultSensitivity = this.minecraft.options.sensitivity().get() * 0.6 + 0.2;
var spyglassSensitivity = Math.pow(defaultSensitivity, 3);
var lookSensitivity = spyglassSensitivity * 8.0;
var sensitivity = Mth.lerp(usageContext.getFovMultiplier(), spyglassSensitivity, lookSensitivity);
var sensitivity = lerp(usageContext.getFovMultiplier(), spyglassSensitivity, lookSensitivity);
var yRotationFlip = this.minecraft.options.invertYMouse().get() ? -1 : 1;
player.turn(this.accumulatedDX * sensitivity, (this.accumulatedDY * sensitivity * yRotationFlip));
returnValue = false;
}
var vehicle = player.getVehicle();
if (!(vehicle instanceof PokemonEntity)) return true;
// Clamp player rotation if riding and the vehicle demands it
if (player.isPassenger() && player.getVehicle() instanceof PokemonEntity vehicle) {
vehicle.clampPassengerRotation(player);
if (player.isPassenger() && vehicle instanceof PokemonEntity pokeVehicle) {
pokeVehicle.clampPassengerRotation(player);
}
if (!(player instanceof OrientationControllable controllable)) return true;
if (!controllable.getOrientationController().isActive()) {
// If the ride is not orientation controllable then return and perform the normal turnPlayer logic
if (!(vehicle instanceof OrientationControllable controllableVehicle) || !controllableVehicle.getOrientationController().isActive()) {
xMouseSmoother.reset();
yMouseSmoother.reset();
pitchSmoother.reset();
rollSmoother.reset();
yawSmoother.reset();
return returnValue;
return true;
}
// Send mouse input to be interpreted into rotation
// deltas by the ride controller
var vehicleController = controllableVehicle.getOrientationController();
var lookaroundButtonPressed = Minecraft.getInstance().mouseHandler.isMiddlePressed();
Pair<Boolean, Boolean> mouseShouldModifyDriver = ((PokemonEntity) vehicle).ifRidingAvailableSupply(new Pair(false, false), (behaviour, settings, state) -> {
return behaviour.mouseModifiesDriverRotation(
settings,
state,
(PokemonEntity) vehicle
);
});
//-------------------------------------------------------------------------
// If looking around then modify the players orientation instead and zero the mouse deltas so the mouse doesn't
// affect the vehicle orientation when looking around.
// If the client is a passenger it will modify these rotations and return
//-------------------------------------------------------------------------
if (lookaroundButtonPressed || player.getControlledVehicle() == null) {
// Modify driver rotations
cobblemon$setDriverRotations(player, cursorDeltaX, cursorDeltaY);
// If the player/passenger is not the driver then return and ignore all vehicle rotation logic
if (player.getControlledVehicle() == null) return false;
// Set to zero so that vehicle rotations don't also get affected by movement.
cursorDeltaX = 0.0;
cursorDeltaY = 0.0;
} else if (mouseShouldModifyDriver.getFirst()) { // If not freelooking then check modification per mouse input dimension
cobblemon$setDriverRotations(player, cursorDeltaX, 0.0);
cursorDeltaX = 0.0;
} else if (mouseShouldModifyDriver.getSecond()) {
cobblemon$setDriverRotations(player, 0.0, cursorDeltaY);
cursorDeltaY = 0.0;
}
//-------------------------------------------------------------------------
// Smoothly return driver rotations to zero when needed
//-------------------------------------------------------------------------
var playerRotater = (RidePassenger)player;
if( !lookaroundButtonPressed && !mouseShouldModifyDriver.getFirst()){
playerRotater.cobblemon$setRideYRot(lerp(min(5.0f * (float)movementTime, 1.0f), Mth.wrapDegrees(playerRotater.cobblemon$getRideYRot()), 0.0f));
}
if( !lookaroundButtonPressed && !mouseShouldModifyDriver.getSecond()) {
playerRotater.cobblemon$setRideXRot(lerp(min(5.0f * (float)movementTime, 1.0f), Mth.wrapDegrees(playerRotater.cobblemon$getRideXRot()), 0.0f));
}
// 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(
vehicleController.rotate(
(float) angVecMouse.x,
(float) angVecMouse.y,
(float) angVecMouse.z
@@ -163,17 +211,16 @@ public abstract class MouseHandlerMixin {
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);
vehicleController.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));
vehicleController.rotate((float) (angRot.x * 10 * d), (float) (angRot.y * 10 * d), (float) (angRot.z * 10 * d));
}
return false;
}
@@ -186,10 +233,11 @@ public abstract class MouseHandlerMixin {
@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.player.getVehicle() instanceof OrientationControllable vehicleControllable)) return;
if (!(vehicleControllable.getOrientationController() instanceof OrientationController vehicleController)) return;
if (vehicleController.getOrientation() == null) return;
if (minecraft.isPaused()) return;
if (isMouseGrabbed()) return;
if (this.isMouseGrabbed()) return; // return if turnPlayer() has already run this pass
this.turnPlayer(cobblemon$timeDelta);
}
@@ -251,6 +299,20 @@ public abstract class MouseHandlerMixin {
return Math.pow(sensitivity, 3);
}
@Unique
private void cobblemon$setDriverRotations(LocalPlayer player, double cursorDeltaX, double cursorDeltaY) {
// the vanilla turn() method redone so that it uses rideRotations.
float f = (float)cursorDeltaY * 0.15F; // Note: turn() uses xRot for pitch (up/down)
float g = (float)cursorDeltaX * 0.15F; // Note: turn() uses yRot for yaw (left/right)
var playerRotater = (RidePassenger)player;
playerRotater.cobblemon$setRideXRot(playerRotater.cobblemon$getRideXRot() + f);
playerRotater.cobblemon$setRideYRot(playerRotater.cobblemon$getRideYRot() + g);
playerRotater.cobblemon$setRideXRot(Mth.clamp(playerRotater.cobblemon$getRideXRot(), -90.0F, 90.0F));
if (player.getVehicle() != null) {
player.getVehicle().onPassengerTurned(player);
}
}
@Unique
private boolean cobblemon$shouldUseAngVelSmoothing() {
var player = minecraft.player;
@@ -0,0 +1,45 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.OrientationControllable;
import net.minecraft.client.CameraType;
import net.minecraft.client.Minecraft;
import net.minecraft.client.Options;
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.CallbackInfoReturnable;
@Mixin(Options.class)
public abstract class OptionsMixin {
/**
* Only allow third person on rollables when motion sick cam is enabled
*/
@Inject(
method = "getCameraType",
at = @At("HEAD"),
cancellable = true
)
private void cobblemon$forceThirdPerson(CallbackInfoReturnable<CameraType> cir) {
var mc = Minecraft.getInstance();
if(!(mc.player instanceof Player player)) return;
if(!(player.getVehicle() instanceof OrientationControllable vehicle)) return;
if(vehicle.getOrientationController() == null) return;
// Force third person if on an active rollable vehicle and roll is disabled.
if (Cobblemon.config.getDisableRoll() && vehicle.getOrientationController().isActive()) {
cir.setReturnValue(CameraType.THIRD_PERSON_BACK);
cir.cancel();
}
}
}
@@ -8,20 +8,24 @@
package com.cobblemon.mod.common.mixin.entity;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.duck.RidePassenger;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
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.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(Entity.class)
public abstract class EntityMixin {
@@ -57,5 +61,42 @@ public abstract class EntityMixin {
return original.call(entity);
}
@Inject(
method = "getEyePosition(F)Lnet/minecraft/world/phys/Vec3;",
at = @At("HEAD"),
cancellable = true
)
private void cobblemon$modifyEyePosition_partial(float partialTicks, CallbackInfoReturnable<Vec3> cir) {
var entity = (Entity)(Object)this;
if (!(entity instanceof Player player)) return;
if (!(player instanceof RidePassenger ridePassenger)) return;
if (!(player.getVehicle() instanceof OrientationControllable vehicle)) return;
var vehicleController = vehicle.getOrientationController();
if (vehicleController == null) return;
// TODO: Determine if it needs partialtick involved.
Vec3 customEyePos = ridePassenger.cobblemon$getRideEyePos();
cir.setReturnValue(customEyePos);
}
@Inject(
method = "getEyePosition()Lnet/minecraft/world/phys/Vec3;",
at = @At("HEAD"),
cancellable = true
)
private void cobblemon$modifyEyePosition_noPartial(CallbackInfoReturnable<Vec3> cir) {
var entity = (Entity)(Object)this;
if (!(entity instanceof Player player)) return;
if (!(player instanceof RidePassenger ridePassenger)) return;
if (!(player.getVehicle() instanceof OrientationControllable vehicle)) return;
var vehicleController = vehicle.getOrientationController();
if (vehicleController == null) return;
// TODO: Determine if it needs partialtick involved.
Vec3 customEyePos = ridePassenger.cobblemon$getRideEyePos();
cir.setReturnValue(customEyePos);
}
}
@@ -83,6 +83,7 @@ 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.CHARACTERISTIC_RAINBOW_ASPECT
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
@@ -293,6 +294,7 @@ object Cobblemon {
SHINY_ASPECT.register()
GENDER_ASPECT.register()
COSMETIC_SLOT_ASPECT.register()
CHARACTERISTIC_RAINBOW_ASPECT.register()
SpeciesFeatures.types["choice"] = ChoiceSpeciesFeatureProvider::class.java
SpeciesFeatures.types["flag"] = FlagSpeciesFeatureProvider::class.java
@@ -593,6 +593,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
val CORNN_BERRY = this.berryBlock("cornn")
val CUSTAP_BERRY = this.berryBlock("custap")
val DURIN_BERRY = this.berryBlock("durin")
val EGGANT_BERRY = this.berryBlock("eggant")
val ENIGMA_BERRY = this.berryBlock("enigma")
val FIGY_BERRY = this.berryBlock("figy")
val GANLON_BERRY = this.berryBlock("ganlon")
@@ -51,6 +51,11 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
.networkSynchronized(FlavourComponent.PACKET_CODEC)
.build())
val RIDE_BOOST: DataComponentType<RideBoostsComponent> = create("ride_boosts", DataComponentType.builder<RideBoostsComponent>()
.persistent(RideBoostsComponent.CODEC)
.networkSynchronized(RideBoostsComponent.PACKET_CODEC)
.build())
val FOOD_COLOUR: DataComponentType<FoodColourComponent> = create("food_colour", DataComponentType.builder<FoodColourComponent>()
.persistent(FoodColourComponent.CODEC)
.networkSynchronized(FoodColourComponent.PACKET_CODEC)
@@ -82,6 +87,7 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:cooking_pot_item"), POT_DATA)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:bait_effects"), BAIT_EFFECTS)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:flavour"), FLAVOUR)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:ride_boosts"), RIDE_BOOST)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:food_colour"), FOOD_COLOUR)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:ingredient"), INGREDIENT)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:food"), FOOD)
@@ -36,7 +36,7 @@ import com.cobblemon.mod.common.item.berry.PPRestoringBerryItem
import com.cobblemon.mod.common.item.berry.PortionHealingBerryItem
import com.cobblemon.mod.common.item.berry.StatusCuringBerryItem
import com.cobblemon.mod.common.item.berry.VolatileCuringBerryItem
import com.cobblemon.mod.common.item.food.PonigiriItem
import com.cobblemon.mod.common.item.interactive.PonigiriItem
import com.cobblemon.mod.common.item.food.SinisterTeaItem
import com.cobblemon.mod.common.item.interactive.*
import com.cobblemon.mod.common.item.interactive.ability.AbilityChangeItem
@@ -517,6 +517,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField val RAWST_BERRY = berryItem("rawst", StatusCuringBerryItem(CobblemonBlocks.RAWST_BERRY, Statuses.BURN))
@JvmField val ASPEAR_BERRY = berryItem("aspear", StatusCuringBerryItem(CobblemonBlocks.ASPEAR_BERRY, Statuses.FROZEN))
@JvmField val PERSIM_BERRY = berryItem("persim", VolatileCuringBerryItem(CobblemonBlocks.PERSIM_BERRY, "confusion"))
@JvmField val EGGANT_BERRY = berryItem("eggant", VolatileCuringBerryItem(CobblemonBlocks.EGGANT_BERRY, "attract"))
@JvmField val RAZZ_BERRY = berryItem("razz", CobblemonBlocks.RAZZ_BERRY)
@JvmField val BLUK_BERRY = berryItem("bluk", CobblemonBlocks.BLUK_BERRY)
@JvmField val NANAB_BERRY = berryItem("nanab", CobblemonBlocks.NANAB_BERRY)
@@ -12,6 +12,7 @@ import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.api.data.DataRegistry
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.mechanics.AprijuicesMechanic
import com.cobblemon.mod.common.mechanics.BerriesMechanic
import com.cobblemon.mod.common.mechanics.PotionsMechanic
import com.cobblemon.mod.common.mechanics.RemediesMechanic
@@ -37,6 +38,7 @@ object CobblemonMechanics : DataRegistry {
var remedies = RemediesMechanic()
var berries = BerriesMechanic()
var potions = PotionsMechanic()
var aprijuices = AprijuicesMechanic()
override fun sync(player: ServerPlayer) {}
@@ -44,6 +46,7 @@ object CobblemonMechanics : DataRegistry {
remedies = loadMechanic(manager, "remedies", RemediesMechanic::class.java)
berries = loadMechanic(manager, "berries", BerriesMechanic::class.java)
potions = loadMechanic(manager, "potions", PotionsMechanic::class.java)
aprijuices = loadMechanic(manager, "aprijuices", AprijuicesMechanic::class.java)
}
private fun <T> loadMechanic(manager: ResourceManager, name: String, clazz: Class<T>): T {
@@ -28,12 +28,13 @@ import com.cobblemon.mod.common.client.net.effect.RunPosableMoLangHandler
import com.cobblemon.mod.common.client.net.effect.SaccharineLogBlockParticlesHandler
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormEntityParticleHandler
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormParticleHandler
import com.cobblemon.mod.common.client.net.gui.ExpGainedDataPacketHandler
import com.cobblemon.mod.common.client.net.gui.InteractPokemonUIPacketHandler
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.orientation.ClientboundUpdateOrientationHandler
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
@@ -132,6 +133,7 @@ import com.cobblemon.mod.common.net.messages.client.trade.TradeOfferNotification
import com.cobblemon.mod.common.net.messages.client.trade.TradeProcessStartedPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeStartedPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeUpdatedPacket
import com.cobblemon.mod.common.net.messages.client.ui.ExpGainedDataPacket
import com.cobblemon.mod.common.net.messages.client.ui.InteractPokemonUIPacket
import com.cobblemon.mod.common.net.messages.client.ui.PokedexUIPacket
import com.cobblemon.mod.common.net.messages.client.ui.SummaryUIPacket
@@ -179,6 +181,7 @@ import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetNicknamePa
import com.cobblemon.mod.common.net.messages.server.pokemon.update.evolution.AcceptEvolutionPacket
import com.cobblemon.mod.common.net.messages.server.riding.DismountPokemonPacket
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateDriverInputPacket
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateRiderRotationPacket
import com.cobblemon.mod.common.net.messages.server.starter.RequestStarterScreenPacket
import com.cobblemon.mod.common.net.messages.server.storage.SwapPCPartyPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.party.MovePartyPokemonPacket
@@ -216,7 +219,7 @@ import com.cobblemon.mod.common.net.serverhandling.dialogue.EscapeDialogueHandle
import com.cobblemon.mod.common.net.serverhandling.dialogue.InputToDialogueHandler
import com.cobblemon.mod.common.net.serverhandling.evolution.AcceptEvolutionHandler
import com.cobblemon.mod.common.net.serverhandling.npc.SaveNPCHandler
import com.cobblemon.mod.common.net.serverhandling.orientation.OrientationPacketHandler
import com.cobblemon.mod.common.net.serverhandling.orientation.ServerboundUpdateOrientationHandler
import com.cobblemon.mod.common.net.serverhandling.pasture.PasturePokemonHandler
import com.cobblemon.mod.common.net.serverhandling.pasture.SetPastureConflictHandler
import com.cobblemon.mod.common.net.serverhandling.pasture.UnpastureAllPokemonHandler
@@ -231,6 +234,7 @@ import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetMarkingsHan
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetNicknameHandler
import com.cobblemon.mod.common.net.serverhandling.riding.DismountPokemonPacketHandler
import com.cobblemon.mod.common.net.serverhandling.riding.DriverInputPacketHandler
import com.cobblemon.mod.common.net.serverhandling.riding.ServerboundUpdateRiderRotationHandler
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
@@ -345,6 +349,7 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(InteractPokemonUIPacket.ID, InteractPokemonUIPacket::decode, InteractPokemonUIPacketHandler))
list.add(PacketRegisterInfo(PlayerInteractOptionsPacket.ID, PlayerInteractOptionsPacket::decode, PlayerInteractOptionsHandler))
list.add(PacketRegisterInfo(PokedexUIPacket.ID, PokedexUIPacket::decode, PokedexUIPacketHandler))
list.add(PacketRegisterInfo(ExpGainedDataPacket.ID, ExpGainedDataPacket::decode, ExpGainedDataPacketHandler))
// Starter packets
list.add(PacketRegisterInfo(OpenStarterUIPacket.ID, OpenStarterUIPacket::decode, StarterUIPacketHandler))
@@ -442,7 +447,7 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(UpdatePastureConflictFlagPacket.ID, UpdatePastureConflictFlagPacket::decode, UpdatePastureConflictFlagHandler))
// Orientation
list.add(PacketRegisterInfo(ClientboundUpdateOrientationPacket.ID, ClientboundUpdateOrientationPacket::decode, S2CUpdateOrientationHandler))
list.add(PacketRegisterInfo(ClientboundUpdateOrientationPacket.ID, ClientboundUpdateOrientationPacket::decode, ClientboundUpdateOrientationHandler))
// Behaviours
list.add(PacketRegisterInfo(PlayPosableAnimationPacket.ID, PlayPosableAnimationPacket::decode, PlayPosableAnimationHandler))
@@ -577,13 +582,14 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(SaveNPCPacket.ID, SaveNPCPacket::decode, SaveNPCHandler))
// Riding packet(s)
list.add(PacketRegisterInfo(ServerboundUpdateOrientationPacket.ID, ServerboundUpdateOrientationPacket::decode, OrientationPacketHandler))
list.add(PacketRegisterInfo(ServerboundUpdateOrientationPacket.ID, ServerboundUpdateOrientationPacket::decode, ServerboundUpdateOrientationHandler))
list.add(PacketRegisterInfo(ServerboundUpdateRidingStatePacket.ID, ServerboundUpdateRidingStatePacket::decode, ServerboundUpdateRidingStateHandler))
list.add(PacketRegisterInfo(ServerboundUpdateRidingStatsPacket.ID, ServerboundUpdateRidingStatsPacket::decode, ServerboundUpdateRidingStatsHandler))
list.add(PacketRegisterInfo(ServerboundUpdateRidingStatRangePacket.ID, ServerboundUpdateRidingStatRangePacket::decode, ServerboundUpdateRidingStatRangeHandler))
list.add(PacketRegisterInfo(ServerboundUpdateRidingSettingsPacket.ID, ServerboundUpdateRidingSettingsPacket::decode, ServerboundUpdateRidingSettingsHandler))
list.add(PacketRegisterInfo(DismountPokemonPacket.ID, DismountPokemonPacket::decode, DismountPokemonPacketHandler))
list.add(PacketRegisterInfo(ServerboundUpdateDriverInputPacket.ID, ServerboundUpdateDriverInputPacket::decode, DriverInputPacketHandler))
list.add(PacketRegisterInfo(ServerboundUpdateRiderRotationPacket.ID, ServerboundUpdateRiderRotationPacket::decode, ServerboundUpdateRiderRotationHandler))
// Cooking
list.add(PacketRegisterInfo(ToggleCookingPotLidPacket.ID, ToggleCookingPotLidPacket::decode, ToggleCookingPotLidHandler))
@@ -26,6 +26,7 @@ import com.cobblemon.mod.common.api.riding.behaviour.types.land.VehicleSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BoatSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BurstSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.DolphinSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.SubmarineSettings
import com.cobblemon.mod.common.net.messages.client.data.RideSettingsSyncPacket
import com.cobblemon.mod.common.util.adapters.ExpressionAdapter
import com.cobblemon.mod.common.util.adapters.ExpressionLikeAdapter
@@ -63,6 +64,7 @@ object CobblemonRideSettings : DataRegistry {
var boat = BoatSettings()
var burst = BurstSettings()
var dolphin = DolphinSettings()
var submarine = SubmarineSettings()
override fun sync(player: ServerPlayer) {
player.sendPacket(
@@ -78,7 +80,8 @@ object CobblemonRideSettings : DataRegistry {
vehicle = vehicle,
boat = boat,
burst = burst,
dolphin = dolphin
dolphin = dolphin,
submarine = submarine
)
)
}
@@ -96,6 +99,7 @@ object CobblemonRideSettings : DataRegistry {
boat = loadStyle(manager, "boat", BoatSettings::class.java)
burst = loadStyle(manager, "burst", BurstSettings::class.java)
dolphin = loadStyle(manager, "dolphin", DolphinSettings::class.java)
submarine = loadStyle(manager, "submarine", SubmarineSettings::class.java)
}
private fun <T : RidingBehaviourSettings> loadStyle(manager: ResourceManager, name: String, clazz: Class<T>): T {
@@ -103,6 +103,11 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
@JvmField
val EVOLUTION = this.create("evolution.full")
@JvmField
val LEVELUP_START = this.create("gui.levelup_start")
@JvmField
val LEVELUP = this.create("gui.levelup")
@JvmField
val PVN_BATTLE = this.create("battle.pvn.default")
@JvmField
@@ -47,12 +47,23 @@ class ApplyBehaviours : BehaviourConfig {
override fun configure(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) {
if (!checkCondition(behaviourConfigurationContext, condition)) return
val configurations = behaviours.map { CobblemonBehaviours.behaviours[it]?.takeIf { it.canBeApplied(entity) } ?: return Cobblemon.LOGGER.warn("Behaviour $it not found") }
val applicableBehaviours = behaviours.mapNotNull {
val resolvedBehaviour = CobblemonBehaviours.behaviours[it]
if (resolvedBehaviour == null) {
Cobblemon.LOGGER.warn("Behaviour $it not found while configuring entity of type ${entity.type}")
} else if (!resolvedBehaviour.canBeApplied(entity)) {
Cobblemon.LOGGER.warn("Behaviour $it cannot be applied to entity ${entity.id}")
} else {
return@mapNotNull resolvedBehaviour
}
null
}
// Why not just add the presets to the context directly?
// Nested preset application is a thing, and I only want to track the top level presets.
// i.e. if a preset applies another preset, I don't want to track the inner preset, since it's redundant if we track the top one.
val originalContextBehaviours = behaviourConfigurationContext.appliedBehaviours.toMutableSet()
configurations.forEach {
applicableBehaviours.forEach {
it.configure(entity, behaviourConfigurationContext)
}
originalContextBehaviours.addAll(behaviours)
@@ -38,7 +38,7 @@ class MoveToOwnerTaskConfig : SingleTaskConfig {
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
): BehaviorControl<in LivingEntity> {
behaviourConfigurationContext.addMemories(
MemoryModuleType.WALK_TARGET,
MemoryModuleType.LOOK_TARGET
@@ -110,7 +110,7 @@ class PlaceHoneyInSaccLeavesTaskConfig : SingleTaskConfig {
override fun stop(level: ServerLevel, entity: LivingEntity, gameTime: Long) {
if (successfulPollinationTicks > REQUIRED_SUCCESSFUL_POLLINATION_TICKS) {
val blockPos = entity.brain.getMemorySafely(CobblemonMemories.NEARBY_SACC_LEAVES).orElse(null)
blockPos.let {
blockPos?.let {
val blockState = level.getBlockState(blockPos)
if (blockState.block == CobblemonBlocks.SACCHARINE_LEAVES) {
val saccAge = blockState.getValue( SaccharineLeafBlock.AGE)
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.api.apricorn
import com.cobblemon.mod.common.CobblemonBlocks
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.cooking.Flavour
import com.cobblemon.mod.common.block.ApricornBlock
import com.cobblemon.mod.common.block.ApricornSaplingBlock
import com.cobblemon.mod.common.item.ApricornItem
@@ -28,43 +27,14 @@ import net.minecraft.world.level.material.MapColor
* @author Licious
* @since October 29th, 2022
*/
enum class Apricorn(
/** A value of true means it buffs that flavour, a value of false means it weakens that flavour. */
val flavourStrength: Map<Flavour, Boolean> = emptyMap()
) : StringRepresentable {
enum class Apricorn : StringRepresentable {
BLACK,
BLUE(
mapOf(
Flavour.DRY to true,
Flavour.BITTER to false
)
),
GREEN(
mapOf(
Flavour.BITTER to true,
Flavour.SWEET to false
)
),
PINK(
mapOf(
Flavour.SWEET to true,
Flavour.SPICY to false
)
),
RED(
mapOf(
Flavour.SPICY to true,
Flavour.SOUR to false
)
),
BLUE,
GREEN,
PINK,
RED,
WHITE,
YELLOW(
mapOf(
Flavour.SOUR to true,
Flavour.DRY to false
)
);
YELLOW;
fun item(): ApricornItem = when(this) {
BLACK -> CobblemonItems.BLACK_APRICORN
@@ -131,5 +101,4 @@ enum class Apricorn(
companion object {
val CODEC: Codec<Apricorn> = StringRepresentable.fromValues(::values)
}
}
@@ -0,0 +1,29 @@
/*
* 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.berry.spawncondition
import com.cobblemon.mod.common.api.berry.Berry
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.core.Holder
import net.minecraft.tags.TagKey
import net.minecraft.util.RandomSource
import net.minecraft.world.level.biome.Biome
class SpecificBiomeCondition : BerrySpawnCondition {
lateinit var biome: TagKey<Biome>
val minGroveSize: Int = 1
val maxGroveSize: Int = 1
override fun canSpawn(berry: Berry, biome: Holder<Biome>) = biome.`is`(this.biome)
override fun getGroveSize(random: RandomSource) = random.nextIntBetweenInclusive(minGroveSize, maxGroveSize)
companion object {
val ID = cobblemonResource("specific_biome")
}
}
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.api.cooking
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import net.minecraft.util.FastColor
import net.minecraft.world.item.ItemStack
@@ -65,6 +66,10 @@ fun getColourMixFromFlavours(dominantFlavours: List<Flavour>, forBubbles: Boolea
)
}
fun getColourMixFromRideStatBoosts(dominantBoosts: Iterable<RidingStat>, forBubbles: Boolean = false): Int? {
return getColourMixFromFlavours(dominantBoosts.map { it.flavour }, forBubbles)
}
fun getColourMixFromColors(colors: List<Int>): Int? {
if (colors.isEmpty()) return null
@@ -22,6 +22,7 @@ interface PokemonSideDelegate : EntitySideDelegate<PokemonEntity> {
fun handleStatus(status: Byte) {}
fun spawnShinyParticle(player: Player) {}
fun spawnAspectParticle() {}
fun applyRenderRotation(partialTick: Float) {}
fun positionRider(passenger: Entity, positionUpdater: MoveFunction)
fun updateAge(age: Int) {}
}
@@ -17,18 +17,11 @@ object SpawnBaitUtils {
return grouped.map { (key, groupEffects) ->
val (type, subcategory) = key
val count = groupEffects.size
val totalChance = groupEffects.sumOf { it.chance }
val totalValue = groupEffects.sumOf { it.value }
val multiplier = when (count) {
2 -> 0.8
3 -> 0.6
else -> 1.0
}
val adjustedChance = (totalChance * multiplier).coerceAtMost(1.0)
val adjustedValue = ceil(totalValue * multiplier)
val adjustedChance = totalChance.coerceAtMost(1.0)
val adjustedValue = ceil(totalValue)
SpawnBait.Effect(type, subcategory, adjustedChance, adjustedValue)
}
@@ -53,8 +53,7 @@ open class MoveTemplate(
val pp: Int,
val priority: Int,
val critRatio: Double,
val effectChances: Array<Double>,
val actionEffect: ActionEffectTimeline?
val effectChances: Array<Double>
) {
val struct: MoStruct by lazy {
QueryStruct(hashMapOf())
@@ -87,8 +86,7 @@ open class MoveTemplate(
pp = 5,
priority = 0,
critRatio = 0.0,
effectChances = emptyArray(),
actionEffect = null
effectChances = emptyArray()
)
companion object {
@@ -92,18 +92,7 @@ object Moves : DataRegistry {
effectChances += secondaryMember.get("chance").asDouble
}
}
val actionEffect = ActionEffects.actionEffects[id.asIdentifierDefaultingNamespace()]
?: run {
ActionEffects.actionEffects["generic_move".asIdentifierDefaultingNamespace()]
// if (damageCategory == DamageCategories.STATUS) {
// ActionEffects.actionEffects[cobblemonResource("status")]
// } else {
// val type = elementalType.name.lowercase()
// val category = damageCategory.name.lowercase()
// ActionEffects.actionEffects["${category}_$type".asIdentifierDefaultingNamespace()]
// }
}
val move = MoveTemplate(id, num, elementalType, damageCategory, power, target, accuracy, pp, priority, critRatio, effectChances.toTypedArray(), actionEffect)
val move = MoveTemplate(id, num, elementalType, damageCategory, power, target, accuracy, pp, priority, critRatio, effectChances.toTypedArray())
this.register(move)
} catch (e: Exception) {
Cobblemon.LOGGER.error("Caught exception trying to resolve the move '{}'", id, e)
@@ -14,6 +14,7 @@ import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.moves.animations.keyframes.*
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.pokemon.adapters.CobblemonStatTypeAdapter
import com.cobblemon.mod.common.util.adapters.*
import com.cobblemon.mod.common.util.cobblemonResource
@@ -91,5 +92,11 @@ object ActionEffects : JsonDataRegistry<ActionEffectTimeline> {
observable.emit(this)
}
fun getEffectWithBattleContext(id: ResourceLocation, pokemon: BattlePokemon): ActionEffectTimeline? {
val species = pokemon.entity?.exposedSpecies ?: pokemon.effectedPokemon.species
val contextedEffect = actionEffects[ResourceLocation.fromNamespaceAndPath(id.namespace, id.path + "_" + species)]
return contextedEffect ?: actionEffects[id]
}
override fun sync(player: ServerPlayer) {}
}
@@ -63,7 +63,7 @@ open class OrientationController(val entity: LivingEntity) {
}
fun getRenderOrientation(delta: Float): Quaternionf {
val old = renderOrientationO ?: renderOrientation ?: orientation
val old = renderOrientationO ?: renderOrientation ?: orientation ?: Matrix3f()
val new = renderOrientation ?: old
val oldQuat = Quaternionf().setFromUnnormalized(old)
val newQuat = Quaternionf().setFromUnnormalized(new)
@@ -170,11 +170,8 @@ object Natures {
* Utility method to get a nature by string
* @return a nature type or null
*/
fun getNature(identifier: String): Nature? {
val nature = getNature(cobblemonResource(identifier))
if(nature != null) return nature
return getNature(ResourceLocation.parse(identifier))
}
fun getNature(identifier: String) = getNature(cobblemonResource(identifier))
?: getNature(ResourceLocation.parse(identifier))
/**
* Helper function for a random Nature
@@ -39,7 +39,6 @@ import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
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
@@ -129,7 +128,6 @@ object PokemonSpecies : JsonDataRegistry<Species> {
.registerTypeAdapter(MobEffect::class.java, RegistryElementAdapter<MobEffect>(BuiltInRegistries::MOB_EFFECT))
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter)
.registerTypeAdapter(RidingBehaviourSettings::class.java, RidingBehaviourSettingsAdapter)
.registerTypeAdapter(RidingStatDefinition::class.java, RidingStatDefinitionAdapter)
.registerTypeAdapter(RideSoundSettingsList::class.java, RideSoundSettingsListAdapter)
.registerTypeAdapter(ObtainableItemCondition::class.java, ObtainableItemConditionAdapter)
.disableHtmlEscaping()
@@ -103,6 +103,18 @@ interface RidingBehaviour<Settings : RidingBehaviourSettings, State : RidingBeha
*/
fun damageOnCollision(settings: Settings, state: State, vehicle: PokemonEntity, impactVec: Vec3): Boolean = false
/**
* Called in [com.cobblemon.mod.common.client.render.pokemon.PokemonRenderer] to allow the controller to set rotations
* through code using partialTicks and right before rendering.
* //TODO: This needs to be further refined. Maybe only the orientationController needs affected? Maybe the rotations set here don't need to be applied to the controller/camera
*/
fun applyRenderRotation(settings: Settings, state: State, vehicle: PokemonEntity, partialTicks: Float) {}
/**
* Determines if the pair of mouse inputs Pair(mouseX, mouseY) get used to modify the driverRotations or the vehicle rotations themselves.
* Driver rotations are the rotations used by passengers or a driver that is freelooking. This does not override the freelook button.
*/
fun mouseModifiesDriverRotation(settings: Settings, state: State, vehicle: PokemonEntity): Pair<Boolean, Boolean> = Pair(false, false)
/**
* Internal helpers to help behaviour specific calculations
@@ -16,6 +16,7 @@ import com.cobblemon.mod.common.api.riding.behaviour.types.land.MinekartBehaviou
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BoatBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BurstBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.DolphinBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.SubmarineBehaviour
import net.minecraft.resources.ResourceLocation
object RidingBehaviours {
@@ -34,6 +35,7 @@ object RidingBehaviours {
register(MinekartBehaviour.KEY, MinekartBehaviour())
register(HoverBehaviour.KEY, HoverBehaviour())
register(RocketBehaviour.KEY, RocketBehaviour())
register(SubmarineBehaviour.KEY, SubmarineBehaviour())
register(CompositeBehaviour.KEY, CompositeBehaviour())
}
@@ -19,7 +19,9 @@ import net.minecraft.world.entity.LivingEntity
import kotlin.collections.get
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec3
import kotlin.collections.get
import kotlin.math.max
/**
* Small wrapper around a RidingBehaviour to provide sane defaults in the event that the behaviour is not active.
@@ -49,6 +51,11 @@ class RidingController(
fun tick() {
if (entity.ticksLived - lastTransitionAge < 10) return
val newTransition = checkForNewTransition(entity.controllingPassenger)
// If transitioning to air then give an upward boost
if (newTransition == RidingStyle.AIR && context?.style != RidingStyle.AIR && context?.state != null) {
val currVel = context!!.state.rideVelocity.get()
context!!.state.rideVelocity.set(Vec3(currVel.x, max(0.4, currVel.y), currVel.z))
}
val behaviourSettings = entity.pokemon.riding.behaviours?.get(newTransition)
if (newTransition != context?.style) {
if (newTransition != null && behaviourSettings != null) {
@@ -59,7 +66,7 @@ class RidingController(
val newKey = behaviourSettings.key
for (passenger in entity.passengers) {
if (passenger is Player && passenger !is ServerPlayer) {
MountedCameraTypeHandler.handleTransition(previousKey ?: continue, newKey)
MountedCameraTypeHandler.handleTransition(passenger, previousKey ?: continue, newKey)
}
}
@@ -34,6 +34,7 @@ import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
import org.joml.Vector3f
import kotlin.math.*
class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
@@ -88,7 +89,8 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
vehicle: PokemonEntity,
driver: Player,
) {
val controller = (driver as OrientationControllable).orientationController
// Also checks stamina
val controller = (vehicle as OrientationControllable).orientationController
if (cos(controller.roll.toRadians()) < 0.0) {
val upsideDownRate = 1.0 / (2.0 * 20.0) // 2 seconds to max out upsideDown force
state.currUpsideDownForce.set( min(1.0, state.currUpsideDownForce.get() + upsideDownRate * abs(cos(controller.roll.toRadians()))))
@@ -109,6 +111,10 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
val stam = state.stamina.get()
val controller = (driver as OrientationControllable).orientationController
if (vehicle.runtime.resolveBoolean(settings.infiniteStamina ?: globalBird.infiniteStamina!!)) {
return
}
// Grab the boost time in seconds and convert to ticks. Then calculate the drain rate as inversely
// proportional to the number of ticks of boost thus making a full boost take x ticks
// in short: "Stamina drains slower at higher values and also replenishes slower"
@@ -135,7 +141,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
vehicle: PokemonEntity,
driver: LivingEntity
): Vec2 {
return Vec2(driver.xRot * 0.5f, driver.yRot)
return Vec2.ZERO//Vec2(driver.xRot * 0.5f, driver.yRot)
}
override fun velocity(
@@ -145,29 +151,26 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
driver: Player,
input: Vec3
): Vec3 {
var upForce = 0.0
var forwardForce = 0.0
val controller = (vehicle as? OrientationControllable)?.orientationController
if (controller == null || controller.orientation == null) return Vec3.ZERO
//Perform ride velocity update
calculateRideSpaceVel(settings, state, vehicle, driver)
//Translate ride space velocity to world space velocity.
val controller = (driver as? OrientationControllable)?.orientationController
if (controller != null) {
//Need to deadzone this when straight up or down
upForce += -1.0 * sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().z
forwardForce += cos(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().z
upForce += cos(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().y
forwardForce += sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().y
}
// The downward force used to encourage players to stop flying upside down.
val extraDownwardForce = state.currUpsideDownForce.get().pow(2) * -0.3 // 6 blocks a second downward
val velocity = Vec3(state.rideVelocity.get().x , upForce + extraDownwardForce, forwardForce)
return velocity
// Convert the local velocity vector into a world vector
val localVelVec = Vector3f(
state.rideVelocity.get().x.toFloat(),
(state.rideVelocity.get().y).toFloat(),
state.rideVelocity.get().z.toFloat() * -1.0f // Flip the z axis to make this left handed
)
// Unrotate preemptively as this vector gets rotatee later down the line in MC logic.
var worldVelVec = localVelVec.mul(controller.orientation).toVec3d().yRot(vehicle.yRot.toRadians())
worldVelVec = worldVelVec.add(0.0, extraDownwardForce, 0.0) // Add the stamina depletion force to bring the ride down.
return worldVelVec
}
/*
@@ -206,7 +209,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
activeInput = true
}
val controller = (driver as? OrientationControllable)?.orientationController
val controller = (vehicle as? OrientationControllable)?.orientationController
if (controller != null) {
//Base glide speed change on current pitch of the ride.
glideSpeedChange = sin(Math.toRadians(controller.pitch.toDouble()))
@@ -282,27 +285,28 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
driver: Player,
deltaTime: Double
): Vec3 {
if (Cobblemon.config.disableRoll) return Vec3.ZERO
if (driver !is OrientationControllable) return Vec3.ZERO
val controller = (driver as OrientationControllable).orientationController
if (vehicle !is OrientationControllable) return Vec3.ZERO
val controller = (vehicle as OrientationControllable).orientationController
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalBird.handlingExpr!!)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalBird.speedExpr!!) / 20.0
val yawDeltaDeg = deltaTime * handling * sin(Math.toRadians(controller.roll.toDouble())) //* sin(Math.toRadians(controller.roll.toDouble())).sign
val trueYawDelt = yawDeltaDeg * abs(cos(Math.toRadians(controller.pitch.toDouble())))// * sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed))
val yawDeltaDeg = deltaTime * handling * sin(Math.toRadians(controller.roll.toDouble()))
val trueYawDelt = yawDeltaDeg * abs(cos(Math.toRadians(controller.pitch.toDouble()))).coerceIn(-1.0, 1.0)
// Dampen yaw when upside down
val yawDampen = (1 - abs(min(cos(controller.roll.toRadians()),0.0f)).pow(2))
val yawDampen = (1 - abs(min(cos(controller.roll.toRadians()),0.0f)))
controller.applyGlobalYaw(trueYawDelt.toFloat() * yawDampen)
// Correct orientation when at low speeds
correctOrientation(settings, state, vehicle, controller, deltaTime)
controller.applyGlobalPitch(state.currPitchCorrectionForce.get().toRadians())
val pCorr = (state.currPitchCorrectionForce.get() * deltaTime).toFloat()
val rCorr = (state.currRollCorrectionForce.get() * deltaTime).toFloat()
controller.applyGlobalPitch(pCorr)
controller.rotateRoll(rCorr)
//yaw, pitch, roll
return Vec3(0.0, 0.0, state.currRollCorrectionForce.get())
return Vec3(0.0, 0.0, 0.0)
}
private fun correctOrientation(
@@ -313,42 +317,48 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
deltaTime: Double
) {
// Calculate correcting roll force.
// Mult by cos(pitch) to ensure that it doesn't roll correct at when diving or climbing
val rollCorrectionTimer = vehicle.runtime.resolveDouble(settings.timeToRollCorrect ?: globalBird.timeToRollCorrect!!)
val maxRollCorrectionRate = if (state.rideVelocity.get().length() < 0.1) 15.0f else 8.0f * (cos(controller.pitch.toRadians())).pow(2) * ((state.noInputTimeRoll.get() - rollCorrectionTimer)).coerceIn(0.0, 1.0).toFloat()
val maxRollForce = maxRollCorrectionRate / 40.0
val rollArrivalDeg = 90.0
if(state.rideVelocity.get().length() < 0.1 || state.noInputTimeRoll.get() > rollCorrectionTimer) {
val arrivalInfluence = (min(rollArrivalDeg,abs(controller.roll).toDouble()) / rollArrivalDeg)
val desiredRollForce = (0 - controller.roll).sign * maxRollCorrectionRate * arrivalInfluence
var steeredRollForce = desiredRollForce - state.currRollCorrectionForce.get()
// If the ride is moving then have the correction rate be an equal proportion of:
// - how rolled are you
// - how long has it been since last mouse input (higher is stronger correction)
// Also the correction is proportional as a whole to how NOT pitched are you so you don't spin endlessly when looking up or down
val howRolledAmI = sqrt(abs((cos(controller.roll.toRadians()) - 1.0f) * 0.5f)) // sqrt so it trends towards one pretty quickly
val maxRollCorrectionRate =
if (state.rideVelocity.get().length() < 0.2) 45.0f
else 45.0f * cos(controller.pitch.toRadians()).pow(2) * (
howRolledAmI*1.0f +
(state.noInputTimeRoll.get() - rollCorrectionTimer).coerceIn(0.0, 1.0).pow(2).toFloat()*0.5f
)
val maxRollForce = maxRollCorrectionRate / 40.0f
val rollArrivalDeg = 35.0f
val desiredRoll = 0.0f
// Ensure the 'steering' correction force doesn't exceed our maxForce
if (abs(steeredRollForce) > maxRollForce) {
steeredRollForce = steeredRollForce.sign * maxRollForce
}
// Apply the 'steered' force (its really not a steering force I need a better name)
state.currRollCorrectionForce.set(
state.currRollCorrectionForce.get() + steeredRollForce
)
} else {
state.currRollCorrectionForce.set(
//reduce rollCorrection gradually
lerp(state.currRollCorrectionForce.get(), 0.0, deltaTime * 0.96)
)
// Calculate the signed error (how far and in what direction)
val rollError = Mth.wrapDegrees(desiredRoll.angleDifference(controller.roll))
// Use the absolute error to calculate the magnitude of the influence
val influenceMagnitude = (min(rollArrivalDeg, abs(rollError)) / rollArrivalDeg)
val desiredRollForce = maxRollCorrectionRate * influenceMagnitude * -rollError.sign
var steeredRollForce = desiredRollForce - state.currRollCorrectionForce.get()
// Ensure the 'steering' correction force doesn't exceed our maxForce
if (abs(steeredRollForce) > maxRollForce) {
steeredRollForce = steeredRollForce.sign * maxRollForce
}
// Apply the 'steered' force (its really not a steering force I need a better name)
state.currRollCorrectionForce.set(
state.currRollCorrectionForce.get() + steeredRollForce
)
//Calculate correcting pitch force.
val maxPitchCorrectionRate = if (state.currUpsideDownForce.get() != 0.0) 8f * state.currUpsideDownForce.get() else 15f
val maxPitchForce = maxPitchCorrectionRate.toFloat() / 40.0f
val pitchArrivalDeg = 45.0f
var desiredPitch = if (state.currUpsideDownForce.get() != 0.0) 90.0 else 0.0
val maxPitchCorrectionRate = if (state.currUpsideDownForce.get() != 0.0) 15.0f * state.currUpsideDownForce.get().toFloat() else 30.0f
val maxPitchForce = maxPitchCorrectionRate / 40.0f
val pitchArrivalDeg = 35.0f
val desiredPitch = if (state.currUpsideDownForce.get() != 0.0) 90.0f else 0.0f
if((state.rideVelocity.get().length() < 0.1 && controller.pitch < 0.0) || state.currUpsideDownForce.get() != 0.0) {
val arrivalInfluence = (min(pitchArrivalDeg,abs(Mth.wrapDegrees(desiredPitch.toFloat().angleDifference(controller.pitch)))) / pitchArrivalDeg)
val desiredPitchForce = maxPitchCorrectionRate.toFloat() * arrivalInfluence
var steeredPitchForce = (desiredPitchForce - state.currPitchCorrectionForce.get())
if((state.rideVelocity.get().length() < 0.1) || state.currUpsideDownForce.get() != 0.0) {
val pitchErrror = Mth.wrapDegrees(desiredPitch.angleDifference(controller.pitch))
val arrivalInfluence = (min(pitchArrivalDeg,abs(pitchErrror)) / pitchArrivalDeg)
val desiredPitchForce = maxPitchCorrectionRate * arrivalInfluence * -pitchErrror.sign
var steeredPitchForce = desiredPitchForce - state.currPitchCorrectionForce.get()
// Ensure the 'steering' correction force doesn't exceed our maxForce
if (abs(steeredPitchForce) > maxPitchForce) {
@@ -398,80 +408,18 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
state.noInputTimePitch.set(0.0)
}
return when {
Cobblemon.config.disableRoll -> noRollRotation(
settings,
state,
vehicle,
driver,
mouseY,
mouseX,
mouseYSmoother,
mouseXSmoother,
sensitivity,
deltaTime
return rollRotation(
settings,
state,
vehicle,
driver,
mouseY,
mouseX,
mouseYSmoother,
mouseXSmoother,
sensitivity,
deltaTime
)
else -> rollRotation(
settings,
state,
vehicle,
driver,
mouseY,
mouseX,
mouseYSmoother,
mouseXSmoother,
sensitivity,
deltaTime
)
}
}
fun noRollRotation(
settings: BirdSettings,
state: BirdState,
vehicle: PokemonEntity,
driver: Player,
mouseY: Double,
mouseX: Double,
mouseYSmoother: SmoothDouble,
mouseXSmoother: SmoothDouble,
sensitivity: Double,
deltaTime: Double
): Vec3 {
if (driver !is OrientationControllable) return Vec3.ZERO
val controller = (driver as OrientationControllable).orientationController
// Set roll to zero if transitioning to noroll config
controller.rotateRoll(controller.roll * -1.0f)
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalBird.handlingExpr!!)
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult ?: globalBird.mouseInputMult!!)
//Smooth out mouse input.
val smoothingSpeed = 4.0
val mouseXc = ((mouseX).coerceIn(-60.0, 60.0) / 60.0) * mouseInputMult
val mouseYc = ((mouseY).coerceIn(-60.0, 60.0) / 60.0) * mouseInputMult
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1, deltaTime * smoothingSpeed);
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1, deltaTime * smoothingSpeed);
//Give the ability to yaw with x mouse input when at low speeds.
val yawForce = xInput * handling //* ( 1.0 - min(sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed)), 0.5))
//Apply yaw globally as we don't want roll or pitch changes due to local yaw when looking up or down.
controller.applyGlobalYaw(yawForce.toFloat() * abs(cos(controller.pitch.toRadians())).pow(2))
var pitchRot = yInput * handling
// Pitch up globally
if (abs(controller.pitch + pitchRot) >= 89.5 ) {
pitchRot = 0.0
mouseYSmoother.reset()
} else {
controller.applyGlobalPitch(pitchRot.toFloat() * -1.0f)
}
//yaw, pitch, roll
return Vec3.ZERO
}
fun rollRotation(
@@ -486,21 +434,23 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
sensitivity: Double,
deltaTime: Double
): Vec3 {
if (driver !is OrientationControllable) return Vec3.ZERO
val controller = (driver as OrientationControllable).orientationController
if (vehicle !is OrientationControllable) return Vec3.ZERO
val controller = (vehicle as OrientationControllable).orientationController
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalBird.speedExpr!!) / 20.0
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult ?: globalBird.mouseInputMult!!)
// Applied to mouseX to prevent turning upside down when trying to roll hard.
val rollDegreeDebuff = if(mouseX.sign.toFloat() == controller.roll.sign) 1 - 0.9f*sqrt(abs(sin(controller.roll.toRadians())))*sqrt(abs(cos(controller.pitch))) else 1.0f
//Smooth out mouse input.
val smoothingSpeed = 2.0
val mouseXc = (mouseX).coerceIn(-45.0, 45.0) * mouseInputMult
val mouseXc = (mouseX).coerceIn(-45.0, 45.0) * mouseInputMult * rollDegreeDebuff
// When upside down apply a pitching force to nudge the player towards a dive. This reduces the 'weirdness'
// of being upside down
val rolledOverPitchCorrection = -8*sqrt(abs(min(cos(controller.roll.toRadians()),0.0f))) * (cos(controller.pitch.toRadians())).pow(2)
val mouseYc = ((mouseY).coerceIn(-45.0, 45.0) * mouseInputMult) + rolledOverPitchCorrection
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1, deltaTime * smoothingSpeed);
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1, deltaTime * smoothingSpeed);
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1, deltaTime * smoothingSpeed)
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1, deltaTime * smoothingSpeed)
val currSpeed = state.rideVelocity.get().length()
val currHorzSpeed = state.rideVelocity.get().horizontalDistance()
val hoverSpeed = 0.1
@@ -508,8 +458,6 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
// Yaw locally when at or below hoverSpeed but not when pitched up or rolled over
val globalYaw = xInput * (1 - sqrt(RidingBehaviour.scaleToRange(currHorzSpeed, hoverSpeed, topSpeed))).coerceIn(0.0, 1.0)
//xInput * 0.4 * cos(controller.roll.toRadians()).coerceIn(0.0f, 1.0f) * yawDampen
controller.applyGlobalYaw(globalYaw.toFloat())
// Pitch locally up or down depending upon a number of factors:
@@ -643,7 +591,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
state: BirdState,
vehicle: PokemonEntity
): Boolean {
return false
return true
}
override fun damageOnCollision(
@@ -51,7 +51,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
val poseProvider = PoseProvider<HoverSettings, HoverState>(PoseType.STAND)
.with(PoseOption(PoseType.WALK) { _, state, _ ->
return@PoseOption abs(state.rideVelocity.get().z) > 0.2
return@PoseOption abs(state.rideVelocity.get().length()) > 0.01
})
override fun isActive(
@@ -161,7 +161,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
driver: Player,
input: Vec3
): Vec3 {
val controller = (driver as? OrientationControllable)?.orientationController
val controller = (vehicle as? OrientationControllable)?.orientationController
if (controller == null || controller.orientation == null) return Vec3.ZERO
//Calculate ride space velocity
@@ -265,8 +265,6 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
driver: Player,
deltaTime: Double
): Vec3 {
//don't yaw this way if
if (Cobblemon.config.disableRoll) return Vec3.ZERO
//Cap at a rate of 5fps so frame skips dont lead to huge jumps
val cappedDeltaTime = min(deltaTime, 0.2)
@@ -298,107 +296,8 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
sensitivity: Double,
deltaTime: Double
): Vec3 {
return when {
Cobblemon.config.disableRoll -> noRollRotation(
settings,
state,
vehicle,
driver,
mouseY,
mouseX,
mouseYSmoother,
mouseXSmoother,
sensitivity,
deltaTime
)
else -> rollRotation(
settings,
state,
vehicle,
driver,
mouseY,
mouseX,
mouseYSmoother,
mouseXSmoother,
sensitivity,
deltaTime
)
}
}
if (vehicle !is OrientationControllable) return Vec3.ZERO
fun noRollRotation(
settings: JetSettings,
state: JetState,
vehicle: PokemonEntity,
driver: Player,
mouseY: Double,
mouseX: Double,
mouseYSmoother: SmoothDouble,
mouseXSmoother: SmoothDouble,
sensitivity: Double,
deltaTime: Double
): Vec3 {
if (driver !is OrientationControllable) return Vec3.ZERO
val controller = (driver as OrientationControllable).orientationController
// Set roll to zero if transitioning to noroll config
controller.rotateRoll(controller.roll * -1.0f)
//Cap at a rate of 5fps so frame skips dont lead to huge jumps
val cappedDeltaTime = min(deltaTime, 0.2)
// Accumulate the mouse input
state.currMouseXForce.set((state.currMouseXForce.get() + (0.0015 * mouseX)).coerceIn(-1.0, 1.0))
state.currMouseYForce.set((state.currMouseYForce.get() + (0.0015 * mouseY)).coerceIn(-1.0, 1.0))
//Get handling in degrees per second
val handlingDebuff = if(state.stamina.get() == 0.0f) 0.5 else 1.0
var handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalJet.handlingExpr!!) * handlingDebuff
//convert it to delta time
handling *= (cappedDeltaTime)
var pitchRot = handling * state.currMouseYForce.get()
// Yaw
val yawRot = handling * 0.5 * state.currMouseXForce.get()
controller.applyGlobalYaw(yawRot.toFloat())
if (abs(controller.pitch + pitchRot) >= 89.5 ) {
pitchRot = 0.0
state.currMouseYForce.set(0.0)
mouseYSmoother.reset()
} else {
controller.applyGlobalPitch(pitchRot.toFloat() * -1.0f)
}
// Have accumulated input begin decay when no input detected
if (abs(mouseX) == 0.0) {
// Have decay on roll be much stronger.
state.currMouseXForce.set(lerp(state.currMouseXForce.get(), 0.0, 0.02))
}
if (mouseY == 0.0) {
state.currMouseYForce.set(lerp(state.currMouseYForce.get(), 0.0, 0.005))
}
//yaw, pitch, roll
return Vec3.ZERO
}
fun rollRotation(
settings: JetSettings,
state: JetState,
vehicle: PokemonEntity,
driver: Player,
mouseY: Double,
mouseX: Double,
mouseYSmoother: SmoothDouble,
mouseXSmoother: SmoothDouble,
sensitivity: Double,
deltaTime: Double
): Vec3 {
if (driver !is OrientationControllable) return Vec3.ZERO
//TODO: figure out a cleaner solution to this issue of large jumps when skipping frames or lagging
//Cap at a rate of 5fps so frame skips dont lead to huge jumps
val cappedDeltaTime = min(deltaTime, 0.2)
// Accumulate the mouse input
@@ -412,14 +311,6 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
//convert it to delta time
handling *= (cappedDeltaTime)
val poke = driver.vehicle as? PokemonEntity
//TODO: reevaluate if deadzones are needed and if they are still causing issues.
//create deadzones for the constant input values.
//val xInput = remapWithDeadzone(state.currMouseXForce, 0.025, 1.0)
//val yInput = remapWithDeadzone(state.currMouseYForce, 0.025, 1.0)
val pitchRot = handling * state.currMouseYForce.get()
// Roll
@@ -430,10 +321,10 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
// Have accumulated input begin decay when no input detected
if (abs(mouseX) == 0.0) {
// Have decay on roll be much stronger.
state.currMouseXForce.set(lerp(state.currMouseXForce.get(), 0.0, 0.02))
state.currMouseXForce.set(lerp(state.currMouseXForce.get(), 0.0, 0.08))
}
if (mouseY == 0.0) {
state.currMouseYForce.set(lerp(state.currMouseYForce.get(), 0.0, 0.005))
state.currMouseYForce.set(lerp(state.currMouseYForce.get(), 0.0, 0.02))
}
//yaw, pitch, roll
@@ -31,6 +31,7 @@ import com.cobblemon.mod.common.util.resolveDouble
import com.cobblemon.mod.common.util.resolveFloat
import com.cobblemon.mod.common.util.writeNullableExpression
import com.cobblemon.mod.common.util.writeRidingStats
import net.minecraft.client.Minecraft
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
@@ -65,7 +66,7 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
val poseProvider = PoseProvider<HorseSettings, HorseState>(PoseType.STAND)
.with(PoseOption(PoseType.WALK) { _, state, vehicle ->
return@PoseOption abs(state.rideVelocity.get().horizontalDistance()) > 0.0
return@PoseOption state.walking.get()
})
override fun isActive(
@@ -111,9 +112,12 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
driver: Player,
input: Vec3
) {
handleSprinting(state, driver)
inAirCheck(state, vehicle)
tickStamina(settings, state, vehicle)
if (vehicle.level().isClientSide) {
handleSprinting(state)
inAirCheck(state, vehicle)
tickStamina(settings, state, vehicle)
state.walking.set(state.rideVelocity.get().horizontalDistance() > 0.01)
}
}
fun inAirCheck(
@@ -134,15 +138,22 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
}
fun handleSprinting(
state: HorseState,
driver: Player
state: HorseState
) {
if (state.sprinting.get()) {
state.sprinting.set(driver.isSprinting && state.stamina.get() > 0.0f)
} else {
// Only allow sprinting to start if over x percentage of stamina left
val stamSprintPerc = 25.0f
state.sprinting.set(driver.isSprinting && state.stamina.get() > stamSprintPerc / 100.0f)
val tryingToSprint = Minecraft.getInstance().options.keySprint.isDown() && Minecraft.getInstance().options.keyUp.isDown()
if (state.stamina.get() <= 0.0f || !Minecraft.getInstance().options.keyUp.isDown()) {
// If stamina runs out or the player is not holding forward then stop sprinting
state.sprinting.set(false)
if (state.stamina.get() <= 0.0f) {
state.sprintToggleable.set(false)
}
} else if (!state.sprinting.get() && !state.sprintToggleable.get() && state.stamina.get() > 0.33f) {
// If you are not sprinting and sprint is not toggleable and you're not trying to sprint and stamina is above 0.3 then enable the toggle
state.sprintToggleable.set(true)
} else if (tryingToSprint && state.sprintToggleable.get()) {
// If you are trying to sprint and the toggle allows it then start sprinting and disable toggle
state.sprinting.set(true)
}
}
@@ -602,12 +613,16 @@ class HorseSettings : RidingBehaviourSettings {
class HorseState : RidingBehaviourState() {
var sprinting = ridingState(false, Side.CLIENT)
var walking = ridingState(false, Side.BOTH)
var sprintToggleable = ridingState(false, Side.CLIENT)
var inAir = ridingState(false, Side.CLIENT)
var jumpTicks = ridingState(0, Side.CLIENT)
override fun encode(buffer: FriendlyByteBuf) {
super.encode(buffer)
buffer.writeBoolean(sprinting.get())
buffer.writeBoolean(walking.get())
buffer.writeBoolean(sprintToggleable.get())
buffer.writeBoolean(inAir.get())
buffer.writeInt(jumpTicks.get())
}
@@ -615,6 +630,8 @@ class HorseState : RidingBehaviourState() {
override fun decode(buffer: FriendlyByteBuf) {
super.decode(buffer)
sprinting.set(buffer.readBoolean(), forced = true)
walking.set(buffer.readBoolean(), forced = true)
sprintToggleable.set(buffer.readBoolean(), forced = true)
inAir.set(buffer.readBoolean(), forced = true)
jumpTicks.set(buffer.readInt(), forced = true)
}
@@ -622,6 +639,8 @@ class HorseState : RidingBehaviourState() {
override fun reset() {
super.reset()
sprinting.set(false, forced = true)
walking.set(false, forced = true)
sprintToggleable.set(false, forced = true)
inAir.set(false, forced = true)
jumpTicks.set(0, forced = true)
}
@@ -630,6 +649,8 @@ class HorseState : RidingBehaviourState() {
it.rideVelocity.set(this.rideVelocity.get(), forced = true)
it.stamina.set(this.stamina.get(), forced = true)
it.sprinting.set(this.sprinting.get(), forced = true)
it.walking.set(this.sprinting.get(), forced = true)
it.sprintToggleable.set(this.sprintToggleable.get(), forced = true)
it.inAir.set(this.inAir.get(), forced = true)
it.jumpTicks.set(this.jumpTicks.get(), forced = true)
}
@@ -637,6 +658,8 @@ class HorseState : RidingBehaviourState() {
override fun shouldSync(previous: RidingBehaviourState): Boolean {
if (previous !is HorseState) return false
if (previous.sprinting.get() != sprinting.get()) return true
if (previous.walking.get() != walking.get()) return true
if (previous.sprintToggleable.get() != sprintToggleable.get()) return true
if (previous.inAir.get() != inAir.get()) return true
return super.shouldSync(previous)
}
@@ -20,6 +20,7 @@ import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
import com.cobblemon.mod.common.api.riding.behaviour.Side
import com.cobblemon.mod.common.api.riding.behaviour.ridingState
import com.cobblemon.mod.common.api.riding.behaviour.types.air.angleDifference
import com.cobblemon.mod.common.api.riding.posing.PoseOption
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
@@ -47,6 +48,7 @@ import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import org.joml.Vector3f
class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
companion object {
@@ -62,7 +64,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
}
val poseProvider = PoseProvider<DolphinSettings, DolphinState>(PoseType.FLOAT)
.with(PoseOption(PoseType.SWIM) { _, _, entity -> entity.deltaMovement.length() > 0.1 })
.with(PoseOption(PoseType.SWIM) { _, state, entity -> abs(state.rideVelocity.get().length()) > 0.05 })
override fun isActive(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity): Boolean {
return vehicle.isInWater || !vehicle.onGround()
@@ -151,37 +153,46 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
driver: Player,
input: Vec3
): Vec3 {
if (driver !is OrientationControllable) return Vec3.ZERO
val controller = (driver as OrientationControllable).orientationController
if (vehicle !is OrientationControllable) return Vec3.ZERO
val controller = (vehicle as OrientationControllable).orientationController
val orientation = controller.orientation ?: return Vec3.ZERO
val runtime = vehicle.runtime
val boostMod = if (state.boosting.get()) runtime.resolveDouble(settings.jumpExpr ?: globalDolphin.jumpExpr!!) else 1.0
val topSpeed = (vehicle.runtime.resolveDouble(settings.speedExpr ?: globalDolphin.speedExpr!!) / 20.0) * boostMod
val accel = (topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalDolphin.accelerationExpr!!) * 20.0)) * boostMod
val strafeFactor = runtime.resolveDouble(settings.strafeFactor ?: globalDolphin.strafeFactor!!)
val reverseDriveFactor = runtime.resolveDouble(settings.reverseDriveFactor ?: globalDolphin.reverseDriveFactor!!)
var currVel = state.rideVelocity.get()
/*********************************************
* Breaching logic
*********************************************/
if (!vehicle.isInWater && !vehicle.isUnderWater)
{
val currVel = state.lastVelocity.get()
val breachBoost = 1.5
var prevVel = state.lastVelocity.get()
val breachBoost = 0.3
state.clearMouseSmoothers.set(false)
if (!state.hasBreached.get()) {
// Apply an upwards breaching force if boosting. This will enhance jumping out of water to feel more
// fun
// Apply an upwards breaching force if boosting. This will enhance jumping out of water to feel more fun
if (state.boosting.get()) {
state.lastVelocity.set(Vec3(currVel.x, currVel.y + breachBoost*sin(-controller.pitch.toRadians()).coerceIn(0.0f,0.5f), currVel.z))
prevVel = Vec3(prevVel.x, prevVel.y + breachBoost*(sin(-controller.pitch.toRadians()).coerceIn(0.0f,0.5f)*2.0).pow(2), prevVel.z)
}
state.hasBreached.set(true)
} else {
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.4
val terminalVel = 2.0
state.lastVelocity.set(Vec3(currVel.x, max(-terminalVel, currVel.y - gravity), currVel.z)) //.yRot(vehicle.yRot.toRadians()))
prevVel = Vec3(prevVel.x, max(-terminalVel, prevVel.y - gravity), prevVel.z)
}
return state.lastVelocity.get()
state.lastVelocity.set(prevVel)
return state.lastVelocity.get().yRot(vehicle.yRot.toRadians())
} else {
state.lastVelocity.set(vehicle.deltaMovement.yRot(vehicle.yRot.toRadians()))
if(state.hasBreached.get()) {
state.clearMouseSmoothers.set(true)
}
state.hasBreached.set(false)
currVel = Vec3(currVel.x, 0.0, currVel.z) // Reset y velocity to account for y vel persistance edgecase
}
/*********************************************
@@ -190,32 +201,30 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
if (vehicle.horizontalCollision || vehicle.verticalCollision) {
// not actually friction
val frictionLimit = 0.1
val postColSpeed = min(max(vehicle.deltaMovement.length(), frictionLimit), state.speed.get())
val postColSpeed = min(max(state.rideVelocity.get().length(), frictionLimit), state.speed.get())
state.speed.set(postColSpeed)
}
val currSpeed = state.speed.get()
val strafeFactor = runtime.resolveDouble(settings.strafeFactor ?: globalDolphin.strafeFactor!!)
val reverseDriveFactor = runtime.resolveDouble(settings.reverseDriveFactor ?: globalDolphin.reverseDriveFactor!!)
val f = -driver.xxa * strafeFactor
when {
driver.zza > 0.0 -> state.speed.set(min(topSpeed, currSpeed + accel))
driver.zza < 0.0 -> state.speed.set(max( -topSpeed * reverseDriveFactor, currSpeed - accel))
else -> state.speed.set( lerp(currSpeed, 0.0, 0.05))
val currZVel = state.rideVelocity.get().z
val newZVel = when {
driver.zza > 0.0 -> min(topSpeed, currZVel + accel)
driver.zza < 0.0 -> max( -topSpeed * reverseDriveFactor, currZVel - accel)
else -> lerp(currZVel, 0.0, 0.05)
}
val vertInput = when {
driver.jumping && state.stamina.get() != 0.0f -> 1.0
driver.isShiftKeyDown -> -1.0
else -> 0.0
} * strafeFactor
state.rideVelocity.set(Vec3(currVel.x, currVel.y, newZVel))
var newVelocity = Vec3(f, vertInput,-state.speed.get())
// Convert the local velocity vector into a world vector
val localVelVec = Vector3f(
state.rideVelocity.get().x.toFloat(),
(state.rideVelocity.get().y).toFloat(),
state.rideVelocity.get().z.toFloat() * -1.0f // Flip the z axis to make this left handed
)
// Align to the ride
newVelocity = controller.orientation?.transform(newVelocity.toVector3f())?.toVec3d() ?: Vec3.ZERO
newVelocity = newVelocity.yRot(vehicle.yRot.toRadians())
val desiredVec = newVelocity.subtract(vehicle.deltaMovement)
return vehicle.deltaMovement.add(desiredVec)
val worldVelVec = localVelVec.mul(orientation).toVec3d()
state.lastVelocity.set(worldVelVec) // Set last velocity to be used during breaching
// Unrotate preemptively as this vector gets rotated later down the line in MC logic.
return worldVelVec.yRot(vehicle.yRot.toRadians())
}
override fun angRollVel(
@@ -225,9 +234,8 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
driver: Player,
deltaTime: Double
): Vec3 {
if (Cobblemon.config.disableRoll) return Vec3.ZERO
if (driver !is OrientationControllable) return Vec3.ZERO
val controller = (driver as OrientationControllable).orientationController
if (vehicle !is OrientationControllable) return Vec3.ZERO
val controller = (vehicle as OrientationControllable).orientationController
val currSpeed = vehicle.deltaMovement.length()
@@ -235,7 +243,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalDolphin.speedExpr!!) / 20.0
val yawDeltaDeg = deltaTime * handling * sin(Math.toRadians(controller.roll.toDouble())).pow(2) * sin(Math.toRadians(controller.roll.toDouble())).sign
val trueYawDelt = yawDeltaDeg * abs(cos(Math.toRadians(controller.pitch.toDouble()))) * sqrt(RidingBehaviour.scaleToRange(currSpeed, 0.0, topSpeed))
val trueYawDelt = yawDeltaDeg * abs(cos(Math.toRadians(controller.pitch.toDouble()))*2).coerceIn(-1.0, 1.0) * sqrt(RidingBehaviour.scaleToRange(currSpeed, 0.0, topSpeed))
// Dampen yaw when upside down
val yawDampen = (1 - abs(min(cos(controller.roll.toRadians()),0.0f)))
@@ -256,45 +264,62 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
driver: Player,
deltaTime: Double
) {
// Calculate correcting roll force.
// Mult by cos(pitch) to ensure that it doesn't roll correct at when diving or climbing
val currSpeed = vehicle.deltaMovement.length()
val rollCorrectionTimer = vehicle.runtime.resolveDouble(settings.timeToRollCorrect ?: globalDolphin.timeToRollCorrect!!)
val maxRollCorrectionRate = if (!(vehicle.isInWater || vehicle.isUnderWater)) 15.0f
else 10.0f * (cos(controller.pitch.toRadians())).pow(2) * ((state.noInputTime.get() - rollCorrectionTimer) * 2).coerceIn(0.0, 1.0).toFloat()
val maxRollForce = maxRollCorrectionRate / 40.0
val rollArrivalDeg = 90.0
if((state.speed.get() < 0.1 && driver.zza.toDouble() == 0.0) || state.noInputTime.get() > rollCorrectionTimer || !(vehicle.isInWater || vehicle.isUnderWater)) {
val arrivalInfluence = (min(rollArrivalDeg,abs(controller.roll).toDouble()) / rollArrivalDeg)
val desiredRollForce = (0 - controller.roll).sign * maxRollCorrectionRate * arrivalInfluence
var steeredRollForce = desiredRollForce - state.currRollCorrectionForce.get()
// Ensure the 'steering' correction force doesn't exceed our maxForce
if (abs(steeredRollForce) > maxRollForce) {
steeredRollForce = steeredRollForce.sign * maxRollForce
}
// Apply the 'steered' force (its really not a steering force I need a better name)
// Stop roll and pitch correction when breached
if (state.hasBreached.get()) {
state.currRollCorrectionForce.set(
state.currRollCorrectionForce.get() + steeredRollForce
lerp(state.currRollCorrectionForce.get(), 0.0, deltaTime * 0.98)
)
} else {
state.currRollCorrectionForce.set(
//reduce rollCorrection gradually
lerp(state.currRollCorrectionForce.get(), 0.0, deltaTime * 0.96)
state.currPitchCorrectionForce.set(
lerp(state.currPitchCorrectionForce.get(), 0.0, deltaTime * 0.98)
)
return
}
//Calculate correcting pitch force.
val maxPitchCorrectionRate = if (!(vehicle.isInWater || vehicle.isUnderWater)) 15.0 else 5.0f * ((state.noInputTime.get() - rollCorrectionTimer) * 2).coerceIn(0.0, 1.0).toFloat() * (1 - (currSpeed * 0.5).coerceIn(0.0, 1.0))
val maxPitchForce = maxPitchCorrectionRate / 40.0
val pitchArrivalDeg = 45.0
val targetPitch = if ((vehicle.isInWater || vehicle.isUnderWater)) 0.0f else (-asin(vehicle.deltaMovement.y / vehicle.deltaMovement.length()).toDegrees()).coerceIn(-45.0f, 45.0f)
// Calculate correcting roll force.
val rollCorrectionTimer = vehicle.runtime.resolveDouble(settings.timeToRollCorrect ?: globalDolphin.timeToRollCorrect!!)
// If the ride is moving then have the correction rate be an equal proportion of:
// - how rolled are you
// - how long has it been since last mouse input (higher is stronger correction)
// Also the correction is proportional as a whole to how NOT pitched are you so you don't spin endlessly when looking up or down
val howRolledAmI = sqrt(abs((cos(controller.roll.toRadians()) - 1.0f) * 0.5f)) // sqrt so it trends towards one pretty quickly
val maxRollCorrectionRate =
if (state.rideVelocity.get().length() < 0.2) 10.0f * abs(cos(controller.pitch.toRadians()))
else 10.0f * cos(controller.pitch.toRadians()).pow(2) * (
howRolledAmI*1.0f +
(state.noInputTime.get() - rollCorrectionTimer).coerceIn(0.0, 1.0).pow(2).toFloat()*0.5f
)
val maxRollForce = maxRollCorrectionRate / 40.0f
val rollArrivalDeg = 60.0f
val desiredRoll = 0.0f
if((state.noInputTime.get() > rollCorrectionTimer && driver.zza.toDouble() == 0.0) || !(vehicle.isInWater || vehicle.isUnderWater)) {
val arrivalInfluence = min(rollArrivalDeg, abs(Mth.wrapDegrees(targetPitch - controller.pitch.toDouble()))) / pitchArrivalDeg
val desiredPitchForce = (targetPitch - controller.pitch).sign * maxPitchCorrectionRate * arrivalInfluence
// Calculate the signed error (how far and in what direction)
val rollError = Mth.wrapDegrees(desiredRoll.angleDifference(controller.roll))
// Use the absolute error to calculate the magnitude of the influence
val influenceMagnitude = (min(rollArrivalDeg, abs(rollError)) / rollArrivalDeg)
val desiredRollForce = maxRollCorrectionRate * influenceMagnitude * -rollError.sign
var steeredRollForce = desiredRollForce - state.currRollCorrectionForce.get()
// Ensure the 'steering' correction force doesn't exceed our maxForce
if (abs(steeredRollForce) > maxRollForce) {
steeredRollForce = steeredRollForce.sign * maxRollForce
}
// Apply the 'steered' force (its really not a steering force I need a better name)
state.currRollCorrectionForce.set(
state.currRollCorrectionForce.get() + steeredRollForce
)
//Calculate correcting pitch force.
val maxPitchCorrectionRate = 5.0f * (state.noInputTime.get() - rollCorrectionTimer).coerceIn(0.0, 1.0).pow(2).toFloat()*0.5f
val maxPitchForce = maxPitchCorrectionRate / 40.0f
val pitchArrivalDeg = 45.0f
val desiredPitch = 0.0f
if(state.rideVelocity.get().length() < 0.1 && state.noInputTime.get() > rollCorrectionTimer) {
val pitchErrror = Mth.wrapDegrees(desiredPitch.angleDifference(controller.pitch))
val arrivalInfluence = (min(pitchArrivalDeg,abs(pitchErrror)) / pitchArrivalDeg)
val desiredPitchForce = maxPitchCorrectionRate * arrivalInfluence * -pitchErrror.sign
var steeredPitchForce = desiredPitchForce - state.currPitchCorrectionForce.get()
// Ensure the 'steering' correction force doesn't exceed our maxForce
@@ -333,130 +358,72 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
} else {
state.noInputTime.set(0.0)
}
return when {
Cobblemon.config.disableRoll -> noRollRotation(
settings,
state,
vehicle,
driver,
mouseY,
mouseX,
mouseYSmoother,
mouseXSmoother,
sensitivity,
deltaTime
)
else -> rollRotation(
settings,
state,
vehicle,
driver,
mouseY,
mouseX,
mouseYSmoother,
mouseXSmoother,
sensitivity,
deltaTime
)
}
}
fun noRollRotation(
settings: DolphinSettings,
state: DolphinState,
vehicle: PokemonEntity,
driver: Player,
mouseY: Double,
mouseX: Double,
mouseYSmoother: SmoothDouble,
mouseXSmoother: SmoothDouble,
sensitivity: Double,
deltaTime: Double
): Vec3 {
if (driver !is OrientationControllable) return Vec3.ZERO
val controller = (driver as OrientationControllable).orientationController
// Set roll to zero if transitioning to noroll config
controller.rotateRoll(controller.roll * -1.0f)
val boostingHandleDebuff = if (state.boosting.get()) 0.2 else 1.0
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalDolphin.handlingExpr!!) * boostingHandleDebuff
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult ?: globalDolphin.mouseInputMult!!)
//Smooth out mouse input.
val smoothingSpeed = 4.0
val mouseXc = ((mouseX).coerceIn(-60.0, 60.0) / 60.0) * mouseInputMult
val mouseYc = ((mouseY).coerceIn(-60.0, 60.0) / 60.0) * mouseInputMult
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1, deltaTime * smoothingSpeed);
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1, deltaTime * smoothingSpeed);
//Give the ability to yaw with x mouse input when at low speeds.
val yawForce = xInput * handling
//Apply yaw globally as we don't want roll or pitch changes due to local yaw when looking up or down.
controller.applyGlobalYaw(yawForce.toFloat() * abs(cos(controller.pitch.toRadians())).pow(2))
var pitchRot = yInput * handling
// Pitch up globally
if (abs(controller.pitch + pitchRot) >= 89.5 ) {
pitchRot = 0.0
mouseYSmoother.reset()
} else {
controller.applyGlobalPitch(pitchRot.toFloat() * -1.0f)
}
//yaw, pitch, roll
return Vec3.ZERO
}
fun rollRotation(
settings: DolphinSettings,
state: DolphinState,
vehicle: PokemonEntity,
driver: Player,
mouseY: Double,
mouseX: Double,
mouseYSmoother: SmoothDouble,
mouseXSmoother: SmoothDouble,
sensitivity: Double,
deltaTime: Double
): Vec3 {
if (driver !is OrientationControllable) return Vec3.ZERO
val controller = (driver as OrientationControllable).orientationController
if (vehicle !is OrientationControllable) return Vec3.ZERO
val controller = (vehicle as OrientationControllable).orientationController
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalDolphin.speedExpr!!) / 20.0
val boostingHandleDebuff = if (state.boosting.get()) 0.2 else 1.0
val boostingHandleDebuff = if (state.boosting.get()) 0.5 else 1.0
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult ?: globalDolphin.mouseInputMult!!) * boostingHandleDebuff
// Applied to mouseX to prevent turning upside down when trying to roll hard.
val rollDegreeDebuff = if(mouseX.sign.toFloat() == controller.roll.sign) 1 - 0.9f*sqrt(abs(sin(controller.roll.toRadians())))*sqrt(abs(cos(controller.pitch))) else 1.0f
//Smooth out mouse input.
val smoothingSpeed = 4.0
val mouseXc = (mouseX).coerceIn(-45.0, 45.0) * mouseInputMult
val mouseYc = (mouseY).coerceIn(-45.0, 45.0) * mouseInputMult
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1, deltaTime * smoothingSpeed);
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1, deltaTime * smoothingSpeed);
val currSpeed = vehicle.deltaMovement.length()
val smoothingSpeed = if(state.hasBreached.get()) 1.0f else 2.0f
// When upside down apply a pitching force to nudge the player towards a dive. This reduces the 'weirdness'
// of being upside down
val rolledOverPitchCorrection = -8*sqrt(abs(min(cos(controller.roll.toRadians()),0.0f))) * (cos(controller.pitch.toRadians())).pow(2)
val mouseXc = (mouseX).coerceIn(-45.0, 45.0) * mouseInputMult * rollDegreeDebuff
val mouseYc = (mouseY).coerceIn(-45.0, 45.0) * mouseInputMult + rolledOverPitchCorrection
val mouseModForce = if (state.hasBreached.get()) 0.15 else 0.1 // Used to effect more spin momentum during breaching
// Yaw locally
// Dampen yaw when rolled or pitched substantially
val yawDampen = abs(cos(controller.pitch.toRadians())).pow(2) * abs(cos(controller.roll.toRadians())).pow(2)
val localYaw = xInput * (1 - (RidingBehaviour.scaleToRange(currSpeed, 0.0, topSpeed))).coerceIn(0.4, 1.0) * yawDampen
if (state.clearMouseSmoothers.get()) {
mouseXSmoother.reset()
mouseYSmoother.reset()
state.clearMouseSmoothers.set(false)
}
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * mouseModForce, deltaTime * smoothingSpeed);
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * mouseModForce, deltaTime * smoothingSpeed);
val currSpeed = state.rideVelocity.get().length()
val currHorzSpeed = state.rideVelocity.get().horizontalDistance()
val hoverSpeed = 0.2
val pitchDamping = abs(cos(controller.pitch.toRadians())).pow(2) // Helper value used when wanting to discard the influence of another value at steep pitches
// Yaw locally when at or below hoverSpeed but not when pitched up or rolled over
val globalYaw = xInput * (1 - sqrt(RidingBehaviour.scaleToRange(currSpeed, 0.0, hoverSpeed))).coerceIn(0.0, 1.0)
controller.rotateYaw(pitchDamping*globalYaw.toFloat())
controller.rotateRoll((1-pitchDamping)*globalYaw.toFloat())
// Pitch locally up or down depending upon a number of factors:
// - Reduce pitch substantially when rolled and in a steep yaw. This prevents the player from ignoring a slow handling stat
// - Do not pitch at all when at or below hover speed
// - pitch faster at higher speeds
val p = 1.0 - vehicle.runtime.resolveDouble(settings.horizontalPitchExpr ?: globalDolphin.horizontalPitchExpr!!)
val localPitch = yInput * (1 - abs(sin(controller.roll.toRadians())) * p * hypot(controller.forwardVector.x.toDouble(), controller.forwardVector.z.toDouble()).toFloat())
// localPitch *= if (currSpeed > hoverSpeed) RidingBehaviour.scaleToRange(currSpeed, hoverSpeed, topSpeed)
// else 0.0
val localPitch = yInput * (1 - abs(sin(controller.roll.toRadians())).pow(2) * p * hypot(controller.forwardVector.x.toDouble(), controller.forwardVector.z.toDouble()).toFloat())
var localYawForce = 0.0
var rollForce = 0.0
// If it has breached then turn the yaw into roll
if (state.hasBreached.get()) {
localYawForce = xInput
} else {
// Roll less when slow and not at all when at hoverSpeed or lower.
// Apply a roll righting force when trying to pitch hard while rolled sideways. This nudges the player towards
// pitching globally
val pitchInfluencedRollCorrection = 0.0
val howRolledAmI = (( cos(controller.roll.toRadians()) - 1).coerceIn(-1.0f, 0.0f) * -2) //Coerce into how much roll you want to let in in the deadzone
val isInputtingTowardsRoll = if ( xInput.sign == controller.roll.sign.toDouble()) 1.0 else 0.0
val inputInRolledDir = if (isInputtingTowardsRoll == 1.0) 1.0 else -cos(controller.roll.toRadians()).coerceIn(-1.0f, 0.0f).toDouble()
val rollDampen = (1 - howRolledAmI.coerceIn(0.0f, 0.5f) * pitchDamping * inputInRolledDir)
rollForce = xInput * sqrt(abs(RidingBehaviour.scaleToRange(currHorzSpeed, 0.0, topSpeed)).coerceIn(0.0,1.0)) * rollDampen + pitchInfluencedRollCorrection
// When already rolled significantly part of the roll force should turn to local yaw force
localYawForce = rollForce * abs(sin(controller.roll.toRadians())).pow(2) * pitchDamping * isInputtingTowardsRoll
}
// Roll less when slow and not at all when at hoverSpeed or lower.
// Apply a roll righting force when trying to pitch hard while rolled sideways. This nudges the player towards
// pitching globally
val rollForce = xInput * ((RidingBehaviour.scaleToRange(currSpeed, 0.0, topSpeed))).coerceIn(0.2, 1.0) //+ pitchInfluencedRollCorrection
//yaw, pitch, roll
return Vec3(localYaw, localPitch, rollForce)
return Vec3(localYawForce, localPitch, rollForce)
}
override fun canJump(
@@ -640,6 +607,7 @@ class DolphinState : RidingBehaviourState() {
var boostIsToggleable = ridingState(false, Side.CLIENT)
var speed = ridingState(0.0, Side.CLIENT)
var hasBreached = ridingState(false, Side.CLIENT)
var clearMouseSmoothers = ridingState(false, Side.CLIENT)
override fun encode(buffer: FriendlyByteBuf) {
super.encode(buffer)
@@ -661,6 +629,7 @@ class DolphinState : RidingBehaviourState() {
boostIsToggleable.set(true, forced = true)
speed.set(0.0, forced = true)
hasBreached.set(false, forced = true)
clearMouseSmoothers.set(false, forced = true)
}
override fun toString(): String {
@@ -678,6 +647,7 @@ class DolphinState : RidingBehaviourState() {
it.boostIsToggleable.set(this.boosting.get(), forced = true)
it.speed.set(this.speed.get(), forced = true)
it.hasBreached.set(this.hasBreached.get(), forced = true)
it.clearMouseSmoothers.set(this.clearMouseSmoothers.get(), forced = true)
}
override fun shouldSync(previous: RidingBehaviourState): Boolean {
@@ -0,0 +1,647 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.riding.behaviour.types.liquid
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangMath.lerp
import com.cobblemon.mod.common.CobblemonRideSettings
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.orientation.OrientationController
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.posing.PoseOption
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.math.geometry.toRadians
import com.cobblemon.mod.common.util.readNullableExpression
import com.cobblemon.mod.common.util.readRidingStats
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.resolveDouble
import com.cobblemon.mod.common.util.toVec3d
import com.cobblemon.mod.common.util.writeNullableExpression
import com.cobblemon.mod.common.util.writeRidingStats
import net.minecraft.core.BlockPos
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.tags.FluidTags
import net.minecraft.util.SmoothDouble
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import org.joml.Vector3f
import kotlin.math.*
class SubmarineBehaviour : RidingBehaviour<SubmarineSettings, SubmarineState> {
companion object {
val KEY = cobblemonResource("liquid/submarine")
}
override val key = KEY
val globalSubmarine: SubmarineSettings
get() = CobblemonRideSettings.submarine
override fun getRidingStyle(settings: SubmarineSettings, state: SubmarineState): RidingStyle {
return RidingStyle.LIQUID
}
val poseProvider = PoseProvider<SubmarineSettings, SubmarineState>(PoseType.FLOAT)
.with(PoseOption(PoseType.SWIM) { _, state, entity -> state.rideVelocity.get().z > 0.05 && !state.onSurface.get()})
.with(PoseOption(PoseType.STAND) { _, state, entity -> state.rideVelocity.get().z <= 0.05 && state.onSurface.get()})
.with(PoseOption(PoseType.STAND) { _, state, entity -> state.rideVelocity.get().z > 0.05 && state.onSurface.get()})
override fun isActive(settings: SubmarineSettings, state: SubmarineState, vehicle: PokemonEntity): Boolean {
return vehicle.isInWater || !vehicle.onGround()
}
override fun pose(settings: SubmarineSettings, state: SubmarineState, vehicle: PokemonEntity): PoseType {
return poseProvider.select(settings, state, vehicle)
}
override fun speed(settings: SubmarineSettings, state: SubmarineState, vehicle: PokemonEntity, driver: Player): Float {
return state.speed.get().toFloat()
}
override fun tick(
settings: SubmarineSettings,
state: SubmarineState,
vehicle: PokemonEntity,
driver: Player,
input: Vec3
) {
if(vehicle.level().isClientSide) {
tickStamina(settings, state, vehicle)
surfaceCheck(settings, state, vehicle, driver)
}
}
fun tickStamina(
settings: SubmarineSettings,
state: SubmarineState,
vehicle: PokemonEntity
) {
val stam = state.stamina.get()
if (vehicle.runtime.resolveBoolean(settings.infiniteStamina ?: globalSubmarine.infiniteStamina!!)) {
return
}
// Grab the boost time in seconds and convert to ticks. Then calculate the drain rate as inversely
// proportional to the number of ticks of boost thus making a full boost take x ticks
// in short: "Stamina drains slower at higher values and also replenishes slower"
val breathTime = vehicle.runtime.resolveDouble(settings.staminaExpr ?: globalSubmarine.staminaExpr!!) * 20.0f
val stamRegenRate = 1.0f / (vehicle.runtime.resolveDouble(settings.staminaRegenTime ?: globalSubmarine.staminaRegenTime!!).toFloat() * 20.0f)
val stamDrainRate = (1.0f / breathTime).toFloat()
val driver = vehicle.controllingPassenger
val newStam = if (driver?.isEyeInFluid(FluidTags.WATER) ?: false) max(0.0f,stam - stamDrainRate)
else min(1.0f,stam + stamRegenRate)
state.stamina.set(newStam)
}
private fun surfaceCheck(
settings: SubmarineSettings,
state: SubmarineState,
vehicle: PokemonEntity,
driver: Player,
) {
// determine if the vehicle is in water.
// if it isn't then set surface flag to false and return
if (!vehicle.isInWater) {
state.onSurface.set(false)
return
}
val hitboxBreachPercentage = vehicle.runtime.resolveDouble(settings.hitboxBreachPercentage ?: globalSubmarine.hitboxBreachPercentage!!)
// Grab the position of the vehicle that when out of the water is considered breached
val vehicleBreachPoint = vehicle.position().add(0.0, vehicle.bbHeight * hitboxBreachPercentage, 0.0)
// If the block above the center is not fluid but this one is then move the center
val centerBlockPos = BlockPos.containing(vehicleBreachPoint)
val centerInWater = !vehicle.level().getFluidState(centerBlockPos).isEmpty
val abovePos = centerBlockPos.above()
val aboveInWater = !vehicle.level().getFluidState(abovePos).isEmpty
if (!centerInWater && !aboveInWater) {
state.onSurface.set(true)
} else {
state.onSurface.set(false)
}
//
// Function for if x mouse and y mouse "get through" to affecting the mounts rotation. Return a Pair<Boolean, Boolean>
}
override fun rotation(
settings: SubmarineSettings,
state: SubmarineState,
vehicle: PokemonEntity,
driver: LivingEntity
): Vec2 {
return Vec2(driver.xRot * 0.5f, driver.yRot)
}
override fun velocity(
settings: SubmarineSettings,
state: SubmarineState,
vehicle: PokemonEntity,
driver: Player,
input: Vec3
): Vec3 {
if (vehicle !is OrientationControllable) return Vec3.ZERO
val controller = (vehicle as OrientationControllable).orientationController
val orientation = controller.orientation ?: return Vec3.ZERO
val runtime = vehicle.runtime
val topSpeed = (vehicle.runtime.resolveDouble(settings.speedExpr ?: globalSubmarine.speedExpr!!) / 20.0)
val accel = (topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalSubmarine.accelerationExpr!!) * 20.0))
val strafeSpeed = (runtime.resolveDouble(settings.jumpExpr ?: globalSubmarine.jumpExpr!!) / 20.0)
val strafeAccel = (strafeSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalSubmarine.accelerationExpr!!) * 20.0))
var currVel = state.rideVelocity.get()
/*********************************************
* Handle collisions
*********************************************/
if (vehicle.horizontalCollision || vehicle.verticalCollision) {
// not actually friction
val frictionLimit = 0.1
val postColSpeed = min(max(state.rideVelocity.get().length(), frictionLimit), state.speed.get())
state.speed.set(postColSpeed)
}
if (vehicle.verticalCollision) {
state.rideVelocity.set(Vec3(state.rideVelocity.get().x, 0.0, state.rideVelocity.get().z))
}
/*********************************************
* Calculate Forward Velocity
*********************************************/
val currZVel = state.rideVelocity.get().z
val newZVel = when {
driver.zza > 0.0 -> min(topSpeed, currZVel + accel)
driver.zza < 0.0 -> max( -strafeSpeed, currZVel - strafeAccel) //TODO: Might need to be applied in addition to the normal braking force?
else -> lerp(currZVel, 0.0, 0.05)
}
currVel = Vec3(currVel.x, currVel.y, newZVel)
/*********************************************
* Calculate up/down Velocity and gravity
*********************************************/
val vertTopSpeed = strafeSpeed
val currYVel = state.rideVelocity.get().y
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.4
val terminalYVel = 2.0
val driverYInput = if(driver.jumping) 1.0 else if (driver.isShiftKeyDown) -1.0 else 0.0
val newYVel = when {
(!vehicle.isInWater) -> max(-terminalYVel, currYVel - gravity)
//(vehicle.isInWater && !vehicle.isUnderWater && currYVel > 0.001) -> lerp(currYVel, 0.0, 0.1) // Make sure that yVel is above a threshold before doing this otherwise it'll block submerging.
//driverYInput != 0.0 && currYVel > 0.001 && driverYInput.sign != currYVel.sign -> lerp(currYVel, 0.0, 0.05)
driverYInput > 0.0 -> min(vertTopSpeed, currYVel + strafeAccel)
driverYInput < 0.0 -> max(-vertTopSpeed, currYVel - strafeAccel)
else -> lerp(currYVel, 0.0, 0.05)
}
currVel = Vec3(currVel.x, newYVel, currVel.z)
/*********************************************
* Calculate left/right strafing Velocity
*********************************************/
val currXVel = state.rideVelocity.get().x
val horzTopSpeed = strafeSpeed
val newXVel = when {
driver.xxa > 0.0 -> min(horzTopSpeed, currXVel + strafeAccel)
driver.xxa < 0.0 -> max( -horzTopSpeed, currXVel - strafeAccel)
else -> lerp(currXVel, 0.0, 0.01)
}
currVel = Vec3(newXVel, currVel.y, currVel.z)
// Set the new velocity
state.rideVelocity.set(currVel)
/*********************************************
* Separate and apply velocities
*********************************************/
// If the driver and vehicles eyes are out of the water but the vehicle is still in it then move only horizontally or downwards as you have surfaced
if (state.onSurface.get()) {
val worldVelVec = Vec3(currVel.x, min(0.0, currVel.y), currVel.z)
return worldVelVec
} else {
val vertVec = Vec3(0.0, currVel.y, 0.0)
val horzVec = Vec3(currVel.x, 0.0, 0.0)
val localZVelVec = Vector3f(0.0f, 0.0f, currVel.z.toFloat() * -1.0f) // Flip the z axis to make this left handed
// Unrotate preemptively as this vector gets rotated later down the line in MC logic.
val worldVelVec = localZVelVec.mul(orientation).toVec3d().yRot(vehicle.yRot.toRadians())
// Add back the horizontal and vertical components.
return worldVelVec.add(vertVec).add(horzVec)
}
}
override fun angRollVel(
settings: SubmarineSettings,
state: SubmarineState,
vehicle: PokemonEntity,
driver: Player,
deltaTime: Double
): Vec3 {
return Vec3.ZERO
}
override fun applyRenderRotation(
settings: SubmarineSettings,
state: SubmarineState,
vehicle: PokemonEntity,
partialTicks: Float
) {
if (vehicle !is OrientationControllable) return
val controller = (vehicle as OrientationControllable).orientationController
var handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalSubmarine.handlingExpr!!)
val surfaceHandlingBoost = if(state.onSurface.get()) 3.0 else 1.0
handling *= surfaceHandlingBoost
val strafeSpeed = (vehicle.runtime.resolveDouble(settings.jumpExpr ?: globalSubmarine.jumpExpr!!) / 20.0)
// Grab the roll and see if its hit the limit yet. If it hasn't then trend it towards its desired roll
val maxRollDeg = if (state.onSurface.get()) 5 else 15
val maxPitchDeg = 15
var rollAmount = 0.0
var pitchAmount = 0.0
// Do horizontal speed based rolling
val maxHorzSpeed = strafeSpeed
val currHorzSpeed = state.rideVelocity.get().x
var desiredRoll = -(currHorzSpeed / maxHorzSpeed).coerceIn(-1.0, 1.0) * maxRollDeg
rollAmount += lerp(controller.roll.toDouble(), desiredRoll, partialTicks.toDouble()) - controller.roll
//val rotAmount //Maybe use some lerp?
controller.rotateRoll(rollAmount.toFloat())
// Do yawRate based rolling
val maxYawRate = handling
val currYawRate = vehicle.ridingAnimationData.rotDeltaSpring.getInterpolated(partialTicks.toDouble(), 2).y()
desiredRoll = -(currYawRate / maxYawRate).coerceIn(-1.0, 1.0) * maxRollDeg
rollAmount += desiredRoll - controller.roll
controller.rotateRoll(rollAmount.toFloat())
//TODO: why does this affect the actual controller but rolling doesn't??
// Do upwards movement based pitching
// val maxVertSpeed = 0.25 //TODO: CONNECT UP TO JUMP
// val currVertSpeed = state.rideVelocity.get().y
// var desiredPitch = -(currVertSpeed / maxVertSpeed) * maxPitchDeg
// pitchAmount += lerp(controller.pitch.toDouble(), desiredPitch, partialTicks.toDouble()) - controller.pitch
// controller.rotatePitch(pitchAmount.toFloat())
}
private fun correctOrientation(
settings: SubmarineSettings,
state: SubmarineState,
vehicle: PokemonEntity,
controller: OrientationController,
driver: Player,
deltaTime: Double
) {
// TODO: remove if not needed
// // Calculate correcting roll force.
// // If the ride is moving then have the correction rate be an equal proportion of:
// // - how rolled are you
// // - how long has it been since last mouse input (higher is stronger correction)
// // Also the correction is proportional as a whole to how NOT pitched are you so you don't spin endlessly when looking up or down
// val howRolledAmI = sqrt(abs((cos(controller.roll.toRadians()) - 1.0f) * 0.5f)) // sqrt so it trends towards one pretty quickly
// val maxRollCorrectionRate =
// if (state.rideVelocity.get().length() < 0.2) 10.0f * abs(cos(controller.pitch.toRadians()))
// else 10.0f * cos(controller.pitch.toRadians()).pow(2) * (
// howRolledAmI*1.0f +
// (state.noInputTime.get() - rollCorrectionTimer).coerceIn(0.0, 1.0).pow(2).toFloat()*0.5f
// )
// val maxRollForce = maxRollCorrectionRate / 40.0f
// val rollArrivalDeg = 60.0f
// val desiredRoll = 0.0f
//
// // Calculate the signed error (how far and in what direction)
// val rollError = Mth.wrapDegrees(desiredRoll.angleDifference(controller.roll))
//
// // Use the absolute error to calculate the magnitude of the influence
// val influenceMagnitude = (min(rollArrivalDeg, abs(rollError)) / rollArrivalDeg)
// val desiredRollForce = maxRollCorrectionRate * influenceMagnitude * -rollError.sign
// var steeredRollForce = desiredRollForce - state.currRollCorrectionForce.get()
//
// // Ensure the 'steering' correction force doesn't exceed our maxForce
// if (abs(steeredRollForce) > maxRollForce) {
// steeredRollForce = steeredRollForce.sign * maxRollForce
// }
//
// // Apply the 'steered' force (its really not a steering force I need a better name)
// state.currRollCorrectionForce.set(
// state.currRollCorrectionForce.get() + steeredRollForce
// )
//Calculate correcting pitch force.
// val rollCorrectionTimer = vehicle.runtime.resolveDouble(settings.timeToRollCorrect ?: globalSubmarine.timeToRollCorrect!!)
// val maxPitchCorrectionRate = 5.0f * (state.noInputTime.get() - rollCorrectionTimer).coerceIn(0.0, 1.0).pow(2).toFloat()*0.5f
// val maxPitchForce = maxPitchCorrectionRate / 40.0f
// val pitchArrivalDeg = 45.0f
// val desiredPitch = 0.0f
//
// if(state.rideVelocity.get().length() < 0.1 && state.noInputTime.get() > rollCorrectionTimer) {
// val pitchErrror = Mth.wrapDegrees(desiredPitch.angleDifference(controller.pitch))
// val arrivalInfluence = (min(pitchArrivalDeg,abs(pitchErrror)) / pitchArrivalDeg)
// val desiredPitchForce = maxPitchCorrectionRate * arrivalInfluence * -pitchErrror.sign
// var steeredPitchForce = desiredPitchForce - state.currPitchCorrectionForce.get()
//
// // Ensure the 'steering' correction force doesn't exceed our maxForce
// if (abs(steeredPitchForce) > maxPitchForce) {
// steeredPitchForce = steeredPitchForce.sign * maxPitchForce
// }
//
// // Apply the 'steered' force (its really not a steering force I need a better name)
// state.currPitchCorrectionForce.set(
// state.currPitchCorrectionForce.get() + steeredPitchForce
// )
// } else {
// state.currPitchCorrectionForce.set(
// //reduce rollCorrection gradually
// lerp(state.currPitchCorrectionForce.get(), 0.0, deltaTime * 0.98)
// )
// }
}
override fun rotationOnMouseXY(
settings: SubmarineSettings,
state: SubmarineState,
vehicle: PokemonEntity,
driver: Player,
mouseY: Double,
mouseX: Double,
mouseYSmoother: SmoothDouble,
mouseXSmoother: SmoothDouble,
sensitivity: Double,
deltaTime: Double
): Vec3 {
if (vehicle !is OrientationControllable) return Vec3.ZERO
val controller = (vehicle as OrientationControllable).orientationController
// Set roll to zero if transitioning to noroll config
controller.rotateRoll(controller.roll * -1.0f)
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalSubmarine.handlingExpr!!)
val surfaceHandlingBoost = if(state.onSurface.get()) 3.0 else 1.0
val effHandling = if(state.onSurface.get()) 1000 * deltaTime else handling * deltaTime
//Smooth out mouse input.
val smoothingSpeed = if(state.onSurface.get()) 10.0 else 2.0
val mouseXc = (mouseX * 0.1).coerceIn(-effHandling, effHandling)
val mouseYc = (mouseY * 0.1).coerceIn(-effHandling, effHandling)
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc, deltaTime * smoothingSpeed);
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc, deltaTime * smoothingSpeed);
// Give the ability to yaw with x mouse input when at low speeds.
val yawForce = xInput
// Apply yaw globally as we don't want roll or pitch changes due to local yaw when looking up or down.
controller.applyGlobalYaw(yawForce.toFloat())
var pitchRot = yInput
// Pitch up globally
val pitchLimit = 45.0
if (abs(controller.pitch + pitchRot) >= pitchLimit ) {
pitchRot = 0.0
mouseYSmoother.reset()
} else {
controller.applyGlobalPitch(pitchRot.toFloat())
}
// If on the surface then conditionally bring the pitch to 0 degrees.
if (state.onSurface.get()) {
//move back at max 30 degrees per second.
val maxCorrectionSpeed = 30.0
val arrivalDegreeWidth = 30.0
val currentPitch = controller.pitch
val correctionDegrees = (-currentPitch / arrivalDegreeWidth).coerceIn(-1.0, 1.0) * maxCorrectionSpeed * deltaTime
controller.applyGlobalPitch(correctionDegrees.toFloat())
// Compensate player ride rotations
// val playerRotater = driver as RideRotation
// playerRotater.`cobblemon$setRideXRot`(playerRotater.`cobblemon$getRideXRot`() - correctionDegrees.toFloat() )
}
//yaw, pitch, roll
return Vec3.ZERO
}
override fun canJump(
settings: SubmarineSettings,
state: SubmarineState,
vehicle: PokemonEntity,
driver: Player
): Boolean {
return false
}
override fun setRideBar(
settings: SubmarineSettings,
state: SubmarineState,
vehicle: PokemonEntity,
driver: Player
): Float {
return (state.stamina.get() / 1.0f)
}
override fun jumpForce(
settings: SubmarineSettings,
state: SubmarineState,
vehicle: PokemonEntity,
driver: Player,
jumpStrength: Int
): Vec3 {
return Vec3.ZERO
}
override fun gravity(
settings: SubmarineSettings,
state: SubmarineState,
vehicle: PokemonEntity,
regularGravity: Double
): Double {
return 0.0
}
override fun rideFovMultiplier(
settings: SubmarineSettings,
state: SubmarineState,
vehicle: PokemonEntity,
driver: Player
): Float {
return 1.0f
}
override fun useAngVelSmoothing(settings: SubmarineSettings, state: SubmarineState, vehicle: PokemonEntity): Boolean {
return false
}
override fun useRidingAltPose(
settings: SubmarineSettings,
state: SubmarineState,
vehicle: PokemonEntity,
driver: Player
): ResourceLocation {
return cobblemonResource("no_pose")
}
override fun inertia(settings: SubmarineSettings, state: SubmarineState, vehicle: PokemonEntity): Double {
return if (!vehicle.isUnderWater) 1.0 else 0.1
}
override fun shouldRoll(settings: SubmarineSettings, state: SubmarineState, vehicle: PokemonEntity): Boolean {
return true
}
override fun turnOffOnGround(settings: SubmarineSettings, state: SubmarineState, vehicle: PokemonEntity): Boolean {
return false
}
override fun dismountOnShift(settings: SubmarineSettings, state: SubmarineState, vehicle: PokemonEntity): Boolean {
return false
}
override fun mouseModifiesDriverRotation(settings: SubmarineSettings, state: SubmarineState, vehicle: PokemonEntity): Pair<Boolean, Boolean> {
return Pair(false, state.onSurface.get())
}
override fun shouldRotatePokemonHead(
settings: SubmarineSettings,
state: SubmarineState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun shouldRotateRiderHead(
settings: SubmarineSettings,
state: SubmarineState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun getRideSounds(
settings: SubmarineSettings,
state: SubmarineState,
vehicle: PokemonEntity
): RideSoundSettingsList {
return settings.rideSounds
}
override fun createDefaultState(settings: SubmarineSettings) = SubmarineState()
}
class SubmarineSettings : RidingBehaviourSettings {
override val key = SubmarineBehaviour.KEY
override val stats = mutableMapOf<RidingStat, IntRange>()
var infiniteStamina: Expression? = null
private set
var staminaRegenTime: Expression? = null
private set
// Percentage (0.0->1.0) of ride needing to be breached out of the water to be considered surfaced.
var hitboxBreachPercentage: Expression? = null
private set
// Tope strafe speed in Bl/s
var jumpExpr: Expression? = null
// Yaw rate in degrees per second
var handlingExpr: Expression? = null
// Top speed in Bl/s
var speedExpr: Expression? = null
// Seconds to get to top speed
var accelerationExpr: Expression? = null
// Seconds of air provided by mount
var staminaExpr: Expression? = null
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeRidingStats(stats)
rideSounds.encode(buffer)
buffer.writeNullableExpression(infiniteStamina)
buffer.writeNullableExpression(staminaRegenTime)
buffer.writeNullableExpression(hitboxBreachPercentage)
buffer.writeNullableExpression(jumpExpr)
buffer.writeNullableExpression(handlingExpr)
buffer.writeNullableExpression(speedExpr)
buffer.writeNullableExpression(accelerationExpr)
buffer.writeNullableExpression(staminaExpr)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
stats.putAll(buffer.readRidingStats())
rideSounds = RideSoundSettingsList.decode(buffer)
infiniteStamina = buffer.readNullableExpression()
staminaRegenTime = buffer.readNullableExpression()
hitboxBreachPercentage = buffer.readNullableExpression()
jumpExpr = buffer.readNullableExpression()
handlingExpr = buffer.readNullableExpression()
speedExpr = buffer.readNullableExpression()
accelerationExpr = buffer.readNullableExpression()
staminaExpr = buffer.readNullableExpression()
}
}
class SubmarineState : RidingBehaviourState() {
var speed = ridingState(0.0, Side.CLIENT)
var currRollCorrectionForce = ridingState(0.0, Side.CLIENT)
var currPitchCorrectionForce = ridingState(0.0, Side.CLIENT)
var noInputTime = ridingState(0.0, Side.CLIENT)
var onSurface = ridingState(false, Side.BOTH)
override fun encode(buffer: FriendlyByteBuf) {
super.encode(buffer)
buffer.writeBoolean(onSurface.get())
}
override fun decode(buffer: FriendlyByteBuf) {
super.decode(buffer)
onSurface.set(buffer.readBoolean(), forced = true)
}
override fun reset() {
super.reset()
speed.set(0.0, forced = true)
currRollCorrectionForce.set(0.0, forced = true)
currPitchCorrectionForce.set(0.0, forced = true)
noInputTime.set(0.0, forced = true)
onSurface.set(false, forced = true)
}
override fun copy() = SubmarineState().also {
it.stamina.set(stamina.get(), forced = true)
it.speed.set(speed.get(), forced = true)
it.rideVelocity.set(rideVelocity.get(), forced = true)
it.currRollCorrectionForce.set(this.currRollCorrectionForce.get(), forced = true)
it.currPitchCorrectionForce.set(this.currPitchCorrectionForce.get(), forced = true)
it.noInputTime.set(this.noInputTime.get(), forced = true)
it.onSurface.set(this.onSurface.get(), forced = true)
}
override fun shouldSync(previous: RidingBehaviourState): Boolean {
if (previous !is SubmarineState) return false
if (previous.onSurface.get() != onSurface.get()) return true
return super.shouldSync(previous)
}
}
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.api.riding.stats
import com.cobblemon.mod.common.api.cooking.Flavour
import com.cobblemon.mod.common.util.lang
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
/**
* A stat that is used for riding Pokémon. The exact implementation of each of these depends on the [com.cobblemon.mod.common.api.riding.controller.RideController].
@@ -29,10 +30,9 @@ enum class RidingStat(
companion object {
fun getByName(name: String) = RidingStat.entries.firstOrNull { it.name.equals(name, ignoreCase = true) }
fun getByFlavour(flavour: Flavour) = RidingStat.entries.firstOrNull { it.flavour == flavour }
}
val displayName: Component
get() = lang("riding.stat.${name.lowercase()}.name")
val description: Component
get() = lang("riding.stat.${name.lowercase()}.desc")
val displayName: MutableComponent
get() = lang("ui.stats.ride.${name.lowercase()}")
}
@@ -1,70 +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.stats
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.net.IntSize
import com.cobblemon.mod.common.util.readSizedInt
import com.cobblemon.mod.common.util.readText
import com.cobblemon.mod.common.util.writeSizedInt
import com.cobblemon.mod.common.util.writeText
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.chat.Component
/**
* The definition of all the different information related to a specific [RidingStat] on a Pokémon species.
*
* @author Hiroku
* @since February 17th, 2025
*/
class RidingStatDefinition {
// This object gets read from a JSON adapter manually (field-by-field) so don't add fields without updating the adapter.
/** A map from [RidingStyle] to a minimum and maximum value for this stat. */
val ranges = mutableMapOf<RidingStyle, IntRange>()
/** Display name for the stat, if null falls back to [RidingStat.displayName]. */
var displayName: Component? = null
/** Description for the stat, if null falls back to [RidingStat.description]. */
var description: Component? = null
fun calculate(style: RidingStyle, aprijuiceBoost: Int): Float {
val range = ranges[style] ?: ranges[RidingStyle.LAND] ?: return 0F
val boostQuotient = aprijuiceBoost / 255F
return range.first + (range.last - range.first) * boostQuotient
}
fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeNullable(displayName) { _, it -> buffer.writeText(it) }
buffer.writeNullable(description) { _, it -> buffer.writeText(it) }
buffer.writeMap(
ranges,
{ _, it -> buffer.writeEnum(it) },
{ _, it ->
buffer.writeSizedInt(IntSize.U_BYTE, it.first)
buffer.writeSizedInt(IntSize.U_BYTE, it.last)
}
)
}
companion object {
fun decode(buffer: RegistryFriendlyByteBuf): RidingStatDefinition {
val displayName = buffer.readNullable { buffer.readText() }
val description = buffer.readNullable { buffer.readText() }
val ranges = buffer.readMap<RidingStyle, IntRange>(
{ buffer.readEnum<RidingStyle>(RidingStyle::class.java) },
{ IntRange(buffer.readSizedInt(IntSize.U_BYTE), buffer.readSizedInt(IntSize.U_BYTE)) }
)
return RidingStatDefinition().apply {
this.displayName = displayName
this.description = description
this.ranges.putAll(ranges)
}
}
}
}
@@ -74,7 +74,7 @@ class RidingAnimationData(val ride: PokemonEntity)
}
if( r ) {
//TODO: should this return?
val controller = (ride.firstPassenger as? OrientationControllable)?.orientationController ?: return
val controller = (ride as? OrientationControllable)?.orientationController ?: return
val orientation = controller.orientation ?: Matrix3f()
val currPitch = controller.pitch
@@ -23,6 +23,7 @@ import com.cobblemon.mod.common.api.spawning.detail.PokemonHerdSpawnDetail
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.api.spawning.influence.BucketMultiplyingInfluence
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.api.spawning.position.AreaSpawnablePositionResolver
import com.cobblemon.mod.common.api.spawning.position.FishingSpawnablePosition
@@ -141,6 +142,16 @@ object BestSpawner {
fishingSpawner = BasicSpawner(
name = "fishing",
spawnPool = CobblemonSpawnPools.WORLD_SPAWN_POOL
)
).also {
it.influences.add(
BucketMultiplyingInfluence(
multipliers = mapOf(
"uncommon" to 2.25f,
"rare" to 5.5f,
"ultra-rare" to 5.5f,
)
)
)
}
}
}
@@ -33,15 +33,15 @@ class TimeRange : IntRanges {
"any" to TimeRange(0..23999),
"day" to TimeRange(23460..23999, 0..12541),
"night" to TimeRange(12542..23459),
"noon" to TimeRange(5000..6999),
"midnight" to TimeRange(17000..18999),
"dawn" to TimeRange(22300..23999, 0..999),
"dusk" to TimeRange(11834..13701),
"twilight" to TimeRange(11834..13701, 22300..23999, 0..166),
"morning" to TimeRange(23000..23999, 0..4999),
"noon" to TimeRange(5000..6999),
"afternoon" to TimeRange(7000..12999),
"predawn" to TimeRange(19000..22999),
"evening" to TimeRange(13000..16999)
"evening" to TimeRange(13000..16999),
"midnight" to TimeRange(17000..18999),
"predawn" to TimeRange(19000..22999),
"dawn" to TimeRange(22300..23999, 0..166),
"dusk" to TimeRange(11834..13701),
"twilight" to TimeRange(11834..13701, 22300..23999, 0..166)
)
}
@@ -34,7 +34,7 @@ import net.minecraft.server.packs.PackType
* A single spawn pool may be used for many different [Spawner]s. Note that changing the
* [details] list will change the spawns for any [Spawner] sharing this pool. If you want
* to make a change for a pool specifically to one spawner, take a copy of the pool using
* [copy], and change that spawner's pool using [Spawner.setSpawnPool].
* [copy], and change that spawner's pool using [Spawner.spawnPool] setters.
*
* @author Hiroku
* @since February 9th, 2022
@@ -12,16 +12,15 @@ import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.fishing.BobberSpawnPokemonEvent
import com.cobblemon.mod.common.api.fishing.SpawnBaitEffects
import com.cobblemon.mod.common.api.fishing.SpawnBait
import com.cobblemon.mod.common.api.fishing.SpawnBaitEffects
import com.cobblemon.mod.common.api.pokemon.Natures
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.SpawnCause
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.api.spawning.influence.SpawnBaitInfluence
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.item.interactive.PokerodItem
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.api.spawning.influence
import com.cobblemon.mod.common.CobblemonBlocks
import com.cobblemon.mod.common.api.Priority
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
import com.cobblemon.mod.common.api.spawning.fishing.FishingSpawnCause
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
@@ -43,6 +44,10 @@ class SaccharineLogSlatheredInfluence(val pos: BlockPos? = null) : SpawningInflu
override fun affectSpawn(action: SpawnAction<*>, entity: Entity) {
if (entity is PokemonEntity) {
// avoid double application or applying to a pokemon that already has a hidden ability
if (entity.pokemon.forcedAspects.contains(HONEY_DRENCHED_ASPECT)
|| entity.pokemon.ability.priority !== Priority.LOWEST) return
if (Math.random() <= HIDDEN_ABILITY_CHANCE) {
FishingSpawnCause.alterHAAttempt(entity)
}
@@ -30,7 +30,7 @@ import kotlin.random.Random
* @author Hiroku
* @since February 14th, 2022
*/
class PlayerSpawner(
open class PlayerSpawner(
player: ServerPlayer,
spawnPool: SpawnPool
) : BasicSpawner(
@@ -20,6 +20,8 @@ import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.api.moves.animations.ActionEffectTimeline
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
import com.cobblemon.mod.common.api.moves.animations.TargetsProvider
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
import com.cobblemon.mod.common.battles.ShowdownInterpreter
@@ -31,6 +33,7 @@ import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.pokemon.evolution.progress.UseMoveEvolutionProgress
import com.cobblemon.mod.common.util.asArrayValue
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.cobblemonResource
import java.util.concurrent.CompletableFuture
@@ -49,7 +52,7 @@ class MoveInstruction(
) : InterpreterInstruction, CauserInstruction {
val effect = message.effectAt(1) ?: Effect.pure("", "")
val move = Moves.getByNameOrDummy(effect.id)
val actionEffect = move.actionEffect
val actionEffect: ActionEffectTimeline? by lazy { ActionEffects.getEffectWithBattleContext(cobblemonResource(move.name), userPokemon) }
val spreadTargets = message.optionalArgument("spread")?.split(",") ?: emptyList()
var future = CompletableFuture.completedFuture(Unit)
@@ -61,6 +64,8 @@ class MoveInstruction(
override fun invoke(battle: PokemonBattle) {
userPokemon = message.battlePokemon(0, battle)!!
targetPokemon = message.battlePokemon(2, battle)
val actionEffect = actionEffect ?: ActionEffects.actionEffects["generic_move".asIdentifierDefaultingNamespace()]
val targetPokemon = targetPokemon // So smart non-null casts can happen
val optionalEffect = message.effect()
@@ -51,7 +51,7 @@ open class PokeSnackBlockEntity(pos: BlockPos, state: BlockState) :
companion object {
const val SPAWNS_PER_BITE = 1
const val RADIUS = 8
const val RANDOM_TICKS_BETWEEN_SPAWNS = 4
const val RANDOM_TICKS_BETWEEN_SPAWNS = 2
const val POKE_SNACK_CRUMBED_ASPECT = "poke_snack_crumbed"
}
@@ -69,14 +69,17 @@ open class PokeSnackBlockEntity(pos: BlockPos, state: BlockState) :
val baitEffects = getBaitEffects()
val highestLureTier = baitEffects
val stackedLureTier = baitEffects
.filter { it.type == SpawnBait.Effects.RARITY_BUCKET }
.maxOfOrNull { it.value }
?.toInt()
?: 0
.sumOf { it.value }
.toInt()
if (highestLureTier > 0) {
it.influences += BucketNormalizingInfluence(tier = highestLureTier)
if (stackedLureTier > 0) {
it.influences += BucketNormalizingInfluence(
tier = stackedLureTier,
gradient = 0.2F,
firstTier = 1.2F
)
}
it.influences += BucketMultiplyingInfluence(
@@ -23,6 +23,7 @@ import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.block.entity.TintBlockEntity
import com.cobblemon.mod.common.client.battle.ClientBattle
import com.cobblemon.mod.common.client.gui.PartyOverlay
import com.cobblemon.mod.common.client.gui.PartyOverlayDataControl
import com.cobblemon.mod.common.client.gui.RideControlsOverlay
import com.cobblemon.mod.common.client.gui.battle.BattleOverlay
import com.cobblemon.mod.common.client.gui.cookingpot.CookingPotScreen
@@ -65,7 +66,7 @@ import com.cobblemon.mod.common.client.tooltips.CobblemonTooltipGenerator
import com.cobblemon.mod.common.client.tooltips.FishingBaitTooltipGenerator
import com.cobblemon.mod.common.client.tooltips.FishingRodTooltipGenerator
import com.cobblemon.mod.common.client.tooltips.PokePuffTooltipGenerator
import com.cobblemon.mod.common.client.tooltips.RecipeSeasoningAbsorbtionTooltipGenerator
import com.cobblemon.mod.common.client.tooltips.RecipeSeasoningAbsorptionTooltipGenerator
import com.cobblemon.mod.common.client.tooltips.SeasoningTooltipGenerator
import com.cobblemon.mod.common.client.tooltips.TooltipManager
import com.cobblemon.mod.common.client.trade.ClientTrade
@@ -192,12 +193,13 @@ object CobblemonClient {
}
}
ClientPlayerIcon.onTick()
PartyOverlayDataControl.tick(event.client.isPaused)
}
}
private fun registerTooltipManagers() {
TooltipManager.registerTooltipGenerator(CobblemonTooltipGenerator)
TooltipManager.registerTooltipGenerator(RecipeSeasoningAbsorbtionTooltipGenerator)
TooltipManager.registerTooltipGenerator(RecipeSeasoningAbsorptionTooltipGenerator)
TooltipManager.registerTooltipGenerator(FishingBaitTooltipGenerator)
TooltipManager.registerTooltipGenerator(SeasoningTooltipGenerator)
TooltipManager.registerTooltipGenerator(FishingRodTooltipGenerator)
@@ -56,8 +56,11 @@ object MountedCameraTypeHandler {
data.updatePerspective(key, currentCameraType)
}
fun handleTransition(fromKey: ResourceLocation, toKey: ResourceLocation) {
fun handleTransition(passenger: Entity, fromKey: ResourceLocation, toKey: ResourceLocation) {
val mc = Minecraft.getInstance()
if (passenger != mc.player) {
return
}
val currentCameraType = mc.options.cameraType
val data = ClientPersistedData.ridingPerspectives
data.updatePerspective(fromKey, currentCameraType)
@@ -491,6 +491,12 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
cryAnimation = animation
}
override fun applyRenderRotation(partialTick: Float) {
currentEntity.ifRidingAvailable { behaviour, settings, state ->
behaviour.applyRenderRotation(settings, state, currentEntity, partialTick)
}
}
fun getSeatLocator(passenger: Entity): String {
val seatIndex = this.getEntity().occupiedSeats.indexOf(passenger)
if (seatIndex == -1) throw IllegalArgumentException("Entity is not currently riding a seat")
@@ -11,26 +11,25 @@ package com.cobblemon.mod.common.client.gui
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.gui.drawPosablePortrait
import com.cobblemon.mod.common.api.text.darkGray
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.battle.BattleGUI
import com.cobblemon.mod.common.client.gui.portrait.FullyAnimatedPortraitDrawer
import com.cobblemon.mod.common.client.gui.portrait.SelectedAnimatedPortraitDrawer
import com.cobblemon.mod.common.client.gui.portrait.InanimatePortraitDrawer
import com.cobblemon.mod.common.client.gui.portrait.PortraitStyle
import com.cobblemon.mod.common.client.gui.toast.CobblemonToast
import com.cobblemon.mod.common.client.keybind.boundKey
import com.cobblemon.mod.common.client.keybind.keybinds.HidePartyBinding
import com.cobblemon.mod.common.client.keybind.keybinds.SummaryBinding
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.render.getDepletableRedGreen
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
import com.cobblemon.mod.common.client.render.renderScaledGuiItemIcon
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import java.util.UUID
import net.minecraft.client.DeltaTracker
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.Gui
@@ -40,6 +39,8 @@ import net.minecraft.client.gui.components.toasts.Toast
import net.minecraft.client.gui.screens.ChatScreen
import net.minecraft.client.gui.screens.Screen
import net.minecraft.util.Mth
import kotlin.math.ceil
import kotlin.math.floor
class PartyOverlay : Gui(Minecraft.getInstance()) {
@@ -58,16 +59,34 @@ class PartyOverlay : Gui(Minecraft.getInstance()) {
private val genderIconMale = cobblemonResource("textures/gui/party/party_gender_male.png")
private val genderIconFemale = cobblemonResource("textures/gui/party/party_gender_female.png")
private val portraitBackground = cobblemonResource("textures/gui/party/party_slot_portrait_background.png")
private val newMovesPopup = cobblemonResource("textures/gui/party/party_slot_notification_new_move.png")
private val newEvoPopup = cobblemonResource("textures/gui/party/party_slot_notification_evolution.png")
private val levelScroll = cobblemonResource("textures/gui/party/party_slot_portrait_level_up.png")
private val inanimatePortraitDrawer = InanimatePortraitDrawer()
private val selectedAnimatedPortraitDrawer = SelectedAnimatedPortraitDrawer()
private val fullyAnimatedPortraitDrawer = FullyAnimatedPortraitDrawer()
private val screenExemptions: List<Class<out Screen>> = listOf(
ChatScreen::class.java,
BattleGUI::class.java
)
fun canRender(): Boolean {
val minecraft = Minecraft.getInstance()
// Hiding if a Screen is open and not exempt
if (minecraft.screen != null) {
if (!screenExemptions.contains(minecraft.screen?.javaClass as Class<out Screen>))
return false
}
if (minecraft.options.hideGui || minecraft.debugOverlay.showDebugScreen()) {
return false
}
// Hiding if toggled via Keybind
if (HidePartyBinding.shouldHide) {
return false
}
return true
}
}
private val screenExemptions: List<Class<out Screen>> = listOf(
ChatScreen::class.java,
BattleGUI::class.java
)
private val stateAtIndex = hashMapOf<Int, Pair<UUID, FloatingState>>()
val starterToast = CobblemonToast(
Mth.createInsecureUUID(),
@@ -88,24 +107,22 @@ class PartyOverlay : Gui(Minecraft.getInstance()) {
attachedToast = false
}
fun getPokemonState(index: Int, pokemonUUID: UUID): FloatingState {
val state = this.stateAtIndex.getOrPut(index) { pokemonUUID to FloatingState() }
if (state.first != pokemonUUID) {
val newState = FloatingState()
this.stateAtIndex[index] = pokemonUUID to newState
return newState
}
return state.second
}
override fun render(context: GuiGraphics, tickCounter: DeltaTracker) {
if (!canRender()) return
val partialDeltaTicks = tickCounter.realtimeDeltaTicks
val minecraft = Minecraft.getInstance()
// Hiding if a Screen is open and not exempt
if (minecraft.screen != null) {
if (!screenExemptions.contains(minecraft.screen?.javaClass as Class<out Screen>))
return
}
if (minecraft.options.hideGui || minecraft.debugOverlay.showDebugScreen()) {
return
}
// Hiding if toggled via Keybind
if (HidePartyBinding.shouldHide) {
return
}
val panelX = 0
val party = CobblemonClient.storage.party
val matrices = context.pose()
@@ -160,13 +177,20 @@ class PartyOverlay : Gui(Minecraft.getInstance()) {
0.0
)
val portraitDrawer = when (Cobblemon.config.partyPortraitAnimations) {
PortraitStyle.NEVER_ANIMATE -> inanimatePortraitDrawer
PortraitStyle.ANIMATE_SELECTED -> selectedAnimatedPortraitDrawer
PortraitStyle.ALWAYS_ANIMATE -> fullyAnimatedPortraitDrawer
val shouldAnimate = when (Cobblemon.config.partyPortraitAnimations) {
PokemonGUIAnimationStyle.NEVER_ANIMATE -> false
PokemonGUIAnimationStyle.ANIMATE_SELECTED -> selectedSlot == index
PokemonGUIAnimationStyle.ALWAYS_ANIMATE -> true
}
portraitDrawer.draw(pokemon, matrices, partialDeltaTicks, selectedSlot == index, index)
drawPosablePortrait(
identifier = pokemon.species.resourceIdentifier,
matrixStack = matrices,
doQuirks = false,
partialTicks = if (shouldAnimate) partialDeltaTicks else 0F,
contextScale = pokemon.form.baseScale,
state = getPokemonState(index = index, pokemonUUID = pokemon.uuid).also { it.currentAspects = pokemon.aspects }
)
matrices.popPose()
context.disableScissor()
@@ -193,6 +217,46 @@ class PartyOverlay : Gui(Minecraft.getInstance()) {
)
if (pokemon != null) {
val expGainedData = PartyOverlayDataControl.getExpGainedData(pokemon.uuid)
//Handling Level Up Scroll / For some reason draw text makes any future blitk render behind our model render, don't ask why
var displayLevelScroll = false
var levelScrollOffset = 0F
var levelScrollPositionOffset = 0F
var levelScrollSize = 17F
if (expGainedData != null && expGainedData.oldLevel != null) {
val ticksWithDelta = (expGainedData.ticks + partialDeltaTicks) - (PartyOverlayDataControl.BAR_UPDATE_BEFORE_TIME + PartyOverlayDataControl.BAR_FLASH_TIME)
val transition = ticksWithDelta / PartyOverlayDataControl.LEVEL_UP_PORTRAIT_TIME
if ((0F..<1F).contains(transition)) {
displayLevelScroll = true
var step = ceil(transition * 14)
if (step <= 4) {
levelScrollSize = step * 4
levelScrollPositionOffset = 17 - (levelScrollSize)
} else if (step <= 10) {
step -= 4
levelScrollOffset = -1 + (step * 4)
} else {
step -= 10
levelScrollSize = 20 - (step * 4)
levelScrollOffset = 23 + (step * 4)
}
}
}
if (displayLevelScroll) {
blitk(
matrixStack = matrices,
texture = levelScroll,
x = (panelX + selectedOffsetX + portraitFrameOffsetX + 2),
y = (indexY + portraitFrameOffsetY + 2 + levelScrollPositionOffset),
height = levelScrollSize,
width = 17,
textureHeight = 43,
vOffset = levelScrollOffset
)
}
val stateIcon = pokemon.state.getIcon(pokemon)
if (stateIcon != null) {
blitk(
@@ -216,9 +280,17 @@ class PartyOverlay : Gui(Minecraft.getInstance()) {
shadow = true
)
var level = pokemon.level
if (expGainedData?.oldLevel != null) {
val ticksWithDelta = expGainedData.ticks + partialDeltaTicks
if (ticksWithDelta < PartyOverlayDataControl.BAR_UPDATE_BEFORE_TIME) {
level = expGainedData.oldLevel!!
}
}
drawScaledText(
context = context,
text = pokemon.level.toString().text(),
text = level.toString().text(),
x = panelX + selectedOffsetX + 6.5F,
y = indexY + 18,
scale = SCALE,
@@ -267,9 +339,47 @@ class PartyOverlay : Gui(Minecraft.getInstance()) {
blue = 0.27F
)
val expRatio: Float
val expForThisLevel = pokemon.experience - if (pokemon.level == 1) 0 else pokemon.experienceGroup.getExperience(pokemon.level)
val expToNextLevel = pokemon.experienceGroup.getExperience(pokemon.level + 1) - pokemon.experienceGroup.getExperience(pokemon.level)
val expRatio = expForThisLevel / expToNextLevel.toFloat()
var expRed = 0.2
var expGreen = 0.65
var expBlue = 0.84
if (expGainedData != null) {
val ticksWithDelta = expGainedData.ticks + partialDeltaTicks
if (expGainedData.oldLevel != null) {
//Handling Exp Bar
if (ticksWithDelta <= PartyOverlayDataControl.BAR_UPDATE_BEFORE_TIME) {
val expForLastLevel = (pokemon.experience - expGainedData.expGained) - if (expGainedData.oldLevel!! == 1) 0 else pokemon.experienceGroup.getExperience(expGainedData.oldLevel!!)
val expNeededForLastLevel = pokemon.experienceGroup.getExperience(expGainedData.oldLevel!! + 1) - pokemon.experienceGroup.getExperience(expGainedData.oldLevel!!)
val transition = (ticksWithDelta / PartyOverlayDataControl.BAR_UPDATE_BEFORE_TIME.toFloat()).coerceIn(0F..1F)
val transitionXP = expForLastLevel + ((expNeededForLastLevel - expForLastLevel) * transition)
expRatio = transitionXP / expNeededForLastLevel
} else if (ticksWithDelta <= PartyOverlayDataControl.BAR_UPDATE_BEFORE_TIME + PartyOverlayDataControl.BAR_FLASH_TIME) {
expRatio = 1F
expRed = 1.0
expGreen = 1.0
expBlue = 1.0
}
else {
val adjustedTicks = ticksWithDelta - (PartyOverlayDataControl.BAR_UPDATE_BEFORE_TIME + PartyOverlayDataControl.BAR_FLASH_TIME)
val transition = (adjustedTicks / PartyOverlayDataControl.BAR_UPDATE_BEFORE_TIME.toFloat()).coerceIn(0F..1F)
val transitionXP = expForThisLevel * transition
expRatio = transitionXP / expToNextLevel
}
} else {
//Handling Exp Bar
val transition = (ticksWithDelta / PartyOverlayDataControl.BAR_UPDATE_NO_LEVEL_TIME.toFloat()).coerceIn(0F..1F)
val transitionXP = expForThisLevel - (expGainedData.expGained * (1 - transition))
expRatio = transitionXP / expToNextLevel
}
} else {
expRatio = expForThisLevel / expToNextLevel.toFloat()
}
val expBarWidth = 1
val expBarHeight = expRatio * barHeightMax
@@ -283,11 +393,80 @@ class PartyOverlay : Gui(Minecraft.getInstance()) {
height = expBarHeight,
textureHeight = expBarHeight / expRatio,
vOffset = barHeightMax - expBarHeight,
red = 0.2,
green = 0.65,
blue = 0.84
red = expRed,
green = expGreen,
blue = expBlue
)
var ticksForMove = -1F
var ticksForEvo = -1F
//Handling Popups
if (expGainedData != null) {
val ticksWithDelta = expGainedData.ticks + partialDeltaTicks
val ticks = ticksWithDelta - (PartyOverlayDataControl.BAR_UPDATE_BEFORE_TIME + PartyOverlayDataControl.BAR_FLASH_TIME)
if (expGainedData.countOfMovesLearned > 0) {
ticksForMove = ticks
}
if (expGainedData.countOfEvosUnlocked > 0) {
ticksForEvo = ticks
}
} else {
val evoGainedData = PartyOverlayDataControl.getEvolutionData(pokemon.uuid)
if (evoGainedData != null) {
ticksForEvo = evoGainedData.ticks + partialDeltaTicks
}
val moveGainedData = PartyOverlayDataControl.getMovesData(pokemon.uuid)
if (moveGainedData != null) {
ticksForMove = moveGainedData.ticks + partialDeltaTicks
}
}
fun popupData(ticks: Float) : Pair<Float, Float> {
if (ticks > 0) {
if (ticks <= PartyOverlayDataControl.POPUP_TIME.fadeIn) {
val transition = ticks / PartyOverlayDataControl.POPUP_TIME.fadeIn
val step = (floor(transition * 3)) + 1
return (4 - step) to (step * 0.25F)
} else if (ticks <= PartyOverlayDataControl.POPUP_TIME.fadeIn + PartyOverlayDataControl.POPUP_TIME.hold) {
return 0F to 1F
} else {
val adjustedPopupTicks = ticks - (PartyOverlayDataControl.POPUP_TIME.fadeIn + PartyOverlayDataControl.POPUP_TIME.hold)
val transition = adjustedPopupTicks / PartyOverlayDataControl.POPUP_TIME.fadeIn
val step = (floor(transition * 3)) + 1
return (-step) to (1F - (step * 0.25F))
}
} else return 0F to 0F
}
val (movePopupYOffset, movePopupTransparency) = popupData(ticksForMove)
val (evoPopupYOffset, evoPopupTransparency) = popupData(ticksForEvo)
if (evoPopupTransparency > 0) {
blitk(
matrixStack = matrices,
texture = newEvoPopup,
x = (panelX + selectedOffsetX + 56.5F) / SCALE,
y = (indexY + 4 + evoPopupYOffset) / SCALE,
width = 37,
height = 20,
scale = SCALE,
alpha = evoPopupTransparency
)
}
if (movePopupTransparency > 0) {
blitk(
matrixStack = matrices,
texture = newMovesPopup,
x = (panelX + selectedOffsetX + 56.5F) / SCALE,
y = (indexY + 17 + movePopupYOffset) / SCALE,
width = 37,
height = 20,
scale = SCALE,
alpha = movePopupTransparency
)
}
val ballIcon = cobblemonResource("textures/gui/ball/" + pokemon.caughtBall.name.path + ".png")
val ballHeight = 22
blitk(
@@ -0,0 +1,227 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui
import com.cobblemon.mod.common.CobblemonSounds
import net.minecraft.client.Minecraft
import net.minecraft.client.resources.sounds.SimpleSoundInstance
import net.minecraft.client.resources.sounds.SoundInstance
import net.minecraft.sounds.SoundEvent
import java.util.*
object PartyOverlayDataControl {
const val BAR_UPDATE_BEFORE_TIME = 15
const val BAR_UPDATE_AFTER_TIME = 15
const val BAR_FLASH_TIME = 2
val POPUP_TIME = FadeTimings(3, 40, 3)
const val LEVEL_UP_PORTRAIT_TIME = 10
val BAR_UPDATE_NO_LEVEL_TIME = BAR_UPDATE_BEFORE_TIME + BAR_UPDATE_AFTER_TIME + BAR_FLASH_TIME
private enum class RenderTarget {
NONE,
EXP_GAINED,
EVOLUTION,
MOVES
}
private val overlayData = mutableMapOf<UUID, OverlayData>()
private var barFillupSound: SoundInstance? = null
private var levelUpJingleSound: SoundInstance? = null
fun pokemonGainedExp(pokemonUuid : UUID, oldLevel: Int?, expGained: Int, countOfMovesLearned: Int, countOfEvosUnlocked: Int = 0) {
var time = BAR_UPDATE_NO_LEVEL_TIME
if (countOfMovesLearned > 0 || countOfEvosUnlocked > 0) {
time = BAR_UPDATE_BEFORE_TIME + BAR_FLASH_TIME + POPUP_TIME.total()
}
val data = overlayData[pokemonUuid]
val newData = ExpGainedData(oldLevel, expGained, countOfMovesLearned, countOfEvosUnlocked).also { it.ticksMax = time }
if (data != null) {
if (data.movesGainedData != null) {
newData.countOfMovesLearned += data.movesGainedData!!.movesCount
}
if (data.evoGainedData != null) {
newData.countOfEvosUnlocked += data.evoGainedData!!.evolutionCount
}
val expData = data.expGainedData
if (expData != null) {
newData.countOfMovesLearned += expData.countOfMovesLearned
newData.countOfEvosUnlocked += expData.countOfEvosUnlocked
if (expData.oldLevel != null) {
newData.oldLevel = expData.oldLevel
}
newData.expGained += expData.expGained
}
}
overlayData[pokemonUuid] = OverlayData(expGainedData = newData)
stopSound(barFillupSound)
barFillupSound = playSound(CobblemonSounds.LEVELUP_START)
}
fun pokemonGainedEvo(pokemonUuid : UUID, evoCount: Int) {
val data = overlayData[pokemonUuid]
val newData = EvolutionGainedData(evoCount).also { it.ticksMax = POPUP_TIME.total() }
if (data != null) {
if (data.expGainedData != null && data.expGainedData!!.ticks < BAR_UPDATE_BEFORE_TIME + BAR_FLASH_TIME) {
data.expGainedData!!.ticksMax = BAR_UPDATE_BEFORE_TIME + BAR_FLASH_TIME + POPUP_TIME.total()
data.expGainedData!!.countOfEvosUnlocked += evoCount
return
}
if (data.evoGainedData != null) {
data.evoGainedData!!.evolutionCount += evoCount
data.evoGainedData!!.ticks = 0
} else {
data.evoGainedData = newData
}
} else {
overlayData[pokemonUuid] = OverlayData(evoGainedData = newData)
}
}
fun pokemonGainedMoves(pokemonUuid : UUID, movesCount: Int) { //Unused for now I think, until TMs work, as I don't think you can get any other way than level up
val data = overlayData[pokemonUuid]
val newData = MovesGainedData(movesCount).also { it.ticksMax = POPUP_TIME.total() }
if (data != null) {
if (data.expGainedData != null && data.expGainedData!!.ticks < BAR_UPDATE_BEFORE_TIME + BAR_FLASH_TIME) {
data.expGainedData!!.ticksMax = BAR_UPDATE_BEFORE_TIME + BAR_FLASH_TIME + POPUP_TIME.total()
data.expGainedData!!.countOfMovesLearned += movesCount
return
}
if (data.movesGainedData != null) {
data.movesGainedData!!.movesCount += movesCount
data.evoGainedData!!.ticks = 0
} else {
data.movesGainedData = newData
}
} else {
overlayData[pokemonUuid] = OverlayData(movesGainedData = newData)
}
}
private fun getCurrentRenderTarget(uuid: UUID): RenderTarget {
val data = overlayData[uuid] ?: return RenderTarget.NONE
if (data.expGainedData != null) return RenderTarget.EXP_GAINED
if (data.evoGainedData != null) return RenderTarget.EVOLUTION
if (data.movesGainedData != null) return RenderTarget.MOVES
return RenderTarget.NONE
}
fun getExpGainedData(uuid: UUID): ExpGainedData? {
if (getCurrentRenderTarget(uuid) != RenderTarget.EXP_GAINED) return null
return overlayData[uuid]?.expGainedData
}
fun getEvolutionData(uuid: UUID): EvolutionGainedData? {
if (getCurrentRenderTarget(uuid) != RenderTarget.EVOLUTION) return null
return overlayData[uuid]?.evoGainedData
}
fun getMovesData(uuid: UUID): MovesGainedData? {
if (getCurrentRenderTarget(uuid) != RenderTarget.MOVES) return null
return overlayData[uuid]?.movesGainedData
}
fun tick(paused: Boolean) {
val deletion = mutableListOf<UUID>()
if (paused) return
overlayData.forEach { (id, data) ->
if (data.expGainedData != null) { // We update this in order and render these in order, and as such we don't tick others
val expGainedData = data.expGainedData!!
expGainedData.ticks += 1
if (expGainedData.ticks == BAR_UPDATE_BEFORE_TIME && expGainedData.oldLevel != null) {
stopSound(levelUpJingleSound)
levelUpJingleSound = playSound(CobblemonSounds.LEVELUP)
}
if (expGainedData.ticks >= expGainedData.ticksMax) {
data.expGainedData = null
}
} else {
if (data.evoGainedData != null) {
val evoGainedData = data.evoGainedData!!
evoGainedData.ticks += 1
if (evoGainedData.ticks >= evoGainedData.ticksMax) {
data.evoGainedData = null
}
}
if (data.movesGainedData != null) {
val movesGainedData = data.movesGainedData!!
movesGainedData.ticks += 1
if (movesGainedData.ticks >= movesGainedData.ticksMax) {
data.movesGainedData = null
}
}
}
if (getCurrentRenderTarget(id) == RenderTarget.NONE) deletion.add(id)
}
deletion.forEach {
overlayData.remove(it)
}
}
fun clear() {
overlayData.clear()
stopSound(barFillupSound)
stopSound(levelUpJingleSound)
}
open class TickCounter(
var ticksMax: Int,
var ticks: Int = 0
)
class ExpGainedData (
var oldLevel: Int?, //If Null we never leveled up
var expGained: Int,
var countOfMovesLearned: Int, //We don't care what moves we learned, just how many
var countOfEvosUnlocked: Int
) : TickCounter(0)
class EvolutionGainedData (
var evolutionCount: Int
) : TickCounter(0)
class MovesGainedData (
var movesCount: Int
) : TickCounter(0)
data class OverlayData(
var expGainedData: ExpGainedData? = null,
var evoGainedData: EvolutionGainedData? = null,
var movesGainedData: MovesGainedData? = null
)
data class FadeTimings(
val fadeIn: Int,
val hold: Int,
val fadeOut: Int
) {
fun total(): Int {
return fadeIn + hold + fadeOut
}
}
//SOUNDS
private fun playSound(soundEvent: SoundEvent): SoundInstance? {
if (!PartyOverlay.canRender()) return null
val soundInstance = SimpleSoundInstance.forUI(soundEvent, 1F)
Minecraft.getInstance().soundManager.play(soundInstance)
return soundInstance
}
private fun stopSound(soundInstance: SoundInstance?) {
if (soundInstance != null) Minecraft.getInstance().soundManager.stop(soundInstance)
}
}
@@ -6,24 +6,20 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.portrait
package com.cobblemon.mod.common.client.gui
import com.mojang.serialization.Codec
import net.minecraft.util.StringRepresentable
enum class PortraitStyle : StringRepresentable {
enum class PokemonGUIAnimationStyle : StringRepresentable {
NEVER_ANIMATE,
ANIMATE_SELECTED,
ALWAYS_ANIMATE;
override fun getSerializedName(): String = this.name
override fun getSerializedName() = this.name
companion object {
@JvmStatic
val CODEC: Codec<PortraitStyle> = StringRepresentable.fromEnum(PortraitStyle::values)
val CODEC: Codec<PokemonGUIAnimationStyle> = StringRepresentable.fromEnum(PokemonGUIAnimationStyle::values)
}
}
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.client.gui.battle
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.gui.drawPosablePortrait
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
@@ -322,7 +323,7 @@ class BattleOverlay : Gui(Minecraft.getInstance()), Schedulable {
reversed = reversed,
doQuirks = false,
state = state,
partialTicks = partialTicks
partialTicks = if (Cobblemon.config.animateBattleTiles) partialTicks else 0F
)
}
matrixStack.popPose()
@@ -13,7 +13,6 @@ import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.moves.MoveTemplate
import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.battles.*
import com.cobblemon.mod.common.client.gui.battle.subscreen.BattleGimmickButton.GimmickTile
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.math.toRGB
import com.mojang.blaze3d.vertex.PoseStack
@@ -139,8 +138,7 @@ abstract class BattleGimmickButton(gimmick: ShowdownMoveset.Gimmick, val x: Floa
pp = gimmickTemplate?.pp ?: moveTemplate.pp,
priority = gimmickTemplate?.priority ?: moveTemplate.priority,
critRatio = gimmickTemplate?.critRatio ?: moveTemplate.critRatio,
effectChances = gimmickTemplate?.effectChances ?: moveTemplate.effectChances,
actionEffect = null
effectChances = gimmickTemplate?.effectChances ?: moveTemplate.effectChances
)
}
}
@@ -299,7 +299,7 @@ class DialogueScreen(var dialogueDTO: DialogueDTO) : Screen("gui.dialogue".asTra
fun playGibberSpeak(delta: Float, text: String, gibber: DialogueGibberDTO) {
timeElapsedSinceGibber += delta / 20
if (timeElapsedSinceGibber < gibber.interval) {
if (timeElapsedSinceGibber < gibber.interval || gibberDone) {
return
}
@@ -8,10 +8,12 @@
package com.cobblemon.mod.common.client.gui.pc
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
import com.cobblemon.mod.common.client.gui.pasture.PasturePCGUIConfiguration
import com.cobblemon.mod.common.client.gui.PokemonGUIAnimationStyle
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
import com.cobblemon.mod.common.client.render.renderScaledGuiItemIcon
@@ -72,12 +74,19 @@ open class StorageSlot(
matrices.translate(posX + (SIZE / 2.0), posY + 1.0, 0.0)
matrices.scale(2.5F, 2.5F, 1F)
val animationConfig = if (this is PartyStorageSlot) Cobblemon.config.summaryProfileAnimations else Cobblemon.config.pcProfileAnimations
val shouldAnimate = when (animationConfig) {
PokemonGUIAnimationStyle.ALWAYS_ANIMATE -> true
PokemonGUIAnimationStyle.NEVER_ANIMATE -> false
PokemonGUIAnimationStyle.ANIMATE_SELECTED -> isHoveredOrFocused
}
drawProfilePokemon(
renderablePokemon = pokemon.asRenderablePokemon(),
matrixStack = matrices,
rotation = Quaternionf().fromEulerXYZDegrees(Vector3f(13F, 35F, 0F)),
state = state,
partialTicks = partialTicks,
partialTicks = if (shouldAnimate) partialTicks else 0F,
scale = 4.5F
)
matrices.popPose()
@@ -1,64 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.portrait
import com.cobblemon.mod.common.api.gui.drawPosablePortrait
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
import com.cobblemon.mod.common.pokemon.Pokemon
import com.mojang.blaze3d.vertex.PoseStack
import java.util.UUID
/**
* Base logic for a [PortraitDrawer] that animates a portrait based on conditions.
*/
abstract class AnimatedPortraitDrawer : PortraitDrawer {
private val state = FloatingState()
private val stateAtIndex = hashMapOf<Int, Pair<UUID, FloatingState>>()
override fun draw(pokemon: Pokemon, poseStack: PoseStack, partialTicks: Float, isSelected: Boolean, index: Int) {
state.currentAspects = pokemon.aspects
drawPosablePortrait(
identifier = pokemon.species.resourceIdentifier,
matrixStack = poseStack,
partialTicks = this.ticksFor(pokemon, isSelected, partialTicks),
contextScale = pokemon.form.baseScale,
state = this.stateFor(pokemon, isSelected, index)
)
}
protected abstract fun shouldAnimate(pokemon: Pokemon, isSelected: Boolean): Boolean
protected open fun ticksFor(pokemon: Pokemon, isSelected: Boolean, partialTicks: Float): Float {
return if (this.shouldAnimate(pokemon, isSelected)) partialTicks else 0F
}
protected open fun stateFor(pokemon: Pokemon, isSelected: Boolean, index: Int): FloatingState {
if (!this.shouldAnimate(pokemon, isSelected)) {
return this.state
}
val state = this.stateAtIndex.getOrPut(index) { pokemon.uuid to this.createState(pokemon) }
if (state.first != pokemon.uuid) {
val newState = this.createState(pokemon)
this.stateAtIndex[index] = pokemon.uuid to newState
return newState
}
return state.second
}
protected open fun createState(pokemon: Pokemon): FloatingState {
val state = FloatingState()
state.currentAspects = pokemon.aspects
return state
}
}
@@ -1,25 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.portrait
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.client.GraphicsStatus
/**
* Draws portraits if the Pokemon is not fainted.
*
* Meant for client with [GraphicsStatus.FABULOUS].
*/
class FullyAnimatedPortraitDrawer : AnimatedPortraitDrawer() {
override fun shouldAnimate(pokemon: Pokemon, isSelected: Boolean): Boolean {
return !pokemon.isFainted()
}
}
@@ -1,37 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.portrait
import com.cobblemon.mod.common.api.gui.drawPosablePortrait
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
import com.cobblemon.mod.common.pokemon.Pokemon
import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.GraphicsStatus
/**
* The simplest implementation, the Pokemon is drawn staying still.
*
* Meant for client with [GraphicsStatus.FAST].
*/
class InanimatePortraitDrawer : PortraitDrawer {
private val state = FloatingState()
override fun draw(pokemon: Pokemon, poseStack: PoseStack, partialTicks: Float, isSelected: Boolean, index: Int) {
state.currentAspects = pokemon.aspects
drawPosablePortrait(
identifier = pokemon.species.resourceIdentifier,
matrixStack = poseStack,
partialTicks = 0F,
contextScale = pokemon.form.baseScale,
state = this.state
)
}
}
@@ -1,38 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.portrait
import com.cobblemon.mod.common.pokemon.Pokemon
import com.mojang.blaze3d.vertex.PoseStack
/**
* Responsible for drawing a Pokemon portrait.
*
* Keep in mind any scaling and handling of the [PoseStack] must be done on the implementations using this system.
*/
fun interface PortraitDrawer {
/**
* Draws the portrait.
*
* @param pokemon The [Pokemon] being drawn.
* @param poseStack The [PoseStack] being used.
* @param partialTicks The partial ticks of the screen using this.
* @param isSelected If the [pokemon] is considered in a selected state for example hovered or the current party member.
* @param index The index of the [pokemon] in the screen using this.
*/
fun draw(
pokemon: Pokemon,
poseStack: PoseStack,
partialTicks: Float,
isSelected: Boolean,
index: Int
)
}
@@ -1,25 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.portrait
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.client.GraphicsStatus
/**
* Draws portraits if the Pokemon is not fainted and selected.
*
* Meant for client with [GraphicsStatus.FANCY].
*/
class SelectedAnimatedPortraitDrawer : AnimatedPortraitDrawer() {
override fun shouldAnimate(pokemon: Pokemon, isSelected: Boolean): Boolean {
return !pokemon.isFainted() && isSelected
}
}
@@ -8,9 +8,11 @@
package com.cobblemon.mod.common.client.gui.summary.widgets
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.PokemonGUIAnimationStyle
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
import com.cobblemon.mod.common.client.gui.summary.Summary
import com.cobblemon.mod.common.client.render.drawScaledText
@@ -154,13 +156,20 @@ class PartySlotWidget(
matrices.pushPose()
matrices.translate(x + (PORTRAIT_DIAMETER / 2.0), y - 3.0, 0.0)
matrices.scale(2.5F, 2.5F, 1F)
val shouldAnimate = when (Cobblemon.config.summaryProfileAnimations) {
PokemonGUIAnimationStyle.ALWAYS_ANIMATE -> true
PokemonGUIAnimationStyle.NEVER_ANIMATE -> false
PokemonGUIAnimationStyle.ANIMATE_SELECTED -> isHovered || isSelected
}
drawProfilePokemon(
species = slotPokemon.species.resourceIdentifier,
matrixStack = matrices,
rotation = Quaternionf().fromEulerXYZDegrees(Vector3f(13F, 35F, 0F)),
state = state,
scale = 4.5F,
partialTicks = delta
partialTicks = if (shouldAnimate) delta else 0F
)
matrices.popPose()
@@ -78,14 +78,17 @@ object PartySendBinding : CobblemonBlockingKeyBinding(
return
}
if (CobblemonClient.storage.selectedSlot == -1) return
if (Minecraft.getInstance().screen != null) return
val selectedPartyPokemon = CobblemonClient.storage.party.get(CobblemonClient.storage.selectedSlot) ?: return
val selectedPartyPokemon = if (CobblemonClient.storage.selectedSlot >= 0) {
CobblemonClient.storage.party.get(CobblemonClient.storage.selectedSlot)
} else {
null
}
if (isRidingPokemon(player) && canAttemptDismount(player, selectedPartyPokemon)) {
sendToServer(DismountPokemonPacket())
}
else if (!isRidingSelectedPokemon(player, selectedPartyPokemon)){
} else if (selectedPartyPokemon != null && !isRidingSelectedPokemon(player, selectedPartyPokemon)){
checkForTargetInteractions(player, selectedPartyPokemon)
}
}
@@ -105,8 +108,7 @@ object PartySendBinding : CobblemonBlockingKeyBinding(
collideBlock = ClipContext.Fluid.NONE)
if (canSendOutPokemon(player, targetEntity)) {
sendToServer(SendOutPokemonPacket(CobblemonClient.storage.selectedSlot))
}
else {
} else {
processEntityTarget(player, selectedPartyPokemon, targetEntity)
}
}
@@ -140,7 +142,7 @@ object PartySendBinding : CobblemonBlockingKeyBinding(
}
}
private fun canAttemptDismount(player: LocalPlayer, selectedPartyPokemon: Pokemon): Boolean {
private fun canAttemptDismount(player: LocalPlayer, selectedPartyPokemon: Pokemon?): Boolean {
if (player.vehicle !is PokemonEntity) return false
val vehicle = player.vehicle as PokemonEntity
if (player != vehicle.controllingPassenger) {
@@ -148,11 +150,10 @@ object PartySendBinding : CobblemonBlockingKeyBinding(
}
val isAirRide = vehicle.ridingController?.context?.style == RidingStyle.AIR
val hasLandRide = vehicle.rideProp.behaviours?.get(RidingStyle.LAND) != null
if (isAirRide && hasLandRide) {
return false
}
else {
return vehicle.pokemon.uuid == selectedPartyPokemon.uuid
return if (isAirRide && hasLandRide) {
false
} else {
vehicle.pokemon.uuid == selectedPartyPokemon?.uuid
}
}
@@ -29,5 +29,6 @@ object RideSettingsSyncHandler : ClientNetworkPacketHandler<RideSettingsSyncPack
CobblemonRideSettings.boat = packet.boat
CobblemonRideSettings.burst = packet.burst
CobblemonRideSettings.dolphin = packet.dolphin
CobblemonRideSettings.submarine = packet.submarine
}
}
@@ -0,0 +1,25 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.net.gui
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
import com.cobblemon.mod.common.client.gui.PartyOverlayDataControl
import com.cobblemon.mod.common.net.messages.client.ui.ExpGainedDataPacket
import net.minecraft.client.Minecraft
object ExpGainedDataPacketHandler: ClientNetworkPacketHandler<ExpGainedDataPacket> {
override fun handle(packet: ExpGainedDataPacket, client: Minecraft) {
PartyOverlayDataControl.pokemonGainedExp(
packet.pokemonUUID,
packet.oldLevel,
packet.expGained,
packet.countOfMovesLearned
)
}
}
@@ -10,22 +10,41 @@ package com.cobblemon.mod.common.client.net.orientation
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
import com.cobblemon.mod.common.duck.RidePassenger
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateOrientationPacket
import net.minecraft.client.Minecraft
import net.minecraft.util.Mth
object S2CUpdateOrientationHandler : ClientNetworkPacketHandler<ClientboundUpdateOrientationPacket> {
object ClientboundUpdateOrientationHandler : ClientNetworkPacketHandler<ClientboundUpdateOrientationPacket> {
override fun handle(packet: ClientboundUpdateOrientationPacket, client: Minecraft) {
client.executeIfPossible {
val level = client.level ?: return@executeIfPossible
val entity = level.getEntity(packet.entityId)
if (entity is OrientationControllable) {
packet.active?.let {
entity.orientationController.active = it
if (!it) {
entity.orientationController.reset()
val player = client.player ?: return@executeIfPossible
val vehicle = level.getEntity(packet.entityId)
val vehicleController = if (vehicle is OrientationControllable) vehicle.orientationController else return@executeIfPossible
// If the client is also the driver of this entity then don't update it as the driver is the source
// of truth on vehicle orientation updates.
if (vehicle.controllingPassenger != player) {
packet.active?.let { shouldUseCustomOrientation ->
val playerIsPassenger = vehicle.passengers.contains(player)
// If the player has just switched to riding a custom orientation ride then set their
// ride x and y rots local to the vehicle rots(if the controller wasn't active and will now be active)
// This ensures that on transition your camera stays in the same spot
if (!vehicleController.isActive() && shouldUseCustomOrientation && playerIsPassenger) {
// Set local to the vehicle x and yrot
val playerRotater = player as RidePassenger?
if (playerRotater != null) {
playerRotater.`cobblemon$setRideXRot`(Mth.wrapDegrees(player.getXRot() - vehicle.getXRot()))
playerRotater.`cobblemon$setRideYRot`(Mth.wrapDegrees(player.getYRot() - vehicle.getYRot()))
}
}
vehicle.orientationController.active = shouldUseCustomOrientation
}
entity.orientationController.updateOrientation { _ -> packet.orientation }
vehicle.orientationController.updateOrientation { _ -> packet.orientation }
}
}
}
@@ -14,6 +14,9 @@ import com.cobblemon.mod.common.api.text.yellow
import com.cobblemon.mod.common.util.lang
import net.minecraft.network.chat.Component
/**
* Used for aprijuice quality.
*/
enum class CookingQuality {
LOW {
override fun getLang(): Component = lang("cooking.cooking_quality.low").red()
@@ -11,14 +11,7 @@ package com.cobblemon.mod.common.client.render
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.animation.PoseTransitionAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockActiveAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockPoseAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.TextureInterpolationType
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Bone
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository
import com.cobblemon.mod.common.util.adapters.*
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
@@ -28,13 +21,10 @@ import com.cobblemon.mod.common.util.toRGBA
import com.google.gson.GsonBuilder
import com.google.gson.annotations.SerializedName
import com.mojang.blaze3d.platform.NativeImage
import com.mojang.blaze3d.systems.RenderSystem
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.RenderStateShard
import net.minecraft.client.renderer.texture.DynamicTexture
import kotlin.math.floor
import net.minecraft.resources.ResourceLocation
import org.joml.Vector2d
import org.joml.Vector3f
import org.joml.Vector4f
@@ -63,7 +53,7 @@ class VaryingRenderableResolver(
}
fun getResolvedTexture(state: PosableState): ResourceLocation {
return getVariationValue(state) { texture }?.getTexture(state)
return getVariationValue(state) { texture }?.invoke(state)
?: throw IllegalStateException("Unable to find a texture for $name with aspects ${state.currentAspects.joinToString()}. This shouldn't be possible if you've defined the fallback variation.")
}
@@ -190,13 +180,6 @@ class ModelAssetVariation(
}
}
/** Needed a custom one, so I can make it take in a dynamic texture instead of resource location */
class DynamicStateShard(texture: DynamicTexture) : RenderStateShard.EmptyTextureStateShard(Runnable {
RenderSystem.setShaderTexture(0, texture.id)
}, Runnable {
texture.close() // Cleanup
})
/**
* Given the animation seconds, returns a texture to use. Only implemented
* by [StaticModelTextureSupplier] and [AnimatedModelTextureSupplier].
@@ -204,17 +187,12 @@ class DynamicStateShard(texture: DynamicTexture) : RenderStateShard.EmptyTexture
* @author Hiroku
* @since February 6th, 2023
*/
interface ModelTextureSupplier {
operator fun invoke(state: PosableState): RenderStateShard.EmptyTextureStateShard
fun getTexture(state: PosableState): ResourceLocation
fun interface ModelTextureSupplier {
operator fun invoke(state: PosableState): ResourceLocation
}
class StaticModelTextureSupplier(val texture: ResourceLocation): ModelTextureSupplier {
override fun invoke(state: PosableState): RenderStateShard.EmptyTextureStateShard {
return RenderStateShard.TextureStateShard(texture, false, false)
}
override fun getTexture(state: PosableState): ResourceLocation {
override fun invoke(state: PosableState): ResourceLocation {
return texture
}
}
@@ -225,196 +203,80 @@ class AnimatedModelTextureSupplier(
val frames: List<ResourceLocation>,
val interpolation: Boolean
): ModelTextureSupplier {
override fun invoke(state: PosableState): RenderStateShard.EmptyTextureStateShard {
var frameIndex = state.animationSeconds * fps
if (frameIndex > frames.size - 1.0f && !loop) frameIndex = frames.size.toFloat() - 1.0f
val i = frameIndex % 1.0
if (interpolation && i != 0.0) {
val o = floor(frameIndex).toInt() % frames.size
val s = if (o == frames.size - 1) 0 else o + 1
val resourceManager = Minecraft.getInstance().resourceManager
val texture1 = NativeImage.read(resourceManager.getResourceOrThrow(frames[o]).open())
val texture2 = NativeImage.read(resourceManager.getResourceOrThrow(frames[s]).open())
val image = getInterpolatedTexture(texture1, texture2, i)
val newTexture = DynamicTexture(image)
newTexture.setFilter(false, false)
return DynamicStateShard(newTexture)
}
else {
val frame = floor(frameIndex).toInt() % frames.size
return RenderStateShard.TextureStateShard(frames[frame], false, false)
}
}
override fun getTexture(state: PosableState): ResourceLocation {
override fun invoke(state: PosableState): ResourceLocation {
val frameIndex = floor(state.animationSeconds * fps).toInt()
return frames[frameIndex]
}
}
class KeyframableTextureSupplier(val textures: MutableList<ResourceLocation>) : ModelTextureSupplier {
var layerName = ""
override fun invoke(state: PosableState): RenderStateShard.EmptyTextureStateShard {
val resourceManager = Minecraft.getInstance().resourceManager
if (state.primaryAnimation?.animation is PoseTransitionAnimation) {
val poseTransition = state.primaryAnimation!!.animation as PoseTransitionAnimation
val (animation1, startTime1) = getMainAnimFromPose(poseTransition.beforePose, state, true)
val (animation2, startTime2) = getMainAnimFromPose(poseTransition.afterPose, state, true)
val texture2 = if (animation2 != null) {
getImageFromAnimation(animation2, state.animationSeconds - startTime2)
} else {
val image = NativeImage.read(resourceManager.getResourceOrThrow(textures[0]).open())
getEmptyTexture(image.width, image.height)
}
val (interpType, cubicValues) = poseTransition.beforePose.texturePhaseOut
?: poseTransition.afterPose.texturePhaseIn
if (interpType == TextureInterpolationType.STEP) {
val newTexture = DynamicTexture(texture2)
newTexture.setFilter(false, false)
return DynamicStateShard(newTexture)
}
val texture1 = if (animation1 != null) {
getImageFromAnimation(animation1, state.animationSeconds - startTime1)
} else {
val image = NativeImage.read(resourceManager.getResourceOrThrow(textures[0]).open())
getEmptyTexture(image.width, image.height)
}
var i =
((state.animationSeconds - poseTransition.startTime) / (poseTransition.endTime - poseTransition.startTime)).toDouble() //Linear
if (interpType == TextureInterpolationType.BEZIER) { //Switch to Bezier
i = resolveCubicValues(cubicValues, i)
}
val newImage = getInterpolatedTexture(texture1, texture2, i)
val newTexture = DynamicTexture(newImage)
newTexture.setFilter(false, false)
return DynamicStateShard(newTexture)
return if (frameIndex >= frames.size && !loop) {
frames.last()
} else {
var animation = if ((state.primaryAnimation?.animation as? BedrockActiveAnimation)?.animation?.cobblemonTextures?.get(layerName) != null) {
(state.primaryAnimation?.animation as? BedrockActiveAnimation)?.animation
} else null
var startTime = 0F
if (animation == null) {
val data = getMainAnimFromPose(state.currentPose?.let(state.currentModel!!.poses::get), state)
animation = data.first
startTime = data.second
}
if (animation != null) {
val image = getImageFromAnimation(animation, state.animationSeconds - startTime)
val newTexture = DynamicTexture(image)
newTexture.setFilter(false, false)
return DynamicStateShard(newTexture)
}
frames[frameIndex % frames.size]
}
val image = NativeImage.read(resourceManager.getResourceOrThrow(textures[0]).open())
val newTexture = DynamicTexture(getEmptyTexture(image.width, image.height))
newTexture.setFilter(false, false)
return DynamicStateShard(newTexture)
}
override fun getTexture(state: PosableState): ResourceLocation {
return textures[0]
}
private fun getMainAnimFromPose(pose: Pose?, state: PosableState, transitionAnimation: Boolean = false): Pair<BedrockAnimation?, Float> {
var animation: BedrockAnimation? = null
var startTime = 0F
pose?.animations?.filterIsInstance<BedrockPoseAnimation>()?.filter{ transitionAnimation || state.shouldIdleRun(it, 0F) }?.forEach {
if (it.animation.cobblemonTextures[layerName] != null) {
animation = it.animation
}
}
if (state.primaryAnimation == null && state.currentModel!!.context.request(RenderContext.DO_QUIRKS) != false) {
pose?.quirks?.forEach { quirk ->
val quirkData = quirk.getOrCreateData(state)
quirkData.animations.filterIsInstance<BedrockActiveAnimation>().forEach {
if (it.animation.cobblemonTextures[layerName] != null) {
startTime = it.startedSeconds
animation = it.animation
}
}
}
}
return Pair(animation, startTime)
}
private fun getImageFromAnimation(animation: BedrockAnimation, animationSeconds: Float): NativeImage {
fun interpolatedTexture(state: PosableState): DynamicTexture? {
val resourceManager = Minecraft.getInstance().resourceManager
val keyframeMap = animation.cobblemonTextures[layerName]
if (keyframeMap.isNullOrEmpty()) {
val image1 = NativeImage.read(resourceManager.getResourceOrThrow(textures[0]).open())
return getEmptyTexture(image1.width, image1.height)
}
var adjustedTime = animationSeconds
if (animation.shouldLoop) adjustedTime %= animation.animationLength.toFloat()
val keyIndex = keyframeMap.keys.indexOfLast { it <= adjustedTime }
val keyframe1 = when(keyIndex) {
-1 -> keyframeMap.firstEntry()
else -> keyframeMap.entries.toMutableList()[keyIndex]
}
val keyframe2 = when {
keyIndex + 1 >= keyframeMap.size -> keyframeMap.lastEntry()
else -> keyframeMap.entries.toMutableList()[keyIndex+1]
val frameIndex = floor(state.animationSeconds * fps).toInt()
try {
if (frameIndex >= frames.size && !loop) {
return DynamicTexture(NativeImage.read(resourceManager.getResourceOrThrow(frames.last()).open()))
}
} catch (e : Exception) {
return null
}
val o = frameIndex % frames.size
val i = state.animationSeconds * fps - frameIndex
val texture1 : NativeImage
var texture2 : NativeImage? = null
val texture2 : NativeImage
if (keyframe1.value.textureSlot == -1 && (keyframe2.value.textureSlot == -1 || keyframe1.value.interpolationType == TextureInterpolationType.STEP)) { // If both -1 we just return empty texture or if Step
val image1 = NativeImage.read(resourceManager.getResourceOrThrow(textures[0]).open())
return getEmptyTexture(image1.width, image1.height)
try { //Try getting images of the frames so we can use them to make a new one
texture1 = NativeImage.read(resourceManager.getResourceOrThrow(frames[o]).open())
val s = if (o == frames.size - 1) 0 else o + 1
texture2 = NativeImage.read(resourceManager.getResourceOrThrow(frames[s]).open())
} catch (e : Exception) {
return null
}
var inverseInterp = false
if (keyframe1.value.textureSlot == -1) {
texture1 = NativeImage.read(resourceManager.getResourceOrThrow(textures[keyframe2.value.textureSlot]).open())
inverseInterp = true
} else if (keyframe2.value.textureSlot == -1) {
texture1 = NativeImage.read(resourceManager.getResourceOrThrow(textures[keyframe1.value.textureSlot]).open())
} else {
texture1 = NativeImage.read(resourceManager.getResourceOrThrow(textures[keyframe1.value.textureSlot]).open())
texture2 = NativeImage.read(resourceManager.getResourceOrThrow(textures[keyframe2.value.textureSlot]).open())
}
for (x in 0..<texture1.width) {
for (y in 0..<texture1.height) {
val color1 = texture1.getPixelRGBA(x,y).toRGBA()
val color2 = texture2.getPixelRGBA(x,y).toRGBA()
val i = (adjustedTime - keyframe1.key.toFloat()) / (keyframe2.key - keyframe1.key)
if (keyframe1.value.textureSlot != keyframe2.value.textureSlot && keyframe1.value.interpolationType == TextureInterpolationType.LINEAR) {
var interp = i
if (inverseInterp) interp = 1 - i
return getInterpolatedTexture(texture1, texture2, interp)
} else if (keyframe1.value.textureSlot != keyframe2.value.textureSlot && keyframe1.value.interpolationType == TextureInterpolationType.BEZIER) {
var cubicInterp = resolveCubicValues(keyframe1.value.cubicLerpFunctionValues, i)
if (inverseInterp) cubicInterp = 1 - cubicInterp
return getInterpolatedTexture(texture1, texture2, cubicInterp)
}
var newRed : Int
var newGreen : Int
var newBlue : Int
var newAlpha : Int
return NativeImage.read(resourceManager.getResourceOrThrow(textures[keyframe1.value.textureSlot]).open())
//Usually full transparent pixel is black, so this makes it copy the other texture colours to look better, however considering if we should allow to disable this feature
if (color1.w == 0F && color2.w == 1F) {
newRed = floor(color2.x * 255).toInt()
newGreen = floor(color2.y * 255).toInt()
newBlue = floor(color2.z * 255).toInt()
} else if (color1.w == 1F && color2.w == 0F) {
newRed = floor(color1.x * 255).toInt()
newGreen = floor(color1.y * 255).toInt()
newBlue = floor(color1.z * 255).toInt()
} else {
newRed = floor((color1.x + (color2.x - color1.x) * i) * 255).toInt()
newGreen = floor((color1.y + (color2.y - color1.y) * i) * 255).toInt()
newBlue = floor((color1.z + (color2.z - color1.z) * i) * 255).toInt()
}
newAlpha = floor((color1.w + (color2.w - color1.w) * i) * 255).toInt()
val finalColor = (newAlpha shl 24) or (newRed shl 16) or (newGreen shl 8) or newBlue
texture1.setPixelRGBA(x,y,finalColor) //We aren't using texture 1 anymore so it's easier to override it
}
}
val newTexture = DynamicTexture(texture1)
newTexture.setFilter(false, false)
return newTexture
}
}
class VariableModelTextureSupplier : ModelTextureSupplier {
override fun invoke(state: PosableState): RenderStateShard.EmptyTextureStateShard {
return RenderStateShard.TextureStateShard(getTexture(state), false, false)
}
override fun getTexture(state: PosableState): ResourceLocation {
override fun invoke(state: PosableState): ResourceLocation {
return state.runtime.environment.variable.map["texture"]?.asString()?.asIdentifierDefaultingNamespace()
?: cobblemonResource("textures/npcs/default.png")
}
@@ -433,75 +295,4 @@ class ModelLayer {
enum class SpriteType {
PORTRAIT,
PROFILE
}
private fun getInterpolatedTexture(texture1: NativeImage, texture2: NativeImage?, i: Double): NativeImage {
for (x in 0..<texture1.width) {
for (y in 0..<texture1.height) {
val color1 = texture1.getPixelRGBA(x,y).toRGBA()
val color2 = texture2?.getPixelRGBA(x,y)?.toRGBA() ?: Vector4f(0F,0F,0F,0F)
var newRed : Int
var newGreen : Int
var newBlue : Int
//Usually full transparent pixel is black, so this makes it copy the other texture colours to look better, however considering if we should allow to disable this feature
if (color1.w == 0F && color2.w > 0F) {
newRed = floor(color2.x * 255).toInt()
newGreen = floor(color2.y * 255).toInt()
newBlue = floor(color2.z * 255).toInt()
} else if (color1.w > 0F && color2.w == 0F) {
newRed = floor(color1.x * 255).toInt()
newGreen = floor(color1.y * 255).toInt()
newBlue = floor(color1.z * 255).toInt()
} else {
newRed = floor((color1.x + (color2.x - color1.x) * i) * 255).toInt()
newGreen = floor((color1.y + (color2.y - color1.y) * i) * 255).toInt()
newBlue = floor((color1.z + (color2.z - color1.z) * i) * 255).toInt()
}
val newAlpha = floor((color1.w + (color2.w - color1.w) * i) * 255).toInt()
val finalColor = (newAlpha shl 24) or (newRed shl 16) or (newGreen shl 8) or newBlue
texture1.setPixelRGBA(x,y,finalColor) //We aren't using texture 1 anymore so it's easier to override it
}
}
return texture1
}
private fun getEmptyTexture(width: Int, height: Int): NativeImage {
return NativeImage(width, height, false)
}
private fun resolveCubicValues(clampedCubicValues : MutableList<Double>, i: Double): Double {
for (value in 0..<clampedCubicValues.size) {
clampedCubicValues[value] = Math.clamp(clampedCubicValues[value],0.0,1.0)
}
val points = mutableListOf<Vector2d>()
for (o in 0..200) {
val t = o/200.0
val omt = (1.0-t)
val point = Vector2d()
point.x = 3.0 * omt * omt * t * clampedCubicValues[0] + 3.0 * omt * t * t * clampedCubicValues[2] + t * t * t
point.y = 3.0 * omt * omt * t * clampedCubicValues[1] + 3.0 * omt * t * t * clampedCubicValues[3] + t * t * t
points.add(point)
}
var beforePoint = Vector2d(0.0,0.0)
var afterPoint = Vector2d(1.0, 1.0)
for (point in points) {
if (point.x > beforePoint.x && point.x < i) {
beforePoint = point
}
if (point.x < afterPoint.x && point.x > i) {
afterPoint = point
}
}
return beforePoint.y + (afterPoint.y - beforePoint.y) * ((i - beforePoint.x) / (afterPoint.x - beforePoint.x))
}
@@ -15,23 +15,26 @@ import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
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.getPoser
import com.cobblemon.mod.common.duck.RidePassenger
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.mixin.accessor.CameraAccessor
import com.mojang.math.Axis
import com.cobblemon.mod.common.util.math.geometry.toRadians
import net.minecraft.client.Camera
import net.minecraft.client.CameraType
import net.minecraft.client.Minecraft
import net.minecraft.util.Mth
import net.minecraft.world.entity.Entity
import net.minecraft.world.level.ClipContext
import net.minecraft.world.phys.HitResult
import net.minecraft.world.phys.Vec3
import org.joml.Matrix3f
import org.joml.Quaternionf
import org.joml.Vector3f
object MountedCameraRenderer
{
fun getCameraPosition(
instance: Camera,
entity: Entity,
driver: Entity,
vehicle: Entity,
x: Double,
y: Double,
@@ -51,99 +54,99 @@ object MountedCameraRenderer
Mth.lerp(instance.partialTickTime.toDouble(), vehicle.zOld, vehicle.z)
)
setup(vehicle, instance.partialTickTime)
val rollable = entity as OrientationControllable
val controller = rollable.getOrientationController()
val rollable = vehicle as OrientationControllable
val vehicleController = rollable.getOrientationController()
val model = getPoser(vehicle.pokemon.species.resourceIdentifier, FloatingState())
val locatorName = delegate.getSeatLocator(driver)
val locator = delegate.locatorStates[locatorName] ?: return null
// First person and view bobbing are handled the same way.
if (!instance.isDetached || Cobblemon.config.thirdPersonViewBobbing) {
val delegate = vehicle.delegate as PokemonClientDelegate
val locatorName = delegate.getSeatLocator(entity)
val locator = delegate.locatorStates[locatorName]
val currEyeHeight: Double = Mth.lerp(instance.partialTickTime.toDouble(), eyeHeight, eyeHeightOld)
val offset = Vector3f(0f, (currEyeHeight - (driver.bbHeight / 2)).toFloat(), 0f)
val eyeOffset = Vector3f(0f, (currEyeHeight - (driver.bbHeight / 2)).toFloat(), 0f)
if (locator == null) {
return null
}
// Get additional offset from poser and add to the eyeHeight offset
val shouldFlip = !(vehicleController.active && vehicleController.orientation != null) // Do not flip the offset for 3rd person reverse unless we are using normal mc camera rotation.
val isFirstPerson = Minecraft.getInstance().options.getCameraType() == CameraType.FIRST_PERSON
val locatorOffset = Vec3(locator.matrix.getTranslation(Vector3f()))
val rotMatrix = if (vehicleController.isActive()) vehicleController.getRenderOrientation(instance.partialTickTime)
else Quaternionf()
.rotateY((180f - Mth.lerp(instance.partialTickTime, vehicle.yBodyRotO, vehicle.yBodyRot).toRadians()))
.rotateX(-1 * Mth.lerp(instance.partialTickTime, vehicle.xRotO, vehicle.xRot).toRadians())
// Get Eye height
val currEyeHeight: Double = Mth.lerp(instance.partialTickTime.toDouble(), eyeHeight, eyeHeightOld)
var offset = Vector3f(0f, (currEyeHeight - (entity.bbHeight / 2)).toFloat(), 0f)
// Get additional offset from poser and add to the eyeHeight offset
if (!instance.isDetached) {
offset.add(
getFirstPersonOffset(model, locatorName)
)
} else {
offset.add(
getThirdPersonOffset(thirdPersonReverse, model.thirdPersonCameraOffset, locatorName)
)
}
if (controller.isActive()) {
offset = controller.getRenderOrientation(instance.partialTickTime).transform(offset)
} else {
offset = Axis.YP.rotationDegrees(180f - instance.yRot).transform(offset)
}
val pos = locatorOffset.add(entityPos).toVector3f()
val offsetDistance = offset.length()
val offsetDirection = offset.mul(1 / offsetDistance)
// Use getMaxZoom to calculate clipping
val maxZoom: Float = getMaxZoom(offsetDistance, offsetDirection, pos, instance)
return Vec3(offsetDirection.mul(maxZoom).add(pos))
eyeOffset.add(getFirstPersonOffset(model, locatorName))
if (isFirstPerson) {
offset.add(
getFirstPersonOffset(model, locatorName)
)
} else if (Cobblemon.config.thirdPersonViewBobbing) {
offset.add(
getThirdPersonOffset(thirdPersonReverse, model.thirdPersonCameraOffset, locatorName, shouldFlip)
)
} else {
val xRot = (-1 * Math.toRadians(instance.xRot.toDouble())).toFloat()
offset.add(
getThirdPersonOffset(thirdPersonReverse, model.thirdPersonCameraOffsetNoViewBobbing, locatorName, shouldFlip)
)
}
// get orientation based on OrientationController or normal rotations
val orientation =
if (controller.isActive() && controller.orientation != null)
controller.orientation!!
else
Matrix3f()
.rotateY(Math.toRadians((180f - instance.yRot).toDouble()).toFloat())
.rotateX(xRot)
// Grab the pokemon's orientation to use as the offset rotation.
rotMatrix.transform(offset)
rotMatrix.transform(eyeOffset)
// Get pivot from poser
val pivotOffsets = model.thirdPersonPivotOffset
val pokemonCenter = Vector3f(0f, entity.bbHeight/2, 0f)
val eyeLocatorOffset = Vec3(locator.matrix.getTranslation(Vector3f()))
val eyePos = eyeLocatorOffset.add(entityPos).toVector3f()
var pivot = Vector3f(pokemonCenter)
val locatorName = delegate.getSeatLocator(entity)
if (pivotOffsets.containsKey(locatorName)) {
pivot = pivotOffsets[locatorName]!!.toVector3f()
pivot.sub(pokemonCenter)
orientation.transform(pivot)
pivot.add(pokemonCenter)
// Get the camera position. If 3rd person viewbobbing is enabled or the player is
// in first person then base the camera position off the seat locator offset
val pos = if (!isFirstPerson || Cobblemon.config.thirdPersonViewBobbing) {
val locatorOffset = Vec3(locator.matrix.getTranslation(Vector3f()))
locatorOffset.add(entityPos).toVector3f()
} else {
val pokemonCenter = Vector3f(0f, driver.bbHeight/2, 0f)
val pivotOffsets = model.thirdPersonPivotOffset
val pivot = Vector3f(pokemonCenter)
val locatorName = delegate.getSeatLocator(driver)
if (pivotOffsets.containsKey(locatorName)) {
val orientation = vehicleController.getRenderOrientation(instance.partialTickTime)
pivot.add(orientation.transform(pivotOffsets[locatorName]!!.toVector3f()))
}
entityPos.toVector3f().add(pivot)
}
val pos = entityPos.toVector3f().add(pivot)
val offsetDistance = offset.length()
val offsetDirection = offset.mul(1 / offsetDistance)
// Get offset from poser
val offset = getThirdPersonOffset(thirdPersonReverse, model.thirdPersonCameraOffsetNoViewBobbing, locatorName)
val eyeOffsetDistance = eyeOffset.length()
val eyeOffsetDirection = eyeOffset.mul(1 / eyeOffsetDistance)
val rotatedOffset = orientation.transform(offset)
val offsetDistance = rotatedOffset.length()
val offsetDirection = rotatedOffset.mul(1 / offsetDistance)
// Use getMaxZoom to calculate clipping
val maxZoom: Float = getMaxZoom(offsetDistance, offsetDirection, pos, instance)
val eyeMaxZoom: Float = getMaxZoom(eyeOffsetDistance, eyeOffsetDirection, eyePos, instance)
// Use getMaxZoom to calculate clipping
val maxZoom: Float = getMaxZoom(offsetDistance, offsetDirection, pos, instance)
val playerRotater = driver as RidePassenger
playerRotater.`cobblemon$setRideEyePos`(Vec3(eyeOffsetDirection.mul(eyeMaxZoom).add(eyePos)))
return Vec3(offsetDirection.mul(maxZoom).add(pos))
}
return Vec3(offsetDirection.mul(maxZoom).add(pos))
}
fun resetDriverRotations(
instance: Camera,
driver: Entity,
) {
val eulerAngs = Quaternionf(instance.rotation()).getEulerAnglesYXZ(Vector3f())
val playerRotater = driver as RidePassenger
// Backticks because kotlin explodes otherwise due to syntax
playerRotater.`cobblemon$setRideXRot`(-eulerAngs.x() * Mth.RAD_TO_DEG)
playerRotater.`cobblemon$setRideYRot`(180.0f - (eulerAngs.y() * Mth.RAD_TO_DEG))
}
private fun getThirdPersonOffset(
thirdPersonReverse: Boolean,
cameraOffsets: Map<String, Vec3>,
locatorName: String
locatorName: String,
shouldFlip: Boolean = true
): Vector3f {
val offset = if (thirdPersonReverse && cameraOffsets.containsKey(locatorName + "_reverse")) {
cameraOffsets[locatorName + "_reverse"]!!.toVector3f()
@@ -152,7 +155,10 @@ object MountedCameraRenderer
} else {
Vector3f(0f, 2f, 4f)
}
if (thirdPersonReverse) offset.z *= -1
// Don't need to account for this since orientation is derived from camera rotation and that z is already flipped.
// Flip only when not on a rollable.
if (thirdPersonReverse && shouldFlip) offset.z *= -1
return offset
}
@@ -9,11 +9,8 @@
package com.cobblemon.mod.common.client.render.color
import com.cobblemon.mod.common.CobblemonItemComponents
import com.cobblemon.mod.common.api.cooking.getColourMixFromFlavours
import com.cobblemon.mod.common.client.pot.CookingQuality
import net.minecraft.ChatFormatting
import com.cobblemon.mod.common.api.cooking.getColourMixFromRideStatBoosts
import net.minecraft.client.color.item.ItemColor
import net.minecraft.util.FastColor
import net.minecraft.world.item.ItemStack
object AprijuiceItemColorProvider : ItemColor {
@@ -23,8 +20,6 @@ object AprijuiceItemColorProvider : ItemColor {
override fun getColor(stack: ItemStack, layer: Int): Int {
if (layer == 0) return -1
val flavourComponent = stack.get(CobblemonItemComponents.FLAVOUR) ?: return -1
// todo we are not coloring the leaf anymore
/*if (layer == LEAF_INDEX) {
val quality = flavourComponent.getQuality()
@@ -38,7 +33,12 @@ object AprijuiceItemColorProvider : ItemColor {
}*/
if (layer == JUICE_INDEX) {
val colorMix = getColourMixFromFlavours(flavourComponent.getDominantFlavours())
val rideBoostsComponent = stack.get(CobblemonItemComponents.RIDE_BOOST) ?: return -1
if (rideBoostsComponent.boosts.isEmpty()) return -1
val highestBoost = rideBoostsComponent.boosts.maxOf { it.value }
val highestRidingStats = rideBoostsComponent.boosts.filter { it.value == highestBoost }.keys
if (highestRidingStats.isEmpty()) return -1
val colorMix = getColourMixFromRideStatBoosts(highestRidingStats)
if (colorMix != null) return colorMix
}
@@ -143,8 +143,8 @@ object CobblemonModelPredicateRegistry {
aprijuice.forEach { aprijuice ->
ItemProperties.register(aprijuice, cobblemonResource("aprijuice_quality")) { stack, world, entity, seed ->
val flavourComponent = stack.get(CobblemonItemComponents.FLAVOUR) ?: return@register 0.0f
val quality = flavourComponent.getQuality()
val rideBoostsComponent = stack.get(CobblemonItemComponents.RIDE_BOOST) ?: return@register 0.0f
val quality = rideBoostsComponent.getQuality()
return@register when (quality) {
CookingQuality.LOW -> 0.0f
@@ -16,7 +16,6 @@ import com.cobblemon.mod.common.client.ClientMoLangFunctions.setupClient
import com.cobblemon.mod.common.client.render.models.blockbench.animation.ActiveAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.animation.PoseAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.animation.SingleBoneLookAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.TextureInterpolationType
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.client.render.models.blockbench.quirk.SimpleQuirk
@@ -178,38 +177,4 @@ class JsonPose(model: PosableModel, json: JsonObject) {
val transitions = json.get("transitions")?.takeIf { it is JsonObject }?.asJsonObject?.entrySet()?.map { (key, value) ->
JsonPoseTransition(key, value.asString.asExpressionLike())
} ?: emptyList()
val texturePhaseIn = json.get("texturePhaseIn")?.takeIf { it is JsonObject }?.asJsonObject?.let { theObject ->
val interpolationType = when(theObject.get("interpolation")?.asString) {
"step" -> TextureInterpolationType.STEP
"linear" -> TextureInterpolationType.LINEAR
"bezier" -> TextureInterpolationType.BEZIER
else -> TextureInterpolationType.STEP
}
val cubicValues = (theObject.get("cubicValues")?.asString ?: "0.0, 0.0, 1.0, 1.0").let { values ->
val list = mutableListOf<Double>()
values.split(",").forEach {
list.add(it.toDouble())
}
list
}
Pair(interpolationType, cubicValues)
} ?: Pair(TextureInterpolationType.STEP, mutableListOf(0.0, 0.0, 1.0, 1.0))
val texturePhaseOut = json.get("texturePhaseOut")?.takeIf { it is JsonObject }?.asJsonObject?.let { theObject ->
val interpolationType = when(theObject.get("interpolation")?.asString) {
"step" -> TextureInterpolationType.STEP
"linear" -> TextureInterpolationType.LINEAR
"bezier" -> TextureInterpolationType.BEZIER
else -> TextureInterpolationType.STEP
}
val cubicValues = (theObject.get("cubicValues")?.asString ?: "0.0, 0.0, 1.0, 1.0").let { values ->
val list = mutableListOf<Double>()
values.split(",").forEach {
list.add(it.toDouble())
}
list
}
Pair(interpolationType, cubicValues)
}
}
@@ -19,13 +19,11 @@ import com.cobblemon.mod.common.client.ClientMoLangFunctions.animationFunctions
import com.cobblemon.mod.common.client.ClientMoLangFunctions.setupClient
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
import com.cobblemon.mod.common.client.render.AnimatedModelTextureSupplier
import com.cobblemon.mod.common.client.render.KeyframableTextureSupplier
import com.cobblemon.mod.common.client.render.ModelLayer
import com.cobblemon.mod.common.client.render.models.blockbench.animation.*
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockActiveAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockAnimationRepository
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockPoseAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.TextureInterpolationType
import com.cobblemon.mod.common.client.render.models.blockbench.frame.ModelFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Bone
@@ -278,9 +276,7 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
onTransitionedInto: (PosableState) -> Unit = {},
animations: Array<PoseAnimation> = emptyArray(),
transformedParts: Array<ModelPartTransformation> = emptyArray(),
quirks: Array<ModelQuirk<*>> = emptyArray(),
texturePhaseIn: Pair<TextureInterpolationType, MutableList<Double>> = Pair(TextureInterpolationType.STEP, mutableListOf(0.0, 0.0, 1.0, 1.0)),
texturePhaseOut: Pair<TextureInterpolationType, MutableList<Double>>? = null
quirks: Array<ModelQuirk<*>> = emptyArray()
): Pose {
return Pose(
poseType.name,
@@ -292,9 +288,7 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
namedAnimations,
animations,
transformedParts,
quirks,
texturePhaseIn,
texturePhaseOut
quirks
).also {
poses[poseType.name] = it
}
@@ -310,9 +304,7 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
onTransitionedInto: (PosableState) -> Unit = {},
animations: Array<PoseAnimation> = emptyArray(),
transformedParts: Array<ModelPartTransformation> = emptyArray(),
quirks: Array<ModelQuirk<*>> = emptyArray(),
texturePhaseIn: Pair<TextureInterpolationType, MutableList<Double>> = Pair(TextureInterpolationType.STEP, mutableListOf(0.0, 0.0, 1.0, 1.0)),
texturePhaseOut: Pair<TextureInterpolationType, MutableList<Double>>? = null
quirks: Array<ModelQuirk<*>> = emptyArray()
): Pose {
return Pose(
poseName,
@@ -324,9 +316,7 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
namedAnimations,
animations,
transformedParts,
quirks,
texturePhaseIn,
texturePhaseOut
quirks
).also {
poses[poseName] = it
}
@@ -342,9 +332,7 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
onTransitionedInto: (PosableState) -> Unit = {},
animations: Array<PoseAnimation> = emptyArray(),
transformedParts: Array<ModelPartTransformation> = emptyArray(),
quirks: Array<ModelQuirk<*>> = emptyArray(),
texturePhaseIn: Pair<TextureInterpolationType, MutableList<Double>> = Pair(TextureInterpolationType.STEP, mutableListOf(0.0, 0.0, 1.0, 1.0)),
texturePhaseOut: Pair<TextureInterpolationType, MutableList<Double>>? = null
quirks: Array<ModelQuirk<*>> = emptyArray()
): Pose {
return Pose(
poseName,
@@ -356,9 +344,7 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
namedAnimations,
animations,
transformedParts,
quirks,
texturePhaseIn,
texturePhaseOut
quirks
).also {
poses[poseName] = it
}
@@ -426,6 +412,13 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
fun registerRelevantPart(pairing: Pair<String, ModelPart>) = registerRelevantPart(pairing.first, pairing.second)
/** Needed a custom one, so I can make it take in a dynamic texture instead of resource location */
class DynamicStateShard(texture: DynamicTexture) : RenderStateShard.EmptyTextureStateShard(Runnable {
RenderSystem.setShaderTexture(0, texture.id)
}, Runnable {
texture.close() // Cleanup
})
/** Renders the model. Assumes rotations have been set. Will simply render the base model and then any extra layers. */
fun render(
context: RenderContext,
@@ -460,9 +453,16 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
val provider = bufferProvider
if (provider != null) {
for (layer in currentLayers) {
(layer.texture as? KeyframableTextureSupplier)?.layerName = layer.name
val texture = layer.texture?.invoke(currentState ?: FloatingState()) ?: continue
val renderLayer = makeLayer(texture, layer.emissive, layer.translucent, layer.translucent_cull)
var renderLayer : RenderType
if (layer.texture is AnimatedModelTextureSupplier && layer.texture.interpolation) {
//Handle Interpolation
val texture = layer.texture.interpolatedTexture(currentState ?: FloatingState()) ?: continue
renderLayer = makeLayer(DynamicStateShard(texture), layer.emissive, layer.translucent, layer.translucent_cull)
}
else {
val texture = layer.texture?.invoke(currentState ?: FloatingState()) ?: continue
renderLayer = getLayer(texture, layer.emissive, layer.translucent, layer.translucent_cull)
}
val consumer = provider.getBuffer(renderLayer)
val tint = layer.tint
val tintRed = (tint.x * r2 * 255).toInt()
@@ -747,9 +747,8 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
// If scale is 0 we start getting NaNs
scale.coerceAtLeast(0.01f)
if (entity.passengers.isNotEmpty() && entity.controllingPassenger is OrientationControllable
&& (entity.controllingPassenger as OrientationControllable).orientationController.active){
val controllingPassenger = entity.controllingPassenger as OrientationControllable
val controller = controllingPassenger.orientationController
&& (entity as OrientationControllable).orientationController.active){
val controller = entity.orientationController
val transformationMatrix = Matrix4f()
val center = Vector3f(0f, entity.bbHeight/2, 0f)
transformationMatrix.translate(center)
@@ -90,9 +90,7 @@ class PoseAdapter(
animations = pose.idleAnimations,
transformedParts = pose.transformedParts,
quirks = pose.quirks.toTypedArray(),
namedAnimations = pose.animations,
texturePhaseIn = pose.texturePhaseIn,
texturePhaseOut = pose.texturePhaseOut
namedAnimations = pose.animations
).also {
it.transitions.putAll(
pose.transitions
@@ -132,9 +132,7 @@ data class BedrockAnimation(
val shouldLoop: Boolean,
val animationLength: Double,
val effects: List<BedrockEffectKeyframe>,
val boneTimelines: Map<String, BedrockBoneTimeline>,
//This is custom data added by our custom BB plugin
val cobblemonTextures: MutableMap<String, TreeMap<Double, CobblemonBedrockTextureKeyframe>>
val boneTimelines: Map<String, BedrockBoneTimeline>
) {
fun checkForErrors() {
boneTimelines.forEach { (_, timeline) ->
@@ -398,14 +396,4 @@ enum class InterpolationType {
enum class Transformation {
POSITION, ROTATION, SCALE
}
data class CobblemonBedrockTextureKeyframe(
val textureSlot: Int,
val cubicLerpFunctionValues: MutableList<Double>,
val interpolationType: TextureInterpolationType
)
enum class TextureInterpolationType {
STEP, LINEAR, BEZIER
}
@@ -20,7 +20,6 @@ import com.google.gson.JsonElement
import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive
import java.lang.reflect.Type
import java.util.TreeMap
/**
* Gson adapter for converting bedrock/blockbench json data into a friendlier object model.
@@ -90,27 +89,7 @@ object BedrockAnimationAdapter : JsonDeserializer<BedrockAnimation> {
)
}
//Custom Data reading added by our custom BB plugin
val cobblemonTextures = mutableMapOf<String, TreeMap<Double, CobblemonBedrockTextureKeyframe>>()
json["cobblemon_texture"]?.asJsonObject?.entrySet()?.forEach { (texture, timeline) ->
val textureTimeline = TreeMap<Double, CobblemonBedrockTextureKeyframe>()
timeline.asJsonObject.entrySet().forEach { (frame, values) ->
val lerpFunctionValues = mutableListOf<Double>()
values.asJsonObject["cobblemon_cubic_lerp_function"].asString.split(",").forEach {
lerpFunctionValues.add(it.toDouble())
}
val interpolation = when(values.asJsonObject["cobblemon_interpolation"].asString) {
"step" -> TextureInterpolationType.STEP
"linear" -> TextureInterpolationType.LINEAR
"bezier" -> TextureInterpolationType.BEZIER
else -> TextureInterpolationType.STEP
}
textureTimeline[frame.toDouble()] = CobblemonBedrockTextureKeyframe(values.asJsonObject["cobblemon_texture_slot"].asInt, lerpFunctionValues, interpolation)
}
cobblemonTextures[texture] = textureTimeline
}
return BedrockAnimation(shouldLoop, animationLength, effects, boneTimelines, cobblemonTextures)
return BedrockAnimation(shouldLoop, animationLength, effects, boneTimelines)
}
else {
throw IllegalStateException("animation json could not be parsed")
@@ -1,73 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen1
import com.cobblemon.mod.common.client.render.models.blockbench.animation.QuadrupedWalkAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.QuadrupedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.CobblemonPose
import com.cobblemon.mod.common.entity.PoseType.Companion.MOVING_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.STATIONARY_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class ArcanineModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame, QuadrupedFrame {
override val rootPart = root.registerChildWithAllChildren("arcanine")
override val head = getPart("head")
override val foreLeftLeg = getPart("left_shoulder")
override val foreRightLeg = getPart("right_shoulder")
override val hindLeftLeg = getPart("left_thigh")
override val hindRightLeg = getPart("right_thigh")
override var portraitScale = 1.37F
override var portraitTranslation = Vec3(-0.58, 1.21, 0.0)
override var profileScale = 0.58F
override var profileTranslation = Vec3(0.14, 0.92, 0.0)
lateinit var standing: CobblemonPose
lateinit var walk: CobblemonPose
override val cryAnimation = CryProvider { bedrockStateful("arcanine", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("arcanine", "blink") }
standing = registerPose(
poseName = "standing",
poseTypes = STATIONARY_POSES + UI_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("arcanine", "idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = MOVING_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("arcanine", "idle"),
QuadrupedWalkAnimation(this)
)
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PosableState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("arcanine", "faint") else null
}
@@ -32,7 +32,7 @@ class SquirtleModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
override var portraitTranslation = Vec3(-0.26, -0.21, 0.0)
override var profileScale = 0.78F
override var profileTranslation = Vec3(-0.04, 0.57, 0.0)
override var profileTranslation = Vec3(0.0, 0.57, 0.0)
lateinit var sleep: CobblemonPose
lateinit var standing: CobblemonPose
@@ -1,93 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen1
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.CobblemonPose
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import com.cobblemon.mod.common.util.isInWater
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class TentacruelModel(root: ModelPart) : PokemonPosableModel(root) {
override val rootPart = root.registerChildWithAllChildren("tentacruel")
override var portraitScale = 1.3F
override var portraitTranslation = Vec3(-0.3, 1.8, 0.0)
override var profileScale = 0.55F
override var profileTranslation = Vec3(0.0, 1.1, 0.0)
lateinit var standing: CobblemonPose
lateinit var walk: CobblemonPose
lateinit var swim: CobblemonPose
lateinit var float: CobblemonPose
lateinit var sleep: CobblemonPose
lateinit var watersleep: CobblemonPose
override fun registerPoses() {
val blink = quirk { bedrockStateful("tentacruel", "blink")}
standing = registerPose(
poseName = "standing",
poseType = PoseType.STAND,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("tentacruel", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseType = PoseType.WALK,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("tentacruel", "ground_idle")
//bedrock("tentacruel", "ground_walk")
)
)
float = registerPose(
poseName = "float",
poseTypes = UI_POSES + PoseType.FLOAT,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("tentacruel", "water_idle")
)
)
swim = registerPose(
poseName = "swim",
poseType = PoseType.SWIM,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("tentacruel", "water_swim")
)
)
sleep = registerPose(
poseName = "sleep",
poseType = PoseType.SLEEP,
condition = { !it.isInWater },
animations = arrayOf(bedrock("tentacruel", "sleep"))
)
watersleep = registerPose(
poseName = "watersleep",
poseType = PoseType.SLEEP,
condition = { it.isInWater },
animations = arrayOf(bedrock("tentacruel", "water_sleep"))
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PosableState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("tentacruel", "faint") else null
}
@@ -1,61 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen2
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class ForretressModel(root: ModelPart) : PokemonPosableModel(root) {
override val rootPart = root.registerChildWithAllChildren("forretress")
override var portraitScale = 1.65F
override var portraitTranslation = Vec3(-0.3, 0.3, 0.0)
override var profileScale = 0.7F
override var profileTranslation = Vec3(0.0, 0.8, 0.0)
lateinit var standing: Pose
lateinit var walk: Pose
lateinit var sleep: Pose
override fun registerPoses() {
val blink = quirk { bedrockStateful("forretress", "blink")}
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("forretress", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("forretress", "ground_walk")
)
)
sleep = registerPose(
poseType = PoseType.SLEEP,
transformTicks = 10,
animations = arrayOf(bedrock("forretress", "sleep"))
)
}
override fun getFaintAnimation(state: PosableState) = if (state.isNotPosedIn(sleep)) bedrockStateful("forretress", "faint") else null
}
@@ -1,109 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen2
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BimanualSwingAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedWalkAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.animation.WingFlapIdleAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.createTransformation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BiWingedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BimanualFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.CobblemonPose
import com.cobblemon.mod.common.client.render.models.blockbench.pose.ModelPartTransformation
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.sineFunction
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.util.math.geometry.toRadians
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class HeracrossModel (root: ModelPart) : PokemonPosableModel(root), BipedFrame, BimanualFrame, BiWingedFrame {
override val rootPart = root.registerChildWithAllChildren("heracross")
override val leftArm = getPart("arm_right")
override val rightArm = getPart("arm_left")
override val leftLeg = getPart("leg_right")
override val rightLeg = getPart("leg_left")
override val leftWing = getPart("wing_right")
override val rightWing = getPart("wing_left")
override var portraitScale = 1.6F
override var portraitTranslation = Vec3(-0.2, 0.37, 0.0)
override var profileScale = 0.7F
override var profileTranslation = Vec3(-0.02, 0.64, 0.0)
lateinit var standing: CobblemonPose
lateinit var walk: CobblemonPose
lateinit var hovering: CobblemonPose
lateinit var flying: CobblemonPose
override fun registerPoses() {
val blink = quirk { bedrockStateful("heracross", "blink") }
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES - PoseType.HOVER,
quirks = arrayOf(blink),
transformedParts = arrayOf(
leftWing.createTransformation().withVisibility(visibility = false),
rightWing.createTransformation().withVisibility(visibility = false)
),
animations = arrayOf(
bedrock("heracross", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES - PoseType.FLY,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("heracross", "ground_idle"),
BipedWalkAnimation(this,0.6F, 1F),
BimanualSwingAnimation(this, 0.6F, 1F)
)
)
hovering = registerPose(
poseName = "hovering",
poseType = PoseType.HOVER,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("heracross", "air_idle"),
WingFlapIdleAnimation(this,
flapFunction = sineFunction(verticalShift = 35F.toRadians(), period = 0.1F, amplitude = 0.6F),
timeVariable = { state, _, _ -> state.animationSeconds },
axis = ModelPartTransformation.Y_AXIS
)
)
)
flying = registerPose(
poseName = "flying",
poseType = PoseType.FLY,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("heracross", "air_idle"),
WingFlapIdleAnimation(this,
flapFunction = sineFunction(verticalShift = 35F.toRadians(), period = 0.1F, amplitude = 0.6F),
timeVariable = { state, _, _ -> state.animationSeconds },
axis = ModelPartTransformation.Y_AXIS
)
),
transformedParts = arrayOf(
rootPart.createTransformation().addRotationDegrees(ModelPartTransformation.X_AXIS, 45)
)
)
}
}
@@ -1,125 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen2
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.CobblemonPose
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.util.isBattling
import com.cobblemon.mod.common.util.isUnderWater
import com.cobblemon.mod.common.util.isInWater
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class LanturnModel (root: ModelPart) : PokemonPosableModel(root) {
override val rootPart = root.registerChildWithAllChildren("lanturn")
override var portraitScale = 1.7F
override var portraitTranslation = Vec3(-0.65, -0.7, 0.0)
override var profileScale = 0.6F
override var profileTranslation = Vec3(0.0, 0.7, 0.0)
lateinit var standing: CobblemonPose
lateinit var walk: CobblemonPose
lateinit var floating: CobblemonPose
lateinit var swimming: CobblemonPose
lateinit var sleep: CobblemonPose
lateinit var watersleep: CobblemonPose
lateinit var battleidle: CobblemonPose
lateinit var waterbattleidle: CobblemonPose
// override val cryAnimation = CryProvider { bedrockStateful("lanturn", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("lanturn", "blink")}
sleep = registerPose(
poseName = "sleeping",
poseType = PoseType.SLEEP,
condition = { !it.isInWater },
animations = arrayOf(bedrock("lanturn", "sleep"))
)
watersleep = registerPose(
poseName = "water_sleeping",
poseType = PoseType.SLEEP,
condition = { it.isInWater },
animations = arrayOf(bedrock("lanturn", "water_sleep"))
)
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.UI_POSES + PoseType.STAND,
transformTicks = 10,
condition = { !it.isBattling && !it.isInWater && !it.isUnderWater},
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("lanturn", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
transformTicks = 10,
poseType = PoseType.WALK,
condition = { !it.isInWater && !it.isUnderWater},
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("lanturn", "ground_walk")
)
)
floating = registerPose(
poseName = "floating",
transformTicks = 10,
poseType = PoseType.FLOAT,
condition = { it.isInWater },
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("lanturn", "water_idle")
)
)
swimming = registerPose(
poseName = "swimming",
transformTicks = 10,
condition = { it.isInWater },
poseTypes = PoseType.MOVING_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("lanturn", "water_swim"),
)
)
battleidle = registerPose(
poseName = "battle_idle",
poseTypes = PoseType.STATIONARY_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
condition = { it.isBattling && !it.isInWater },
animations = arrayOf(
bedrock("lanturn", "ground_battle_idle")
)
)
waterbattleidle = registerPose(
poseName = "water_battle_idle",
poseTypes = PoseType.STATIONARY_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
condition = { it.isBattling && it.isInWater },
animations = arrayOf(
bedrock("lanturn", "water_battle_idle")
)
)
}
override fun getFaintAnimation(state: PosableState) = if (state.isPosedIn(standing, walk, battleidle, sleep)) bedrockStateful("lanturn", "faint") else if (state.isPosedIn( waterbattleidle, watersleep, floating, swimming )) bedrockStateful("lanturn", "water_faint") else null
}
@@ -1,87 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen2
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.PoseType.Companion.MOVING_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.STATIONARY_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import com.cobblemon.mod.common.util.isBattling
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class SteelixModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
override val rootPart = root.registerChildWithAllChildren("steelix")
override val head = getPart("head")
override var portraitScale = 0.7F
override var portraitTranslation = Vec3(-0.7, 1.8, 0.0)
override var profileScale = 0.4F
override var profileTranslation = Vec3(-0.1, 1.1, -6.0)
lateinit var standing: Pose
lateinit var walking: Pose
lateinit var sleep: Pose
lateinit var battleidle: Pose
override val cryAnimation = CryProvider { bedrockStateful("steelix", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("steelix", "blink") }
val jitter = quirk { bedrockStateful("steelix", "quirk_jawjitter") }
sleep = registerPose(
poseType = PoseType.SLEEP,
animations = arrayOf(bedrock("steelix", "sleep"))
)
standing = registerPose(
poseName = "standing",
poseTypes = STATIONARY_POSES + UI_POSES,
transformTicks = 0,
condition = { !it.isBattling },
quirks = arrayOf(blink, jitter),
animations = arrayOf(
singleBoneLook(),
bedrock("steelix", "ground_idle")
)
)
walking = registerPose(
poseName = "walking",
poseTypes = MOVING_POSES,
transformTicks = 0,
quirks = arrayOf(blink, jitter),
animations = arrayOf(
singleBoneLook(),
bedrock("steelix", "ground_walk")
)
)
battleidle = registerPose(
poseName = "battle_idle",
poseTypes = PoseType.STATIONARY_POSES,
transformTicks = 0,
quirks = arrayOf(blink, jitter),
condition = { it.isBattling },
animations = arrayOf(
singleBoneLook(),
bedrock("steelix", "battle_idle")
)
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PosableState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walking, battleidle, sleep)) bedrockStateful("steelix", "faint") else null
}
@@ -1,67 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen3
import com.cobblemon.mod.common.client.render.models.blockbench.animation.QuadrupedWalkAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.QuadrupedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class CameruptModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame, QuadrupedFrame {
override val rootPart = root.registerChildWithAllChildren("camerupt")
override val head = getPart("neck")
override val foreLeftLeg = getPart("leg_front_left")
override val foreRightLeg = getPart("leg_front_right")
override val hindLeftLeg = getPart("leg_back_left")
override val hindRightLeg = getPart("leg_back_right")
override var portraitScale = 1.2F
override var portraitTranslation = Vec3(-0.85, 0.1, 0.0)
override var profileScale = 0.55F
override var profileTranslation = Vec3(0.0, 0.85, 0.0)
lateinit var standing: Pose
lateinit var walk: Pose
override fun registerPoses() {
val blink = quirk { bedrockStateful("camerupt", "blink") }
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.UI_POSES + PoseType.STATIONARY_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("camerupt", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
QuadrupedWalkAnimation(this, periodMultiplier = 1.1F),
singleBoneLook(),
bedrock("camerupt", "ground_idle")
)
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PosableState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("camerupt", "faint") else null
}
@@ -1,58 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen3
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType.Companion.MOVING_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.STATIONARY_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class RayquazaModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
override val rootPart = root.registerChildWithAllChildren("rayquaza")
override val head = getPart("head")
override var portraitScale = 1.5F
override var portraitTranslation = Vec3(-1.85, 3.5, 0.0)
override var profileScale = 0.4F
override var profileTranslation = Vec3(0.0, 1.3, -6.0)
lateinit var standing: Pose
lateinit var walk: Pose
override fun registerPoses() {
standing = registerPose(
poseName = "standing",
poseTypes = STATIONARY_POSES + UI_POSES,
animations = arrayOf(
singleBoneLook(),
bedrock("rayquaza", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = MOVING_POSES,
animations = arrayOf(
singleBoneLook(),
bedrock("rayquaza", "ground_idle")
//bedrock("rayquaza", "ground_walk")
)
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PosableState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("rayquaza", "faint") else null
}
@@ -1,87 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen5
import com.cobblemon.mod.common.client.render.models.blockbench.animation.WaveAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.animation.WaveSegment
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.CobblemonPose
import com.cobblemon.mod.common.client.render.models.blockbench.pose.ModelPartTransformation
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.sineFunction
import com.cobblemon.mod.common.entity.PoseType.Companion.MOVING_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.STATIONARY_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class BasculinModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
override val rootPart = root.registerChildWithAllChildren("basculin")
override val head = getPart("head")
override var portraitScale = 2.0F
override var portraitTranslation = Vec3(-0.3, -1.2, 0.0)
override var profileScale = 0.8F
override var profileTranslation = Vec3(0.0, 0.5, 0.0)
lateinit var standing: CobblemonPose
lateinit var walk: CobblemonPose
val tail = getPart("tail_fin")
val wtail = WaveSegment(tail, 7F)
override val cryAnimation = CryProvider { bedrockStateful("basculin", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("basculin", "blink") }
standing = registerPose(
poseName = "standing",
poseTypes = STATIONARY_POSES + UI_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("basculin", "water_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = MOVING_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("basculin", "water_idle"),
WaveAnimation(
waveFunction = sineFunction(
period = 8F,
amplitude = 0.4F
),
basedOnLimbSwing = true,
oscillationsScalar = 8F,
head = head,
rotationAxis = ModelPartTransformation.Y_AXIS,
motionAxis = ModelPartTransformation.X_AXIS,
headLength = 0.1F,
segments = arrayOf(
wtail
)
)
)
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PosableState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("basculin", "faint") else null
}
@@ -1,81 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen5
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.CobblemonPose
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.util.isBattling
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class DarmanitanModel (root: ModelPart) : PokemonPosableModel(root) {
override val rootPart = root.registerChildWithAllChildren("darmanitan")
override var portraitScale = 0.96F
override var portraitTranslation = Vec3(-0.35, 0.71, 0.0)
override var profileScale = 0.57F
override var profileTranslation = Vec3(-0.11, 0.73, 0.0)
lateinit var standing: CobblemonPose
lateinit var walk: CobblemonPose
lateinit var sleep: CobblemonPose
lateinit var battleidle: CobblemonPose
override val cryAnimation = CryProvider { bedrockStateful("darmanitan", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("darmanitan", "blink") }
val quirk = quirk(secondsBetweenOccurrences = 15F to 100F) { bedrockStateful("darmanitan", "quirk") }
sleep = registerPose(
poseName = "sleeping",
poseType = PoseType.SLEEP,
animations = arrayOf(bedrock("darmanitan", "sleep"))
)
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
quirks = arrayOf(blink, quirk),
condition = { !it.isBattling },
animations = arrayOf(
bedrock("darmanitan", "ground_idle")
)
)
battleidle = registerPose(
poseName = "battle_idle",
poseTypes = PoseType.STATIONARY_POSES,
transformTicks = 10,
quirks = arrayOf(blink, quirk),
condition = { it.isBattling },
animations = arrayOf(
bedrock("darmanitan", "battle_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("darmanitan", "ground_walk")
)
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PosableState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("darmanitan", "faint") else null
}
@@ -1,71 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen5
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.CobblemonPose
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.util.isBattling
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class DarmanitanZenModel (root: ModelPart) : PokemonPosableModel(root) {
override val rootPart = root.registerChildWithAllChildren("darmanitan")
override var portraitScale = 1.05F
override var portraitTranslation = Vec3(-0.28, 0.48, 0.0)
override var profileScale = 0.63F
override var profileTranslation = Vec3(0.0, 0.69, 0.0)
lateinit var standing: CobblemonPose
lateinit var walk: CobblemonPose
lateinit var sleep: CobblemonPose
lateinit var battleIdle: CobblemonPose
override fun registerPoses() {
sleep = registerPose(
poseName = "sleeping",
poseType = PoseType.SLEEP,
animations = arrayOf(bedrock("darmanitan_zen", "sleep"))
)
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
condition = { !it.isBattling },
animations = arrayOf(
bedrock("darmanitan_zen", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
animations = arrayOf(
bedrock("darmanitan_zen", "ground_walk")
)
)
battleIdle = registerPose(
poseName = "battle_idle",
poseTypes = PoseType.STATIONARY_POSES,
condition = { it.isBattling },
animations = arrayOf(
bedrock("darmanitan_zen", "battle_idle")
)
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PosableState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("darmanitan_zen", "faint") else null
}
@@ -26,11 +26,11 @@ class ChespinModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame, B
override val leftLeg = getPart("leg_left")
override val rightLeg = getPart("leg_right")
override var portraitScale = 2.7F
override var portraitTranslation = Vec3(-0.15, -1.2, 0.0)
override var portraitScale = 2.03F
override var portraitTranslation = Vec3(-0.12, -0.44, 0.0)
override var profileScale = 0.85F
override var profileTranslation = Vec3(0.0, 0.5, 0.0)
override var profileScale = 0.77F
override var profileTranslation = Vec3(0.0, 0.6, 0.0)
lateinit var standing: Pose
lateinit var walk: Pose

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