forked from mirrors/cobblemon
Compare commits
530
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78dfb30759 | ||
|
|
db10ae2183 | ||
|
|
548b8522a8 | ||
|
|
4791b146dc | ||
|
|
b7a9931393 | ||
|
|
9a35a11440 | ||
|
|
6d4a50cee2 | ||
|
|
818a637897 | ||
|
|
4c96ab9b28 | ||
|
|
d5a316b9ae | ||
|
|
87e32f0e9b | ||
|
|
53372e5400 | ||
|
|
2fbb1f354c | ||
|
|
bf06476558 | ||
|
|
2e3a742477 | ||
|
|
94c7542d2c | ||
|
|
fa2b6b5996 | ||
|
|
8e77dc70f6 | ||
|
|
51e3748adb | ||
|
|
d8cf2701c7 | ||
|
|
31b7c83369 | ||
|
|
a170123e2c | ||
|
|
c3c17a228e | ||
|
|
41d607ad56 | ||
|
|
58b3596948 | ||
|
|
ed4456d756 | ||
|
|
481734e6b1 | ||
|
|
636792fa1f | ||
|
|
ad65002b19 | ||
|
|
903f3b6d5a | ||
|
|
4456743e97 | ||
|
|
d0989e5ece | ||
|
|
ce3f703de2 | ||
|
|
180229db61 | ||
|
|
b6e10c016e | ||
|
|
8ed68742df | ||
|
|
ce09236dab | ||
|
|
0bb968dd73 | ||
|
|
f3d4cb34e0 | ||
|
|
16aa930735 | ||
|
|
55733e8ad5 | ||
|
|
67591efdad | ||
|
|
5d300c8157 | ||
|
|
3b6e1bd429 | ||
|
|
c77198a845 | ||
|
|
bd32c07cc1 | ||
|
|
1eb7354fc0 | ||
|
|
52ad6a3250 | ||
|
|
aa57bbbf34 | ||
|
|
26278eeb46 | ||
|
|
99cfc4f147 | ||
|
|
89281a1f6d | ||
|
|
ad044fb05c | ||
|
|
7088ce3828 | ||
|
|
fc6a712906 | ||
|
|
1d0397c399 | ||
|
|
8ad443a4c5 | ||
|
|
0d1f9a0093 | ||
|
|
7587344cb4 | ||
|
|
f0926d7524 | ||
|
|
7c3b17d841 | ||
|
|
263e3cd79f | ||
|
|
2ba8062915 | ||
|
|
edc298bf64 | ||
|
|
aa1158fcfe | ||
|
|
ab40ae81c4 | ||
|
|
b5df1878ed | ||
|
|
800965cb40 | ||
|
|
b00f1d9352 | ||
|
|
a588cfadf8 | ||
|
|
2eb4062f67 | ||
|
|
43321d8872 | ||
|
|
a5ea584b75 | ||
|
|
6a9edc2e67 | ||
|
|
12377deff7 | ||
|
|
583377d39b | ||
|
|
2c1da8df06 | ||
|
|
737ddd973b | ||
|
|
d749b04261 | ||
|
|
93deb3a669 | ||
|
|
dca45a95e9 | ||
|
|
0412f16460 | ||
|
|
efd4a72287 | ||
|
|
a62e5a6571 | ||
|
|
0ec337ee6c | ||
|
|
76df0a429d | ||
|
|
08ea9378de | ||
|
|
c2bd21ab96 | ||
|
|
5db8ccfad4 | ||
|
|
aa206d386a | ||
|
|
bba8a12a2a | ||
|
|
a235af6c01 | ||
|
|
a666a74a32 | ||
|
|
c285120ff4 | ||
|
|
dbd2890120 | ||
|
|
a5db46cdc5 | ||
|
|
765cf9b96a | ||
|
|
efe7952869 | ||
|
|
9e434de285 | ||
|
|
712fe2c250 | ||
|
|
5db5a2ed35 | ||
|
|
49c96e6fc8 | ||
|
|
d56c9a3f5a | ||
|
|
6c4c973ed8 | ||
|
|
a117303147 | ||
|
|
dbf248a508 | ||
|
|
56d75b14fa | ||
|
|
87311e2d7d | ||
|
|
1b525ca3a5 | ||
|
|
78ec9d3723 | ||
|
|
afa5d22c8e | ||
|
|
1cdf664dce | ||
|
|
bd63fb686d | ||
|
|
0f9a422d44 | ||
|
|
32d18b47f9 | ||
|
|
c618bf95fa | ||
|
|
18a8aa12d1 | ||
|
|
308ca6369d | ||
|
|
79fd9f4e04 | ||
|
|
38219bbca2 | ||
|
|
87de288777 | ||
|
|
b6f1d6c62e | ||
|
|
a7bd70e334 | ||
|
|
43310d3a3b | ||
|
|
d12bdc3745 | ||
|
|
08c8623951 | ||
|
|
6ae375cf4e | ||
|
|
29c65c18ff | ||
|
|
b90384fe08 | ||
|
|
d2ff7e3748 | ||
|
|
ea6452287b | ||
|
|
b1f7731563 | ||
|
|
5ad881235f | ||
|
|
05796998e8 | ||
|
|
aaf1aad9a4 | ||
|
|
b315fdcadc | ||
|
|
5b95f0a1c7 | ||
|
|
7641a839c2 | ||
|
|
6ed6ca6937 | ||
|
|
be106a465e | ||
|
|
e7c0374c83 | ||
|
|
7fd1a443ac | ||
|
|
31359077ed | ||
|
|
7f683bd618 | ||
|
|
012f15878e | ||
|
|
2ea5eb2823 | ||
|
|
f406b9695d | ||
|
|
f6dbe40246 | ||
|
|
a1f6e47f42 | ||
|
|
04f5dbfca0 | ||
|
|
fd940b5549 | ||
|
|
3e37c1af32 | ||
|
|
8b6b9ffede | ||
|
|
329a13867c | ||
|
|
e109b0fc9f | ||
|
|
5de7f96fef | ||
|
|
5c6632c3fb | ||
|
|
ebde125895 | ||
|
|
c45c74c508 | ||
|
|
cd6bc23825 | ||
|
|
3df95df1f9 | ||
|
|
ad3ee75c22 | ||
|
|
ce3935950a | ||
|
|
e1380df7d1 | ||
|
|
cf8cfb8dad | ||
|
|
3729728124 | ||
|
|
1f62f447b5 | ||
|
|
3092abfdd0 | ||
|
|
3150f33e9e | ||
|
|
40d7e7bccb | ||
|
|
cdbe61843f | ||
|
|
b13c168347 | ||
|
|
f77fa82e4f | ||
|
|
2e2b10311d | ||
|
|
2af5de8ce4 | ||
|
|
674e1df96d | ||
|
|
a4a2ae6133 | ||
|
|
66eea576f3 | ||
|
|
53a516266f | ||
|
|
f732442133 | ||
|
|
34cad13f57 | ||
|
|
e879a755a4 | ||
|
|
2ac9c827d4 | ||
|
|
a0ef46a18c | ||
|
|
9dc15c9096 | ||
|
|
346e2cd094 | ||
|
|
f7794d50dd | ||
|
|
3b8d67ce64 | ||
|
|
105d3a7d90 | ||
|
|
8a685cefc0 | ||
|
|
3ff536dc02 | ||
|
|
638ec03244 | ||
|
|
37438c9fdd | ||
|
|
cbc4b52193 | ||
|
|
9a760d8eae | ||
|
|
b7d26d99cd | ||
|
|
04c8b1e95b | ||
|
|
e4149b2a5e | ||
|
|
b446af166d | ||
|
|
3cdaa513f8 | ||
|
|
75e0da38c4 | ||
|
|
ca43fcf3a2 | ||
|
|
4f71e11383 | ||
|
|
5b56a5758a | ||
|
|
424889e820 | ||
|
|
6d3f00cd58 | ||
|
|
1d375bdffa | ||
|
|
e563bbee49 | ||
|
|
e2ccb20402 | ||
|
|
8fe2dc8304 | ||
|
|
4ddc167254 | ||
|
|
e3502ec320 | ||
|
|
d8d0ade350 | ||
|
|
e2ac4449b3 | ||
|
|
ecec7ccee3 | ||
|
|
228282f083 | ||
|
|
3bc7d39800 | ||
|
|
3dc5873642 | ||
|
|
a5c3d2f800 | ||
|
|
00d2bbbe42 | ||
|
|
8d6291c4c2 | ||
|
|
2b77d030e8 | ||
|
|
029927af6a | ||
|
|
bf791dc4d1 | ||
|
|
56ad481304 | ||
|
|
cc7a9ae44a | ||
|
|
664f79f3a7 | ||
|
|
9a2ed78b0d | ||
|
|
558907eb5c | ||
|
|
370b689ab4 | ||
|
|
e2740babc5 | ||
|
|
3393ab17d8 | ||
|
|
645029a9c7 | ||
|
|
f168744ccd | ||
|
|
67210cb5f9 | ||
|
|
779fbb4f38 | ||
|
|
ce04f546e7 | ||
|
|
a36437ea16 | ||
|
|
aa5af84283 | ||
|
|
2ee556dc31 | ||
|
|
4457df9aba | ||
|
|
e1bd0d688f | ||
|
|
0bce2d8645 | ||
|
|
dfe240f1d3 | ||
|
|
f1ac0fe99f | ||
|
|
c2f575bf22 | ||
|
|
beddea1ecb | ||
|
|
04236fcf33 | ||
|
|
05e87b7faf | ||
|
|
e9d298834a | ||
|
|
ce113cbb80 | ||
|
|
61ebe7124a | ||
|
|
462c0b35b8 | ||
|
|
0b55c88ec4 | ||
|
|
942da6b2f6 | ||
|
|
056917f7df | ||
|
|
2aa9379987 | ||
|
|
3a6a3253a3 | ||
|
|
1580a06a8e | ||
|
|
e2bfdd3e76 | ||
|
|
1d24565aa6 | ||
|
|
2d80402688 | ||
|
|
40b351a303 | ||
|
|
dc32bd1845 | ||
|
|
a096485155 | ||
|
|
f1ae7c4476 | ||
|
|
bcfac2f68a | ||
|
|
ff1902770c | ||
|
|
ebb01d4fcc | ||
|
|
20614b6b07 | ||
|
|
b32b5a66a4 | ||
|
|
ad92804fce | ||
|
|
ca93955134 | ||
|
|
2c8fd29cf1 | ||
|
|
a9eec5f568 | ||
|
|
eb783e13ba | ||
|
|
ec9f7665e2 | ||
|
|
f19ae0120e | ||
|
|
d674ff1d40 | ||
|
|
37d6f58536 | ||
|
|
2ce23e072d | ||
|
|
d37dfee032 | ||
|
|
831903b8e3 | ||
|
|
d382f48e2d | ||
|
|
ac9d85929e | ||
|
|
88c981bd6a | ||
|
|
f0baa21b1f | ||
|
|
2a62b49eab | ||
|
|
4ee628ea7a | ||
|
|
64c0001aff | ||
|
|
320b2ff528 | ||
|
|
cf8a1f5447 | ||
|
|
696d98f7cd | ||
|
|
909b90aea1 | ||
|
|
04296ae04e | ||
|
|
025dea4201 | ||
|
|
0b392c66eb | ||
|
|
5733074bd8 | ||
|
|
a0630ff738 | ||
|
|
88ff4e37b8 | ||
|
|
2a7503db06 | ||
|
|
1480bd97c2 | ||
|
|
35b7a80501 | ||
|
|
91949414cc | ||
|
|
43dbfc772e | ||
|
|
e14e44a1c5 | ||
|
|
46cc276e2e | ||
|
|
10bfe3b29d | ||
|
|
ff88a3c756 | ||
|
|
4d47cb8385 | ||
|
|
8aba2bc279 | ||
|
|
fe91e912d0 | ||
|
|
680bc1d205 | ||
|
|
eee3296120 | ||
|
|
d0c4bd5a16 | ||
|
|
500069e5d8 | ||
|
|
13ae174c66 | ||
|
|
3bf777c2a9 | ||
|
|
b5174686b4 | ||
|
|
26a7bc29b3 | ||
|
|
05cd37eb38 | ||
|
|
3ac3fcbcda | ||
|
|
27b3884f27 | ||
|
|
15e8f46702 | ||
|
|
e91b1061d3 | ||
|
|
f042d167d1 | ||
|
|
eff3bd91d7 | ||
|
|
1391bfee01 | ||
|
|
e8b6bee139 | ||
|
|
a98bc80005 | ||
|
|
06d25a043c | ||
|
|
6aa3b8b92f | ||
|
|
7381b47830 | ||
|
|
b2a704fa12 | ||
|
|
41ed476c00 | ||
|
|
44ddf097d6 | ||
|
|
cb1386fdb3 | ||
|
|
0dafe79032 | ||
|
|
51cd929d1c | ||
|
|
5468d5e888 | ||
|
|
0174b8bc67 | ||
|
|
e3b8fbb973 | ||
|
|
7103df0116 | ||
|
|
42fba85241 | ||
|
|
f9f11c7137 | ||
|
|
a51acead21 | ||
|
|
7a3ab17e57 | ||
|
|
d4e741c614 | ||
|
|
bee1797ff5 | ||
|
|
72fa7d42d5 | ||
|
|
7a4bf4cc5d | ||
|
|
ab66e13c8c | ||
|
|
eaeea493a1 | ||
|
|
0bfaedbf6e | ||
|
|
52f9f45608 | ||
|
|
dfa9dc1e2b | ||
|
|
c8f5538ae8 | ||
|
|
5af5a59ff1 | ||
|
|
eb37ecbe56 | ||
|
|
d7b528aef1 | ||
|
|
2908ca3a89 | ||
|
|
aa04dc077b | ||
|
|
c1c064e657 | ||
|
|
c0a509820f | ||
|
|
aa0b71aad7 | ||
|
|
55c5dfddc4 | ||
|
|
b40c69a1d9 | ||
|
|
ca20f432b3 | ||
|
|
63fe61b913 | ||
|
|
7605b526c8 | ||
|
|
189f39f1c1 | ||
|
|
8c718069c7 | ||
|
|
c2facdf5ce | ||
|
|
0c5dcb26f6 | ||
|
|
b88634691b | ||
|
|
df9dcdd622 | ||
|
|
15943fae47 | ||
|
|
78a71ba252 | ||
|
|
7f1acd16b5 | ||
|
|
be038d2348 | ||
|
|
b3b11012f4 | ||
|
|
095c466b89 | ||
|
|
4c3cd6fdc4 | ||
|
|
2379b22135 | ||
|
|
f36fa861da | ||
|
|
3b4f9d0fba | ||
|
|
42dabbf16a | ||
|
|
07abff7583 | ||
|
|
dbd9bfacc5 | ||
|
|
6ed1ea0cad | ||
|
|
5384fec57e | ||
|
|
1868bcf4d7 | ||
|
|
42059b9931 | ||
|
|
24890225a2 | ||
|
|
e4e32769dc | ||
|
|
3420422a4b | ||
|
|
18564baec7 | ||
|
|
b0c6d83913 | ||
|
|
5016b2b227 | ||
|
|
4727321d47 | ||
|
|
6e96e92d33 | ||
|
|
a8c92ce641 | ||
|
|
032d2e5c95 | ||
|
|
ec9d7d9dae | ||
|
|
217dab4dea | ||
|
|
452a92daa9 | ||
|
|
fb364deff9 | ||
|
|
ac824ce39c | ||
|
|
fa74a1c45a | ||
|
|
1cce1d1a44 | ||
|
|
b7f537e34c | ||
|
|
0311a8a4ef | ||
|
|
14ecc13cb3 | ||
|
|
5baca56d6d | ||
|
|
91c9692db1 | ||
|
|
bb7b1a8dee | ||
|
|
e6322e5fb7 | ||
|
|
3c5f04b52a | ||
|
|
5d78a5b573 | ||
|
|
4f58615065 | ||
|
|
32a4161a69 | ||
|
|
c5708145cd | ||
|
|
118f207adb | ||
|
|
a5826028b3 | ||
|
|
fa3f6053c3 | ||
|
|
bc60fe58b1 | ||
|
|
f9a5e9a9e4 | ||
|
|
6f6ee6f160 | ||
|
|
8a3dbd5e4a | ||
|
|
e052ad2281 | ||
|
|
8938ea13a4 | ||
|
|
907aed4c63 | ||
|
|
60798aae03 | ||
|
|
6f09957fbf | ||
|
|
6fc2344473 | ||
|
|
c82059ae72 | ||
|
|
a080db690f | ||
|
|
d82849ddd3 | ||
|
|
71a3fe6e48 | ||
|
|
5e934ac7a6 | ||
|
|
6aa5bde424 | ||
|
|
fee7488090 | ||
|
|
f5e5e3411e | ||
|
|
818f9365ec | ||
|
|
f81db9df3d | ||
|
|
8162e3a30d | ||
|
|
5a00bbf02d | ||
|
|
ff7fff52ec | ||
|
|
087fab4065 | ||
|
|
bed2897950 | ||
|
|
d51f0711b7 | ||
|
|
728860332d | ||
|
|
fe0ca14b3b | ||
|
|
7e57c3c862 | ||
|
|
0f4fdfcedd | ||
|
|
1cb77f35b5 | ||
|
|
9ac23be59b | ||
|
|
e469409a22 | ||
|
|
28db8e98d7 | ||
|
|
08bf3d4f8f | ||
|
|
88380bfb76 | ||
|
|
83d7cbbefa | ||
|
|
426d7c8e5e | ||
|
|
602d88db5e | ||
|
|
751d3be872 | ||
|
|
358a62d5b1 | ||
|
|
409814823b | ||
|
|
5d8f45b058 | ||
|
|
3001e8bf84 | ||
|
|
80de268987 | ||
|
|
b08004b6c6 | ||
|
|
2783a52495 | ||
|
|
3ae3f0db95 | ||
|
|
a15f026739 | ||
|
|
bc8ed8bb38 | ||
|
|
efa246272b | ||
|
|
e29d84a71a | ||
|
|
6c671b5e08 | ||
|
|
3ffd9ecc28 | ||
|
|
281131f5b3 | ||
|
|
3c67cd2a59 | ||
|
|
74f5c69bb5 | ||
|
|
8f82e1d960 | ||
|
|
c5497cfb2b | ||
|
|
94b6b3b948 | ||
|
|
8855c5ad7f | ||
|
|
5015a0a56c | ||
|
|
ec51fc0701 | ||
|
|
13287e6f76 | ||
|
|
a65dfcd3cd | ||
|
|
18183dd649 | ||
|
|
40c1f074bb | ||
|
|
5d1219e467 | ||
|
|
568773f1a8 | ||
|
|
f0c3cde213 | ||
|
|
def5d09903 | ||
|
|
ad5e2ddc41 | ||
|
|
2d2f92c7a7 | ||
|
|
2bd4d1fb08 | ||
|
|
4c342c2047 | ||
|
|
24cee748d5 | ||
|
|
fbec6f03e7 | ||
|
|
8ab0288ab2 | ||
|
|
cc426a5ca9 | ||
|
|
e4f04b605a | ||
|
|
48441b1232 | ||
|
|
589d3614b8 | ||
|
|
fe386095de | ||
|
|
5640177c99 | ||
|
|
ff8c8b1bfa | ||
|
|
4bccd9b5aa | ||
|
|
8c1c5ac957 | ||
|
|
a20ec4d1c5 | ||
|
|
12b4e58dea | ||
|
|
940ec82182 | ||
|
|
67c01ce5f2 | ||
|
|
e603325eef | ||
|
|
afd09e93eb | ||
|
|
92775a4b8a | ||
|
|
6a2668171e | ||
|
|
5b7dc9f8f4 | ||
|
|
75dc76020f | ||
|
|
56289ce5bf | ||
|
|
e4ec7b0ae7 | ||
|
|
f38ef0b6b9 | ||
|
|
d5bb3e2d97 | ||
|
|
66f6555711 | ||
|
|
472ca9c748 | ||
|
|
054e4dffdc | ||
|
|
be5ee8a6a3 |
@@ -15,6 +15,9 @@
|
||||
- baseScale in NPCs has been removed from classes and presets, and in the entity it is replaced with hitboxScale and renderScale.
|
||||
- Added `EntityCallbacks` to `NPCEntity` and `PokemonEntity`. You can trigger custom callback types by using `[Entity].callbacks.process(...)`
|
||||
|
||||
### Fixes
|
||||
- Fixed Pokémon with a moving walk target frequently spinning in place as they re-calculate their path.
|
||||
|
||||
### MoLang & Datapacks
|
||||
- Added a `behaviours` datapack folder which motivates the Behaviour Editor screen on NPCs and Pokémon.
|
||||
- Added "callback" support on PokemonEntity and NPCEntity that allows MoLang scripts to be executed in response to certain scenarios.
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
### Changes
|
||||
- Slots in pastures will now have darker backgrounds and italicised Pokémon names if the Pokémon is not owned by the player.
|
||||
- The pasture slot will show the name of the Pokémon owner when hovered if the Pokémon is not owned.
|
||||
- Increased the default PC box count from 30 to 40 to accommodate the fact that we have way more Pokémon now. If you've changed this config option then you'll need to edit it again after starting up with 1.7!
|
||||
|
||||
### Developer
|
||||
- Added `RenamePCBoxEvent.Pre` and `RenamePCBoxEvent.Post` events to prevent players from renaming a box or changing their input.
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
### Fixes
|
||||
- Fixed the "enabled" property in spawn files not actually being respected. Where do they find these devs?
|
||||
- Fixed Terralith's shrubland not counting as plains for spawning purposes the way it was intended to.
|
||||
|
||||
### Developer
|
||||
- Spawning Influences now have the context of what the other buckets are when adjusting bucket weights. This will break existing influences that do bucket weight adjustment.
|
||||
|
||||
+186
@@ -2,6 +2,7 @@
|
||||
## [1.7.0 (Month xth, 2025)](#1-7-0)
|
||||
|
||||
### Additions
|
||||
- Added `/npcdelete` uuid parameter support and usuable to console
|
||||
- Added `/spectateBattle <player>` command to spectate battles without having to manually walk up to the target.
|
||||
- Added an in-game configuration screen, allowing all settings from `main.json` to be edited directly in-game.
|
||||
- Added `/cobblemonconfig reload` command to reload `main.json` configuration. **Note:** Some settings require a server restart to take effect; use this command cautiously.
|
||||
@@ -12,6 +13,7 @@
|
||||
- Added cosmetic item functionality for Pokémon. Certain cosmetic items can be given to applicable Pokémon via the interact menu.
|
||||
- Added the various log blocks as cosmetic items for Timburr and Komala.
|
||||
- Added cosmetics for Spoink, Gurdurr, Conkeldurr, Squirtle Line, Sneasler, Sandile line, Treecko line, Braixen, Delphox, and Dragonite.
|
||||
- Added Gilded Chest cosmetics for Chest Gimmighoul and Gholdengo
|
||||
- Added `visibility/hidden`, `visibility/hat` and `visibility/face` tags to control where and how certain items are rendered.
|
||||
- Added Pokémon markings, toggleable within the summary.
|
||||
- Added `/boxcount` command to change PC boxes amount
|
||||
@@ -142,6 +144,8 @@
|
||||
- Dedenne
|
||||
- Noibat
|
||||
- Noivern
|
||||
- Furfrou
|
||||
- You can change Furfrou's form by using shears on it while it holds a certain dye in its cosmetic slot.
|
||||
|
||||
#### Gen 7
|
||||
- Yungoos
|
||||
@@ -198,6 +202,8 @@
|
||||
- Tropius
|
||||
- Nosepass
|
||||
- Probopass
|
||||
- Sneasel
|
||||
- Weavile
|
||||
- Sneasler
|
||||
- Braixen
|
||||
- Delphox
|
||||
@@ -240,6 +246,7 @@
|
||||
- Volcarona
|
||||
- Dwebble
|
||||
- Crustle
|
||||
- Mr. Mime
|
||||
- Flygon
|
||||
- Pichu
|
||||
- Pikachu
|
||||
@@ -258,6 +265,34 @@
|
||||
- Klinklang
|
||||
- Baltoy
|
||||
- Claydol
|
||||
- Mamoswine
|
||||
- Rufflet
|
||||
- Braviary
|
||||
- Girafarig
|
||||
- Farigiraf
|
||||
- Rookidee
|
||||
- Corvisquire
|
||||
- Corviknight
|
||||
- Venipede
|
||||
- Whirlipede
|
||||
- Scolipede
|
||||
- Venusaur
|
||||
- Parasect
|
||||
- Bastiodon
|
||||
- Amaura
|
||||
- Varoom
|
||||
- Revavroom
|
||||
- Bouffalant
|
||||
- Lickilicky
|
||||
- Blaziken
|
||||
- Rhyhorn
|
||||
- Rhydon
|
||||
- Rhyperior
|
||||
- Tauros
|
||||
- Tauros - Paldea Aqua
|
||||
- Tauros - Paldea Blaze
|
||||
- Tauros - Paldea Combat
|
||||
- Dewgong
|
||||
|
||||
### Model updates for the following Pokémon
|
||||
- Cleffa
|
||||
@@ -336,13 +371,38 @@
|
||||
- Murkrow
|
||||
- Zorua
|
||||
- Zoroark
|
||||
- Mime.Jr
|
||||
- Mr. Mime
|
||||
- Pichu
|
||||
- Pikachu
|
||||
- Raichu
|
||||
- Alolan Raichu
|
||||
- Dusknoir
|
||||
- Deino
|
||||
- Zweilous
|
||||
- Hydreigon
|
||||
- Dreepy
|
||||
- Drakloak
|
||||
- Dragapult
|
||||
- Mamoswine
|
||||
- Rookidee
|
||||
- Corvisquire
|
||||
- Corviknight
|
||||
- Venipede
|
||||
- Whirlipede
|
||||
- Scolipede
|
||||
- Farigiraf
|
||||
- Staryu
|
||||
- Starmie
|
||||
- Gimmighoul
|
||||
- Gholdengo
|
||||
- Starly
|
||||
- Staravia
|
||||
- Staraptor
|
||||
- Varoom
|
||||
- Revavroom
|
||||
- Nickit
|
||||
- Thievul
|
||||
|
||||
### Changes
|
||||
- Changed pokemon caught and seen count to update based on the current pokedex being looked
|
||||
@@ -406,6 +466,7 @@
|
||||
- Updated `EvolutionCompleteEvent` parameters to include the Source Pokemon that evolved.
|
||||
- Updated `HatchEggEvent.Post` to include the Pokemon that hatched.
|
||||
- Roseli Berry Trees now naturally generate in their preferred biomes.
|
||||
- Refactored dynamic lights compat to be more future proof. The dynamic light support is only tested with LambDynamicLights, on NeoForged use that mod in combination with Sinytra Connector
|
||||
|
||||
### Fixes
|
||||
- Fixed game crashing when removing national pokedex using datapacks
|
||||
@@ -488,6 +549,8 @@
|
||||
- Fixed Pokémon with alternate forms being created with an incorrect 'forced' tag on their ability.
|
||||
- Fixed busted abilities and moves in Pokémon data due to removed datapacks etc. causing storage corruption. It now just rerolls their ability / uses Tackle.
|
||||
- Fixed singular Pokémon corruption causing entire storage corruption. Storages will now skip corrupted Pokémon and print an error to console.
|
||||
- Fixed species additions not being able to properly mark a species as implemented.
|
||||
- Fixed Pokémon item models not showing a glint when enchanted.
|
||||
|
||||
### Developer
|
||||
- A finished battle now has winners and losers set inside of `PokemonBattle` instead of them always being empty.
|
||||
@@ -618,6 +681,129 @@
|
||||
- Added `q.pokemon.is_busy` Returns whether the Pokémon is busy.
|
||||
- Added `q.pokemon.is_rideable` Returns whether the Pokémon is rideable.
|
||||
- Fixed `background` field and added `textColor` field for dialogues.
|
||||
- Added support to reload some data registries:
|
||||
- MoLang scripts
|
||||
- Callbacks
|
||||
- Spawn Detail Presets
|
||||
- Spawn Pools
|
||||
- Spawn Rules
|
||||
- Cosmetic Items
|
||||
- Dialogues
|
||||
- Fossils
|
||||
- Natural Materials
|
||||
- Action Effects
|
||||
- Mechanics
|
||||
- Unlockable Wallpapers
|
||||
- Starter Data
|
||||
|
||||
### Particles
|
||||
Added new/updated particles for the following moves:
|
||||
- Absorb
|
||||
- Acid
|
||||
- Acid Spray
|
||||
- Aerial Ace
|
||||
- Amnesia
|
||||
- Aurora Beam
|
||||
- Bite
|
||||
- Body Slam
|
||||
- Bubble
|
||||
- Bubble Beam
|
||||
- Bulldoze
|
||||
- Bullet Punch
|
||||
- Close Combat
|
||||
- Confusion
|
||||
- Crunch
|
||||
- Cotton Guard
|
||||
- Double Team
|
||||
- Dragon Claw
|
||||
- Egg Bomb
|
||||
- Ember
|
||||
- Eruption
|
||||
- Explosion
|
||||
- Fire Blast
|
||||
- Fire Punch
|
||||
- Fire Spin
|
||||
- Flame Burst
|
||||
- Flame Charge
|
||||
- Flamethrower
|
||||
- Flame Wheel
|
||||
- Frost Breath
|
||||
- Frustration
|
||||
- Giga Drain
|
||||
- Growl
|
||||
- Haze
|
||||
- Hidden Power
|
||||
- Horn Attack
|
||||
- Hyper Fang
|
||||
- Hypnosis
|
||||
- Ice Beam
|
||||
- Ice Punch
|
||||
- Ice Shard
|
||||
- Icy Wind
|
||||
- Infestation
|
||||
- Kinesis
|
||||
- Lava Plume
|
||||
- Leech Life
|
||||
- Leech Seed
|
||||
- Leer
|
||||
- Lick
|
||||
- Magical Leaf
|
||||
- Mega Drain
|
||||
- Minimize
|
||||
- Mist
|
||||
- Mud Bomb
|
||||
- MudSport
|
||||
- Mystical Fire
|
||||
- Nasty Plot
|
||||
- Poison Fang
|
||||
- Poison Gas
|
||||
- Poison Powder
|
||||
- Population Bomb
|
||||
- Pound
|
||||
- Powder
|
||||
- Powder Snow
|
||||
- Present
|
||||
- Protect
|
||||
- Psybeam
|
||||
- Psychic
|
||||
- Pursuit
|
||||
- Quick Attack
|
||||
- Rage Powder
|
||||
- Razor Leaf
|
||||
- Return
|
||||
- Rock Throw
|
||||
- Sand Attack
|
||||
- Scary Face
|
||||
- Scratch
|
||||
- Seed Bomb
|
||||
- Seismic Toss
|
||||
- Self Destruct
|
||||
- Shadow Ball
|
||||
- Shadow Claw
|
||||
- Sing
|
||||
- Sleep Powder
|
||||
- Sludge Bomb
|
||||
- Smokescreen
|
||||
- Spike
|
||||
- Spore
|
||||
- Stomp
|
||||
- String Shot
|
||||
- Stun Spore
|
||||
- Super Fang
|
||||
- Swords Dance
|
||||
- Tackle
|
||||
- Tail Whip
|
||||
- Thunder
|
||||
- Thunder Punch
|
||||
- Thunderbolt
|
||||
- Thunder Shock
|
||||
- Thunder Wave
|
||||
- Toxic
|
||||
- Water Gun
|
||||
- Water Pulse
|
||||
- Water Sport
|
||||
- Will-O-Wisp
|
||||
- Withdraw
|
||||
|
||||
## [1.6.1 (January 26th, 2025)](#1-6-1)
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ repositories {
|
||||
maven(url = "${rootProject.projectDir}/deps")
|
||||
maven(url = "https://api.modrinth.com/maven")
|
||||
maven(url = "https://maven.neoforged.net/releases")
|
||||
maven(url = "https://maven.gegy.dev")
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
@@ -44,6 +45,12 @@ dependencies {
|
||||
modCompileOnly(libs.bundles.common.integrations.compileOnly) {
|
||||
isTransitive = false
|
||||
}
|
||||
// LambDynamicLights is handled differently because we need the Mojang-mappings version of it.
|
||||
modCompileOnly(libs.lambDynamicLights) {
|
||||
capabilities {
|
||||
requireCapability("dev.lambdaurora.lambdynamiclights:api-mojmap")
|
||||
}
|
||||
}
|
||||
// Flywheel has no common dep so just pick one and don't use any platform specific code in common
|
||||
// modCompileOnly(libs.flywheelFabric)
|
||||
|
||||
@@ -94,4 +101,4 @@ sourceSets {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.compat;
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonEntities;
|
||||
import com.cobblemon.mod.common.client.render.layer.PokemonOnShoulderRenderer;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.cobblemon.mod.common.item.PokedexItem;
|
||||
import com.cobblemon.mod.common.pokemon.FormData;
|
||||
import com.cobblemon.mod.common.pokemon.lighthing.LightingData;
|
||||
import dev.lambdaurora.lambdynlights.LambDynLights;
|
||||
import dev.lambdaurora.lambdynlights.api.DynamicLightHandler;
|
||||
import dev.lambdaurora.lambdynlights.api.DynamicLightHandlers;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
|
||||
// Java class due to relying on entrypoint on Fabric side
|
||||
public class LambDynamicLightsCompat {
|
||||
|
||||
// This is all called on client side
|
||||
// These things are checked every tick so try to keep it as free as possible
|
||||
// That being said it's unlikely school Toshiba person is running dynamic lighting and shaders to enjoy this
|
||||
// Make sure not to call anything Fabric specific :)
|
||||
public static void hookCompat() {
|
||||
// Pokémon entities
|
||||
DynamicLightHandlers.registerDynamicLightHandler(
|
||||
CobblemonEntities.POKEMON,
|
||||
DynamicLightHandler.makeHandler(
|
||||
LambDynamicLightsCompat::resolvedPokemonLightLevel,
|
||||
pokemon -> false
|
||||
)
|
||||
);
|
||||
|
||||
// Shouldered Pokémon
|
||||
DynamicLightHandlers.registerDynamicLightHandler(
|
||||
EntityType.PLAYER,
|
||||
DynamicLightHandler.makeHandler(
|
||||
LambDynamicLightsCompat::resolvedPlayerLightLevel,
|
||||
player -> false
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private static int resolvedPokemonLightLevel(PokemonEntity pokemon) {
|
||||
var underwater = !pokemon.level().getFluidState(BlockPos.containing(pokemon.getX(), pokemon.getEyeY(), pokemon.getZ())).isEmpty();
|
||||
|
||||
ItemStack item = pokemon.getShownItem();
|
||||
int itemLightLevel = item.isEmpty()?0:LambDynLights.getLuminanceFromItemStack(item, underwater);
|
||||
int formLightLevel = extractFormLightLevel(pokemon.getForm(), underwater).orElse(0);
|
||||
|
||||
return Math.max(itemLightLevel, formLightLevel);
|
||||
}
|
||||
|
||||
private static int resolvedPlayerLightLevel(Player player) {
|
||||
var underwater = !player.level().getFluidState(BlockPos.containing(player.getX(), player.getEyeY(), player.getZ())).isEmpty();
|
||||
final int leftShoulderLightLevel = extractShoulderLightLevel(PokemonOnShoulderRenderer.shoulderDataFrom(player.getShoulderEntityLeft()), underwater);
|
||||
final int rightShoulderLightLevel = extractShoulderLightLevel(PokemonOnShoulderRenderer.shoulderDataFrom(player.getShoulderEntityRight()), underwater);
|
||||
final int itemLightLevel = customHeldItemLightLevel(player.getMainHandItem(), player.getOffhandItem());
|
||||
|
||||
return Math.max(itemLightLevel, Math.max(leftShoulderLightLevel, rightShoulderLightLevel));
|
||||
}
|
||||
|
||||
private static Optional<Integer> extractFormLightLevel(@NotNull FormData form, boolean underwater) {
|
||||
if (form.getLightingData() == null || !liquidGlowModeSupport(form.getLightingData().getLiquidGlowMode(), underwater)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(form.getLightingData().getLightLevel());
|
||||
}
|
||||
|
||||
private static int extractShoulderLightLevel(@Nullable PokemonOnShoulderRenderer.ShoulderData shoulderData, boolean underwater) {
|
||||
if (shoulderData == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ItemStack item = shoulderData.getShownItem();
|
||||
int itemLightLevel = item.isEmpty() ? 0 :LambDynLights.getLuminanceFromItemStack(item, underwater);
|
||||
int formLightLevel = extractFormLightLevel(shoulderData.getForm(), underwater).orElse(0);
|
||||
|
||||
return Math.max(itemLightLevel, formLightLevel);
|
||||
}
|
||||
|
||||
private static int customHeldItemLightLevel(ItemStack mainhand, ItemStack offhand) {
|
||||
return (mainhand.getItem() instanceof PokedexItem || offhand.getItem() instanceof PokedexItem) ? 13 : 0;
|
||||
}
|
||||
|
||||
private static boolean liquidGlowModeSupport(@NotNull LightingData.LiquidGlowMode liquidGlowMode, boolean underwater) {
|
||||
return underwater ? liquidGlowMode.getGlowsUnderwater() : liquidGlowMode.getGlowsInLand();
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.compat.lambdynamiclights;
|
||||
|
||||
import com.cobblemon.mod.common.pokemon.FormData;
|
||||
import com.cobblemon.mod.common.pokemon.lighthing.LightingData;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public abstract class CustomLuminance {
|
||||
protected static Optional<Integer> extractFormLightLevel(@NotNull FormData form, boolean underwater) {
|
||||
if (form.getLightingData() == null || !liquidGlowModeSupport(form.getLightingData().getLiquidGlowMode(), underwater)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(form.getLightingData().getLightLevel());
|
||||
}
|
||||
|
||||
protected static boolean liquidGlowModeSupport(@NotNull LightingData.LiquidGlowMode liquidGlowMode, boolean underwater) {
|
||||
return underwater ? liquidGlowMode.getGlowsUnderwater() : liquidGlowMode.getGlowsInLand();
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.compat.lambdynamiclights;
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon;
|
||||
import dev.lambdaurora.lambdynlights.api.DynamicLightsContext;
|
||||
import dev.lambdaurora.lambdynlights.api.DynamicLightsInitializer;
|
||||
import dev.lambdaurora.lambdynlights.api.entity.luminance.EntityLuminance;
|
||||
import dev.lambdaurora.lambdynlights.api.item.ItemLightSourceManager;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
|
||||
public class LambDynamicLightsInitializer implements DynamicLightsInitializer {
|
||||
public static final EntityLuminance.Type POKEMON_LUMINANCE = EntityLuminance.Type.registerSimple(
|
||||
ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, "pokemon"),
|
||||
PokemonLuminance.INSTANCE
|
||||
);
|
||||
public static final EntityLuminance.Type PLAYER_LUMINANCE = EntityLuminance.Type.registerSimple(
|
||||
ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, "shoulder"),
|
||||
PlayerLuminance.INSTANCE
|
||||
);
|
||||
|
||||
@Override
|
||||
public void onInitializeDynamicLights(DynamicLightsContext context) {
|
||||
Cobblemon.LOGGER.info("Lamb Dynamic Lights compatibility enabled");
|
||||
|
||||
context.entityLightSourceManager().onRegisterEvent().register(ctx -> {
|
||||
ctx.register(EntityType.PLAYER, PlayerLuminance.INSTANCE);
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings({"removal", "UnstableApiUsage"})
|
||||
@Override
|
||||
public void onInitializeDynamicLights(ItemLightSourceManager itemLightSourceManager) {}
|
||||
}
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.compat.lambdynamiclights;
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon;
|
||||
import com.cobblemon.mod.common.client.render.layer.PokemonOnShoulderRenderer;
|
||||
import dev.lambdaurora.lambdynlights.api.entity.luminance.EntityLuminance;
|
||||
import dev.lambdaurora.lambdynlights.api.item.ItemLightSourceManager;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Range;
|
||||
|
||||
public final class PlayerLuminance extends CustomLuminance implements EntityLuminance {
|
||||
public static final PlayerLuminance INSTANCE = new PlayerLuminance();
|
||||
private static final TagKey<Item> POKEDEX = TagKey.create(Registries.ITEM, ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, "pokedex"));
|
||||
|
||||
@Override
|
||||
public @NotNull Type type() {
|
||||
return LambDynamicLightsInitializer.PLAYER_LUMINANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Range(from = 0L, to = 15L) int getLuminance(@NotNull ItemLightSourceManager itemLightSourceManager, @NotNull Entity entity) {
|
||||
if (entity instanceof Player player) {
|
||||
final int shoulderLightLevel = shoulderLuminance(itemLightSourceManager, player);
|
||||
|
||||
final int itemLightLevel = player.isHolding(stack -> stack.is(POKEDEX)) ? 13 : 0;
|
||||
|
||||
return Math.max(shoulderLightLevel, itemLightLevel);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private @Range(from = 0L, to = 15L) static int shoulderLuminance(@NotNull ItemLightSourceManager itemLightSourceManager, @NotNull Player player) {
|
||||
final var leftShoulder = PokemonOnShoulderRenderer.shoulderDataFrom(player.getShoulderEntityLeft());
|
||||
final var rightShoulder = PokemonOnShoulderRenderer.shoulderDataFrom(player.getShoulderEntityRight());
|
||||
|
||||
if (leftShoulder == null && rightShoulder == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
final int leftShoulderLightLevel = extractShoulderLightLevel(leftShoulder, itemLightSourceManager, player.isUnderWater());
|
||||
final int rightShoulderLightLevel = extractShoulderLightLevel(rightShoulder, itemLightSourceManager, player.isUnderWater());
|
||||
return Math.max(leftShoulderLightLevel, rightShoulderLightLevel);
|
||||
}
|
||||
|
||||
private static int extractShoulderLightLevel(@Nullable PokemonOnShoulderRenderer.ShoulderData shoulderData, @NotNull ItemLightSourceManager itemLightSourceManager, boolean underwater) {
|
||||
if (shoulderData == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ItemStack item = shoulderData.getShownItem();
|
||||
int itemLightLevel = item.isEmpty() ? 0 : itemLightSourceManager.getLuminance(item, underwater);
|
||||
int formLightLevel = extractFormLightLevel(shoulderData.getForm(), underwater).orElse(0);
|
||||
|
||||
return Math.max(itemLightLevel, formLightLevel);
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.compat.lambdynamiclights;
|
||||
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import dev.lambdaurora.lambdynlights.api.entity.luminance.EntityLuminance;
|
||||
import dev.lambdaurora.lambdynlights.api.item.ItemLightSourceManager;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Range;
|
||||
|
||||
public final class PokemonLuminance extends CustomLuminance implements EntityLuminance {
|
||||
public static final PokemonLuminance INSTANCE = new PokemonLuminance();
|
||||
|
||||
private PokemonLuminance() {}
|
||||
|
||||
@Override
|
||||
public @NotNull Type type() {
|
||||
return LambDynamicLightsInitializer.POKEMON_LUMINANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Range(from = 0L, to = 15L) int getLuminance(
|
||||
@NotNull ItemLightSourceManager itemLightSourceManager,
|
||||
@NotNull Entity entity
|
||||
) {
|
||||
if (entity instanceof PokemonEntity pokemon) {
|
||||
boolean underwater = pokemon.isUnderWater();
|
||||
ItemStack item = pokemon.getShownItem();
|
||||
int itemLightLevel = item.isEmpty() ? 0 : itemLightSourceManager.getLuminance(item, underwater);
|
||||
int formLightLevel = extractFormLightLevel(pokemon.getForm(), underwater).orElse(0);
|
||||
|
||||
return Math.max(itemLightLevel, formLightLevel);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,9 +25,9 @@ public class TargetingConditionsMixin {
|
||||
@Inject(method = "test", at = @At("HEAD"), cancellable = true)
|
||||
public void test(@Nullable LivingEntity baseEntity, LivingEntity targetEntity, CallbackInfoReturnable<Boolean> ci) {
|
||||
if (targetEntity instanceof ServerPlayer player) {
|
||||
boolean canTargetInBattle = player.level().getGameRules().getBoolean(CobblemonGameRules.MOB_TARGET_IN_BATTLE);
|
||||
if (player.level().getGameRules().getBoolean(CobblemonGameRules.MOB_TARGET_IN_BATTLE)) return;
|
||||
boolean targetInBattle = Cobblemon.INSTANCE.getBattleRegistry().getBattleByParticipatingPlayer(player) != null;
|
||||
if (!canTargetInBattle && targetInBattle) ci.setReturnValue(false);
|
||||
if (targetInBattle) ci.setReturnValue(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-7
@@ -31,14 +31,15 @@ public class AbstractClientPlayerMixin {
|
||||
PokedexUsageContext usageContext = CobblemonClient.INSTANCE.getPokedexUsageContext();
|
||||
if (usageContext.getScanningGuiOpen()) {
|
||||
cir.setReturnValue(usageContext.getFovMultiplier());
|
||||
}
|
||||
} else {
|
||||
// Only modify fov through riding if the pokedex is not open
|
||||
LocalPlayer player = (LocalPlayer) (Object) this;
|
||||
if (player != null && player.isPassenger() && player.getVehicle() instanceof PokemonEntity) {
|
||||
PokemonEntity ride = (PokemonEntity) player.getVehicle();
|
||||
|
||||
LocalPlayer player = (LocalPlayer) (Object) this;
|
||||
if (player != null && player.isPassenger() && player.getVehicle() instanceof PokemonEntity) {
|
||||
PokemonEntity ride = (PokemonEntity) player.getVehicle();
|
||||
|
||||
//Return custom fov mult
|
||||
cir.setReturnValue(ride.rideFovMult());
|
||||
// Return custom fov mult for riding
|
||||
cir.setReturnValue(ride.rideFovMult());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.client.render.player.MountedPlayerRenderer;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import net.minecraft.client.model.AgeableListModel;
|
||||
import net.minecraft.client.model.HumanoidModel;
|
||||
import net.minecraft.client.model.Model;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
/**
|
||||
* This mixin adds to the head of the renderToBuffer function and allows us to detect if it is a HumanoidModel and
|
||||
* is being rendered as part of a player in the world who is riding a PokemonEntity. If so we need to obey any
|
||||
* root animations that have been applied earlier.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since May 16, 2025
|
||||
*/
|
||||
@Mixin(AgeableListModel.class)
|
||||
public class AgeableListModelMixin {
|
||||
@Inject(method = "renderToBuffer", at = @At("HEAD"))
|
||||
private void cobblemon$renderToBuffer(
|
||||
PoseStack poseStack,
|
||||
VertexConsumer vertexConsumer,
|
||||
int packedLight,
|
||||
int packedOverlay,
|
||||
int colour,
|
||||
CallbackInfo ci
|
||||
) {
|
||||
Model model = (Model) (Object) this;
|
||||
if (model instanceof HumanoidModel<?> && MountedPlayerRenderer.shouldApplyRootAnimation) {
|
||||
MountedPlayerRenderer.INSTANCE.animateRoot(poseStack);
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
-15
@@ -19,20 +19,17 @@ import net.minecraft.world.entity.LivingEntity;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
@Mixin(HumanoidModel.class)
|
||||
public class HumanoidModelMixin {
|
||||
@Unique HashMap<String, ModelPart> relevantPartsByName = new HashMap<>();
|
||||
|
||||
@Shadow @Final
|
||||
public ModelPart head;
|
||||
@Shadow @Final
|
||||
public ModelPart hat;
|
||||
@Shadow @Final
|
||||
public ModelPart rightArm;
|
||||
@Shadow @Final
|
||||
public ModelPart leftArm;
|
||||
@@ -40,6 +37,8 @@ public class HumanoidModelMixin {
|
||||
public ModelPart rightLeg;
|
||||
@Shadow @Final
|
||||
public ModelPart leftLeg;
|
||||
@Shadow @Final
|
||||
public ModelPart body;
|
||||
|
||||
@Inject(method = "setupAnim*", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/model/geom/ModelPart;copyFrom(Lnet/minecraft/client/model/geom/ModelPart;)V", shift = At.Shift.BEFORE))
|
||||
public void cobblemon$animateRiders(
|
||||
@@ -51,19 +50,23 @@ public class HumanoidModelMixin {
|
||||
float headPitch,
|
||||
CallbackInfo ci
|
||||
) {
|
||||
if (relevantPartsByName.isEmpty()) {
|
||||
relevantPartsByName.put("arm_left", leftArm);
|
||||
relevantPartsByName.put("arm_right", rightArm);
|
||||
relevantPartsByName.put("leg_left", leftLeg);
|
||||
relevantPartsByName.put("leg_right", rightLeg);
|
||||
relevantPartsByName.put("head", head);
|
||||
MountedPlayerRenderer.shouldApplyRootAnimation = false;
|
||||
|
||||
if (MountedPlayerRenderer.relevantPartsByName.size() <= 1) {
|
||||
MountedPlayerRenderer.relevantPartsByName.put("arm_left", leftArm);
|
||||
MountedPlayerRenderer.relevantPartsByName.put("arm_right", rightArm);
|
||||
MountedPlayerRenderer.relevantPartsByName.put("leg_left", leftLeg);
|
||||
MountedPlayerRenderer.relevantPartsByName.put("leg_right", rightLeg);
|
||||
MountedPlayerRenderer.relevantPartsByName.put("head", head);
|
||||
MountedPlayerRenderer.relevantPartsByName.put("hat", hat);
|
||||
MountedPlayerRenderer.relevantPartsByName.put("torso", body);
|
||||
}
|
||||
|
||||
Entity vehicle = entity.getVehicle();
|
||||
if (vehicle instanceof PokemonEntity pokemonEntity && entity instanceof OrientationControllable) {
|
||||
var shouldRotatePlayerHead = pokemonEntity.ifRidingAvailableSupply(false, (behaviour, settings, state) -> {
|
||||
return behaviour.shouldRotateRiderHead(settings, state, pokemonEntity);
|
||||
});
|
||||
var shouldRotatePlayerHead = pokemonEntity.ifRidingAvailableSupply(false, (behaviour, settings, state) ->
|
||||
behaviour.shouldRotateRiderHead(settings, state, pokemonEntity)
|
||||
);
|
||||
|
||||
if (!shouldRotatePlayerHead) {
|
||||
netHeadYaw = 0f;
|
||||
@@ -76,7 +79,8 @@ public class HumanoidModelMixin {
|
||||
|
||||
if (!(entity instanceof AbstractClientPlayer player)) return;
|
||||
|
||||
MountedPlayerRenderer.INSTANCE.animate(pokemonEntity, player, relevantPartsByName, netHeadYaw, headPitch, ageInTicks, limbSwing, limbSwingAmount);
|
||||
MountedPlayerRenderer.shouldApplyRootAnimation = true;
|
||||
MountedPlayerRenderer.INSTANCE.animate((HumanoidModel<AbstractClientPlayer>)(Object)this, pokemonEntity, player, netHeadYaw, headPitch, ageInTicks, limbSwing, limbSwingAmount);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -174,6 +174,16 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
|
||||
return this.cobblemon$isDoubleJumping;
|
||||
}
|
||||
|
||||
@Inject(method = "isHandsBusy", at = @At("HEAD"), cancellable = true)
|
||||
private void cobblemon$isHandsBusy(CallbackInfoReturnable<Boolean> cir) {
|
||||
Entity vehicle = this.getVehicle();
|
||||
if (vehicle instanceof PokemonEntity pokemonEntity) {
|
||||
int seatIndex = pokemonEntity.getPassengers().indexOf(this);
|
||||
Seat seat = pokemonEntity.getSeats().get(seatIndex);
|
||||
cir.setReturnValue(seat.getHandsBusy());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public HitResult pick(double hitDistance, float partialTicks, boolean hitFluids) {
|
||||
Entity vehicle = this.getVehicle();
|
||||
|
||||
@@ -117,6 +117,8 @@ public abstract class MouseHandlerMixin {
|
||||
@Local(argsOnly = true) double d,
|
||||
@Local(argsOnly = true) double movementTime
|
||||
) {
|
||||
boolean returnValue = true;
|
||||
|
||||
PokedexUsageContext usageContext = CobblemonClient.INSTANCE.getPokedexUsageContext();
|
||||
if (usageContext.getScanningGuiOpen()) {
|
||||
this.smoothTurnY.reset();
|
||||
@@ -127,7 +129,7 @@ public abstract class MouseHandlerMixin {
|
||||
var sensitivity = Mth.lerp(usageContext.getFovMultiplier(), spyglassSensitivity, lookSensitivity);
|
||||
var yRotationFlip = this.minecraft.options.invertYMouse().get() ? -1 : 1;
|
||||
player.turn(this.accumulatedDX * sensitivity, (this.accumulatedDY * sensitivity * yRotationFlip));
|
||||
return false;
|
||||
returnValue = false;
|
||||
}
|
||||
|
||||
// Clamp player rotation if riding and the vehicle demands it
|
||||
@@ -143,35 +145,33 @@ public abstract class MouseHandlerMixin {
|
||||
pitchSmoother.reset();
|
||||
rollSmoother.reset();
|
||||
yawSmoother.reset();
|
||||
return true;
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
//Send mouse input to be interpreted into rotation
|
||||
//deltas by the ride controller
|
||||
// Send mouse input to be interpreted into rotation
|
||||
// deltas by the ride controller
|
||||
Vec3 angVecMouse = cobblemon$getRideMouseRotation(cursorDeltaX, cursorDeltaY, movementTime);
|
||||
|
||||
//Perform Rotation using mouse influenced rotation deltas.
|
||||
// Perform Rotation using mouse influenced rotation deltas.
|
||||
controllable.getOrientationController().rotate(
|
||||
(float) angVecMouse.x,
|
||||
(float) angVecMouse.y,
|
||||
(float) angVecMouse.z
|
||||
);
|
||||
|
||||
//Gather and apply the current rotation deltas
|
||||
// Gather and apply the current rotation deltas
|
||||
var angRot = cobblemon$getAngularVelocity(movementTime);
|
||||
|
||||
//Apply smoothing if requested by the controller.
|
||||
//This Might be best if done by the controller itself?
|
||||
if(cobblemon$shouldUseAngVelSmoothing())
|
||||
{
|
||||
// Apply smoothing if requested by the controller.
|
||||
// This Might be best if done by the controller itself?
|
||||
if(cobblemon$shouldUseAngVelSmoothing()) {
|
||||
var yaw = yawSmoother.getNewDeltaValue(angRot.x * 0.5f, d);
|
||||
var pitch = pitchSmoother.getNewDeltaValue(angRot.y * 0.5f, d);
|
||||
var roll = rollSmoother.getNewDeltaValue(angRot.z * 0.5f, d);
|
||||
controllable.getOrientationController().rotate((float) yaw, (float) pitch, (float) roll);
|
||||
}
|
||||
//Otherwise simply apply the smoothing
|
||||
else
|
||||
{
|
||||
// Otherwise simply apply the smoothing
|
||||
else {
|
||||
controllable.getOrientationController().rotate((float) (angRot.x * 10 * d), (float) (angRot.y * 10 * d), (float) (angRot.z * 10 * d));
|
||||
}
|
||||
return false;
|
||||
|
||||
+3
-4
@@ -10,8 +10,7 @@ package com.cobblemon.mod.common.mixin.client.snapshots;
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBuildDetails;
|
||||
import com.cobblemon.mod.common.client.gui.snapshots.SnapshotWarningScreen;
|
||||
import com.cobblemon.mod.common.client.snapshots.SnapshotAcknowledgementData;
|
||||
import com.cobblemon.mod.common.client.snapshots.SnapshotAcknowledgementReader;
|
||||
import com.cobblemon.mod.common.client.persisted.ClientPersistedData;
|
||||
import kotlin.Unit;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.client.Minecraft;
|
||||
@@ -42,7 +41,7 @@ public final class SnapshotWarningMixin {
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
|
||||
SnapshotAcknowledgementReader.INSTANCE.write(new SnapshotAcknowledgementData(CobblemonBuildDetails.VERSION, dsa));
|
||||
ClientPersistedData.INSTANCE.writeSnapshotsData(new ClientPersistedData.SnapshotAcknowledgementData(CobblemonBuildDetails.VERSION, dsa));
|
||||
|
||||
runnable.run();
|
||||
return Unit.INSTANCE;
|
||||
@@ -54,7 +53,7 @@ public final class SnapshotWarningMixin {
|
||||
@Unique
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
private boolean cobblemon$isAcknowledged() {
|
||||
SnapshotAcknowledgementData data = SnapshotAcknowledgementReader.INSTANCE.read();
|
||||
ClientPersistedData.SnapshotAcknowledgementData data = ClientPersistedData.INSTANCE.readSnapshotsData();
|
||||
if (data == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,10 @@ package com.cobblemon.mod.common.mixin.entity;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
@@ -39,10 +42,17 @@ public abstract class EntityMixin {
|
||||
target = "Lnet/minecraft/world/entity/Entity;onGround()Z"
|
||||
)
|
||||
)
|
||||
public boolean cobblemon$forceOnGroundForStepUp(Entity entity, Operation<Boolean> original)
|
||||
{
|
||||
public boolean cobblemon$forceOnGroundForStepUp(Entity entity, Operation<Boolean> original) {
|
||||
if (entity instanceof PokemonEntity && entity.hasControllingPassenger()) {
|
||||
return true;
|
||||
BlockPos below = entity.blockPosition().below();
|
||||
Level level = entity.level();
|
||||
var blockStateBelow = level.getBlockState(below);
|
||||
boolean isAirOrLiquid = blockStateBelow.isAir() || !blockStateBelow.getFluidState().isEmpty();
|
||||
boolean canSupportEntity = blockStateBelow.isFaceSturdy(level, below, Direction.UP);
|
||||
boolean standingOnSolid = canSupportEntity && !isAirOrLiquid;
|
||||
if (standingOnSolid) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return original.call(entity);
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val APRICORN_FENCE = this.create("apricorn_fence", FenceBlock(BlockBehaviour.Properties.of().mapColor(APRICORN_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
|
||||
@JvmField
|
||||
val APRICORN_FENCE_GATE = this.create("apricorn_fence_gate", FenceGateBlock(APRICORN_WOOD_TYPE, BlockBehaviour.Properties.of().mapColor(APRICORN_PLANKS.defaultMapColor()).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
|
||||
val APRICORN_FENCE_GATE = this.create("apricorn_fence_gate", FenceGateBlock(APRICORN_WOOD_TYPE, BlockBehaviour.Properties.of().mapColor(APRICORN_PLANKS.defaultMapColor()).strength(2.0f, 3.0f).sound(SoundType.WOOD).ignitedByLava().forceSolidOn()))
|
||||
@JvmField
|
||||
val APRICORN_BUTTON = this.create("apricorn_button", BlocksInvoker.createWoodenButtonBlock(APRICORN_BLOCK_SET_TYPE))
|
||||
@JvmField
|
||||
@@ -205,7 +205,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val SACCHARINE_FENCE = this.create("saccharine_fence", FenceBlock(BlockBehaviour.Properties.of().mapColor(SACCHARINE_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
|
||||
@JvmField
|
||||
val SACCHARINE_FENCE_GATE = this.create("saccharine_fence_gate", FenceGateBlock(SACCHARINE_WOOD_TYPE, BlockBehaviour.Properties.of().mapColor(SACCHARINE_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
|
||||
val SACCHARINE_FENCE_GATE = this.create("saccharine_fence_gate", FenceGateBlock(SACCHARINE_WOOD_TYPE, BlockBehaviour.Properties.of().mapColor(SACCHARINE_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD).ignitedByLava().forceSolidOn()))
|
||||
@JvmField
|
||||
val SACCHARINE_BUTTON = this.create("saccharine_button", BlocksInvoker.createWoodenButtonBlock(BlockSetType.OAK))
|
||||
@JvmField
|
||||
@@ -213,7 +213,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val SACCHARINE_SIGN = this.create("saccharine_sign", CobblemonSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_SIGN), SACCHARINE_WOOD_TYPE))
|
||||
@JvmField
|
||||
val SACCHARINE_WALL_SIGN = this.create("saccharine_wall_sign", CobblemonWallSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_WALL_SIGN), SACCHARINE_WOOD_TYPE))
|
||||
val SACCHARINE_WALL_SIGN = this.create("saccharine_wall_sign", CobblemonWallSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_SIGN), SACCHARINE_WOOD_TYPE))
|
||||
@JvmField
|
||||
val SACCHARINE_HANGING_SIGN = this.create("saccharine_hanging_sign", CobblemonHangingSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_WALL_HANGING_SIGN), SACCHARINE_WOOD_TYPE))
|
||||
@JvmField
|
||||
|
||||
@@ -26,10 +26,10 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
|
||||
.networkSynchronized(PokemonItemComponent.PACKET_CODEC)
|
||||
.build())
|
||||
|
||||
val HELD_ITEM_REP: DataComponentType<HeldItemCapableComponent> = DataComponentType.builder<HeldItemCapableComponent>()
|
||||
.persistent(HeldItemCapableComponent.CODEC)
|
||||
.networkSynchronized(HeldItemCapableComponent.PACKET_CODEC)
|
||||
.build()
|
||||
val HELD_ITEM_EFFECT: DataComponentType<HeldItemEffectComponent> = create("held_item_effect", DataComponentType.builder<HeldItemEffectComponent>()
|
||||
.persistent(HeldItemEffectComponent.CODEC)
|
||||
.networkSynchronized(HeldItemEffectComponent.PACKET_CODEC)
|
||||
.build())
|
||||
|
||||
val BAIT: DataComponentType<RodBaitComponent> = create("bait", DataComponentType.builder<RodBaitComponent>()
|
||||
.persistent(RodBaitComponent.CODEC)
|
||||
@@ -86,6 +86,7 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:ingredient"), INGREDIENT)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:food"), FOOD)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:mob_effects"), MOB_EFFECTS)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:held_item_effect"), HELD_ITEM_EFFECT)
|
||||
}
|
||||
|
||||
override val registry = BuiltInRegistries.DATA_COMPONENT_TYPE
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.cobblemon.mod.common.block.BerryBlock
|
||||
import com.cobblemon.mod.common.block.MintBlock
|
||||
import com.cobblemon.mod.common.block.MintBlock.MintType
|
||||
import com.cobblemon.mod.common.client.pokedex.PokedexType
|
||||
import com.cobblemon.mod.common.block.campfirepot.CampfirePotColor
|
||||
import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
|
||||
import com.cobblemon.mod.common.item.*
|
||||
import com.cobblemon.mod.common.item.armor.CobblemonArmorTrims
|
||||
@@ -192,19 +193,19 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
|
||||
val campfire_pots = mutableListOf<CampfirePotItem>()
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_RED = campfirePotItem(CobblemonBlocks.RED_CAMPFIRE_POT, "red")
|
||||
val CAMPFIRE_POT_RED = campfirePotItem(CobblemonBlocks.RED_CAMPFIRE_POT, CampfirePotColor.RED)
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_YELLOW = campfirePotItem(CobblemonBlocks.YELLOW_CAMPFIRE_POT, "yellow")
|
||||
val CAMPFIRE_POT_YELLOW = campfirePotItem(CobblemonBlocks.YELLOW_CAMPFIRE_POT, CampfirePotColor.YELLOW)
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_GREEN = campfirePotItem(CobblemonBlocks.GREEN_CAMPFIRE_POT, "green")
|
||||
val CAMPFIRE_POT_GREEN = campfirePotItem(CobblemonBlocks.GREEN_CAMPFIRE_POT, CampfirePotColor.GREEN)
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_BLUE = campfirePotItem(CobblemonBlocks.BLUE_CAMPFIRE_POT, "blue")
|
||||
val CAMPFIRE_POT_BLUE = campfirePotItem(CobblemonBlocks.BLUE_CAMPFIRE_POT, CampfirePotColor.BLUE)
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_PINK = campfirePotItem(CobblemonBlocks.PINK_CAMPFIRE_POT, "pink")
|
||||
val CAMPFIRE_POT_PINK = campfirePotItem(CobblemonBlocks.PINK_CAMPFIRE_POT, CampfirePotColor.PINK)
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_BLACK = campfirePotItem(CobblemonBlocks.BLACK_CAMPFIRE_POT, "black")
|
||||
val CAMPFIRE_POT_BLACK = campfirePotItem(CobblemonBlocks.BLACK_CAMPFIRE_POT, CampfirePotColor.BLACK)
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_WHITE = campfirePotItem(CobblemonBlocks.WHITE_CAMPFIRE_POT, "white")
|
||||
val CAMPFIRE_POT_WHITE = campfirePotItem(CobblemonBlocks.WHITE_CAMPFIRE_POT, CampfirePotColor.WHITE)
|
||||
|
||||
@JvmField
|
||||
val HEARTY_GRAINS = compostableItem("hearty_grains", ItemNameBlockItem(CobblemonBlocks.HEARTY_GRAINS, Properties().rarity(Rarity.COMMON)))
|
||||
@@ -1500,8 +1501,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
}
|
||||
)
|
||||
|
||||
private fun campfirePotItem(block: Block, type: String): CampfirePotItem {
|
||||
val item = create("campfire_pot_${type}", CampfirePotItem(block))
|
||||
private fun campfirePotItem(block: Block, color: CampfirePotColor): CampfirePotItem {
|
||||
val item = create("campfire_pot_${color.suffix}", CampfirePotItem(block, color))
|
||||
campfire_pots.add(item)
|
||||
return item
|
||||
}
|
||||
@@ -1648,4 +1649,4 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
private fun foodItem(nutrition: Int, saturationModifier: Float): Item {
|
||||
return CobblemonItem(Item.Properties().food(FoodProperties.Builder().nutrition(nutrition).saturationModifier(saturationModifier).build()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ object CobblemonMechanics : DataRegistry {
|
||||
var potions = PotionsMechanic()
|
||||
|
||||
override fun sync(player: ServerPlayer) {}
|
||||
|
||||
override fun reload(manager: ResourceManager) {
|
||||
remedies = loadMechanic(manager, "remedies", RemediesMechanic::class.java)
|
||||
berries = loadMechanic(manager, "berries", BerriesMechanic::class.java)
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.client.net.OpenBehaviourEditorHandler
|
||||
import com.cobblemon.mod.common.client.net.CalculateSeatPositionsHandler
|
||||
import com.cobblemon.mod.common.client.net.OpenBehaviourEditorHandler
|
||||
import com.cobblemon.mod.common.client.net.PlayerInteractOptionsHandler
|
||||
import com.cobblemon.mod.common.client.net.SetClientPlayerDataHandler
|
||||
import com.cobblemon.mod.common.client.net.animation.PlayPosableAnimationHandler
|
||||
@@ -20,7 +20,7 @@ import com.cobblemon.mod.common.client.net.callback.party.OpenPartyCallbackHandl
|
||||
import com.cobblemon.mod.common.client.net.callback.partymove.OpenPartyMoveCallbackHandler
|
||||
import com.cobblemon.mod.common.client.net.cooking.ToggleCookingPotLidHandler
|
||||
import com.cobblemon.mod.common.client.net.data.DataRegistrySyncPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.data.UnlockReloadPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.data.RideSettingsSyncHandler
|
||||
import com.cobblemon.mod.common.client.net.dialogue.DialogueClosedHandler
|
||||
import com.cobblemon.mod.common.client.net.dialogue.DialogueOpenedHandler
|
||||
import com.cobblemon.mod.common.client.net.effect.PokeSnackBlockParticlesHandler
|
||||
@@ -65,8 +65,8 @@ import com.cobblemon.mod.common.client.net.trade.TradeProcessStartedHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeStartedHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeUpdatedHandler
|
||||
import com.cobblemon.mod.common.net.PacketRegisterInfo
|
||||
import com.cobblemon.mod.common.net.messages.client.OpenBehaviourEditorPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.CalculateSeatPositionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.OpenBehaviourEditorPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.PlayerInteractOptionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
|
||||
@@ -157,6 +157,7 @@ import com.cobblemon.mod.common.net.messages.server.callback.party.PartyPokemonS
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.party.PartySelectCancelledPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.partymove.PartyMoveSelectCancelledPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.partymove.PartyPokemonMoveSelectedPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.debug.ServerboundUpdateRidingSettingsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.debug.ServerboundUpdateRidingStatRangePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.debug.ServerboundUpdateRidingStatsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.dialogue.EscapeDialoguePacket
|
||||
@@ -170,11 +171,11 @@ import com.cobblemon.mod.common.net.messages.server.pasture.UnpasturePokemonPack
|
||||
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.FinishScanningPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.StartScanningPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.interact.InteractPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetItemHiddenPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetNicknamePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetActiveMarkPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetMarkingsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.ServerboundUpdateRidingStatePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetActiveMarkPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetItemHiddenPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetMarkingsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetNicknamePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.evolution.AcceptEvolutionPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.riding.DismountPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateDriverInputPacket
|
||||
@@ -208,6 +209,7 @@ import com.cobblemon.mod.common.net.serverhandling.callback.party.PartyPokemonSe
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.party.PartySelectCancelledHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.partymove.PartyMoveSelectCancelledHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.partymove.PartyPokemonMoveSelectedHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.debug.ServerboundUpdateRidingSettingsHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.debug.ServerboundUpdateRidingStatRangeHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.debug.ServerboundUpdateRidingStatsHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.dialogue.EscapeDialogueHandler
|
||||
@@ -222,11 +224,11 @@ import com.cobblemon.mod.common.net.serverhandling.pasture.UnpasturePokemonHandl
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.FinishScanningHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.StartScanningHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.interact.InteractPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetItemHiddenHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetNicknameHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetActiveMarkHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetMarkingsHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.ServerboundUpdateRidingStateHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetActiveMarkHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetItemHiddenHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetMarkingsHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetNicknameHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.riding.DismountPokemonPacketHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.riding.DriverInputPacketHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.starter.RequestStarterScreenHandler
|
||||
@@ -310,6 +312,7 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(MarksPotentialUpdatePacket.ID, MarksPotentialUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(MarkingsUpdatePacket.ID, MarkingsUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(RideBoostsUpdatePacket.ID, RideBoostsUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(RideStaminaUpdatePacket.ID, RideStaminaUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
|
||||
// Evolution start
|
||||
list.add(PacketRegisterInfo(AddEvolutionPacket.ID, AddEvolutionPacket::decode, PokemonUpdatePacketHandler()))
|
||||
@@ -388,7 +391,6 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(BerryRegistrySyncPacket.ID, BerryRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(SpeciesRegistrySyncPacket.ID, SpeciesRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(PropertiesCompletionRegistrySyncPacket.ID, PropertiesCompletionRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(UnlockReloadPacket.ID, UnlockReloadPacket::decode, UnlockReloadPacketHandler))
|
||||
list.add(PacketRegisterInfo(StandardSpeciesFeatureSyncPacket.ID, StandardSpeciesFeatureSyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(GlobalSpeciesFeatureSyncPacket.ID, GlobalSpeciesFeatureSyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(SpeciesFeatureAssignmentSyncPacket.ID, SpeciesFeatureAssignmentSyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
@@ -405,6 +407,7 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(CosmeticItemAssignmentSyncPacket.ID, CosmeticItemAssignmentSyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(BehaviourSyncPacket.ID, BehaviourSyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(MarkRegistrySyncPacket.ID, MarkRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(RideSettingsSyncPacket.ID, RideSettingsSyncPacket::decode, RideSettingsSyncHandler))
|
||||
|
||||
// Effects
|
||||
list.add(PacketRegisterInfo(SpawnSnowstormParticlePacket.ID, SpawnSnowstormParticlePacket::decode, SpawnSnowstormParticleHandler))
|
||||
@@ -578,6 +581,7 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateRidingStatePacket.ID, ServerboundUpdateRidingStatePacket::decode, ServerboundUpdateRidingStateHandler))
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateRidingStatsPacket.ID, ServerboundUpdateRidingStatsPacket::decode, ServerboundUpdateRidingStatsHandler))
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateRidingStatRangePacket.ID, ServerboundUpdateRidingStatRangePacket::decode, ServerboundUpdateRidingStatRangeHandler))
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateRidingSettingsPacket.ID, ServerboundUpdateRidingSettingsPacket::decode, ServerboundUpdateRidingSettingsHandler))
|
||||
list.add(PacketRegisterInfo(DismountPokemonPacket.ID, DismountPokemonPacket::decode, DismountPokemonPacketHandler))
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateDriverInputPacket.ID, ServerboundUpdateDriverInputPacket::decode, DriverInputPacketHandler))
|
||||
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
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.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.BirdSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.GliderSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.HelicopterSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.HoverSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.JetSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.RocketSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.MinekartSettings
|
||||
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.net.messages.client.data.RideSettingsSyncPacket
|
||||
import com.cobblemon.mod.common.util.adapters.ExpressionAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.ExpressionLikeAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.FloatNumberRangeAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.GsonBuilder
|
||||
import net.minecraft.advancements.critereon.MinMaxBounds
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
import net.minecraft.server.packs.resources.ResourceManager
|
||||
|
||||
object CobblemonRideSettings : DataRegistry {
|
||||
override val id: ResourceLocation = cobblemonResource("ride_settings")
|
||||
override val type = PackType.SERVER_DATA
|
||||
override val observable = SimpleObservable<CobblemonRideSettings>()
|
||||
val gson = GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
|
||||
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
|
||||
.registerTypeAdapter(MinMaxBounds.Doubles::class.java, FloatNumberRangeAdapter)
|
||||
.create()
|
||||
|
||||
var bird = BirdSettings()
|
||||
var glider = GliderSettings()
|
||||
var helicopter = HelicopterSettings()
|
||||
var hover = HoverSettings()
|
||||
var jet = JetSettings()
|
||||
var rocket = RocketSettings()
|
||||
|
||||
var horse = HorseSettings()
|
||||
var minekart = MinekartSettings()
|
||||
var vehicle = VehicleSettings()
|
||||
|
||||
var boat = BoatSettings()
|
||||
var burst = BurstSettings()
|
||||
var dolphin = DolphinSettings()
|
||||
|
||||
override fun sync(player: ServerPlayer) {
|
||||
player.sendPacket(
|
||||
RideSettingsSyncPacket(
|
||||
bird = bird,
|
||||
glider = glider,
|
||||
helicopter = helicopter,
|
||||
hover = hover,
|
||||
jet = jet,
|
||||
rocket = rocket,
|
||||
horse = horse,
|
||||
minekart = minekart,
|
||||
vehicle = vehicle,
|
||||
boat = boat,
|
||||
burst = burst,
|
||||
dolphin = dolphin
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun reload(manager: ResourceManager) {
|
||||
bird = loadStyle(manager, "bird", BirdSettings::class.java)
|
||||
glider = loadStyle(manager, "glider", GliderSettings::class.java)
|
||||
helicopter = loadStyle(manager, "helicopter", HelicopterSettings::class.java)
|
||||
hover = loadStyle(manager, "hover", HoverSettings::class.java)
|
||||
jet = loadStyle(manager, "jet", JetSettings::class.java)
|
||||
rocket = loadStyle(manager, "rocket", RocketSettings::class.java)
|
||||
horse = loadStyle(manager, "horse", HorseSettings::class.java)
|
||||
minekart = loadStyle(manager, "minekart", MinekartSettings::class.java)
|
||||
vehicle = loadStyle(manager, "vehicle", VehicleSettings::class.java)
|
||||
boat = loadStyle(manager, "boat", BoatSettings::class.java)
|
||||
burst = loadStyle(manager, "burst", BurstSettings::class.java)
|
||||
dolphin = loadStyle(manager, "dolphin", DolphinSettings::class.java)
|
||||
}
|
||||
|
||||
private fun <T : RidingBehaviourSettings> loadStyle(manager: ResourceManager, name: String, clazz: Class<T>): T {
|
||||
manager.getResourceOrThrow(cobblemonResource("ride_settings/$name.json")).open().use {
|
||||
return gson.fromJson(it.reader(), clazz)
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -12,11 +12,9 @@ import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.datafixers.util.Either
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
@@ -27,7 +25,9 @@ import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.sensing.SensorType
|
||||
|
||||
/**
|
||||
* Checks nearby entities and counts how many have nominated this entity as their leader.
|
||||
* Checks nearby entities and counts how many have nominated this entity as their leader. We normally update this
|
||||
* as people join and leave the herd, but this is a fallback to make sure we don't get stuck with an incorrect value
|
||||
* like if the entities die or whatever.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since June 17th, 2025
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ class FindAirTaskConfig : SingleTaskConfig {
|
||||
it.absent(CobblemonMemories.PATH_COOLDOWN)
|
||||
).apply(it) { walkTarget, pathCooldown ->
|
||||
Trigger { world, entity, time ->
|
||||
if (!entity.getIsSubmerged()) {
|
||||
if (!entity.getIsSubmerged() || entity.isInLava) {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
|
||||
+9
-9
@@ -8,17 +8,16 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
|
||||
import com.cobblemon.mod.common.api.storage.party.PartyStore
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.ai.ToleratedHerdLeader
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.datafixers.util.Either
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
@@ -57,21 +56,21 @@ class FindHerdLeaderTaskConfig : SingleTaskConfig {
|
||||
!it.brain.hasMemoryValue(CobblemonMemories.POKEMON_DROWSY)
|
||||
}.forEach { possibleLeader ->
|
||||
possibleLeader as PokemonEntity
|
||||
val matchingHerdLeader = entity.behaviour.herd.bestMatchLeader(entity, possibleLeader) ?: return@forEach
|
||||
val alreadyIsALeader = possibleLeader.brain.hasMemoryValue(CobblemonMemories.HERD_SIZE)
|
||||
val nestedHerdTier = currentLeaderEntity?.getHerdTier() ?: 0
|
||||
if (nestedHerdTier >= matchingHerdLeader.tier) {
|
||||
val bestMatchingHerdLeader = entity.behaviour.herd.bestMatchLeader(entity, possibleLeader) ?: return@forEach
|
||||
val possibleLeaderIsAlreadyALeader = possibleLeader.brain.hasMemoryValue(CobblemonMemories.HERD_SIZE)
|
||||
val currentLeaderHerdTier = currentLeaderEntity?.getHerdTier() ?: entity.getHerdTier()
|
||||
if (currentLeaderHerdTier >= bestMatchingHerdLeader.tier) {
|
||||
return@forEach // This leader is following a leader of a higher tier, we can't replace them
|
||||
}
|
||||
val distance = entity.distanceTo(possibleLeader)
|
||||
if (matchingHerdLeader.tier < bestTier) {
|
||||
if (bestMatchingHerdLeader.tier < bestTier) {
|
||||
return@forEach // No need to check further if this leader is worse than the best found so far
|
||||
} else if ((distance > bestDistance && !alreadyIsALeader) && matchingHerdLeader.tier == bestTier) {
|
||||
} else if ((distance > bestDistance && !possibleLeaderIsAlreadyALeader) && bestMatchingHerdLeader.tier == bestTier) {
|
||||
return@forEach // No need to check further if this leader is farther than the best found so far, unless they are already a leader - I'd prefer to join a pack
|
||||
} else {
|
||||
bestLeader = possibleLeader
|
||||
bestDistance = distance
|
||||
bestTier = matchingHerdLeader.tier
|
||||
bestTier = bestMatchingHerdLeader.tier
|
||||
}
|
||||
}
|
||||
return bestLeader ?: currentLeaderEntity
|
||||
@@ -119,6 +118,7 @@ class FindHerdLeaderTaskConfig : SingleTaskConfig {
|
||||
|
||||
herdLeader.set(bestLeader.uuid.toString())
|
||||
bestLeader.brain.eraseMemory(CobblemonMemories.HERD_LEADER)
|
||||
bestLeader.adjustHerdSize(1)
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
|
||||
+11
@@ -14,15 +14,19 @@ import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.FindRestingPlaceTask
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
import com.mojang.datafixers.util.Either
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
|
||||
class FindRestingPlaceTaskConfig : SingleTaskConfig {
|
||||
val condition: ExpressionLike = "true".asExpressionLike()
|
||||
val horizontalSearchDistance: ExpressionOrEntityVariable = Either.left("16".asExpression())
|
||||
val verticalSearchDistance: ExpressionOrEntityVariable = Either.left("5".asExpression())
|
||||
|
||||
@@ -31,6 +35,13 @@ class FindRestingPlaceTaskConfig : SingleTaskConfig {
|
||||
entity: LivingEntity,
|
||||
behaviourConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
if (entity !is PokemonEntity) {
|
||||
return null
|
||||
}
|
||||
if (!behaviourConfigurationContext.runtime.resolveBoolean(condition)) {
|
||||
return null
|
||||
}
|
||||
|
||||
behaviourConfigurationContext.addMemories(
|
||||
CobblemonMemories.PATH_COOLDOWN,
|
||||
CobblemonMemories.POKEMON_DROWSY,
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@ class GoToLandTaskConfig : SingleTaskConfig {
|
||||
it.absent(CobblemonMemories.PATH_COOLDOWN)
|
||||
).apply(it) { walkTarget, pathCooldown ->
|
||||
Trigger { world, entity, time ->
|
||||
if (!entity.isInLiquid) {
|
||||
if (!entity.isInWater) {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
|
||||
+6
-1
@@ -11,15 +11,20 @@ package com.cobblemon.mod.common.api.ai.config.task
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.CobblemonSensors
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.GoToSleepTask
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
import com.mojang.datafixers.util.Either
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
|
||||
class GoToSleepTaskConfig : SingleTaskConfig {
|
||||
var onlyFromStatus: ExpressionOrEntityVariable = Either.left("false".asExpression())
|
||||
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = emptyList<MoLangConfigVariable>()
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
@@ -34,6 +39,6 @@ class GoToSleepTaskConfig : SingleTaskConfig {
|
||||
CobblemonMemories.POKEMON_SLEEPING
|
||||
)
|
||||
behaviourConfigurationContext.addSensors(CobblemonSensors.POKEMON_DROWSY)
|
||||
return WrapperLivingEntityTask(GoToSleepTask.create(), PokemonEntity::class.java)
|
||||
return WrapperLivingEntityTask(GoToSleepTask.create(onlyFromStatus.resolveBoolean(behaviourConfigurationContext.runtime)), PokemonEntity::class.java)
|
||||
}
|
||||
}
|
||||
+13
-2
@@ -10,17 +10,28 @@ package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
|
||||
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
|
||||
import com.cobblemon.mod.common.entity.npc.ai.LookAtSpeakerTask
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.mojang.datafixers.util.Either
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
|
||||
class LookAtSpeakerTaskConfig : SingleTaskConfig {
|
||||
val condition: ExpressionOrEntityVariable = Either.left("true".asExpression())
|
||||
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = emptyList<MoLangConfigVariable>()
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
behaviourConfigurationContext: BehaviourConfigurationContext
|
||||
) = LookAtSpeakerTask.create().also {
|
||||
behaviourConfigurationContext.addMemories(MemoryModuleType.LOOK_TARGET, CobblemonMemories.DIALOGUES)
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
if (!condition.resolveBoolean(behaviourConfigurationContext.runtime)) {
|
||||
return null
|
||||
}
|
||||
|
||||
return LookAtSpeakerTask.create().also {
|
||||
behaviourConfigurationContext.addMemories(MemoryModuleType.LOOK_TARGET, CobblemonMemories.DIALOGUES)
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -8,15 +8,14 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.datafixers.util.Either
|
||||
import java.util.UUID
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
@@ -82,6 +81,8 @@ class MaintainHerdLeaderTaskConfig : SingleTaskConfig {
|
||||
if (bestLeader != null && bestLeader != leader) {
|
||||
bestLeader.brain.eraseMemory(CobblemonMemories.HERD_LEADER)
|
||||
entity.brain.setMemory(CobblemonMemories.HERD_LEADER, bestLeader.uuid.toString())
|
||||
bestLeader.adjustHerdSize(1)
|
||||
leader.adjustHerdSize(-1)
|
||||
}
|
||||
}
|
||||
return@Trigger true
|
||||
|
||||
+64
-27
@@ -14,6 +14,8 @@ import com.cobblemon.mod.common.api.ai.CobblemonWanderControl
|
||||
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.entity.OmniPathingEntity
|
||||
import com.cobblemon.mod.common.entity.ai.CobblemonRandomSurfacePos
|
||||
import com.cobblemon.mod.common.entity.ai.CobblemonWalkTarget
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.mainThreadRuntime
|
||||
@@ -26,6 +28,7 @@ import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorUtils
|
||||
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
@@ -37,27 +40,31 @@ import net.minecraft.world.level.pathfinder.PathType
|
||||
class WanderTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val WANDER = "wander" // Category
|
||||
const val MAX_LOOK_DOWN_DISTANCE = 64
|
||||
}
|
||||
|
||||
val condition = booleanVariable(WANDER, "wanders", true).asExpressible()
|
||||
val wanderChance = numberVariable(WANDER, "wander_chance", 1/(20 * 6F)).asExpressible()
|
||||
val wanderChance = numberVariable(WANDER, "wander_chance", 1 / (20 * 6F)).asExpressible()
|
||||
val horizontalRange = numberVariable(WANDER, "horizontal_wander_range", 10).asExpressible()
|
||||
val verticalRange = numberVariable(WANDER, "vertical_wander_range", 5).asExpressible()
|
||||
val speedMultiplier = numberVariable(SharedEntityVariables.MOVEMENT_CATEGORY, SharedEntityVariables.WALK_SPEED, 0.35).asExpressible()
|
||||
val avoidTargetingAir: ExpressionOrEntityVariable = Either.left("true".asExpression()) // Whether to avoid air blocks when wandering
|
||||
val speedMultiplier =
|
||||
numberVariable(SharedEntityVariables.MOVEMENT_CATEGORY, SharedEntityVariables.WALK_SPEED, 0.35).asExpressible()
|
||||
val avoidTargetingAir: ExpressionOrEntityVariable =
|
||||
Either.left("true".asExpression()) // Whether to avoid air blocks when wandering
|
||||
val minimumHeight: ExpressionOrEntityVariable = Either.left("0".asExpression()) // Height off the ground
|
||||
val maximumHeight: ExpressionOrEntityVariable = Either.left("-1".asExpression()) // Height off the ground
|
||||
|
||||
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(
|
||||
condition,
|
||||
wanderChance,
|
||||
horizontalRange,
|
||||
verticalRange,
|
||||
speedMultiplier,
|
||||
avoidTargetingAir,
|
||||
minimumHeight,
|
||||
maximumHeight
|
||||
).asVariables()
|
||||
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) =
|
||||
listOf(
|
||||
condition,
|
||||
wanderChance,
|
||||
horizontalRange,
|
||||
verticalRange,
|
||||
speedMultiplier,
|
||||
avoidTargetingAir,
|
||||
minimumHeight,
|
||||
maximumHeight
|
||||
).asVariables()
|
||||
|
||||
private fun applyHeightConstraints(
|
||||
pos: BlockPos,
|
||||
@@ -73,10 +80,18 @@ class WanderTaskConfig : SingleTaskConfig {
|
||||
val altitude = if (!block.isAir) {
|
||||
0
|
||||
} else {
|
||||
(1..64).firstOrNull {
|
||||
(1..MAX_LOOK_DOWN_DISTANCE).firstOrNull {
|
||||
val newPos = pos.below(it)
|
||||
return@firstOrNull !world.getBlockState(newPos).isAir
|
||||
} ?: Int.MAX_VALUE
|
||||
if (!world.getBlockState(newPos).isAir) {
|
||||
return@firstOrNull true
|
||||
} else if (newPos.y <= world.minBuildHeight) {
|
||||
// Don't adjust downward into the void (Mostly if we're in the end)
|
||||
// This doesn't stop fliers from wandering off end islands,
|
||||
// but it does stop them from diving directly into the void.
|
||||
return pos
|
||||
}
|
||||
return@firstOrNull false
|
||||
} ?: MAX_LOOK_DOWN_DISTANCE
|
||||
}
|
||||
|
||||
if (altitude > maximumHeight && maximumHeight >= 0) {
|
||||
@@ -117,7 +132,7 @@ class WanderTaskConfig : SingleTaskConfig {
|
||||
it.registered(CobblemonMemories.WANDER_CONTROL)
|
||||
).apply(it) { walkTarget, lookTarget, pathCooldown, wanderControl ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob || entity.isUnderWater) {
|
||||
if (entity !is PathfinderMob || (entity.isUnderWater && !(entity.canBreatheUnderwater() && !((entity as OmniPathingEntity).canSwimInWater())))) {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
@@ -125,10 +140,9 @@ class WanderTaskConfig : SingleTaskConfig {
|
||||
mainThreadRuntime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
val avoidsTargetingAir = avoidTargetingAir.resolveBoolean(mainThreadRuntime)
|
||||
val wanderChance = mainThreadRuntime.resolveFloat(wanderChanceExpression)
|
||||
if (wanderChance <= 0 || world.random.nextFloat() > wanderChance || !wanderControl.allowLand) {
|
||||
if (wanderChance <= 0 || world.random.nextFloat() > wanderChance) {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
pathCooldown.setWithExpiry(true, wanderControl.pathCooldownTicks.toLong())
|
||||
val minimumHeight = minimumHeight.resolveInt(mainThreadRuntime)
|
||||
val maximumHeight = maximumHeight.resolveInt(mainThreadRuntime)
|
||||
@@ -141,21 +155,35 @@ class WanderTaskConfig : SingleTaskConfig {
|
||||
HoverRandomPos.getPos(
|
||||
entity,
|
||||
horizontalRange.resolveInt(mainThreadRuntime),
|
||||
maxOf(verticalRange.resolveInt(mainThreadRuntime), maximumHeight), // In case they fly up pretty high and need to find a way down
|
||||
verticalRange.resolveInt(mainThreadRuntime),
|
||||
entity.random.nextFloat() - 0.5,
|
||||
entity.random.nextFloat() - 0.5,
|
||||
Math.PI.toFloat(),
|
||||
maximumHeight,
|
||||
minimumHeight
|
||||
)
|
||||
} else if (entity.isUnderWater && entity.canBreatheUnderwater() && !((entity as OmniPathingEntity).canSwimInWater())) {
|
||||
BehaviorUtils.getRandomSwimmablePos(
|
||||
entity,
|
||||
horizontalRange.resolveInt(mainThreadRuntime),
|
||||
verticalRange.resolveInt(mainThreadRuntime)
|
||||
)
|
||||
} else if (entity is OmniPathingEntity && (entity.canWalkOnWater() || entity.canWalkOnLava())) {
|
||||
CobblemonRandomSurfacePos.getPos(
|
||||
entity,
|
||||
horizontalRange.resolveInt(mainThreadRuntime),
|
||||
verticalRange.resolveInt(mainThreadRuntime)
|
||||
)
|
||||
} else {
|
||||
LandRandomPos.getPos(
|
||||
entity,
|
||||
horizontalRange.resolveInt(mainThreadRuntime),
|
||||
verticalRange.resolveInt(mainThreadRuntime)
|
||||
)
|
||||
LandRandomPos.getPos(
|
||||
entity,
|
||||
horizontalRange.resolveInt(mainThreadRuntime),
|
||||
verticalRange.resolveInt(mainThreadRuntime)
|
||||
)
|
||||
} ?: continue
|
||||
|
||||
if (targetVec == null) continue
|
||||
|
||||
pos = applyHeightConstraints(
|
||||
pos = BlockPos.containing(targetVec),
|
||||
minimumHeight = minimumHeight,
|
||||
@@ -173,8 +201,17 @@ class WanderTaskConfig : SingleTaskConfig {
|
||||
pos = pos,
|
||||
speedModifier = speedMultiplier.resolveFloat(mainThreadRuntime),
|
||||
completionRange = 0,
|
||||
nodeTypeFilter = { nodeType -> nodeType !in listOf(PathType.WATER, PathType.WATER_BORDER) },
|
||||
destinationNodeTypeFilter = { nodeType -> !avoidsTargetingAir || nodeType !in listOf(PathType.OPEN) }
|
||||
nodeTypeFilter = { nodeType ->
|
||||
entity.canBreatheUnderwater() || nodeType !in listOf(
|
||||
PathType.WATER,
|
||||
PathType.WATER_BORDER
|
||||
)
|
||||
},
|
||||
destinationNodeTypeFilter = { nodeType ->
|
||||
!avoidsTargetingAir || nodeType !in listOf(
|
||||
PathType.OPEN
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
lookTarget.set(BlockPosTracker(pos.toVec3d().add(0.0, entity.eyeHeight.toDouble(), 0.0)))
|
||||
|
||||
+29
@@ -31,8 +31,10 @@ import net.minecraft.world.entity.ai.behavior.BlockPosTracker
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.level.material.Fluids
|
||||
import net.minecraft.world.level.pathfinder.PathType
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import kotlin.math.ceil
|
||||
|
||||
class WaterWanderTaskConfig : SingleTaskConfig {
|
||||
val condition = booleanVariable(WANDER, "water_wanders", true).asExpressible()
|
||||
@@ -100,6 +102,33 @@ class WaterWanderTaskConfig : SingleTaskConfig {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
// Move target pos down so that we don't move out of water too much
|
||||
val requiredDepth = ceil(0.5 + entity.eyeHeight)
|
||||
// Get the current depth of the block
|
||||
var depth = 1
|
||||
var testPos = pos.above()
|
||||
var blockState = entity.level().getBlockState(testPos)
|
||||
while (blockState.fluidState.type == Fluids.WATER) {
|
||||
depth++
|
||||
testPos = testPos!!.above()
|
||||
blockState = entity.level().getBlockState(testPos)
|
||||
}
|
||||
|
||||
if (depth < requiredDepth) {
|
||||
// Iterate down until we reach the desired min depth or we reach a non water block
|
||||
pos = pos.below()
|
||||
blockState = entity.level().getBlockState(pos)
|
||||
while (blockState.fluidState.type == Fluids.WATER && depth < requiredDepth) {
|
||||
depth ++
|
||||
pos = pos!!.below()
|
||||
blockState = entity.level().getBlockState(pos)
|
||||
}
|
||||
}
|
||||
|
||||
if (depth < requiredDepth) {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
walkTarget.set(
|
||||
CobblemonWalkTarget(
|
||||
pos = pos,
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.cooking
|
||||
|
||||
import net.minecraft.ChatFormatting
|
||||
import net.minecraft.util.StringRepresentable
|
||||
|
||||
/**
|
||||
@@ -18,16 +17,16 @@ import net.minecraft.util.StringRepresentable
|
||||
* @author Licious
|
||||
* @since November 28th, 2022
|
||||
*/
|
||||
enum class Flavour(val colour: Int, val chatFormatting: ChatFormatting): StringRepresentable {
|
||||
SPICY(0x00FF00, ChatFormatting.RED),
|
||||
DRY(0xFF0000, ChatFormatting.DARK_AQUA),
|
||||
SWEET(0x0000FF, ChatFormatting.LIGHT_PURPLE),
|
||||
BITTER(0xFFFF00, ChatFormatting.GREEN),
|
||||
SOUR(0xFF00FF, ChatFormatting.YELLOW),
|
||||
MILD(0xFF00FF, ChatFormatting.DARK_PURPLE);
|
||||
enum class Flavour(val colour: Int): StringRepresentable {
|
||||
SPICY(0xFA795E),
|
||||
DRY(0x65AFF3),
|
||||
SWEET(0xFF82CF),
|
||||
BITTER(0x77C96A),
|
||||
SOUR(0xF3D465),
|
||||
MILD(0x9D99BF);
|
||||
|
||||
override fun getSerializedName() = name
|
||||
companion object {
|
||||
val CODEC = StringRepresentable.fromEnum<Flavour> { Flavour.entries.toTypedArray() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,13 +9,11 @@
|
||||
package com.cobblemon.mod.common.api.cooking
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItemComponents
|
||||
import com.cobblemon.mod.common.item.PokePuffItem
|
||||
import com.cobblemon.mod.common.item.PokePuffItem.Companion.DISLIKED_FLAVOR_MULTIPLIER
|
||||
import com.cobblemon.mod.common.item.PokePuffItem.Companion.LIKED_FLAVOR_MULTIPLIER
|
||||
import com.cobblemon.mod.common.pokemon.Nature
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
object PokePuffUtils {
|
||||
|
||||
@@ -25,11 +23,12 @@ object PokePuffUtils {
|
||||
|
||||
val (baseFriendship) = calculateBaseFriendship(stack)
|
||||
|
||||
return when {
|
||||
dominantFlavour == nature.favouriteFlavour -> (baseFriendship / LIKED_FLAVOR_MULTIPLIER).toInt()
|
||||
dominantFlavour == Flavour.MILD && isNeutralNature -> (baseFriendship / 2.0).coerceAtLeast(1.0).toInt()
|
||||
else -> (-baseFriendship / DISLIKED_FLAVOR_MULTIPLIER).toInt()
|
||||
}
|
||||
val multiplier = if (
|
||||
dominantFlavour == nature.favouriteFlavour ||
|
||||
(dominantFlavour == Flavour.MILD && isNeutralNature)
|
||||
) LIKED_FLAVOR_MULTIPLIER else -DISLIKED_FLAVOR_MULTIPLIER
|
||||
|
||||
return (baseFriendship / multiplier).toInt()
|
||||
}
|
||||
|
||||
fun calculateBaseFriendship(stack: ItemStack): Pair<Double, Double> {
|
||||
@@ -77,8 +76,8 @@ object PokePuffUtils {
|
||||
Flavour.SPICY -> "attack"
|
||||
Flavour.DRY -> "special_attack"
|
||||
Flavour.SWEET -> "speed"
|
||||
Flavour.BITTER -> "special_defense"
|
||||
Flavour.SOUR -> "defense"
|
||||
Flavour.BITTER -> "special_defence"
|
||||
Flavour.SOUR -> "defence"
|
||||
Flavour.MILD -> "neutral"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,14 +18,13 @@ import net.minecraft.resources.ResourceLocation
|
||||
* @since August 5th, 2022
|
||||
*/
|
||||
interface DataProvider {
|
||||
|
||||
/**
|
||||
* Registers a [DataRegistry] to listen for updates.
|
||||
* The updates will automatically happen on the correct sides based on [DataRegistry.type].
|
||||
*
|
||||
* @param registry The [DataRegistry] being registered.
|
||||
*/
|
||||
fun <T : DataRegistry> register(registry: T): T
|
||||
fun <T : DataRegistry> register(registry: T, reloadable: Boolean): T
|
||||
|
||||
/**
|
||||
* Attempts to find a [DataRegistry] with the given [ResourceLocation].
|
||||
|
||||
@@ -152,6 +152,9 @@ object CobblemonEvents {
|
||||
@JvmField
|
||||
val RIDE_EVENT_PRE = CancelableObservable<RidePokemonEvent.Pre>()
|
||||
|
||||
@JvmField
|
||||
val RIDE_EVENT_APPLY_STAMINA = EventObservable<RidePokemonEvent.ApplyStamina>()
|
||||
|
||||
@JvmField
|
||||
val RIDE_EVENT_POST = EventObservable<RidePokemonEvent.Post>()
|
||||
|
||||
|
||||
+32
-2
@@ -8,9 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokemon
|
||||
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
@@ -49,4 +47,36 @@ interface RidePokemonEvent {
|
||||
"pokemon" to pokemon.struct
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Event published when stamina is being applied. At this point you can modify the stamina that will get used on the
|
||||
* mount or you can make the stamina infinite. Handy hey.
|
||||
*/
|
||||
class ApplyStamina(
|
||||
override val player: ServerPlayer,
|
||||
override val pokemon: PokemonEntity,
|
||||
var rideStamina: Float
|
||||
) : RidePokemonEvent {
|
||||
val context = mutableMapOf(
|
||||
"player" to player.asMoLangValue(),
|
||||
"pokemon" to pokemon.struct,
|
||||
"ride_stamina" to DoubleValue(rideStamina)
|
||||
)
|
||||
val functions = moLangFunctionMap(
|
||||
"set_ride_stamina" to { params ->
|
||||
rideStamina = params.getDouble(0).toFloat().coerceIn(0F, 1F)
|
||||
DoubleValue.ONE
|
||||
},
|
||||
"set_infinite_stamina" to { _ ->
|
||||
rideStamina = -1F
|
||||
DoubleValue.ONE
|
||||
},
|
||||
)
|
||||
|
||||
fun setInfiniteStamina() {
|
||||
rideStamina = -1F
|
||||
}
|
||||
|
||||
fun isInfiniteStamina() = rideStamina == -1F
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.item
|
||||
|
||||
import com.cobblemon.mod.common.api.data.DataRegistry
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
import net.minecraft.server.packs.resources.ResourceManager
|
||||
import java.io.File
|
||||
|
||||
object HeldItems : DataRegistry {
|
||||
|
||||
override val id = cobblemonResource("held_items")
|
||||
override val type = PackType.SERVER_DATA
|
||||
override val observable = SimpleObservable<HeldItems>()
|
||||
override fun sync(player: ServerPlayer) {}
|
||||
|
||||
// val heldItems = PrioritizedList<BagItemLike>()
|
||||
internal val heldItemsScripts = mutableMapOf<String, String>() // showdown item ID to JavaScript
|
||||
val showdownItems = hashSetOf<String>()
|
||||
|
||||
override fun reload(manager: ResourceManager) {
|
||||
heldItemsScripts.clear()
|
||||
ShowdownService.service.resetRegistryData("heldItem")
|
||||
manager.listResources("held_items") { it.path.endsWith(".js") }.forEach { (identifier, resource) ->
|
||||
resource.open().use { stream ->
|
||||
stream.bufferedReader().use { reader ->
|
||||
val resolvedIdentifier = ResourceLocation.fromNamespaceAndPath(identifier.namespace, File(identifier.path).nameWithoutExtension)
|
||||
val js = reader.readText()
|
||||
heldItemsScripts[resolvedIdentifier.path] = js
|
||||
}
|
||||
}
|
||||
}
|
||||
ShowdownService.service.sendRegistryData(heldItemsScripts, "heldItem")
|
||||
|
||||
val itemsJson = ShowdownService.service.getRegistryData("heldItem")
|
||||
for (i in 0 until itemsJson.size()) {
|
||||
val jsItem = itemsJson[i].asJsonObject
|
||||
showdownItems += jsItem.get("id").asString
|
||||
}
|
||||
|
||||
CobblemonHeldItemManager.load()
|
||||
|
||||
this.observable.emit(this)
|
||||
}
|
||||
}
|
||||
@@ -1236,7 +1236,7 @@ object MoLangFunctions {
|
||||
}
|
||||
}
|
||||
map.put("has_walk_target") { _ ->
|
||||
DoubleValue(entity.brain.getMemory(MemoryModuleType.WALK_TARGET).isPresent || entity.isPathFinding)
|
||||
DoubleValue(entity.brain.getMemorySafely(MemoryModuleType.WALK_TARGET).isPresent || entity.isPathFinding)
|
||||
}
|
||||
}
|
||||
map
|
||||
|
||||
+2
-1
@@ -15,6 +15,7 @@ import net.minecraft.world.entity.LivingEntity
|
||||
import org.joml.Matrix3f
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.asin
|
||||
import kotlin.math.sign
|
||||
|
||||
@@ -113,7 +114,7 @@ open class OrientationController(val entity: LivingEntity) {
|
||||
|
||||
fun applyGlobalPitch(deltaPitchDegrees: Float) = updateOrientation { original ->
|
||||
val currQuat = Quaternionf().setFromUnnormalized(original)
|
||||
val horzLeftVector = Vector3f(0.0f, this.upVector.y.sign, 0.0f).cross(this.forwardVector)
|
||||
val horzLeftVector = Vector3f(0.0f, -abs(this.upVector.y.sign), 0.0f).cross(this.forwardVector)
|
||||
// Avoid NaN issue when normalizing and the forwardVector and upVector are equal
|
||||
val pitchAxis = if (horzLeftVector.lengthSquared() < 0.01) this.leftVector else horzLeftVector
|
||||
|
||||
|
||||
@@ -131,6 +131,7 @@ open class PokemonProperties {
|
||||
props.species = parseSpeciesIdentifier(keyPairs)
|
||||
props.form = parseForm(keyPairs)
|
||||
props.friendship = parseIntProperty(keyPairs, listOf("friendship"))?.coerceIn(0, Cobblemon.config.maxPokemonFriendship)
|
||||
props.fullness = parseIntProperty(keyPairs, listOf("fullness"))
|
||||
props.pokeball = parseIdentifierOfRegistry(keyPairs, listOf("pokeball")) { identifier -> PokeBalls.getPokeBall(identifier)?.name?.toString() }
|
||||
props.nature = parseIdentifierOfRegistry(keyPairs, listOf("nature")) { identifier -> Natures.getNature(identifier)?.name?.toString() }
|
||||
props.ability = parseStringOfRegistry(keyPairs, listOf("ability")) { Abilities.get(it)?.name }
|
||||
@@ -326,6 +327,7 @@ open class PokemonProperties {
|
||||
var gender: Gender? = null
|
||||
var level: Int? = null
|
||||
var friendship: Int? = null
|
||||
var fullness: Int? = null
|
||||
var pokeball: String? = null
|
||||
var nature: String? = null
|
||||
var ability: String? = null
|
||||
@@ -397,6 +399,7 @@ open class PokemonProperties {
|
||||
pokemon.setFriendship(pokemon.form.baseFriendship)
|
||||
}
|
||||
}
|
||||
fullness?.let { pokemon.currentFullness = it }
|
||||
pokeball?.let { PokeBalls.getPokeBall(it.asIdentifierDefaultingNamespace())?.let { pokeball -> pokemon.caughtBall = pokeball } }
|
||||
nature?.let { Natures.getNature(it.asIdentifierDefaultingNamespace())?.let { nature -> pokemon.nature = nature } }
|
||||
customProperties.forEach { it.apply(pokemon) }
|
||||
@@ -504,6 +507,7 @@ open class PokemonProperties {
|
||||
nickname?.takeIf { it.string != pokemon.nickname?.string }?.let { return false }
|
||||
form?.takeIf { !it.equals(pokemon.form.name, true) }?.let { return false }
|
||||
friendship?.takeIf { it != pokemon.friendship }?.let { return false }
|
||||
fullness?.takeIf { it != pokemon.currentFullness }?.let { return false }
|
||||
pokeball?.takeIf { it != pokemon.caughtBall.name.toString() }?.let { return false }
|
||||
nature?.takeIf { it != pokemon.nature.name.toString() }?.let { return false }
|
||||
ability?.takeIf { it != pokemon.ability.name }?.let { return false }
|
||||
@@ -588,6 +592,7 @@ open class PokemonProperties {
|
||||
originalTrainer?.takeIf { it != properties.originalTrainer }?.let{ return false }
|
||||
originalTrainerType?.takeIf { it != properties.originalTrainerType }?.let{ return false }
|
||||
moves?.takeIf { it.any { move -> properties.moves?.none { it == move } == true } }?.let { return false }
|
||||
fullness?.takeIf { it != properties.fullness }?.let { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -636,6 +641,7 @@ open class PokemonProperties {
|
||||
nickname?.let { nbt.putString(DataKeys.POKEMON_NICKNAME, Component.Serializer.toJson(it, registryLookup)) }
|
||||
form?.let { nbt.putString(DataKeys.POKEMON_FORM_ID, it) }
|
||||
friendship?.let { nbt.putInt(DataKeys.POKEMON_FRIENDSHIP, it) }
|
||||
fullness?.let { nbt.putInt(DataKeys.POKEMON_FULLNESS, it) }
|
||||
pokeball?.let { nbt.putString(DataKeys.POKEMON_CAUGHT_BALL, it) }
|
||||
nature?.let { nbt.putString(DataKeys.POKEMON_NATURE, it) }
|
||||
ability?.let { nbt.putString(DataKeys.POKEMON_ABILITY, it) }
|
||||
@@ -667,6 +673,7 @@ open class PokemonProperties {
|
||||
nickname = if (tag.contains(DataKeys.POKEMON_NICKNAME)) Component.Serializer.fromJson(tag.getString(DataKeys.POKEMON_NICKNAME), registryLookup) else null
|
||||
form = if (tag.contains(DataKeys.POKEMON_FORM_ID)) tag.getString(DataKeys.POKEMON_FORM_ID) else null
|
||||
friendship = if (tag.contains(DataKeys.POKEMON_FRIENDSHIP)) tag.getInt(DataKeys.POKEMON_FRIENDSHIP) else null
|
||||
fullness = if (tag.contains(DataKeys.POKEMON_FULLNESS)) tag.getInt(DataKeys.POKEMON_FULLNESS) else null
|
||||
pokeball = if (tag.contains(DataKeys.POKEMON_CAUGHT_BALL)) tag.getString(DataKeys.POKEMON_CAUGHT_BALL) else null
|
||||
nature = if (tag.contains(DataKeys.POKEMON_NATURE)) tag.getString(DataKeys.POKEMON_NATURE) else null
|
||||
ability = if (tag.contains(DataKeys.POKEMON_ABILITY)) tag.getString(DataKeys.POKEMON_ABILITY) else null
|
||||
@@ -700,6 +707,7 @@ open class PokemonProperties {
|
||||
//nickname?.let { json.addProperty(DataKeys.POKEMON_NICKNAME, Text.Serialization.toJsonString(it)) }
|
||||
form?.let { json.addProperty(DataKeys.POKEMON_FORM_ID, it) }
|
||||
friendship?.let { json.addProperty(DataKeys.POKEMON_FRIENDSHIP, it) }
|
||||
fullness?.let {json.addProperty(DataKeys.POKEMON_FULLNESS, it)}
|
||||
pokeball?.let { json.addProperty(DataKeys.POKEMON_CAUGHT_BALL, it) }
|
||||
nature?.let { json.addProperty(DataKeys.POKEMON_NATURE, it) }
|
||||
ability?.let { json.addProperty(DataKeys.POKEMON_ABILITY, it) }
|
||||
@@ -732,6 +740,7 @@ open class PokemonProperties {
|
||||
//nickname = json.get(DataKeys.POKEMON_NICKNAME)?.asString?.let { Text.Serialization.fromJson(it) }
|
||||
form = json.get(DataKeys.POKEMON_FORM_ID)?.asString
|
||||
friendship = json.get(DataKeys.POKEMON_FRIENDSHIP)?.asInt
|
||||
fullness = json.get(DataKeys.POKEMON_FULLNESS)?.asInt
|
||||
pokeball = json.get(DataKeys.POKEMON_CAUGHT_BALL)?.asString
|
||||
nature = json.get(DataKeys.POKEMON_NATURE)?.asString
|
||||
ability = json.get(DataKeys.POKEMON_ABILITY)?.asString
|
||||
@@ -763,6 +772,7 @@ open class PokemonProperties {
|
||||
shiny?.let { pieces.add("shiny=$it") }
|
||||
gender?.let { pieces.add("gender=$it")}
|
||||
friendship?.let { pieces.add("friendship=$it") }
|
||||
fullness?.let {pieces.add("fullness=$it")}
|
||||
pokeball?.let { pieces.add("pokeball=$it") }
|
||||
nature?.let { pieces.add("nature=$it") }
|
||||
ability?.let { pieces.add("ability=$it") }
|
||||
|
||||
@@ -43,7 +43,6 @@ import com.cobblemon.mod.common.api.riding.stats.RidingStatDefinition
|
||||
import com.cobblemon.mod.common.api.spawning.TimeRange
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.api.types.adapters.ElementalTypeAdapter
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
@@ -54,10 +53,8 @@ import com.cobblemon.mod.common.pokemon.ai.ObtainableItemConditionAdapter
|
||||
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonEvolutionAdapter
|
||||
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonPreEvolutionAdapter
|
||||
import com.cobblemon.mod.common.pokemon.evolution.adapters.LegacyItemConditionWrapperAdapter
|
||||
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
|
||||
import com.cobblemon.mod.common.util.adapters.*
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.ifClient
|
||||
import com.google.common.collect.HashBasedTable
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
@@ -65,13 +62,11 @@ import com.google.gson.reflect.TypeToken
|
||||
import com.mojang.datafixers.util.Either
|
||||
import net.minecraft.advancements.critereon.ItemPredicate
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
import net.minecraft.tags.TagKey
|
||||
import net.minecraft.world.effect.MobEffect
|
||||
import net.minecraft.world.entity.EntityDimensions
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
@@ -151,24 +146,17 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
|
||||
val species: Collection<Species>
|
||||
get() = this.speciesByIdentifier.values
|
||||
val implemented = mutableListOf<Species>()
|
||||
val implemented: List<Species>
|
||||
get() = this.species.filter { it.implemented }
|
||||
|
||||
init {
|
||||
SpeciesAdditions.observable.subscribe {
|
||||
implemented.clear()
|
||||
this.species.forEach(Species::initialize)
|
||||
this.species.forEach {
|
||||
if (it.implemented) {
|
||||
this.implemented.add(it)
|
||||
}
|
||||
}
|
||||
this.species.forEach(Species::resolveEvolutionMoves)
|
||||
Cobblemon.showdownThread.queue {
|
||||
it.resetRegistryData("species")
|
||||
it.sendRegistryData(allShowdownSpecies(), "species")
|
||||
it.indicateSpeciesInitialized()
|
||||
// Reload this with the mod
|
||||
CobblemonHeldItemManager.load()
|
||||
Cobblemon.LOGGER.info("Loaded {} Pokémon species", this.speciesByIdentifier.size)
|
||||
this.observable.emit(this)
|
||||
}
|
||||
@@ -233,7 +221,6 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
|
||||
override fun reload(data: Map<ResourceLocation, Species>) {
|
||||
this.speciesByIdentifier.clear()
|
||||
this.implemented.clear()
|
||||
this.speciesByDex.clear()
|
||||
data.forEach { (identifier, species) ->
|
||||
species.resourceIdentifier = identifier
|
||||
@@ -241,11 +228,6 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
this.speciesByDex.remove(old.resourceIdentifier.namespace, old.nationalPokedexNumber)
|
||||
}
|
||||
this.speciesByDex.put(species.resourceIdentifier.namespace, species.nationalPokedexNumber, species)
|
||||
ifClient {
|
||||
if (species.implemented) {
|
||||
this.implemented.add(species)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,9 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.riding
|
||||
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
|
||||
import net.minecraft.world.entity.PlayerRideableJumping
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,10 +9,7 @@
|
||||
package com.cobblemon.mod.common.api.riding
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStatDefinition
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.util.adapters.RidingBehaviourSettingsAdapter
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
@@ -32,10 +29,10 @@ class RidingProperties(
|
||||
{ buffer.readEnumConstant(RidingStyle::class.java) },
|
||||
{
|
||||
val key = buffer.readResourceLocation()
|
||||
val settings = RidingBehaviourSettingsAdapter.types[key]?.getConstructor()?.newInstance()
|
||||
val behaviour = RidingBehaviourSettingsAdapter.types[key]?.getConstructor()?.newInstance()
|
||||
?: error("Unknown controller key: $key")
|
||||
settings.decode(buffer)
|
||||
settings
|
||||
behaviour.decode(buffer)
|
||||
behaviour
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -51,7 +48,10 @@ class RidingProperties(
|
||||
buffer.writeMap(
|
||||
v,
|
||||
{ _, style -> buffer.writeEnumConstant(style) },
|
||||
{ _, behaviour -> behaviour.encode(buffer) }
|
||||
{ _, behaviour ->
|
||||
buffer.writeResourceLocation(behaviour.key)
|
||||
behaviour.encode(buffer)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,8 @@ data class Seat(
|
||||
val locator: String?,
|
||||
val offset: Vec3?,
|
||||
val poseOffsets: MutableList<SeatPoseOffset>?,
|
||||
val poseAnimations: MutableList<SeatPoseAnimations>?
|
||||
val poseAnimations: MutableList<SeatPoseAnimations>?,
|
||||
val handsBusy: Boolean = false,
|
||||
) : Encodable {
|
||||
fun getOffset(poseType: PoseType) : Vec3 {
|
||||
return poseOffsets?.firstOrNull { poseType in it.poseTypes }?.offset ?: Vec3.ZERO
|
||||
@@ -40,6 +41,7 @@ data class Seat(
|
||||
buffer.writeNullable(poseAnimations) { _, v ->
|
||||
buffer.writeCollection(v) { _, animations -> animations.encode(buffer) }
|
||||
}
|
||||
buffer.writeBoolean(handsBusy)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@@ -48,7 +50,8 @@ data class Seat(
|
||||
buffer.readNullable { buffer.readString() },
|
||||
buffer.readNullable { buffer.readVec3() },
|
||||
buffer.readNullable { buffer.readList { SeatPoseOffset.decode(buffer) } },
|
||||
buffer.readNullable { buffer.readList { SeatPoseAnimations.decode(buffer) } }
|
||||
buffer.readNullable { buffer.readList { SeatPoseAnimations.decode(buffer) } },
|
||||
buffer.readBoolean()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+5
-7
@@ -10,10 +10,8 @@ package com.cobblemon.mod.common.api.riding.behaviour
|
||||
|
||||
import com.cobblemon.mod.common.api.net.Decodable
|
||||
import com.cobblemon.mod.common.api.net.Encodable
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
/**
|
||||
* Represents static settings of a riding behaviour.
|
||||
@@ -21,17 +19,17 @@ import net.minecraft.world.phys.Vec3
|
||||
* Typically this will be initialized for each pokemon form during deserialization
|
||||
* to determine how they should ride.
|
||||
*
|
||||
* These also exist in a datapacked folder, ride_settings, which are used for the fallback
|
||||
* values if non-stat settings are omitted in the pokemon form JSON.
|
||||
*
|
||||
* @author landonjw
|
||||
*/
|
||||
interface RidingBehaviourSettings: Encodable, Decodable {
|
||||
val key: ResourceLocation
|
||||
val stats: MutableMap<RidingStat, IntRange>
|
||||
|
||||
fun calculate(stat: RidingStat, boosts: Int): Float {
|
||||
fun calculate(stat: RidingStat, boostAmount: Float): Float {
|
||||
val range = stats[stat] ?: return 0F
|
||||
val boostQuotient = boosts / 255F
|
||||
return range.first + (range.last - range.first) * boostQuotient
|
||||
return range.first + boostAmount
|
||||
}
|
||||
|
||||
fun hasStat(stat: RidingStat) = stats.containsKey(stat)
|
||||
}
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ import net.minecraft.world.phys.Vec3
|
||||
* This is intended to contain mutable state that is passed between the client and server
|
||||
* or temporary state that is used during the riding process.
|
||||
*
|
||||
* This will be destroyed when the a user dismounts the Pokemon.
|
||||
* This will be destroyed when the main rider dismounts the Pokemon.
|
||||
*
|
||||
* @author landonjw
|
||||
*/
|
||||
|
||||
+13
@@ -8,13 +8,17 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.DoubleJump
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.client.MountedCameraTypeHandler
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.tags.FluidTags
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import kotlin.collections.get
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.entity.player.Player
|
||||
|
||||
/**
|
||||
* Small wrapper around a RidingBehaviour to provide sane defaults in the event that the behaviour is not active.
|
||||
@@ -49,6 +53,15 @@ class RidingController(
|
||||
if (newTransition != null && behaviourSettings != null) {
|
||||
val newState = RidingBehaviours.get(behaviourSettings.key).createDefaultState(behaviourSettings)
|
||||
context?.let { newState.stamina.set(it.state.stamina.get(), forced = true) }
|
||||
context?.let { newState.rideVelocity.set(it.state.rideVelocity.get(), forced = true) }
|
||||
val previousKey = context?.behaviour
|
||||
val newKey = behaviourSettings.key
|
||||
for (passenger in entity.passengers) {
|
||||
if (passenger is Player && passenger !is ServerPlayer) {
|
||||
MountedCameraTypeHandler.handleTransition(previousKey ?: continue, newKey)
|
||||
}
|
||||
}
|
||||
|
||||
context = ActiveRidingContext(behaviourSettings.key, behaviourSettings, newState, newTransition)
|
||||
lastTransitionAge = entity.ticksLived
|
||||
} else {
|
||||
|
||||
+174
-80
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.air
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.MoLangMath.lerp
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
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
|
||||
@@ -23,15 +24,16 @@ import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import kotlin.math.*
|
||||
|
||||
class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
@@ -48,8 +50,22 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
val poseProvider = PoseProvider<BirdSettings, BirdState>(PoseType.HOVER)
|
||||
.with(PoseOption(PoseType.FLY) { _, state, _ -> state.rideVelocity.get().z > 0.2 })
|
||||
|
||||
val globalBird: BirdSettings
|
||||
get() = CobblemonRideSettings.bird
|
||||
|
||||
override fun isActive(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity): Boolean {
|
||||
return true
|
||||
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
|
||||
//Need to check other fluids
|
||||
if (vehicle.isInWater || vehicle.isUnderWater) {
|
||||
return@any false
|
||||
}
|
||||
//This might not actually work, depending on what the yPos actually is. yPos of the middle of the entity? the feet?
|
||||
if (it.y.toDouble() == (vehicle.position().y)) {
|
||||
val blockState = vehicle.level().getBlockState(it.below())
|
||||
return@any !(!blockState.isAir && blockState.fluidState.isEmpty)
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun pose(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity): PoseType {
|
||||
@@ -62,10 +78,37 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
|
||||
override fun tick(
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
) {
|
||||
if(vehicle.level().isClientSide) {
|
||||
tickStamina(settings, state, vehicle, driver)
|
||||
checkUpsideDown(state, vehicle, driver)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkUpsideDown(
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
) {
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
if (cos(controller.roll.toRadians()) < 0.0) {
|
||||
val upsideDownRate = 1.0 / (2.0 * 20.0) // 2 seconds to max out upsideDown force
|
||||
state.currUpsideDownForce.set( min(1.0, state.currUpsideDownForce.get() + upsideDownRate * abs(cos(controller.roll.toRadians()))))
|
||||
} else if (state.stamina.get() == 0.0f) { // checks stamina depletion and forces dive if depleted
|
||||
val upsideDownRate = 1.0 / (2.0 * 20.0) // 1 seconds to max out
|
||||
state.currUpsideDownForce.set( min(1.0, state.currUpsideDownForce.get() + upsideDownRate))
|
||||
} else { // Its right side up or has stamina so reset it
|
||||
state.currUpsideDownForce.set(0.0)
|
||||
}
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
|
||||
fun tickStamina(
|
||||
@@ -79,11 +122,12 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
// 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 totalStam = vehicle.runtime.resolveDouble(settings.staminaExpr) * 20.0f
|
||||
val totalStam = vehicle.runtime.resolveDouble(settings.staminaExpr ?: globalBird.staminaExpr!!) * 20.0f
|
||||
val stamDrainRate = (1.0f / totalStam).toFloat()
|
||||
|
||||
val newStam = if (driver.zza > 0.0) max(0.0f,stam - stamDrainRate)
|
||||
else min(1.0f,stam + stamDrainRate)
|
||||
else if (!state.gliding.get()) max(0.0f,stam - stamDrainRate * 0.5f) // if hovering half the stam drain
|
||||
else min(1.0f,stam + stamDrainRate * 0.2f)
|
||||
|
||||
state.stamina.set(newStam)
|
||||
}
|
||||
@@ -104,8 +148,6 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
var yVel = 0.0
|
||||
var leftForce = 0.0
|
||||
var upForce = 0.0
|
||||
var forwardForce = 0.0
|
||||
|
||||
@@ -124,7 +166,10 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
forwardForce += sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().y
|
||||
}
|
||||
|
||||
val velocity = Vec3(state.rideVelocity.get().x , upForce, forwardForce)
|
||||
// The downward force used to encourage players to stop flying upside down.
|
||||
val extraDownwardForce = state.currUpsideDownForce.get().pow(2) * -0.3 // 6 blocks a second downward
|
||||
|
||||
val velocity = Vec3(state.rideVelocity.get().x , upForce + extraDownwardForce, forwardForce)
|
||||
return velocity
|
||||
}
|
||||
|
||||
@@ -133,9 +178,9 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
*/
|
||||
fun calculateRideSpaceVel(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity, driver: Player) {
|
||||
// retrieve stats
|
||||
val topSpeed = (vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0)
|
||||
val accel = topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr) * 20.0)
|
||||
val glideTopSpeed = topSpeed * vehicle.runtime.resolveDouble(settings.glidespeedExpr)
|
||||
val topSpeed = (vehicle.runtime.resolveDouble(settings.speedExpr ?: globalBird.speedExpr!!) / 20.0)
|
||||
val accel = topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalBird.accelerationExpr!!) * 20.0)
|
||||
val glideTopSpeed = topSpeed * vehicle.runtime.resolveDouble(settings.glideSpeedExpr ?: globalBird.glideSpeedExpr!!)
|
||||
var glideSpeedChange = 0.0
|
||||
var activeInput = false
|
||||
|
||||
@@ -219,7 +264,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
|
||||
//Check if the ride should be gliding
|
||||
if (driver.isLocalPlayer) {
|
||||
if (activeInput && state.stamina.get() > 0.0) {
|
||||
if ((activeInput && state.stamina.get() > 0.0) || state.rideVelocity.get().z < 0.2) {
|
||||
state.gliding.set(false)
|
||||
} else {
|
||||
state.gliding.set(true)
|
||||
@@ -244,21 +289,23 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0
|
||||
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())).pow(2) * sin(Math.toRadians(controller.roll.toDouble())).sign
|
||||
val trueYawDelt = yawDeltaDeg * abs(cos(Math.toRadians(controller.pitch.toDouble()))) * sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed))
|
||||
val yawDeltaDeg = deltaTime * handling * sin(Math.toRadians(controller.roll.toDouble())) //* sin(Math.toRadians(controller.roll.toDouble())).sign
|
||||
val trueYawDelt = yawDeltaDeg * abs(cos(Math.toRadians(controller.pitch.toDouble())))// * sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed))
|
||||
|
||||
// Dampen yaw when upside down
|
||||
val yawDampen = (1 - abs(min(cos(controller.roll.toRadians()),0.0f)))
|
||||
val yawDampen = (1 - abs(min(cos(controller.roll.toRadians()),0.0f)).pow(2))
|
||||
controller.applyGlobalYaw(trueYawDelt.toFloat() * yawDampen)
|
||||
|
||||
// Correct orientation when at low speeds
|
||||
correctOrientation(settings, state, vehicle, controller, deltaTime)
|
||||
|
||||
controller.applyGlobalPitch(state.currPitchCorrectionForce.get().toRadians())
|
||||
|
||||
//yaw, pitch, roll
|
||||
return Vec3(0.0, state.currPitchCorrectionForce.get(), state.currRollCorrectionForce.get())
|
||||
return Vec3(0.0, 0.0, state.currRollCorrectionForce.get())
|
||||
}
|
||||
|
||||
private fun correctOrientation(
|
||||
@@ -270,11 +317,11 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
) {
|
||||
// Calculate correcting roll force.
|
||||
// Mult by cos(pitch) to ensure that it doesn't roll correct at when diving or climbing
|
||||
val rollCorrectionTimer = vehicle.runtime.resolveDouble(settings.timeToRollCorrect)
|
||||
val maxRollCorrectionRate = if (state.rideVelocity.get().length() < 0.1) 15.0f else 8.0f * (cos(controller.pitch.toRadians())).pow(2) * ((state.noInputTime.get() - rollCorrectionTimer) * 2).coerceIn(0.0, 1.0).toFloat()
|
||||
val 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.noInputTime.get() > rollCorrectionTimer) {
|
||||
if(state.rideVelocity.get().length() < 0.1 || state.noInputTimeRoll.get() > rollCorrectionTimer) {
|
||||
val arrivalInfluence = (min(rollArrivalDeg,abs(controller.roll).toDouble()) / rollArrivalDeg)
|
||||
val desiredRollForce = (0 - controller.roll).sign * maxRollCorrectionRate * arrivalInfluence
|
||||
var steeredRollForce = desiredRollForce - state.currRollCorrectionForce.get()
|
||||
@@ -296,13 +343,18 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
}
|
||||
|
||||
//Calculate correcting pitch force.
|
||||
val maxPitchCorrectionRate = 15
|
||||
val maxPitchForce = maxPitchCorrectionRate / 40.0
|
||||
val pitchArrivalDeg = 90.0
|
||||
if(state.rideVelocity.get().length() < 0.1) {
|
||||
val arrivalInfluence = (min(pitchArrivalDeg,abs(controller.pitch).toDouble()) / pitchArrivalDeg)
|
||||
val desiredPitchForce = (0 - controller.pitch).sign * maxPitchCorrectionRate * arrivalInfluence
|
||||
var steeredPitchForce = desiredPitchForce - state.currPitchCorrectionForce.get()
|
||||
val maxPitchCorrectionRate = if (state.currUpsideDownForce.get() != 0.0) 8f * state.currUpsideDownForce.get() else 15f
|
||||
val maxPitchForce = maxPitchCorrectionRate.toFloat() / 40.0f
|
||||
val pitchArrivalDeg = 45.0f
|
||||
var desiredPitch = if (state.currUpsideDownForce.get() != 0.0) 90.0 else 0.0
|
||||
// if (cos(controller.roll.toRadians()) < 0.0) {
|
||||
// desiredPitch *= -1 // If not rolled over then flip the pitch force. There are better ways to do this :(
|
||||
// }
|
||||
|
||||
if((state.rideVelocity.get().length() < 0.1 && controller.pitch < 0.0) || state.currUpsideDownForce.get() != 0.0) {
|
||||
val arrivalInfluence = (min(pitchArrivalDeg,abs(Mth.wrapDegrees(desiredPitch.toFloat().angleDifference(controller.pitch)))) / pitchArrivalDeg)
|
||||
val desiredPitchForce = maxPitchCorrectionRate.toFloat() * arrivalInfluence
|
||||
var steeredPitchForce = (desiredPitchForce - state.currPitchCorrectionForce.get())
|
||||
|
||||
// Ensure the 'steering' correction force doesn't exceed our maxForce
|
||||
if (abs(steeredPitchForce) > maxPitchForce) {
|
||||
@@ -333,12 +385,23 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
// Begin the roll correction time counter if not enough mouse input
|
||||
// is detected
|
||||
if (abs(mouseX) < 1 && abs(mouseY) < 1) {
|
||||
state.noInputTime.set(state.noInputTime.get() + deltaTime)
|
||||
if (mouseX < 1 && abs(mouseY) < 1) {
|
||||
state.noInputTimeRoll.set(state.noInputTimeRoll.get() + deltaTime)
|
||||
} else if (cos(controller.roll.toRadians()) < 0.1) {
|
||||
state.noInputTimeRoll.set(state.noInputTimeRoll.get() + deltaTime * 0.5)
|
||||
}
|
||||
else {
|
||||
state.noInputTimeRoll.set(0.0)
|
||||
}
|
||||
|
||||
if (abs(mouseY) < 1) {
|
||||
state.noInputTimePitch.set(state.noInputTimePitch.get() + deltaTime)
|
||||
} else {
|
||||
state.noInputTime.set(0.0)
|
||||
state.noInputTimePitch.set(0.0)
|
||||
}
|
||||
|
||||
return when {
|
||||
@@ -387,8 +450,8 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
// Set roll to zero if transitioning to noroll config
|
||||
controller.rotateRoll(controller.roll * -1.0f)
|
||||
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
|
||||
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult)
|
||||
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
|
||||
@@ -432,11 +495,11 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0
|
||||
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult)
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalBird.speedExpr!!) / 20.0
|
||||
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult ?: globalBird.mouseInputMult!!)
|
||||
|
||||
//Smooth out mouse input.
|
||||
val smoothingSpeed = 4.0
|
||||
val smoothingSpeed = 2.0
|
||||
val mouseXc = (mouseX).coerceIn(-45.0, 45.0) * mouseInputMult
|
||||
// When upside down apply a pitching force to nudge the player towards a dive. This reduces the 'weirdness'
|
||||
// of being upside down
|
||||
@@ -445,26 +508,31 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1, deltaTime * smoothingSpeed);
|
||||
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1, deltaTime * smoothingSpeed);
|
||||
val currSpeed = state.rideVelocity.get().length()
|
||||
val currHorzSpeed = state.rideVelocity.get().horizontalDistance()
|
||||
val hoverSpeed = 0.1
|
||||
|
||||
// Yaw locally when at or below hoverSpeed
|
||||
val localYaw = if (currSpeed < hoverSpeed) xInput * (1 - sqrt(RidingBehaviour.scaleToRange(currSpeed, hoverSpeed, topSpeed))).coerceIn(0.0, 1.0)
|
||||
else xInput * 0.4 * cos(controller.roll.toRadians()).coerceIn(0.0f, 1.0f)
|
||||
// Yaw locally when at or below hoverSpeed but not when pitched up or rolled over
|
||||
val yawDampen = abs(cos(controller.pitch.toRadians())).pow(2) * abs(cos(controller.roll.toRadians())).pow(2)
|
||||
val localYaw = if (currHorzSpeed < hoverSpeed) xInput * (1 - sqrt(RidingBehaviour.scaleToRange(currHorzSpeed, hoverSpeed, topSpeed))).coerceIn(0.0, 1.0)
|
||||
else 0.0 //xInput * 0.4 * cos(controller.roll.toRadians()).coerceIn(0.0f, 1.0f) * yawDampen
|
||||
|
||||
// Pitch locally up or down depending upon a number of factors:
|
||||
// - Reduce pitch substantially when rolled and in a steep yaw. This prevents the player from ignoring a slow handling stat
|
||||
// - 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)
|
||||
val p = 1.0 - vehicle.runtime.resolveDouble(settings.horizontalPitchExpr ?: globalBird.horizontalPitchExpr!!)
|
||||
var localPitch = yInput * (1 - abs(sin(controller.roll.toRadians())).pow(2) * p * hypot(controller.forwardVector.x.toDouble(), controller.forwardVector.z.toDouble()).toFloat())
|
||||
localPitch *= if (currSpeed > hoverSpeed) RidingBehaviour.scaleToRange(currSpeed, hoverSpeed, topSpeed)
|
||||
localPitch *= if (currSpeed > hoverSpeed || yInput > 0.0) RidingBehaviour.scaleToRange(currSpeed, 0.0, topSpeed).coerceIn(0.2, 1.0)
|
||||
else 0.0
|
||||
|
||||
// Roll less when slow and not at all when at hoverSpeed or lower.
|
||||
// Apply a roll righting force when trying to pitch hard while rolled sideways. This nudges the player towards
|
||||
// pitching globally
|
||||
val pitchInfluencedRollCorrection = 0.4*(abs(yInput - localPitch) * controller.roll.sign * -1.0)
|
||||
val rollForce = if (currSpeed > hoverSpeed) xInput * sqrt((RidingBehaviour.scaleToRange(currSpeed, hoverSpeed, topSpeed))) + pitchInfluencedRollCorrection
|
||||
val pitchInfluencedRollCorrection = 0.0
|
||||
val howRolledAmI = (( cos(controller.roll.toRadians()) - 1).coerceIn(-1.0f, 0.0f) * -2).coerceIn(0.0f, 0.7f) //Coerce into how much roll you want to let in in the deadzone
|
||||
val inputInRolledDir = if (xInput.sign.toFloat() == controller.roll.sign) 1.0 else -cos(controller.roll.toRadians()).coerceIn(-1.0f, 0.0f).toDouble()
|
||||
val rollDampen = (1 - howRolledAmI * abs(cos(controller.pitch.toRadians())).pow(2) * inputInRolledDir)
|
||||
val rollForce = if (currHorzSpeed > hoverSpeed) xInput * sqrt(abs(RidingBehaviour.scaleToRange(currHorzSpeed, hoverSpeed, topSpeed)).coerceIn(0.0,1.0)) * rollDampen + pitchInfluencedRollCorrection
|
||||
else 0.0
|
||||
|
||||
//yaw, pitch, roll
|
||||
@@ -515,8 +583,8 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0
|
||||
val glideTopSpeed = topSpeed * vehicle.runtime.resolveDouble(settings.glidespeedExpr)
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalBird.speedExpr!!) / 20.0
|
||||
val glideTopSpeed = topSpeed * vehicle.runtime.resolveDouble(settings.glideSpeedExpr ?: globalBird.glideSpeedExpr!!)
|
||||
val currSpeed = state.rideVelocity.get().length()
|
||||
|
||||
// Must I ensure that topspeed is greater than minimum?
|
||||
@@ -578,6 +646,17 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun damageOnCollision(
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity,
|
||||
impactVec: Vec3
|
||||
): Boolean {
|
||||
if (!state.gliding.get()) return false
|
||||
val impactSpeed = impactVec.horizontalDistance().toFloat() * 10f
|
||||
return vehicle.causeFallDamage(impactSpeed, 1f, vehicle.damageSources().flyIntoWall())
|
||||
}
|
||||
|
||||
override fun getRideSounds(
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
@@ -593,57 +672,56 @@ class BirdSettings : RidingBehaviourSettings {
|
||||
override val key = BirdBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var infiniteAltitude: Expression = "false".asExpression()
|
||||
var infiniteAltitude: Expression? = null
|
||||
private set
|
||||
|
||||
var infiniteStamina: Expression = "false".asExpression()
|
||||
var infiniteStamina: Expression? = null
|
||||
private set
|
||||
|
||||
var timeToRollCorrect: Expression = "0.5".asExpression()
|
||||
var timeToRollCorrect: Expression? = null
|
||||
private set
|
||||
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 90.0, 45.0)".asExpression()
|
||||
var horizontalPitchExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 0.2, 0.1)".asExpression()
|
||||
var mouseInputMult: Expression = "q.get_ride_stats('SKILL', 'AIR', 1.2, 0.8)".asExpression()
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 14.0, 8.0)".asExpression()
|
||||
var handlingExpr: Expression? = null
|
||||
var horizontalPitchExpr: Expression? = null
|
||||
var mouseInputMult: Expression? = null
|
||||
var speedExpr: Expression? = null
|
||||
// Seconds from stationary to top speed
|
||||
var accelerationExpr: Expression = "q.get_ride_stats('ACCELERATION', 'AIR', 2.0, 5.0)".asExpression()
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 22.0, 12.0)".asExpression()
|
||||
var accelerationExpr: Expression? = null
|
||||
var staminaExpr: Expression? = null
|
||||
|
||||
var glidespeedExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 2.2, 1.5)".asExpression()
|
||||
var glideSpeedExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(infiniteAltitude)
|
||||
buffer.writeExpression(infiniteStamina)
|
||||
buffer.writeExpression(glidespeedExpr)
|
||||
buffer.writeExpression(handlingExpr)
|
||||
buffer.writeExpression(horizontalPitchExpr)
|
||||
buffer.writeExpression(mouseInputMult)
|
||||
buffer.writeExpression(speedExpr)
|
||||
buffer.writeExpression(accelerationExpr)
|
||||
buffer.writeExpression(staminaExpr)
|
||||
buffer.writeExpression(timeToRollCorrect)
|
||||
buffer.writeNullableExpression(infiniteAltitude)
|
||||
buffer.writeNullableExpression(infiniteStamina)
|
||||
buffer.writeNullableExpression(glideSpeedExpr)
|
||||
buffer.writeNullableExpression(handlingExpr)
|
||||
buffer.writeNullableExpression(horizontalPitchExpr)
|
||||
buffer.writeNullableExpression(mouseInputMult)
|
||||
buffer.writeNullableExpression(speedExpr)
|
||||
buffer.writeNullableExpression(accelerationExpr)
|
||||
buffer.writeNullableExpression(staminaExpr)
|
||||
buffer.writeNullableExpression(timeToRollCorrect)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
infiniteAltitude = buffer.readExpression()
|
||||
infiniteStamina = buffer.readExpression()
|
||||
glidespeedExpr = buffer.readExpression()
|
||||
handlingExpr = buffer.readExpression()
|
||||
horizontalPitchExpr = buffer.readExpression()
|
||||
mouseInputMult = buffer.readExpression()
|
||||
speedExpr = buffer.readExpression()
|
||||
accelerationExpr = buffer.readExpression()
|
||||
staminaExpr = buffer.readExpression()
|
||||
timeToRollCorrect = buffer.readExpression()
|
||||
infiniteAltitude = buffer.readNullableExpression()
|
||||
infiniteStamina = buffer.readNullableExpression()
|
||||
glideSpeedExpr = buffer.readNullableExpression()
|
||||
handlingExpr = buffer.readNullableExpression()
|
||||
horizontalPitchExpr = buffer.readNullableExpression()
|
||||
mouseInputMult = buffer.readNullableExpression()
|
||||
speedExpr = buffer.readNullableExpression()
|
||||
accelerationExpr = buffer.readNullableExpression()
|
||||
staminaExpr = buffer.readNullableExpression()
|
||||
timeToRollCorrect = buffer.readNullableExpression()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -652,11 +730,12 @@ class BirdState : RidingBehaviourState() {
|
||||
var lastGlide = ridingState(-100L, Side.CLIENT)
|
||||
var currRollCorrectionForce = ridingState(0.0, Side.CLIENT)
|
||||
var currPitchCorrectionForce = ridingState(0.0, Side.CLIENT)
|
||||
var noInputTime = ridingState(0.0, Side.CLIENT)
|
||||
var noInputTimeRoll = ridingState(0.0, Side.CLIENT)
|
||||
var noInputTimePitch = ridingState(0.0, Side.CLIENT)
|
||||
var currUpsideDownForce = ridingState(0.0, Side.CLIENT)
|
||||
|
||||
override fun encode(buffer: FriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
buffer.writeFloat(stamina.get())
|
||||
buffer.writeBoolean(gliding.get())
|
||||
}
|
||||
|
||||
@@ -671,7 +750,8 @@ class BirdState : RidingBehaviourState() {
|
||||
lastGlide.set(-100L, forced = true)
|
||||
currRollCorrectionForce.set(0.0, forced = true)
|
||||
currPitchCorrectionForce.set(0.0, forced = true)
|
||||
noInputTime.set(0.0, forced = true)
|
||||
noInputTimeRoll.set(0.0, forced = true)
|
||||
noInputTimePitch.set(0.0, forced = true)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
@@ -685,7 +765,8 @@ class BirdState : RidingBehaviourState() {
|
||||
it.lastGlide.set(this.lastGlide.get(), forced = true)
|
||||
it.currRollCorrectionForce.set(this.currRollCorrectionForce.get(), forced = true)
|
||||
it.currPitchCorrectionForce.set(this.currPitchCorrectionForce.get(), forced = true)
|
||||
it.noInputTime.set(this.noInputTime.get(), forced = true)
|
||||
it.noInputTimeRoll.set(this.noInputTimeRoll.get(), forced = true)
|
||||
it.noInputTimePitch.set(this.noInputTimePitch.get(), forced = true)
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
@@ -694,3 +775,16 @@ class BirdState : RidingBehaviourState() {
|
||||
return super.shouldSync(previous)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the shortest difference between two angles.
|
||||
* The result will be in the range [-180, 180].
|
||||
* A positive result indicates a clockwise direction from this angle to the other,
|
||||
* a negative result indicates a counter-clockwise direction.
|
||||
*/
|
||||
fun Float.angleDifference(other: Float): Float {
|
||||
var diff = other - this
|
||||
while (diff <= -180F) diff += 360F
|
||||
while (diff > 180F) diff -= 360F
|
||||
return diff
|
||||
}
|
||||
|
||||
+16
-14
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.air
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
|
||||
@@ -37,6 +38,9 @@ class GliderBehaviour : RidingBehaviour<GliderSettings, RidingBehaviourState> {
|
||||
|
||||
override val key = KEY
|
||||
|
||||
val globalGlider: GliderSettings
|
||||
get() = CobblemonRideSettings.glider
|
||||
|
||||
override fun getRidingStyle(settings: GliderSettings, state: RidingBehaviourState): RidingStyle {
|
||||
return RidingStyle.AIR
|
||||
}
|
||||
@@ -53,7 +57,7 @@ class GliderBehaviour : RidingBehaviour<GliderSettings, RidingBehaviourState> {
|
||||
}
|
||||
|
||||
override fun speed(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity, driver: Player): Float {
|
||||
return vehicle.runtime.resolveFloat(settings.speed)
|
||||
return vehicle.runtime.resolveFloat(settings.speed ?: globalGlider.speed!!)
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
@@ -72,10 +76,9 @@ class GliderBehaviour : RidingBehaviour<GliderSettings, RidingBehaviourState> {
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
val xVector = if (vehicle.runtime.resolveBoolean(settings.canStrafe)) driver.xxa.toDouble() else 0.0
|
||||
val yVector = -vehicle.runtime.resolveDouble(settings.glideSpeed)
|
||||
val xVector = if (vehicle.runtime.resolveBoolean(settings.canStrafe ?: globalGlider.canStrafe!!)) driver.xxa.toDouble() else 0.0
|
||||
val yVector = -vehicle.runtime.resolveDouble(settings.glideSpeed ?: globalGlider.glideSpeed!!)
|
||||
val zVector = driver.zza.toDouble()
|
||||
val speedStat = settings.calculate(RidingStat.SPEED, 0)
|
||||
return Vec3(xVector, yVector, zVector)
|
||||
}
|
||||
|
||||
@@ -196,31 +199,30 @@ class GliderSettings : RidingBehaviourSettings {
|
||||
override val key = GliderBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var glideSpeed: Expression = "0.1".asExpression()
|
||||
var glideSpeed: Expression? = null
|
||||
private set
|
||||
|
||||
var speed: Expression = "1.0".asExpression()
|
||||
var speed: Expression? = null
|
||||
private set
|
||||
|
||||
var canStrafe: Expression = "false".asExpression()
|
||||
var canStrafe: Expression? = null
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(glideSpeed)
|
||||
buffer.writeExpression(speed)
|
||||
buffer.writeExpression(canStrafe)
|
||||
buffer.writeNullableExpression(glideSpeed)
|
||||
buffer.writeNullableExpression(speed)
|
||||
buffer.writeNullableExpression(canStrafe)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
glideSpeed = buffer.readExpression()
|
||||
speed = buffer.readExpression()
|
||||
canStrafe = buffer.readExpression()
|
||||
glideSpeed = buffer.readNullableExpression()
|
||||
speed = buffer.readNullableExpression()
|
||||
canStrafe = buffer.readNullableExpression()
|
||||
}
|
||||
}
|
||||
|
||||
+27
-19
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.air
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
|
||||
@@ -21,7 +22,16 @@ 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.*
|
||||
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.readNullableExpression
|
||||
import com.cobblemon.mod.common.util.readRidingStats
|
||||
import com.cobblemon.mod.common.util.resolveDouble
|
||||
import com.cobblemon.mod.common.util.writeNullableExpression
|
||||
import com.cobblemon.mod.common.util.writeRidingStats
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sin
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.SmoothDouble
|
||||
@@ -30,9 +40,6 @@ import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sin
|
||||
|
||||
class HelicopterBehaviour : RidingBehaviour<HelicopterSettings, RidingBehaviourState> {
|
||||
companion object {
|
||||
@@ -41,6 +48,8 @@ class HelicopterBehaviour : RidingBehaviour<HelicopterSettings, RidingBehaviourS
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
val globalHelicopter: HelicopterSettings
|
||||
get() = CobblemonRideSettings.helicopter
|
||||
|
||||
override fun getRidingStyle(settings: HelicopterSettings, state: RidingBehaviourState): RidingStyle {
|
||||
return RidingStyle.AIR
|
||||
@@ -93,8 +102,8 @@ class HelicopterBehaviour : RidingBehaviour<HelicopterSettings, RidingBehaviourS
|
||||
val runtime = vehicle.runtime
|
||||
|
||||
//If the player is not modulating height then hover
|
||||
var yVel = if (driver.jumping) runtime.resolveDouble(settings.verticalVelocity)
|
||||
else if (driver.isShiftKeyDown) -runtime.resolveDouble(settings.verticalVelocity)
|
||||
var yVel = if (driver.jumping) runtime.resolveDouble(settings.verticalVelocity ?: globalHelicopter.verticalVelocity!!)
|
||||
else if (driver.isShiftKeyDown) -runtime.resolveDouble(settings.verticalVelocity ?: globalHelicopter.verticalVelocity!!)
|
||||
else (0.0)
|
||||
|
||||
yVel *= 0.25
|
||||
@@ -290,36 +299,35 @@ class HelicopterSettings : RidingBehaviourSettings {
|
||||
override val key = HelicopterBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var gravity: Expression = "1.0".asExpression()
|
||||
var gravity: Expression? = null
|
||||
private set
|
||||
|
||||
var horizontalAcceleration: Expression = "0.1".asExpression()
|
||||
var horizontalAcceleration: Expression? = null
|
||||
private set
|
||||
|
||||
var verticalVelocity: Expression = "1.0".asExpression()
|
||||
var verticalVelocity: Expression? = null
|
||||
private set
|
||||
|
||||
var speed: Expression = "1.0".asExpression()
|
||||
var speed: Expression? = null
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(gravity)
|
||||
buffer.writeExpression(horizontalAcceleration)
|
||||
buffer.writeExpression(verticalVelocity)
|
||||
buffer.writeExpression(speed)
|
||||
buffer.writeNullableExpression(gravity)
|
||||
buffer.writeNullableExpression(horizontalAcceleration)
|
||||
buffer.writeNullableExpression(verticalVelocity)
|
||||
buffer.writeNullableExpression(speed)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
gravity = buffer.readExpression()
|
||||
horizontalAcceleration = buffer.readExpression()
|
||||
verticalVelocity = buffer.readExpression()
|
||||
speed = buffer.readExpression()
|
||||
gravity = buffer.readNullableExpression()
|
||||
horizontalAcceleration = buffer.readNullableExpression()
|
||||
verticalVelocity = buffer.readNullableExpression()
|
||||
speed = buffer.readNullableExpression()
|
||||
}
|
||||
}
|
||||
|
||||
+108
-85
@@ -9,10 +9,9 @@
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types.air
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseState
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
@@ -27,7 +26,6 @@ import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.level.ClipContext
|
||||
@@ -44,6 +42,8 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
val globalHover: HoverSettings
|
||||
get() = CobblemonRideSettings.hover
|
||||
|
||||
override fun getRidingStyle(settings: HoverSettings, state: HoverState): RidingStyle {
|
||||
return RidingStyle.AIR
|
||||
@@ -87,17 +87,28 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
checkTooHigh(settings, state, vehicle)
|
||||
tickStamina(settings, state, vehicle)
|
||||
return state.speed.get().toFloat()
|
||||
}
|
||||
|
||||
override fun tick(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
) {
|
||||
if(vehicle.level().isClientSide) {
|
||||
tickStamina(settings, state, vehicle)
|
||||
checkTooHigh(settings, state, vehicle)
|
||||
}
|
||||
}
|
||||
|
||||
fun checkTooHigh(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity,
|
||||
) {
|
||||
val heightLimit = vehicle.runtime.resolveDouble(settings.jumpExpr)
|
||||
val heightLimit = vehicle.runtime.resolveDouble(settings.jumpExpr ?: globalHover.jumpExpr!!)
|
||||
val pos = vehicle.position()
|
||||
val level = Minecraft.getInstance().player?.level() ?: return
|
||||
val hit = level.clip(
|
||||
@@ -118,11 +129,13 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity
|
||||
) {
|
||||
if (vehicle.runtime.resolveBoolean(settings.infiniteStamina ?: globalHover.infiniteStamina!!)) {
|
||||
return
|
||||
}
|
||||
val stam = state.stamina.get()
|
||||
val stamDrainRate = (1.0f / vehicle.runtime.resolveDouble(settings.staminaExpr)).toFloat() / 20.0f
|
||||
|
||||
val newStam = if (state.tooHigh.get()) max(0.0f,stam - stamDrainRate)
|
||||
else min(1.0f,stam + stamDrainRate * 2)
|
||||
val stamDrainRate = (1.0f / vehicle.runtime.resolveDouble(settings.staminaExpr ?: globalHover.staminaExpr!!)).toFloat() / 20.0f
|
||||
val stamReplenishRate = (1.0f / vehicle.runtime.resolveDouble(settings.stamReplenishTimeSeconds ?: globalHover.stamReplenishTimeSeconds!!) ).toFloat() / 20.0f // 2 seconds to replenish a full bar of stamina
|
||||
val newStam = if (state.tooHigh.get()) max(0.0f, stam - stamDrainRate) else min(1.0f,stam + stamReplenishRate)
|
||||
|
||||
state.stamina.set(newStam)
|
||||
}
|
||||
@@ -142,11 +155,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
) {
|
||||
val f = Mth.wrapDegrees(driver.yRot - vehicle.yRot)
|
||||
val lookYawLimit = 90.0f
|
||||
val g = Mth.clamp(f, -lookYawLimit, lookYawLimit)
|
||||
driver.yRotO += g - f
|
||||
driver.yRot = driver.yRot + g - f
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -157,9 +166,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
driver: LivingEntity
|
||||
): Vec2 {
|
||||
val turnAmount = calcRotAmount(settings, state, vehicle, driver)
|
||||
|
||||
return Vec2(driver.xRot, vehicle.yRot + turnAmount )
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -174,26 +181,19 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Float {
|
||||
//In degrees per second
|
||||
val handling = 220.0
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalHover.speedExpr!!) / 20.0
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalHover.handlingExpr!!) / 20.0
|
||||
val maxYawDiff = 90.0f
|
||||
|
||||
//Normalize the current rotation diff
|
||||
val rotDiff = Mth.wrapDegrees(driver.yRot - vehicle.yRot)
|
||||
val rotDiffNorm = rotDiff / maxYawDiff
|
||||
// Normalize the current rotation diff
|
||||
val rotDiff = Mth.wrapDegrees(driver.yRot - vehicle.yRot).coerceIn(-maxYawDiff,maxYawDiff)
|
||||
val rotDiffInfl = sqrt(abs(rotDiff / maxYawDiff)) * rotDiff.sign
|
||||
|
||||
//Take the square root so that the ride levels out quicker when at lower differences between entity
|
||||
//y and driver y
|
||||
//This influences the speed of the turn based on how far in one direction you're looking
|
||||
val rotDiffMod = (sqrt(abs(rotDiffNorm)) * rotDiffNorm.sign)
|
||||
var turnRate = (handling.toFloat()) * rotDiffInfl
|
||||
turnRate -= (turnRate * 0.5f) * (RidingBehaviour.scaleToRange(vehicle.deltaMovement.length(), 0.0, topSpeed).coerceIn(0.0, 1.0)).pow(2).toFloat()
|
||||
|
||||
//Take the inverse so that you turn less at higher speeds
|
||||
val normSpeed = 1.0f // = 1.0f - 0.5f*RidingBehaviour.scaleToRange(state.rideVelocityocity.length(), 0.0, topSpeed).toFloat()
|
||||
|
||||
val turnRate = (handling.toFloat() / 20.0f)
|
||||
|
||||
//Ensure you only ever rotate as much difference as there is between the angles.
|
||||
val turnSpeed = turnRate * rotDiffMod * normSpeed
|
||||
// Ensure you only ever rotate as much difference as there is between the angles.
|
||||
val turnSpeed = turnRate
|
||||
val rotAmount = turnSpeed.coerceIn(-abs(rotDiff), abs(rotDiff))
|
||||
|
||||
return rotAmount
|
||||
@@ -217,10 +217,10 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
driver: Player
|
||||
): Vec3 {
|
||||
// Convert these to their tick values
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr) / 400.0
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalHover.speedExpr!!) / 20.0
|
||||
val accel = topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalHover.accelerationExpr!!) * 20.0)
|
||||
|
||||
var newVelocity = vehicle.deltaMovement
|
||||
var newVelocity = state.rideVelocity.get()
|
||||
|
||||
// Check for collisions and if found, update the speed to reflect it
|
||||
if (vehicle.verticalCollision || vehicle.horizontalCollision) {
|
||||
@@ -230,59 +230,54 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
// align the velocity vector to be in local vehicle space
|
||||
val yawAligned = Matrix3f().rotateY(vehicle.yRot.toRadians())
|
||||
newVelocity = (newVelocity.toVector3f().mul(yawAligned)).toVec3d()
|
||||
if (newVelocity.length() < 0.001) {
|
||||
// Zero out the vector to ignore lingering small values that influence
|
||||
// direction of movement when scaled by stored speed value.
|
||||
newVelocity = Vec3.ZERO
|
||||
}
|
||||
newVelocity = newVelocity.normalize().scale(state.speed.get())
|
||||
|
||||
// Vertical movement based on driver input.
|
||||
val vertInput = when {
|
||||
driver.jumping && state.stamina.get() != 0.0f -> 1.0
|
||||
state.stamina.get() == 0.0f -> -1.0
|
||||
driver.jumping -> 1.0
|
||||
driver.isShiftKeyDown -> -1.0
|
||||
else -> 0.0
|
||||
}
|
||||
|
||||
// Normalize and scale to accel so that diagonal movement
|
||||
// doesn't speed up the ride more than input in one direction
|
||||
val inputVel = Vec3(
|
||||
var inputVel = Vec3(
|
||||
driver.xxa.sign.toDouble(),
|
||||
vertInput,
|
||||
driver.zza.sign.toDouble()
|
||||
).normalize().scale(accel)
|
||||
|
||||
// Scale down input force that opposes our current movement. How much it is scaled
|
||||
// down depends on our handling/skill
|
||||
// val handlingMod = if(newVelocity.length() > 0.001) 1 - handling*sqrt(newVelocity.length() / topSpeed)*( 0.5 * (1 + newVelocity.normalize().dot(inputVel.normalize()) * -1.0))
|
||||
// else 1.0
|
||||
|
||||
newVelocity = newVelocity.add(inputVel)
|
||||
).normalize()
|
||||
|
||||
// Air Friction/Resistance
|
||||
// When the dot between friction and input is -1 we want to apply none of it.
|
||||
// In short we want to avoid applying friction that opposes our input force.
|
||||
// otherwise we would not be able to reach our top speed when acceleration is
|
||||
// low.
|
||||
val frictionConst = vehicle.runtime.resolveDouble(settings.handlingExpr)
|
||||
val frictionConst = 0.02
|
||||
var frictionForce = newVelocity.scale(-1.0).scale(frictionConst)
|
||||
val frictionApplied = if(inputVel.lengthSqr() != 0.0) 1 - 0.5 * (1 + -1.0 * frictionForce.normalize().dot(inputVel.normalize()))
|
||||
val frictionApplied = if(inputVel.lengthSqr() != 0.0) 1 - (0.5*(1 + -1.0 * (inputVel.normalize().dot(frictionForce.normalize()))))
|
||||
else 1.0
|
||||
frictionForce = frictionForce.scale((frictionApplied).pow(4))
|
||||
frictionForce = frictionForce.scale((frictionApplied))
|
||||
newVelocity = newVelocity.add(frictionForce)
|
||||
|
||||
// Apply height limit forces
|
||||
if (state.tooHigh.get()) {
|
||||
newVelocity = newVelocity.scale(0.96)
|
||||
if (state.stamina.get() == 0.0f) {
|
||||
newVelocity = newVelocity.subtract(0.0, 0.01, 0.0)
|
||||
}
|
||||
}
|
||||
// Ensure that if you're not applying force where the ride is facing then you apply a portion of debuff
|
||||
val strafeDebuff = 0.5
|
||||
val vehicleForwardVec = Vec3(0.0, 0.0, 1.0)
|
||||
val playerForwardVec = inputVel //.yRot(vehicle.yRot)
|
||||
val inputStrength = accel - (accel * strafeDebuff*(1 - vehicleForwardVec.dot(playerForwardVec).coerceIn(0.0, 1.0)))
|
||||
inputVel = inputVel.scale(inputStrength)
|
||||
|
||||
newVelocity = newVelocity.add(inputVel)
|
||||
|
||||
// Set speed to new magnitude to avoid the horrible things minecraft friction
|
||||
// does to our vectors :(
|
||||
state.speed.set(min(topSpeed, newVelocity.length()))
|
||||
|
||||
// Check to see if this new velocity will exceed top speed and if it will then cap it
|
||||
newVelocity = if (newVelocity.length() > topSpeed) newVelocity.normalize().scale(topSpeed) else newVelocity
|
||||
|
||||
val revertYawAligned = Matrix3f().rotateY(-vehicle.yRot.toRadians())
|
||||
state.rideVelocity.set((newVelocity.toVector3f().mul(revertYawAligned)).toVec3d())
|
||||
|
||||
return newVelocity
|
||||
}
|
||||
|
||||
@@ -355,7 +350,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return if (state.tooHigh.get()) 0.9f else 1.0f
|
||||
return if (state.isBoosting.get()) 1.1f else 1.0f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(
|
||||
@@ -438,79 +433,107 @@ class HoverSettings : RidingBehaviourSettings {
|
||||
override val key = HoverBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var canJump = "true".asExpression()
|
||||
var infiniteStamina: Expression? = null
|
||||
private set
|
||||
|
||||
var canJump: Expression? = null
|
||||
private set
|
||||
|
||||
var stamReplenishTimeSeconds: Expression? = null
|
||||
private set
|
||||
|
||||
// Speed in block per second
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 13.0, 6.0)".asExpression()
|
||||
var speedExpr: Expression? = null
|
||||
private set
|
||||
|
||||
// Acceleration in blocks per second per second
|
||||
var accelerationExpr: Expression =
|
||||
"q.get_ride_stats('ACCELERATION', 'AIR', 7.0, 3.0)".asExpression()
|
||||
// Acceleration in number of seconds to top speed
|
||||
var accelerationExpr: Expression? = null
|
||||
private set
|
||||
|
||||
// Amount of seconds before stamina depletion
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 7, 3)".asExpression()
|
||||
// Amount of seconds between boosts
|
||||
var staminaExpr: Expression? = null
|
||||
private set
|
||||
|
||||
// Height in blocks above the ground before stamina loss
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 10.0, 5.0)".asExpression()
|
||||
// Blocks before stamina drain
|
||||
var jumpExpr: Expression? = null
|
||||
private set
|
||||
|
||||
// Amount of air friction applied. 0.2 means 20 percent of the velocity is opposed per tick
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 0.1, 0.02)".asExpression()
|
||||
// Turn rate in degrees per second when stationary
|
||||
var handlingExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(speedExpr)
|
||||
buffer.writeExpression(accelerationExpr)
|
||||
buffer.writeExpression(staminaExpr)
|
||||
buffer.writeExpression(jumpExpr)
|
||||
buffer.writeExpression(handlingExpr)
|
||||
buffer.writeNullableExpression(infiniteStamina)
|
||||
buffer.writeNullableExpression(canJump)
|
||||
buffer.writeNullableExpression(stamReplenishTimeSeconds)
|
||||
buffer.writeNullableExpression(speedExpr)
|
||||
buffer.writeNullableExpression(accelerationExpr)
|
||||
buffer.writeNullableExpression(staminaExpr)
|
||||
buffer.writeNullableExpression(jumpExpr)
|
||||
buffer.writeNullableExpression(handlingExpr)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
speedExpr = buffer.readExpression()
|
||||
accelerationExpr = buffer.readExpression()
|
||||
staminaExpr = buffer.readExpression()
|
||||
jumpExpr = buffer.readExpression()
|
||||
handlingExpr = buffer.readExpression()
|
||||
infiniteStamina = buffer.readNullableExpression()
|
||||
canJump = buffer.readNullableExpression()
|
||||
stamReplenishTimeSeconds = buffer.readNullableExpression()
|
||||
speedExpr = buffer.readNullableExpression()
|
||||
accelerationExpr = buffer.readNullableExpression()
|
||||
staminaExpr = buffer.readNullableExpression()
|
||||
jumpExpr = buffer.readNullableExpression()
|
||||
handlingExpr = buffer.readNullableExpression()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class HoverState : RidingBehaviourState() {
|
||||
var speed = ridingState(0.0, Side.CLIENT)
|
||||
var isBoosting = ridingState(false, Side.BOTH)
|
||||
var boostVec = ridingState(Vec3.ZERO, Side.CLIENT)
|
||||
var boostTicks = ridingState(0, Side.CLIENT)
|
||||
var tooHigh = ridingState(false, Side.CLIENT)
|
||||
|
||||
override fun encode(buffer: FriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
buffer.writeDouble(speed.get())
|
||||
buffer.writeBoolean(isBoosting.get())
|
||||
buffer.writeBoolean(tooHigh.get())
|
||||
}
|
||||
|
||||
override fun decode(buffer: FriendlyByteBuf) {
|
||||
super.decode(buffer)
|
||||
speed.set(buffer.readDouble(), forced = true)
|
||||
isBoosting.set(buffer.readBoolean(), forced = true)
|
||||
tooHigh.set(buffer.readBoolean(), forced = true)
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
super.reset()
|
||||
speed.set(0.0, forced = true)
|
||||
isBoosting.set(false, forced = true)
|
||||
boostVec.set(Vec3.ZERO, forced = true)
|
||||
boostTicks.set(0, forced = true)
|
||||
tooHigh.set(false, forced = true)
|
||||
}
|
||||
|
||||
override fun copy() = HoverState().also {
|
||||
it.rideVelocity.set(this.rideVelocity.get(), forced = true)
|
||||
it.stamina.set(this.stamina.get(), forced = true)
|
||||
it.isBoosting.set(this.isBoosting.get(), forced = true)
|
||||
it.boostVec.set(this.boostVec.get(), forced = true)
|
||||
it.boostTicks.set(this.boostTicks.get(), forced = true)
|
||||
it.tooHigh.set(this.tooHigh.get(), forced = true)
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
if (previous !is HoverState) return false
|
||||
if (previous.isBoosting.get() != isBoosting.get()) return true
|
||||
return super.shouldSync(previous)
|
||||
}
|
||||
}
|
||||
|
||||
+131
-68
@@ -11,22 +11,35 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.air
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.MoLangMath.lerp
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseState
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.Side
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.ridingState
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.config.CobblemonConfig
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import 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.resolveDouble
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import com.cobblemon.mod.common.util.writeNullableExpression
|
||||
import com.cobblemon.mod.common.util.writeRidingStats
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
@@ -34,7 +47,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 kotlin.math.*
|
||||
import org.joml.Vector3f
|
||||
|
||||
class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
companion object {
|
||||
@@ -47,6 +60,9 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
return RidingStyle.AIR
|
||||
}
|
||||
|
||||
val globalJet: JetSettings
|
||||
get() = CobblemonRideSettings.jet
|
||||
|
||||
val poseProvider = PoseProvider<JetSettings, JetState>(PoseType.HOVER)
|
||||
.with(PoseOption(PoseType.FLY) { _, state, _ -> state.rideVelocity.get().z > 0.1 })
|
||||
|
||||
@@ -70,11 +86,14 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
}
|
||||
|
||||
override fun speed(settings: JetSettings, state: JetState, vehicle: PokemonEntity, driver: Player): Float {
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
|
||||
override fun tick(settings: JetSettings, state: JetState, vehicle: PokemonEntity, driver: Player, input: Vec3) {
|
||||
if(vehicle.level().isClientSide) {
|
||||
handleBoosting(state)
|
||||
tickStamina(settings, state, vehicle)
|
||||
}
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
|
||||
fun handleBoosting(
|
||||
@@ -84,7 +103,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
val boostKeyPressed = Minecraft.getInstance().options.keySprint.isDown()
|
||||
|
||||
if(state.stamina.get() != 0.0f && boostKeyPressed) {
|
||||
if (state.stamina.get() >= 0.25f) {
|
||||
if (state.stamina.get() >= 0.0f) {
|
||||
//If on the previous tick the boost key was held then don't change if the ride is boosting
|
||||
if(state.boostIsToggleable.get()) {
|
||||
//flip the boosting state if boost key is pressed
|
||||
@@ -108,13 +127,19 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
) {
|
||||
val stam = state.stamina.get()
|
||||
var newStam = stam
|
||||
val stamDrainRate = (1.0f / vehicle.runtime.resolveDouble(settings.staminaExpr)).toFloat() / 20.0f
|
||||
val stamDrainRate = (1.0f / vehicle.runtime.resolveDouble(settings.staminaExpr ?: globalJet.staminaExpr!!)).toFloat() / 20.0f
|
||||
|
||||
if (state.boosting.get()) {
|
||||
newStam = max(0.0f,stam - stamDrainRate)
|
||||
|
||||
newStam = max(0.0f,stam - stamDrainRate * 1.5f)
|
||||
} else {
|
||||
newStam = min(1.0f,stam + stamDrainRate * 0.25f)
|
||||
newStam = max(0.0f,stam - stamDrainRate)
|
||||
}
|
||||
|
||||
// If out of stamina then increase noStamTickCnt
|
||||
if (newStam == 0.0f) {
|
||||
state.noStamTickCnt.set(state.noStamTickCnt.get() + 1)
|
||||
} else {
|
||||
state.noStamTickCnt.get()
|
||||
}
|
||||
|
||||
state.stamina.set(newStam)
|
||||
@@ -136,23 +161,26 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
var upForce = 0.0
|
||||
var forwardForce = 0.0
|
||||
|
||||
val controller = (driver as? OrientationControllable)?.orientationController
|
||||
if (controller == null || controller.orientation == null) return Vec3.ZERO
|
||||
|
||||
//Calculate ride space velocity
|
||||
calculateRideSpaceVel(settings, state, vehicle, driver)
|
||||
|
||||
//Translate ride space velocity to world space velocity.
|
||||
if (controller != null) {
|
||||
upForce = -1.0 * sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().z
|
||||
forwardForce = cos(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().z
|
||||
}
|
||||
// The downward force used to encourage players to stop flying upside down.
|
||||
val maxDownwardForceTickCnt = 20.0
|
||||
val extraDownwardForce = if(state.stamina.get() == 0.0f) -0.3 * (state.noStamTickCnt.get() / maxDownwardForceTickCnt).coerceIn(0.0, 1.0) else 0.0 // 6 blocks a second downward
|
||||
|
||||
val velocity = Vec3(0.0, upForce, forwardForce)
|
||||
// Convert the local velocity vector into a world vector
|
||||
val localVelVec = Vector3f(
|
||||
state.rideVelocity.get().x.toFloat(),
|
||||
(state.rideVelocity.get().y).toFloat(),
|
||||
state.rideVelocity.get().z.toFloat() * -1.0f // Flip the z axis to make this left handed? orr.. right handed? idk, flip it though
|
||||
)
|
||||
var worldVelVec = localVelVec.mul(controller.orientation).toVec3d().yRot(vehicle.yRot.toRadians()) // Unrotate preemptively as this vector gets rotate later down the line in MC logic.
|
||||
worldVelVec = worldVelVec.add(0.0, extraDownwardForce, 0.0) // Add the stamina depletion force to bring the ride down.
|
||||
|
||||
return velocity
|
||||
return worldVelVec
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -164,17 +192,28 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
) {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr) / 400.0
|
||||
val minSpeed = vehicle.runtime.resolveDouble(settings.minSpeed) / 20.0
|
||||
val speed = state.rideVelocity.get().length()
|
||||
val boostMult = vehicle.runtime.resolveDouble(settings.jumpExpr)
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalJet.speedExpr!!) / 20.0
|
||||
val accel = topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalJet.accelerationExpr!!) * 20.0)
|
||||
val deccel = vehicle.runtime.resolveDouble(settings.deccelRate ?: globalJet.deccelRate!!)//0.005
|
||||
val minSpeed = topSpeed * vehicle.runtime.resolveDouble(settings.minSpeedFactor ?: globalJet.minSpeedFactor!!)
|
||||
val speed = state.rideVelocity.get().z
|
||||
val boostMult = vehicle.runtime.resolveDouble(settings.jumpExpr ?: globalJet.jumpExpr!!)
|
||||
|
||||
val boostTopSpeed = topSpeed * boostMult
|
||||
val boostAccel = accel * boostMult
|
||||
|
||||
//speed up and slow down based on input
|
||||
if (state.boosting.get() && speed < boostTopSpeed) {
|
||||
if (state.stamina.get() == 0.0f) {
|
||||
// Decelerate currently always a constant half of max acceleration.
|
||||
state.rideVelocity.set(
|
||||
Vec3(
|
||||
state.rideVelocity.get().x,
|
||||
state.rideVelocity.get().y,
|
||||
max(state.rideVelocity.get().z - (deccel * 1.3), 0.3 * 0.5)
|
||||
)
|
||||
)
|
||||
}
|
||||
else if (state.boosting.get() && speed < boostTopSpeed) {
|
||||
state.rideVelocity.set(
|
||||
Vec3(
|
||||
state.rideVelocity.get().x,
|
||||
@@ -198,7 +237,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
Vec3(
|
||||
state.rideVelocity.get().x,
|
||||
state.rideVelocity.get().y,
|
||||
max(state.rideVelocity.get().z - ((accel) / 2), minSpeed)
|
||||
max(state.rideVelocity.get().z - (deccel), minSpeed)
|
||||
)
|
||||
)
|
||||
} else if (speed > topSpeed) {
|
||||
@@ -206,6 +245,17 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
state.rideVelocity.get().scale(0.98)
|
||||
)
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Kill strafing velocities carried over from controller transition. Don't
|
||||
* kill immediately to give some momentum to transitions.
|
||||
***************************************************************************/
|
||||
state.rideVelocity.set(Vec3(
|
||||
lerp(state.rideVelocity.get().x, 0.0, 0.03),
|
||||
lerp(state.rideVelocity.get().y, 0.0, 0.03),
|
||||
state.rideVelocity.get().z
|
||||
))
|
||||
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
@@ -222,7 +272,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
val cappedDeltaTime = min(deltaTime, 0.2)
|
||||
|
||||
//Get handling in degrees per second
|
||||
val yawRotRate = vehicle.runtime.resolveDouble(settings.handlingYawExpr)
|
||||
val yawRotRate = vehicle.runtime.resolveDouble(settings.handlingYawExpr ?: globalJet.handlingYawExpr!!)
|
||||
|
||||
//Base the change off of deltatime.
|
||||
var handlingYaw = yawRotRate * (cappedDeltaTime)
|
||||
@@ -302,7 +352,8 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
state.currMouseYForce.set((state.currMouseYForce.get() + (0.0015 * mouseY)).coerceIn(-1.0, 1.0))
|
||||
|
||||
//Get handling in degrees per second
|
||||
var handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
|
||||
val handlingDebuff = if(state.stamina.get() == 0.0f) 0.5 else 1.0
|
||||
var handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalJet.handlingExpr!!) * handlingDebuff
|
||||
//convert it to delta time
|
||||
handling *= (cappedDeltaTime)
|
||||
|
||||
@@ -355,7 +406,8 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
state.currMouseYForce.set((state.currMouseYForce.get() + (0.0015 * mouseY)).coerceIn(-1.0, 1.0))
|
||||
|
||||
//Get handling in degrees per second
|
||||
var handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
|
||||
val handlingDebuff = if(state.stamina.get() == 0.0f) 0.5 else 1.0
|
||||
var handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalJet.handlingExpr!!) * handlingDebuff
|
||||
|
||||
//convert it to delta time
|
||||
handling *= (cappedDeltaTime)
|
||||
@@ -431,7 +483,12 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return if (state.boosting.get()) 1.2f else 1.0f
|
||||
val maxDownwardForceTickCnt = 20.0f
|
||||
return when {
|
||||
state.noStamTickCnt.get() != 0 -> 1f - 0.2f*(state.noStamTickCnt.get() / maxDownwardForceTickCnt).coerceIn(0.0f,1.0f)
|
||||
state.boosting.get() -> 1.2f
|
||||
else -> 1.0f
|
||||
}
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(settings: JetSettings, state: JetState, vehicle: PokemonEntity): Boolean {
|
||||
@@ -504,66 +561,70 @@ class JetSettings : RidingBehaviourSettings {
|
||||
override val key = JetBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var gravity: Expression = "0".asExpression()
|
||||
var gravity: Expression? = null
|
||||
private set
|
||||
|
||||
var minSpeed: Expression = "12.0".asExpression()
|
||||
var deccelRate: Expression? = null
|
||||
private set
|
||||
|
||||
var handlingYawExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 50.0, 25.0)".asExpression()
|
||||
// Mult to top speed in order to derive minSpeed
|
||||
var minSpeedFactor: Expression? = null
|
||||
private set
|
||||
|
||||
var handlingYawExpr: Expression? = null
|
||||
private set
|
||||
|
||||
// Make configurable by json
|
||||
var infiniteStamina: Expression = "false".asExpression()
|
||||
var infiniteStamina: Expression? = null
|
||||
private set
|
||||
|
||||
// Boost power. Mult for top speed and accel while boosting
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 1.7, 1.2)".asExpression()
|
||||
var jumpExpr: Expression? = null
|
||||
private set
|
||||
|
||||
// Turn rate in degrees per second
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 120.0, 60.0)".asExpression()
|
||||
var handlingExpr: Expression? = null
|
||||
private set
|
||||
// Top Speed in blocks per second
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 36.0, 20.0)".asExpression()
|
||||
var speedExpr: Expression? = null
|
||||
private set
|
||||
// Acceleration in blocks per s^2
|
||||
var accelerationExpr: Expression =
|
||||
"q.get_ride_stats('ACCELERATION', 'AIR', 5.0, 2.5)".asExpression()
|
||||
// Seconds to get to top speed
|
||||
var accelerationExpr: Expression? = null
|
||||
private set
|
||||
// Time in seconds to drain full bar of stamina when boosting
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 8.0, 4.0)".asExpression()
|
||||
// Time in seconds to drain full bar of stamina flying
|
||||
var staminaExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(gravity)
|
||||
buffer.writeExpression(minSpeed)
|
||||
buffer.writeExpression(handlingYawExpr)
|
||||
buffer.writeExpression(infiniteStamina)
|
||||
buffer.writeExpression(jumpExpr)
|
||||
buffer.writeExpression(handlingExpr)
|
||||
buffer.writeExpression(speedExpr)
|
||||
buffer.writeExpression(accelerationExpr)
|
||||
buffer.writeExpression(staminaExpr)
|
||||
buffer.writeNullableExpression(gravity)
|
||||
buffer.writeNullableExpression(deccelRate)
|
||||
buffer.writeNullableExpression(minSpeedFactor)
|
||||
buffer.writeNullableExpression(handlingYawExpr)
|
||||
buffer.writeNullableExpression(infiniteStamina)
|
||||
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)
|
||||
gravity = buffer.readExpression()
|
||||
minSpeed = buffer.readExpression()
|
||||
handlingYawExpr = buffer.readExpression()
|
||||
infiniteStamina = buffer.readExpression()
|
||||
jumpExpr = buffer.readExpression()
|
||||
handlingExpr = buffer.readExpression()
|
||||
speedExpr = buffer.readExpression()
|
||||
accelerationExpr = buffer.readExpression()
|
||||
staminaExpr = buffer.readExpression()
|
||||
gravity = buffer.readNullableExpression()
|
||||
deccelRate = buffer.readNullableExpression()
|
||||
minSpeedFactor = buffer.readNullableExpression()
|
||||
handlingYawExpr = buffer.readNullableExpression()
|
||||
infiniteStamina = buffer.readNullableExpression()
|
||||
jumpExpr = buffer.readNullableExpression()
|
||||
handlingExpr = buffer.readNullableExpression()
|
||||
speedExpr = buffer.readNullableExpression()
|
||||
accelerationExpr = buffer.readNullableExpression()
|
||||
staminaExpr = buffer.readNullableExpression()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -574,6 +635,7 @@ class JetState : RidingBehaviourState() {
|
||||
var boosting = ridingState(false, Side.BOTH)
|
||||
var boostIsToggleable = ridingState(false, Side.BOTH)
|
||||
var canSpeedBurst = ridingState(false, Side.BOTH)
|
||||
var noStamTickCnt = ridingState(0, Side.CLIENT) // Value that is increased for every tick you are out of stamina
|
||||
|
||||
override fun encode(buffer: FriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
@@ -600,6 +662,7 @@ class JetState : RidingBehaviourState() {
|
||||
boosting.set(false, forced = true)
|
||||
boostIsToggleable.set(false, forced = true)
|
||||
canSpeedBurst.set(true, forced = true)
|
||||
noStamTickCnt.set(0, forced = true)
|
||||
}
|
||||
|
||||
override fun copy() = JetState().also {
|
||||
|
||||
+65
-41
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.air
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.MoLangMath.lerp
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
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
|
||||
@@ -40,6 +41,8 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
val globalRocket: RocketSettings
|
||||
get() = CobblemonRideSettings.rocket
|
||||
|
||||
override fun getRidingStyle(settings: RocketSettings, state: RocketState): RidingStyle {
|
||||
return RidingStyle.AIR
|
||||
@@ -84,9 +87,9 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
|
||||
driver: Player
|
||||
): Float {
|
||||
// Use this as a "tick" function and check to see if the driver is "boosting"
|
||||
if(vehicle.level().isClientSide) {
|
||||
if (vehicle.level().isClientSide) {
|
||||
handleBoosting(settings, state, vehicle, driver)
|
||||
tickStamina(settings, state, vehicle)
|
||||
tickStamina(settings, state, vehicle, driver)
|
||||
}
|
||||
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
@@ -101,7 +104,6 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
|
||||
//If the forward key is not held then it cannot be boosting
|
||||
if(Minecraft.getInstance().options.keyUp.isDown() && state.stamina.get() != 0.0f) {
|
||||
val boostKeyPressed = Minecraft.getInstance().options.keySprint.isDown()
|
||||
if (state.stamina.get() >= 0.25f) {
|
||||
//If on the previous tick the boost key was held then don't change if the ride is boosting
|
||||
if(state.boostIsToggleable.get() && boostKeyPressed) {
|
||||
//flip the boosting state if boost key is pressed
|
||||
@@ -109,7 +111,6 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
|
||||
}
|
||||
//If the boost key is not held then next tick boosting is toggleable
|
||||
state.boostIsToggleable.set(!boostKeyPressed)
|
||||
}
|
||||
} else {
|
||||
//Turn off boost and reset boost params
|
||||
state.boostIsToggleable.set(true)
|
||||
@@ -121,18 +122,22 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
|
||||
fun tickStamina(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
) {
|
||||
val stam = state.stamina.get()
|
||||
|
||||
if (vehicle.runtime.resolveBoolean(settings.infiniteStamina ?: globalRocket.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 boostTime = vehicle.runtime.resolveDouble(settings.staminaExpr) * 20.0f
|
||||
val boostTime = vehicle.runtime.resolveDouble(settings.staminaExpr ?: globalRocket.staminaExpr!!) * 20.0f
|
||||
val stamDrainRate = (1.0f / boostTime).toFloat()
|
||||
|
||||
val newStam = if (state.boosting.get()) max(0.0f,stam - stamDrainRate)
|
||||
else min(1.0f,stam + stamDrainRate)
|
||||
val newStam = if(state.boosting.get() || driver.jumping) max(0.0f,stam - stamDrainRate) else stam
|
||||
|
||||
state.stamina.set(newStam)
|
||||
}
|
||||
@@ -181,13 +186,13 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
|
||||
|
||||
var newMomentum = state.turnMomentum.get().toDouble()
|
||||
// Degrees per tick
|
||||
val maxTurnMomentum = vehicle.runtime.resolveDouble(settings.maxTurnRate) / 20.0f
|
||||
val maxTurnMomentum = vehicle.runtime.resolveDouble(settings.maxTurnRate ?: globalRocket.maxTurnRate!!) / 20.0f
|
||||
// Turn rate increase per tick. Based off number of seconds to get to max turn rate
|
||||
val turningAcceleration = maxTurnMomentum / (vehicle.runtime.resolveDouble(settings.handlingExpr) * 20.0f)
|
||||
val turningAcceleration = maxTurnMomentum / (vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalRocket.handlingExpr!!) * 20.0f)
|
||||
val turnInput = (driver.xxa *-1.0f) * turningAcceleration
|
||||
|
||||
// Base boost stats off of normal turning stats
|
||||
val boostHandlingMod = vehicle.runtime.resolveDouble(settings.boostHandlingMod)
|
||||
val boostHandlingMod = vehicle.runtime.resolveDouble(settings.boostHandlingMod ?: globalRocket.boostHandlingMod!!)
|
||||
val boostMaxTurnMomentum = maxTurnMomentum * boostHandlingMod
|
||||
val boostTurnInput = turnInput * boostHandlingMod
|
||||
|
||||
@@ -232,9 +237,9 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Vec3 {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
|
||||
val jump = vehicle.runtime.resolveDouble(settings.jumpExpr)
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalRocket.speedExpr!!)
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalRocket.accelerationExpr!!)
|
||||
val jump = vehicle.runtime.resolveDouble(settings.jumpExpr ?: globalRocket.jumpExpr!!)
|
||||
|
||||
var newVelocity = vehicle.deltaMovement
|
||||
|
||||
@@ -258,7 +263,7 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
|
||||
(newVelocity.z + (accel * forwardInput.toDouble())))
|
||||
|
||||
} else if ((state.boosting.get())) {
|
||||
val boostMod = vehicle.runtime.resolveDouble(settings.boostSpeedMod)
|
||||
val boostMod = vehicle.runtime.resolveDouble(settings.boostSpeedMod ?: globalRocket.boostSpeedMod!!)
|
||||
val boostSpeed = topSpeed * boostMod
|
||||
val boostAccel = if(newVelocity.length() < boostSpeed) accel * (boostMod/1.5) else 0.0
|
||||
val forwardInput = 1.0f
|
||||
@@ -266,7 +271,7 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
|
||||
|
||||
if(state.canSpeedBurst.get()) {
|
||||
burst = 0.5f
|
||||
state.stamina.set(max(0.0f,state.stamina.get() - 0.1f))
|
||||
state.stamina.set(max(0.0f,state.stamina.get() - 0.05f))
|
||||
state.canSpeedBurst.set(false)
|
||||
}
|
||||
|
||||
@@ -301,7 +306,7 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
|
||||
} else {
|
||||
if (driver.jumping && !(vehicle.deltaMovement.y > maxVertSpeed && (newVelocity.y.sign.toFloat() > 0.0))) {
|
||||
// More force if traveling downwards to allow for quicker fall stops
|
||||
vertInput = if (newVelocity.y < 0) jump * 3 else jump
|
||||
vertInput = (if (state.stamina.get() == 0.0f) -1.0f else if(newVelocity.y < 0) jump.toFloat() * 3.0f else jump.toFloat()).toDouble()
|
||||
// Reset falldistance if upward motion is detected
|
||||
vehicle.resetFallDistance()
|
||||
newVelocity = Vec3(
|
||||
@@ -400,8 +405,8 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
|
||||
driver: Player
|
||||
): Float {
|
||||
if (state.boosting.get()) {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val boostMod = vehicle.runtime.resolveDouble(settings.boostSpeedMod)
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalRocket.speedExpr!!)
|
||||
val boostMod = vehicle.runtime.resolveDouble(settings.boostSpeedMod ?: globalRocket.boostSpeedMod!!)
|
||||
val normalizedBoostSpeed = RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), topSpeed, topSpeed * boostMod)
|
||||
return 1.0f + normalizedBoostSpeed.pow(2).toFloat() * 0.2f
|
||||
} else {
|
||||
@@ -474,6 +479,18 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
override fun damageOnCollision(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity,
|
||||
impactVec: Vec3
|
||||
): Boolean {
|
||||
if (!state.boosting.get()) return false
|
||||
val impactSpeed = impactVec.horizontalDistance().toFloat() * 10f
|
||||
return vehicle.causeFallDamage(impactSpeed, 1f, vehicle.damageSources().flyIntoWall())
|
||||
}
|
||||
|
||||
override fun getRideSounds(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
@@ -489,60 +506,67 @@ class RocketSettings : RidingBehaviourSettings {
|
||||
override val key = RocketBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var infiniteStamina: Expression? = null
|
||||
private set
|
||||
// Boost multiplier for speed
|
||||
var boostSpeedMod: Expression = "5.0".asExpression()
|
||||
var boostSpeedMod: Expression? = null
|
||||
private set
|
||||
|
||||
// Boost multiplier for handling
|
||||
var boostHandlingMod: Expression = "0.3".asExpression()
|
||||
var boostHandlingMod: Expression? = null
|
||||
private set
|
||||
|
||||
// Max turn rate in degrees per second
|
||||
var maxTurnRate: Expression = "100.0".asExpression()
|
||||
var maxTurnRate: Expression? = null
|
||||
private set
|
||||
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 1.2, 0.6)".asExpression()
|
||||
var speedExpr: Expression? = null
|
||||
private set
|
||||
|
||||
// Max accel is a whole 1.0 in 1 second. The conversion in the function below is to convert seconds to ticks
|
||||
var accelerationExpr: Expression =
|
||||
"q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 1.5)), (1.0 / (20.0 * 3.0))) * 0.5".asExpression()
|
||||
var accelerationExpr: Expression? = null
|
||||
private set
|
||||
|
||||
// Boost time in seconds
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 7.5, 2.5)".asExpression()
|
||||
// air time in seconds
|
||||
var staminaExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 2.0, 1.0)".asExpression()
|
||||
var jumpExpr: Expression? = null
|
||||
private set
|
||||
|
||||
// How long it takes to get to max turn rate from rest (in seconds)
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 0.5, 3.0)".asExpression()
|
||||
var handlingExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(speedExpr)
|
||||
buffer.writeExpression(accelerationExpr)
|
||||
buffer.writeExpression(staminaExpr)
|
||||
buffer.writeExpression(jumpExpr)
|
||||
buffer.writeExpression(handlingExpr)
|
||||
buffer.writeNullableExpression(infiniteStamina)
|
||||
buffer.writeNullableExpression(boostSpeedMod)
|
||||
buffer.writeNullableExpression(boostHandlingMod)
|
||||
buffer.writeNullableExpression(maxTurnRate)
|
||||
buffer.writeNullableExpression(speedExpr)
|
||||
buffer.writeNullableExpression(accelerationExpr)
|
||||
buffer.writeNullableExpression(staminaExpr)
|
||||
buffer.writeNullableExpression(jumpExpr)
|
||||
buffer.writeNullableExpression(handlingExpr)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
speedExpr = buffer.readExpression()
|
||||
accelerationExpr = buffer.readExpression()
|
||||
staminaExpr = buffer.readExpression()
|
||||
jumpExpr = buffer.readExpression()
|
||||
handlingExpr = buffer.readExpression()
|
||||
infiniteStamina = buffer.readNullableExpression()
|
||||
boostSpeedMod = buffer.readNullableExpression()
|
||||
boostHandlingMod = buffer.readNullableExpression()
|
||||
maxTurnRate = buffer.readNullableExpression()
|
||||
speedExpr = buffer.readNullableExpression()
|
||||
accelerationExpr = buffer.readNullableExpression()
|
||||
staminaExpr = buffer.readNullableExpression()
|
||||
jumpExpr = buffer.readNullableExpression()
|
||||
handlingExpr = buffer.readNullableExpression()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class RocketState : RidingBehaviourState() {
|
||||
|
||||
+2
-1
@@ -387,9 +387,10 @@ open class CompositeSettings : RidingBehaviourSettings {
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeRidingStats(stats)
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeResourceLocation(transitionStrategy)
|
||||
buffer.writeResourceLocation(defaultBehaviour.key)
|
||||
defaultBehaviour.encode(buffer)
|
||||
buffer.writeResourceLocation(alternateBehaviour.key)
|
||||
alternateBehaviour.encode(buffer)
|
||||
}
|
||||
|
||||
|
||||
+4
@@ -41,6 +41,10 @@ interface CompositeRidingStrategy<T : CompositeSettings> {
|
||||
toState: RidingBehaviourState,
|
||||
toSettings: RidingBehaviourSettings
|
||||
) {
|
||||
if (vehicle.level().isClientSide) {
|
||||
val fromSettings = if (settings.defaultBehaviour == toSettings) settings.alternateBehaviour else settings.defaultBehaviour
|
||||
|
||||
}
|
||||
toState.stamina.set(fromState.stamina.get())
|
||||
toState.rideVelocity.set(fromState.rideVelocity.get())
|
||||
fromState.reset()
|
||||
|
||||
+130
-60
@@ -9,15 +9,34 @@
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types.land
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.Side
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.ridingState
|
||||
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.*
|
||||
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
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.resolveFloat
|
||||
import com.cobblemon.mod.common.util.writeNullableExpression
|
||||
import com.cobblemon.mod.common.util.writeRidingStats
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sign
|
||||
import kotlin.math.sqrt
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
@@ -30,7 +49,6 @@ import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import kotlin.math.*
|
||||
|
||||
class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
companion object {
|
||||
@@ -38,6 +56,8 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
val globalHorse: HorseSettings
|
||||
get() = CobblemonRideSettings.horse
|
||||
|
||||
override fun getRidingStyle(settings: HorseSettings, state: HorseState): RidingStyle {
|
||||
return RidingStyle.LAND
|
||||
@@ -81,13 +101,21 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
// Use this as a "tick" function and calculate sprinting and inAir state here
|
||||
handleSprinting(settings, state, vehicle, driver)
|
||||
inAirCheck(state, vehicle)
|
||||
tickStamina(settings, state, vehicle, driver)
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
|
||||
override fun tick(
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
) {
|
||||
handleSprinting(state, driver)
|
||||
inAirCheck(state, vehicle)
|
||||
tickStamina(settings, state, vehicle)
|
||||
}
|
||||
|
||||
fun inAirCheck(
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
@@ -100,16 +128,13 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
val canSupportEntity = blockStateBelow.isFaceSturdy(vehicle.level(), posBelow, Direction.UP)
|
||||
val standingOnSolid = canSupportEntity && !isAirOrLiquid
|
||||
|
||||
|
||||
// inAir if not on the ground
|
||||
val inAir = !(vehicle.deltaMovement.y == 0.0 || standingOnSolid)
|
||||
state.inAir.set(inAir)
|
||||
}
|
||||
|
||||
fun handleSprinting(
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
) {
|
||||
if (state.sprinting.get()) {
|
||||
@@ -125,11 +150,15 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
) {
|
||||
val stam = state.stamina.get()
|
||||
|
||||
if (vehicle.runtime.resolveBoolean(settings.infiniteStamina ?: globalHorse.infiniteStamina!!)) {
|
||||
return
|
||||
}
|
||||
|
||||
var newStam = stam
|
||||
val stamDrainRate = (1.0f / vehicle.runtime.resolveDouble(settings.staminaExpr)).toFloat() / 20.0f
|
||||
val stamDrainRate = (1.0f / vehicle.runtime.resolveDouble(settings.staminaExpr ?: globalHorse.staminaExpr!!)).toFloat() / 20.0f
|
||||
|
||||
if (state.sprinting.get()) {
|
||||
newStam = max(0.0f,stam - stamDrainRate)
|
||||
@@ -179,10 +208,10 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Float {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalHorse.speedExpr!!)
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalHorse.handlingExpr!!)
|
||||
val walkHandlingBoost = 5
|
||||
val maxYawDiff = vehicle.runtime.resolveFloat(settings.lookYawLimit)
|
||||
val maxYawDiff = vehicle.runtime.resolveFloat(settings.lookYawLimit ?: globalHorse.lookYawLimit!!)
|
||||
|
||||
// Normalize the current rotation diff
|
||||
val rotDiff = Mth.wrapDegrees(driver.yRot - vehicle.yRot).coerceIn(-maxYawDiff,maxYawDiff)
|
||||
@@ -226,24 +255,37 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Vec3 {
|
||||
val canSprint = vehicle.runtime.resolveBoolean(settings.canSprint)
|
||||
val canJump = vehicle.runtime.resolveBoolean(settings.canJump)
|
||||
val jumpForce = vehicle.runtime.resolveDouble(settings.jumpExpr)
|
||||
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val canSprint = vehicle.runtime.resolveBoolean(settings.canSprint ?: globalHorse.canSprint!!)
|
||||
val canJump = vehicle.runtime.resolveBoolean(settings.canJump ?: globalHorse.canJump!!)
|
||||
val jumpForce = vehicle.runtime.resolveDouble(settings.jumpExpr ?: globalHorse.jumpExpr!!) * 0.75
|
||||
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalHorse.speedExpr!!)
|
||||
val walkSpeed = getWalkSpeed(vehicle)
|
||||
val topSpeed = if(canSprint && state.sprinting.get()) rideTopSpeed else walkSpeed
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
|
||||
val accel = topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalHorse.accelerationExpr!!) * 20.0)
|
||||
var activeInput = false
|
||||
|
||||
/******************************************************
|
||||
* Gather the previous velocity and set the horizontal
|
||||
* fields of the velocity vector to the magnitude of the
|
||||
* ones from deltaMovement. This is important as when a
|
||||
* collision happens the Deltamovement will reflect it
|
||||
* but rideVelocity won't.
|
||||
* Gather the previous velocity and check for horizontal
|
||||
* collisions
|
||||
*****************************************************/
|
||||
var newVelocity = Vec3(state.rideVelocity.get().x, 0.0, state.rideVelocity.get().z).normalize().scale(vehicle.deltaMovement.horizontalDistance())
|
||||
newVelocity = newVelocity.add(0.0, state.rideVelocity.get().y, 0.0)
|
||||
val dmSpeed = vehicle.deltaMovement.length()
|
||||
val rvSpeed = state.rideVelocity.get().length()
|
||||
var newVelocity = state.rideVelocity.get() //.normalize().scale(vehicle.deltaMovement.horizontalDistance())
|
||||
|
||||
/******************************************************
|
||||
* Gather the previous velocity and check for horizontal
|
||||
* collisions
|
||||
*****************************************************/
|
||||
// if (dmSpeed > rvSpeed) {
|
||||
// // align the velocity vector to be in local vehicle space
|
||||
// newVelocity = vehicle.deltaMovement
|
||||
// val yawAligned = Matrix3f().rotateY(-vehicle.yRot.toRadians())
|
||||
// newVelocity = (newVelocity.toVector3f().mul(yawAligned)).toVec3d()
|
||||
// }
|
||||
|
||||
if (vehicle.horizontalCollision) {
|
||||
newVelocity = newVelocity.normalize().scale(vehicle.deltaMovement.length())
|
||||
}
|
||||
|
||||
/******************************************************
|
||||
* Speed up and slow down based on input
|
||||
@@ -252,7 +294,7 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
|
||||
// If on a tight turn then do not speed up past half of top speed in order to turn quicker
|
||||
// Also determine how fast to be slowing down based on how far turned you are
|
||||
val lookYawLimit = vehicle.runtime.resolveFloat(settings.lookYawLimit)
|
||||
val lookYawLimit = vehicle.runtime.resolveFloat(settings.lookYawLimit ?: globalHorse.lookYawLimit!!)
|
||||
val percOfMaxTurnSpeed = abs(Mth.wrapDegrees(driver.yRot - vehicle.yRot) / lookYawLimit) * 100.0f
|
||||
val turnPercThresh = 0.0f
|
||||
val s = min(((percOfMaxTurnSpeed - turnPercThresh) / (100.0f - turnPercThresh)).pow(1),1.0f)
|
||||
@@ -282,12 +324,14 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
/******************************************************
|
||||
* Gravity logic
|
||||
*****************************************************/
|
||||
if (vehicle.onGround()) { newVelocity = Vec3(newVelocity.x, 0.0, newVelocity.z) }
|
||||
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.6
|
||||
val terminalVel = 2.0
|
||||
if (!vehicle.onGround() && state.jumpTicks.get() <= 0) {
|
||||
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.6
|
||||
val terminalVel = 2.0
|
||||
newVelocity = Vec3(newVelocity.x, max(newVelocity.y - gravity, -terminalVel), newVelocity.z)
|
||||
} else if(vehicle.onGround()) {
|
||||
newVelocity = Vec3(newVelocity.x, 0.0, newVelocity.z)
|
||||
}
|
||||
|
||||
val fallingForce = gravity
|
||||
newVelocity = Vec3(newVelocity.x, max(newVelocity.y - fallingForce, -terminalVel), newVelocity.z)
|
||||
|
||||
/******************************************************
|
||||
* Ground Friction
|
||||
@@ -299,8 +343,21 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
/******************************************************
|
||||
* Jump Logic
|
||||
*****************************************************/
|
||||
if (driver.jumping && vehicle.onGround() && canJump && driver.deltaMovement.y <= 0.1) {
|
||||
newVelocity = Vec3(newVelocity.x, jumpForce, newVelocity.z)
|
||||
if (state.jumpTicks.get() > 0 || (state.jumpTicks.get() >= 0 && driver.jumping && vehicle.onGround() && canJump && driver.deltaMovement.y <= 0.1)) {
|
||||
// Spread out jumpforce so that variable height jumps are possible
|
||||
val jumpInputTicks = 6
|
||||
if (driver.jumping && (state.jumpTicks.get() >= 0 && state.jumpTicks.get() < jumpInputTicks)) {
|
||||
val appliedJumpForce = ((jumpForce*1.5) / jumpInputTicks) //* (1 - state.jumpTicks.get() / jumpInputTicks)
|
||||
newVelocity = Vec3(newVelocity.x, newVelocity.y + appliedJumpForce, newVelocity.z)
|
||||
state.jumpTicks.set(state.jumpTicks.get() + 1)
|
||||
} else {
|
||||
// Set delay before next jump is possible after hitting ground
|
||||
val tickJumpDelay = 3
|
||||
state.jumpTicks.set(-tickJumpDelay)
|
||||
}
|
||||
} else if (vehicle.onGround() && state.jumpTicks.get() < 0) {
|
||||
// Tick off the delay once reaching the ground
|
||||
state.jumpTicks.set(state.jumpTicks.get() + 1)
|
||||
}
|
||||
|
||||
//Zero out lateral velocity possibly picked up from a controller transition
|
||||
@@ -479,82 +536,94 @@ class HorseSettings : RidingBehaviourSettings {
|
||||
override val key = HorseBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var canJump = "true".asExpression()
|
||||
var infiniteStamina: Expression? = null
|
||||
private set
|
||||
var canJump: Expression? = null
|
||||
private set
|
||||
|
||||
var canSprint = "true".asExpression()
|
||||
var canSprint: Expression? = null
|
||||
private set
|
||||
|
||||
var lookYawLimit = "90.0f".asExpression()
|
||||
var lookYawLimit: Expression? = null
|
||||
private set
|
||||
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'LAND', 1.0, 0.4)".asExpression()
|
||||
var speedExpr: Expression? = null
|
||||
private set
|
||||
|
||||
// Max accel is a whole 1.0 in 1 second. The conversion in the function below is to convert seconds to ticks
|
||||
var accelerationExpr: Expression =
|
||||
"q.get_ride_stats('ACCELERATION', 'LAND', (1.0 / (20.0 * 0.8)), (1.0 / (20.0 * 3.0)))".asExpression()
|
||||
var accelerationExpr: Expression? = null
|
||||
private set
|
||||
|
||||
// Between 30 seconds and 10 seconds at the lowest when at full speed.
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'LAND', 30.0, 10.0)".asExpression()
|
||||
var staminaExpr: Expression? = null
|
||||
private set
|
||||
|
||||
//Between a one block jump and a ten block jump
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'LAND', 1.5, 0.8)".asExpression()
|
||||
//Between a one block jump and a six block jump
|
||||
var jumpExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'LAND', 180.0, 40.0)".asExpression()
|
||||
var handlingExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(speedExpr)
|
||||
buffer.writeExpression(accelerationExpr)
|
||||
buffer.writeExpression(staminaExpr)
|
||||
buffer.writeExpression(jumpExpr)
|
||||
buffer.writeExpression(handlingExpr)
|
||||
buffer.writeExpression(canJump)
|
||||
buffer.writeExpression(canSprint)
|
||||
buffer.writeNullableExpression(infiniteStamina)
|
||||
buffer.writeNullableExpression(canJump)
|
||||
buffer.writeNullableExpression(canSprint)
|
||||
buffer.writeNullableExpression(lookYawLimit)
|
||||
buffer.writeNullableExpression(speedExpr)
|
||||
buffer.writeNullableExpression(accelerationExpr)
|
||||
buffer.writeNullableExpression(staminaExpr)
|
||||
buffer.writeNullableExpression(jumpExpr)
|
||||
buffer.writeNullableExpression(handlingExpr)
|
||||
buffer.writeNullableExpression(canJump)
|
||||
buffer.writeNullableExpression(canSprint)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
speedExpr = buffer.readExpression()
|
||||
accelerationExpr = buffer.readExpression()
|
||||
staminaExpr = buffer.readExpression()
|
||||
jumpExpr = buffer.readExpression()
|
||||
handlingExpr = buffer.readExpression()
|
||||
canJump = buffer.readExpression()
|
||||
canSprint = buffer.readExpression()
|
||||
infiniteStamina = buffer.readNullableExpression()
|
||||
canJump = buffer.readNullableExpression()
|
||||
canSprint = buffer.readNullableExpression()
|
||||
lookYawLimit = buffer.readNullableExpression()
|
||||
speedExpr = buffer.readNullableExpression()
|
||||
accelerationExpr = buffer.readNullableExpression()
|
||||
staminaExpr = buffer.readNullableExpression()
|
||||
jumpExpr = buffer.readNullableExpression()
|
||||
handlingExpr = buffer.readNullableExpression()
|
||||
canJump = buffer.readNullableExpression()
|
||||
canSprint = buffer.readNullableExpression()
|
||||
}
|
||||
}
|
||||
|
||||
class HorseState : RidingBehaviourState() {
|
||||
var sprinting = 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(inAir.get())
|
||||
buffer.writeInt(jumpTicks.get())
|
||||
}
|
||||
|
||||
override fun decode(buffer: FriendlyByteBuf) {
|
||||
super.decode(buffer)
|
||||
sprinting.set(buffer.readBoolean(), forced = true)
|
||||
inAir.set(buffer.readBoolean(), forced = true)
|
||||
jumpTicks.set(buffer.readInt(), forced = true)
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
super.reset()
|
||||
sprinting.set(false, forced = true)
|
||||
inAir.set(false, forced = true)
|
||||
jumpTicks.set(0, forced = true)
|
||||
}
|
||||
|
||||
override fun copy() = HorseState().also {
|
||||
@@ -562,6 +631,7 @@ class HorseState : RidingBehaviourState() {
|
||||
it.stamina.set(this.stamina.get(), forced = true)
|
||||
it.sprinting.set(this.sprinting.get(), forced = true)
|
||||
it.inAir.set(this.inAir.get(), forced = true)
|
||||
it.jumpTicks.set(this.jumpTicks.get(), forced = true)
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
|
||||
+54
-28
@@ -10,18 +10,36 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.land
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.MoLangMath.lerp
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
|
||||
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.SidedRidingState
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.ridingState
|
||||
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.*
|
||||
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
|
||||
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.resolveFloat
|
||||
import com.cobblemon.mod.common.util.writeNullableExpression
|
||||
import com.cobblemon.mod.common.util.writeRidingStats
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sign
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
@@ -32,12 +50,6 @@ import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import org.joml.Matrix3f
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sign
|
||||
|
||||
class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
|
||||
companion object {
|
||||
@@ -45,6 +57,8 @@ class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
val globalMinekart: MinekartSettings
|
||||
get() = CobblemonRideSettings.minekart
|
||||
|
||||
override fun getRidingStyle(settings: MinekartSettings, state: MinekartState): RidingStyle {
|
||||
return RidingStyle.LAND
|
||||
@@ -84,7 +98,7 @@ class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
|
||||
driver: Player
|
||||
): Float {
|
||||
|
||||
val boostMax = vehicle.runtime.resolveFloat(settings.boostLimit)
|
||||
val boostMax = vehicle.runtime.resolveFloat(settings.boostLimit ?: globalMinekart.boostLimit!!)
|
||||
//TODO: should this be by how much turn has accumulated or simply time turning... or both?
|
||||
val boostPerSecond = 20.0f
|
||||
val boostLoss = 40.0f
|
||||
@@ -142,8 +156,8 @@ class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
|
||||
var newMomentum = state.turnMomentum.get().toDouble()
|
||||
|
||||
//TODO: maybe tie this to max turnspeeds rather than the turn acceleration
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val turningAcceleration = (vehicle.runtime.resolveDouble(settings.handlingExpr) * 1.5 / 20.0f) / 20.0f * 4.0
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalMinekart.speedExpr!!)
|
||||
val turningAcceleration = (vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalMinekart.handlingExpr!!) * 1.5 / 20.0f) / 20.0f * 4.0
|
||||
val turnInput = (driver.xxa *-1.0f) * turningAcceleration
|
||||
val driftInput = turnInput * 0.25f
|
||||
|
||||
@@ -202,9 +216,9 @@ class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
|
||||
): Vec3 {
|
||||
|
||||
// Check to see if the ride should be walking or sprinting
|
||||
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) * 0.6 * 2.0
|
||||
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalMinekart.speedExpr!!) * 0.6 * 2.0
|
||||
val topSpeed = if (state.drifting.get()) rideTopSpeed * 0.7 else rideTopSpeed
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr) * 0.3 * 3 * 2.0
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalMinekart.accelerationExpr!!) * 0.3 * 3 * 2.0
|
||||
val driftAngle = 20.0f
|
||||
|
||||
//Align the direction of this vector with the vehicles current heading.
|
||||
@@ -327,7 +341,7 @@ class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return vehicle.runtime.resolveBoolean(settings.canJump)
|
||||
return vehicle.runtime.resolveBoolean(settings.canJump ?: globalMinekart.canJump!!)
|
||||
}
|
||||
|
||||
override fun setRideBar(
|
||||
@@ -447,45 +461,57 @@ class MinekartSettings : RidingBehaviourSettings {
|
||||
override val key = MinekartBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var canJump = "true".asExpression()
|
||||
var infiniteStamina: Expression? = null
|
||||
private set
|
||||
var canJump: Expression? = null
|
||||
private set
|
||||
|
||||
var boostLimit = "100.0f".asExpression()
|
||||
var boostLimit: Expression? = null
|
||||
private set
|
||||
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'LAND', 1.0, 0.3)".asExpression()
|
||||
var speedExpr: Expression? = null
|
||||
private set
|
||||
|
||||
// Max accel is a whole 1.0 in 1 second. The conversion in the function below is to convert seconds to ticks
|
||||
var accelerationExpr: Expression =
|
||||
"q.get_ride_stats('ACCELERATION', 'LAND', (1.0 / (20.0 * 1.5)), (1.0 / (20.0 * 5.0)))".asExpression()
|
||||
var accelerationExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'LAND', 30.0, 10.0)".asExpression()
|
||||
var staminaExpr: Expression? = null
|
||||
private set
|
||||
|
||||
//Between a one block jump and a ten block jump
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'LAND', 10.0, 1.0)".asExpression()
|
||||
var jumpExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'LAND', 140.0, 20.0)".asExpression()
|
||||
var handlingExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(canJump)
|
||||
buffer.writeExpression(boostLimit)
|
||||
buffer.writeNullableExpression(infiniteStamina)
|
||||
buffer.writeNullableExpression(canJump)
|
||||
buffer.writeNullableExpression(boostLimit)
|
||||
buffer.writeNullableExpression(speedExpr)
|
||||
buffer.writeNullableExpression(accelerationExpr)
|
||||
buffer.writeNullableExpression(staminaExpr)
|
||||
buffer.writeNullableExpression(jumpExpr)
|
||||
buffer.writeNullableExpression(handlingExpr)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
canJump = buffer.readExpression()
|
||||
boostLimit = buffer.readExpression()
|
||||
infiniteStamina = buffer.readNullableExpression()
|
||||
canJump = buffer.readNullableExpression()
|
||||
boostLimit = buffer.readNullableExpression()
|
||||
speedExpr = buffer.readNullableExpression()
|
||||
accelerationExpr = buffer.readNullableExpression()
|
||||
staminaExpr = buffer.readNullableExpression()
|
||||
jumpExpr = buffer.readNullableExpression()
|
||||
handlingExpr = buffer.readNullableExpression()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+66
-38
@@ -10,18 +10,35 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.land
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.MoLangMath.lerp
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
|
||||
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.SidedRidingState
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.ridingState
|
||||
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.*
|
||||
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
|
||||
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 kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.sign
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
@@ -33,10 +50,6 @@ import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import org.joml.Matrix3f
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.sign
|
||||
|
||||
class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
companion object {
|
||||
@@ -44,6 +57,8 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
val globalVehicle: VehicleSettings
|
||||
get() = CobblemonRideSettings.vehicle
|
||||
|
||||
override fun getRidingStyle(settings: VehicleSettings, state: VehicleState): RidingStyle {
|
||||
return RidingStyle.LAND
|
||||
@@ -126,7 +141,7 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
var newMomentum = state.turnMomentum.get().toDouble()
|
||||
|
||||
// Grab turningAcceleration and divide 20 twice to get
|
||||
val turningAcceleration = (vehicle.runtime.resolveDouble(settings.handlingExpr) * 1.5 / 20.0f) / 20.0f * 4.0
|
||||
val turningAcceleration = (vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalVehicle.handlingExpr!!) * 1.5 / 20.0f) / 20.0f * 4.0
|
||||
val turnInput = (driver.xxa *-1.0f) * turningAcceleration
|
||||
|
||||
val maxTurnMomentum = 80.0f / 20.0f
|
||||
@@ -137,7 +152,7 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
|
||||
if(state.drifting.get() || state.inAir.get()) {
|
||||
if( newMomentum < driftMaxTurnMomentum) {
|
||||
driftTurnInput = driftTurnInput * (vehicle.deltaMovement.horizontalDistance() / vehicle.runtime.resolveDouble(settings.speedExpr))
|
||||
driftTurnInput = driftTurnInput * (vehicle.deltaMovement.horizontalDistance() / vehicle.runtime.resolveDouble(settings.speedExpr ?: globalVehicle.speedExpr!!))
|
||||
newMomentum += driftTurnInput
|
||||
}
|
||||
newMomentum = lerp(newMomentum, 0.0, 0.03)
|
||||
@@ -177,10 +192,10 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
|
||||
// Check to see if the ride should be walking or sprinting
|
||||
//val walkSpeed = getWalkSpeed(vehicle)
|
||||
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) * 0.6 * 2.0
|
||||
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalVehicle.speedExpr!!) * 0.6 * 2.0
|
||||
val topSpeed = if (state.drifting.get()) rideTopSpeed * 0.5 else rideTopSpeed
|
||||
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr) * 0.3 * 3 * 2.0
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalVehicle.accelerationExpr!!) * 0.3 * 3 * 2.0
|
||||
|
||||
//Flag for determining if player is actively inputting
|
||||
var activeInput = false
|
||||
@@ -250,7 +265,7 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
}
|
||||
|
||||
//TODO: Change this so its tied to a jumping stat and representative of the amount of jumps
|
||||
val canJump = vehicle.runtime.resolveBoolean(settings.canJump)
|
||||
val canJump = vehicle.runtime.resolveBoolean(settings.canJump ?: globalVehicle.canJump!!)
|
||||
//Jump the thang!
|
||||
if (driver.jumping && vehicle.onGround() && canJump) {
|
||||
val jumpForce = 0.5
|
||||
@@ -296,7 +311,7 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return vehicle.runtime.resolveBoolean(settings.canJump)
|
||||
return vehicle.runtime.resolveBoolean(settings.canJump ?: globalVehicle.canJump!!)
|
||||
}
|
||||
|
||||
override fun setRideBar(
|
||||
@@ -412,66 +427,79 @@ class VehicleSettings : RidingBehaviourSettings {
|
||||
override val key = VehicleBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var canJump = "true".asExpression()
|
||||
var infiniteStamina: Expression? = null
|
||||
private set
|
||||
|
||||
var speed = "0.3".asExpression()
|
||||
var canJump: Expression? = null
|
||||
private set
|
||||
|
||||
var driveFactor = "1.0".asExpression()
|
||||
var speed: Expression? = null
|
||||
private set
|
||||
|
||||
var reverseDriveFactor = "0.25".asExpression()
|
||||
var driveFactor: Expression? = null
|
||||
private set
|
||||
|
||||
var rotationSpeed = "45/20".asExpression()
|
||||
var reverseDriveFactor: Expression? = null
|
||||
private set
|
||||
|
||||
var lookYawLimit = "101".asExpression()
|
||||
var rotationSpeed: Expression? = null
|
||||
private set
|
||||
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'LAND', 1.0, 0.3)".asExpression()
|
||||
var lookYawLimit: Expression? = null
|
||||
private set
|
||||
|
||||
var speedExpr: Expression? = null
|
||||
private set
|
||||
|
||||
// Max accel is a whole 1.0 in 1 second. The conversion in the function below is to convert seconds to ticks
|
||||
var accelerationExpr: Expression =
|
||||
"q.get_ride_stats('ACCELERATION', 'LAND', (1.0 / (20.0 * 1.5)), (1.0 / (20.0 * 5.0)))".asExpression()
|
||||
var accelerationExpr: Expression? = null
|
||||
private set
|
||||
|
||||
// Between 30 seconds and 10 seconds at the lowest when at full speed.
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'LAND', 30.0, 10.0)".asExpression()
|
||||
var staminaExpr: Expression? = null
|
||||
private set
|
||||
|
||||
//Between a one block jump and a ten block jump
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'LAND', 10.0, 1.0)".asExpression()
|
||||
var jumpExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'LAND', 140.0, 20.0)".asExpression()
|
||||
var handlingExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(canJump)
|
||||
buffer.writeExpression(speed)
|
||||
buffer.writeExpression(driveFactor)
|
||||
buffer.writeExpression(reverseDriveFactor)
|
||||
buffer.writeExpression(rotationSpeed)
|
||||
buffer.writeExpression(lookYawLimit)
|
||||
buffer.writeNullableExpression(infiniteStamina)
|
||||
buffer.writeNullableExpression(canJump)
|
||||
buffer.writeNullableExpression(speed)
|
||||
buffer.writeNullableExpression(driveFactor)
|
||||
buffer.writeNullableExpression(reverseDriveFactor)
|
||||
buffer.writeNullableExpression(rotationSpeed)
|
||||
buffer.writeNullableExpression(lookYawLimit)
|
||||
buffer.writeNullableExpression(speedExpr)
|
||||
buffer.writeNullableExpression(accelerationExpr)
|
||||
buffer.writeNullableExpression(staminaExpr)
|
||||
buffer.writeNullableExpression(jumpExpr)
|
||||
buffer.writeNullableExpression(handlingExpr)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
canJump = buffer.readExpression()
|
||||
speed = buffer.readExpression()
|
||||
driveFactor = buffer.readExpression()
|
||||
reverseDriveFactor = buffer.readExpression()
|
||||
rotationSpeed = buffer.readExpression()
|
||||
lookYawLimit = buffer.readExpression()
|
||||
infiniteStamina = buffer.readNullableExpression()
|
||||
canJump = buffer.readNullableExpression()
|
||||
speed = buffer.readNullableExpression()
|
||||
driveFactor = buffer.readNullableExpression()
|
||||
reverseDriveFactor = buffer.readNullableExpression()
|
||||
rotationSpeed = buffer.readNullableExpression()
|
||||
lookYawLimit = buffer.readNullableExpression()
|
||||
speedExpr = buffer.readNullableExpression()
|
||||
accelerationExpr = buffer.readNullableExpression()
|
||||
staminaExpr = buffer.readNullableExpression()
|
||||
jumpExpr = buffer.readNullableExpression()
|
||||
handlingExpr = buffer.readNullableExpression()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+96
-37
@@ -8,17 +8,34 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types.liquid
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
|
||||
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.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.*
|
||||
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
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.resolveFloat
|
||||
import com.cobblemon.mod.common.util.writeNullableExpression
|
||||
import com.cobblemon.mod.common.util.writeRidingStats
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.Mth
|
||||
@@ -28,9 +45,6 @@ import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
|
||||
companion object {
|
||||
@@ -38,13 +52,17 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
val globalBoat: BoatSettings
|
||||
get() = CobblemonRideSettings.boat
|
||||
|
||||
override fun getRidingStyle(settings: BoatSettings, state: BoatState): RidingStyle {
|
||||
return RidingStyle.LIQUID
|
||||
}
|
||||
|
||||
val poseProvider = PoseProvider<BoatSettings, BoatState>(PoseType.FLOAT)
|
||||
.with(PoseOption(PoseType.SWIM) { _, _, entity -> entity.isSwimming && entity.entityData.get(PokemonEntity.MOVING) })
|
||||
val poseProvider = PoseProvider<BoatSettings, BoatState>(PoseType.STAND)
|
||||
.with(PoseOption(PoseType.WALK) { _, state, _ ->
|
||||
abs(state.rideVelocity.get().horizontalDistance()) > 0.2
|
||||
})
|
||||
|
||||
override fun isActive(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity): Boolean {
|
||||
if (state.jumpBuffer.get() != -1) {
|
||||
@@ -85,7 +103,7 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
|
||||
|
||||
if (!state.isVehicleSprinting.get()) {
|
||||
if (state.staminaBuffer.get() >= 20) {
|
||||
val staminaIncrease = vehicle.runtime.resolveDouble(settings.staminaIncreaseExpr)
|
||||
val staminaIncrease = vehicle.runtime.resolveDouble(settings.staminaIncreaseExpr ?: globalBoat.staminaIncreaseExpr!!)
|
||||
state.stamina.set(min(1.0f, state.stamina.get() + 0.01f * staminaIncrease.toFloat()))
|
||||
}
|
||||
}
|
||||
@@ -132,14 +150,14 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
|
||||
}
|
||||
|
||||
private fun applyVelocityFromInput(velocity: Vec3, vehicle: PokemonEntity, driver: Player, settings: BoatSettings, state: BoatState): Vec3 {
|
||||
val speed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val acceleration = vehicle.runtime.resolveDouble(settings.accelerationExpr)
|
||||
val speed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalBoat.speedExpr!!)
|
||||
val acceleration = speed / (vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalBoat.accelerationExpr!!) * 20)
|
||||
|
||||
if (state.jumpBuffer.get() != -1 || !(vehicle.isInWater || vehicle.isUnderWater)) {
|
||||
return velocity
|
||||
}
|
||||
|
||||
val sprintModifier = vehicle.runtime.resolveDouble(settings.sprintSpeedModifier)
|
||||
val sprintModifier = vehicle.runtime.resolveDouble(settings.sprintSpeedModifier ?: globalBoat.sprintSpeedModifier!!)
|
||||
val activeSprintModifier = if (state.isVehicleSprinting.get() && hasStamina(state)) sprintModifier else 1.0
|
||||
val forwardInput = driver.zza.toDouble()
|
||||
val delta = when {
|
||||
@@ -162,23 +180,34 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
|
||||
if (!driver.jumping) return velocity // Not jumping
|
||||
if (state.jumpBuffer.get() != -1) return velocity // Already jumped very recently
|
||||
|
||||
val jumpStrength = vehicle.runtime.resolveDouble(settings.jumpStrengthExpr)
|
||||
val jumpStrength = vehicle.runtime.resolveDouble(settings.jumpStrengthExpr ?: globalBoat.jumpStrengthExpr!!)
|
||||
state.jumpBuffer.set(0)
|
||||
return Vec3(velocity.x, velocity.y + jumpStrength, velocity.z)
|
||||
}
|
||||
|
||||
private fun applyGravity(velocity: Vec3, vehicle: PokemonEntity, settings: BoatSettings, state: BoatState): Vec3 {
|
||||
if (state.jumpBuffer.get() == -1 && (vehicle.isInWater || vehicle.isUnderWater)) {
|
||||
val verticalVelocity = if (vehicle.isUnderWater) 0.5 else 0.0
|
||||
return Vec3(velocity.x, verticalVelocity, velocity.z)
|
||||
if (shouldFloatHigher(vehicle, settings)) {
|
||||
return Vec3(velocity.x, 0.5, velocity.z)
|
||||
}
|
||||
else {
|
||||
return Vec3(velocity.x, 0.0, velocity.z)
|
||||
}
|
||||
}
|
||||
val terminalVelocity = vehicle.runtime.resolveDouble(settings.terminalVelocity)
|
||||
val terminalVelocity = vehicle.runtime.resolveDouble(settings.terminalVelocity ?: globalBoat.terminalVelocity!!)
|
||||
val gravity = (9.8 / ( 20.0)) * 0.2
|
||||
return Vec3(velocity.x, max(velocity.y - gravity, terminalVelocity), velocity.z)
|
||||
}
|
||||
|
||||
private fun shouldFloatHigher(vehicle: PokemonEntity, settings: BoatSettings): Boolean {
|
||||
val surfaceOffset = vehicle.runtime.resolveFloat(settings.surfaceLevelOffset ?: globalBoat.surfaceLevelOffset!!)
|
||||
val blockPos = BlockPos.containing(vehicle.x, vehicle.eyeY + surfaceOffset, vehicle.z)
|
||||
val fluidState = vehicle.level().getFluidState(blockPos)
|
||||
return !fluidState.isEmpty
|
||||
}
|
||||
|
||||
private fun applyStrafeRotation(vehicle: PokemonEntity, driver: Player, settings: BoatSettings, state: BoatState) {
|
||||
val skillModifier = vehicle.runtime.resolveDouble(settings.rotationspeedModifierExpr)
|
||||
val skillModifier = vehicle.runtime.resolveDouble(settings.rotationSpeedModifierExpr ?: globalBoat.rotationSpeedModifierExpr!!)
|
||||
val strafe = driver.xxa
|
||||
if (abs(strafe) > 0) {
|
||||
val increase = if (strafe > 0) -1 else 1
|
||||
@@ -191,7 +220,10 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
|
||||
}
|
||||
|
||||
private fun consumeStamina(vehicle: PokemonEntity, driver: Player, settings: BoatSettings, state: BoatState, drain: Float) {
|
||||
val staminaModifier = vehicle.runtime.resolveFloat(settings.staminaDecreaseExpr)
|
||||
val staminaModifier = vehicle.runtime.resolveFloat(settings.staminaDecreaseExpr ?: globalBoat.staminaDecreaseExpr!!)
|
||||
if (staminaModifier == 0F) {
|
||||
return
|
||||
}
|
||||
state.stamina.set(max(0f, state.stamina.get() - drain / staminaModifier))
|
||||
if (state.stamina.get() == 0f) {
|
||||
state.isVehicleSprinting.set(false)
|
||||
@@ -297,11 +329,8 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
if (state.rideVelocity.get().z > topSpeed * 0.8) {
|
||||
return Mth.lerp((state.rideVelocity.get().z - (topSpeed * 0.8)) / (topSpeed * 0.2), 1.0, 1.2).toFloat()
|
||||
}
|
||||
return 1.0f
|
||||
val sprintFov = vehicle.runtime.resolveFloat(settings.sprintFovModifier ?: globalBoat.sprintFovModifier!!)
|
||||
return if (state.isVehicleSprinting.get()) sprintFov else 1.0f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity): Boolean {
|
||||
@@ -314,7 +343,14 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): ResourceLocation {
|
||||
return cobblemonResource("no_pose")
|
||||
val isInWater = Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
|
||||
if (vehicle.isInWater || vehicle.isUnderWater) {
|
||||
return@any true
|
||||
}
|
||||
val blockState = vehicle.level().getBlockState(it)
|
||||
return@any !blockState.fluidState.isEmpty
|
||||
}
|
||||
return if (!isInWater) cobblemonResource("in_air") else cobblemonResource("no_pose")
|
||||
}
|
||||
|
||||
override fun inertia(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity): Double {
|
||||
@@ -338,7 +374,7 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
|
||||
state: BoatState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return vehicle.runtime.resolveBoolean(settings.rotatePokemonHead)
|
||||
return vehicle.runtime.resolveBoolean(settings.rotatePokemonHead ?: globalBoat.rotatePokemonHead!!)
|
||||
}
|
||||
|
||||
override fun shouldRotateRiderHead(
|
||||
@@ -368,44 +404,67 @@ class BoatSettings : RidingBehaviourSettings {
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
var terminalVelocity = "-2.0".asExpression()
|
||||
var terminalVelocity: Expression? = null
|
||||
private set
|
||||
|
||||
var rotatePokemonHead = "true".asExpression()
|
||||
var rotatePokemonHead: Expression? = null
|
||||
private set
|
||||
|
||||
var staminaIncreaseExpr = "q.get_ride_stats('STAMINA', 'LIQUID', 2.0, 1.0)".asExpression()
|
||||
var staminaIncreaseExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var staminaDecreaseExpr = "q.get_ride_stats('STAMINA', 'LIQUID', 5.0, 1.0)".asExpression()
|
||||
var staminaDecreaseExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var rotationspeedModifierExpr = "q.get_ride_stats('SKILL', 'LIQUID', 1.5, 0.5)".asExpression()
|
||||
var rotationSpeedModifierExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var jumpStrengthExpr = "q.get_ride_stats('JUMP', 'LIQUID', 2.5, 1.0)".asExpression()
|
||||
var jumpStrengthExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var speedExpr = "q.get_ride_stats('SPEED', 'LIQUID', 2.0, 0.5)".asExpression()
|
||||
var speedExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var accelerationExpr = "q.get_ride_stats('ACCELERATION', 'LIQUID', 0.5, 0.05)".asExpression()
|
||||
var accelerationExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var sprintSpeedModifier = "1.5".asExpression()
|
||||
var sprintSpeedModifier: Expression? = null
|
||||
private set
|
||||
|
||||
var sprintFovModifier: Expression? = null
|
||||
|
||||
var surfaceLevelOffset: Expression? = null
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(terminalVelocity)
|
||||
buffer.writeNullableExpression(terminalVelocity)
|
||||
buffer.writeNullableExpression(rotatePokemonHead)
|
||||
buffer.writeNullableExpression(staminaIncreaseExpr)
|
||||
buffer.writeNullableExpression(staminaDecreaseExpr)
|
||||
buffer.writeNullableExpression(rotationSpeedModifierExpr)
|
||||
buffer.writeNullableExpression(jumpStrengthExpr)
|
||||
buffer.writeNullableExpression(speedExpr)
|
||||
buffer.writeNullableExpression(accelerationExpr)
|
||||
buffer.writeNullableExpression(sprintSpeedModifier)
|
||||
buffer.writeNullableExpression(sprintFovModifier)
|
||||
buffer.writeNullableExpression(surfaceLevelOffset)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
terminalVelocity = buffer.readExpression()
|
||||
terminalVelocity = buffer.readNullableExpression()
|
||||
rotatePokemonHead = buffer.readNullableExpression()
|
||||
staminaIncreaseExpr = buffer.readNullableExpression()
|
||||
staminaDecreaseExpr = buffer.readNullableExpression()
|
||||
rotationSpeedModifierExpr = buffer.readNullableExpression()
|
||||
jumpStrengthExpr = buffer.readNullableExpression()
|
||||
speedExpr = buffer.readNullableExpression()
|
||||
accelerationExpr = buffer.readNullableExpression()
|
||||
sprintSpeedModifier = buffer.readNullableExpression()
|
||||
sprintFovModifier = buffer.readNullableExpression()
|
||||
surfaceLevelOffset = buffer.readNullableExpression()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+14
-8
@@ -8,10 +8,14 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types.liquid
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
|
||||
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.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
@@ -21,6 +25,7 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.readRidingStats
|
||||
import com.cobblemon.mod.common.util.writeNullable
|
||||
import com.cobblemon.mod.common.util.writeRidingStats
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
@@ -38,6 +43,8 @@ class BurstBehaviour : RidingBehaviour<BurstSettings, BurstState> {
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
val globalBurst: BurstSettings
|
||||
get() = CobblemonRideSettings.burst
|
||||
|
||||
override fun getRidingStyle(settings: BurstSettings, state: BurstState): RidingStyle {
|
||||
return RidingStyle.LIQUID
|
||||
@@ -67,7 +74,7 @@ class BurstBehaviour : RidingBehaviour<BurstSettings, BurstState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
if(state.dashing.get()) {
|
||||
if (state.dashing.get()) {
|
||||
state.ticks.set(state.ticks.get() + 1)
|
||||
if(state.ticks.get() >= DASH_TICKS) {
|
||||
state.dashing.set(false)
|
||||
@@ -77,7 +84,7 @@ class BurstBehaviour : RidingBehaviour<BurstSettings, BurstState> {
|
||||
}
|
||||
|
||||
state.dashing.set(true)
|
||||
return settings.dashSpeed
|
||||
return settings.dashSpeed ?: globalBurst.dashSpeed!!
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
@@ -239,22 +246,21 @@ class BurstSettings : RidingBehaviourSettings {
|
||||
override val key = BurstBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var dashSpeed = 1F
|
||||
var dashSpeed: Float? = null
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeFloat(dashSpeed)
|
||||
buffer.writeNullable(dashSpeed) { buf, speed -> buf.writeFloat(speed) }
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
dashSpeed = buffer.readFloat()
|
||||
dashSpeed = buffer.readNullable { buf -> buf.readFloat() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+140
-99
@@ -10,46 +10,43 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.liquid
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.MoLangMath.lerp
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
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.behaviour.types.air.BirdSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.BirdState
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.JetSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.JetState
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.RocketSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.RocketState
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.Side
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.ridingState
|
||||
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.*
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.math.geometry.toDegrees
|
||||
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 kotlin.math.*
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import org.joml.Vector3f
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.hypot
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sign
|
||||
import kotlin.math.sin
|
||||
import kotlin.math.sqrt
|
||||
|
||||
class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
companion object {
|
||||
@@ -57,6 +54,8 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
val globalDolphin: DolphinSettings
|
||||
get() = CobblemonRideSettings.dolphin
|
||||
|
||||
override fun getRidingStyle(settings: DolphinSettings, state: DolphinState): RidingStyle {
|
||||
return RidingStyle.LIQUID
|
||||
@@ -74,18 +73,28 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
}
|
||||
|
||||
override fun speed(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity, driver: Player): Float {
|
||||
return state.speed.get().toFloat()
|
||||
}
|
||||
|
||||
override fun tick(
|
||||
settings: DolphinSettings,
|
||||
state: DolphinState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
) {
|
||||
if(vehicle.level().isClientSide) {
|
||||
handleBoosting(state)
|
||||
handleBoosting(state, vehicle)
|
||||
tickStamina(settings, state, vehicle)
|
||||
}
|
||||
return state.speed.get().toFloat()
|
||||
}
|
||||
|
||||
fun handleBoosting(
|
||||
state: DolphinState,
|
||||
vehicle: PokemonEntity
|
||||
) {
|
||||
//If the forward key is not held then it cannot be boosting
|
||||
if(Minecraft.getInstance().options.keyUp.isDown() && state.stamina.get() != 0.0f) {
|
||||
if(Minecraft.getInstance().options.keyUp.isDown() && state.stamina.get() != 0.0f && (vehicle.isInWater || vehicle.isUnderWater)) {
|
||||
val boostKeyPressed = Minecraft.getInstance().options.keySprint.isDown()
|
||||
if (state.stamina.get() >= 0.25f) {
|
||||
//If on the previous tick the boost key was held then don't change if the ride is boosting
|
||||
@@ -110,10 +119,14 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
) {
|
||||
val stam = state.stamina.get()
|
||||
|
||||
if (vehicle.runtime.resolveBoolean(settings.infiniteStamina ?: globalDolphin.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 boostTime = vehicle.runtime.resolveDouble(settings.staminaExpr) * 20.0f
|
||||
val boostTime = vehicle.runtime.resolveDouble(settings.staminaExpr ?: globalDolphin.staminaExpr!!) * 20.0f
|
||||
val stamDrainRate = (1.0f / boostTime).toFloat()
|
||||
|
||||
val newStam = if (state.boosting.get()) max(0.0f,stam - stamDrainRate)
|
||||
@@ -142,24 +155,48 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
|
||||
val runtime = vehicle.runtime
|
||||
val boostMod = if (state.boosting.get()) runtime.resolveDouble(settings.jumpExpr) else 1.0
|
||||
val topSpeed = (vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0) * boostMod
|
||||
val accel = (topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr) * 20.0)) * boostMod
|
||||
state.lastVelocity.set(vehicle.deltaMovement.yRot(vehicle.yRot.toRadians()))
|
||||
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
|
||||
|
||||
/*********************************************
|
||||
* Breaching logic
|
||||
*********************************************/
|
||||
if (!vehicle.isInWater && !vehicle.isUnderWater)
|
||||
{
|
||||
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.6
|
||||
val terminalVel = 2.0
|
||||
val currVel = state.lastVelocity.get()
|
||||
state.lastVelocity.set(Vec3(currVel.x, max(-terminalVel, currVel.y - gravity), currVel.z))
|
||||
val breachBoost = 1.5
|
||||
if (!state.hasBreached.get()) {
|
||||
// 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))
|
||||
}
|
||||
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()))
|
||||
}
|
||||
return state.lastVelocity.get()
|
||||
} else {
|
||||
state.lastVelocity.set(vehicle.deltaMovement.yRot(vehicle.yRot.toRadians()))
|
||||
state.hasBreached.set(false)
|
||||
}
|
||||
|
||||
/*********************************************
|
||||
* Handle collisions
|
||||
*********************************************/
|
||||
if (vehicle.horizontalCollision || vehicle.verticalCollision) {
|
||||
state.speed.set(vehicle.deltaMovement.length())
|
||||
// not actually friction
|
||||
val frictionLimit = 0.1
|
||||
val postColSpeed = min(max(vehicle.deltaMovement.length(), frictionLimit), state.speed.get())
|
||||
state.speed.set(postColSpeed)
|
||||
}
|
||||
|
||||
val currSpeed = state.speed.get()
|
||||
val strafeFactor = runtime.resolveDouble(settings.strafeFactor)
|
||||
val reverseDriveFactor = runtime.resolveDouble(settings.reverseDriveFactor)
|
||||
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))
|
||||
@@ -178,9 +215,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
newVelocity = controller.orientation?.transform(newVelocity.toVector3f())?.toVec3d() ?: Vec3.ZERO
|
||||
newVelocity = newVelocity.yRot(vehicle.yRot.toRadians())
|
||||
val desiredVec = newVelocity.subtract(vehicle.deltaMovement)
|
||||
//val inpVel = if (desiredVec.lengthSqr() > 0.001) desiredVec.normalize().scale(0.1) else Vec3.ZERO
|
||||
return vehicle.deltaMovement.add(desiredVec)
|
||||
//return newVelocity
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
@@ -196,8 +231,8 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
|
||||
val currSpeed = vehicle.deltaMovement.length()
|
||||
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalDolphin.handlingExpr!!)
|
||||
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))
|
||||
@@ -207,7 +242,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
controller.applyGlobalYaw(trueYawDelt.toFloat() * yawDampen)
|
||||
|
||||
// Correct orientation when at low speeds
|
||||
correctOrientation(settings, state, vehicle, controller, deltaTime)
|
||||
correctOrientation(settings, state, vehicle, controller, driver, deltaTime)
|
||||
|
||||
//yaw, pitch, roll
|
||||
return Vec3(0.0, state.currPitchCorrectionForce.get(), state.currRollCorrectionForce.get())
|
||||
@@ -218,16 +253,19 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
state: DolphinState,
|
||||
vehicle: PokemonEntity,
|
||||
controller: OrientationController,
|
||||
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)
|
||||
val maxRollCorrectionRate = 10.0f * (cos(controller.pitch.toRadians())).pow(2) * ((state.noInputTime.get() - rollCorrectionTimer) * 2).coerceIn(0.0, 1.0).toFloat()
|
||||
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.noInputTime.get() > rollCorrectionTimer) {
|
||||
|
||||
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()
|
||||
@@ -249,12 +287,14 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
}
|
||||
|
||||
//Calculate correcting pitch force.
|
||||
val maxPitchCorrectionRate = 10.0f * ((state.noInputTime.get() - rollCorrectionTimer) * 2).coerceIn(0.0, 1.0).toFloat()
|
||||
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 = 90.0
|
||||
if(state.noInputTime.get() > rollCorrectionTimer) {
|
||||
val arrivalInfluence = (min(pitchArrivalDeg,abs(controller.pitch).toDouble()) / pitchArrivalDeg)
|
||||
val desiredPitchForce = (0 - controller.pitch).sign * maxPitchCorrectionRate * arrivalInfluence
|
||||
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)
|
||||
|
||||
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
|
||||
var steeredPitchForce = desiredPitchForce - state.currPitchCorrectionForce.get()
|
||||
|
||||
// Ensure the 'steering' correction force doesn't exceed our maxForce
|
||||
@@ -288,7 +328,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
): Vec3 {
|
||||
// Begin the roll correction time counter if not enough mouse input
|
||||
// is detected
|
||||
if (abs(mouseX) < 1 && abs(mouseY) < 1 && (!vehicle.isInWater && !vehicle.isUnderWater)) {
|
||||
if (abs(mouseX) < 1 && abs(mouseY) < 1 && (vehicle.isInWater || vehicle.isUnderWater)) {
|
||||
state.noInputTime.set(state.noInputTime.get() + deltaTime)
|
||||
} else {
|
||||
state.noInputTime.set(0.0)
|
||||
@@ -339,8 +379,9 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
// Set roll to zero if transitioning to noroll config
|
||||
controller.rotateRoll(controller.roll * -1.0f)
|
||||
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
|
||||
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult)
|
||||
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
|
||||
@@ -383,8 +424,9 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
): Vec3 {
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0
|
||||
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult)
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalDolphin.speedExpr!!) / 20.0
|
||||
val boostingHandleDebuff = if (state.boosting.get()) 0.2 else 1.0
|
||||
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult ?: globalDolphin.mouseInputMult!!) * boostingHandleDebuff
|
||||
|
||||
//Smooth out mouse input.
|
||||
val smoothingSpeed = 4.0
|
||||
@@ -393,17 +435,17 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1, deltaTime * smoothingSpeed);
|
||||
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1, deltaTime * smoothingSpeed);
|
||||
val currSpeed = vehicle.deltaMovement.length()
|
||||
val hoverSpeed = topSpeed * 0.5
|
||||
|
||||
// Yaw locally when at or below hoverSpeed
|
||||
val localYaw = xInput * (1 - (RidingBehaviour.scaleToRange(currSpeed, 0.0, topSpeed))).coerceIn(0.2, 1.0)
|
||||
//else xInput * 0.4 * cos(controller.roll.toRadians()).coerceIn(0.0f, 1.0f)
|
||||
// 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
|
||||
|
||||
// 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)
|
||||
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
|
||||
@@ -411,7 +453,6 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
// 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.4*(abs(yInput - localPitch) * controller.roll.sign * -1.0)
|
||||
val rollForce = xInput * ((RidingBehaviour.scaleToRange(currSpeed, 0.0, topSpeed))).coerceIn(0.2, 1.0) //+ pitchInfluencedRollCorrection
|
||||
|
||||
//yaw, pitch, roll
|
||||
@@ -452,7 +493,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
vehicle: PokemonEntity,
|
||||
regularGravity: Double
|
||||
): Double {
|
||||
return if (!vehicle.isInWater && !vehicle.isUnderWater) 1.0 else 0.0
|
||||
return 0.0 //if (!vehicle.isInWater && !vehicle.isUnderWater) 1.0 else 0.0
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(
|
||||
@@ -461,14 +502,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
if (state.boosting.get()) {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0
|
||||
val boostMod = vehicle.runtime.resolveDouble(settings.jumpExpr)
|
||||
val normalizedBoostSpeed = RidingBehaviour.scaleToRange(state.speed.get(), topSpeed, topSpeed * boostMod)
|
||||
return 1.0f + normalizedBoostSpeed.pow(2).toFloat() * 0.2f
|
||||
} else {
|
||||
return 1.0f
|
||||
}
|
||||
return if (state.boosting.get()) 1.2f else 1.0f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity): Boolean {
|
||||
@@ -532,62 +566,66 @@ class DolphinSettings : RidingBehaviourSettings {
|
||||
override val key = DolphinBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var canJump = "true".asExpression()
|
||||
var infiniteStamina: Expression? = null
|
||||
private set
|
||||
|
||||
var reverseDriveFactor = "0.25".asExpression()
|
||||
var canJump: Expression? = null
|
||||
private set
|
||||
|
||||
var strafeFactor = "0.2".asExpression()
|
||||
var reverseDriveFactor: Expression? = null
|
||||
private set
|
||||
|
||||
var timeToRollCorrect: Expression = "0.5".asExpression()
|
||||
var strafeFactor: Expression? = null
|
||||
private set
|
||||
|
||||
var timeToRollCorrect: Expression? = null
|
||||
private set
|
||||
|
||||
// Boost influence when boosting
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'LIQUID', 2.2, 1.2)".asExpression()
|
||||
var jumpExpr: Expression? = null
|
||||
// Yaw rate in degrees per second
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'LIQUID', 100.0, 45.0)".asExpression()
|
||||
var horizontalPitchExpr: Expression = "q.get_ride_stats('SKILL', 'LIQUID', 0.4, 0.2)".asExpression()
|
||||
var mouseInputMult: Expression = "q.get_ride_stats('SKILL', 'LIQUID', 1.2, 0.8)".asExpression()
|
||||
var handlingExpr: Expression? = null
|
||||
var horizontalPitchExpr: Expression? = null
|
||||
var mouseInputMult: Expression? = null
|
||||
// Top speed in Bl/s
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'LIQUID', 15.0, 5.0)".asExpression()
|
||||
var speedExpr: Expression? = null
|
||||
// Seconds to get to top speed
|
||||
var accelerationExpr: Expression = "q.get_ride_stats('ACCELERATION', 'LIQUID', 2.0, 5.0)".asExpression()
|
||||
var accelerationExpr: Expression? = null
|
||||
// Seconds of boost
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'LIQUID', 7.0, 3.0)".asExpression()
|
||||
var staminaExpr: Expression? = null
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(canJump)
|
||||
buffer.writeExpression(reverseDriveFactor)
|
||||
buffer.writeExpression(strafeFactor)
|
||||
buffer.writeExpression(jumpExpr)
|
||||
buffer.writeExpression(handlingExpr)
|
||||
buffer.writeExpression(horizontalPitchExpr)
|
||||
buffer.writeExpression(mouseInputMult)
|
||||
buffer.writeExpression(speedExpr)
|
||||
buffer.writeExpression(accelerationExpr)
|
||||
buffer.writeExpression(staminaExpr)
|
||||
buffer.writeNullableExpression(infiniteStamina)
|
||||
buffer.writeNullableExpression(canJump)
|
||||
buffer.writeNullableExpression(reverseDriveFactor)
|
||||
buffer.writeNullableExpression(strafeFactor)
|
||||
buffer.writeNullableExpression(jumpExpr)
|
||||
buffer.writeNullableExpression(handlingExpr)
|
||||
buffer.writeNullableExpression(horizontalPitchExpr)
|
||||
buffer.writeNullableExpression(mouseInputMult)
|
||||
buffer.writeNullableExpression(speedExpr)
|
||||
buffer.writeNullableExpression(accelerationExpr)
|
||||
buffer.writeNullableExpression(staminaExpr)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
canJump = buffer.readExpression()
|
||||
reverseDriveFactor = buffer.readExpression()
|
||||
strafeFactor = buffer.readExpression()
|
||||
jumpExpr = buffer.readExpression()
|
||||
handlingExpr = buffer.readExpression()
|
||||
horizontalPitchExpr = buffer.readExpression()
|
||||
mouseInputMult = buffer.readExpression()
|
||||
speedExpr = buffer.readExpression()
|
||||
accelerationExpr = buffer.readExpression()
|
||||
staminaExpr = buffer.readExpression()
|
||||
infiniteStamina = buffer.readNullableExpression()
|
||||
canJump = buffer.readNullableExpression()
|
||||
reverseDriveFactor = buffer.readNullableExpression()
|
||||
strafeFactor = buffer.readNullableExpression()
|
||||
jumpExpr = buffer.readNullableExpression()
|
||||
handlingExpr = buffer.readNullableExpression()
|
||||
horizontalPitchExpr = buffer.readNullableExpression()
|
||||
mouseInputMult = buffer.readNullableExpression()
|
||||
speedExpr = buffer.readNullableExpression()
|
||||
accelerationExpr = buffer.readNullableExpression()
|
||||
staminaExpr = buffer.readNullableExpression()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -599,6 +637,7 @@ class DolphinState : RidingBehaviourState() {
|
||||
var boosting = ridingState(false, Side.BOTH)
|
||||
var boostIsToggleable = ridingState(false, Side.CLIENT)
|
||||
var speed = ridingState(0.0, Side.CLIENT)
|
||||
var hasBreached = ridingState(false, Side.CLIENT)
|
||||
|
||||
override fun encode(buffer: FriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
@@ -619,6 +658,7 @@ class DolphinState : RidingBehaviourState() {
|
||||
boosting.set(false, forced = true)
|
||||
boostIsToggleable.set(true, forced = true)
|
||||
speed.set(0.0, forced = true)
|
||||
hasBreached.set(false, forced = true)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
@@ -635,6 +675,7 @@ class DolphinState : RidingBehaviourState() {
|
||||
it.boosting.set(this.boosting.get(), forced = true)
|
||||
it.boostIsToggleable.set(this.boosting.get(), forced = true)
|
||||
it.speed.set(this.speed.get(), forced = true)
|
||||
it.hasBreached.set(this.hasBreached.get(), forced = true)
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
|
||||
@@ -42,9 +42,10 @@ class RideLoopSound(val ride: PokemonEntity, val soundSettings: RideSoundSetting
|
||||
var fade: Float = 0.0f
|
||||
var shouldMuffle: Boolean = soundSettings.muffleEnabled
|
||||
var rideAttenuation: RideAttenuationModel = soundSettings.attenuationModel
|
||||
var muffleAmount: Float = 1.0f // Currently slightly misleading. 1.0 is on muffle and 0.0 is full hFgain muting
|
||||
// TODO: revisit below flag once multi passenger support is enabled
|
||||
var isPassenger: Boolean = Minecraft.getInstance().player?.id == ride.controllingPassenger?.id
|
||||
var muffleAmount: Float = 1.0f // Currently slightly misleading. 1.0 is no muffle and 0.0 is full hFgain muting
|
||||
var isPassenger: Boolean = ride.passengers.any { passenger ->
|
||||
Minecraft.getInstance().player?.id == passenger.id
|
||||
}
|
||||
|
||||
init {
|
||||
this.looping = true
|
||||
|
||||
@@ -21,11 +21,11 @@ import net.minecraft.network.chat.Component
|
||||
enum class RidingStat(
|
||||
val flavour: Flavour
|
||||
) {
|
||||
SPEED(Flavour.SWEET),
|
||||
ACCELERATION(Flavour.SPICY),
|
||||
SKILL(Flavour.DRY),
|
||||
JUMP(Flavour.BITTER),
|
||||
STAMINA(Flavour.SOUR);
|
||||
SPEED(Flavour.SWEET),
|
||||
STAMINA(Flavour.SOUR),
|
||||
JUMP(Flavour.BITTER);
|
||||
|
||||
companion object {
|
||||
fun getByName(name: String) = RidingStat.entries.firstOrNull { it.name.equals(name, ignoreCase = true) }
|
||||
|
||||
@@ -34,7 +34,7 @@ object CobblemonSpawnPools {
|
||||
SpawnablePositionTypePrecalculation,
|
||||
BucketPrecalculation,
|
||||
BiomePrecalculation
|
||||
)
|
||||
), reloadable = true
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+1
-70
@@ -24,14 +24,12 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.PokeSnackBlockParticlesPacket
|
||||
import com.cobblemon.mod.common.util.math.pow
|
||||
import com.cobblemon.mod.common.util.toBlockPos
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.sqrt
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.entity.ai.village.poi.PoiManager
|
||||
import net.minecraft.world.entity.ai.village.poi.PoiType
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
@@ -114,73 +112,6 @@ object LurePokeSnackDetector : SpawningInfluenceDetector {
|
||||
}
|
||||
}
|
||||
|
||||
fun attemptSafeMove2(level: ServerLevel, startPos: BlockPos, entity: PokemonEntity, ) {
|
||||
val inWater = entity.isInWater
|
||||
|
||||
// Random offset in a cardinal direction (north, south, east, west)
|
||||
val randomDirection = listOf(Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST).random()
|
||||
val initialOffsetPos = startPos.relative(randomDirection)
|
||||
|
||||
// Variables to track the best (closest) safe position
|
||||
var safePos: BlockPos? = null
|
||||
var bestDistanceSq = Double.MAX_VALUE
|
||||
|
||||
val maxRadius = 10
|
||||
|
||||
// Search within a sphere around the initial offset position
|
||||
for (dx in -maxRadius..maxRadius) {
|
||||
for (dy in -maxRadius..maxRadius) {
|
||||
for (dz in -maxRadius..maxRadius) {
|
||||
val candidatePos = initialOffsetPos.offset(dx, dy, dz)
|
||||
val distanceSq = startPos.distSqr(candidatePos)
|
||||
|
||||
// Check if within sphere radius
|
||||
if (dx*dx + dy*dy + dz*dz <= maxRadius*maxRadius) {
|
||||
// Check collision at candidate position
|
||||
val currentBox = entity.boundingBox.move(
|
||||
candidatePos.x - entity.x,
|
||||
candidatePos.y - entity.y,
|
||||
candidatePos.z - entity.z
|
||||
)
|
||||
|
||||
if (level.noCollision(entity, currentBox)) {
|
||||
val blockBelowPos = candidatePos.below()
|
||||
val blockState = level.getBlockState(blockBelowPos)
|
||||
|
||||
val isWater = blockState.block == Blocks.WATER
|
||||
if (inWater) {
|
||||
if (isWater) {
|
||||
if (distanceSq < bestDistanceSq) {
|
||||
bestDistanceSq = distanceSq
|
||||
safePos = candidatePos
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!blockState.isAir) {
|
||||
if (distanceSq < bestDistanceSq) {
|
||||
bestDistanceSq = distanceSq
|
||||
safePos = candidatePos
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (safePos != null) {
|
||||
entity.moveTo(safePos.toVec3d())
|
||||
PokeSnackBlockParticlesPacket(startPos, safePos).sendToPlayersAround(
|
||||
startPos.x.toDouble(),
|
||||
startPos.y.toDouble(),
|
||||
startPos.z.toDouble(),
|
||||
64.0,
|
||||
level.dimension()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun attemptSafeMove(level: ServerLevel, startPos: BlockPos, entity: PokemonEntity) {
|
||||
val inWater = entity.isInWater
|
||||
val maxDistance = 10 // maximum search radius
|
||||
@@ -255,4 +186,4 @@ object LurePokeSnackDetector : SpawningInfluenceDetector {
|
||||
}
|
||||
|
||||
override fun detectFromBlock(level: ServerLevel, pos: BlockPos, blockState: BlockState) = emptyList<SpawningZoneInfluence>()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,6 +161,10 @@ open class PlayerPartyStore(
|
||||
if (pokemon.interactionCooldowns.any()) {
|
||||
pokemon.tickInteractionCooldown()
|
||||
}
|
||||
|
||||
if (pokemon.entity?.passengers?.isNotEmpty() != true) {
|
||||
pokemon.rideStamina += 0.1F // Recover all stamina in 10 seconds, as long as no one's on it
|
||||
}
|
||||
}
|
||||
// Friendship
|
||||
// ToDo expand this down the line just a very basic implementation for the first releases
|
||||
|
||||
@@ -100,7 +100,7 @@ class HeartyGrainsBlock(settings: Properties) : CropBlock(settings), SimpleWater
|
||||
override fun randomTick(state: BlockState, world: ServerLevel, pos: BlockPos, random: RandomSource) {
|
||||
if (state.getValue(HALF) == DoubleBlockHalf.UPPER || !canGrow(world, pos, state)) return
|
||||
|
||||
if (hasSufficientLight(world, pos) && random.nextInt(7) == 0) {
|
||||
if (hasSufficientLight(world, pos) && random.nextInt(16) == 0) {
|
||||
grow(world, pos, state, 1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block.campfirepot
|
||||
|
||||
enum class CampfirePotColor(val suffix: String) {
|
||||
RED("red"),
|
||||
BLUE("blue"),
|
||||
GREEN("green"),
|
||||
PINK("pink"),
|
||||
WHITE("white"),
|
||||
YELLOW("yellow"),
|
||||
BLACK("black");
|
||||
}
|
||||
@@ -56,7 +56,7 @@ class CookingPotMenu : RecipeBookMenu<CraftingInput, CookingPotRecipeBase>, Cont
|
||||
CampfireBlockEntity.Companion.CRAFTING_GRID_WIDTH
|
||||
)
|
||||
this.resultContainer = ResultContainer()
|
||||
this.containerData = SimpleContainerData(3)
|
||||
this.containerData = SimpleContainerData(4)
|
||||
this.addDataSlots(containerData)
|
||||
this.player = playerInventory.player
|
||||
this.level = playerInventory.player.level()
|
||||
@@ -280,4 +280,4 @@ class CookingPotMenu : RecipeBookMenu<CraftingInput, CookingPotRecipeBase>, Cont
|
||||
value: Int
|
||||
) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
-6
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.block.entity
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonRecipeTypes
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.cooking.Seasonings
|
||||
import com.cobblemon.mod.common.api.cooking.getColourMixFromSeasonings
|
||||
import com.cobblemon.mod.common.block.campfirepot.CampfireBlock
|
||||
import com.cobblemon.mod.common.item.components.PotComponent
|
||||
@@ -57,8 +58,8 @@ import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.gameevent.GameEvent
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import org.joml.Vector4f
|
||||
import com.cobblemon.mod.common.item.CampfirePotItem
|
||||
import java.util.*
|
||||
import kotlin.math.sign
|
||||
|
||||
class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlockEntity(
|
||||
CobblemonBlockEntities.CAMPFIRE,
|
||||
@@ -84,6 +85,7 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
|
||||
const val COOKING_PROGRESS_INDEX = 0
|
||||
const val COOKING_PROGRESS_TOTAL_TIME_INDEX = 1
|
||||
const val IS_LID_OPEN_INDEX = 2
|
||||
const val COOKING_POT_COLOR_INDEX = 3
|
||||
|
||||
const val BASE_BROTH_COLOR = 0xFDFACF
|
||||
const val BASE_BROTH_BUBBLE_COLOR = 0xFFFEFDE4.toInt()
|
||||
@@ -242,6 +244,7 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
|
||||
COOKING_PROGRESS_INDEX -> this@CampfireBlockEntity.cookingProgress
|
||||
COOKING_PROGRESS_TOTAL_TIME_INDEX -> this@CampfireBlockEntity.cookingTotalTime
|
||||
IS_LID_OPEN_INDEX -> if (this@CampfireBlockEntity.blockState.getValue(CampfireBlock.LID)) 0 else 1
|
||||
COOKING_POT_COLOR_INDEX -> (this@CampfireBlockEntity.getPotItem()?.item as? CampfirePotItem)?.color?.ordinal ?: 0
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
@@ -251,11 +254,12 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
|
||||
COOKING_PROGRESS_INDEX -> this@CampfireBlockEntity.cookingProgress = value
|
||||
COOKING_PROGRESS_TOTAL_TIME_INDEX -> this@CampfireBlockEntity.cookingTotalTime = value
|
||||
IS_LID_OPEN_INDEX -> this@CampfireBlockEntity.toggleLid(value == 1)
|
||||
COOKING_POT_COLOR_INDEX -> {}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getCount(): Int {
|
||||
return 3
|
||||
return 4
|
||||
}
|
||||
}
|
||||
|
||||
@@ -474,11 +478,12 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
|
||||
}
|
||||
|
||||
override fun canPlaceItemThroughFace(index: Int, itemStack: ItemStack, direction: Direction?): Boolean {
|
||||
return when (direction) {
|
||||
Direction.UP -> SEASONING_SLOTS.contains(index)
|
||||
else -> CRAFTING_GRID_SLOTS.contains(index)
|
||||
return if(direction == Direction.UP && Seasonings.isSeasoning(itemStack)){
|
||||
SEASONING_SLOTS.contains(index)
|
||||
} else {
|
||||
CRAFTING_GRID_SLOTS.contains(index)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,15 @@ import com.cobblemon.mod.common.client.gui.cookingpot.CookingPotScreen
|
||||
import com.cobblemon.mod.common.client.particle.BedrockParticleOptionsRepository
|
||||
import com.cobblemon.mod.common.client.render.ClientPlayerIcon
|
||||
import com.cobblemon.mod.common.client.render.DeferredRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.*
|
||||
import com.cobblemon.mod.common.client.render.block.BerryBlockRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.CampfireBlockEntityRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.DisplayCaseRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.FossilAnalyzerRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.GildedChestBlockRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.HealingMachineRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.LecternBlockEntityRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.PokeSnackBlockEntityRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.RestorationTankRenderer
|
||||
import com.cobblemon.mod.common.client.render.boat.CobblemonBoatRenderer
|
||||
import com.cobblemon.mod.common.client.render.color.AprijuiceItemColorProvider
|
||||
import com.cobblemon.mod.common.client.render.color.PokeBaitItemColorProvider
|
||||
@@ -42,7 +50,9 @@ import com.cobblemon.mod.common.client.render.item.CobblemonBuiltinItemRendererR
|
||||
import com.cobblemon.mod.common.client.render.item.PokemonItemRenderer
|
||||
import com.cobblemon.mod.common.client.render.layer.PokemonOnShoulderRenderer
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockAnimationRepository
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.*
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.BerryModelRepository
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.MiscModelRepository
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository
|
||||
import com.cobblemon.mod.common.client.render.npc.NPCRenderer
|
||||
import com.cobblemon.mod.common.client.render.pokeball.PokeBallRenderer
|
||||
import com.cobblemon.mod.common.client.render.pokemon.PokemonRenderer
|
||||
@@ -50,18 +60,25 @@ import com.cobblemon.mod.common.client.requests.ClientPlayerActionRequests
|
||||
import com.cobblemon.mod.common.client.sound.BattleMusicController
|
||||
import com.cobblemon.mod.common.client.sound.EntitySoundTracker
|
||||
import com.cobblemon.mod.common.client.storage.ClientStorageManager
|
||||
import com.cobblemon.mod.common.client.tooltips.*
|
||||
import com.cobblemon.mod.common.client.tooltips.AprijuiceTooltipGenerator
|
||||
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.SeasoningTooltipGenerator
|
||||
import com.cobblemon.mod.common.client.tooltips.TooltipManager
|
||||
import com.cobblemon.mod.common.client.trade.ClientTrade
|
||||
import com.cobblemon.mod.common.data.CobblemonDataProvider
|
||||
import com.cobblemon.mod.common.debug.RidingDebugRenderer
|
||||
import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.item.food.SinisterTeaItem
|
||||
import com.cobblemon.mod.common.platform.events.PlatformEvents
|
||||
import com.cobblemon.mod.common.pokedex.scanner.PokedexUsageContext
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.isLookingAt
|
||||
import io.github.flemmli97.debugutils.api.RegisterDebugRenderers
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.color.block.BlockColor
|
||||
import net.minecraft.client.color.item.ItemColor
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.screens.MenuScreens
|
||||
import net.minecraft.client.gui.screens.Screen
|
||||
@@ -104,7 +121,7 @@ object CobblemonClient {
|
||||
teamData = ClientPlayerTeamData()
|
||||
clientPokedexData = ClientPokedexManager(mutableMapOf())
|
||||
storage.onLogin()
|
||||
CobblemonDataProvider.canReload = false
|
||||
// CobblemonDataProvider.canReload = false
|
||||
}
|
||||
|
||||
fun onLogout() {
|
||||
@@ -113,7 +130,7 @@ object CobblemonClient {
|
||||
battleOverlay.onLogout()
|
||||
ClientTaskTracker.clear()
|
||||
checkedStarterScreen = false
|
||||
CobblemonDataProvider.canReload = true
|
||||
// CobblemonDataProvider.canReload = true
|
||||
DeferredRenderer.clearAll()
|
||||
ClientPlayerIcon.clear()
|
||||
}
|
||||
@@ -179,6 +196,11 @@ object CobblemonClient {
|
||||
}
|
||||
ClientPlayerIcon.onTick()
|
||||
}
|
||||
|
||||
val debugId = cobblemonResource("riding")
|
||||
RegisterDebugRenderers.registerCustomDebugRenderer(debugId, RidingDebugRenderer)
|
||||
RegisterDebugRenderers.registerServerToggle(debugId)
|
||||
RegisterDebugRenderers.registerClientHandler(debugId) { RidingDebugRenderer.clientHandler(it) }
|
||||
}
|
||||
|
||||
private fun registerTooltipManagers() {
|
||||
@@ -421,5 +443,4 @@ object CobblemonClient {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.client.persisted.ClientPersistedData
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import net.minecraft.client.CameraType
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.entity.Entity
|
||||
|
||||
/**
|
||||
* The purpose here is to keep track of the last camera type that was used for each riding behaviour or just
|
||||
* the last overall, then change the camera type where necessary when mounting/dismounting or switching
|
||||
* between different riding behaviours. This is dependent on a config option, but the general idea is we
|
||||
* want the camera to be whatever the player feels most comfy with without them having to fiddle with it
|
||||
* more than once.
|
||||
*/
|
||||
object MountedCameraTypeHandler {
|
||||
var unmountedCameraType: CameraType = CameraType.FIRST_PERSON
|
||||
|
||||
fun handleMount(passenger: Entity, vehicle: PokemonEntity) {
|
||||
val mc = Minecraft.getInstance()
|
||||
val key = vehicle.ridingController?.context?.settings?.key
|
||||
if (passenger != mc.player || key == null) {
|
||||
return
|
||||
}
|
||||
unmountedCameraType = mc.options.cameraType
|
||||
val data = ClientPersistedData.ridingPerspectives
|
||||
if (Cobblemon.config.rememberRidingCamera) {
|
||||
val lastCameraTypeForKey = data.perspectives[key]
|
||||
if (lastCameraTypeForKey != null) {
|
||||
mc.options.cameraType = lastCameraTypeForKey
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun handleDismount(passenger: Entity, vehicle: PokemonEntity) {
|
||||
val mc = Minecraft.getInstance()
|
||||
val key = vehicle.ridingController?.context?.settings?.key
|
||||
if (passenger != mc.player || key == null) {
|
||||
return
|
||||
}
|
||||
val currentCameraType = mc.options.cameraType
|
||||
if (currentCameraType != unmountedCameraType) {
|
||||
mc.options.cameraType = unmountedCameraType
|
||||
}
|
||||
val data = ClientPersistedData.ridingPerspectives
|
||||
data.updatePerspective(key, currentCameraType)
|
||||
}
|
||||
|
||||
fun handleTransition(fromKey: ResourceLocation, toKey: ResourceLocation) {
|
||||
val mc = Minecraft.getInstance()
|
||||
val currentCameraType = mc.options.cameraType
|
||||
val data = ClientPersistedData.ridingPerspectives
|
||||
data.updatePerspective(fromKey, currentCameraType)
|
||||
if (Cobblemon.config.rememberRidingCamera) {
|
||||
val lastCameraTypeForKey = data.perspectives[toKey]
|
||||
if (lastCameraTypeForKey != null) {
|
||||
mc.options.cameraType = lastCameraTypeForKey
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -135,7 +135,11 @@ class RideControlsOverlay : Gui(Minecraft.getInstance()) {
|
||||
// Configure what controls to show for each behaviour
|
||||
when (currentBehaviourKey) {
|
||||
BirdBehaviour.KEY -> {}
|
||||
BoatBehaviour.KEY -> {}
|
||||
BoatBehaviour.KEY -> {
|
||||
showVerticalMouse = false
|
||||
showHorizontalMouse = false
|
||||
showSneakKey = false
|
||||
}
|
||||
BurstBehaviour.KEY -> {}
|
||||
DolphinBehaviour.KEY -> {}
|
||||
GliderBehaviour.KEY -> {}
|
||||
|
||||
+2
@@ -12,7 +12,9 @@ import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.config.CobblemonConfig
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
import com.cobblemon.mod.common.util.ifServer
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.cobblemon.mod.common.util.runOnServer
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.components.Button
|
||||
import net.minecraft.client.gui.components.EditBox
|
||||
|
||||
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.client.gui.cookingpot
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.block.campfirepot.CampfirePotColor
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.components.Button
|
||||
@@ -21,6 +22,7 @@ import net.minecraft.sounds.SoundEvents
|
||||
class CookButton(
|
||||
pX: Int, pY: Int,
|
||||
var selected: Boolean = false,
|
||||
var color: CampfirePotColor,
|
||||
onPress: OnPress
|
||||
): Button(pX, pY, SIZE.toInt(), SIZE.toInt(), Component.literal("Cook"), onPress, DEFAULT_NARRATION) {
|
||||
|
||||
@@ -28,11 +30,11 @@ class CookButton(
|
||||
private const val SIZE = 20F
|
||||
|
||||
private val buttonResource = cobblemonResource("textures/gui/campfirepot/button.png")
|
||||
private val buttonIconResource = cobblemonResource("textures/item/campfire_pots/campfire_pot_red.png")
|
||||
private val buttonIconOpenResource = cobblemonResource("textures/item/campfire_pots/campfire_pot_red_open.png")
|
||||
}
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
|
||||
val closedIcon = cobblemonResource("textures/item/campfire_pots/campfire_pot_${color.suffix}.png")
|
||||
val openIcon = cobblemonResource("textures/item/campfire_pots/campfire_pot_${color.suffix}_open.png")
|
||||
blitk(
|
||||
matrixStack = context.pose(),
|
||||
texture = buttonResource,
|
||||
@@ -46,7 +48,7 @@ class CookButton(
|
||||
|
||||
blitk(
|
||||
matrixStack = context.pose(),
|
||||
texture = if (selected) buttonIconResource else buttonIconOpenResource,
|
||||
texture = if (selected) closedIcon else openIcon,
|
||||
x = x + 2,
|
||||
y = y + 2,
|
||||
width = 16,
|
||||
|
||||
+5
-1
@@ -11,8 +11,10 @@ package com.cobblemon.mod.common.client.gui.cookingpot
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendToServer
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.block.campfirepot.CampfirePotColor
|
||||
import com.cobblemon.mod.common.block.campfirepot.CookingPotMenu
|
||||
import com.cobblemon.mod.common.block.campfirepot.SeasoningSlot
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.COOKING_POT_COLOR_INDEX
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.IS_LID_OPEN_INDEX
|
||||
import com.cobblemon.mod.common.integration.jei.CobblemonJeiPlugin
|
||||
import com.cobblemon.mod.common.integration.jei.cooking.CampfirePotRecipeCategory
|
||||
@@ -118,7 +120,8 @@ class CookingPotScreen(
|
||||
this.titleLabelY = 6
|
||||
|
||||
if (::cookButton.isInitialized) removeWidget(cookButton)
|
||||
cookButton = CookButton(this.leftPos + 97, topPos + 56, menu.containerData.get(IS_LID_OPEN_INDEX) == 0) {
|
||||
val color = CampfirePotColor.entries[menu.containerData.get(COOKING_POT_COLOR_INDEX).coerceIn(0, CampfirePotColor.entries.lastIndex)]
|
||||
cookButton = CookButton(this.leftPos + 97, topPos + 56, menu.containerData.get(IS_LID_OPEN_INDEX) == 0, color) {
|
||||
val isLidClosed = menu.containerData.get(IS_LID_OPEN_INDEX) == 0
|
||||
sendToServer(ToggleCookingPotLidPacket(isLidClosed))
|
||||
}
|
||||
@@ -161,6 +164,7 @@ class CookingPotScreen(
|
||||
mouseY: Int,
|
||||
partialTicks: Float
|
||||
) {
|
||||
cookButton.color = CampfirePotColor.entries[menu.containerData.get(COOKING_POT_COLOR_INDEX).coerceIn(0, CampfirePotColor.entries.lastIndex)]
|
||||
cookButton.selected = menu.containerData.get(IS_LID_OPEN_INDEX) == 0
|
||||
cookButton.setPosition(this.leftPos + 97, topPos + 56)
|
||||
|
||||
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.debug.riding
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.CobblemonNetwork
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.net.messages.server.debug.ServerboundUpdateRidingSettingsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.debug.ServerboundUpdateRidingStatsPacket
|
||||
import com.cobblemon.mod.common.net.serverhandling.debug.ServerboundUpdateRidingSettingsHandler
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.getString
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.cobblemon.mod.common.util.resolve
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.components.Button
|
||||
import net.minecraft.client.gui.components.EditBox
|
||||
import net.minecraft.client.gui.screens.Screen
|
||||
import java.awt.Color
|
||||
|
||||
/**
|
||||
* Ride Settings Editor to change the underlying expressions and
|
||||
* stat mins and maxes of your ride on the fly (pun intended)
|
||||
*
|
||||
* Description
|
||||
*
|
||||
* @author Jackson
|
||||
* @since September 13, 2025
|
||||
*/
|
||||
class RideSettingsEditorGUI(val parentScreen: Screen, val vehicle: PokemonEntity, val rideStyle: RidingStyle, val rideSettings: RidingBehaviourSettings) : Screen(lang("ui.debug.riding_settings")), CobblemonRenderable {
|
||||
|
||||
private val settingWidgets = mutableListOf<Pair<String, EditBox>>()
|
||||
private val originalValues = mutableMapOf<String, String>() // Used to keep track of saved state of editboxes
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
clearWidgets()
|
||||
|
||||
// Take note of the current saved values in the settings
|
||||
if (originalValues.isEmpty()) {
|
||||
val expressionVariables = getExpressionVariables(rideSettings)
|
||||
for ((name, value) in expressionVariables) {
|
||||
originalValues[name] = value
|
||||
}
|
||||
}
|
||||
|
||||
addRenderableWidget(
|
||||
Button.builder("Back".text()) {
|
||||
this.minecraft?.setScreen(parentScreen)
|
||||
}.bounds(10, 10, 40, 20).build()
|
||||
)
|
||||
|
||||
addRenderableWidget(
|
||||
Button.builder("Save".text()) { button ->
|
||||
saveRideSettings()
|
||||
}.bounds(300 + 110, 10, 40, 20).build()
|
||||
)
|
||||
|
||||
createExpressionEditBoxes()
|
||||
|
||||
}
|
||||
|
||||
override fun render(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int, partialTick: Float) {
|
||||
// Make the background blank and transparent?
|
||||
guiGraphics.fill(0, 0, width, height, Color(0, 0, 0, 100).rgb)
|
||||
|
||||
super.render(guiGraphics, mouseX, mouseY, partialTick)
|
||||
|
||||
for ((name, editBox) in settingWidgets) {
|
||||
// Position the text next to the EditBox
|
||||
guiGraphics.drawString(font, name, editBox.x - 105, editBox.y + 5, 0xFFFFFF)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun createExpressionEditBoxes() {
|
||||
val expressionVariables = getExpressionVariables(rideSettings)
|
||||
var currentY = 40
|
||||
for (variable in expressionVariables) {
|
||||
val (name, value) = variable
|
||||
|
||||
val editBox = EditBox(this.minecraft!!.font, 110, currentY, 300, 20, name.text())
|
||||
editBox.setMaxLength(256)
|
||||
editBox.value = value
|
||||
|
||||
// Dry run expression changes and if valid then turn white and if
|
||||
// invalid then turn red. If it remains the same it is green
|
||||
editBox.setResponder { newExpression ->
|
||||
when {
|
||||
!validateExpression(newExpression) -> editBox.setTextColor(0xFF5555)
|
||||
newExpression == originalValues[name] -> editBox.setTextColor(0x55FF55)
|
||||
else -> editBox.setTextColor(0xFFFFFF)
|
||||
}
|
||||
}
|
||||
|
||||
// Set all Boxes to green as they are currently saved
|
||||
editBox.setTextColor(0x55FF55)
|
||||
|
||||
// Add widget
|
||||
addRenderableWidget(editBox)
|
||||
// Add widget to list
|
||||
settingWidgets.add(name to editBox)
|
||||
|
||||
currentY += 25
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveRideSettings() {
|
||||
for ((name, editBox) in settingWidgets) {
|
||||
// If expression is valid then save and turn green
|
||||
if (validateExpression(editBox.value)) {
|
||||
ServerboundUpdateRidingSettingsHandler.modifyRideSettingsExpression(
|
||||
vehicle,
|
||||
rideStyle,
|
||||
name,
|
||||
editBox.value
|
||||
)
|
||||
// Update the running list of last saved values too
|
||||
originalValues[name] = editBox.value
|
||||
|
||||
// Update server with
|
||||
CobblemonNetwork.sendToServer(
|
||||
ServerboundUpdateRidingSettingsPacket(
|
||||
vehicle.id,
|
||||
rideStyle,
|
||||
name,
|
||||
editBox.value
|
||||
)
|
||||
)
|
||||
editBox.setTextColor(0x55FF55)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun validateExpression(expression: String): Boolean {
|
||||
return try {
|
||||
// Attempt to parse as expression
|
||||
val parsedExpression = expression.asExpression()
|
||||
// Dry run to ensure its not borked.
|
||||
vehicle.runtime.resolve(parsedExpression)
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private fun getExpressionVariables(settings: RidingBehaviourSettings): List<Pair<String, String>> {
|
||||
val clazz = settings.javaClass
|
||||
val fields = clazz.declaredFields
|
||||
|
||||
// Iterate through all expression fields and return the list of those that
|
||||
// aer expressions
|
||||
return fields.mapNotNull { field ->
|
||||
if (field.type == Expression::class.java) {
|
||||
field.isAccessible = true // Make private fields accessible
|
||||
val name = field.name
|
||||
// Get the value, cast it, and call .getString() on it
|
||||
val value = (field.get(settings) as Expression).getString()
|
||||
|
||||
// Create the pair and return it for the list
|
||||
name to value
|
||||
} else {
|
||||
// If it's not an Expression (they all honestly should be if being used by the
|
||||
// behaviour in question) then ignore
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
-17
@@ -33,26 +33,28 @@ class RidingStatsDebugGUI(val vehicle: PokemonEntity) : Screen(lang("ui.debug.ri
|
||||
|
||||
var ridingStyle: RidingStyle = RidingStyle.LAND
|
||||
|
||||
val changeRidingStyle: Button
|
||||
private lateinit var changeRidingStyle: Button
|
||||
|
||||
val speedSlider: SettingsSlider
|
||||
val accelerationSlider: SettingsSlider
|
||||
val skillSlider: SettingsSlider
|
||||
val jumpSlider: SettingsSlider
|
||||
val staminaSlider: SettingsSlider
|
||||
private lateinit var speedSlider: SettingsSlider
|
||||
private lateinit var accelerationSlider: SettingsSlider
|
||||
private lateinit var skillSlider: SettingsSlider
|
||||
private lateinit var jumpSlider: SettingsSlider
|
||||
private lateinit var staminaSlider: SettingsSlider
|
||||
|
||||
val minSpeedInput: RidingStatInputWidget
|
||||
val maxSpeedInput: RidingStatInputWidget
|
||||
val minAccelerationInput: RidingStatInputWidget
|
||||
val maxAccelerationInput: RidingStatInputWidget
|
||||
val minSkillInput: RidingStatInputWidget
|
||||
val maxSkillInput: RidingStatInputWidget
|
||||
val minJumpInput: RidingStatInputWidget
|
||||
val maxJumpInput: RidingStatInputWidget
|
||||
val minStaminaInput: RidingStatInputWidget
|
||||
val maxStaminaInput: RidingStatInputWidget
|
||||
private lateinit var minSpeedInput: RidingStatInputWidget
|
||||
private lateinit var maxSpeedInput: RidingStatInputWidget
|
||||
private lateinit var minAccelerationInput: RidingStatInputWidget
|
||||
private lateinit var maxAccelerationInput: RidingStatInputWidget
|
||||
private lateinit var minSkillInput: RidingStatInputWidget
|
||||
private lateinit var maxSkillInput: RidingStatInputWidget
|
||||
private lateinit var minJumpInput: RidingStatInputWidget
|
||||
private lateinit var maxJumpInput: RidingStatInputWidget
|
||||
private lateinit var minStaminaInput: RidingStatInputWidget
|
||||
private lateinit var maxStaminaInput: RidingStatInputWidget
|
||||
|
||||
init {
|
||||
public override fun init() {
|
||||
super.init()
|
||||
clearWidgets()
|
||||
ridingStyle = vehicle.ifRidingAvailableSupply(RidingStyle.LAND) { behaviour, settings, state ->
|
||||
behaviour.getRidingStyle(settings, state)
|
||||
}
|
||||
@@ -94,6 +96,15 @@ class RidingStatsDebugGUI(val vehicle: PokemonEntity) : Screen(lang("ui.debug.ri
|
||||
}.bounds(getScaledWidth() - 210, 220, 200, 20).build()
|
||||
)
|
||||
|
||||
addRenderableWidget(
|
||||
Button.builder("Edit Ride Settings".text()) { button ->
|
||||
// Grab the ride settings for the currently being configured style
|
||||
val rideSettings = vehicle.ridingController?.behaviours?.get(ridingStyle) ?: return@builder
|
||||
// Open up GUI for editing the given settings
|
||||
this.minecraft?.setScreen(RideSettingsEditorGUI(this, vehicle, ridingStyle, rideSettings))
|
||||
}.bounds(10, 240, 200, 20).build()
|
||||
)
|
||||
|
||||
refresh()
|
||||
}
|
||||
|
||||
|
||||
@@ -266,6 +266,7 @@ class PCGUI(
|
||||
tooltipKey = "ui.sort.${typeName}",
|
||||
label = "sort_${typeName}"
|
||||
) {
|
||||
storageWidget.resetSelected()
|
||||
SortPCBoxPacket(pc.uuid, storageWidget.box, sortType, hasShiftDown()).sendToServer()
|
||||
}.also {
|
||||
it.visible = displayOptions
|
||||
@@ -719,7 +720,7 @@ class PCGUI(
|
||||
if (pasture.pastureScrollList.isHovered) pasture.pastureScrollList.mouseScrolled(mouseX, mouseY, amount, verticalAmount)
|
||||
}
|
||||
if (storageWidget.isHovered && mouseX < (storageWidget.x + StorageWidget.SCREEN_WIDTH)) this.storageWidget.box -= verticalAmount.toInt()
|
||||
if (wallpaperWidget.isHovered) wallpaperWidget.mouseScrolled(mouseX, mouseY, amount, verticalAmount)
|
||||
if (::wallpaperWidget.isInitialized && wallpaperWidget.isHovered) wallpaperWidget.mouseScrolled(mouseX, mouseY, amount, verticalAmount)
|
||||
|
||||
val infoBoxX = ((width - BASE_WIDTH) / 2) + 9
|
||||
val infoBoxY = ((height - BASE_HEIGHT) / 2) + 128
|
||||
|
||||
@@ -492,7 +492,7 @@ class StorageWidget(
|
||||
}
|
||||
}
|
||||
|
||||
private fun resetSelected() {
|
||||
fun resetSelected() {
|
||||
selectedPosition = null
|
||||
grabbedSlot = null
|
||||
pcGui.setPreviewPokemon(null)
|
||||
|
||||
@@ -82,9 +82,6 @@ class PokedexGUI private constructor(
|
||||
private val arrowUpIcon = cobblemonResource("textures/gui/pokedex/arrow_up.png")
|
||||
private val arrowDownIcon = cobblemonResource("textures/gui/pokedex/arrow_down.png")
|
||||
|
||||
private val tooltipEdge = cobblemonResource("textures/gui/pokedex/tooltip_edge.png")
|
||||
private val tooltipBackground = cobblemonResource("textures/gui/pokedex/tooltip_background.png")
|
||||
|
||||
private val tabSelectArrow = cobblemonResource("textures/gui/pokedex/select_arrow.png")
|
||||
private val tabIcons = arrayOf(
|
||||
cobblemonResource("textures/gui/pokedex/tab_info.png"),
|
||||
@@ -325,17 +322,15 @@ class PokedexGUI private constructor(
|
||||
|
||||
// Search type tooltip
|
||||
if (searchByTypeButton.isButtonHovered(mouseX, mouseY)) {
|
||||
matrices.pushPose()
|
||||
matrices.translate(0.0, 0.0, 1000.0)
|
||||
val searchTypeText = lang("ui.pokedex.search.search_by", lang("ui.pokedex.search.type.${selectedSearchByType.name.lowercase()}")).bold()
|
||||
val searchTypeTextWidth = Minecraft.getInstance().font.width(searchTypeText.font(CobblemonResources.DEFAULT_LARGE))
|
||||
val tooltipWidth = searchTypeTextWidth + 6
|
||||
|
||||
blitk(matrixStack = matrices, texture = tooltipEdge, x = mouseX - (tooltipWidth / 2) - 1, y = mouseY - 16, width = 1, height = 11)
|
||||
blitk(matrixStack = matrices, texture = tooltipBackground, x = mouseX - (tooltipWidth / 2), y = mouseY - 16, width = tooltipWidth, height = 11)
|
||||
blitk(matrixStack = matrices, texture = tooltipEdge, x = mouseX + (tooltipWidth / 2), y = mouseY - 16, width = 1, height = 11)
|
||||
drawScaledText(context = context, font = CobblemonResources.DEFAULT_LARGE, text = searchTypeText, x = mouseX, y = mouseY - 15, shadow = true, centered = true)
|
||||
matrices.popPose()
|
||||
renderTooltip(
|
||||
context,
|
||||
searchTypeText,
|
||||
mouseX,
|
||||
mouseY,
|
||||
delta,
|
||||
-14
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -481,6 +476,8 @@ class PokedexGUI private constructor(
|
||||
|
||||
fun displaytabInfoElement(tabIndex: Int, update: Boolean = true) {
|
||||
val showActiveTab = selectedEntry?.let { selectedForm in CobblemonClient.clientPokedexData.getCaughtForms(it) } == true
|
||||
var statSubIndex = 0
|
||||
|
||||
if (tabButtons.isNotEmpty() && tabButtons.size > tabIndex) {
|
||||
tabButtons.forEachIndexed { index, tab -> tab.isWidgetActive = showActiveTab && index == tabIndex
|
||||
}
|
||||
@@ -491,6 +488,14 @@ class PokedexGUI private constructor(
|
||||
removeWidget((tabInfoElement as AbilitiesWidget).rightButton)
|
||||
}
|
||||
|
||||
if (tabInfoIndex == TAB_STATS && tabInfoElement is StatsWidget) {
|
||||
statSubIndex = (tabInfoElement as StatsWidget).selectedStatTypeIndex
|
||||
removeWidget((tabInfoElement as StatsWidget).leftButton)
|
||||
removeWidget((tabInfoElement as StatsWidget).rightButton)
|
||||
removeWidget((tabInfoElement as StatsWidget).leftSubButton)
|
||||
removeWidget((tabInfoElement as StatsWidget).rightSubButton)
|
||||
}
|
||||
|
||||
tabInfoIndex = tabIndex
|
||||
if (::tabInfoElement.isInitialized) removeWidget(tabInfoElement)
|
||||
|
||||
@@ -508,7 +513,9 @@ class PokedexGUI private constructor(
|
||||
tabInfoElement = SizeWidget(x + 180, y + 135)
|
||||
}
|
||||
TAB_STATS -> {
|
||||
tabInfoElement = StatsWidget(x + 180, y + 135)
|
||||
tabInfoElement = StatsWidget(x + 180, y + 135).also {
|
||||
it.selectedStatTypeIndex = statSubIndex
|
||||
}
|
||||
}
|
||||
TAB_DROPS -> {
|
||||
tabInfoElement = DropsScrollingWidget(x + 189, y + 135)
|
||||
@@ -555,6 +562,17 @@ class PokedexGUI private constructor(
|
||||
}
|
||||
TAB_STATS -> {
|
||||
(tabInfoElement as StatsWidget).baseStats = form.baseStats
|
||||
(tabInfoElement as StatsWidget).rideProperties = form.riding
|
||||
|
||||
form.riding.behaviours?.let {
|
||||
addRenderableWidget((tabInfoElement as StatsWidget).leftButton)
|
||||
addRenderableWidget((tabInfoElement as StatsWidget).rightButton)
|
||||
|
||||
if (it.size > 1) {
|
||||
addRenderableWidget((tabInfoElement as StatsWidget).leftSubButton)
|
||||
addRenderableWidget((tabInfoElement as StatsWidget).rightSubButton)
|
||||
}
|
||||
}
|
||||
}
|
||||
TAB_DROPS -> {
|
||||
(tabInfoElement as DropsScrollingWidget).dropTable = form.drops
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.pokedex
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.text.font
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
|
||||
private val tooltipEdge = cobblemonResource("textures/gui/pokedex/tooltip_edge.png")
|
||||
private val tooltipBackground = cobblemonResource("textures/gui/pokedex/tooltip_background.png")
|
||||
|
||||
fun renderTooltip(context: GuiGraphics, text: MutableComponent, posX: Int, posY: Int, delta: Float, offsetY: Int = 0) {
|
||||
val poseStack = context.pose()
|
||||
val textWidth = Minecraft.getInstance().font.width(text.font(CobblemonResources.DEFAULT_LARGE))
|
||||
val tooltipWidth = textWidth + 6
|
||||
val tooltipHeight = 11
|
||||
val tooltipTop = posY + offsetY
|
||||
|
||||
poseStack.pushPose()
|
||||
poseStack.translate(0.0, 0.0, 1000.0)
|
||||
Minecraft.getInstance().let {
|
||||
it.mainRenderTarget.bindWrite(false)
|
||||
context.enableScissor(
|
||||
posX - (tooltipWidth / 2),
|
||||
tooltipTop + 1,
|
||||
(posX - (tooltipWidth / 2)) + tooltipWidth,
|
||||
tooltipTop + 10
|
||||
)
|
||||
it.gameRenderer.processBlurEffect(delta)
|
||||
context.disableScissor()
|
||||
it.mainRenderTarget.bindWrite(true)
|
||||
}
|
||||
|
||||
blitk(matrixStack = poseStack, texture = tooltipEdge, x = posX - (tooltipWidth / 2) - 1, y = tooltipTop, width = 1, height = tooltipHeight)
|
||||
blitk(matrixStack = poseStack, texture = tooltipBackground, x = posX - (tooltipWidth / 2), y = tooltipTop, width = tooltipWidth, height = tooltipHeight)
|
||||
blitk(matrixStack = poseStack, texture = tooltipEdge, x = posX + (tooltipWidth / 2), y = tooltipTop, width = 1, height = tooltipHeight)
|
||||
drawScaledText(context = context, font = CobblemonResources.DEFAULT_LARGE, text = text, x = posX, y = tooltipTop + 1, shadow = true, centered = true)
|
||||
poseStack.popPose()
|
||||
}
|
||||
@@ -31,6 +31,7 @@ class ScaledButton(
|
||||
): Button(buttonX.toInt(), buttonY.toInt(), buttonWidth.toInt(), buttonHeight.toInt(), "".text(), clickAction, DEFAULT_NARRATION), CobblemonRenderable {
|
||||
|
||||
var isWidgetActive = false
|
||||
var isVisible = true
|
||||
|
||||
override fun mouseDragged(d: Double, e: Double, i: Int, f: Double, g: Double) = false
|
||||
override fun defaultButtonNarrationText(builder: NarrationElementOutput) {
|
||||
@@ -38,8 +39,7 @@ class ScaledButton(
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
|
||||
val matrices = context.pose()
|
||||
|
||||
if (resource != null) {
|
||||
if (isVisible && resource != null) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = resource,
|
||||
@@ -55,14 +55,14 @@ class ScaledButton(
|
||||
}
|
||||
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
if (active && isButtonHovered(mouseX, mouseY)) {
|
||||
if (isVisible && active && isButtonHovered(mouseX, mouseY)) {
|
||||
super.mouseClicked(mouseX, mouseY, button)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun playDownSound(soundManager: SoundManager) {
|
||||
if (active && !this.silent) {
|
||||
if (isVisible && active && !this.silent) {
|
||||
soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.POKEDEX_CLICK_SHORT, 1.0F))
|
||||
}
|
||||
}
|
||||
@@ -71,4 +71,4 @@ class ScaledButton(
|
||||
return mouseX.toFloat() in (buttonX..(buttonX + (buttonWidth.toFloat() * scale)))
|
||||
&& mouseY.toFloat() in (buttonY..(buttonY + (buttonHeight.toFloat() * scale)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-5
@@ -48,12 +48,11 @@ class AbilitiesWidget(x: Int, y: Int): InfoTextScrollWidget(pX = x, pY = y) {
|
||||
init { setAbility() }
|
||||
|
||||
private fun switchAbility(nextIndex: Boolean) {
|
||||
if (nextIndex) {
|
||||
if (selectedAbilitiesIndex < abilitiesList.lastIndex) selectedAbilitiesIndex++
|
||||
else selectedAbilitiesIndex = 0
|
||||
val size = abilitiesList.size
|
||||
selectedAbilitiesIndex = if (nextIndex) {
|
||||
(selectedAbilitiesIndex + 1) % size
|
||||
} else {
|
||||
if (selectedAbilitiesIndex > 0) selectedAbilitiesIndex--
|
||||
else selectedAbilitiesIndex = abilitiesList.lastIndex
|
||||
(selectedAbilitiesIndex - 1 + size) % size
|
||||
}
|
||||
setAbility()
|
||||
}
|
||||
|
||||
+9
-10
@@ -33,6 +33,7 @@ import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.PORTRAIT_
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.PORTRAIT_POKE_BALL_WIDTH
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.SCALE
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.ScaledButton
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.renderTooltip
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.client.render.drawScaledTextJustifiedRight
|
||||
@@ -94,9 +95,6 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
||||
private val buttonGenderFemale = cobblemonResource("textures/gui/pokedex/button_female.png")
|
||||
private val buttonNone = cobblemonResource("textures/gui/pokedex/button_none.png")
|
||||
private val buttonShiny = cobblemonResource("textures/gui/pokedex/button_shiny.png")
|
||||
|
||||
private val tooltipEdge = cobblemonResource("textures/gui/pokedex/tooltip_edge.png")
|
||||
private val tooltipBackground = cobblemonResource("textures/gui/pokedex/tooltip_background.png")
|
||||
}
|
||||
|
||||
var currentEntry : PokedexEntry? = null
|
||||
@@ -440,13 +438,14 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
||||
// Tooltip
|
||||
if (it.isVisible() && it.getWidget().isButtonHovered(mouseX, mouseY)) {
|
||||
val variationText = it.variation.displayName.asTranslated().bold()
|
||||
val variationTextWidth = Minecraft.getInstance().font.width(variationText.font(CobblemonResources.DEFAULT_LARGE))
|
||||
val tooltipWidth = variationTextWidth + 6
|
||||
|
||||
blitk(matrixStack = matrices, texture = tooltipEdge, x = mouseX - (tooltipWidth / 2) - 1, y = mouseY + 8, width = 1, height = 11)
|
||||
blitk(matrixStack = matrices, texture = tooltipBackground, x = mouseX - (tooltipWidth / 2), y = mouseY + 8, width = tooltipWidth, height = 11)
|
||||
blitk(matrixStack = matrices, texture = tooltipEdge, x = mouseX + (tooltipWidth / 2), y = mouseY + 8, width = 1, height = 11)
|
||||
drawScaledText(context = context, font = CobblemonResources.DEFAULT_LARGE, text = variationText, x = mouseX, y = mouseY + 9, shadow = true, centered = true)
|
||||
renderTooltip(
|
||||
context,
|
||||
variationText,
|
||||
mouseX,
|
||||
mouseY,
|
||||
delta,
|
||||
10
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+218
-17
@@ -11,18 +11,24 @@ package com.cobblemon.mod.common.client.gui.pokedex.widgets
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stat
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.riding.RidingProperties
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.HALF_OVERLAY_WIDTH
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.POKEMON_DESCRIPTION_HEIGHT
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.SCALE
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.ScaledButton
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.renderTooltip
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.client.render.drawScaledTextJustifiedRight
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.util.FastColor
|
||||
|
||||
class StatsWidget(val pX: Int, val pY: Int) : SoundlessWidget(
|
||||
pX,
|
||||
@@ -33,6 +39,18 @@ class StatsWidget(val pX: Int, val pY: Int) : SoundlessWidget(
|
||||
) {
|
||||
|
||||
companion object {
|
||||
const val STAT_TYPE_BASE = 0
|
||||
const val STAT_TYPE_RIDE = 1
|
||||
const val MAX_BAR_WIDTH = 75
|
||||
|
||||
private val statTypes = listOf(STAT_TYPE_BASE, STAT_TYPE_RIDE)
|
||||
|
||||
private val divider = cobblemonResource("textures/gui/pokedex/info_stat_divider.png")
|
||||
private val arrowLeft = cobblemonResource("textures/gui/pokedex/info_arrow_left.png")
|
||||
private val arrowRight = cobblemonResource("textures/gui/pokedex/info_arrow_right.png")
|
||||
private val subArrowLeft = cobblemonResource("textures/gui/pokedex/info_sub_arrow_left.png")
|
||||
private val subArrowRight = cobblemonResource("textures/gui/pokedex/info_sub_arrow_right.png")
|
||||
|
||||
private val statLabels = arrayOf(
|
||||
lang("ui.stats.hp"),
|
||||
lang("ui.stats.atk"),
|
||||
@@ -52,12 +70,91 @@ class StatsWidget(val pX: Int, val pY: Int) : SoundlessWidget(
|
||||
)
|
||||
}
|
||||
|
||||
val leftButton: ScaledButton = ScaledButton(
|
||||
pX + 2.5F,
|
||||
pY - 8F,
|
||||
7,
|
||||
10,
|
||||
arrowLeft,
|
||||
clickAction = { switchStatType(false) }
|
||||
)
|
||||
|
||||
val rightButton: ScaledButton = ScaledButton(
|
||||
pX + 133F,
|
||||
pY - 8F,
|
||||
7,
|
||||
10,
|
||||
arrowRight,
|
||||
clickAction = { switchStatType(true) }
|
||||
)
|
||||
|
||||
val leftSubButton: ScaledButton = ScaledButton(
|
||||
pX + 2.5F,
|
||||
pY + 18.5F,
|
||||
7,
|
||||
10,
|
||||
subArrowLeft,
|
||||
clickAction = { switchRideBehaviour(false) }
|
||||
)
|
||||
|
||||
val rightSubButton: ScaledButton = ScaledButton(
|
||||
pX + 133F,
|
||||
pY + 18.5F,
|
||||
7,
|
||||
10,
|
||||
subArrowRight,
|
||||
clickAction = { switchRideBehaviour(true) }
|
||||
)
|
||||
|
||||
var selectedRideBehavioursIndex: Int = 0
|
||||
|
||||
var selectedStatTypeIndex: Int = STAT_TYPE_BASE
|
||||
set(value) {
|
||||
field = value
|
||||
|
||||
val isRideStatVisible = value == STAT_TYPE_RIDE
|
||||
leftSubButton.visible = isRideStatVisible
|
||||
rightSubButton.visible = isRideStatVisible
|
||||
}
|
||||
|
||||
var baseStats: Map<Stat, Int>? = null
|
||||
var rideProperties: RidingProperties? = null
|
||||
|
||||
private fun switchStatType(nextIndex: Boolean) {
|
||||
val size = statTypes.size
|
||||
selectedStatTypeIndex = if (nextIndex) {
|
||||
(selectedStatTypeIndex + 1) % size
|
||||
} else {
|
||||
(selectedStatTypeIndex - 1 + size) % size
|
||||
}
|
||||
|
||||
val showSubArrowButtons = selectedStatTypeIndex == STAT_TYPE_RIDE
|
||||
leftSubButton.visible = showSubArrowButtons
|
||||
rightSubButton.visible = showSubArrowButtons
|
||||
}
|
||||
|
||||
private fun switchRideBehaviour(nextIndex: Boolean) {
|
||||
val size = rideProperties?.behaviours?.size ?: 0
|
||||
selectedRideBehavioursIndex = if (nextIndex) {
|
||||
(selectedRideBehavioursIndex + 1) % size
|
||||
} else {
|
||||
(selectedRideBehavioursIndex - 1 + size) % size
|
||||
}
|
||||
}
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
if (baseStats == null) return
|
||||
when (selectedStatTypeIndex) {
|
||||
STAT_TYPE_BASE -> {
|
||||
renderBaseStats(context)
|
||||
}
|
||||
STAT_TYPE_RIDE -> {
|
||||
renderRideStats(context, mouseX, mouseY, delta)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val matrices = context.pose()
|
||||
private fun renderBaseStats(context: GuiGraphics) {
|
||||
if (baseStats == null) return
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
@@ -68,18 +165,9 @@ class StatsWidget(val pX: Int, val pY: Int) : SoundlessWidget(
|
||||
shadow = true
|
||||
)
|
||||
|
||||
// Divider
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = CobblemonResources.WHITE,
|
||||
x = pX + 50.5,
|
||||
y = pY + 4,
|
||||
height = 33.5,
|
||||
width = 1,
|
||||
red = 0.71F,
|
||||
green = 0.95F,
|
||||
blue = 0.95F
|
||||
)
|
||||
val matrices = context.pose()
|
||||
|
||||
blitk(matrixStack = matrices, texture = divider, x = pX + 51, y = pY + 4, height = 33.5, width = 1)
|
||||
|
||||
for (i in statLabels.indices) {
|
||||
drawScaledText(
|
||||
@@ -107,10 +195,10 @@ class StatsWidget(val pX: Int, val pY: Int) : SoundlessWidget(
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = CobblemonResources.WHITE,
|
||||
x = pX + 54.5,
|
||||
x = pX + 55,
|
||||
y = pY + 4 + (6 * i),
|
||||
height = 3.5F,
|
||||
width = (statValue / 255F) * 77,
|
||||
width = (statValue / 255F) * MAX_BAR_WIDTH,
|
||||
red = red,
|
||||
green = green,
|
||||
blue = blue
|
||||
@@ -119,6 +207,119 @@ class StatsWidget(val pX: Int, val pY: Int) : SoundlessWidget(
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderRideStats(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
rideProperties?.behaviours?.let { behaviours ->
|
||||
val selectedBehaviour = behaviours.entries.toList()[selectedRideBehavioursIndex]
|
||||
val behaviourStats = selectedBehaviour.value.stats
|
||||
|
||||
val matrices = context.pose()
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = Component.translatable("cobblemon.ui.pokedex.info.stats_ride").bold(),
|
||||
x = pX + 9,
|
||||
y = pY - 10,
|
||||
shadow = true
|
||||
)
|
||||
|
||||
rideProperties?.seats?.size?.let { seats ->
|
||||
drawScaledTextJustifiedRight(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = Component.translatable("cobblemon.ui.pokedex.info.stats_ride.seats", seats).bold(),
|
||||
x = pX + 130,
|
||||
y = pY - 10,
|
||||
shadow = true
|
||||
)
|
||||
}
|
||||
|
||||
blitk(matrixStack = matrices, texture = divider, x = pX + 51, y = pY + 10, height = 27.5, width = 1)
|
||||
blitk(matrixStack = matrices, texture = divider, x = pX + 9, y = pY + 2, height = 6, width = 121)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = lang("ui.ride_style.${selectedBehaviour.key.name.lowercase()}").bold(),
|
||||
x = pX + 69.5,
|
||||
y = pY + 3,
|
||||
colour = 0x3A96B6,
|
||||
scale = SCALE,
|
||||
centered = true
|
||||
)
|
||||
|
||||
RidingStat.entries.forEachIndexed { index, stat ->
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = lang("ui.stats.ride.${stat.name.lowercase()}").bold(),
|
||||
x = pX + 9,
|
||||
y = pY + 10 + (6 * index),
|
||||
colour = 0x606B6E,
|
||||
scale = SCALE
|
||||
)
|
||||
|
||||
behaviourStats[stat]?.let { range ->
|
||||
val startPosY = pY + 10 + (6F * index)
|
||||
|
||||
val red = FastColor.ARGB32.red(stat.flavour.colour) / 255F
|
||||
val green = FastColor.ARGB32.green(stat.flavour.colour) / 255F
|
||||
val blue = FastColor.ARGB32.blue(stat.flavour.colour) / 255F
|
||||
|
||||
val barStart = pX + 55F
|
||||
val baseStatWidth = (range.start / 100F) * MAX_BAR_WIDTH
|
||||
val boostedStatWidth = ((range.endInclusive / 100F) * MAX_BAR_WIDTH) - baseStatWidth
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = CobblemonResources.WHITE,
|
||||
x = barStart,
|
||||
y = startPosY,
|
||||
height = 3.5F,
|
||||
width = baseStatWidth,
|
||||
red = red,
|
||||
green = green,
|
||||
blue = blue
|
||||
)
|
||||
|
||||
val isBoostedStatHovered = mouseX.toFloat() in ((barStart + baseStatWidth)..(barStart + baseStatWidth + boostedStatWidth)) && mouseY.toFloat() in (startPosY..(startPosY + 3.5F))
|
||||
|
||||
drawScaledTextJustifiedRight(
|
||||
context = context,
|
||||
text = if (isBoostedStatHovered) range.endInclusive.toString().text().bold() else range.start.toString().text(),
|
||||
x = pX + 48,
|
||||
y = startPosY,
|
||||
colour = 0x3A96B6,
|
||||
scale = SCALE
|
||||
)
|
||||
|
||||
// Boostable range
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = CobblemonResources.WHITE,
|
||||
x = barStart + baseStatWidth,
|
||||
y = startPosY,
|
||||
height = 3.5F,
|
||||
width = boostedStatWidth,
|
||||
red = red,
|
||||
green = green,
|
||||
blue = blue,
|
||||
alpha = if (isBoostedStatHovered) 1F else 0.5F
|
||||
)
|
||||
|
||||
if (isBoostedStatHovered) renderTooltip(
|
||||
context,
|
||||
lang("ui.stats.ride.boost_maximum").bold(),
|
||||
mouseX,
|
||||
mouseY,
|
||||
delta,
|
||||
-16
|
||||
)
|
||||
}
|
||||
}
|
||||
} ?: run {
|
||||
// If ride properties null, reset index to base stats
|
||||
selectedStatTypeIndex = STAT_TYPE_BASE
|
||||
}
|
||||
}
|
||||
|
||||
private fun getStatValueRGB(value: Int): Triple<Float, Float, Float> {
|
||||
when {
|
||||
value >= 150 -> return Triple(0.647F, 0.929F, 0.647F)
|
||||
@@ -128,4 +329,4 @@ class StatsWidget(val pX: Int, val pY: Int) : SoundlessWidget(
|
||||
}
|
||||
return Triple(0.964F, 0.64F, 0.502F)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ class SummaryButton(
|
||||
override fun defaultButtonNarrationText(builder: NarrationElementOutput) {
|
||||
}
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
|
||||
public override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
|
||||
if (!this.renderRequirement.invoke(this)) {
|
||||
return
|
||||
}
|
||||
|
||||
+47
-39
@@ -28,83 +28,91 @@ import net.minecraft.world.phys.Vec3
|
||||
* @author Hiroku
|
||||
* @since November 13th, 2023
|
||||
*/
|
||||
class BarSummarySpeciesFeatureRenderer(
|
||||
open class BarSummarySpeciesFeatureRenderer(
|
||||
override val name: String,
|
||||
val displayName: MutableComponent,
|
||||
val min: Int,
|
||||
val max: Int,
|
||||
val colour: Vec3,
|
||||
val underlay: ResourceLocation,
|
||||
val overlay: ResourceLocation,
|
||||
val pokemon: Pokemon
|
||||
val pokemon: Pokemon,
|
||||
val min: Int,
|
||||
val max: Int,
|
||||
val value: Int = 0,
|
||||
val colour: Vec3? = null
|
||||
) : SummarySpeciesFeatureRenderer<IntSpeciesFeature> {
|
||||
override fun render(GuiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon, feature: IntSpeciesFeature) {
|
||||
val value = feature.value
|
||||
val barRatio = (value - min) / (max - min).toFloat()
|
||||
val barWidth = Mth.ceil(barRatio * 108)
|
||||
override fun render(guiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon, feature: IntSpeciesFeature) {
|
||||
renderElement(guiGraphics, x, y, pokemon, feature.value)
|
||||
}
|
||||
|
||||
open fun renderElement(guiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon, barValue: Int = value) {
|
||||
val barRatio = (barValue - min) / (max - min).toFloat()
|
||||
val barWidth = Mth.ceil(barRatio * 110)
|
||||
|
||||
blitk(
|
||||
matrixStack = GuiGraphics.pose(),
|
||||
matrixStack = guiGraphics.pose(),
|
||||
texture = underlay,
|
||||
x = x,
|
||||
y = y,
|
||||
height = 28,
|
||||
width = 124
|
||||
height = 24,
|
||||
width = 116
|
||||
)
|
||||
|
||||
val red = colour.x / 255F
|
||||
val green = colour.y / 255F
|
||||
val blue = colour.z / 255F
|
||||
renderBar(guiGraphics, x, y, barValue, barRatio, barWidth)
|
||||
|
||||
blitk(
|
||||
matrixStack = GuiGraphics.pose(),
|
||||
texture = CobblemonResources.WHITE,
|
||||
x = x + 8,
|
||||
y = y + 16,
|
||||
height = 10,
|
||||
width = barWidth,
|
||||
red = red,
|
||||
green = green,
|
||||
blue = blue
|
||||
)
|
||||
|
||||
blitk(
|
||||
matrixStack = GuiGraphics.pose(),
|
||||
matrixStack = guiGraphics.pose(),
|
||||
texture = overlay,
|
||||
x = x / StatWidget.SCALE,
|
||||
y = (y + 16) / StatWidget.SCALE,
|
||||
x = (x + 3) / StatWidget.SCALE,
|
||||
y = (y + 13) / StatWidget.SCALE,
|
||||
height = 20,
|
||||
width = 248,
|
||||
width = 220,
|
||||
scale = StatWidget.SCALE
|
||||
)
|
||||
|
||||
// Label
|
||||
drawScaledText(
|
||||
context = GuiGraphics,
|
||||
context = guiGraphics,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = displayName.bold(),
|
||||
x = x + 62,
|
||||
x = x + 58,
|
||||
y = y + 2.5,
|
||||
centered = true,
|
||||
shadow = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = GuiGraphics,
|
||||
text = value.toString().text(),
|
||||
x = x + 11,
|
||||
context = guiGraphics,
|
||||
text = barValue.toString().text(),
|
||||
x = x + 9,
|
||||
y = y + 6,
|
||||
scale = StatWidget.SCALE,
|
||||
centered = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = GuiGraphics,
|
||||
context = guiGraphics,
|
||||
text = "${Mth.floor(barRatio * 100)}%".text(),
|
||||
x = x + 113,
|
||||
x = x + 107,
|
||||
y = y + 6,
|
||||
scale = StatWidget.SCALE,
|
||||
centered = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
open fun renderBar(guiGraphics: GuiGraphics, x: Float, y: Float, barValue: Int, barRatio: Float, barWidth: Int) {
|
||||
val red = (colour?.x ?: 255.0) / 255.0
|
||||
val green = (colour?.y ?: 255.0) / 255.0
|
||||
val blue = (colour?.z ?: 255.0) / 255.0
|
||||
|
||||
blitk(
|
||||
matrixStack = guiGraphics.pose(),
|
||||
texture = CobblemonResources.WHITE,
|
||||
x = x + 3,
|
||||
y = y + 13,
|
||||
height = 10,
|
||||
width = barWidth,
|
||||
red = red,
|
||||
green = green,
|
||||
blue = blue
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -23,10 +23,10 @@ interface SummarySpeciesFeatureRenderer<T : SynchronizedSpeciesFeature> {
|
||||
/** The name of the feature (so we know where to look to find it in the [Pokemon]) */
|
||||
val name: String
|
||||
/** Draws it at a particular position. */
|
||||
fun render(GuiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon, feature: T)
|
||||
fun render(GuiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon): Boolean {
|
||||
fun render(guiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon, feature: T)
|
||||
fun render(guiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon): Boolean {
|
||||
val feature = pokemon.getFeature<T>(name) ?: return false
|
||||
render(GuiGraphics, x, y, pokemon, feature)
|
||||
render(guiGraphics, x, y, pokemon, feature)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+402
-448
@@ -14,12 +14,16 @@ import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.SynchronizedSpeciesFeatureProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stat
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.api.text.italicise
|
||||
import com.cobblemon.mod.common.api.text.onHover
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.summary.SummaryButton
|
||||
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.BarSummarySpeciesFeatureRenderer
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.stats.features.FriendshipFeatureRenderer
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.stats.features.FullnessFeatureRenderer
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
@@ -39,8 +43,6 @@ import net.minecraft.util.Mth.ceil
|
||||
import net.minecraft.util.Mth.floor
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import org.joml.Vector3f
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
|
||||
class StatWidget(
|
||||
pX: Int, pY: Int,
|
||||
@@ -51,63 +53,129 @@ class StatWidget(
|
||||
companion object {
|
||||
// Stat tab options
|
||||
private const val STATS = "stats"
|
||||
private const val IV = "iv"
|
||||
private const val EV = "ev"
|
||||
private const val IV = "ivs"
|
||||
private const val EV = "evs"
|
||||
private const val RIDE = "ride"
|
||||
private const val OTHER = "other"
|
||||
|
||||
private val statOptions = listOf(STATS, IV, EV, OTHER)
|
||||
|
||||
private const val statTabWidth = 24
|
||||
private const val OTHER_STAT_BARS_PER_PAGE = 4
|
||||
private const val STAT_TAB_WIDTH_6 = 22
|
||||
private const val STAT_TAB_WIDTH_5 = 24
|
||||
private const val MARKER_WIDTH = 8
|
||||
private const val WIDTH = 134
|
||||
private const val HEIGHT = 148
|
||||
const val SCALE = 0.5F
|
||||
|
||||
private const val WHITE = 0x00FFFFFF
|
||||
private const val GREY = 0x00AAAAAA
|
||||
private const val GREY = 0x008F8F8F
|
||||
private const val BLUE = 0x00548BFB
|
||||
private const val RED = 0x00FB5454
|
||||
|
||||
private val statsBaseResource = cobblemonResource("textures/gui/summary/summary_stats_chart_base.png")
|
||||
private val statsChartResource = cobblemonResource("textures/gui/summary/summary_stats_chart.png")
|
||||
private val statsChartPentagonResource = cobblemonResource("textures/gui/summary/summary_stats_chart_pentagon.png")
|
||||
private val statsOtherBaseResource = cobblemonResource("textures/gui/summary/summary_stats_other_base.png")
|
||||
private val statsOtherBarTemplate = cobblemonResource("textures/gui/summary/summary_stats_other_bar.png")
|
||||
private val friendshipOverlayResource = cobblemonResource("textures/gui/summary/summary_stats_friendship_overlay.png")
|
||||
private val fullnessOverlayResource = cobblemonResource("textures/gui/summary/summary_stats_fullness_overlay.png")
|
||||
private val statsOtherSidebarsResource = cobblemonResource("textures/gui/summary/summary_stats_other_sidebars.png")
|
||||
private val statsOtherSidebarArrowLeft = cobblemonResource("textures/gui/summary/summary_stats_other_sidebar_arrow_left.png")
|
||||
private val statsOtherSidebarArrowRight = cobblemonResource("textures/gui/summary/summary_stats_other_sidebar_arrow_right.png")
|
||||
private val tabMarkerResource = cobblemonResource("textures/gui/summary/summary_stats_tab_marker.png")
|
||||
private val statIncreaseResource = cobblemonResource("textures/gui/summary/summary_stats_icon_increase.png")
|
||||
private val statDecreaseResource = cobblemonResource("textures/gui/summary/summary_stats_icon_decrease.png")
|
||||
|
||||
private val statsLabel = lang("ui.stats")
|
||||
private val ivLabel = lang("ui.stats.ivs")
|
||||
private val evLabel = lang("ui.stats.evs")
|
||||
private val otherLabel = lang("ui.stats.other")
|
||||
private val statLabels: Map<MutableComponent, Stats> = mapOf(
|
||||
lang("ui.stats.hp").bold() to Stats.HP,
|
||||
lang("ui.stats.atk").bold() to Stats.ATTACK,
|
||||
lang("ui.stats.def").bold() to Stats.DEFENCE,
|
||||
lang("ui.stats.speed").bold() to Stats.SPEED,
|
||||
lang("ui.stats.sp_def").bold() to Stats.SPECIAL_DEFENCE,
|
||||
lang("ui.stats.sp_atk").bold() to Stats.SPECIAL_ATTACK
|
||||
)
|
||||
|
||||
private val hpLabel = lang("ui.stats.hp")
|
||||
private val spAtkLabel = lang("ui.stats.sp_atk")
|
||||
private val atkLabel = lang("ui.stats.atk")
|
||||
private val spDefLabel = lang("ui.stats.sp_def")
|
||||
private val defLabel = lang("ui.stats.def")
|
||||
private val speedLabel = lang("ui.stats.speed")
|
||||
private val hexagonVerticesOffset = listOf(
|
||||
Pair(67.0, 10.5), // 12 o'clock
|
||||
Pair(122.0, 42.5), // 2 o'clock
|
||||
Pair(122.0, 93.5), // 4 o'clock
|
||||
Pair(67.0, 124.5), // 6 o'clock
|
||||
Pair(12.0, 93.5), // 8 o'clock
|
||||
Pair(12.0, 42.5) // 10 o'clock
|
||||
)
|
||||
|
||||
private val pentagonVerticesOffset = listOf(
|
||||
Pair(67.0, 10.5), // 12 o'clock
|
||||
Pair(123.0, 47.5), // 2 o'clock
|
||||
Pair(103.0, 112.5), // 5 o'clock
|
||||
Pair(31.0, 112.5), // 7 o'clock
|
||||
Pair(11.0, 47.5), // 10 o'clock
|
||||
)
|
||||
}
|
||||
|
||||
var effectiveBattleIVs: Map<Stat, Int> = Stats.PERMANENT.associateWith { stat ->
|
||||
pokemon.ivs.getEffectiveBattleIV(stat)
|
||||
}
|
||||
private val statOptions = if (pokemon.form.riding.behaviours != null) listOf(STATS, IV, EV, RIDE, OTHER) else listOf(STATS, IV, EV, OTHER)
|
||||
|
||||
private var _statTabIndex: Int = tabIndex
|
||||
var statTabIndex: Int
|
||||
get() = _statTabIndex.coerceIn(0, statOptions.size - 1)
|
||||
set(value) {
|
||||
_statTabIndex = value.coerceIn(0, statOptions.size - 1)
|
||||
}
|
||||
|
||||
private var _rideBehaviourIndex: Int = 0
|
||||
var rideBehaviourIndex: Int
|
||||
get() = _rideBehaviourIndex.coerceIn(0, (pokemon.form.riding.behaviours?.size ?: 1) - 1)
|
||||
set(value) {
|
||||
_rideBehaviourIndex = value.coerceIn(0, (pokemon.form.riding.behaviours?.size ?: 1) - 1)
|
||||
}
|
||||
|
||||
val universalFeatures = listOf(
|
||||
FriendshipFeatureRenderer(pokemon),
|
||||
FullnessFeatureRenderer(pokemon)
|
||||
)
|
||||
|
||||
var statTabIndex = tabIndex
|
||||
val renderableFeatures = SpeciesFeatures
|
||||
.getFeaturesFor(pokemon.species)
|
||||
.filterIsInstance<SynchronizedSpeciesFeatureProvider<*>>()
|
||||
.mapNotNull { it.getRenderer(pokemon) }
|
||||
|
||||
private fun drawTriangle(
|
||||
colour: Vector3f,
|
||||
v1: Vec2,
|
||||
v2: Vec2,
|
||||
v3: Vec2
|
||||
) {
|
||||
var otherStatsPageIndex: Int = 0
|
||||
|
||||
var otherStatLeftButton: SummaryButton? = null
|
||||
var otherStatRightButton: SummaryButton? = null
|
||||
|
||||
init {
|
||||
if (renderableFeatures.size + universalFeatures.size > OTHER_STAT_BARS_PER_PAGE) {
|
||||
otherStatLeftButton = SummaryButton(
|
||||
x - 1F,
|
||||
y + 67F,
|
||||
14,
|
||||
14,
|
||||
resource = statsOtherSidebarArrowLeft,
|
||||
scale = SCALE,
|
||||
clickAction = { switchOtherStatsPage(false) }
|
||||
).also { addWidget(it) }
|
||||
|
||||
otherStatRightButton = SummaryButton(
|
||||
x + 128F,
|
||||
y + 67F,
|
||||
14,
|
||||
14,
|
||||
resource = statsOtherSidebarArrowRight,
|
||||
scale = SCALE,
|
||||
clickAction = { switchOtherStatsPage(true) }
|
||||
).also { addWidget(it) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun switchOtherStatsPage(nextIndex: Boolean) {
|
||||
val pages = ceil((renderableFeatures.size + universalFeatures.size).toDouble() / OTHER_STAT_BARS_PER_PAGE.toDouble())
|
||||
otherStatsPageIndex = if (nextIndex) {
|
||||
(otherStatsPageIndex + 1) % pages
|
||||
} else {
|
||||
(otherStatsPageIndex - 1 + pages) % pages
|
||||
}
|
||||
}
|
||||
|
||||
private fun drawTriangle(colour: Vector3f, v1: Vec2, v2: Vec2, v3: Vec2, opacity: Float = 0.6F) {
|
||||
CobblemonResources.WHITE.let { RenderSystem.setShaderTexture(0, it) }
|
||||
RenderSystem.setShaderColor(colour.x, colour.y, colour.z, 0.6F)
|
||||
RenderSystem.setShaderColor(colour.x, colour.y, colour.z, opacity)
|
||||
val bufferBuilder = Tesselator.getInstance().begin(VertexFormat.Mode.TRIANGLES, DefaultVertexFormat.POSITION)
|
||||
bufferBuilder.addVertex(v1.x, v1.y, 10F)
|
||||
bufferBuilder.addVertex(v2.x, v2.y, 10F)
|
||||
@@ -116,396 +184,325 @@ class StatWidget(
|
||||
RenderSystem.setShaderColor(1F, 1F, 1F, 1F)
|
||||
}
|
||||
|
||||
private fun drawStatHexagon(stats: Map<Stat, Int>, colour: Vector3f, maximum: Int) {
|
||||
val hexLeftX = x + 25.5
|
||||
val hexTopY = y + 22
|
||||
val hexAttackY = hexTopY + 24.5
|
||||
val hexDefenceY = hexAttackY + 47.0
|
||||
val hexBottomY = hexDefenceY + 24.5
|
||||
val hexRightX = x + 108.5
|
||||
val hexCenterX = (hexLeftX + hexRightX) / 2
|
||||
val hexCenterY = (hexTopY + hexBottomY) / 2
|
||||
val minTriangleSize = 8F
|
||||
val minXTriangleLen = sin(Math.toRadians(61.0)).toFloat() * minTriangleSize * 0.95F
|
||||
val minYTriangleLen = cos(Math.toRadians(60.0)).toFloat() * minTriangleSize
|
||||
/**
|
||||
* Render a 5 or 6 sided polygon
|
||||
*
|
||||
* @param ratios list of ratios for how far each vertex is from the center, starting from top vertex going clockwise
|
||||
* @param colour the colour to render
|
||||
*/
|
||||
private fun drawStatPolygon(ratios: List<Float>, colour: Vector3f) {
|
||||
val sides = ratios.size
|
||||
if (sides !in 5 .. 6) return
|
||||
|
||||
val triangleLongEdge = (hexCenterY - hexTopY - minTriangleSize).toFloat()
|
||||
val triangleMediumEdge = (triangleLongEdge * sin(Math.toRadians(61.0))).toFloat()
|
||||
val triangleShortEdge = (triangleLongEdge * cos(Math.toRadians(61.0))).toFloat()
|
||||
val topVertexY = y + 22F
|
||||
val centerX = x + 67F
|
||||
val centerY = topVertexY + if (sides > 5) 48F else 49F
|
||||
|
||||
val hpRatio = (stats.getOrDefault(Stats.HP, 0).toFloat() / maximum).coerceIn(0F, 1F)
|
||||
val atkRatio = (stats.getOrDefault(Stats.ATTACK, 0).toFloat() / maximum).coerceIn(0F, 1F)
|
||||
val defRatio = (stats.getOrDefault(Stats.DEFENCE, 0).toFloat() / maximum).coerceIn(0F, 1F)
|
||||
val spAtkRatio = (stats.getOrDefault(Stats.SPECIAL_ATTACK, 0).toFloat() / maximum).coerceIn(0F, 1F)
|
||||
val spDefRatio = (stats.getOrDefault(Stats.SPECIAL_DEFENCE, 0).toFloat() / maximum).coerceIn(0F, 1F)
|
||||
val spdRatio = (stats.getOrDefault(Stats.SPEED, 0).toFloat() / maximum).coerceIn(0F, 1F)
|
||||
val radius = centerY - topVertexY
|
||||
|
||||
val hpPoint = Vec2(
|
||||
hexCenterX.toFloat(),
|
||||
hexCenterY.toFloat() - minTriangleSize - hpRatio * triangleLongEdge
|
||||
)
|
||||
val startAngleDeg = -90.0
|
||||
val angleStepDeg = 360.0 / sides
|
||||
|
||||
val attackPoint = Vec2(
|
||||
hexCenterX.toFloat() + minXTriangleLen + atkRatio * triangleMediumEdge,
|
||||
hexCenterY.toFloat() - minYTriangleLen - atkRatio * triangleShortEdge
|
||||
)
|
||||
// Generate the angles for each vertex
|
||||
val anglesDeg = List(sides) { i -> startAngleDeg + i * angleStepDeg }
|
||||
val anglesRad = anglesDeg.map { Math.toRadians(it) }
|
||||
|
||||
val defencePoint = Vec2(
|
||||
hexCenterX.toFloat() + minXTriangleLen + defRatio * triangleMediumEdge,
|
||||
hexCenterY.toFloat() + minYTriangleLen + defRatio * triangleShortEdge
|
||||
)
|
||||
|
||||
val specialAttackPoint = Vec2(
|
||||
hexCenterX.toFloat() - minXTriangleLen - spAtkRatio * triangleMediumEdge,
|
||||
hexCenterY.toFloat() - minYTriangleLen - spAtkRatio * triangleShortEdge
|
||||
)
|
||||
|
||||
val specialDefencePoint = Vec2(
|
||||
hexCenterX.toFloat() - minXTriangleLen - spDefRatio * triangleMediumEdge,
|
||||
hexCenterY.toFloat() + minYTriangleLen + spDefRatio * triangleShortEdge
|
||||
)
|
||||
|
||||
val speedPoint = Vec2(
|
||||
hexCenterX.toFloat(),
|
||||
hexCenterY.toFloat() + minTriangleSize + spdRatio * triangleLongEdge
|
||||
)
|
||||
|
||||
val centerPoint = Vec2(
|
||||
hexCenterX.toFloat(),
|
||||
hexCenterY.toFloat()
|
||||
)
|
||||
|
||||
// 1-o'clock
|
||||
drawTriangle(colour, hpPoint, centerPoint, attackPoint)
|
||||
// 3-o'clock
|
||||
drawTriangle(colour, attackPoint, centerPoint, defencePoint)
|
||||
// 5-o'clock
|
||||
drawTriangle(colour, defencePoint, centerPoint, speedPoint)
|
||||
// 7-o'clock
|
||||
drawTriangle(colour, speedPoint, centerPoint, specialDefencePoint)
|
||||
// 9-o'clock
|
||||
drawTriangle(colour, specialDefencePoint, centerPoint, specialAttackPoint)
|
||||
// 11-o'clock
|
||||
drawTriangle(colour, specialAttackPoint, centerPoint, hpPoint)
|
||||
}
|
||||
|
||||
private fun drawFriendship(moduleX: Int, moduleY: Int, matrices: PoseStack, context: GuiGraphics, friendship: Int) {
|
||||
val barRatio = friendship / 255F
|
||||
val barWidth = ceil(barRatio * 108)
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = statsOtherBarTemplate,
|
||||
x = moduleX,
|
||||
y = moduleY,
|
||||
height = 28,
|
||||
width = 124
|
||||
)
|
||||
|
||||
val red = 1
|
||||
val green: Number = if (pokemon.friendship >= 160) 0.28 else 0.56
|
||||
val blue: Number = if (pokemon.friendship >= 160) 0.4 else 0.64
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = CobblemonResources.WHITE,
|
||||
x = moduleX + 8,
|
||||
y = moduleY + 18,
|
||||
height = 8,
|
||||
width = barWidth,
|
||||
red = red,
|
||||
green = green,
|
||||
blue = blue
|
||||
)
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = friendshipOverlayResource,
|
||||
x = moduleX / SCALE,
|
||||
y = (moduleY + 16) / SCALE,
|
||||
height = 20,
|
||||
width = 248,
|
||||
scale = SCALE
|
||||
)
|
||||
|
||||
// Label
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = lang("ui.stats.friendship").bold(),
|
||||
x = moduleX + 62,
|
||||
y = moduleY + 2.5,
|
||||
centered = true,
|
||||
shadow = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = friendship.toString().text(),
|
||||
x = moduleX + 11,
|
||||
y = moduleY + 6,
|
||||
scale = SCALE,
|
||||
centered = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = "${floor(barRatio * 100)}%".text(),
|
||||
x = moduleX + 113,
|
||||
y = moduleY + 6,
|
||||
scale = SCALE,
|
||||
centered = true
|
||||
)
|
||||
}
|
||||
|
||||
private fun drawFullness(moduleX: Int, moduleY: Int, matrices: PoseStack, context: GuiGraphics, pokemon: Pokemon) {
|
||||
val currentFullness = pokemon.currentFullness
|
||||
val maxFullness = pokemon.getMaxFullness()
|
||||
val barRatio = currentFullness.toFloat() / maxFullness.toFloat()
|
||||
val barWidth = ceil(barRatio * 108)
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = statsOtherBarTemplate,
|
||||
x = moduleX,
|
||||
y = moduleY,
|
||||
height = 28,
|
||||
width = 124
|
||||
)
|
||||
|
||||
val (red, green, blue) = when {
|
||||
barRatio <= 0.33 -> Triple(0f, 1f, 0f) // Green (full green)
|
||||
barRatio <= 0.66 -> Triple(1f, 1f, 0f) // Yellow (red + green)
|
||||
else -> Triple(1f, 0f, 0f) // Red (full red)
|
||||
// Calculate maximum points on the circle
|
||||
val maxPoints = anglesRad.map { angle ->
|
||||
Vec2(
|
||||
centerX + radius * Math.cos(angle).toFloat(),
|
||||
centerY + radius * Math.sin(angle).toFloat()
|
||||
)
|
||||
}
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = CobblemonResources.WHITE,
|
||||
x = moduleX + 8,
|
||||
y = moduleY + 18,
|
||||
height = 8,
|
||||
width = barWidth,
|
||||
red = red,
|
||||
green = green,
|
||||
blue = blue
|
||||
)
|
||||
// Coerce values as 0.0 will prevent triangles from rendering
|
||||
val coercedRatios = ratios.map { it.coerceIn(5 / radius, 1F) }
|
||||
|
||||
// Draw the overlay
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = fullnessOverlayResource,
|
||||
x = moduleX / SCALE,
|
||||
y = (moduleY + 16) / SCALE,
|
||||
height = 20,
|
||||
width = 248,
|
||||
scale = SCALE
|
||||
)
|
||||
// Interpolate vertices based on ratios
|
||||
val vertices = maxPoints.mapIndexed { index, maxPoint ->
|
||||
Vec2(
|
||||
centerX + (maxPoint.x - centerX) * coercedRatios[index],
|
||||
centerY + (maxPoint.y - centerY) * coercedRatios[index]
|
||||
)
|
||||
}
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = "Fullness".text().bold(),
|
||||
x = moduleX + 62,
|
||||
y = moduleY + 2.5,
|
||||
centered = true,
|
||||
shadow = true
|
||||
)
|
||||
val centerPoint = Vec2(centerX, centerY)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = currentFullness.toString().text(),
|
||||
x = moduleX + 11,
|
||||
y = moduleY + 6,
|
||||
scale = SCALE,
|
||||
centered = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = "${floor(barRatio * 100)}%".text(),
|
||||
x = moduleX + 113,
|
||||
y = moduleY + 6,
|
||||
scale = SCALE,
|
||||
centered = true
|
||||
)
|
||||
// Draw triangles between each vertex and the next clockwise starting from top vertex
|
||||
RenderSystem.disableDepthTest()
|
||||
for (i in vertices.indices) {
|
||||
val nextIndex = (i + 1) % vertices.size
|
||||
drawTriangle(colour, vertices[i], centerPoint, vertices[nextIndex])
|
||||
}
|
||||
RenderSystem.enableDepthTest()
|
||||
}
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
|
||||
val renderChart = statOptions.get(statTabIndex) != OTHER
|
||||
val renderOtherStats = statOptions.get(statTabIndex) == OTHER
|
||||
val renderPentagonStats = statOptions.get(statTabIndex) == RIDE
|
||||
val matrices = context.pose()
|
||||
|
||||
// Background
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = if (renderChart) statsBaseResource else statsOtherBaseResource,
|
||||
texture = if (renderOtherStats) statsOtherBaseResource else statsBaseResource,
|
||||
x= x,
|
||||
y = y,
|
||||
width = width,
|
||||
height = height
|
||||
)
|
||||
|
||||
// Chart
|
||||
if (renderChart) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = statsChartResource,
|
||||
x= (x + 25.5) / SCALE,
|
||||
y = (y + 22) / SCALE,
|
||||
width = 166,
|
||||
height = 192,
|
||||
scale = SCALE
|
||||
)
|
||||
}
|
||||
when (statOptions.get(statTabIndex)) {
|
||||
STATS -> drawStatHexagon(
|
||||
mapOf(
|
||||
Stats.HP to pokemon.maxHealth,
|
||||
Stats.ATTACK to pokemon.attack,
|
||||
Stats.DEFENCE to pokemon.defence,
|
||||
Stats.SPECIAL_ATTACK to pokemon.specialAttack,
|
||||
Stats.SPECIAL_DEFENCE to pokemon.specialDefence,
|
||||
Stats.SPEED to pokemon.speed
|
||||
),
|
||||
colour = Vector3f(50F/255, 215F/255F, 1F),
|
||||
maximum = 400
|
||||
)
|
||||
IV -> drawStatHexagon(
|
||||
effectiveBattleIVs,
|
||||
colour = Vector3f(216F/255, 100F/255, 1F),
|
||||
maximum = 31
|
||||
)
|
||||
EV -> drawStatHexagon(
|
||||
pokemon.evs.associate { it.key to it.value },
|
||||
colour = Vector3f(1F, 1F, 100F/255),
|
||||
maximum = 252
|
||||
)
|
||||
}
|
||||
if (renderOtherStats) {
|
||||
val barOffsetY = 29
|
||||
val barPosX = x + 9F
|
||||
var drawY = y + 15F
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = statsLabel.bold(),
|
||||
x = x + 31,
|
||||
y = y + 143,
|
||||
scale = SCALE,
|
||||
colour = if (statOptions.get(statTabIndex) == STATS) WHITE else GREY,
|
||||
centered = true
|
||||
)
|
||||
val featuresList: MutableList<Any> = renderableFeatures.toMutableList()
|
||||
featuresList.addAll(0, universalFeatures)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = ivLabel.bold(),
|
||||
x = x + 55,
|
||||
y = y + 143,
|
||||
scale = SCALE,
|
||||
colour = if (statOptions.get(statTabIndex) == IV) WHITE else GREY,
|
||||
centered = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = evLabel.bold(),
|
||||
x = x + 79,
|
||||
y = y + 143,
|
||||
scale = SCALE,
|
||||
colour = if (statOptions.get(statTabIndex) == EV) WHITE else GREY,
|
||||
centered = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = otherLabel.bold(),
|
||||
x = x + 103,
|
||||
y = y + 143,
|
||||
scale = SCALE,
|
||||
colour = if (statOptions.get(statTabIndex) == OTHER) WHITE else GREY,
|
||||
centered = true
|
||||
)
|
||||
|
||||
val paddingLeft = (WIDTH - ((statOptions.size + 1) * statTabWidth)) / 2
|
||||
blitk(
|
||||
matrixStack = context.pose(),
|
||||
texture = tabMarkerResource,
|
||||
x= ((x + paddingLeft + ((statTabIndex + 1) * statTabWidth)) / SCALE) - 2,
|
||||
y = (y + 140) / SCALE,
|
||||
width = 8,
|
||||
height = 4,
|
||||
scale = SCALE,
|
||||
)
|
||||
|
||||
if (renderChart) {
|
||||
// Stat Labels
|
||||
renderTextAtVertices(
|
||||
context = context,
|
||||
hp = hpLabel.bold(),
|
||||
spAtk = spAtkLabel.bold(),
|
||||
atk = atkLabel.bold(),
|
||||
spDef = spDefLabel.bold(),
|
||||
def = defLabel.bold(),
|
||||
speed = speedLabel.bold()
|
||||
val pageFeatures = featuresList.subList(
|
||||
otherStatsPageIndex * OTHER_STAT_BARS_PER_PAGE,
|
||||
minOf((otherStatsPageIndex * OTHER_STAT_BARS_PER_PAGE) + OTHER_STAT_BARS_PER_PAGE, featuresList.size)
|
||||
)
|
||||
|
||||
// Stat Values
|
||||
renderTextAtVertices(
|
||||
context = context,
|
||||
offsetY = 5.5,
|
||||
enableColour = false,
|
||||
hp = getStatValueAsText(Stats.HP),
|
||||
spAtk = getStatValueAsText(Stats.SPECIAL_ATTACK),
|
||||
atk = getStatValueAsText(Stats.ATTACK),
|
||||
spDef = getStatValueAsText(Stats.SPECIAL_DEFENCE),
|
||||
def = getStatValueAsText(Stats.DEFENCE),
|
||||
speed = getStatValueAsText(Stats.SPEED)
|
||||
)
|
||||
|
||||
// Nature-modified Stat Icons
|
||||
if (statOptions.get(statTabIndex) == STATS) {
|
||||
val nature = pokemon.effectiveNature
|
||||
renderModifiedStatIcon(matrices, nature.increasedStat, true)
|
||||
renderModifiedStatIcon(matrices, nature.decreasedStat, false)
|
||||
for (feature in pageFeatures) {
|
||||
if (feature is BarSummarySpeciesFeatureRenderer) {
|
||||
val rendered = feature.render(
|
||||
guiGraphics = context,
|
||||
x = barPosX,
|
||||
y = drawY,
|
||||
pokemon = pokemon
|
||||
)
|
||||
if (rendered) drawY += barOffsetY
|
||||
}
|
||||
}
|
||||
if (featuresList.size > 4) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = statsOtherSidebarsResource,
|
||||
x= x,
|
||||
y = y + 13,
|
||||
width = 134,
|
||||
height = 115
|
||||
)
|
||||
otherStatLeftButton?.renderWidget(context, pMouseX, pMouseY, pPartialTicks)
|
||||
otherStatRightButton?.renderWidget(context, pMouseX, pMouseY, pPartialTicks)
|
||||
}
|
||||
} else {
|
||||
var drawY = y + 11
|
||||
|
||||
drawFriendship(x + 5, drawY, matrices, context, pokemon.friendship)
|
||||
drawY += 30
|
||||
|
||||
drawFullness(x + 5, drawY, matrices, context, pokemon)
|
||||
drawY += 30
|
||||
|
||||
for (renderableFeature in renderableFeatures) {
|
||||
val rendered = renderableFeature.render(
|
||||
GuiGraphics = context,
|
||||
x = x + 5F,
|
||||
y = drawY.toFloat(),
|
||||
pokemon = pokemon
|
||||
if (renderPentagonStats) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = statsChartPentagonResource,
|
||||
x= (x + 20.5) / SCALE,
|
||||
y = (y + 22) / SCALE,
|
||||
width = 186,
|
||||
height = 176,
|
||||
scale = SCALE
|
||||
)
|
||||
|
||||
if (rendered) {
|
||||
drawY += 30
|
||||
pokemon.form.riding.behaviours?.let {
|
||||
val behaviours = it.entries.toList()
|
||||
val selectedBehaviour = behaviours[rideBehaviourIndex]
|
||||
|
||||
val pentagonColour = when (selectedBehaviour.key) {
|
||||
RidingStyle.AIR -> {
|
||||
if (behaviours.size > 1 && centerHovered(pMouseX, pMouseY))
|
||||
Vector3f(70F/255F, 235F/225F, 195F/255F)
|
||||
else Vector3f(40F/255F, 205F/255F, 165F/255F)
|
||||
}
|
||||
RidingStyle.LIQUID -> {
|
||||
if (behaviours.size > 1 && centerHovered(pMouseX, pMouseY))
|
||||
Vector3f(95F/255F, 165F/255F, 1F)
|
||||
else Vector3f(65F/255F, 135F/255F, 1F)
|
||||
}
|
||||
else -> {
|
||||
if (behaviours.size > 1 && centerHovered(pMouseX, pMouseY))
|
||||
Vector3f(1F, 195F/255F, 30F/255F)
|
||||
else Vector3f(1F, 165F/255F, 0F)
|
||||
}
|
||||
}
|
||||
|
||||
drawStatPolygon(
|
||||
ratios = RidingStat.entries.map { pokemon.getRideStat(selectedBehaviour.key, it) / 100F },
|
||||
colour = pentagonColour
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = lang("ui.ride_style.${selectedBehaviour.key.name.lowercase()}").bold(),
|
||||
x = x + (WIDTH / 2),
|
||||
y = y + 66,
|
||||
shadow = true,
|
||||
centered = true
|
||||
)
|
||||
|
||||
// Stat Labels
|
||||
renderPolygonLabels(
|
||||
context = context,
|
||||
labels = RidingStat.entries.toList().map { stat ->
|
||||
if (statLabelsHovered(pentagonVerticesOffset, pMouseX, pMouseY))
|
||||
"${floor(pokemon.getRideStat(selectedBehaviour.key, stat))}/${selectedBehaviour.value.stats[stat]?.endInclusive}".text()
|
||||
else lang("ui.stats.ride.${stat.name.lowercase()}").bold()
|
||||
},
|
||||
verticesOffset = pentagonVerticesOffset
|
||||
)
|
||||
|
||||
// Stat Values
|
||||
renderPolygonLabels(
|
||||
context = context,
|
||||
labels = RidingStat.entries.toList().map { stat ->
|
||||
if (statLabelsHovered(pentagonVerticesOffset, pMouseX, pMouseY))
|
||||
"${floor((pokemon.getRideStat(selectedBehaviour.key, stat) / (selectedBehaviour.value.stats[stat]?.endInclusive ?: 1)) * 100)}%".text()
|
||||
else floor(pokemon.getRideStat(selectedBehaviour.key, stat)).toString().text()
|
||||
},
|
||||
verticesOffset = pentagonVerticesOffset,
|
||||
offsetY = 5.5
|
||||
)
|
||||
}
|
||||
|
||||
} else {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = statsChartResource,
|
||||
x= (x + 25.5) / SCALE,
|
||||
y = (y + 22) / SCALE,
|
||||
width = 166,
|
||||
height = 192,
|
||||
scale = SCALE
|
||||
)
|
||||
|
||||
when (statOptions.get(statTabIndex)) {
|
||||
STATS -> drawStatPolygon(
|
||||
listOf(
|
||||
pokemon.maxHealth,
|
||||
pokemon.attack,
|
||||
pokemon.defence,
|
||||
pokemon.speed,
|
||||
pokemon.specialDefence,
|
||||
pokemon.specialAttack
|
||||
).map { it / 400F },
|
||||
colour = Vector3f(50F/255F, 215F/255F, 1F)
|
||||
)
|
||||
IV -> drawStatPolygon(
|
||||
statLabels.values.map { pokemon.ivs.getEffectiveBattleIV(it) / 31F },
|
||||
colour = Vector3f(216F/255, 100F/255, 1F)
|
||||
)
|
||||
EV -> drawStatPolygon(
|
||||
statLabels.values.map { (pokemon.evs[it]?.toFloat() ?: 0F) / 252F },
|
||||
colour = Vector3f(1F, 1F, 100F/255F)
|
||||
)
|
||||
}
|
||||
|
||||
val labelsHovered = statLabelsHovered(hexagonVerticesOffset, pMouseX, pMouseY)
|
||||
|
||||
val labels = statLabels.map { stat ->
|
||||
if (labelsHovered) {
|
||||
when (statOptions.get(statTabIndex)) {
|
||||
IV -> {
|
||||
"${
|
||||
if (pokemon.ivs.isHyperTrained(stat.value)) "${pokemon.ivs[stat.value]} (${pokemon.ivs.hyperTrainedIVs[stat.value]})"
|
||||
else pokemon.ivs[stat.value].toString()
|
||||
}/31".text()
|
||||
}
|
||||
EV -> "${pokemon.evs.getOrDefault(stat.value)}/252".text()
|
||||
else -> stat.key
|
||||
}
|
||||
} else { stat.key }
|
||||
}
|
||||
|
||||
// Stat Labels
|
||||
renderPolygonLabels(
|
||||
context = context,
|
||||
labels = labels,
|
||||
verticesOffset = hexagonVerticesOffset,
|
||||
enableColour = true
|
||||
)
|
||||
|
||||
// Stat Values
|
||||
renderPolygonLabels(
|
||||
context = context,
|
||||
labels = statValuesAsText(
|
||||
statLabels.values.toList(),
|
||||
labelsHovered
|
||||
),
|
||||
verticesOffset = hexagonVerticesOffset,
|
||||
offsetY = 5.5
|
||||
)
|
||||
|
||||
// Nature-modified Stat Icons
|
||||
if (statOptions.get(statTabIndex) == STATS) {
|
||||
val nature = pokemon.effectiveNature
|
||||
renderModifiedStatIcon(matrices, nature.increasedStat, true)
|
||||
renderModifiedStatIcon(matrices, nature.decreasedStat, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stat type select bar
|
||||
val hasRideBehaviour = pokemon.form.riding.behaviours != null
|
||||
val startPosX = x + if (hasRideBehaviour) 23 else 31
|
||||
val statTabWidth = if (hasRideBehaviour) STAT_TAB_WIDTH_6 else STAT_TAB_WIDTH_5
|
||||
|
||||
statOptions.forEachIndexed { index, stat ->
|
||||
val statKey = if (stat == STATS) "" else ".$stat"
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = lang("ui.stats$statKey").bold(),
|
||||
x = startPosX + (statTabWidth * index),
|
||||
y = y + 143,
|
||||
scale = SCALE,
|
||||
colour = if (statOptions.get(statTabIndex) == stat) WHITE else GREY,
|
||||
centered = true
|
||||
)
|
||||
}
|
||||
|
||||
blitk(
|
||||
matrixStack = context.pose(),
|
||||
texture = tabMarkerResource,
|
||||
x= (startPosX + (statTabIndex * statTabWidth) - (MARKER_WIDTH / 4)) / SCALE,
|
||||
y = (y + 140) / SCALE,
|
||||
width = MARKER_WIDTH,
|
||||
height = 4,
|
||||
scale = SCALE
|
||||
)
|
||||
}
|
||||
|
||||
override fun mouseClicked(pMouseX: Double, pMouseY: Double, pButton: Int): Boolean {
|
||||
val index = getTabIndexFromPos(pMouseX, pMouseY)
|
||||
// Only play sound here as the rest of the widget is meant to be silent
|
||||
if (index in 0..4 && statTabIndex != index) {
|
||||
if (index in 0 until statOptions.size && statTabIndex != index) {
|
||||
statTabIndex = index
|
||||
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.GUI_CLICK, 1.0F))
|
||||
}
|
||||
|
||||
if (statOptions.get(statTabIndex) == RIDE && ((pokemon.riding.behaviours?.size ?: 0) > 1) && centerHovered(pMouseX, pMouseY)) {
|
||||
rideBehaviourIndex = (rideBehaviourIndex + 1) % (pokemon.riding.behaviours?.size ?: 1)
|
||||
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.GUI_CLICK, 1.0F))
|
||||
}
|
||||
|
||||
return super.mouseClicked(pMouseX, pMouseY, pButton)
|
||||
}
|
||||
|
||||
private fun getStatValueAsText(stat: Stat): MutableComponent {
|
||||
val value = when (statOptions.get(statTabIndex)) {
|
||||
STATS -> if (stat == Stats.HP) "${pokemon.currentHealth} / ${pokemon.maxHealth}" else pokemon.getStat(stat).toString()
|
||||
IV -> if (pokemon.ivs.isHyperTrained(stat)) "${pokemon.ivs[stat]} (${pokemon.ivs.hyperTrainedIVs[stat]})" else pokemon.ivs[stat].toString()
|
||||
EV -> pokemon.evs.getOrDefault(stat).toString()
|
||||
else -> "0"
|
||||
private fun statValuesAsText(stats: List<Stat>, asPercent: Boolean): List<MutableComponent> {
|
||||
return stats.map { stat ->
|
||||
when (statOptions.get(statTabIndex)) {
|
||||
STATS -> (if (stat == Stats.HP) "${pokemon.currentHealth} / ${pokemon.maxHealth}" else pokemon.getStat(stat).toString()).text()
|
||||
IV -> {
|
||||
(if (asPercent) {
|
||||
"${floor((((if (pokemon.ivs.isHyperTrained(stat)) pokemon.ivs.hyperTrainedIVs[stat] else pokemon.ivs[stat]) ?: 0) / 31.0) * 100)}%"
|
||||
} else {
|
||||
if (pokemon.ivs.isHyperTrained(stat)) "${pokemon.ivs[stat]} (${pokemon.ivs.hyperTrainedIVs[stat]})"
|
||||
else pokemon.ivs[stat].toString()
|
||||
}).text()
|
||||
}
|
||||
EV -> {
|
||||
(if (asPercent) "${floor((pokemon.evs.getOrDefault(stat) / 252.0) * 100)}%"
|
||||
else pokemon.evs.getOrDefault(stat).toString()).text()
|
||||
}
|
||||
else -> "0".text()
|
||||
}
|
||||
}
|
||||
return value.text()
|
||||
}
|
||||
|
||||
private fun renderModifiedStatIcon(pPoseStack: PoseStack, stat: Stat?, increasedStat: Boolean) {
|
||||
@@ -534,7 +531,7 @@ class StatWidget(
|
||||
}
|
||||
}
|
||||
|
||||
private fun getModifiedStatColour(stat: Stat, enableColour: Boolean): Int {
|
||||
private fun getModifiedStatColour(stat: Stat?, enableColour: Boolean): Int {
|
||||
if (statOptions.get(statTabIndex) == STATS && enableColour) {
|
||||
val nature = pokemon.effectiveNature
|
||||
|
||||
@@ -544,81 +541,38 @@ class StatWidget(
|
||||
return WHITE
|
||||
}
|
||||
|
||||
private fun renderTextAtVertices(
|
||||
context: GuiGraphics,
|
||||
offsetY: Double = 0.0,
|
||||
enableColour: Boolean = true,
|
||||
hp: MutableComponent,
|
||||
spAtk: MutableComponent,
|
||||
atk: MutableComponent,
|
||||
spDef: MutableComponent,
|
||||
def: MutableComponent,
|
||||
speed: MutableComponent
|
||||
) {
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = hp,
|
||||
x = x + 67,
|
||||
y = y + 10.5 + offsetY,
|
||||
scale = SCALE,
|
||||
colour = getModifiedStatColour(Stats.HP, enableColour),
|
||||
centered = true
|
||||
)
|
||||
private fun renderPolygonLabels(context: GuiGraphics, labels: List<MutableComponent>, verticesOffset: List<Pair<Double, Double>>, offsetY: Double = 0.0, enableColour: Boolean = false) {
|
||||
if (labels.size != verticesOffset.size) return
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = spAtk,
|
||||
x = x + 12,
|
||||
y = y + 42.5 + offsetY,
|
||||
scale = SCALE,
|
||||
colour = getModifiedStatColour(Stats.SPECIAL_ATTACK, enableColour),
|
||||
centered = true
|
||||
)
|
||||
labels.forEachIndexed { index, label ->
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = label,
|
||||
x = x + verticesOffset[index].first,
|
||||
y = y + verticesOffset[index].second + offsetY,
|
||||
scale = SCALE,
|
||||
colour = getModifiedStatColour(statLabels[label], enableColour),
|
||||
centered = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = atk,
|
||||
x = x + 122,
|
||||
y = y + 42.5 + offsetY,
|
||||
scale = SCALE,
|
||||
colour = getModifiedStatColour(Stats.ATTACK,enableColour),
|
||||
centered = true
|
||||
)
|
||||
private fun centerHovered(pMouseX: Number, pMouseY: Number): Boolean {
|
||||
return (pMouseX.toInt() in (x + 44)..(x + 90)) && (pMouseY.toInt() in (y + 47)..(y + 93))
|
||||
}
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = spDef,
|
||||
x = x + 12,
|
||||
y = y + 93.5 + offsetY,
|
||||
scale = SCALE,
|
||||
colour = getModifiedStatColour(Stats.SPECIAL_DEFENCE, enableColour),
|
||||
centered = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = def,
|
||||
x = x + 122,
|
||||
y = y + 93.5 + offsetY,
|
||||
scale = SCALE,
|
||||
colour = getModifiedStatColour(Stats.DEFENCE, enableColour),
|
||||
centered = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = speed,
|
||||
x = x + 67,
|
||||
y = y + 124.5 + offsetY,
|
||||
scale = SCALE,
|
||||
colour = getModifiedStatColour(Stats.SPEED, enableColour),
|
||||
centered = true
|
||||
)
|
||||
private fun statLabelsHovered(labelOffsets: List<Pair<Double, Double>>, mouseX: Int, mouseY: Int): Boolean {
|
||||
return labelOffsets.any { pos ->
|
||||
(mouseX.toDouble() in (x + pos.first - 12)..(x + pos.first + 12))
|
||||
&& (mouseY.toDouble() in (y + pos.second - 1)..(y + pos.second + 10))
|
||||
}
|
||||
}
|
||||
|
||||
private fun getTabIndexFromPos(mouseX: Double, mouseY: Double): Int {
|
||||
val paddingLeft = ((WIDTH - ((statOptions.size + 1) * statTabWidth)) / 2.0) + (statTabWidth / 2)
|
||||
val left = x + paddingLeft
|
||||
val hasRideBehaviour = pokemon.form.riding.behaviours != null
|
||||
val left = x + if (hasRideBehaviour) ((23 + 1) / 2).toFloat() else ((31 + 1) / 2).toFloat()
|
||||
val statTabWidth = if (hasRideBehaviour) STAT_TAB_WIDTH_6 else STAT_TAB_WIDTH_5
|
||||
|
||||
val top = y + 140.0
|
||||
if (mouseX in left..(left + (statTabWidth * (statOptions.size + 1))) && mouseY in top..(top + 9.0)) {
|
||||
var startX = left
|
||||
@@ -631,4 +585,4 @@ class StatWidget(
|
||||
}
|
||||
return -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.summary.widgets.screens.stats.features
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.BarSummarySpeciesFeatureRenderer
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
|
||||
class FriendshipFeatureRenderer(
|
||||
val selectedPokemon: Pokemon
|
||||
) : BarSummarySpeciesFeatureRenderer(
|
||||
"friendship",
|
||||
lang("ui.stats.friendship"),
|
||||
cobblemonResource("textures/gui/summary/summary_stats_other_bar.png"),
|
||||
cobblemonResource("textures/gui/summary/summary_stats_friendship_overlay.png"),
|
||||
selectedPokemon,
|
||||
0,
|
||||
255,
|
||||
selectedPokemon.friendship
|
||||
) {
|
||||
override fun render(guiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon): Boolean {
|
||||
renderElement(guiGraphics, x, y, pokemon)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun renderBar(guiGraphics: GuiGraphics, x: Float, y: Float, barValue: Int, barRatio: Float, barWidth: Int) {
|
||||
val red = 1
|
||||
val green: Number = if (barValue >= 160) 0.28 else 0.56
|
||||
val blue: Number = if (barValue >= 160) 0.4 else 0.64
|
||||
|
||||
blitk(
|
||||
matrixStack = guiGraphics.pose(),
|
||||
texture = CobblemonResources.WHITE,
|
||||
x = x + 3,
|
||||
y = y + 13,
|
||||
height = 10,
|
||||
width = barWidth,
|
||||
red = red,
|
||||
green = green,
|
||||
blue = blue
|
||||
)
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.summary.widgets.screens.stats.features
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.BarSummarySpeciesFeatureRenderer
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
|
||||
class FullnessFeatureRenderer(
|
||||
val selectedPokemon: Pokemon
|
||||
) : BarSummarySpeciesFeatureRenderer(
|
||||
"fullness",
|
||||
lang("ui.stats.fullness"),
|
||||
cobblemonResource("textures/gui/summary/summary_stats_other_bar.png"),
|
||||
cobblemonResource("textures/gui/summary/summary_stats_fullness_overlay.png"),
|
||||
selectedPokemon,
|
||||
0,
|
||||
selectedPokemon.getMaxFullness(),
|
||||
selectedPokemon.currentFullness
|
||||
) {
|
||||
override fun render(guiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon): Boolean {
|
||||
renderElement(guiGraphics, x, y, pokemon)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun renderBar(guiGraphics: GuiGraphics, x: Float, y: Float, barValue: Int, barRatio: Float, barWidth: Int) {
|
||||
val (red, green, blue) = when {
|
||||
barRatio <= 0.33 -> Triple(120F/255F, 200F/255F, 80F/255F) // Green
|
||||
barRatio <= 0.66 -> Triple(240F/255F, 200F/255F, 65F/255F) // Yellow
|
||||
else -> Triple(230F/255F, 80F/255F, 65F/255F) // Red
|
||||
}
|
||||
|
||||
blitk(
|
||||
matrixStack = guiGraphics.pose(),
|
||||
texture = CobblemonResources.WHITE,
|
||||
x = x + 3,
|
||||
y = y + 13,
|
||||
height = 10,
|
||||
width = barWidth,
|
||||
red = red,
|
||||
green = green,
|
||||
blue = blue
|
||||
)
|
||||
}
|
||||
}
|
||||
+15
-1
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.client.keybind.keybinds
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendToServer
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.battles.BattleFormat
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.client.battle.ClientBattle
|
||||
@@ -84,7 +85,7 @@ object PartySendBinding : CobblemonBlockingKeyBinding(
|
||||
if (isRidingPokemon(player) && canAttemptDismount(player, selectedPartyPokemon)) {
|
||||
sendToServer(DismountPokemonPacket())
|
||||
}
|
||||
else {
|
||||
else if (!isRidingSelectedPokemon(player, selectedPartyPokemon)){
|
||||
checkForTargetInteractions(player, selectedPartyPokemon)
|
||||
}
|
||||
}
|
||||
@@ -145,6 +146,11 @@ object PartySendBinding : CobblemonBlockingKeyBinding(
|
||||
if (player != vehicle.controllingPassenger) {
|
||||
return true
|
||||
}
|
||||
val isAirRide = vehicle.ridingController?.context?.style == RidingStyle.AIR
|
||||
val hasLandRide = vehicle.rideProp.behaviours?.get(RidingStyle.LAND) != null
|
||||
if (isAirRide && hasLandRide) {
|
||||
return false
|
||||
}
|
||||
else {
|
||||
return vehicle.pokemon.uuid == selectedPartyPokemon.uuid
|
||||
}
|
||||
@@ -157,6 +163,14 @@ object PartySendBinding : CobblemonBlockingKeyBinding(
|
||||
return true
|
||||
}
|
||||
|
||||
private fun isRidingSelectedPokemon(player: LocalPlayer, selectedPartyPokemon: Pokemon, ignoreControlling: Boolean = false): Boolean {
|
||||
if (!player.isPassenger) return false
|
||||
if (player.vehicle !is PokemonEntity) return false
|
||||
val vehicle = player.vehicle as PokemonEntity
|
||||
if (ignoreControlling && player.vehicle!!.controllingPassenger == player) return false
|
||||
return vehicle.pokemon.uuid == selectedPartyPokemon.uuid
|
||||
}
|
||||
|
||||
override fun onPress() {
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.net.data
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.net.messages.client.data.RideSettingsSyncPacket
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
object RideSettingsSyncHandler : ServerNetworkPacketHandler<RideSettingsSyncPacket> {
|
||||
override fun handle(packet: RideSettingsSyncPacket, server: MinecraftServer, player: ServerPlayer) {
|
||||
CobblemonRideSettings.bird = packet.bird
|
||||
CobblemonRideSettings.glider = packet.glider
|
||||
CobblemonRideSettings.helicopter = packet.helicopter
|
||||
CobblemonRideSettings.hover = packet.hover
|
||||
CobblemonRideSettings.jet = packet.jet
|
||||
CobblemonRideSettings.rocket = packet.rocket
|
||||
|
||||
CobblemonRideSettings.horse = packet.horse
|
||||
CobblemonRideSettings.minekart = packet.minekart
|
||||
CobblemonRideSettings.vehicle = packet.vehicle
|
||||
|
||||
CobblemonRideSettings.boat = packet.boat
|
||||
CobblemonRideSettings.burst = packet.burst
|
||||
CobblemonRideSettings.dolphin = packet.dolphin
|
||||
}
|
||||
}
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.net.data
|
||||
|
||||
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.data.CobblemonDataProvider
|
||||
import com.cobblemon.mod.common.net.messages.client.data.UnlockReloadPacket
|
||||
import net.minecraft.client.Minecraft
|
||||
|
||||
internal object UnlockReloadPacketHandler : ClientNetworkPacketHandler<UnlockReloadPacket> {
|
||||
override fun handle(packet: UnlockReloadPacket, client: Minecraft) {
|
||||
CobblemonDataProvider.canReload = true
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.persisted
|
||||
|
||||
import com.cobblemon.mod.common.config.CobblemonConfig
|
||||
import java.io.BufferedReader
|
||||
import java.io.BufferedWriter
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileWriter
|
||||
import java.io.InputStreamReader
|
||||
import java.nio.file.Paths
|
||||
import kotlin.io.path.exists
|
||||
import net.minecraft.client.CameraType
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
object ClientPersistedData {
|
||||
|
||||
val gson = CobblemonConfig.GSON
|
||||
|
||||
val snapshotsPath = Paths.get("cobblemon").resolve("snapshots.json")
|
||||
val ridingPerspectivesPath = Paths.get("cobblemon").resolve("riding_perspectives.json")
|
||||
|
||||
var ridingPerspectives: RidingPerspectiveData = readRidingPerspectivesData() ?: RidingPerspectiveData()
|
||||
|
||||
fun readSnapshotsData() : SnapshotAcknowledgementData? {
|
||||
if (snapshotsPath.exists()) {
|
||||
BufferedReader(InputStreamReader(FileInputStream(snapshotsPath.toFile()))).use { reader ->
|
||||
return gson.fromJson(reader, SnapshotAcknowledgementData::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun writeSnapshotsData(data: SnapshotAcknowledgementData) {
|
||||
snapshotsPath.toFile().parentFile.mkdirs()
|
||||
BufferedWriter(FileWriter(snapshotsPath.toFile())).use { writer -> gson.toJson(data, writer) }
|
||||
}
|
||||
|
||||
fun readRidingPerspectivesData() : RidingPerspectiveData? {
|
||||
if (ridingPerspectivesPath.exists()) {
|
||||
BufferedReader(InputStreamReader(FileInputStream(ridingPerspectivesPath.toFile()))).use { reader ->
|
||||
return gson.fromJson(reader, RidingPerspectiveData::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun saveRidingPerspectivesData() {
|
||||
ridingPerspectivesPath.toFile().parentFile.mkdirs()
|
||||
BufferedWriter(FileWriter(ridingPerspectivesPath.toFile())).use { writer -> gson.toJson(ridingPerspectives, writer) }
|
||||
}
|
||||
|
||||
data class SnapshotAcknowledgementData(val version: String, val dontShowAgain: Boolean)
|
||||
data class RidingPerspectiveData(
|
||||
val perspectives: MutableMap<ResourceLocation, CameraType> = mutableMapOf()
|
||||
) {
|
||||
fun updatePerspective(key: ResourceLocation, cameraType: CameraType) {
|
||||
perspectives[key] = cameraType
|
||||
saveRidingPerspectivesData()
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
-3
@@ -17,10 +17,12 @@ import com.cobblemon.mod.common.util.math.fromEulerXYZDegrees
|
||||
import com.mojang.blaze3d.platform.Lighting
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer
|
||||
import com.mojang.blaze3d.vertex.VertexMultiConsumer
|
||||
import net.minecraft.client.renderer.LightTexture
|
||||
import net.minecraft.client.renderer.MultiBufferSource
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import net.minecraft.client.renderer.texture.OverlayTexture
|
||||
import net.minecraft.core.component.DataComponents
|
||||
import net.minecraft.world.item.ItemDisplayContext
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import org.joml.Quaternionf
|
||||
@@ -46,8 +48,6 @@ class PokemonItemRenderer : CobblemonBuiltinItemRenderer {
|
||||
context.put(RenderContext.POSABLE_STATE, state)
|
||||
state.currentModel = model
|
||||
|
||||
val renderLayer = RenderType.entityCutout(VaryingModelRepository.getTexture(species.resourceIdentifier, state))
|
||||
|
||||
val transformations = positions[mode]!!
|
||||
|
||||
if (mode == ItemDisplayContext.GUI) {
|
||||
@@ -64,7 +64,19 @@ class PokemonItemRenderer : CobblemonBuiltinItemRenderer {
|
||||
val rotation = Quaternionf().fromEulerXYZDegrees(Vector3f(transformations.rotation.x, transformations.rotation.y, transformations.rotation.z))
|
||||
matrices.mulPose(rotation)
|
||||
rotation.conjugate()
|
||||
val vertexConsumer: VertexConsumer = vertexConsumers.getBuffer(renderLayer)
|
||||
|
||||
val renderLayer = RenderType.entityCutout(VaryingModelRepository.getTexture(species.resourceIdentifier, state))
|
||||
val isEnchanted = stack.get(DataComponents.ENCHANTMENTS)?.isEmpty == false
|
||||
val vertexConsumer: VertexConsumer =
|
||||
if (isEnchanted) {
|
||||
VertexMultiConsumer.create(
|
||||
vertexConsumers.getBuffer(RenderType.entityGlintDirect()),
|
||||
vertexConsumers.getBuffer(renderLayer),
|
||||
)
|
||||
} else {
|
||||
vertexConsumers.getBuffer(renderLayer)
|
||||
}
|
||||
|
||||
matrices.pushPose()
|
||||
val packedLight = if (mode == ItemDisplayContext.GUI) {
|
||||
LightTexture.pack(13, 13)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user