forked from mirrors/cobblemon
Compare commits
896
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c1d30c039 | ||
|
|
be466d1610 | ||
|
|
d75faae1a0 | ||
|
|
202bee5690 | ||
|
|
730dc88ede | ||
|
|
4f0a667f23 | ||
|
|
f1186f469c | ||
|
|
91eeecf6e4 | ||
|
|
f6cae9d52e | ||
|
|
ed94d76e98 | ||
|
|
6e2b76d6a1 | ||
|
|
2b70b222df | ||
|
|
e9f0ef6497 | ||
|
|
a4dd34235a | ||
|
|
8501fd3b18 | ||
|
|
52896b208e | ||
|
|
6ad6487dbe | ||
|
|
703ccb9d8e | ||
|
|
c33d4b591e | ||
|
|
e31f2a2598 | ||
|
|
513c958da1 | ||
|
|
9e1fde04ef | ||
|
|
6e4565d49b | ||
|
|
91f1523ef5 | ||
|
|
e949626f06 | ||
|
|
9678fcc009 | ||
|
|
4fbe3e406d | ||
|
|
00a1e77a9f | ||
|
|
e055dede73 | ||
|
|
98cfa0ac91 | ||
|
|
f2441e7b27 | ||
|
|
46c247c742 | ||
|
|
3d99db64b9 | ||
|
|
4f07746355 | ||
|
|
2f0a201cf1 | ||
|
|
778f24acf1 | ||
|
|
aa9c7b4f55 | ||
|
|
96e2e4a6fe | ||
|
|
20d903cffe | ||
|
|
d4383fa317 | ||
|
|
1687fa12db | ||
|
|
cf599de339 | ||
|
|
c5f92c1ad4 | ||
|
|
1d99da78e0 | ||
|
|
050a2a6a4b | ||
|
|
c80e7f4a51 | ||
|
|
f4fbdf8735 | ||
|
|
f2aa4141c0 | ||
|
|
aeded69b25 | ||
|
|
16b25a2e6e | ||
|
|
e66ebfb7a8 | ||
|
|
70e79e6fe4 | ||
|
|
797e0704e9 | ||
|
|
0b184a722b | ||
|
|
1a34c2942f | ||
|
|
2a20398b42 | ||
|
|
3931295086 | ||
|
|
550e579190 | ||
|
|
e7d710e0a3 | ||
|
|
acb86b1935 | ||
|
|
fd8e9b49b5 | ||
|
|
1fdbee91df | ||
|
|
bfab1d7070 | ||
|
|
e61e1390cb | ||
|
|
18151ff2f0 | ||
|
|
d5b1dfa9da | ||
|
|
9f0ddf4dd3 | ||
|
|
ef3ea62ae0 | ||
|
|
6b43bf9f48 | ||
|
|
a4012895f3 | ||
|
|
7f6a3e8e87 | ||
|
|
7a84ea723f | ||
|
|
2aad33ded5 | ||
|
|
254c95fa15 | ||
|
|
5aab7f67e1 | ||
|
|
ae4decacc7 | ||
|
|
ae5a9be626 | ||
|
|
05faea4ec8 | ||
|
|
05edd34db6 | ||
|
|
c198f8fccd | ||
|
|
521f9aaafa | ||
|
|
8d38505993 | ||
|
|
7c5fab02a8 | ||
|
|
7e5a267c89 | ||
|
|
5b23b0cfbe | ||
|
|
778ceff183 | ||
|
|
f32050c8b5 | ||
|
|
3522bac72a | ||
|
|
ecf037666f | ||
|
|
618d062ca9 | ||
|
|
49841b40ec | ||
|
|
87141e1137 | ||
|
|
1f962d664f | ||
|
|
09797f25b1 | ||
|
|
3911413c69 | ||
|
|
e075e60231 | ||
|
|
acdd98573b | ||
|
|
12f3e7b78b | ||
|
|
a462fe5efc | ||
|
|
2390662fc8 | ||
|
|
1bd5d2372f | ||
|
|
12f6bb55c2 | ||
|
|
fdb37ba5e8 | ||
|
|
7a99845d0f | ||
|
|
54d183c8d3 | ||
|
|
29713bbe1f | ||
|
|
247052ee67 | ||
|
|
14b7e93965 | ||
|
|
85a4793a1e | ||
|
|
29f6524c49 | ||
|
|
1e617a684b | ||
|
|
c9a0352a7c | ||
|
|
1a5808e5a6 | ||
|
|
f14d242907 | ||
|
|
4998ee2c5b | ||
|
|
015402d54b | ||
|
|
6f697d2b9a | ||
|
|
f18752d484 | ||
|
|
5876c02d44 | ||
|
|
c890f6910c | ||
|
|
d157536e1e | ||
|
|
dac2d9e96e | ||
|
|
5c71680db9 | ||
|
|
5f5c036f8d | ||
|
|
1f16c2d542 | ||
|
|
2da9dc50f7 | ||
|
|
092f9ef9f9 | ||
|
|
2f12b05d43 | ||
|
|
4299aff1bf | ||
|
|
fb90ccb86e | ||
|
|
c112f9615c | ||
|
|
fc882190fe | ||
|
|
15d7cd815d | ||
|
|
6b5969fff6 | ||
|
|
5053c633b7 | ||
|
|
c4294697e6 | ||
|
|
dfa1c5e369 | ||
|
|
51f3b4357d | ||
|
|
220a9741ae | ||
|
|
48a9446f73 | ||
|
|
f1edfc453e | ||
|
|
8d612ec395 | ||
|
|
6d9daf3cf6 | ||
|
|
c7ecfc74de | ||
|
|
fefdfd877b | ||
|
|
35fb170225 | ||
|
|
d2e405131f | ||
|
|
b6ec0205ef | ||
|
|
e57a42e755 | ||
|
|
f34a03c8d6 | ||
|
|
8b1599705f | ||
|
|
e7f6fc32c4 | ||
|
|
5119617f00 | ||
|
|
6e84efb53c | ||
|
|
de3191b768 | ||
|
|
76ec4b4d5a | ||
|
|
25702ade61 | ||
|
|
c6da88d0b2 | ||
|
|
c4acc09fbd | ||
|
|
53b434906b | ||
|
|
b22a31909d | ||
|
|
cbbbacc215 | ||
|
|
d22d81209d | ||
|
|
89b989c46c | ||
|
|
714d51b26e | ||
|
|
8571d78894 | ||
|
|
72c57fde39 | ||
|
|
fc5c479f31 | ||
|
|
d3c54924be | ||
|
|
65c5e95ddf | ||
|
|
fc1dec6b4b | ||
|
|
7604af9308 | ||
|
|
ab1e138ace | ||
|
|
62ee94c71d | ||
|
|
0fae95aabc | ||
|
|
bbfd864b33 | ||
|
|
b107d76762 | ||
|
|
8d4f10349c | ||
|
|
f36787e67f | ||
|
|
fc198c599e | ||
|
|
26f753880c | ||
|
|
a9ebb0b6f2 | ||
|
|
a7158ee57a | ||
|
|
06c3fd3f09 | ||
|
|
4e196e8a29 | ||
|
|
871f33f442 | ||
|
|
21c1f912a7 | ||
|
|
a0d9ce12a6 | ||
|
|
c07173fc0f | ||
|
|
3faf8ac302 | ||
|
|
e85b3f3cc5 | ||
|
|
251a09f78c | ||
|
|
776fb5413f | ||
|
|
f645fd9b60 | ||
|
|
ac161ae5b4 | ||
|
|
fc3542e3eb | ||
|
|
7c44dd204e | ||
|
|
88932c8c41 | ||
|
|
411f98d0ae | ||
|
|
02a1929ede | ||
|
|
ff90059eec | ||
|
|
eca14f61ac | ||
|
|
9ccf2bb9ad | ||
|
|
eacb0fe587 | ||
|
|
1752588cbc | ||
|
|
9e09ac52fc | ||
|
|
845ade1aeb | ||
|
|
f6bd04f016 | ||
|
|
afa1d9af8b | ||
|
|
bf6db8b745 | ||
|
|
9aef7b9c3b | ||
|
|
c5bebb1abb | ||
|
|
990fed2fe0 | ||
|
|
4a30bb3b04 | ||
|
|
fe77a3cd30 | ||
|
|
de9e1949fe | ||
|
|
fac8c5f510 | ||
|
|
2d077c607a | ||
|
|
f75e3d11da | ||
|
|
f46968f878 | ||
|
|
3439aeff1e | ||
|
|
2ada7e0b68 | ||
|
|
b8f73f277d | ||
|
|
7f6197ab8b | ||
|
|
6df2b996e2 | ||
|
|
ddc4bd358d | ||
|
|
e197287775 | ||
|
|
be019c79df | ||
|
|
73fe851c58 | ||
|
|
8544744530 | ||
|
|
32d7bfcef3 | ||
|
|
27263cd057 | ||
|
|
55f0962205 | ||
|
|
63143832e0 | ||
|
|
29215f5c0c | ||
|
|
70bf3e7805 | ||
|
|
40c5df46ce | ||
|
|
b4d9d1b963 | ||
|
|
e753c75c31 | ||
|
|
4e96222a4d | ||
|
|
4ee90d4cfb | ||
|
|
540cd612c1 | ||
|
|
1b1d22fdf3 | ||
|
|
1ee1001774 | ||
|
|
533272ef09 | ||
|
|
cf5e1bf142 | ||
|
|
4b8196685a | ||
|
|
d1053468bf | ||
|
|
f0c41865db | ||
|
|
2b2264bdca | ||
|
|
1e32f7b80a | ||
|
|
cfc8edfa9e | ||
|
|
417b49beda | ||
|
|
a2f6a35227 | ||
|
|
b9f1cc5883 | ||
|
|
ad132fe729 | ||
|
|
f33892fcd1 | ||
|
|
91300e4d03 | ||
|
|
2a56709395 | ||
|
|
fc135faf57 | ||
|
|
cdd72c664e | ||
|
|
5f025d0f68 | ||
|
|
6af2c807ab | ||
|
|
c45c90c6c7 | ||
|
|
1ec97c4c90 | ||
|
|
f0bcd38ac7 | ||
|
|
f4ba446c79 | ||
|
|
60f98ebfce | ||
|
|
08c2a0d07b | ||
|
|
1f68fdac8c | ||
|
|
e8d4f0fcf4 | ||
|
|
36a93bda5a | ||
|
|
5d522bd608 | ||
|
|
8d60b07c54 | ||
|
|
5f9cc86044 | ||
|
|
35436c1d09 | ||
|
|
a6b7e07cf5 | ||
|
|
0ba6215b8d | ||
|
|
e2f6fae689 | ||
|
|
296aa01d0f | ||
|
|
12ab3f2d07 | ||
|
|
5a7a374bc9 | ||
|
|
ceece45ad4 | ||
|
|
b344ad66c5 | ||
|
|
d66793d186 | ||
|
|
cc00962752 | ||
|
|
dbc7af4ad4 | ||
|
|
5f5bf01cb5 | ||
|
|
04f2af22f3 | ||
|
|
ea916a0db8 | ||
|
|
3f03afc7eb | ||
|
|
e940483863 | ||
|
|
9e3d3df3a9 | ||
|
|
c18368d777 | ||
|
|
3894a99bc4 | ||
|
|
aab5b55204 | ||
|
|
3e6e346eb9 | ||
|
|
3a83ed424f | ||
|
|
7bef6db7b7 | ||
|
|
71775328ef | ||
|
|
cbccc612a4 | ||
|
|
63fd5d90c1 | ||
|
|
92336d6107 | ||
|
|
727785b0b3 | ||
|
|
762241420e | ||
|
|
93011f084b | ||
|
|
7c90a8425e | ||
|
|
8674cbe788 | ||
|
|
8075fe0bae | ||
|
|
f3f29ca0ea | ||
|
|
0152803192 | ||
|
|
2544cbc252 | ||
|
|
5e094241e3 | ||
|
|
bd028c328d | ||
|
|
598cb1d964 | ||
|
|
79d264fd94 | ||
|
|
5f691d3f1c | ||
|
|
7be0163e76 | ||
|
|
c0b3068824 | ||
|
|
9036a2f317 | ||
|
|
dd35adc05d | ||
|
|
26b5707317 | ||
|
|
75e39f5595 | ||
|
|
7fdeaeabef | ||
|
|
c75ab28002 | ||
|
|
809ffd8b90 | ||
|
|
da7610951d | ||
|
|
62a8320068 | ||
|
|
f21b38f66a | ||
|
|
76fdd1fa98 | ||
|
|
596765bb52 | ||
|
|
297aafba45 | ||
|
|
156983bf58 | ||
|
|
c69930b836 | ||
|
|
6926932340 | ||
|
|
221b3308fa | ||
|
|
66840670b6 | ||
|
|
28887d9116 | ||
|
|
6ef1bac1d7 | ||
|
|
5d6a7807e5 | ||
|
|
20b7eb17f3 | ||
|
|
a0a682e224 | ||
|
|
103ac86345 | ||
|
|
8f705a698d | ||
|
|
f4fb09947e | ||
|
|
97e31e341b | ||
|
|
813789235d | ||
|
|
d161b11892 | ||
|
|
5a82e7e185 | ||
|
|
3bc8d3ce75 | ||
|
|
d9e9fd0f4e | ||
|
|
fddb203db3 | ||
|
|
8a8cace5e8 | ||
|
|
5f5b4ee3c1 | ||
|
|
aa6581ae39 | ||
|
|
8aa9e0191f | ||
|
|
8dc09796f8 | ||
|
|
8f93712edb | ||
|
|
b91d1b931e | ||
|
|
9d84cbb3ac | ||
|
|
8e730f2b5c | ||
|
|
7b5809fd55 | ||
|
|
bc682ddf62 | ||
|
|
990ead0544 | ||
|
|
4cf55dc95d | ||
|
|
27a9f3c704 | ||
|
|
d70bb243b2 | ||
|
|
301a3efb7c | ||
|
|
05b7cbe20a | ||
|
|
48cbbc1bef | ||
|
|
f8f7c28274 | ||
|
|
0ebb53888a | ||
|
|
83aba7e4a6 | ||
|
|
db10ae2183 | ||
|
|
c0071c1525 | ||
|
|
71f7b3b3d8 | ||
|
|
548b8522a8 | ||
|
|
4791b146dc | ||
|
|
b7a9931393 | ||
|
|
9a35a11440 | ||
|
|
6d4a50cee2 | ||
|
|
818a637897 | ||
|
|
4c96ab9b28 | ||
|
|
d5a316b9ae | ||
|
|
87e32f0e9b | ||
|
|
bd6d7b7149 | ||
|
|
73275626a3 | ||
|
|
0686428138 | ||
|
|
f05e6008e5 | ||
|
|
63de8fe44a | ||
|
|
53372e5400 | ||
|
|
2fbb1f354c | ||
|
|
bf06476558 | ||
|
|
2e3a742477 | ||
|
|
94c7542d2c | ||
|
|
fa2b6b5996 | ||
|
|
8e77dc70f6 | ||
|
|
51e3748adb | ||
|
|
d8cf2701c7 | ||
|
|
4450164d9c | ||
|
|
31b7c83369 | ||
|
|
a170123e2c | ||
|
|
b99899353c | ||
|
|
c3c17a228e | ||
|
|
41d607ad56 | ||
|
|
58b3596948 | ||
|
|
ed4456d756 | ||
|
|
608b711833 | ||
|
|
3adc1b9f30 | ||
|
|
478cbc2489 | ||
|
|
481734e6b1 | ||
|
|
636792fa1f | ||
|
|
ad65002b19 | ||
|
|
903f3b6d5a | ||
|
|
4456743e97 | ||
|
|
d0989e5ece | ||
|
|
ce3f703de2 | ||
|
|
03edb5a385 | ||
|
|
180229db61 | ||
|
|
cf77713860 | ||
|
|
b6e10c016e | ||
|
|
8ed68742df | ||
|
|
0494193c9b | ||
|
|
7bee34170d | ||
|
|
ce09236dab | ||
|
|
0bb968dd73 | ||
|
|
f3d4cb34e0 | ||
|
|
3a22e387b6 | ||
|
|
956b3bfcbd | ||
|
|
16aa930735 | ||
|
|
55733e8ad5 | ||
|
|
67591efdad | ||
|
|
5d300c8157 | ||
|
|
3b6e1bd429 | ||
|
|
c77198a845 | ||
|
|
bd32c07cc1 | ||
|
|
1eb7354fc0 | ||
|
|
52ad6a3250 | ||
|
|
aa57bbbf34 | ||
|
|
26278eeb46 | ||
|
|
99cfc4f147 | ||
|
|
89281a1f6d | ||
|
|
ad044fb05c | ||
|
|
7088ce3828 | ||
|
|
a9ceddd94a | ||
|
|
fc6a712906 | ||
|
|
1d0397c399 | ||
|
|
8ad443a4c5 | ||
|
|
0d1f9a0093 | ||
|
|
7587344cb4 | ||
|
|
f0926d7524 | ||
|
|
7c3b17d841 | ||
|
|
e99c55e812 | ||
|
|
5053d559bc | ||
|
|
1a30109f1c | ||
|
|
263e3cd79f | ||
|
|
2ba8062915 | ||
|
|
edc298bf64 | ||
|
|
aa1158fcfe | ||
|
|
ab40ae81c4 | ||
|
|
b5df1878ed | ||
|
|
800965cb40 | ||
|
|
b00f1d9352 | ||
|
|
a588cfadf8 | ||
|
|
2eb4062f67 | ||
|
|
43321d8872 | ||
|
|
a5ea584b75 | ||
|
|
6a9edc2e67 | ||
|
|
12377deff7 | ||
|
|
583377d39b | ||
|
|
2c1da8df06 | ||
|
|
737ddd973b | ||
|
|
d749b04261 | ||
|
|
93deb3a669 | ||
|
|
dca45a95e9 | ||
|
|
0412f16460 | ||
|
|
efd4a72287 | ||
|
|
5088497575 | ||
|
|
a62e5a6571 | ||
|
|
0ec337ee6c | ||
|
|
76df0a429d | ||
|
|
08ea9378de | ||
|
|
c2bd21ab96 | ||
|
|
5db8ccfad4 | ||
|
|
aa206d386a | ||
|
|
bba8a12a2a | ||
|
|
a235af6c01 | ||
|
|
a666a74a32 | ||
|
|
c285120ff4 | ||
|
|
dbd2890120 | ||
|
|
a5db46cdc5 | ||
|
|
765cf9b96a | ||
|
|
efe7952869 | ||
|
|
9e434de285 | ||
|
|
712fe2c250 | ||
|
|
5db5a2ed35 | ||
|
|
49c96e6fc8 | ||
|
|
31129ef14d | ||
|
|
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 | ||
|
|
17c71efb17 | ||
|
|
e80eb91e69 | ||
|
|
e2ac4449b3 | ||
|
|
ecec7ccee3 | ||
|
|
228282f083 | ||
|
|
3bc7d39800 | ||
|
|
3dc5873642 | ||
|
|
a5b6a954fd | ||
|
|
a5c3d2f800 | ||
|
|
00d2bbbe42 | ||
|
|
8d6291c4c2 | ||
|
|
2b77d030e8 | ||
|
|
029927af6a | ||
|
|
bf791dc4d1 | ||
|
|
56ad481304 | ||
|
|
cc7a9ae44a | ||
|
|
664f79f3a7 | ||
|
|
2a295a8796 | ||
|
|
558907eb5c | ||
|
|
370b689ab4 | ||
|
|
e2740babc5 | ||
|
|
3393ab17d8 | ||
|
|
645029a9c7 | ||
|
|
f168744ccd | ||
|
|
67210cb5f9 | ||
|
|
779fbb4f38 | ||
|
|
2ee556dc31 | ||
|
|
4457df9aba | ||
|
|
e1bd0d688f | ||
|
|
0bce2d8645 | ||
|
|
beddea1ecb | ||
|
|
40b351a303 | ||
|
|
dc32bd1845 | ||
|
|
a096485155 | ||
|
|
f1ae7c4476 | ||
|
|
bcfac2f68a | ||
|
|
ff1902770c | ||
|
|
ebb01d4fcc | ||
|
|
2a62b49eab | ||
|
|
0b392c66eb | ||
|
|
5733074bd8 | ||
|
|
88ff4e37b8 | ||
|
|
245226f4f6 | ||
|
|
35b7a80501 | ||
|
|
91949414cc | ||
|
|
d3e1c2c79d | ||
|
|
43dbfc772e | ||
|
|
10bfe3b29d | ||
|
|
ff88a3c756 | ||
|
|
4d47cb8385 | ||
|
|
8aba2bc279 | ||
|
|
fe91e912d0 | ||
|
|
680bc1d205 | ||
|
|
eee3296120 | ||
|
|
e04e82c6de | ||
|
|
d0c4bd5a16 | ||
|
|
500069e5d8 | ||
|
|
13ae174c66 | ||
|
|
95589dd48b | ||
|
|
4cb17135bd | ||
|
|
8dc23160fc | ||
|
|
4a96a41c8e | ||
|
|
3bf777c2a9 | ||
|
|
b5174686b4 | ||
|
|
26a7bc29b3 | ||
|
|
05cd37eb38 | ||
|
|
3f18e7c0bf | ||
|
|
3ac3fcbcda | ||
|
|
27b3884f27 | ||
|
|
15e8f46702 | ||
|
|
e91b1061d3 | ||
|
|
3c3a23a397 | ||
|
|
438de6ed6d | ||
|
|
95399a32a8 | ||
|
|
7089ff68d4 | ||
|
|
c754d820a5 | ||
|
|
81c819ddd3 | ||
|
|
f042d167d1 | ||
|
|
eff3bd91d7 | ||
|
|
ec3e0bfd04 | ||
|
|
1391bfee01 | ||
|
|
b61d739e46 | ||
|
|
7a050d5374 | ||
|
|
52076de76a | ||
|
|
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 | ||
|
|
be8dfd7fa2 | ||
|
|
940ec82182 | ||
|
|
67c01ce5f2 | ||
|
|
e603325eef | ||
|
|
afd09e93eb | ||
|
|
92775a4b8a | ||
|
|
6a2668171e | ||
|
|
5b7dc9f8f4 | ||
|
|
75dc76020f | ||
|
|
56289ce5bf | ||
|
|
e4ec7b0ae7 | ||
|
|
f38ef0b6b9 | ||
|
|
d5bb3e2d97 | ||
|
|
66f6555711 | ||
|
|
472ca9c748 | ||
|
|
054e4dffdc | ||
|
|
be5ee8a6a3 |
+2
-1
@@ -12,7 +12,8 @@ out
|
||||
*.ipr
|
||||
*.iws
|
||||
*.iml
|
||||
.idea
|
||||
.idea/*
|
||||
!.idea/icon.png
|
||||
|
||||
# gradle
|
||||
build
|
||||
|
||||
Generated
BIN
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
@@ -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.
|
||||
|
||||
+10
-1
@@ -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.
|
||||
@@ -11,6 +12,14 @@
|
||||
- SpawningContext is now SpawnablePosition
|
||||
- WorldSlice is SpawningZone
|
||||
- SpawningProspector is now SpawningZoneGenerator
|
||||
- Majorly refactored the hierarchy of Spawner
|
||||
- The base Spawner interface provides more functions to allow single-point and area spawning given appropriate inputs.
|
||||
- TickingSpawner is removed in favour of outside code handling ticking logic.
|
||||
- AreaSpawner is removed.
|
||||
- BasicSpawner is the first implementation of Spawner which can be used for any purpose.
|
||||
- PlayerSpawners are now mixin'd into ServerPlayer and ticked from ServerPlayer#tick.
|
||||
- A hierarchy diagram can be found at `./docs/spawner-hierarchy.png` in the mod repository.
|
||||
- The SpawnerManager class has been removed as its functionality is all now handled elsewhere.
|
||||
- Renamed things in Spawn Rules to go with the other renames:
|
||||
- contextSelector is now spawnablePositionSelector
|
||||
- contextSelector is now spawnablePositionSelector
|
||||
- context is now spawnable_position
|
||||
+222
-1
@@ -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.
|
||||
@@ -33,6 +34,7 @@
|
||||
- Added Hyper Training items (IV Modification) as well as some additional candy items to do so (Health Candy, Sickly Candy)
|
||||
- Added Galarica Nut Bushes
|
||||
- Many Pokémon (mostly cats) are now feared by phantoms
|
||||
- Combees are now capable of gathering nectar from flowers and delivering it to either Saccharine leaves or hives. (Hive interactions are restricted to wild Combees)
|
||||
- Lightning is now affected by a Pokemon's ability/typing
|
||||
- Pokémon with the ability Lightning Rod draw in lightning similar to a lightning rod block albeit with a lower priority and range, gain immunity to lightning damage, and receive a temporary damage buff.
|
||||
- Pokémon with the ability Motor Drive are immune to lightning damage and receive a temporary speed buff when struck by lightning
|
||||
@@ -47,11 +49,15 @@
|
||||
- Berries can now be smelted into their respective dyes.
|
||||
- Added Syrupy Apples.
|
||||
- Added `/runmolang <molang> [<npc>|<player>|<pokemon>]` command that executes a MoLang expression with the provided options as environment variables, as well as the entity (as `q.entity`) that executed the command.
|
||||
- Added new 69th berry, Eggant.
|
||||
- Added bubble quirk to Kingler; like Krabby, Kingler will blows bubbles during dusk.
|
||||
- Added `.Pre` and `.Post` to the following events:
|
||||
- `PokemonRecallEvent`
|
||||
- `TradeEvent`
|
||||
- Added a new gamerule, 'healerHealsPC', when set to true a successful use of a healer will also heal all of the Pokemon in that player's PC.
|
||||
- Mooshtanks will switch between their red and brown variant when hit by lightning
|
||||
- Smeargle spawns with differing tail colour depending on its [Characteristic](https://bulbapedia.bulbagarden.net/wiki/Characteristic) stat
|
||||
- Added smooth level-up animations to the party overlay to replace chat messages.
|
||||
|
||||
### Pokémon Added
|
||||
|
||||
@@ -83,6 +89,8 @@
|
||||
- Altaria
|
||||
- Snorunt
|
||||
- Glalie
|
||||
- Shuppet
|
||||
- Banette
|
||||
- Latias
|
||||
- Latios
|
||||
|
||||
@@ -131,6 +139,11 @@
|
||||
- Swadloon
|
||||
- Leavanny
|
||||
- Druddigon
|
||||
- Minccino
|
||||
- Cinccino
|
||||
- Vanillite
|
||||
- Vanillish
|
||||
- Vanilluxe
|
||||
|
||||
#### Gen 6
|
||||
- Skiddo
|
||||
@@ -143,6 +156,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
|
||||
@@ -277,6 +292,37 @@
|
||||
- Parasect
|
||||
- Bastiodon
|
||||
- Amaura
|
||||
- Varoom
|
||||
- Revavroom
|
||||
- Bouffalant
|
||||
- Lickilicky
|
||||
- Blaziken
|
||||
- Rhyhorn
|
||||
- Rhydon
|
||||
- Rhyperior
|
||||
- Tauros
|
||||
- Tauros - Paldea Aqua
|
||||
- Tauros - Paldea Blaze
|
||||
- Tauros - Paldea Combat
|
||||
- Dewgong
|
||||
- Exeggutor - Kanto
|
||||
- Corphish (updated placeholders for crustaceous tripod gait legs)
|
||||
- Crawdaunt (updated placeholders for crustaceous bipedal legs)
|
||||
- Dragapult
|
||||
- Camerupt
|
||||
- Relicanth
|
||||
- Heracross
|
||||
- Salamence
|
||||
- Staraptor
|
||||
- Dusknoir
|
||||
- Arcanine
|
||||
- Carvanha
|
||||
- Sharpedo
|
||||
- Mimikyu
|
||||
- Dewgong
|
||||
- Mime Jr
|
||||
- Gyarados
|
||||
- Lapras
|
||||
|
||||
### Model updates for the following Pokémon
|
||||
- Cleffa
|
||||
@@ -304,6 +350,8 @@
|
||||
- Nosepass
|
||||
- Probopass
|
||||
- Kangaskhan
|
||||
- Scorbunny
|
||||
- Raboot
|
||||
- Cinderace
|
||||
- Magnemite
|
||||
- Magneton
|
||||
@@ -383,6 +431,25 @@
|
||||
- Starly
|
||||
- Staravia
|
||||
- Staraptor
|
||||
- Varoom
|
||||
- Revavroom
|
||||
- Nickit
|
||||
- Thievul
|
||||
- Litwick
|
||||
- Lampent
|
||||
- Chandelure
|
||||
- Rayquaza
|
||||
- Aerodactyl
|
||||
- Basculin
|
||||
- Basculegion
|
||||
- Shroodle
|
||||
- Grafaiai
|
||||
- Steelix
|
||||
- Dratini
|
||||
- Dragonair
|
||||
- Dragonite
|
||||
- Cutiefly
|
||||
- Ribombee
|
||||
|
||||
### Changes
|
||||
- Changed pokemon caught and seen count to update based on the current pokedex being looked
|
||||
@@ -396,6 +463,8 @@
|
||||
- Updated some item tags to better integrate behaviours between Cobblemon, Vanilla Minecraft, and other mods
|
||||
- Removed Cooked Meat, Raw Meat, Protein Ingredients, and Zinc Ingredients, the first two are now using `c` namespace tags, the latter have better integrated use of tags within their recipes which removes need for custom tags.
|
||||
- Added our seeds tag into `#c:seeds`, which is now made use of for the Miracle Seed recipe.
|
||||
- Updated the `#cobblemon:berries` tag to use subtags located in `data/cobblemon/tags/item/berries`, such as `#cobblemon:berries/hp_recovery` for sorting purposes
|
||||
- Moved the tag `#cobblemon:mutated_berries` to `#cobblemon:berries/mutation_result`
|
||||
- Added herbs and snowballs to the consumable in PvE and Wild battle tags
|
||||
- Tweaked the Natural Materials Vanilla file to fit with the changes to tags
|
||||
- Substantially optimised spawning checks mainly by front-loading biome filtering.
|
||||
@@ -446,6 +515,9 @@
|
||||
- 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
|
||||
- Shulker Boxes and Traveler's Backpacks AND Packed Up backpacks can no longer be held by Pokémon. Thanks Monocle ;) You could re-enable this with our datapack (but you'd be crazy).
|
||||
- Moomoo Milk now clears Pokémon stat changes when used in battle.
|
||||
|
||||
### Fixes
|
||||
- Fixed game crashing when removing national pokedex using datapacks
|
||||
@@ -528,6 +600,12 @@
|
||||
- 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.
|
||||
- Fixed some specific bag items not being dropped when used in battle.
|
||||
- The Corphish line will now sink in water.
|
||||
- Fixed the "use all berry bait" achievement not being progressed
|
||||
- Fixed bobber hook and berry sprouts texture sizes causing mipmap issues.
|
||||
|
||||
### Developer
|
||||
- A finished battle now has winners and losers set inside of `PokemonBattle` instead of them always being empty.
|
||||
@@ -565,10 +643,25 @@
|
||||
- Added a `hoverText` option to PartySelectCallback, to display a tooltip on hovering over a Pokémon in the selection screen.
|
||||
- `PokemonEntity` instances spawned into the world now appropriately finalize the spawn for mod compatibility.
|
||||
- Added PokedexManager.obtain as a replacement for .catch which is not a friendly function name in Java.
|
||||
|
||||
- Added `Pokemon#hyperTrainIV()` and `IVs#setHyperTrainedIV(Stat, Int)`
|
||||
- `ElementalType` now implements `ShowdownIdentifiable` to ensure the communcation with showdown stays consistent (also in regards to TeraTypes)
|
||||
- Pokemon no longer have a change observable
|
||||
- Pokémon can now have a behaviour changing the value of a species feature on lightning hit:
|
||||
```JSON
|
||||
{
|
||||
"behaviour": {
|
||||
"lightningHit": {
|
||||
"rotateFeatures": [
|
||||
{
|
||||
"key": "mooshtank",
|
||||
"chain": ["red", "brown"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
This will change the value of `mooshtank` from `red` to `brown` and vice versa when a lightning strikes this Pokémon. Only chains of which a value is the current value will be considered, so multiple entries for the same key with different chains can be added.
|
||||
|
||||
### MoLang & Datapacks
|
||||
- The following usages for item predicates can now use item conditions like advancements do, you can learn about them in the [Minecraft wiki](https://minecraft.wiki/w/Advancement_definition#minecraft:filled_bucket)
|
||||
@@ -658,6 +751,133 @@
|
||||
- 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
|
||||
- Fixed `cobblemon:reel_in_pokemon` criteria not working when used together with a `baitId`
|
||||
- Also changed the default from `cobblemon:empty_bait` to `any`
|
||||
- The previous default is still available by using the above as baitId
|
||||
- Added support for species-specific move action effects, using the format `{move_id}_{species}.json`.
|
||||
|
||||
### Particles
|
||||
Added new/updated particles for the following moves:
|
||||
- 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)
|
||||
|
||||
@@ -844,6 +1064,7 @@
|
||||
- Parametric particle motion now works.
|
||||
- Event-spawned particles now work.
|
||||
- Particles can now have independent coordinate spaces.
|
||||
- Fixed suffocation of Pokémon when a pokeball breaks near a wall
|
||||
|
||||
### Developer
|
||||
- Updated the Pokédex data updated events to always include a `Pokemon` instance, and optionally a `DisguiseData` instance.
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common;
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawner;
|
||||
|
||||
/**
|
||||
* Used for mixin shenanigans. ServerPlayers now store their Cobblemon spawner
|
||||
* inside, so this interface helps execute that.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since October 27th, 2025
|
||||
*/
|
||||
public interface PlayerSpawnerAccessor {
|
||||
PlayerSpawner getPlayerSpawner();
|
||||
void setPlayerSpawner(PlayerSpawner spawner);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.duck;
|
||||
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public interface RidePassenger {
|
||||
void cobblemon$setRideXRot(float rideXRot);
|
||||
float cobblemon$getRideXRot();
|
||||
void cobblemon$setRideYRot(float rideYRot);
|
||||
float cobblemon$getRideYRot();
|
||||
void cobblemon$setRideEyePos(Vec3 rideEyePos);
|
||||
Vec3 cobblemon$getRideEyePos();
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.duck.RidePassenger;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.projectile.AbstractArrow;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(AbstractArrow.class)
|
||||
public abstract class AbstractArrowMixin {
|
||||
|
||||
@Inject(
|
||||
method = "<init>(Lnet/minecraft/world/entity/EntityType;Lnet/minecraft/world/entity/LivingEntity;Lnet/minecraft/world/level/Level;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemStack;)V",
|
||||
at = @At("TAIL")
|
||||
)
|
||||
private void cobblemon$adjustSpawnPosition(
|
||||
EntityType<? extends AbstractArrow> entityType,
|
||||
LivingEntity owner,
|
||||
Level level,
|
||||
ItemStack pickupItemStack,
|
||||
@Nullable ItemStack firedFromWeapon,
|
||||
CallbackInfo ci
|
||||
) {
|
||||
AbstractArrow arrow = (AbstractArrow)(Object)this;
|
||||
if(!(owner.getVehicle() instanceof OrientationControllable vehicle)) return;
|
||||
if(!(vehicle instanceof PokemonEntity)) return;
|
||||
var vehicleController = vehicle.getOrientationController();
|
||||
|
||||
// If this is a pokemon ride passenger then use the rider eye position to set the arrow spawn position.
|
||||
if (owner instanceof RidePassenger ridePassenger && vehicleController != null ) {
|
||||
var eyePos = ridePassenger.cobblemon$getRideEyePos(); //.add(0.0, -2.0, 0.0);
|
||||
arrow.setPos(eyePos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories;
|
||||
import com.cobblemon.mod.common.api.ai.config.task.PathToBeeHiveTaskConfig;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.tags.BlockTags;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.item.component.CustomData;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.BeehiveBlock;
|
||||
import net.minecraft.world.level.block.HorizontalDirectionalBlock;
|
||||
import net.minecraft.world.level.block.entity.BeehiveBlockEntity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@Mixin(BeehiveBlockEntity.Occupant.class)
|
||||
public abstract class BeeOccupantMixin {
|
||||
|
||||
@Inject(method = "createEntity", at = @At("RETURN"), cancellable = true)
|
||||
private void cobblemon$createEntity(Level level, BlockPos pos, CallbackInfoReturnable<Entity> cir) {
|
||||
// Needing to replicate all the post op stuff on the hive isn't great
|
||||
if (cir.getReturnValue() == null) {
|
||||
final BeehiveBlockEntity.Occupant occupant = (BeehiveBlockEntity.Occupant) (Object) this;
|
||||
CompoundTag compoundTag = occupant.entityData().copyTag();
|
||||
Entity entity = EntityType.loadEntityRecursive(compoundTag, level, (entityx) -> entityx);
|
||||
if (entity instanceof PokemonEntity pokemonEntity) {
|
||||
var state = level.getBlockState(pos);
|
||||
var newPos = Vec3.ZERO;
|
||||
var newYaw = 0f;
|
||||
var brain = pokemonEntity.getBrain();
|
||||
if (state.is(BlockTags.BEEHIVES)) {
|
||||
// position in front of the entrance, face away from the block
|
||||
var facing = state.getValue(HorizontalDirectionalBlock.FACING);
|
||||
newPos = pos.relative(facing).getCenter();
|
||||
newYaw = facing.toYRot();
|
||||
brain.setMemory(CobblemonMemories.INSTANCE.getHIVE_LOCATION(), pos); // This needs to be set in the case that the hive was picked up and moved.
|
||||
} else {
|
||||
// Block has likely been destroyed
|
||||
var center = pos.getCenter();
|
||||
newPos = new Vec3(
|
||||
center.x + level.random.nextFloat() * 0.6 - 0.3,
|
||||
center.y + level.random.nextFloat() * 0.6 - 0.3,
|
||||
center.z + level.random.nextFloat() * 0.6 - 0.3
|
||||
);
|
||||
newYaw = level.random.nextFloat() * 360F;
|
||||
brain.eraseMemory(CobblemonMemories.INSTANCE.getHIVE_LOCATION());
|
||||
}
|
||||
entity.yRotO = newYaw;
|
||||
entity.setPos(newPos);
|
||||
// Do honey logic
|
||||
var hasNectar = brain.getMemory(CobblemonMemories.INSTANCE.getHAS_NECTAR()).orElse(false);
|
||||
if (hasNectar) {
|
||||
// Remove nectar and reset got to hive cooldown
|
||||
brain.eraseMemory(CobblemonMemories.INSTANCE.getHAS_NECTAR());
|
||||
brain.setMemoryWithExpiry(CobblemonMemories.INSTANCE.getHIVE_COOLDOWN(), true, PathToBeeHiveTaskConfig.STAY_OUT_OF_HIVE_COOLDOWN);
|
||||
// Increment honey level of the hive
|
||||
if (state.is(BlockTags.BEEHIVES, (blockStateBase) -> blockStateBase.hasProperty(BeehiveBlock.HONEY_LEVEL))) {
|
||||
int i = state.getValue(BeehiveBlock.HONEY_LEVEL);
|
||||
if (i < 5) {
|
||||
int j = level.random.nextInt(100) == 0 ? 2 : 1;
|
||||
if (i + j > 5) {
|
||||
--j;
|
||||
}
|
||||
level.setBlockAndUpdate(pos, state.setValue(BeehiveBlock.HONEY_LEVEL, i + j));
|
||||
}
|
||||
}
|
||||
}
|
||||
cir.setReturnValue(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "of", at = @At("HEAD"), cancellable = true)
|
||||
private static void cobblemon$of(Entity entity, CallbackInfoReturnable<BeehiveBlockEntity.Occupant> cir) {
|
||||
if (entity instanceof PokemonEntity pokemonEntity) {
|
||||
List<String> IGNORED_BEE_TAGS = Arrays.asList("Air", "ArmorDropChances", "ArmorItems", "Brain", "CanPickUpLoot", "DeathTime", "FallDistance", "FallFlying", "Fire", "HandDropChances", "HandItems", "HurtByTimestamp", "HurtTime", "LeftHanded", "Motion", "NoGravity", "OnGround", "PortalCooldown", "Pos", "Rotation", "Passengers", "leash", "UUID");
|
||||
CompoundTag compoundTag = new CompoundTag();
|
||||
entity.save(compoundTag);
|
||||
Objects.requireNonNull(compoundTag);
|
||||
IGNORED_BEE_TAGS.forEach(compoundTag::remove);
|
||||
Boolean hasNectar = pokemonEntity.getBrain().getMemory(CobblemonMemories.INSTANCE.getHAS_NECTAR()).orElse(false);
|
||||
cir.setReturnValue(new BeehiveBlockEntity.Occupant(CustomData.of(compoundTag), 0, hasNectar ? 2400 : 600));
|
||||
cir.cancel();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.cobblemon.mod.common.mixin.accessor.BeeDataAccessor;
|
||||
import com.cobblemon.mod.common.mixin.accessor.BeehiveBlockEntityAccessor;
|
||||
import com.cobblemon.mod.common.mixin.accessor.BlockEntityAccessor;
|
||||
import com.google.common.collect.Lists;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.entity.BeehiveBlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(BeehiveBlockEntity.class)
|
||||
public abstract class BeehiveBlockEntityMixin {
|
||||
|
||||
|
||||
// Accessor for the private 'stored' field in BeehiveBlockEntity
|
||||
@Accessor("stored")
|
||||
public abstract List<BeeDataAccessor> getStored();
|
||||
|
||||
@Inject(method = "emptyAllLivingFromHive", at = @At("HEAD"))
|
||||
private void cobblemon$emptyAllLivingFromHive(@Nullable Player player, BlockState state, BeehiveBlockEntity.BeeReleaseStatus releaseStatus, CallbackInfo ci) {
|
||||
if (player != null) {
|
||||
final BeehiveBlockEntity blockEntity = (BeehiveBlockEntity) (Object) this;
|
||||
// if the block is sedated or the player is too far away, no aggro should be applied
|
||||
if (blockEntity.isSedated() || player.position().distanceToSqr(blockEntity.getBlockPos().getCenter()) > 16.0)
|
||||
return;
|
||||
// from this point need to access private members of the block entity
|
||||
final BeehiveBlockEntityAccessor blockEntityAccessor = (BeehiveBlockEntityAccessor) this;
|
||||
List<Object> storedList = blockEntityAccessor.getStored();
|
||||
|
||||
List<Entity> list = Lists.newArrayList();
|
||||
storedList.removeIf((beeData) -> {
|
||||
BeehiveBlockEntity.Occupant occupant = ((BeeDataAccessor) beeData).invokeToOccupant();
|
||||
// Checking the for the tag allows us to make sure it's one of our pokemon before trying to rehydrate it into an entity
|
||||
boolean isPokemon = false;
|
||||
if (occupant.entityData().contains("id")) {
|
||||
isPokemon = "cobblemon:pokemon".equals(occupant.entityData().getUnsafe().getString("id"));
|
||||
}
|
||||
if (isPokemon) {
|
||||
return BeehiveBlockEntityAccessor.releaseOccupant(
|
||||
((BlockEntityAccessor) blockEntityAccessor).getLevel(),
|
||||
((BlockEntityAccessor) blockEntityAccessor).getWorldPosition(),
|
||||
state,
|
||||
occupant,
|
||||
list,
|
||||
releaseStatus,
|
||||
blockEntityAccessor.savedFlowerPos());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
);
|
||||
for (Entity entity : list) {
|
||||
if (entity instanceof PokemonEntity) {
|
||||
((PokemonEntity) entity).getBrain().setMemory(MemoryModuleType.ATTACK_TARGET, player);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Redirect(
|
||||
method = "releaseOccupant",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/level/Level;addFreshEntity(Lnet/minecraft/world/entity/Entity;)Z"
|
||||
)
|
||||
)
|
||||
private static boolean cobblemon$captureReleasedEntity(
|
||||
Level level, Entity entity,
|
||||
Level lvl, BlockPos pos, BlockState state,
|
||||
BeehiveBlockEntity.Occupant occupant, @Nullable List<Entity> bees,
|
||||
BeehiveBlockEntity.BeeReleaseStatus releaseStatus, @Nullable BlockPos flowerPos) {
|
||||
|
||||
if (bees != null && !bees.contains(entity)) {
|
||||
// Avoid duplicates if vanilla also adds bees to the list on your version
|
||||
bees.add(entity);
|
||||
}
|
||||
return level.addFreshEntity(entity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.duck.ChannelDuck;
|
||||
import com.mojang.blaze3d.audio.Channel;
|
||||
import org.lwjgl.openal.AL10;
|
||||
|
||||
@@ -8,18 +8,25 @@
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonNetwork;
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.api.orientation.OrientationController;
|
||||
import com.cobblemon.mod.common.api.pokemon.effect.ShoulderEffectRegistry;
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle;
|
||||
import com.cobblemon.mod.common.api.riding.Seat;
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.SubmarineBehaviour;
|
||||
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate;
|
||||
import com.cobblemon.mod.common.client.render.MatrixWrapper;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.cobblemon.mod.common.net.messages.server.orientation.ServerboundUpdateOrientationPacket;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.effect.MobEffectInstance;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.ClipContext;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
@@ -32,6 +39,7 @@ import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(LivingEntity.class)
|
||||
public abstract class LivingEntityMixin extends Entity implements OrientationControllable {
|
||||
@@ -55,6 +63,14 @@ public abstract class LivingEntityMixin extends Entity implements OrientationCon
|
||||
}
|
||||
}
|
||||
|
||||
@WrapOperation(method = "baseTick", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;isInWall()Z"))
|
||||
public boolean cobblemon$preventMountSuffocation(LivingEntity instance, Operation<Boolean> original) {
|
||||
if (instance.getVehicle() instanceof PokemonEntity) {
|
||||
return false;
|
||||
}
|
||||
return original.call(instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull HitResult pick(double hitDistance, float partialTicks, boolean hitFluids) {
|
||||
Entity vehicle = this.getVehicle();
|
||||
@@ -116,4 +132,31 @@ public abstract class LivingEntityMixin extends Entity implements OrientationCon
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "decreaseAirSupply", at = @At("HEAD"), cancellable = true)
|
||||
private void cobblemon$conditionalWaterBreathing(
|
||||
int currentAir,
|
||||
CallbackInfoReturnable<Integer> cir
|
||||
) {
|
||||
var entity = (LivingEntity) (Object) this;
|
||||
if (!(entity instanceof Player player)) return;
|
||||
if (!(player.getVehicle() instanceof PokemonEntity vehicle)) return;
|
||||
|
||||
var ridingController = vehicle.getRidingController();
|
||||
if (ridingController == null) return;
|
||||
|
||||
var activeContext = ridingController.getContext();
|
||||
if (activeContext == null) return;
|
||||
|
||||
// If the rider is currently on a submarine and the ride still has stamina.
|
||||
var stamina = activeContext.getState().getStamina().get();
|
||||
if (
|
||||
activeContext.getStyle() == RidingStyle.LIQUID &&
|
||||
activeContext.getBehaviour().equals(SubmarineBehaviour.Companion.getKEY()) &&
|
||||
stamina > 0.0f
|
||||
) {
|
||||
cir.setReturnValue(currentAir);
|
||||
cir.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.cobblemon.mod.common.api.storage.party.PartyStore;
|
||||
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore;
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags;
|
||||
import com.cobblemon.mod.common.duck.PlayerDuck;
|
||||
import com.cobblemon.mod.common.duck.RidePassenger;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.cobblemon.mod.common.pokedex.scanner.PokedexEntityData;
|
||||
import com.cobblemon.mod.common.pokedex.scanner.ScannableEntity;
|
||||
@@ -47,6 +48,7 @@ import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.food.FoodProperties;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Vector3f;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
@@ -63,7 +65,7 @@ import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Mixin(Player.class)
|
||||
public abstract class PlayerMixin extends LivingEntity implements ScannableEntity, OrientationControllable, PlayerDuck {
|
||||
public abstract class PlayerMixin extends LivingEntity implements ScannableEntity, OrientationControllable, PlayerDuck, RidePassenger {
|
||||
|
||||
@Unique
|
||||
private Vector3f cobblemon$driverInput;
|
||||
@@ -91,6 +93,12 @@ public abstract class PlayerMixin extends LivingEntity implements ScannableEntit
|
||||
@Shadow protected int jumpTriggerTime;
|
||||
@Unique private final OrientationController cobblemon$orientationController = new OrientationController(this);
|
||||
|
||||
@Unique private float cobblemon$rideXRot = 0.0f;
|
||||
|
||||
@Unique private float cobblemon$rideYRot = 0.0f;
|
||||
|
||||
@Unique private Vec3 cobblemon$rideEyePos = Vec3.ZERO;
|
||||
|
||||
protected PlayerMixin(EntityType<? extends LivingEntity> p_20966_, Level p_20967_) {
|
||||
super(p_20966_, p_20967_);
|
||||
}
|
||||
@@ -117,11 +125,6 @@ public abstract class PlayerMixin extends LivingEntity implements ScannableEntit
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "tick", at = @At("HEAD"))
|
||||
private void cobblemon$updateRenderOrientation(CallbackInfo ci) {
|
||||
this.cobblemon$orientationController.tick();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityDimensions getDefaultDimensions(Pose pose) {
|
||||
if (this.getVehicle() instanceof PokemonEntity) {
|
||||
@@ -307,4 +310,34 @@ public abstract class PlayerMixin extends LivingEntity implements ScannableEntit
|
||||
return cobblemon$lastSentDriverInput;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float cobblemon$getRideXRot() {
|
||||
return this.cobblemon$rideXRot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cobblemon$setRideXRot(float rideXRot) {
|
||||
this.cobblemon$rideXRot = rideXRot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float cobblemon$getRideYRot() {
|
||||
return this.cobblemon$rideYRot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cobblemon$setRideYRot(float rideYRot) {
|
||||
this.cobblemon$rideYRot = rideYRot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3 cobblemon$getRideEyePos() {
|
||||
return this.cobblemon$rideEyePos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cobblemon$setRideEyePos(Vec3 rideEyePos) {
|
||||
this.cobblemon$rideEyePos = rideEyePos;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,33 +8,70 @@
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon;
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.PlayerSpawnerAccessor;
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawner;
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawnerFactory;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules;
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(ServerPlayer.class)
|
||||
public abstract class ServerPlayerMixin {
|
||||
public abstract class ServerPlayerMixin implements PlayerSpawnerAccessor {
|
||||
@Shadow
|
||||
public abstract ServerLevel serverLevel();
|
||||
|
||||
@Unique
|
||||
public PlayerSpawner cobblemon$spawner;
|
||||
|
||||
@Override
|
||||
public PlayerSpawner getPlayerSpawner() {
|
||||
ServerPlayer player = (ServerPlayer)(Object)this;
|
||||
if (cobblemon$spawner == null) {
|
||||
cobblemon$spawner = PlayerSpawnerFactory.INSTANCE.create(player);
|
||||
}
|
||||
return cobblemon$spawner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerSpawner(PlayerSpawner spawner) {
|
||||
this.cobblemon$spawner = spawner;
|
||||
}
|
||||
|
||||
@Inject(method = "tick", at = @At("TAIL"))
|
||||
private void cobblemon$tickSpawner(CallbackInfo ci) {
|
||||
if (!Cobblemon.config.getEnableSpawning()) {
|
||||
return;
|
||||
} else if (!serverLevel().getGameRules().getBoolean(CobblemonGameRules.DO_POKEMON_SPAWNING)) {
|
||||
return;
|
||||
}
|
||||
getPlayerSpawner().tick();
|
||||
}
|
||||
|
||||
@Inject(method = "rideTick", at = @At("HEAD"))
|
||||
private void cobblemon$updateOrientationControllerRideTick(CallbackInfo ci) {
|
||||
if (!(this instanceof OrientationControllable controllable)) return;
|
||||
var shouldUseCustomOrientation = cobblemon$shouldUseCustomOrientation((ServerPlayer)(Object)this);
|
||||
controllable.getOrientationController().setActive(shouldUseCustomOrientation);
|
||||
ServerPlayer player = (ServerPlayer)(Object)this;
|
||||
Entity vehicle = player.getVehicle();
|
||||
if (!(vehicle instanceof OrientationControllable controllableVehicle)) return;
|
||||
var shouldUseCustomOrientation = cobblemon$shouldUseCustomOrientation(vehicle);
|
||||
controllableVehicle.getOrientationController().setActive(shouldUseCustomOrientation);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private boolean cobblemon$shouldUseCustomOrientation(ServerPlayer player) {
|
||||
var playerVehicle = player.getVehicle();
|
||||
if (playerVehicle == null) return false;
|
||||
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return false;
|
||||
private boolean cobblemon$shouldUseCustomOrientation(Entity entity) {
|
||||
if (entity == null) return false;
|
||||
if (!(entity instanceof PokemonEntity pokemonEntity)) return false;
|
||||
return pokemonEntity.ifRidingAvailableSupply(false, (behaviour, settings, state) -> {
|
||||
return behaviour.shouldRoll(settings, state, pokemonEntity);
|
||||
});
|
||||
@@ -42,8 +79,9 @@ public abstract class ServerPlayerMixin {
|
||||
|
||||
@Inject(method = "stopRiding", at = @At("HEAD"))
|
||||
public void cobblemon$resetOrientationOnDismount(CallbackInfo ci) {
|
||||
if (!(this instanceof OrientationControllable controllable)) return;
|
||||
controllable.getOrientationController().setActive(false);
|
||||
ServerPlayer player = (ServerPlayer)(Object)this;
|
||||
if (!(player.getVehicle() instanceof OrientationControllable controllableVehicle)) return;
|
||||
controllableVehicle.getOrientationController().setActive(false);
|
||||
}
|
||||
|
||||
//TODO: Switch to sending out on load instead of preventing saving
|
||||
@@ -51,5 +89,4 @@ public abstract class ServerPlayerMixin {
|
||||
private boolean cobblemon$cancelSavingPokemonMounts(boolean original, @Local(ordinal=0) Entity entity) {
|
||||
return original && !(entity instanceof PokemonEntity);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin.accessor;
|
||||
|
||||
|
||||
import net.minecraft.world.level.block.entity.BeehiveBlockEntity;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
@Mixin(targets = "net.minecraft.world.level.block.entity.BeehiveBlockEntity$BeeData")
|
||||
public interface BeeDataAccessor {
|
||||
@Invoker("toOccupant")
|
||||
BeehiveBlockEntity.Occupant invokeToOccupant();
|
||||
}
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin.accessor;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.entity.BeehiveBlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(BeehiveBlockEntity.class)
|
||||
public interface BeehiveBlockEntityAccessor {
|
||||
@Accessor("stored")
|
||||
List<Object> getStored();
|
||||
@Accessor("savedFlowerPos")
|
||||
BlockPos savedFlowerPos();
|
||||
|
||||
|
||||
/**
|
||||
* Access the private static method:
|
||||
* private static void releaseOccupant(Level level, BlockPos pos, BeeData beeData, ...)
|
||||
*/
|
||||
@Invoker("releaseOccupant")
|
||||
static boolean releaseOccupant(Level level, BlockPos pos, BlockState state, BeehiveBlockEntity.Occupant occupant, @Nullable List<Entity> storedInHives, BeehiveBlockEntity.BeeReleaseStatus releaseStatus, @Nullable BlockPos storedFlowerPos) {
|
||||
throw new AssertionError(); // Mixin will overwrite
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin.accessor;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(BlockEntity.class)
|
||||
public interface BlockEntityAccessor {
|
||||
@Accessor("level") Level getLevel();
|
||||
@Accessor("worldPosition") BlockPos getWorldPosition();
|
||||
}
|
||||
+9
-7
@@ -31,14 +31,16 @@ 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;
|
||||
// Only modify fov for the driver and not the passengers
|
||||
if (player != null && player.isPassenger() && player.getVehicle() instanceof PokemonEntity && player.getVehicle().getControllingPassenger() == player) {
|
||||
PokemonEntity ride = (PokemonEntity) player.getVehicle();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,14 +14,18 @@ import com.cobblemon.mod.common.api.orientation.OrientationController;
|
||||
import com.cobblemon.mod.common.api.riding.Rideable;
|
||||
import com.cobblemon.mod.common.client.MountedPokemonAnimationRenderController;
|
||||
import com.cobblemon.mod.common.client.render.camera.MountedCameraRenderer;
|
||||
import com.cobblemon.mod.common.duck.RidePassenger;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import com.mojang.blaze3d.Blaze3D;
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Quaternionf;
|
||||
import org.joml.Vector3f;
|
||||
@@ -48,45 +52,51 @@ public abstract class CameraMixin {
|
||||
|
||||
@Shadow protected abstract void setPosition(Vec3 pos);
|
||||
|
||||
@Unique private float returnTimer = 0;
|
||||
@Unique private float rollAngleStart = 0;
|
||||
@Unique Minecraft minecraft = Minecraft.getInstance();
|
||||
@Shadow private float partialTickTime;
|
||||
|
||||
// Used to help in camera rotation rendering when transitioning
|
||||
// from rolled to unrolled.
|
||||
@Unique private float cobblemon$returnTimer = 0;
|
||||
@Unique private float cobblemon$rollAngleStart = 0;
|
||||
|
||||
@Unique private double cobblemon$lastHandledRotationTime = Double.MIN_VALUE;
|
||||
@Unique private double cobblemon$frameTime = 0.0;
|
||||
|
||||
// Used to as the render rotation for the motion sick camera.
|
||||
@Unique @Nullable private Quaternionf cobblemon$smoothRotation = null;
|
||||
|
||||
|
||||
@Inject(method = "setRotation", at = @At("HEAD"), cancellable = true)
|
||||
|
||||
public void cobblemon$setRotation(float f, float g, CallbackInfo ci) {
|
||||
if (!(this.entity instanceof OrientationControllable controllable)) return;
|
||||
var controller = controllable.getOrientationController();
|
||||
if (!controller.isActive() && controller.getOrientation() != null) {
|
||||
if(this.returnTimer < 1) {
|
||||
//Rotation is taken from entity since we no longer handle mouse ourselves
|
||||
//Stops a period of time when you can't input anything.
|
||||
controller.getOrientation().set(
|
||||
new Matrix3f()
|
||||
.rotateY((float) Math.toRadians(180 - this.entity.getYRot()))
|
||||
.rotateX((float) Math.toRadians(-this.entity.getXRot()))
|
||||
);
|
||||
if(rollAngleStart == 0){
|
||||
this.returnTimer = 1;
|
||||
controller.reset();
|
||||
return;
|
||||
}
|
||||
controller.getOrientation().rotateZ((float) Math.toRadians(-rollAngleStart*(1-returnTimer)));
|
||||
cobblemon$applyRotation();
|
||||
this.returnTimer += .05F;
|
||||
ci.cancel();
|
||||
} else {
|
||||
this.returnTimer = 1;
|
||||
controller.reset();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (controller.getOrientation() == null) return;
|
||||
cobblemon$applyRotation();
|
||||
// Calculate the current frametime
|
||||
double d = Blaze3D.getTime();
|
||||
this.cobblemon$frameTime = (this.cobblemon$lastHandledRotationTime != Double.MIN_VALUE) ? d - this.cobblemon$lastHandledRotationTime : 0.0;
|
||||
this.cobblemon$lastHandledRotationTime = d;
|
||||
|
||||
this.returnTimer = 0;
|
||||
this.rollAngleStart = controller.getRoll();
|
||||
ci.cancel();
|
||||
// Do no camera rotations if the game is paused
|
||||
if(Minecraft.getInstance().isPaused()) return;
|
||||
|
||||
// Don't assume the camera to be attached to an entity. Ponder Scenes from create et.al. for example aren't.
|
||||
if (this.entity == null) return;
|
||||
var vehicle = this.entity.getVehicle();
|
||||
if (!(vehicle instanceof OrientationControllable controllableVehicle)) return;
|
||||
var instance = (Camera)(Object)this;
|
||||
|
||||
// If the current vehicle has no orientation then return and perform
|
||||
// vanilla camera rotations
|
||||
var vehicleController = controllableVehicle.getOrientationController();
|
||||
if (vehicleController.getOrientation() == null) return;
|
||||
|
||||
// If the controller has been deactivated but the orientation isn't null yet
|
||||
// then perform transition from rolled ride/camera to vanilla
|
||||
if (!vehicleController.isActive() && vehicleController.getOrientation() != null) {
|
||||
cobblemon$applyTransitionRotation(vehicleController, ci);
|
||||
} else { // Otherwise perform cobblemon specific camera rotation and set transition helper variables
|
||||
cobblemon$applyRotation();
|
||||
this.cobblemon$returnTimer = 0;
|
||||
this.cobblemon$rollAngleStart = instance.rotation().getEulerAnglesYXZ(new Vector3f()).z();
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@WrapOperation(method = "setup", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;setPosition(DDD)V"))
|
||||
@@ -114,25 +124,153 @@ public abstract class CameraMixin {
|
||||
}
|
||||
}
|
||||
|
||||
// reset the rotation values if it is now not being ridden.
|
||||
cobblemon$smoothRotation = null;
|
||||
original.call(instance, x, y, z);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void cobblemon$applyRotation(){
|
||||
if (!(this.entity instanceof OrientationControllable controllable) || controllable.getOrientationController().getOrientation() == null) return;
|
||||
var controller = controllable.getOrientationController();
|
||||
var newRotation = controller.getOrientation().normal(new Matrix3f()).getNormalizedRotation(new Quaternionf());
|
||||
if (this.minecraft.options.getCameraType().isMirrored()) {
|
||||
var driver = this.entity;
|
||||
if (!(driver.getVehicle() instanceof OrientationControllable controllableVehicle) ||
|
||||
controllableVehicle.getOrientationController().getOrientation() == null ||
|
||||
!controllableVehicle.getOrientationController().getActive()
|
||||
) return;
|
||||
|
||||
// Grab the driver and vehicle orientation controllers.
|
||||
var vehicleController = controllableVehicle.getOrientationController();
|
||||
|
||||
var isDriving = driver.getControlledVehicle() != null;
|
||||
|
||||
// Grab the ride orientation
|
||||
var newRotation = new Quaternionf();
|
||||
|
||||
// If the player is driving then use the current orientation since the driver sets it.
|
||||
// If not then use the smoothed render orientation for passengers otherwise the vehicle rotation will be jerky
|
||||
if (isDriving) {
|
||||
newRotation = vehicleController.getOrientation().normal(new Matrix3f()).getNormalizedRotation(new Quaternionf());
|
||||
} else {
|
||||
newRotation = vehicleController.getRenderOrientation(this.partialTickTime);
|
||||
}
|
||||
|
||||
// Unroll the current rotation if no roll is requested and not currently freelooking.
|
||||
if (Cobblemon.config.getDisableRoll()) {
|
||||
// Init the smooth rotation if it has not been set yet.
|
||||
if (cobblemon$smoothRotation == null) {
|
||||
cobblemon$smoothRotation = new Quaternionf().set(this.rotation);
|
||||
}
|
||||
var rideAngs = newRotation.getEulerAnglesYXZ(new Vector3f());
|
||||
var cameraAngs = cobblemon$smoothRotation.getEulerAnglesYXZ(new Vector3f());
|
||||
|
||||
// var vehicle = (PokemonEntity)driver.getVehicle();
|
||||
// var vel = vehicle.getRidingAnimationData().getVelocitySpring().getInterpolated(this.partialTickTime, 0);
|
||||
// // Grab the target yaw as the current rides yaw. If the horizontal velocity is high enough to be used to
|
||||
// // derive a yaw then use it.
|
||||
// var targetYaw = (double) rideAngs.y();
|
||||
// if ( vel.horizontalDistance() > 0.01) {
|
||||
// // lerp the new rotation towards this y rot at a rate proportional to the horizontal movement speed?
|
||||
// targetYaw = Mth.wrapDegrees(Math.toDegrees(Mth.atan2(vel.x(), vel.z())) + 180.0f);
|
||||
// }
|
||||
|
||||
var degDiff = Mth.degreesDifference((float)Math.toDegrees(cameraAngs.y()), (float)Math.toDegrees(rideAngs.y));
|
||||
var pitchDeadzoneDeg = 15.0; // x degrees of deadzone about +-90 degrees of pitch.
|
||||
var pitchDeg = vehicleController.getPitch();
|
||||
// ConsiderPitch to be already 90 if within the deadzone around it.
|
||||
var cutoffPitch = Math.min(1.0,(Mth.abs(pitchDeg) / (90.0f - pitchDeadzoneDeg))) * 90.0f * Math.signum(pitchDeg);
|
||||
var lerpRateMod = Mth.cos((float)Math.toRadians(cutoffPitch));
|
||||
|
||||
// Apply smoothing from the cameras current yaw to the yaw of the velocity vector of the ride.
|
||||
var k = 10.0f; // Some smoothing constant k
|
||||
var smoothingFactor = lerpRateMod * cobblemon$frameTime * k;
|
||||
var newYaw = cameraAngs.y() + (Math.toRadians(degDiff) * smoothingFactor);
|
||||
|
||||
var newPitch = (float)Mth.lerp(cobblemon$frameTime * k, cameraAngs.x(), rideAngs.x());
|
||||
//var newYaw = (float)Mth.lerp(smoothingFactor, cameraAngs.y(), rideAngs.y());
|
||||
var maxRoll = Math.toRadians(15.0f);
|
||||
var newRoll = (float)Mth.lerp(cobblemon$frameTime * k / 2.0, cameraAngs.z(), Math.abs(lerpRateMod) * Math.sin(rideAngs.z()) * maxRoll);
|
||||
|
||||
// Set rotations
|
||||
var smoothedRot = new Quaternionf().rotateYXZ((float)newYaw, newPitch, newRoll);
|
||||
cobblemon$smoothRotation.set(smoothedRot);
|
||||
newRotation.set(smoothedRot);
|
||||
|
||||
}
|
||||
|
||||
var rotationOffset = 0;
|
||||
if (Minecraft.getInstance().options.getCameraType().isMirrored()) {
|
||||
newRotation.rotateY((float)Math.toRadians(180));
|
||||
rotationOffset = 180;
|
||||
}
|
||||
|
||||
// Get the drivers local look angle in relation to the ride and apply it.
|
||||
if (vehicleController.getActive()) {
|
||||
var vehicleMatrix = new Matrix3f().set(newRotation);
|
||||
var driverMatrix = new Matrix3f();// Base the driver
|
||||
var playerRotater = (RidePassenger)driver;
|
||||
newRotation = vehicleMatrix.mul(
|
||||
driverMatrix.rotateYXZ(
|
||||
-Mth.DEG_TO_RAD * (playerRotater.cobblemon$getRideYRot()),
|
||||
-Mth.DEG_TO_RAD * playerRotater.cobblemon$getRideXRot(),
|
||||
0.0f)
|
||||
).normal(new Matrix3f()).getNormalizedRotation(new Quaternionf());
|
||||
}
|
||||
this.rotation.set(newRotation);
|
||||
this.xRot = controller.getPitch();
|
||||
this.yRot = controller.getYaw();
|
||||
var eulerAngs = newRotation.getEulerAnglesXYZ(new Vector3f());
|
||||
this.xRot = eulerAngs.x() * Mth.RAD_TO_DEG;
|
||||
this.yRot = eulerAngs.y() * Mth.RAD_TO_DEG;
|
||||
// Set the drivers rotations to match
|
||||
var driverEulerAngs = new Quaternionf(this.rotation).getEulerAnglesYXZ(new Vector3f());
|
||||
driver.setXRot(-driverEulerAngs.x() * Mth.RAD_TO_DEG + rotationOffset);
|
||||
driver.setYRot(180.0f - (driverEulerAngs.y() * Mth.RAD_TO_DEG));
|
||||
|
||||
OrientationController.Companion.getFORWARDS().rotate(this.rotation, this.forwards);
|
||||
OrientationController.Companion.getUP().rotate(this.rotation, this.up);
|
||||
OrientationController.Companion.getLEFT().rotate(this.rotation, this.left);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void cobblemon$applyTransitionRotation(
|
||||
OrientationController vehicleController,
|
||||
CallbackInfo ci
|
||||
) {
|
||||
// If the transition has just started then
|
||||
if (this.cobblemon$returnTimer == 0.0) {
|
||||
MountedCameraRenderer.INSTANCE.resetDriverRotations((Camera)(Object)this, this.entity);
|
||||
}
|
||||
|
||||
if(this.cobblemon$returnTimer < 1) {
|
||||
//Rotation is taken from entity since we no longer handle mouse ourselves
|
||||
//Stops a period of time when you can't input anything.
|
||||
float interpolatedRoll = Mth.lerp(this.cobblemon$returnTimer, this.cobblemon$rollAngleStart, 0.0f);
|
||||
float pitch = (float) Math.toRadians(-this.entity.getXRot());
|
||||
float yaw = (float) Math.toRadians(180 - this.entity.getYRot());
|
||||
|
||||
Quaternionf interRot = new Quaternionf();
|
||||
interRot.rotationYXZ(yaw, pitch, interpolatedRoll);
|
||||
|
||||
this.rotation.set(interRot);
|
||||
var eulerAngs = interRot.getEulerAnglesXYZ(new Vector3f());
|
||||
this.xRot = eulerAngs.x() * Mth.RAD_TO_DEG;
|
||||
this.yRot = eulerAngs.y() * Mth.RAD_TO_DEG;
|
||||
OrientationController.Companion.getFORWARDS().rotate(this.rotation, this.forwards);
|
||||
OrientationController.Companion.getUP().rotate(this.rotation, this.up);
|
||||
OrientationController.Companion.getLEFT().rotate(this.rotation, this.left);
|
||||
|
||||
if(cobblemon$rollAngleStart == 0){
|
||||
this.cobblemon$returnTimer = 1;
|
||||
vehicleController.reset();
|
||||
MountedCameraRenderer.INSTANCE.resetDriverRotations((Camera)(Object)this, this.entity);
|
||||
return;
|
||||
}
|
||||
this.cobblemon$returnTimer += (partialTickTime * (1.0f/20.0f));
|
||||
ci.cancel();
|
||||
} else {
|
||||
this.cobblemon$returnTimer = 1;
|
||||
vehicleController.reset();
|
||||
MountedCameraRenderer.INSTANCE.resetDriverRotations((Camera)(Object)this, this.entity);
|
||||
}
|
||||
}
|
||||
|
||||
@WrapOperation(method = "setup", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;move(FFF)V", ordinal = 0))
|
||||
public void stopMoveIfRolling(Camera instance, float zoom, float dy, float dx, Operation<Void> original) {
|
||||
Entity entity = instance.getEntity();
|
||||
|
||||
@@ -9,15 +9,21 @@
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.client.DeltaTracker;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.GameRenderer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@Mixin(GameRenderer.class)
|
||||
public class GameRendererMixin {
|
||||
@Unique
|
||||
@@ -42,4 +48,16 @@ public class GameRendererMixin {
|
||||
lastTime = newTime;
|
||||
}
|
||||
|
||||
@ModifyArg(
|
||||
method = "pick(Lnet/minecraft/world/entity/Entity;DDF)Lnet/minecraft/world/phys/HitResult;",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/entity/projectile/ProjectileUtil;getEntityHitResult(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/phys/AABB;Ljava/util/function/Predicate;D)Lnet/minecraft/world/phys/EntityHitResult;"
|
||||
),
|
||||
index = 4
|
||||
)
|
||||
private Predicate<Entity> filterPokemonVehicle(Predicate<Entity> original, @Local(ordinal = 0, argsOnly = true) Entity entity) {
|
||||
return (entityX) -> !(entity.getVehicle() == entityX && entityX instanceof PokemonEntity) && original.test(entityX);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+27
-15
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.client.render.player.MountedPlayerRenderer;
|
||||
import com.cobblemon.mod.common.duck.RidePassenger;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import net.minecraft.client.model.HumanoidModel;
|
||||
import net.minecraft.client.model.geom.ModelPart;
|
||||
@@ -19,20 +20,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 +38,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 +51,30 @@ 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 (entity instanceof RidePassenger playerRotater) {
|
||||
//this.head.yRot = playerRotater.cobblemon$getRideYRot();
|
||||
this.head.xRot = playerRotater.cobblemon$getRideXRot();
|
||||
headPitch = playerRotater.cobblemon$getRideXRot();
|
||||
}
|
||||
|
||||
|
||||
if (!shouldRotatePlayerHead) {
|
||||
netHeadYaw = 0f;
|
||||
@@ -76,7 +87,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,9 +19,11 @@ import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState;
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel;
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository;
|
||||
import com.cobblemon.mod.common.duck.PlayerDuck;
|
||||
import com.cobblemon.mod.common.duck.RidePassenger;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.cobblemon.mod.common.net.messages.server.orientation.ServerboundUpdateOrientationPacket;
|
||||
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateDriverInputPacket;
|
||||
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateRiderRotationPacket;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.client.CameraType;
|
||||
import net.minecraft.client.Minecraft;
|
||||
@@ -29,12 +31,10 @@ import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.client.player.Input;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.ClipContext;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -55,6 +55,10 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
|
||||
|
||||
@Unique Matrix3f cobblemon$lastOrientation;
|
||||
|
||||
@Unique float cobblemon$lastRideXRot;
|
||||
@Unique float cobblemon$lastRideYRot;
|
||||
@Unique Vec3 cobblemon$lastRideEyePos;
|
||||
|
||||
@Unique boolean cobblemon$isDoubleJumping = false;
|
||||
|
||||
@Shadow
|
||||
@@ -72,30 +76,35 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
|
||||
super(clientLevel, gameProfile);
|
||||
}
|
||||
|
||||
// @Inject(method = "sendPosition", at = @At("TAIL"))
|
||||
// private void cobblemon$updateRotationMatrix(CallbackInfo ci) {
|
||||
// if (!(this instanceof OrientationControllable controllable)) return;
|
||||
// var controller = controllable.getOrientationController();
|
||||
// if (!controller.isActive() || controller.getOrientation() == cobblemon$lastOrientation) return;
|
||||
// cobblemon$lastOrientation = controller.getOrientation() != null ? new Matrix3f(controller.getOrientation()) : null;
|
||||
// CobblemonNetwork.INSTANCE.sendToServer(new ServerboundUpdateOrientationPacket(controller.getOrientation()));
|
||||
// }
|
||||
|
||||
@Inject(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientPacketListener;send(Lnet/minecraft/network/protocol/Packet;)V"))
|
||||
private void cobblemon$updateRotationMatrixPassenger(CallbackInfo ci) {
|
||||
if (!(this instanceof OrientationControllable controllable)) return;
|
||||
var controller = controllable.getOrientationController();
|
||||
if (!controller.isActive() || controller.getOrientation() == cobblemon$lastOrientation) return;
|
||||
cobblemon$lastOrientation = controller.getOrientation() != null ? new Matrix3f(controller.getOrientation()) : null;
|
||||
CobblemonNetwork.INSTANCE.sendToServer(new ServerboundUpdateOrientationPacket(controller.getOrientation()));
|
||||
if (!(this.getVehicle() instanceof OrientationControllable controllableVehicle)) return;
|
||||
var vehicleController = controllableVehicle.getOrientationController();
|
||||
if (!vehicleController.isActive() || vehicleController.getOrientation() == cobblemon$lastOrientation) return;
|
||||
cobblemon$lastOrientation = vehicleController.getOrientation() != null ? new Matrix3f(vehicleController.getOrientation()) : null;
|
||||
CobblemonNetwork.INSTANCE.sendToServer(new ServerboundUpdateOrientationPacket(this.getVehicle().getId(), vehicleController.getOrientation()));
|
||||
}
|
||||
|
||||
@Inject(method = "rideTick", at = @At("HEAD"))
|
||||
private void cobblemon$updateOrientationControllerRideTick(CallbackInfo ci) {
|
||||
if (Minecraft.getInstance().player != (Object)this) return;
|
||||
if (!(this instanceof OrientationControllable controllable)) return;
|
||||
var driver = (LocalPlayer)(Object)this;
|
||||
var vehicle = driver.getVehicle();
|
||||
if (!(this.getVehicle() instanceof OrientationControllable controllableVehicle)) return;
|
||||
var shouldUseCustomOrientation = cobblemon$shouldUseCustomOrientation((LocalPlayer)(Object)this);
|
||||
controllable.getOrientationController().setActive(shouldUseCustomOrientation);
|
||||
var vehicleController = controllableVehicle.getOrientationController();
|
||||
|
||||
// If the player has just switched to riding a custom orientation ride then set their
|
||||
// x and y rots local to the vehicle rots(if the controller wasn't active and will now be active)
|
||||
// This ensures that on transition your camera stays in the same spot
|
||||
if (!vehicleController.isActive() && shouldUseCustomOrientation) {
|
||||
// Set local to the vehicle x and yrot so
|
||||
var playerRotater = (RidePassenger)driver;
|
||||
playerRotater.cobblemon$setRideXRot(Mth.wrapDegrees(driver.getXRot() - vehicle.getXRot()));
|
||||
playerRotater.cobblemon$setRideYRot(Mth.wrapDegrees(driver.getYRot() - vehicle.getYRot()));
|
||||
}
|
||||
|
||||
vehicleController.setActive(shouldUseCustomOrientation);
|
||||
}
|
||||
|
||||
@Inject(method = "rideTick", at = @At("HEAD"))
|
||||
@@ -115,6 +124,36 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
|
||||
CobblemonNetwork.INSTANCE.sendToServer(new ServerboundUpdateDriverInputPacket(driverInput));
|
||||
}
|
||||
|
||||
@Inject(method = "rideTick", at = @At("HEAD"))
|
||||
private void cobblemon$updateRiderRotationsRideTick(CallbackInfo ci) {
|
||||
if (Minecraft.getInstance().player != (Object)this) return;
|
||||
if (!(this.getVehicle() instanceof PokemonEntity pokemonEntity)) return;
|
||||
|
||||
var passenger = (LocalPlayer)(Object)this;
|
||||
var playerRotater = (RidePassenger)passenger;
|
||||
|
||||
// Gather Rider Rotation info
|
||||
var rideXRot = playerRotater.cobblemon$getRideXRot();
|
||||
var rideYRot = playerRotater.cobblemon$getRideYRot();
|
||||
var rideEyePos = playerRotater.cobblemon$getRideEyePos();
|
||||
|
||||
// Check for a change in the values before sending them.
|
||||
if (cobblemon$lastRideXRot != rideXRot ||
|
||||
cobblemon$lastRideYRot != rideYRot ||
|
||||
cobblemon$lastRideEyePos != rideEyePos
|
||||
) {
|
||||
|
||||
// Update 'last' values
|
||||
cobblemon$lastRideXRot = rideXRot;
|
||||
cobblemon$lastRideYRot = rideYRot;
|
||||
cobblemon$lastRideEyePos = rideEyePos;
|
||||
|
||||
CobblemonNetwork.INSTANCE.sendToServer(new ServerboundUpdateRiderRotationPacket(rideXRot, rideYRot, rideEyePos));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Unique
|
||||
private boolean cobblemon$shouldUseCustomOrientation(LocalPlayer player) {
|
||||
var playerVehicle = player.getVehicle();
|
||||
@@ -129,8 +168,8 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
|
||||
public void stopRiding() {
|
||||
super.stopRiding();
|
||||
if (Minecraft.getInstance().player != (Object)this) return;
|
||||
if (!(this instanceof OrientationControllable controllable)) return;
|
||||
controllable.getOrientationController().setActive(false);
|
||||
if (!(this instanceof OrientationControllable controllableVehicle)) return;
|
||||
controllableVehicle.getOrientationController().setActive(false);
|
||||
}
|
||||
|
||||
@Inject(method = "getJumpRidingScale", at = @At("HEAD"), cancellable = true)
|
||||
@@ -161,7 +200,7 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
|
||||
this.cobblemon$survivalJumpTriggerTime = 0;
|
||||
}
|
||||
else {
|
||||
this.cobblemon$survivalJumpTriggerTime = 7;
|
||||
this.cobblemon$survivalJumpTriggerTime = 12;
|
||||
}
|
||||
}
|
||||
else if (this.cobblemon$survivalJumpTriggerTime > 0) {
|
||||
@@ -174,37 +213,31 @@ 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();
|
||||
if (vehicle instanceof PokemonEntity pokemonEntity) {
|
||||
PokemonClientDelegate delegate = (PokemonClientDelegate) pokemonEntity.getDelegate();
|
||||
String locatorName = delegate.getSeatLocator(this);
|
||||
MatrixWrapper locator = delegate.getLocatorStates().get(locatorName);
|
||||
|
||||
if (locator == null) {
|
||||
return super.pick(hitDistance, partialTicks, hitFluids);
|
||||
}
|
||||
// Get player object
|
||||
var passenger = (LocalPlayer)(Object)this;
|
||||
var playerRotater = (RidePassenger)passenger;
|
||||
|
||||
Vec3 locatorOffset = locator != null ? new Vec3(locator.getMatrix().getTranslation(new Vector3f())) : Vec3.ZERO;
|
||||
|
||||
OrientationController controller = this.getOrientationController();
|
||||
|
||||
float currEyeHeight = this.getEyeHeight();
|
||||
Vector3f offset = new Vector3f(new Vector3f(0f, currEyeHeight - (this.getBbHeight() / 2), 0f));
|
||||
|
||||
if (Minecraft.getInstance().options.getCameraType() == CameraType.FIRST_PERSON) {
|
||||
PosableModel model = VaryingModelRepository.INSTANCE.getPoser(pokemonEntity.getPokemon().getSpecies().getResourceIdentifier(), new FloatingState());
|
||||
offset.add(cobblemon$getFirstPersonOffset(model, locatorName));
|
||||
}
|
||||
|
||||
Matrix3f orientation = controller.isActive() && controller.getOrientation() != null ? controller.getOrientation() : new Matrix3f();
|
||||
Vec3 rotatedEyeHeight = new Vec3(orientation.transform(offset));
|
||||
|
||||
Vec3 eyePosition = locatorOffset.add(pokemonEntity.position()).add(rotatedEyeHeight);
|
||||
// Gather eye position from stored 1st person camera position calculation
|
||||
var eyePosition = playerRotater.cobblemon$getRideEyePos();
|
||||
|
||||
Vec3 viewVector = this.getViewVector(partialTicks);
|
||||
Vec3 viewDistanceVector = eyePosition.add(viewVector.x * hitDistance, viewVector.y * hitDistance, viewVector.z * hitDistance);
|
||||
|
||||
return this.level()
|
||||
.clip(
|
||||
new ClipContext(
|
||||
|
||||
@@ -10,13 +10,16 @@ package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon;
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.api.orientation.OrientationController;
|
||||
import com.cobblemon.mod.common.client.CobblemonClient;
|
||||
import com.cobblemon.mod.common.client.keybind.keybinds.PartySendBinding;
|
||||
import com.cobblemon.mod.common.duck.RidePassenger;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.cobblemon.mod.common.pokedex.scanner.PokedexUsageContext;
|
||||
import com.llamalad7.mixinextras.injector.v2.WrapWithCondition;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import com.mojang.blaze3d.Blaze3D;
|
||||
import kotlin.Pair;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.MouseHandler;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
@@ -32,6 +35,9 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import static com.google.common.primitives.Floats.min;
|
||||
import static net.minecraft.util.Mth.lerp;
|
||||
|
||||
@Mixin(MouseHandler.class)
|
||||
public abstract class MouseHandlerMixin {
|
||||
@Shadow private double accumulatedScrollY;
|
||||
@@ -124,20 +130,21 @@ public abstract class MouseHandlerMixin {
|
||||
var defaultSensitivity = this.minecraft.options.sensitivity().get() * 0.6 + 0.2;
|
||||
var spyglassSensitivity = Math.pow(defaultSensitivity, 3);
|
||||
var lookSensitivity = spyglassSensitivity * 8.0;
|
||||
var sensitivity = Mth.lerp(usageContext.getFovMultiplier(), spyglassSensitivity, lookSensitivity);
|
||||
var sensitivity = lerp(usageContext.getFovMultiplier(), spyglassSensitivity, lookSensitivity);
|
||||
var yRotationFlip = this.minecraft.options.invertYMouse().get() ? -1 : 1;
|
||||
player.turn(this.accumulatedDX * sensitivity, (this.accumulatedDY * sensitivity * yRotationFlip));
|
||||
return false;
|
||||
}
|
||||
|
||||
var vehicle = player.getVehicle();
|
||||
if (!(vehicle instanceof PokemonEntity)) return true;
|
||||
|
||||
// Clamp player rotation if riding and the vehicle demands it
|
||||
if (player.isPassenger() && player.getVehicle() instanceof PokemonEntity vehicle) {
|
||||
vehicle.clampPassengerRotation(player);
|
||||
if (player.isPassenger() && vehicle instanceof PokemonEntity pokeVehicle) {
|
||||
pokeVehicle.clampPassengerRotation(player);
|
||||
}
|
||||
|
||||
if (!(player instanceof OrientationControllable controllable)) return true;
|
||||
|
||||
if (!controllable.getOrientationController().isActive()) {
|
||||
// If the ride is not orientation controllable then return and perform the normal turnPlayer logic
|
||||
if (!(vehicle instanceof OrientationControllable controllableVehicle) || !controllableVehicle.getOrientationController().isActive()) {
|
||||
xMouseSmoother.reset();
|
||||
yMouseSmoother.reset();
|
||||
pitchSmoother.reset();
|
||||
@@ -146,34 +153,74 @@ public abstract class MouseHandlerMixin {
|
||||
return true;
|
||||
}
|
||||
|
||||
//Send mouse input to be interpreted into rotation
|
||||
//deltas by the ride controller
|
||||
var vehicleController = controllableVehicle.getOrientationController();
|
||||
var lookaroundButtonPressed = Minecraft.getInstance().mouseHandler.isMiddlePressed();
|
||||
Pair<Boolean, Boolean> mouseShouldModifyDriver = ((PokemonEntity) vehicle).ifRidingAvailableSupply(new Pair(false, false), (behaviour, settings, state) -> {
|
||||
return behaviour.mouseModifiesDriverRotation(
|
||||
settings,
|
||||
state,
|
||||
(PokemonEntity) vehicle
|
||||
);
|
||||
});
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// If looking around then modify the players orientation instead and zero the mouse deltas so the mouse doesn't
|
||||
// affect the vehicle orientation when looking around.
|
||||
// If the client is a passenger it will modify these rotations and return
|
||||
//-------------------------------------------------------------------------
|
||||
if (lookaroundButtonPressed || player.getControlledVehicle() == null) {
|
||||
// Modify driver rotations
|
||||
cobblemon$setDriverRotations(player, cursorDeltaX, cursorDeltaY);
|
||||
|
||||
// If the player/passenger is not the driver then return and ignore all vehicle rotation logic
|
||||
if (player.getControlledVehicle() == null) return false;
|
||||
|
||||
// Set to zero so that vehicle rotations don't also get affected by movement.
|
||||
cursorDeltaX = 0.0;
|
||||
cursorDeltaY = 0.0;
|
||||
} else if (mouseShouldModifyDriver.getFirst()) { // If not freelooking then check modification per mouse input dimension
|
||||
cobblemon$setDriverRotations(player, cursorDeltaX, 0.0);
|
||||
cursorDeltaX = 0.0;
|
||||
} else if (mouseShouldModifyDriver.getSecond()) {
|
||||
cobblemon$setDriverRotations(player, 0.0, cursorDeltaY);
|
||||
cursorDeltaY = 0.0;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Smoothly return driver rotations to zero when needed
|
||||
//-------------------------------------------------------------------------
|
||||
var playerRotater = (RidePassenger)player;
|
||||
if( !lookaroundButtonPressed && !mouseShouldModifyDriver.getFirst()){
|
||||
playerRotater.cobblemon$setRideYRot(lerp(min(5.0f * (float)movementTime, 1.0f), Mth.wrapDegrees(playerRotater.cobblemon$getRideYRot()), 0.0f));
|
||||
}
|
||||
if( !lookaroundButtonPressed && !mouseShouldModifyDriver.getSecond()) {
|
||||
playerRotater.cobblemon$setRideXRot(lerp(min(5.0f * (float)movementTime, 1.0f), Mth.wrapDegrees(playerRotater.cobblemon$getRideXRot()), 0.0f));
|
||||
}
|
||||
|
||||
// Send mouse input to be interpreted into rotation deltas by the ride controller
|
||||
Vec3 angVecMouse = cobblemon$getRideMouseRotation(cursorDeltaX, cursorDeltaY, movementTime);
|
||||
|
||||
//Perform Rotation using mouse influenced rotation deltas.
|
||||
controllable.getOrientationController().rotate(
|
||||
// Perform Rotation using mouse influenced rotation deltas.
|
||||
vehicleController.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.
|
||||
if(cobblemon$shouldUseAngVelSmoothing()) {
|
||||
var yaw = yawSmoother.getNewDeltaValue(angRot.x * 0.5f, d);
|
||||
var pitch = pitchSmoother.getNewDeltaValue(angRot.y * 0.5f, d);
|
||||
var roll = rollSmoother.getNewDeltaValue(angRot.z * 0.5f, d);
|
||||
controllable.getOrientationController().rotate((float) yaw, (float) pitch, (float) roll);
|
||||
vehicleController.rotate((float) yaw, (float) pitch, (float) roll);
|
||||
}
|
||||
//Otherwise simply apply the smoothing
|
||||
else
|
||||
{
|
||||
controllable.getOrientationController().rotate((float) (angRot.x * 10 * d), (float) (angRot.y * 10 * d), (float) (angRot.z * 10 * d));
|
||||
else {
|
||||
vehicleController.rotate((float) (angRot.x * 10 * d), (float) (angRot.y * 10 * d), (float) (angRot.z * 10 * d));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -186,10 +233,11 @@ public abstract class MouseHandlerMixin {
|
||||
@Inject(method = "handleAccumulatedMovement", at = @At("TAIL"))
|
||||
private void cobblemon$maintainMovementWhenInScreens(CallbackInfo ci) {
|
||||
if (minecraft.player == null) return;
|
||||
if (!(minecraft.player instanceof OrientationControllable controllable)) return;
|
||||
if (!controllable.getOrientationController().isActive()) return;
|
||||
if (!(minecraft.player.getVehicle() instanceof OrientationControllable vehicleControllable)) return;
|
||||
if (!(vehicleControllable.getOrientationController() instanceof OrientationController vehicleController)) return;
|
||||
if (vehicleController.getOrientation() == null) return;
|
||||
if (minecraft.isPaused()) return;
|
||||
if (isMouseGrabbed()) return;
|
||||
if (this.isMouseGrabbed()) return; // return if turnPlayer() has already run this pass
|
||||
|
||||
this.turnPlayer(cobblemon$timeDelta);
|
||||
}
|
||||
@@ -251,6 +299,20 @@ public abstract class MouseHandlerMixin {
|
||||
return Math.pow(sensitivity, 3);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void cobblemon$setDriverRotations(LocalPlayer player, double cursorDeltaX, double cursorDeltaY) {
|
||||
// the vanilla turn() method redone so that it uses rideRotations.
|
||||
float f = (float)cursorDeltaY * 0.15F; // Note: turn() uses xRot for pitch (up/down)
|
||||
float g = (float)cursorDeltaX * 0.15F; // Note: turn() uses yRot for yaw (left/right)
|
||||
var playerRotater = (RidePassenger)player;
|
||||
playerRotater.cobblemon$setRideXRot(playerRotater.cobblemon$getRideXRot() + f);
|
||||
playerRotater.cobblemon$setRideYRot(playerRotater.cobblemon$getRideYRot() + g);
|
||||
playerRotater.cobblemon$setRideXRot(Mth.clamp(playerRotater.cobblemon$getRideXRot(), -90.0F, 90.0F));
|
||||
if (player.getVehicle() != null) {
|
||||
player.getVehicle().onPassengerTurned(player);
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private boolean cobblemon$shouldUseAngVelSmoothing() {
|
||||
var player = minecraft.player;
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon;
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import net.minecraft.client.CameraType;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.Options;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(Options.class)
|
||||
public abstract class OptionsMixin {
|
||||
|
||||
/**
|
||||
* Only allow third person on rollables when motion sick cam is enabled
|
||||
*/
|
||||
@Inject(
|
||||
method = "getCameraType",
|
||||
at = @At("HEAD"),
|
||||
cancellable = true
|
||||
)
|
||||
private void cobblemon$forceThirdPerson(CallbackInfoReturnable<CameraType> cir) {
|
||||
var mc = Minecraft.getInstance();
|
||||
if(!(mc.player instanceof Player player)) return;
|
||||
if(!(player.getVehicle() instanceof OrientationControllable vehicle)) return;
|
||||
if(vehicle.getOrientationController() == null) return;
|
||||
|
||||
// Force third person if on an active rollable vehicle and roll is disabled.
|
||||
if (Cobblemon.config.getDisableRoll() && vehicle.getOrientationController().isActive()) {
|
||||
cir.setReturnValue(CameraType.THIRD_PERSON_BACK);
|
||||
cir.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
+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;
|
||||
}
|
||||
|
||||
@@ -8,17 +8,24 @@
|
||||
|
||||
package com.cobblemon.mod.common.mixin.entity;
|
||||
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.duck.RidePassenger;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(Entity.class)
|
||||
public abstract class EntityMixin {
|
||||
@@ -39,13 +46,57 @@ 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);
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "getEyePosition(F)Lnet/minecraft/world/phys/Vec3;",
|
||||
at = @At("HEAD"),
|
||||
cancellable = true
|
||||
)
|
||||
private void cobblemon$modifyEyePosition_partial(float partialTicks, CallbackInfoReturnable<Vec3> cir) {
|
||||
var entity = (Entity)(Object)this;
|
||||
if (!(entity instanceof Player player)) return;
|
||||
if (!(player instanceof RidePassenger ridePassenger)) return;
|
||||
if (!(player.getVehicle() instanceof OrientationControllable vehicle)) return;
|
||||
var vehicleController = vehicle.getOrientationController();
|
||||
if (vehicleController == null) return;
|
||||
|
||||
|
||||
// TODO: Determine if it needs partialtick involved.
|
||||
Vec3 customEyePos = ridePassenger.cobblemon$getRideEyePos();
|
||||
cir.setReturnValue(customEyePos);
|
||||
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "getEyePosition()Lnet/minecraft/world/phys/Vec3;",
|
||||
at = @At("HEAD"),
|
||||
cancellable = true
|
||||
)
|
||||
private void cobblemon$modifyEyePosition_noPartial(CallbackInfoReturnable<Vec3> cir) {
|
||||
var entity = (Entity)(Object)this;
|
||||
if (!(entity instanceof Player player)) return;
|
||||
if (!(player instanceof RidePassenger ridePassenger)) return;
|
||||
if (!(player.getVehicle() instanceof OrientationControllable vehicle)) return;
|
||||
var vehicleController = vehicle.getOrientationController();
|
||||
if (vehicleController == null) return;
|
||||
|
||||
// TODO: Determine if it needs partialtick involved.
|
||||
Vec3 customEyePos = ridePassenger.cobblemon$getRideEyePos();
|
||||
cir.setReturnValue(customEyePos);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -83,6 +83,7 @@ import com.cobblemon.mod.common.net.messages.client.settings.ServerSettingsPacke
|
||||
import com.cobblemon.mod.common.permission.LaxPermissionValidator
|
||||
import com.cobblemon.mod.common.platform.events.PlatformEvents
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.aspects.CHARACTERISTIC_RAINBOW_ASPECT
|
||||
import com.cobblemon.mod.common.pokemon.aspects.COSMETIC_SLOT_ASPECT
|
||||
import com.cobblemon.mod.common.pokemon.aspects.GENDER_ASPECT
|
||||
import com.cobblemon.mod.common.pokemon.aspects.SHINY_ASPECT
|
||||
@@ -293,6 +294,7 @@ object Cobblemon {
|
||||
SHINY_ASPECT.register()
|
||||
GENDER_ASPECT.register()
|
||||
COSMETIC_SLOT_ASPECT.register()
|
||||
CHARACTERISTIC_RAINBOW_ASPECT.register()
|
||||
|
||||
SpeciesFeatures.types["choice"] = ChoiceSpeciesFeatureProvider::class.java
|
||||
SpeciesFeatures.types["flag"] = FlagSpeciesFeatureProvider::class.java
|
||||
|
||||
@@ -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
|
||||
@@ -593,6 +593,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
val CORNN_BERRY = this.berryBlock("cornn")
|
||||
val CUSTAP_BERRY = this.berryBlock("custap")
|
||||
val DURIN_BERRY = this.berryBlock("durin")
|
||||
val EGGANT_BERRY = this.berryBlock("eggant")
|
||||
val ENIGMA_BERRY = this.berryBlock("enigma")
|
||||
val FIGY_BERRY = this.berryBlock("figy")
|
||||
val GANLON_BERRY = this.berryBlock("ganlon")
|
||||
|
||||
@@ -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)
|
||||
@@ -51,6 +51,11 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
|
||||
.networkSynchronized(FlavourComponent.PACKET_CODEC)
|
||||
.build())
|
||||
|
||||
val RIDE_BOOST: DataComponentType<RideBoostsComponent> = create("ride_boosts", DataComponentType.builder<RideBoostsComponent>()
|
||||
.persistent(RideBoostsComponent.CODEC)
|
||||
.networkSynchronized(RideBoostsComponent.PACKET_CODEC)
|
||||
.build())
|
||||
|
||||
val FOOD_COLOUR: DataComponentType<FoodColourComponent> = create("food_colour", DataComponentType.builder<FoodColourComponent>()
|
||||
.persistent(FoodColourComponent.CODEC)
|
||||
.networkSynchronized(FoodColourComponent.PACKET_CODEC)
|
||||
@@ -82,10 +87,12 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:cooking_pot_item"), POT_DATA)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:bait_effects"), BAIT_EFFECTS)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:flavour"), FLAVOUR)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:ride_boosts"), RIDE_BOOST)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:food_colour"), FOOD_COLOUR)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:ingredient"), INGREDIENT)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:food"), FOOD)
|
||||
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
|
||||
@@ -35,7 +36,7 @@ import com.cobblemon.mod.common.item.berry.PPRestoringBerryItem
|
||||
import com.cobblemon.mod.common.item.berry.PortionHealingBerryItem
|
||||
import com.cobblemon.mod.common.item.berry.StatusCuringBerryItem
|
||||
import com.cobblemon.mod.common.item.berry.VolatileCuringBerryItem
|
||||
import com.cobblemon.mod.common.item.food.PonigiriItem
|
||||
import com.cobblemon.mod.common.item.interactive.PonigiriItem
|
||||
import com.cobblemon.mod.common.item.food.SinisterTeaItem
|
||||
import com.cobblemon.mod.common.item.interactive.*
|
||||
import com.cobblemon.mod.common.item.interactive.ability.AbilityChangeItem
|
||||
@@ -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)))
|
||||
@@ -516,6 +517,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField val RAWST_BERRY = berryItem("rawst", StatusCuringBerryItem(CobblemonBlocks.RAWST_BERRY, Statuses.BURN))
|
||||
@JvmField val ASPEAR_BERRY = berryItem("aspear", StatusCuringBerryItem(CobblemonBlocks.ASPEAR_BERRY, Statuses.FROZEN))
|
||||
@JvmField val PERSIM_BERRY = berryItem("persim", VolatileCuringBerryItem(CobblemonBlocks.PERSIM_BERRY, "confusion"))
|
||||
@JvmField val EGGANT_BERRY = berryItem("eggant", VolatileCuringBerryItem(CobblemonBlocks.EGGANT_BERRY, "attract"))
|
||||
@JvmField val RAZZ_BERRY = berryItem("razz", CobblemonBlocks.RAZZ_BERRY)
|
||||
@JvmField val BLUK_BERRY = berryItem("bluk", CobblemonBlocks.BLUK_BERRY)
|
||||
@JvmField val NANAB_BERRY = berryItem("nanab", CobblemonBlocks.NANAB_BERRY)
|
||||
@@ -695,7 +697,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val SUPERB_REMEDY = compostableItem("superb_remedy", RemedyItem(RemedyItem.SUPERB), 0.85f)
|
||||
@JvmField
|
||||
val MOOMOO_MILK = create("moomoo_milk", CobblemonItem(Item.Properties().craftRemainder(Items.GLASS_BOTTLE).stacksTo((16))))
|
||||
val MOOMOO_MILK = create("moomoo_milk", MoomooMilk(Item.Properties().craftRemainder(Items.GLASS_BOTTLE).stacksTo((16))))
|
||||
|
||||
@JvmField
|
||||
val POTION = create("potion", PotionItem(PotionType.POTION))
|
||||
@@ -1500,8 +1502,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 +1650,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()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.api.data.DataRegistry
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.mechanics.AprijuicesMechanic
|
||||
import com.cobblemon.mod.common.mechanics.BerriesMechanic
|
||||
import com.cobblemon.mod.common.mechanics.PotionsMechanic
|
||||
import com.cobblemon.mod.common.mechanics.RemediesMechanic
|
||||
@@ -37,12 +38,15 @@ object CobblemonMechanics : DataRegistry {
|
||||
var remedies = RemediesMechanic()
|
||||
var berries = BerriesMechanic()
|
||||
var potions = PotionsMechanic()
|
||||
var aprijuices = AprijuicesMechanic()
|
||||
|
||||
override fun sync(player: ServerPlayer) {}
|
||||
|
||||
override fun reload(manager: ResourceManager) {
|
||||
remedies = loadMechanic(manager, "remedies", RemediesMechanic::class.java)
|
||||
berries = loadMechanic(manager, "berries", BerriesMechanic::class.java)
|
||||
potions = loadMechanic(manager, "potions", PotionsMechanic::class.java)
|
||||
aprijuices = loadMechanic(manager, "aprijuices", AprijuicesMechanic::class.java)
|
||||
}
|
||||
|
||||
private fun <T> loadMechanic(manager: ResourceManager, name: String, clazz: Class<T>): T {
|
||||
|
||||
@@ -15,12 +15,12 @@ import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.ListCodec
|
||||
import com.mojang.serialization.codecs.PrimitiveCodec
|
||||
import net.minecraft.core.UUIDUtil
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import java.util.Optional
|
||||
import java.util.UUID
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.GlobalPos
|
||||
import net.minecraft.core.UUIDUtil
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import java.util.*
|
||||
|
||||
object CobblemonMemories {
|
||||
val memories = mutableMapOf<String, MemoryModuleType<*>>()
|
||||
@@ -48,10 +48,13 @@ object CobblemonMemories {
|
||||
val TIME_TRYING_TO_REACH_BERRY_BUSH = register<Int>("time_trying_to_reach_berry_bush")
|
||||
val IS_CONSUMING_ITEM = register<Boolean>("is_consuming_item")
|
||||
val RECENTLY_ATE_GRASS = register<Boolean>("recently_ate_grass")
|
||||
val HIVE_LOCATION = register<BlockPos>("hive_location")
|
||||
val HIVE_LOCATION = register<BlockPos>("hive_location", BlockPos.CODEC)
|
||||
val HIVE_BLACKLIST = register<List<BlockPos>>("hive_blacklist")
|
||||
val HIVE_COOLDOWN = register<Boolean>("hive_cooldown")
|
||||
val NEARBY_FLOWERS = register<List<BlockPos>>("nearby_flower")
|
||||
val POLLINATED = register<Boolean>("pollinated", PrimitiveCodec.BOOL)
|
||||
val NEARBY_FLOWER = register<BlockPos>("nearby_flower", BlockPos.CODEC)
|
||||
val PATH_TO_NEARBY_FLOWER_COOLDOWN = register<Boolean>("path_to_nearby_flower_cooldown")
|
||||
val HAS_NECTAR = register<Boolean>("has_nectar", PrimitiveCodec.BOOL)
|
||||
val NEARBY_SACC_LEAVES = register<BlockPos>("nearby_sacc_leaves", BlockPos.CODEC)
|
||||
/** who am i following rn? */
|
||||
val HERD_LEADER = register<String>("herd_leader", PrimitiveCodec.STRING)
|
||||
/** how many are following me rn? */
|
||||
|
||||
@@ -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
|
||||
@@ -28,12 +28,13 @@ import com.cobblemon.mod.common.client.net.effect.RunPosableMoLangHandler
|
||||
import com.cobblemon.mod.common.client.net.effect.SaccharineLogBlockParticlesHandler
|
||||
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormEntityParticleHandler
|
||||
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormParticleHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.ExpGainedDataPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.InteractPokemonUIPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.PokedexUIPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.SummaryUIPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.npc.CloseNPCEditorHandler
|
||||
import com.cobblemon.mod.common.client.net.npc.OpenNPCEditorHandler
|
||||
import com.cobblemon.mod.common.client.net.orientation.S2CUpdateOrientationHandler
|
||||
import com.cobblemon.mod.common.client.net.orientation.ClientboundUpdateOrientationHandler
|
||||
import com.cobblemon.mod.common.client.net.pasture.ClosePastureHandler
|
||||
import com.cobblemon.mod.common.client.net.pasture.OpenPastureHandler
|
||||
import com.cobblemon.mod.common.client.net.pasture.PokemonPasturedHandler
|
||||
@@ -65,8 +66,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
|
||||
@@ -132,6 +133,7 @@ import com.cobblemon.mod.common.net.messages.client.trade.TradeOfferNotification
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeProcessStartedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeStartedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeUpdatedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.ExpGainedDataPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.InteractPokemonUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.PokedexUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.SummaryUIPacket
|
||||
@@ -157,6 +159,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,14 +173,15 @@ 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
|
||||
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateRiderRotationPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.starter.RequestStarterScreenPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.SwapPCPartyPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.party.MovePartyPokemonPacket
|
||||
@@ -208,13 +212,14 @@ 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
|
||||
import com.cobblemon.mod.common.net.serverhandling.dialogue.InputToDialogueHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.evolution.AcceptEvolutionHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.npc.SaveNPCHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.orientation.OrientationPacketHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.orientation.ServerboundUpdateOrientationHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pasture.PasturePokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pasture.SetPastureConflictHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pasture.UnpastureAllPokemonHandler
|
||||
@@ -222,13 +227,14 @@ 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.riding.ServerboundUpdateRiderRotationHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.starter.RequestStarterScreenHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.starter.SelectStarterPacketHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.BenchMoveHandler
|
||||
@@ -310,6 +316,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()))
|
||||
@@ -342,6 +349,7 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(InteractPokemonUIPacket.ID, InteractPokemonUIPacket::decode, InteractPokemonUIPacketHandler))
|
||||
list.add(PacketRegisterInfo(PlayerInteractOptionsPacket.ID, PlayerInteractOptionsPacket::decode, PlayerInteractOptionsHandler))
|
||||
list.add(PacketRegisterInfo(PokedexUIPacket.ID, PokedexUIPacket::decode, PokedexUIPacketHandler))
|
||||
list.add(PacketRegisterInfo(ExpGainedDataPacket.ID, ExpGainedDataPacket::decode, ExpGainedDataPacketHandler))
|
||||
|
||||
// Starter packets
|
||||
list.add(PacketRegisterInfo(OpenStarterUIPacket.ID, OpenStarterUIPacket::decode, StarterUIPacketHandler))
|
||||
@@ -388,7 +396,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 +412,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))
|
||||
@@ -439,7 +447,7 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(UpdatePastureConflictFlagPacket.ID, UpdatePastureConflictFlagPacket::decode, UpdatePastureConflictFlagHandler))
|
||||
|
||||
// Orientation
|
||||
list.add(PacketRegisterInfo(ClientboundUpdateOrientationPacket.ID, ClientboundUpdateOrientationPacket::decode, S2CUpdateOrientationHandler))
|
||||
list.add(PacketRegisterInfo(ClientboundUpdateOrientationPacket.ID, ClientboundUpdateOrientationPacket::decode, ClientboundUpdateOrientationHandler))
|
||||
|
||||
// Behaviours
|
||||
list.add(PacketRegisterInfo(PlayPosableAnimationPacket.ID, PlayPosableAnimationPacket::decode, PlayPosableAnimationHandler))
|
||||
@@ -574,12 +582,14 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(SaveNPCPacket.ID, SaveNPCPacket::decode, SaveNPCHandler))
|
||||
|
||||
// Riding packet(s)
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateOrientationPacket.ID, ServerboundUpdateOrientationPacket::decode, OrientationPacketHandler))
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateOrientationPacket.ID, ServerboundUpdateOrientationPacket::decode, ServerboundUpdateOrientationHandler))
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateRidingStatePacket.ID, ServerboundUpdateRidingStatePacket::decode, ServerboundUpdateRidingStateHandler))
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateRidingStatsPacket.ID, ServerboundUpdateRidingStatsPacket::decode, ServerboundUpdateRidingStatsHandler))
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateRidingStatRangePacket.ID, ServerboundUpdateRidingStatRangePacket::decode, ServerboundUpdateRidingStatRangeHandler))
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateRidingSettingsPacket.ID, ServerboundUpdateRidingSettingsPacket::decode, ServerboundUpdateRidingSettingsHandler))
|
||||
list.add(PacketRegisterInfo(DismountPokemonPacket.ID, DismountPokemonPacket::decode, DismountPokemonPacketHandler))
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateDriverInputPacket.ID, ServerboundUpdateDriverInputPacket::decode, DriverInputPacketHandler))
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateRiderRotationPacket.ID, ServerboundUpdateRiderRotationPacket::decode, ServerboundUpdateRiderRotationHandler))
|
||||
|
||||
// Cooking
|
||||
list.add(PacketRegisterInfo(ToggleCookingPotLidPacket.ID, ToggleCookingPotLidPacket::decode, ToggleCookingPotLidHandler))
|
||||
|
||||
@@ -26,8 +26,6 @@ object CobblemonPoiTypes: PlatformRegistry<Registry<PoiType>, ResourceKey<Regist
|
||||
@JvmField
|
||||
val NURSE_KEY = createKey("nurse", CobblemonBlocks.HEALING_MACHINE, 1, 1)
|
||||
@JvmField
|
||||
val POKE_SNACK_KEY = createKey("poke_snack", CobblemonBlocks.POKE_SNACK, 0, 1)
|
||||
@JvmField
|
||||
val SACCHARINE_LOG_SLATHERED_KEY = createKey("saccharine_log_slathered", CobblemonBlocks.SACCHARINE_LOG_SLATHERED, 0, 1)
|
||||
// TODO after 1.7
|
||||
// @JvmField
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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.api.riding.behaviour.types.liquid.SubmarineSettings
|
||||
import com.cobblemon.mod.common.net.messages.client.data.RideSettingsSyncPacket
|
||||
import com.cobblemon.mod.common.util.adapters.ExpressionAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.ExpressionLikeAdapter
|
||||
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()
|
||||
var submarine = SubmarineSettings()
|
||||
|
||||
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,
|
||||
submarine = submarine
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
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)
|
||||
submarine = loadStyle(manager, "submarine", SubmarineSettings::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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,8 @@ object CobblemonSensors {
|
||||
|
||||
val POKEMON_NEARBY_WANTED_ITEM = register("pokemon_nearby_wanted_item", ::PokemonItemSensor)
|
||||
|
||||
val NEARBY_SACC_LEAVES = register("nearby_sacc_leaves", ::SacLeavesSensor)
|
||||
|
||||
fun <E : Entity, U : Sensor<E>> register(id: String, supplier: Supplier<U>): SensorType<U> {
|
||||
val sensor = SensorType(supplier)
|
||||
sensors[id] = sensor
|
||||
|
||||
@@ -103,6 +103,11 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
@JvmField
|
||||
val EVOLUTION = this.create("evolution.full")
|
||||
|
||||
@JvmField
|
||||
val LEVELUP_START = this.create("gui.levelup_start")
|
||||
@JvmField
|
||||
val LEVELUP = this.create("gui.levelup")
|
||||
|
||||
@JvmField
|
||||
val PVN_BATTLE = this.create("battle.pvn.default")
|
||||
@JvmField
|
||||
|
||||
+5
-4
@@ -22,18 +22,19 @@ class ReelInPokemonContext(val species : ResourceLocation, val baitId: ResourceL
|
||||
class ReelInPokemonCriterionCondition(
|
||||
playerCtx: Optional<ContextAwarePredicate>,
|
||||
val species: String,
|
||||
val baitId: String
|
||||
val baitId: String // also allows "cobblemon:empty_bait" to indicate that no bait is allowed for this criterion to match
|
||||
) : SimpleCriterionCondition<ReelInPokemonContext>(playerCtx) {
|
||||
|
||||
companion object {
|
||||
val CODEC: Codec<ReelInPokemonCriterionCondition> = RecordCodecBuilder.create { it.group(
|
||||
EntityPredicate.ADVANCEMENT_CODEC.optionalFieldOf("player").forGetter { it.playerCtx },
|
||||
Codec.STRING.optionalFieldOf("species", "any").forGetter { it.species },
|
||||
Codec.STRING.optionalFieldOf("baitId", "empty_bait").forGetter { it.baitId }
|
||||
).apply(it, { playerCtx, pokemonId, baitId -> ReelInPokemonCriterionCondition(playerCtx, pokemonId, baitId.ifEmpty { "empty_bait" }) }) }
|
||||
Codec.STRING.optionalFieldOf("baitId", "any").forGetter { it.baitId }
|
||||
).apply(it, { playerCtx, pokemonId, baitId -> ReelInPokemonCriterionCondition(playerCtx, pokemonId, baitId) }) }
|
||||
}
|
||||
|
||||
override fun matches(player: ServerPlayer, context: ReelInPokemonContext): Boolean {
|
||||
return (context.species == this.species.asIdentifierDefaultingNamespace() || this.species == "any") && (context.baitId == this.baitId.asIdentifierDefaultingNamespace() || this.baitId == "empty_bait")
|
||||
return (context.species == this.species.asIdentifierDefaultingNamespace() || this.species == "any")
|
||||
&& (context.baitId == this.baitId.asIdentifierDefaultingNamespace() || this.baitId == "any")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.ai
|
||||
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.Behavior
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.MemoryStatus
|
||||
import net.minecraft.world.entity.schedule.Activity
|
||||
|
||||
/**
|
||||
* A [Behavior] subclass that represents tasks that check that the entity is still in one
|
||||
* of the activities that this task is part of. If the entity is no longer doing any of those
|
||||
* activities, the task will be interrupted and you can handle any cleanup in [onInterrupted].
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since October 17th, 2025
|
||||
*/
|
||||
abstract class ActivityAwareBehavior<E : LivingEntity> : Behavior<E> {
|
||||
constructor(entryCondition: Map<MemoryModuleType<*>, MemoryStatus>) : super(entryCondition)
|
||||
constructor(entryCondition: Map<MemoryModuleType<*>, MemoryStatus>, duration: Int) : super(entryCondition, duration)
|
||||
constructor(entryCondition: Map<MemoryModuleType<*>, MemoryStatus>, minDuration: Int, maxDuration: Int) : super(entryCondition, minDuration, maxDuration)
|
||||
|
||||
val activities: MutableSet<Activity> = mutableSetOf()
|
||||
|
||||
fun isStillDoingActivity(entity: E): Boolean {
|
||||
return activities.any(entity.brain::isActive)
|
||||
}
|
||||
|
||||
override fun canStillUse(level: ServerLevel, entity: E, gameTime: Long) = isStillDoingActivity(entity)
|
||||
|
||||
open fun onInterrupted(level: ServerLevel, entity: E, gameTime: Long) {
|
||||
// No-op
|
||||
}
|
||||
|
||||
override fun stop(level: ServerLevel, entity: E, gameTime: Long) {
|
||||
super.stop(level, entity, gameTime)
|
||||
if (!isStillDoingActivity(entity)) {
|
||||
onInterrupted(level, entity, gameTime)
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
-1
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.ai
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.mojang.datafixers.util.Pair
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.Mob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.schedule.Activity
|
||||
|
||||
@@ -23,6 +22,13 @@ class ActivityConfigurationContext(val activity: Activity) {
|
||||
}
|
||||
|
||||
fun apply(entity: LivingEntity) {
|
||||
tasks.forEach {
|
||||
val activityAwareBehavior = it.second as? ActivityAwareBehavior<*>
|
||||
?: (it.second as? WrapperLivingEntityTask<*>)?.task as? ActivityAwareBehavior
|
||||
?: return@forEach
|
||||
activityAwareBehavior.activities += activity
|
||||
}
|
||||
|
||||
entity.brain.addActivity(activity, ImmutableList.copyOf(tasks))
|
||||
}
|
||||
}
|
||||
@@ -47,12 +47,23 @@ class ApplyBehaviours : BehaviourConfig {
|
||||
override fun configure(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) {
|
||||
if (!checkCondition(behaviourConfigurationContext, condition)) return
|
||||
|
||||
val configurations = behaviours.map { CobblemonBehaviours.behaviours[it]?.takeIf { it.canBeApplied(entity) } ?: return Cobblemon.LOGGER.warn("Behaviour $it not found") }
|
||||
val applicableBehaviours = behaviours.mapNotNull {
|
||||
val resolvedBehaviour = CobblemonBehaviours.behaviours[it]
|
||||
if (resolvedBehaviour == null) {
|
||||
Cobblemon.LOGGER.warn("Behaviour $it not found while configuring entity of type ${entity.type}")
|
||||
} else if (!resolvedBehaviour.canBeApplied(entity)) {
|
||||
Cobblemon.LOGGER.warn("Behaviour $it cannot be applied to entity ${entity.id}")
|
||||
} else {
|
||||
return@mapNotNull resolvedBehaviour
|
||||
}
|
||||
null
|
||||
}
|
||||
|
||||
// Why not just add the presets to the context directly?
|
||||
// Nested preset application is a thing, and I only want to track the top level presets.
|
||||
// i.e. if a preset applies another preset, I don't want to track the inner preset, since it's redundant if we track the top one.
|
||||
val originalContextBehaviours = behaviourConfigurationContext.appliedBehaviours.toMutableSet()
|
||||
configurations.forEach {
|
||||
applicableBehaviours.forEach {
|
||||
it.configure(entity, behaviourConfigurationContext)
|
||||
}
|
||||
originalContextBehaviours.addAll(behaviours)
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ import net.minecraft.world.entity.schedule.Activity
|
||||
|
||||
class ActivityChangeTaskConfig : SingleTaskConfig {
|
||||
val activity: Activity = Activity.IDLE
|
||||
val condition: ExpressionLike = "false".asExpressionLike()
|
||||
val condition: ExpressionLike = "true".asExpressionLike()
|
||||
|
||||
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext): List<MoLangConfigVariable> = emptyList()
|
||||
|
||||
|
||||
+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
|
||||
|
||||
+2
-1
@@ -17,6 +17,7 @@ import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.getMemorySafely
|
||||
import com.cobblemon.mod.common.util.resolveString
|
||||
import com.mojang.datafixers.util.Either
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
@@ -62,7 +63,7 @@ class MemoryAspectTaskConfig : SingleTaskConfig {
|
||||
override fun canStillUse(level: ServerLevel, entity: LivingEntity, gameTime: Long) = true
|
||||
|
||||
override fun tick(level: ServerLevel, owner: LivingEntity, gameTime: Long) {
|
||||
val hasMemory = !owner.brain.hasMemoryValue(memory)
|
||||
val hasMemory = owner.brain.hasMemoryValue(memory)
|
||||
if (owner is PokemonEntity) {
|
||||
if (!hasMemory && aspect in owner.pokemon.forcedAspects) {
|
||||
owner.pokemon.forcedAspects -= aspect
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ class MoveToOwnerTaskConfig : SingleTaskConfig {
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
behaviourConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
): BehaviorControl<in LivingEntity> {
|
||||
behaviourConfigurationContext.addMemories(
|
||||
MemoryModuleType.WALK_TARGET,
|
||||
MemoryModuleType.LOOK_TARGET
|
||||
|
||||
+118
-2
@@ -8,16 +8,31 @@
|
||||
|
||||
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.asVariables
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToBeeHiveTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PlaceHoneyInHiveTask
|
||||
import com.cobblemon.mod.common.util.distanceTo
|
||||
import com.cobblemon.mod.common.util.getMemorySafely
|
||||
import net.minecraft.core.BlockPos
|
||||
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.Behavior
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.MemoryStatus
|
||||
import net.minecraft.world.entity.ai.memory.WalkTarget
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class PathToBeeHiveTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val HONEY = "honey"
|
||||
const val STAY_OUT_OF_HIVE_COOLDOWN = 400
|
||||
|
||||
}
|
||||
|
||||
val condition = booleanVariable(HONEY, "can_add_honey", true).asExpressible()
|
||||
@@ -36,6 +51,107 @@ class PathToBeeHiveTaskConfig : SingleTaskConfig {
|
||||
if (!checkCondition(behaviourConfigurationContext.runtime, condition)) {
|
||||
return null
|
||||
}
|
||||
return PathToBeeHiveTask.create()
|
||||
behaviourConfigurationContext.addSensors(CobblemonSensors.NEARBY_BEE_HIVE)
|
||||
return object : Behavior<LivingEntity>(
|
||||
mapOf(
|
||||
MemoryModuleType.LOOK_TARGET to MemoryStatus.REGISTERED,
|
||||
MemoryModuleType.WALK_TARGET to MemoryStatus.VALUE_ABSENT,
|
||||
CobblemonMemories.HAS_NECTAR to MemoryStatus.REGISTERED,
|
||||
CobblemonMemories.HIVE_LOCATION to MemoryStatus.VALUE_PRESENT,
|
||||
CobblemonMemories.HIVE_COOLDOWN to MemoryStatus.VALUE_ABSENT,
|
||||
CobblemonMemories.HIVE_BLACKLIST to MemoryStatus.REGISTERED,
|
||||
),
|
||||
400,
|
||||
400
|
||||
) {
|
||||
|
||||
val maxTicks = 400
|
||||
var traveledTicks = 0
|
||||
|
||||
override fun checkExtraStartConditions(level: ServerLevel, owner: LivingEntity): Boolean {
|
||||
return (owner is PathfinderMob && owner.isAlive && (owner is PokemonEntity && PlaceHoneyInHiveTask.wantsToEnterHive(owner))) && owner.brain.checkMemory(
|
||||
MemoryModuleType.WALK_TARGET, MemoryStatus.VALUE_ABSENT)
|
||||
}
|
||||
|
||||
override fun canStillUse(level: ServerLevel, entity: LivingEntity, gameTime: Long): Boolean {
|
||||
if (maxTicks >= traveledTicks) return false
|
||||
val pos = entity.brain.getMemory(CobblemonMemories.HIVE_LOCATION)
|
||||
return !pos.isEmpty && entity.distanceTo(pos.get()) > 1.0
|
||||
}
|
||||
|
||||
override fun start(level: ServerLevel, entity: LivingEntity, gameTime: Long) {
|
||||
super.start(level, entity, gameTime)
|
||||
val hiveLocation = entity.brain.getMemorySafely(CobblemonMemories.HIVE_LOCATION).orElse(null)
|
||||
val world = entity.level()
|
||||
val sidesByClosest = listOf(
|
||||
hiveLocation.north(),
|
||||
hiveLocation.south(),
|
||||
hiveLocation.east(),
|
||||
hiveLocation.west()
|
||||
).sortedBy { it.distSqr(entity.blockPosition()) }
|
||||
|
||||
var openSide = BlockPos.ZERO
|
||||
for (side in sidesByClosest) {
|
||||
if (world.getBlockState(side).isAir) {
|
||||
openSide = side
|
||||
break
|
||||
}
|
||||
}
|
||||
if (openSide == BlockPos.ZERO) {
|
||||
if (world.getBlockState(hiveLocation.above()).isAir) {
|
||||
// If the above position is also air, we can use it
|
||||
openSide = hiveLocation.above() // Fallback to above if no open sides found
|
||||
} else if (world.getBlockState(hiveLocation.below()).isAir) {
|
||||
// If the below position is also air, we can use it
|
||||
openSide = hiveLocation.below() // Fallback to below if no open sides found
|
||||
} else {
|
||||
// If no open sides or above/below positions are found, we cannot proceed
|
||||
return
|
||||
}
|
||||
}
|
||||
val targetVec = Vec3.atCenterOf(openSide)
|
||||
|
||||
// Set path target toward hive
|
||||
if (pathfindDirectlyTowards(openSide, entity)) {
|
||||
entity.brain.setMemory(MemoryModuleType.WALK_TARGET,WalkTarget(targetVec, 0.35F, 0))
|
||||
entity.brain.setMemory(MemoryModuleType.LOOK_TARGET,BlockPosTracker(targetVec.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
|
||||
traveledTicks = 0
|
||||
} else {
|
||||
dropAndBlackListHive(entity)
|
||||
}
|
||||
}
|
||||
|
||||
override fun tick(level: ServerLevel, owner: LivingEntity, gameTime: Long) {
|
||||
traveledTicks++
|
||||
}
|
||||
|
||||
override fun stop(level: ServerLevel, entity: LivingEntity, gameTime: Long) {
|
||||
if (maxTicks < traveledTicks) {
|
||||
dropAndBlackListHive(entity)
|
||||
entity.brain.setMemoryWithExpiry(CobblemonMemories.HIVE_COOLDOWN, true, 100)
|
||||
}
|
||||
}
|
||||
|
||||
fun dropAndBlackListHive(entity: LivingEntity) {
|
||||
val hivePos = entity.brain.getMemorySafely(CobblemonMemories.HIVE_LOCATION).orElse(null)
|
||||
if (hivePos == null) return
|
||||
val blackList = entity.brain.getMemorySafely(CobblemonMemories.HIVE_BLACKLIST).orElse(emptyList()).toMutableList()
|
||||
|
||||
blackList.add(hivePos)
|
||||
if (blackList.count() > 3) {
|
||||
blackList.removeFirst()
|
||||
}
|
||||
entity.brain.eraseMemory(CobblemonMemories.HIVE_LOCATION)
|
||||
entity.brain.setMemory(CobblemonMemories.HIVE_BLACKLIST, blackList)
|
||||
|
||||
}
|
||||
|
||||
private fun pathfindDirectlyTowards(pos: BlockPos, entity: LivingEntity): Boolean {
|
||||
if (entity !is PathfinderMob) return false
|
||||
val nav = entity.navigation
|
||||
val path = nav. createPath(pos, 0)
|
||||
return path != null && path.canReach()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-4
@@ -8,7 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.CobblemonSensors
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
@@ -41,12 +40,14 @@ class PathToFlowerTaskConfig : SingleTaskConfig {
|
||||
return null
|
||||
}
|
||||
behaviourConfigurationContext.addMemories(
|
||||
CobblemonMemories.NEARBY_FLOWERS,
|
||||
CobblemonMemories.NEARBY_FLOWER,
|
||||
CobblemonMemories.PATH_TO_NEARBY_FLOWER_COOLDOWN,
|
||||
CobblemonMemories.HIVE_COOLDOWN,
|
||||
CobblemonMemories.HIVE_LOCATION,
|
||||
CobblemonMemories.POLLINATED
|
||||
CobblemonMemories.HIVE_BLACKLIST,
|
||||
CobblemonMemories.HAS_NECTAR,
|
||||
)
|
||||
behaviourConfigurationContext.addSensors(CobblemonSensors.NEARBY_BEE_HIVE, CobblemonSensors.NEARBY_FLOWER)
|
||||
behaviourConfigurationContext.addSensors(CobblemonSensors.NEARBY_FLOWER)
|
||||
return PathToFlowerTask.create()
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.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.asVariables
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToSacLeafTask
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
|
||||
class PathToSaccLeavesTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val POLLINATE = "pollinate"
|
||||
}
|
||||
|
||||
val condition = booleanVariable(POLLINATE, "can_pollinate", true).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(
|
||||
condition
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
behaviourConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
if (entity !is PokemonEntity) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (!checkCondition(behaviourConfigurationContext.runtime, condition)) {
|
||||
return null
|
||||
}
|
||||
behaviourConfigurationContext.addMemories(
|
||||
CobblemonMemories.NEARBY_SACC_LEAVES,
|
||||
CobblemonMemories.HAS_NECTAR
|
||||
)
|
||||
behaviourConfigurationContext.addSensors(CobblemonSensors.NEARBY_SACC_LEAVES, CobblemonSensors.NEARBY_FLOWER)
|
||||
return PathToSacLeafTask.create()
|
||||
}
|
||||
}
|
||||
+5
-2
@@ -8,8 +8,8 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PlaceHoneyInHiveTask
|
||||
@@ -37,6 +37,9 @@ class PlaceHoneyInHiveTaskConfig : SingleTaskConfig {
|
||||
if (!checkCondition(behaviourConfigurationContext.runtime, condition)) {
|
||||
return null
|
||||
}
|
||||
return PlaceHoneyInHiveTask.create()
|
||||
return WrapperLivingEntityTask(
|
||||
PlaceHoneyInHiveTask.create(),
|
||||
PokemonEntity::class.java
|
||||
)
|
||||
}
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonActivities
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.block.SaccharineLeafBlock
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.getMemorySafely
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.Behavior
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.MemoryStatus
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class PlaceHoneyInSaccLeavesTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val POLLINATE = "pollinate"
|
||||
const val MIN_DURATION : Int = 300
|
||||
const val MAX_DURATION : Int = 400
|
||||
const val REQUIRED_SUCCESSFUL_POLLINATION_TICKS = 250
|
||||
}
|
||||
|
||||
val condition = booleanVariable(POLLINATE, "can_pollinate", true).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(
|
||||
condition
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
behaviourConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
if (entity !is PokemonEntity) {
|
||||
return null
|
||||
}
|
||||
if (!checkCondition(behaviourConfigurationContext.runtime, condition)) {
|
||||
return null
|
||||
}
|
||||
|
||||
return object : Behavior<LivingEntity>(
|
||||
mapOf(
|
||||
CobblemonMemories.NEARBY_SACC_LEAVES to MemoryStatus.VALUE_PRESENT,
|
||||
MemoryModuleType.WALK_TARGET to MemoryStatus.VALUE_ABSENT,
|
||||
CobblemonMemories.HAS_NECTAR to MemoryStatus.VALUE_PRESENT,
|
||||
),
|
||||
MIN_DURATION,
|
||||
MAX_DURATION
|
||||
) {
|
||||
var successfulPollinationTicks = 0
|
||||
var lastSoundPlayedTick = 0
|
||||
var hoverPos: Vec3? = null
|
||||
|
||||
override fun checkExtraStartConditions(level: ServerLevel, owner: LivingEntity): Boolean {
|
||||
if (level.isNight || level.isRaining || !(entity.brain.getMemorySafely(CobblemonMemories.HAS_NECTAR).orElse(false))) return false
|
||||
val optionalBlockPos = entity.brain.getMemorySafely(CobblemonMemories.NEARBY_SACC_LEAVES)
|
||||
if (!optionalBlockPos.isPresent || level.getBlockState(optionalBlockPos.get()).block != CobblemonBlocks.SACCHARINE_LEAVES) return false
|
||||
return Vec3.atCenterOf(optionalBlockPos.get()).distanceTo(entity.position()) <= 0.6
|
||||
}
|
||||
|
||||
override fun canStillUse(level: ServerLevel, entity: LivingEntity, gameTime: Long): Boolean {
|
||||
return checkExtraStartConditions(level, entity) && entity.brain.activeActivities.indexOf(
|
||||
CobblemonActivities.POKEMON_POLLINATION) != -1
|
||||
}
|
||||
|
||||
override fun start(level: ServerLevel, entity: LivingEntity, gameTime: Long) {
|
||||
super.start(level, entity, gameTime)
|
||||
hoverPos = null
|
||||
}
|
||||
|
||||
override fun tick(level: ServerLevel, owner: LivingEntity, gameTime: Long) {
|
||||
++successfulPollinationTicks
|
||||
if (entity.random.nextFloat() < 0.05f && successfulPollinationTicks > this.lastSoundPlayedTick + 60) {
|
||||
this.lastSoundPlayedTick = successfulPollinationTicks
|
||||
entity.playSound(SoundEvents.BEEHIVE_DRIP, 1.0f, 1.0f)
|
||||
}
|
||||
val leavesPos = entity.brain.getMemorySafely(CobblemonMemories.NEARBY_SACC_LEAVES).orElse(null)
|
||||
|
||||
val bl = hoverPos == null || entity.position().distanceTo(hoverPos!!) <= 0.1
|
||||
val bl3 = entity.random.nextInt(40) == 0
|
||||
if (bl && bl3) {
|
||||
// mimics the wriggling over a flower the vanilla bees do
|
||||
if (hoverPos == null) {
|
||||
hoverPos = leavesPos.bottomCenter.add(0.0, 0.4, 0.0)
|
||||
}
|
||||
if (leavesPos != null) {
|
||||
hoverPos = leavesPos.bottomCenter.add(Vec3((entity.random.nextDouble() * 2.0 - 1.0) * (1.0/5.0), 0.3 + (entity.random.nextDouble() * 2.0 - 1.0) * (1.0/7.0), (entity.random.nextDouble() * 2.0 - 1.0) * (1.0/5.0)))
|
||||
entity.getMoveControl()
|
||||
.setWantedPosition(hoverPos!!.x(), hoverPos!!.y(), hoverPos!!.z(), 0.35)
|
||||
entity.lookControl.setLookAt(leavesPos.bottomCenter.add(0.0,0.5,0.0))
|
||||
}
|
||||
} else if (!bl) {
|
||||
entity.getMoveControl()
|
||||
.setWantedPosition(hoverPos!!.x(), hoverPos!!.y(), hoverPos!!.z(), 0.35)
|
||||
}
|
||||
}
|
||||
|
||||
override fun stop(level: ServerLevel, entity: LivingEntity, gameTime: Long) {
|
||||
if (successfulPollinationTicks > REQUIRED_SUCCESSFUL_POLLINATION_TICKS) {
|
||||
val blockPos = entity.brain.getMemorySafely(CobblemonMemories.NEARBY_SACC_LEAVES).orElse(null)
|
||||
blockPos?.let {
|
||||
val blockState = level.getBlockState(blockPos)
|
||||
if (blockState.block == CobblemonBlocks.SACCHARINE_LEAVES) {
|
||||
val saccAge = blockState.getValue( SaccharineLeafBlock.AGE)
|
||||
if (saccAge < SaccharineLeafBlock.MAX_AGE) {
|
||||
entity.brain.eraseMemory(CobblemonMemories.HAS_NECTAR)
|
||||
val newBlockState = blockState.setValue(SaccharineLeafBlock.AGE, saccAge + 1)
|
||||
level.setBlock(blockPos, newBlockState, 3)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
successfulPollinationTicks = 0
|
||||
entity.brain.eraseMemory(MemoryModuleType.WALK_TARGET)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+52
-24
@@ -8,17 +8,14 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonActivities
|
||||
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.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.getMemorySafely
|
||||
import com.cobblemon.mod.common.util.sendParticlesServer
|
||||
import com.mojang.datafixers.util.Either
|
||||
import net.minecraft.core.particles.ParticleTypes
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.Behavior
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
@@ -29,10 +26,11 @@ import net.minecraft.world.phys.Vec3
|
||||
class PollinateFlowerTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val POLLINATE = "pollinate"
|
||||
const val PATH_TO_FLOWER_COOLDOWN_TICKS : Long = 200
|
||||
const val HAS_NECTAR_ASPECT = "has_nectar"
|
||||
}
|
||||
|
||||
val condition = booleanVariable(POLLINATE, "can_pollinate", true).asExpressible()
|
||||
val durationTicks: ExpressionOrEntityVariable = Either.left("30".asExpression())
|
||||
|
||||
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(
|
||||
condition
|
||||
@@ -51,22 +49,28 @@ class PollinateFlowerTaskConfig : SingleTaskConfig {
|
||||
|
||||
return object : Behavior<LivingEntity>(
|
||||
mapOf(
|
||||
CobblemonMemories.NEARBY_FLOWERS to MemoryStatus.VALUE_PRESENT,
|
||||
CobblemonMemories.NEARBY_FLOWER to MemoryStatus.VALUE_PRESENT,
|
||||
MemoryModuleType.WALK_TARGET to MemoryStatus.VALUE_ABSENT,
|
||||
CobblemonMemories.POLLINATED to MemoryStatus.VALUE_ABSENT,
|
||||
CobblemonMemories.HAS_NECTAR to MemoryStatus.REGISTERED,
|
||||
CobblemonMemories.HIVE_COOLDOWN to MemoryStatus.VALUE_ABSENT,
|
||||
),
|
||||
15,
|
||||
45
|
||||
600,
|
||||
625
|
||||
) {
|
||||
val REQUIRED_SUCCESSFUL_POLLINATION_TICKS = 400
|
||||
var successfulPollinationTicks = 0
|
||||
var lastSoundPlayedTick = 0
|
||||
var hoverPos: Vec3? = null
|
||||
|
||||
override fun checkExtraStartConditions(level: ServerLevel, owner: LivingEntity): Boolean {
|
||||
return entity.brain.getMemorySafely(CobblemonMemories.NEARBY_FLOWERS).orElse(emptyList()).any {
|
||||
Vec3.atCenterOf(it).distanceTo(entity.position()) <= 1.0
|
||||
}
|
||||
if (level.isNight || level.isRaining || entity.brain.getMemorySafely(CobblemonMemories.HAS_NECTAR).orElse(false)) return false
|
||||
val optionalBlockPos = entity.brain.getMemorySafely(CobblemonMemories.NEARBY_FLOWER)
|
||||
return optionalBlockPos.isPresent && Vec3.atCenterOf(optionalBlockPos.get()).distanceTo(entity.position()) <= 1.0
|
||||
}
|
||||
|
||||
override fun canStillUse(level: ServerLevel, entity: LivingEntity, gameTime: Long): Boolean {
|
||||
return checkExtraStartConditions(level, entity)
|
||||
return checkExtraStartConditions(level, entity) && entity.brain.activeActivities.indexOf(
|
||||
CobblemonActivities.POKEMON_POLLINATION) != -1
|
||||
}
|
||||
|
||||
override fun start(level: ServerLevel, entity: LivingEntity, gameTime: Long) {
|
||||
@@ -74,20 +78,44 @@ class PollinateFlowerTaskConfig : SingleTaskConfig {
|
||||
}
|
||||
|
||||
override fun tick(level: ServerLevel, owner: LivingEntity, gameTime: Long) {
|
||||
level.sendParticlesServer(
|
||||
particleType = ParticleTypes.DRIPPING_HONEY,
|
||||
position = owner.eyePosition,
|
||||
particles = 3,
|
||||
offset = Vec3.ZERO,
|
||||
speed = 0.0
|
||||
)
|
||||
++successfulPollinationTicks
|
||||
if (entity.random.nextFloat() < 0.05f && successfulPollinationTicks > this.lastSoundPlayedTick + 60) {
|
||||
this.lastSoundPlayedTick = successfulPollinationTicks
|
||||
entity.playSound(SoundEvents.BEE_POLLINATE, 1.0f, 1.0f)
|
||||
}
|
||||
val flowerPos = entity.brain.getMemorySafely(CobblemonMemories.NEARBY_FLOWER).orElse(null)
|
||||
|
||||
|
||||
val bl = hoverPos == null || entity.position().distanceTo(hoverPos!!) <= 0.1
|
||||
val bl3 = entity.random.nextInt(40) == 0
|
||||
if (bl && bl3) {
|
||||
// mimics the wriggling over a flower the vanilla bees do
|
||||
if (hoverPos == null) {
|
||||
hoverPos = flowerPos.bottomCenter.add(0.0, 0.4, 0.0)
|
||||
}
|
||||
|
||||
if (flowerPos != null) {
|
||||
hoverPos = flowerPos.bottomCenter.add(Vec3((entity.random.nextDouble() * 2.0 - 1.0) * (1.0/5.0), 0.3 + (entity.random.nextDouble() * 2.0 - 1.0) * (1.0/7.0), (entity.random.nextDouble() * 2.0 - 1.0) * (1.0/5.0)))
|
||||
entity.getMoveControl()
|
||||
.setWantedPosition(hoverPos!!.x(), hoverPos!!.y(), hoverPos!!.z(), 0.35)
|
||||
entity.lookControl.setLookAt(flowerPos.bottomCenter.add(0.0,0.5,0.0))
|
||||
}
|
||||
} else if (!bl) {
|
||||
entity.getMoveControl()
|
||||
.setWantedPosition(hoverPos!!.x(), hoverPos!!.y(), hoverPos!!.z(), 0.35)
|
||||
}
|
||||
entity.lookControl.setLookAt(flowerPos.bottomCenter.add(0.0,0.5,0.0))
|
||||
|
||||
}
|
||||
|
||||
override fun stop(level: ServerLevel, entity: LivingEntity, gameTime: Long) {
|
||||
val flowerStillThere = canStillUse(level, entity, gameTime)
|
||||
if (flowerStillThere) {
|
||||
entity.brain.setMemory(CobblemonMemories.POLLINATED, true)
|
||||
|
||||
if (successfulPollinationTicks > REQUIRED_SUCCESSFUL_POLLINATION_TICKS) {
|
||||
entity.brain.setMemory(CobblemonMemories.HAS_NECTAR, true)
|
||||
}
|
||||
successfulPollinationTicks = 0
|
||||
entity.brain.setMemoryWithExpiry(CobblemonMemories.PATH_TO_NEARBY_FLOWER_COOLDOWN, true, PATH_TO_FLOWER_COOLDOWN_TICKS)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,9 @@ interface TaskConfig {
|
||||
cobblemonResource("go_to_land") to GoToLandTaskConfig::class.java,
|
||||
cobblemonResource("path_to_hive") to PathToBeeHiveTaskConfig::class.java,
|
||||
cobblemonResource("place_honey_in_hive") to PlaceHoneyInHiveTaskConfig::class.java,
|
||||
cobblemonResource("place_honey_in_sacc_leaves") to PlaceHoneyInSaccLeavesTaskConfig::class.java,
|
||||
cobblemonResource("path_to_flower") to PathToFlowerTaskConfig::class.java,
|
||||
cobblemonResource("path_to_sacc_leaves") to PathToSaccLeavesTaskConfig::class.java,
|
||||
cobblemonResource("pollinate_flower") to PollinateFlowerTaskConfig::class.java,
|
||||
cobblemonResource("go_to_land") to GoToLandTaskConfig::class.java,
|
||||
cobblemonResource("manage_flight_in_battle") to ManageFlightInBattleTaskConfig::class.java,
|
||||
|
||||
+52
-24
@@ -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
|
||||
@@ -41,24 +44,27 @@ class WanderTaskConfig : SingleTaskConfig {
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -77,7 +83,7 @@ class WanderTaskConfig : SingleTaskConfig {
|
||||
(1..MAX_LOOK_DOWN_DISTANCE).firstOrNull {
|
||||
val newPos = pos.below(it)
|
||||
if (!world.getBlockState(newPos).isAir) {
|
||||
return@firstOrNull true
|
||||
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,
|
||||
@@ -126,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
|
||||
}
|
||||
|
||||
@@ -134,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)
|
||||
@@ -157,14 +162,28 @@ class WanderTaskConfig : SingleTaskConfig {
|
||||
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,
|
||||
@@ -182,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,
|
||||
|
||||
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.api.apricorn
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.api.cooking.Flavour
|
||||
import com.cobblemon.mod.common.block.ApricornBlock
|
||||
import com.cobblemon.mod.common.block.ApricornSaplingBlock
|
||||
import com.cobblemon.mod.common.item.ApricornItem
|
||||
@@ -28,43 +27,14 @@ import net.minecraft.world.level.material.MapColor
|
||||
* @author Licious
|
||||
* @since October 29th, 2022
|
||||
*/
|
||||
enum class Apricorn(
|
||||
/** A value of true means it buffs that flavour, a value of false means it weakens that flavour. */
|
||||
val flavourStrength: Map<Flavour, Boolean> = emptyMap()
|
||||
) : StringRepresentable {
|
||||
|
||||
enum class Apricorn : StringRepresentable {
|
||||
BLACK,
|
||||
BLUE(
|
||||
mapOf(
|
||||
Flavour.DRY to true,
|
||||
Flavour.BITTER to false
|
||||
)
|
||||
),
|
||||
GREEN(
|
||||
mapOf(
|
||||
Flavour.BITTER to true,
|
||||
Flavour.SWEET to false
|
||||
)
|
||||
),
|
||||
PINK(
|
||||
mapOf(
|
||||
Flavour.SWEET to true,
|
||||
Flavour.SPICY to false
|
||||
)
|
||||
),
|
||||
RED(
|
||||
mapOf(
|
||||
Flavour.SPICY to true,
|
||||
Flavour.SOUR to false
|
||||
)
|
||||
),
|
||||
BLUE,
|
||||
GREEN,
|
||||
PINK,
|
||||
RED,
|
||||
WHITE,
|
||||
YELLOW(
|
||||
mapOf(
|
||||
Flavour.SOUR to true,
|
||||
Flavour.DRY to false
|
||||
)
|
||||
);
|
||||
YELLOW;
|
||||
|
||||
fun item(): ApricornItem = when(this) {
|
||||
BLACK -> CobblemonItems.BLACK_APRICORN
|
||||
@@ -131,5 +101,4 @@ enum class Apricorn(
|
||||
companion object {
|
||||
val CODEC: Codec<Apricorn> = StringRepresentable.fromValues(::values)
|
||||
}
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.berry.spawncondition
|
||||
|
||||
import com.cobblemon.mod.common.api.berry.Berry
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.tags.TagKey
|
||||
import net.minecraft.util.RandomSource
|
||||
import net.minecraft.world.level.biome.Biome
|
||||
|
||||
class SpecificBiomeCondition : BerrySpawnCondition {
|
||||
lateinit var biome: TagKey<Biome>
|
||||
val minGroveSize: Int = 1
|
||||
val maxGroveSize: Int = 1
|
||||
|
||||
override fun canSpawn(berry: Berry, biome: Holder<Biome>) = biome.`is`(this.biome)
|
||||
override fun getGroveSize(random: RandomSource) = random.nextIntBetweenInclusive(minGroveSize, maxGroveSize)
|
||||
|
||||
companion object {
|
||||
val ID = cobblemonResource("specific_biome")
|
||||
}
|
||||
}
|
||||
@@ -8,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() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.cooking
|
||||
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import net.minecraft.util.FastColor
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
@@ -65,6 +66,10 @@ fun getColourMixFromFlavours(dominantFlavours: List<Flavour>, forBubbles: Boolea
|
||||
)
|
||||
}
|
||||
|
||||
fun getColourMixFromRideStatBoosts(dominantBoosts: Iterable<RidingStat>, forBubbles: Boolean = false): Int? {
|
||||
return getColourMixFromFlavours(dominantBoosts.map { it.flavour }, forBubbles)
|
||||
}
|
||||
|
||||
fun getColourMixFromColors(colors: List<Int>): Int? {
|
||||
if (colors.isEmpty()) return null
|
||||
|
||||
|
||||
@@ -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].
|
||||
|
||||
@@ -22,6 +22,7 @@ interface PokemonSideDelegate : EntitySideDelegate<PokemonEntity> {
|
||||
fun handleStatus(status: Byte) {}
|
||||
fun spawnShinyParticle(player: Player) {}
|
||||
fun spawnAspectParticle() {}
|
||||
fun applyRenderRotation(partialTick: Float) {}
|
||||
fun positionRider(passenger: Entity, positionUpdater: MoveFunction)
|
||||
fun updateAge(age: Int) {}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -17,18 +17,11 @@ object SpawnBaitUtils {
|
||||
return grouped.map { (key, groupEffects) ->
|
||||
val (type, subcategory) = key
|
||||
|
||||
val count = groupEffects.size
|
||||
val totalChance = groupEffects.sumOf { it.chance }
|
||||
val totalValue = groupEffects.sumOf { it.value }
|
||||
|
||||
val multiplier = when (count) {
|
||||
2 -> 0.8
|
||||
3 -> 0.6
|
||||
else -> 1.0
|
||||
}
|
||||
|
||||
val adjustedChance = (totalChance * multiplier).coerceAtMost(1.0)
|
||||
val adjustedValue = ceil(totalValue * multiplier)
|
||||
val adjustedChance = totalChance.coerceAtMost(1.0)
|
||||
val adjustedValue = ceil(totalValue)
|
||||
|
||||
SpawnBait.Effect(type, subcategory, adjustedChance, adjustedValue)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@ import com.bedrockk.molang.runtime.MoLangEnvironment
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.struct.ArrayStruct
|
||||
import com.bedrockk.molang.runtime.struct.MoStruct
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.bedrockk.molang.runtime.struct.VariableStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
@@ -40,7 +39,6 @@ import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
|
||||
import com.cobblemon.mod.common.api.moves.animations.NPCProvider
|
||||
import com.cobblemon.mod.common.api.moves.animations.TargetsProvider
|
||||
import com.cobblemon.mod.common.api.npc.NPCClass
|
||||
import com.cobblemon.mod.common.api.npc.NPCClasses
|
||||
import com.cobblemon.mod.common.api.npc.configuration.interaction.DialogueNPCInteractionConfiguration
|
||||
import com.cobblemon.mod.common.api.npc.configuration.interaction.ScriptNPCInteractionConfiguration
|
||||
@@ -57,7 +55,6 @@ import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.api.pokemon.experience.SidemodExperienceSource
|
||||
import com.cobblemon.mod.common.api.pokemon.moves.LearnsetQuery
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.riding.Rideable
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker
|
||||
import com.cobblemon.mod.common.api.scheduling.Schedulable
|
||||
@@ -137,7 +134,6 @@ import net.minecraft.world.entity.LightningBolt
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.Mob
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.PlayerRideable
|
||||
import net.minecraft.world.entity.TamableAnimal
|
||||
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
@@ -145,7 +141,6 @@ import net.minecraft.world.entity.ai.memory.MemoryStatus
|
||||
import net.minecraft.world.entity.ai.memory.WalkTarget
|
||||
import net.minecraft.world.entity.animal.Animal
|
||||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.entity.monster.Enemy
|
||||
import net.minecraft.world.entity.monster.Monster
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
@@ -160,9 +155,6 @@ import net.minecraft.world.level.levelgen.Heightmap
|
||||
import net.minecraft.world.level.pathfinder.PathType
|
||||
import net.minecraft.world.phys.AABB
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import kotlin.text.get
|
||||
import kotlin.text.toInt
|
||||
import kotlin.toString
|
||||
|
||||
/**
|
||||
* Holds a bunch of useful MoLang trickery that can be used or extended in API
|
||||
@@ -1185,16 +1177,28 @@ object MoLangFunctions {
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("has_memory_value") { params ->
|
||||
val memoryTypes = params.params.map {
|
||||
it.asString().asIdentifierDefaultingNamespace()
|
||||
}.map(BuiltInRegistries.MEMORY_MODULE_TYPE::get)
|
||||
return@put DoubleValue(memoryTypes.all { entity.brain.checkMemory(it, MemoryStatus.VALUE_PRESENT) })
|
||||
for (param in params.params) {
|
||||
if (!entity.hasMemoryFromString(param.asString())) {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("lacks_memory_value") { params ->
|
||||
val memoryTypes = params.params.map {
|
||||
it.asString().asIdentifierDefaultingNamespace()
|
||||
}.map(BuiltInRegistries.MEMORY_MODULE_TYPE::get)
|
||||
return@put DoubleValue(memoryTypes.none { entity.brain.checkMemory(it, MemoryStatus.VALUE_PRESENT) })
|
||||
for (param in params.params) {
|
||||
if (entity.hasMemoryFromString(param.asString())) {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("is_doing_activity") { params ->
|
||||
for (param in params.params) {
|
||||
if (entity.isDoingActivityFromString(param.asString())) {
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
}
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
map.put("can_see") { params ->
|
||||
val target = params.get<MoValue>(0)
|
||||
@@ -1236,7 +1240,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
|
||||
@@ -1252,15 +1256,6 @@ object MoLangFunctions {
|
||||
map.put("in_battle") { DoubleValue(npc.isInBattle()) }
|
||||
map.put("battles") { ArrayStruct(npc.battleIds.mapNotNull { BattleRegistry.getBattle(it)?.struct }.mapIndexed { index, value -> "$index" to value }.toMap()) }
|
||||
map.put("stop_battles") { _ -> npc.battleIds.forEach { BattleRegistry.getBattle(it)?.stop() } }
|
||||
map.put("is_doing_activity") { params ->
|
||||
val identifiers = params.params.map { it.asString().asIdentifierDefaultingNamespace(namespace = "minecraft") }
|
||||
val activities = identifiers.mapNotNull { identifier -> BuiltInRegistries.ACTIVITY.get(identifier) }
|
||||
if (activities.isNotEmpty()) {
|
||||
return@put DoubleValue(activities.any { activity -> npc.brain.isActive(activity) })
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("run_script_on_client") { params ->
|
||||
val world = npc.level()
|
||||
if (world is ServerLevel) {
|
||||
@@ -1905,6 +1900,9 @@ object MoLangFunctions {
|
||||
map.put("is_holding_item") { DoubleValue(!pokemonEntity.entityData.get(PokemonEntity.SHOWN_HELD_ITEM).let {
|
||||
it.isEmpty || it.`is`(CobblemonItemTags.WEARABLE_HAT_ITEMS) || it.`is`(CobblemonItemTags.WEARABLE_FACE_ITEMS)
|
||||
}) }
|
||||
map.put("riding_style") {
|
||||
StringValue(pokemonEntity.ifRidingAvailableSupply("") { behaviour, settings, state -> behaviour.getRidingStyle(settings, state).name })
|
||||
}
|
||||
map.put("is_wearing_hat") { DoubleValue(pokemonEntity.entityData.get(PokemonEntity.SHOWN_HELD_ITEM).`is`(CobblemonItemTags.WEARABLE_HAT_ITEMS)) }
|
||||
map.put("is_wearing_face") { DoubleValue(pokemonEntity.entityData.get(PokemonEntity.SHOWN_HELD_ITEM).`is`(CobblemonItemTags.WEARABLE_FACE_ITEMS)) }
|
||||
map.put("is_pastured") {
|
||||
|
||||
@@ -53,8 +53,7 @@ open class MoveTemplate(
|
||||
val pp: Int,
|
||||
val priority: Int,
|
||||
val critRatio: Double,
|
||||
val effectChances: Array<Double>,
|
||||
val actionEffect: ActionEffectTimeline?
|
||||
val effectChances: Array<Double>
|
||||
) {
|
||||
val struct: MoStruct by lazy {
|
||||
QueryStruct(hashMapOf())
|
||||
@@ -87,8 +86,7 @@ open class MoveTemplate(
|
||||
pp = 5,
|
||||
priority = 0,
|
||||
critRatio = 0.0,
|
||||
effectChances = emptyArray(),
|
||||
actionEffect = null
|
||||
effectChances = emptyArray()
|
||||
)
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -92,18 +92,7 @@ object Moves : DataRegistry {
|
||||
effectChances += secondaryMember.get("chance").asDouble
|
||||
}
|
||||
}
|
||||
val actionEffect = ActionEffects.actionEffects[id.asIdentifierDefaultingNamespace()]
|
||||
?: run {
|
||||
ActionEffects.actionEffects["generic_move".asIdentifierDefaultingNamespace()]
|
||||
// if (damageCategory == DamageCategories.STATUS) {
|
||||
// ActionEffects.actionEffects[cobblemonResource("status")]
|
||||
// } else {
|
||||
// val type = elementalType.name.lowercase()
|
||||
// val category = damageCategory.name.lowercase()
|
||||
// ActionEffects.actionEffects["${category}_$type".asIdentifierDefaultingNamespace()]
|
||||
// }
|
||||
}
|
||||
val move = MoveTemplate(id, num, elementalType, damageCategory, power, target, accuracy, pp, priority, critRatio, effectChances.toTypedArray(), actionEffect)
|
||||
val move = MoveTemplate(id, num, elementalType, damageCategory, power, target, accuracy, pp, priority, critRatio, effectChances.toTypedArray())
|
||||
this.register(move)
|
||||
} catch (e: Exception) {
|
||||
Cobblemon.LOGGER.error("Caught exception trying to resolve the move '{}'", id, e)
|
||||
|
||||
@@ -14,6 +14,7 @@ import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.moves.animations.keyframes.*
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stat
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.pokemon.adapters.CobblemonStatTypeAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.*
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
@@ -91,5 +92,11 @@ object ActionEffects : JsonDataRegistry<ActionEffectTimeline> {
|
||||
observable.emit(this)
|
||||
}
|
||||
|
||||
fun getEffectWithBattleContext(id: ResourceLocation, pokemon: BattlePokemon): ActionEffectTimeline? {
|
||||
val species = pokemon.entity?.exposedSpecies ?: pokemon.effectedPokemon.species
|
||||
val contextedEffect = actionEffects[ResourceLocation.fromNamespaceAndPath(id.namespace, id.path + "_" + species)]
|
||||
return contextedEffect ?: actionEffects[id]
|
||||
}
|
||||
|
||||
override fun sync(player: ServerPlayer) {}
|
||||
}
|
||||
+3
-2
@@ -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
|
||||
|
||||
@@ -62,7 +63,7 @@ open class OrientationController(val entity: LivingEntity) {
|
||||
}
|
||||
|
||||
fun getRenderOrientation(delta: Float): Quaternionf {
|
||||
val old = renderOrientationO ?: renderOrientation ?: orientation
|
||||
val old = renderOrientationO ?: renderOrientation ?: orientation ?: Matrix3f()
|
||||
val new = renderOrientation ?: old
|
||||
val oldQuat = Quaternionf().setFromUnnormalized(old)
|
||||
val newQuat = Quaternionf().setFromUnnormalized(new)
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -170,11 +170,8 @@ object Natures {
|
||||
* Utility method to get a nature by string
|
||||
* @return a nature type or null
|
||||
*/
|
||||
fun getNature(identifier: String): Nature? {
|
||||
val nature = getNature(cobblemonResource(identifier))
|
||||
if(nature != null) return nature
|
||||
return getNature(ResourceLocation.parse(identifier))
|
||||
}
|
||||
fun getNature(identifier: String) = getNature(cobblemonResource(identifier))
|
||||
?: getNature(ResourceLocation.parse(identifier))
|
||||
|
||||
/**
|
||||
* Helper function for a random Nature
|
||||
|
||||
@@ -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") }
|
||||
@@ -796,8 +806,8 @@ open class PokemonProperties {
|
||||
return PokemonProperties().loadFromJSON(this.saveToJSON())
|
||||
}
|
||||
|
||||
// If the config value is at least 1, then do 1/x and use that as the property chance
|
||||
private fun Float.checkRate(): Boolean = if (this >= 1) (Random.Default.nextFloat() < 1 / this) else Random.Default.nextFloat() < this
|
||||
// If the config value is at least 0, then do 1/x and use that as the property chance
|
||||
private fun Float.checkRate(): Boolean = this > 0 && (Random.nextFloat() < 1 / this)
|
||||
|
||||
/**
|
||||
* Attempts to find an ability by ID and resolve if it should be forced or if it's legal for the given form.
|
||||
|
||||
@@ -39,11 +39,9 @@ import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStatDefinition
|
||||
import com.cobblemon.mod.common.api.spawning.TimeRange
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.api.types.adapters.ElementalTypeAdapter
|
||||
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 +52,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 +61,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
|
||||
@@ -134,7 +128,6 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
.registerTypeAdapter(MobEffect::class.java, RegistryElementAdapter<MobEffect>(BuiltInRegistries::MOB_EFFECT))
|
||||
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter)
|
||||
.registerTypeAdapter(RidingBehaviourSettings::class.java, RidingBehaviourSettingsAdapter)
|
||||
.registerTypeAdapter(RidingStatDefinition::class.java, RidingStatDefinitionAdapter)
|
||||
.registerTypeAdapter(RideSoundSettingsList::class.java, RideSoundSettingsListAdapter)
|
||||
.registerTypeAdapter(ObtainableItemCondition::class.java, ObtainableItemConditionAdapter)
|
||||
.disableHtmlEscaping()
|
||||
@@ -151,24 +144,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 +219,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 +226,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -9,8 +9,9 @@
|
||||
package com.cobblemon.mod.common.api.pokemon.aspect
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.api.ai.config.task.PollinateFlowerTaskConfig
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SaccharineLogSlatheredInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.prospecting.LurePokeSnackDetector
|
||||
import com.cobblemon.mod.common.block.entity.PokeSnackBlockEntity
|
||||
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
|
||||
import net.minecraft.core.particles.BlockParticleOption
|
||||
import net.minecraft.core.particles.ParticleOptions
|
||||
@@ -22,11 +23,12 @@ import net.minecraft.resources.ResourceLocation
|
||||
*/
|
||||
val aspectParticleMap: Map<String, ParticleData> = mapOf(
|
||||
SaccharineLogSlatheredInfluence.HONEY_DRENCHED_ASPECT to ParticleData.MinecraftParticle(ParticleTypes.FALLING_HONEY, 0.075, 1),
|
||||
LurePokeSnackDetector.POKE_SNACK_CRUMBED_ASPECT to ParticleData.MinecraftParticle(
|
||||
PokeSnackBlockEntity.POKE_SNACK_CRUMBED_ASPECT to ParticleData.MinecraftParticle(
|
||||
BlockParticleOption(
|
||||
ParticleTypes.BLOCK,
|
||||
CobblemonBlocks.POKE_SNACK.defaultBlockState()
|
||||
), 0.05, 3),
|
||||
PollinateFlowerTaskConfig.HAS_NECTAR_ASPECT to ParticleData.MinecraftParticle(ParticleTypes.FALLING_NECTAR, 0.075, 1)
|
||||
)
|
||||
|
||||
sealed class ParticleData {
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+12
@@ -103,6 +103,18 @@ interface RidingBehaviour<Settings : RidingBehaviourSettings, State : RidingBeha
|
||||
*/
|
||||
fun damageOnCollision(settings: Settings, state: State, vehicle: PokemonEntity, impactVec: Vec3): Boolean = false
|
||||
|
||||
/**
|
||||
* Called in [com.cobblemon.mod.common.client.render.pokemon.PokemonRenderer] to allow the controller to set rotations
|
||||
* through code using partialTicks and right before rendering.
|
||||
* //TODO: This needs to be further refined. Maybe only the orientationController needs affected? Maybe the rotations set here don't need to be applied to the controller/camera
|
||||
*/
|
||||
fun applyRenderRotation(settings: Settings, state: State, vehicle: PokemonEntity, partialTicks: Float) {}
|
||||
|
||||
/**
|
||||
* Determines if the pair of mouse inputs Pair(mouseX, mouseY) get used to modify the driverRotations or the vehicle rotations themselves.
|
||||
* Driver rotations are the rotations used by passengers or a driver that is freelooking. This does not override the freelook button.
|
||||
*/
|
||||
fun mouseModifiesDriverRotation(settings: Settings, state: State, vehicle: PokemonEntity): Pair<Boolean, Boolean> = Pair(false, false)
|
||||
|
||||
/**
|
||||
* Internal helpers to help behaviour specific calculations
|
||||
|
||||
+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).coerceAtMost(range.endInclusive.toFloat())
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
+2
@@ -16,6 +16,7 @@ import com.cobblemon.mod.common.api.riding.behaviour.types.land.MinekartBehaviou
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BoatBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BurstBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.DolphinBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.SubmarineBehaviour
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
object RidingBehaviours {
|
||||
@@ -34,6 +35,7 @@ object RidingBehaviours {
|
||||
register(MinekartBehaviour.KEY, MinekartBehaviour())
|
||||
register(HoverBehaviour.KEY, HoverBehaviour())
|
||||
register(RocketBehaviour.KEY, RocketBehaviour())
|
||||
register(SubmarineBehaviour.KEY, SubmarineBehaviour())
|
||||
register(CompositeBehaviour.KEY, CompositeBehaviour())
|
||||
}
|
||||
|
||||
|
||||
+33
-2
@@ -8,13 +8,20 @@
|
||||
|
||||
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
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import kotlin.collections.get
|
||||
import kotlin.math.max
|
||||
|
||||
/**
|
||||
* Small wrapper around a RidingBehaviour to provide sane defaults in the event that the behaviour is not active.
|
||||
@@ -44,11 +51,25 @@ class RidingController(
|
||||
fun tick() {
|
||||
if (entity.ticksLived - lastTransitionAge < 10) return
|
||||
val newTransition = checkForNewTransition(entity.controllingPassenger)
|
||||
// If transitioning to air then give an upward boost
|
||||
if (newTransition == RidingStyle.AIR && context?.style != RidingStyle.AIR && context?.state != null) {
|
||||
val currVel = context!!.state.rideVelocity.get()
|
||||
context!!.state.rideVelocity.set(Vec3(currVel.x, max(0.4, currVel.y), currVel.z))
|
||||
}
|
||||
val behaviourSettings = entity.pokemon.riding.behaviours?.get(newTransition)
|
||||
if (newTransition != context?.style) {
|
||||
if (newTransition != null && behaviourSettings != null) {
|
||||
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(passenger, previousKey ?: continue, newKey)
|
||||
}
|
||||
}
|
||||
|
||||
context = ActiveRidingContext(behaviourSettings.key, behaviourSettings, newState, newTransition)
|
||||
lastTransitionAge = entity.ticksLived
|
||||
} else {
|
||||
@@ -96,12 +117,22 @@ class RidingController(
|
||||
}
|
||||
|
||||
private fun canTransitionToLand(): Boolean {
|
||||
if (entity.isInLiquid || entity.isUnderWater) return false
|
||||
val hasLandController = entity.pokemon.riding.behaviours?.get(RidingStyle.LAND) != null
|
||||
if (hasLandController && (entity.isInLiquid || entity.isUnderWater)) return false
|
||||
if (!hasLandController && (entity.isEyeInFluid(FluidTags.WATER) || entity.isEyeInFluid(FluidTags.LAVA))) return false
|
||||
return entity.onGround()
|
||||
}
|
||||
|
||||
private fun canTransitionToLiquid(): Boolean {
|
||||
return entity.isInLiquid || entity.isUnderWater
|
||||
// If it has a liquid controller to transition to and it isn't currently an air controller then transition on
|
||||
// touching water. If not then check for eyes in water. This allows fliers to fly closely above water and
|
||||
// for walkers that don't have a liquid controller to be able to walk in not deep water
|
||||
return if (entity.pokemon.riding.behaviours?.get(RidingStyle.LIQUID) != null && context?.style != RidingStyle.AIR) {
|
||||
entity.isInLiquid || entity.isUnderWater
|
||||
} else {
|
||||
entity.isEyeInFluid(FluidTags.WATER) || entity.isEyeInFluid(FluidTags.LAVA)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun canTransitionToAir(driver: LivingEntity?): Boolean {
|
||||
|
||||
+256
-202
@@ -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,17 @@ 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 org.joml.Vector3f
|
||||
import kotlin.math.*
|
||||
|
||||
class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
@@ -48,8 +51,11 @@ 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 !(vehicle.isInWater || vehicle.isUnderWater)
|
||||
}
|
||||
|
||||
override fun pose(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity): PoseType {
|
||||
@@ -74,6 +80,25 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
) {
|
||||
if(vehicle.level().isClientSide) {
|
||||
tickStamina(settings, state, vehicle, driver)
|
||||
checkUpsideDown(state, vehicle, driver)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkUpsideDown(
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
) {
|
||||
// Also checks stamina
|
||||
val controller = (vehicle as OrientationControllable).orientationController
|
||||
if (cos(controller.roll.toRadians()) < 0.0) {
|
||||
val upsideDownRate = 1.0 / (2.0 * 20.0) // 2 seconds to max out upsideDown force
|
||||
state.currUpsideDownForce.set( min(1.0, state.currUpsideDownForce.get() + upsideDownRate * abs(cos(controller.roll.toRadians()))))
|
||||
} 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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,15 +109,28 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
driver: Player
|
||||
) {
|
||||
val stam = state.stamina.get()
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
|
||||
if (vehicle.runtime.resolveBoolean(settings.infiniteStamina ?: globalBird.infiniteStamina!!)) {
|
||||
return
|
||||
}
|
||||
|
||||
// Grab the boost time in seconds and convert to ticks. Then calculate the drain rate as inversely
|
||||
// proportional to the number of ticks of boost thus making a full boost take x ticks
|
||||
// in short: "Stamina drains slower at higher values and also replenishes slower"
|
||||
val totalStam = vehicle.runtime.resolveDouble(settings.staminaExpr) * 20.0f
|
||||
val stamDrainRate = (1.0f / totalStam).toFloat()
|
||||
val totalStam = vehicle.runtime.resolveDouble(settings.staminaExpr ?: globalBird.staminaExpr!!) * 20.0f
|
||||
val horzMod = vehicle.runtime.resolveDouble(settings.stamDrainHorzMod ?: globalBird.stamDrainHorzMod!!)
|
||||
val glideMod = vehicle.runtime.resolveFloat(settings.stamDrainPercRateGlide ?: globalBird.stamDrainPercRateGlide!!)
|
||||
val stamDrainPerRateDive = vehicle.runtime.resolveDouble(settings.stamDrainPerRateDive ?: globalBird.stamDrainPerRateDive!!).coerceIn(0.0, 1.0) // Ensure its between 1 and 0 or things get weird
|
||||
|
||||
val newStam = if (driver.zza > 0.0) max(0.0f,stam - stamDrainRate)
|
||||
else min(1.0f,stam + stamDrainRate)
|
||||
val stamDrainHorz = if(vehicle.deltaMovement.horizontalDistance() < 1e-6) horzMod else max(1.0 + ((horzMod - 1.0) * atan( vehicle.deltaMovement.y / vehicle.deltaMovement.horizontalDistance())), 1.0)
|
||||
val diveAmount = sin(controller.pitch.toRadians()).coerceIn(0.0f, 1.0f)// from 0.0 -> 1.0 how steep is the current dive angle
|
||||
val diveMod = ((1.0 - diveAmount) * ( 1.0 - stamDrainPerRateDive ) + stamDrainPerRateDive).toFloat() // smooth between 1.0 and the dive drain mod depending upon how dived the ride is
|
||||
val stamDrainRate = (1.0f / totalStam).toFloat() * stamDrainHorz.toFloat()
|
||||
|
||||
val newStam = if (driver.deltaMovement.length() > 0.1) max(0.0f,stam - stamDrainRate) // Drain at normal fly rate
|
||||
else if (state.gliding.get()) min(1.0f,stam - stamDrainRate * glideMod * diveMod) // Gliding drain logic
|
||||
else max(0.0f,stam - stamDrainRate * 0.5f) // if hovering half the stam drain
|
||||
|
||||
state.stamina.set(newStam)
|
||||
}
|
||||
@@ -103,7 +141,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Vec2 {
|
||||
return Vec2(driver.xRot * 0.5f, driver.yRot)
|
||||
return Vec2.ZERO//Vec2(driver.xRot * 0.5f, driver.yRot)
|
||||
}
|
||||
|
||||
override fun velocity(
|
||||
@@ -113,28 +151,26 @@ 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
|
||||
val controller = (vehicle as? OrientationControllable)?.orientationController
|
||||
if (controller == null || controller.orientation == null) return Vec3.ZERO
|
||||
|
||||
//Perform ride velocity update
|
||||
calculateRideSpaceVel(settings, state, vehicle, driver)
|
||||
|
||||
//Translate ride space velocity to world space velocity.
|
||||
val controller = (driver as? OrientationControllable)?.orientationController
|
||||
if (controller != null) {
|
||||
//Need to deadzone this when straight up or down
|
||||
// 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
|
||||
|
||||
upForce += -1.0 * sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().z
|
||||
forwardForce += cos(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().z
|
||||
// Convert the local velocity vector into a world vector
|
||||
val localVelVec = Vector3f(
|
||||
state.rideVelocity.get().x.toFloat(),
|
||||
(state.rideVelocity.get().y).toFloat(),
|
||||
state.rideVelocity.get().z.toFloat() * -1.0f // Flip the z axis to make this left handed
|
||||
)
|
||||
// Unrotate preemptively as this vector gets rotatee later down the line in MC logic.
|
||||
var worldVelVec = localVelVec.mul(controller.orientation).toVec3d().yRot(vehicle.yRot.toRadians())
|
||||
worldVelVec = worldVelVec.add(0.0, extraDownwardForce, 0.0) // Add the stamina depletion force to bring the ride down.
|
||||
|
||||
upForce += cos(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().y
|
||||
forwardForce += sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().y
|
||||
}
|
||||
|
||||
val velocity = Vec3(state.rideVelocity.get().x , upForce, forwardForce)
|
||||
return velocity
|
||||
return worldVelVec
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -142,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
|
||||
|
||||
@@ -155,8 +191,8 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
newVelocity = newVelocity.normalize().scale(vehicle.deltaMovement.length())
|
||||
}
|
||||
|
||||
//speed up and slow down based on input
|
||||
if (driver.zza != 0.0f && state.stamina.get() > 0.0) {
|
||||
// speed up and slow down based on input
|
||||
if (driver.zza != 0.0f && state.stamina.get() > 0.0 && newVelocity.length() < glideTopSpeed) {
|
||||
//make sure it can't exceed top speed
|
||||
val forwardInput = when {
|
||||
state.stamina.get() == 0.0f -> 0.0
|
||||
@@ -173,7 +209,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
activeInput = true
|
||||
}
|
||||
|
||||
val controller = (driver as? OrientationControllable)?.orientationController
|
||||
val controller = (vehicle as? OrientationControllable)?.orientationController
|
||||
if (controller != null) {
|
||||
//Base glide speed change on current pitch of the ride.
|
||||
glideSpeedChange = sin(Math.toRadians(controller.pitch.toDouble()))
|
||||
@@ -228,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)
|
||||
@@ -249,15 +285,13 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (Cobblemon.config.disableRoll) return Vec3.ZERO
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
if (vehicle !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (vehicle as OrientationControllable).orientationController
|
||||
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalBird.handlingExpr!!)
|
||||
|
||||
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()))
|
||||
val trueYawDelt = yawDeltaDeg * abs(cos(Math.toRadians(controller.pitch.toDouble()))).coerceIn(-1.0, 1.0)
|
||||
|
||||
// Dampen yaw when upside down
|
||||
val yawDampen = (1 - abs(min(cos(controller.roll.toRadians()),0.0f)))
|
||||
@@ -266,8 +300,13 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
// Correct orientation when at low speeds
|
||||
correctOrientation(settings, state, vehicle, controller, deltaTime)
|
||||
|
||||
val pCorr = (state.currPitchCorrectionForce.get() * deltaTime).toFloat()
|
||||
val rCorr = (state.currRollCorrectionForce.get() * deltaTime).toFloat()
|
||||
controller.applyGlobalPitch(pCorr)
|
||||
controller.rotateRoll(rCorr)
|
||||
|
||||
//yaw, pitch, roll
|
||||
return Vec3(0.0, state.currPitchCorrectionForce.get(), state.currRollCorrectionForce.get())
|
||||
return Vec3(0.0, 0.0, 0.0)
|
||||
}
|
||||
|
||||
private fun correctOrientation(
|
||||
@@ -278,39 +317,47 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
deltaTime: Double
|
||||
) {
|
||||
// Calculate correcting roll force.
|
||||
// Mult by cos(pitch) to ensure that it doesn't roll correct at when diving or climbing
|
||||
val rollCorrectionTimer = vehicle.runtime.resolveDouble(settings.timeToRollCorrect)
|
||||
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 maxRollForce = maxRollCorrectionRate / 40.0
|
||||
val rollArrivalDeg = 90.0
|
||||
if(state.rideVelocity.get().length() < 0.1 || state.noInputTime.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()
|
||||
val rollCorrectionTimer = vehicle.runtime.resolveDouble(settings.timeToRollCorrect ?: globalBird.timeToRollCorrect!!)
|
||||
// If the ride is moving then have the correction rate be an equal proportion of:
|
||||
// - how rolled are you
|
||||
// - how long has it been since last mouse input (higher is stronger correction)
|
||||
// Also the correction is proportional as a whole to how NOT pitched are you so you don't spin endlessly when looking up or down
|
||||
val howRolledAmI = sqrt(abs((cos(controller.roll.toRadians()) - 1.0f) * 0.5f)) // sqrt so it trends towards one pretty quickly
|
||||
val maxRollCorrectionRate =
|
||||
if (state.rideVelocity.get().length() < 0.2) 45.0f
|
||||
else 45.0f * cos(controller.pitch.toRadians()).pow(2) * (
|
||||
howRolledAmI*1.0f +
|
||||
(state.noInputTimeRoll.get() - rollCorrectionTimer).coerceIn(0.0, 1.0).pow(2).toFloat()*0.5f
|
||||
)
|
||||
val maxRollForce = maxRollCorrectionRate / 40.0f
|
||||
val rollArrivalDeg = 35.0f
|
||||
val desiredRoll = 0.0f
|
||||
|
||||
// Ensure the 'steering' correction force doesn't exceed our maxForce
|
||||
if (abs(steeredRollForce) > maxRollForce) {
|
||||
steeredRollForce = steeredRollForce.sign * maxRollForce
|
||||
}
|
||||
|
||||
// Apply the 'steered' force (its really not a steering force I need a better name)
|
||||
state.currRollCorrectionForce.set(
|
||||
state.currRollCorrectionForce.get() + steeredRollForce
|
||||
)
|
||||
} else {
|
||||
state.currRollCorrectionForce.set(
|
||||
//reduce rollCorrection gradually
|
||||
lerp(state.currRollCorrectionForce.get(), 0.0, deltaTime * 0.96)
|
||||
)
|
||||
// Calculate the signed error (how far and in what direction)
|
||||
val rollError = Mth.wrapDegrees(desiredRoll.angleDifference(controller.roll))
|
||||
// Use the absolute error to calculate the magnitude of the influence
|
||||
val influenceMagnitude = (min(rollArrivalDeg, abs(rollError)) / rollArrivalDeg)
|
||||
val desiredRollForce = maxRollCorrectionRate * influenceMagnitude * -rollError.sign
|
||||
var steeredRollForce = desiredRollForce - state.currRollCorrectionForce.get()
|
||||
// Ensure the 'steering' correction force doesn't exceed our maxForce
|
||||
if (abs(steeredRollForce) > maxRollForce) {
|
||||
steeredRollForce = steeredRollForce.sign * maxRollForce
|
||||
}
|
||||
// Apply the 'steered' force (its really not a steering force I need a better name)
|
||||
state.currRollCorrectionForce.set(
|
||||
state.currRollCorrectionForce.get() + steeredRollForce
|
||||
)
|
||||
|
||||
//Calculate correcting pitch force.
|
||||
val maxPitchCorrectionRate = 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
|
||||
val maxPitchCorrectionRate = if (state.currUpsideDownForce.get() != 0.0) 15.0f * state.currUpsideDownForce.get().toFloat() else 30.0f
|
||||
val maxPitchForce = maxPitchCorrectionRate / 40.0f
|
||||
val pitchArrivalDeg = 35.0f
|
||||
val desiredPitch = if (state.currUpsideDownForce.get() != 0.0) 90.0f else 0.0f
|
||||
|
||||
if((state.rideVelocity.get().length() < 0.1) || state.currUpsideDownForce.get() != 0.0) {
|
||||
val pitchErrror = Mth.wrapDegrees(desiredPitch.angleDifference(controller.pitch))
|
||||
val arrivalInfluence = (min(pitchArrivalDeg,abs(pitchErrror)) / pitchArrivalDeg)
|
||||
val desiredPitchForce = maxPitchCorrectionRate * arrivalInfluence * -pitchErrror.sign
|
||||
var steeredPitchForce = desiredPitchForce - state.currPitchCorrectionForce.get()
|
||||
|
||||
// Ensure the 'steering' correction force doesn't exceed our maxForce
|
||||
@@ -341,89 +388,38 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
mouseXSmoother: SmoothDouble,
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
// 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)
|
||||
} else {
|
||||
state.noInputTime.set(0.0)
|
||||
}
|
||||
|
||||
return when {
|
||||
Cobblemon.config.disableRoll -> noRollRotation(
|
||||
settings,
|
||||
state,
|
||||
vehicle,
|
||||
driver,
|
||||
mouseY,
|
||||
mouseX,
|
||||
mouseYSmoother,
|
||||
mouseXSmoother,
|
||||
sensitivity,
|
||||
deltaTime
|
||||
)
|
||||
else -> rollRotation(
|
||||
settings,
|
||||
state,
|
||||
vehicle,
|
||||
driver,
|
||||
mouseY,
|
||||
mouseX,
|
||||
mouseYSmoother,
|
||||
mouseXSmoother,
|
||||
sensitivity,
|
||||
deltaTime
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun noRollRotation(
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
mouseX: Double,
|
||||
mouseYSmoother: SmoothDouble,
|
||||
mouseXSmoother: SmoothDouble,
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
|
||||
// Set roll to zero if transitioning to noroll config
|
||||
controller.rotateRoll(controller.roll * -1.0f)
|
||||
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
|
||||
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult)
|
||||
|
||||
//Smooth out mouse input.
|
||||
val smoothingSpeed = 4.0
|
||||
val mouseXc = ((mouseX).coerceIn(-60.0, 60.0) / 60.0) * mouseInputMult
|
||||
val mouseYc = ((mouseY).coerceIn(-60.0, 60.0) / 60.0) * mouseInputMult
|
||||
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1, deltaTime * smoothingSpeed);
|
||||
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1, deltaTime * smoothingSpeed);
|
||||
|
||||
//Give the ability to yaw with x mouse input when at low speeds.
|
||||
val yawForce = xInput * handling //* ( 1.0 - min(sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed)), 0.5))
|
||||
|
||||
//Apply yaw globally as we don't want roll or pitch changes due to local yaw when looking up or down.
|
||||
controller.applyGlobalYaw(yawForce.toFloat() * abs(cos(controller.pitch.toRadians())).pow(2))
|
||||
|
||||
var pitchRot = yInput * handling
|
||||
|
||||
// Pitch up globally
|
||||
if (abs(controller.pitch + pitchRot) >= 89.5 ) {
|
||||
pitchRot = 0.0
|
||||
mouseYSmoother.reset()
|
||||
} else {
|
||||
controller.applyGlobalPitch(pitchRot.toFloat() * -1.0f)
|
||||
// Begin the roll correction time counter if not enough mouse input
|
||||
// is detected
|
||||
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)
|
||||
}
|
||||
|
||||
//yaw, pitch, roll
|
||||
return Vec3.ZERO
|
||||
if (abs(mouseY) < 1) {
|
||||
state.noInputTimePitch.set(state.noInputTimePitch.get() + deltaTime)
|
||||
} else {
|
||||
state.noInputTimePitch.set(0.0)
|
||||
}
|
||||
|
||||
return rollRotation(
|
||||
settings,
|
||||
state,
|
||||
vehicle,
|
||||
driver,
|
||||
mouseY,
|
||||
mouseX,
|
||||
mouseYSmoother,
|
||||
mouseXSmoother,
|
||||
sensitivity,
|
||||
deltaTime
|
||||
)
|
||||
}
|
||||
|
||||
fun rollRotation(
|
||||
@@ -438,47 +434,57 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
if (vehicle !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (vehicle as OrientationControllable).orientationController
|
||||
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) / 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!!)
|
||||
// Applied to mouseX to prevent turning upside down when trying to roll hard.
|
||||
val rollDegreeDebuff = if(mouseX.sign.toFloat() == controller.roll.sign) 1 - 0.9f*sqrt(abs(sin(controller.roll.toRadians())))*sqrt(abs(cos(controller.pitch))) else 1.0f
|
||||
|
||||
//Smooth out mouse input.
|
||||
val smoothingSpeed = 4.0
|
||||
val mouseXc = (mouseX).coerceIn(-45.0, 45.0) * mouseInputMult
|
||||
val smoothingSpeed = 2.0
|
||||
val mouseXc = (mouseX).coerceIn(-45.0, 45.0) * mouseInputMult * rollDegreeDebuff
|
||||
// When upside down apply a pitching force to nudge the player towards a dive. This reduces the 'weirdness'
|
||||
// of being upside down
|
||||
val rolledOverPitchCorrection = -8*sqrt(abs(min(cos(controller.roll.toRadians()),0.0f))) * (cos(controller.pitch.toRadians())).pow(2)
|
||||
val mouseYc = ((mouseY).coerceIn(-45.0, 45.0) * mouseInputMult) + rolledOverPitchCorrection
|
||||
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1, deltaTime * smoothingSpeed);
|
||||
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1, deltaTime * smoothingSpeed);
|
||||
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1, deltaTime * smoothingSpeed)
|
||||
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1, deltaTime * smoothingSpeed)
|
||||
val currSpeed = state.rideVelocity.get().length()
|
||||
val currHorzSpeed = state.rideVelocity.get().horizontalDistance()
|
||||
val hoverSpeed = 0.1
|
||||
val pitchDamping = abs(cos(controller.pitch.toRadians())).pow(2) // Helper value used when wanting to discard the influence of another value at steep pitches
|
||||
|
||||
// Yaw locally when at or below hoverSpeed
|
||||
val yawDampen = abs(cos(controller.pitch.toRadians())).pow(2) * abs(cos(controller.roll.toRadians())).pow(2)
|
||||
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) * yawDampen
|
||||
// Yaw locally when at or below hoverSpeed but not when pitched up or rolled over
|
||||
val globalYaw = xInput * (1 - sqrt(RidingBehaviour.scaleToRange(currHorzSpeed, hoverSpeed, topSpeed))).coerceIn(0.0, 1.0)
|
||||
controller.applyGlobalYaw(globalYaw.toFloat())
|
||||
|
||||
// Pitch locally up or down depending upon a number of factors:
|
||||
// - Reduce pitch substantially when rolled and in a steep yaw. This prevents the player from ignoring a slow handling stat
|
||||
// - Do not pitch at all when at or below hover speed
|
||||
// - pitch faster at higher speeds
|
||||
val p = 1.0 - vehicle.runtime.resolveDouble(settings.horizontalPitchExpr)
|
||||
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.0 // if ( currSpeed > hoverSpeed * 2) 0.4*(abs(yInput - localPitch) * controller.roll.sign * -1.0) else 0.0
|
||||
val rollForce = if (currSpeed > hoverSpeed) xInput * sqrt(abs(RidingBehaviour.scaleToRange(currSpeed, hoverSpeed, topSpeed)).coerceIn(0.0,1.0)) + pitchInfluencedRollCorrection
|
||||
val pitchInfluencedRollCorrection = 0.0
|
||||
val howRolledAmI = (( cos(controller.roll.toRadians()) - 1).coerceIn(-1.0f, 0.0f) * -2) //Coerce into how much roll you want to let in in the deadzone
|
||||
val isInputtingTowardsRoll = if ( xInput.sign == controller.roll.sign.toDouble()) 1.0 else 0.0
|
||||
val inputInRolledDir = if (isInputtingTowardsRoll == 1.0) 1.0 else -cos(controller.roll.toRadians()).coerceIn(-1.0f, 0.0f).toDouble()
|
||||
val rollDampen = (1 - howRolledAmI.coerceIn(0.0f, 0.7f) * pitchDamping * inputInRolledDir)
|
||||
val rollForce = if (currHorzSpeed > hoverSpeed) xInput * sqrt(abs(RidingBehaviour.scaleToRange(currHorzSpeed, hoverSpeed, topSpeed)).coerceIn(0.0,1.0)) * rollDampen + pitchInfluencedRollCorrection
|
||||
else 0.0
|
||||
|
||||
// When already rolled significantly part of the roll force should turn to local yaw force
|
||||
val localYawForce = rollForce * abs(sin(controller.roll.toRadians())).pow(2) * pitchDamping * isInputtingTowardsRoll
|
||||
|
||||
//yaw, pitch, roll
|
||||
return Vec3(localYaw, localPitch, rollForce)
|
||||
return Vec3(localYawForce, localPitch, rollForce)
|
||||
}
|
||||
|
||||
|
||||
@@ -525,12 +531,12 @@ 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 currSpeed = state.rideVelocity.get().length()
|
||||
|
||||
// Must I ensure that topspeed is greater than minimum?
|
||||
val normalizedGlideSpeed = RidingBehaviour.scaleToRange(currSpeed, topSpeed, glideTopSpeed)
|
||||
val speedOverTopSpeed = max( 0.0, currSpeed - topSpeed)
|
||||
|
||||
val normalizedGlideSpeed = RidingBehaviour.scaleToRange(speedOverTopSpeed, 0.0, 0.3)
|
||||
|
||||
// Only ever want the fov change to be a max of 0.2 and for it to have non linear scaling.
|
||||
return 1.0f + normalizedGlideSpeed.pow(2).toFloat() * 0.2f
|
||||
@@ -585,7 +591,18 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
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(
|
||||
@@ -603,57 +620,78 @@ 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
|
||||
|
||||
// debuff to stamina drain (increases stamina drain?) depending on how horizontal you're looking (so just drain more when climbing in altitude)
|
||||
// So for the base 1.5: If looking straight up and holding forward you drain at 150 percent the rate you would if
|
||||
// you were horizontal.
|
||||
var stamDrainHorzMod: Expression? = null
|
||||
private set
|
||||
|
||||
// long ass variable name
|
||||
// Percent of stamina drain during glide (percent of normal drain rate) So base is 25 percent. (change to 0 if you want no drain or negative if you want regen)
|
||||
var stamDrainPercRateGlide: Expression? = null
|
||||
private set
|
||||
|
||||
// Percent of stamina drain as a factor of how hard you're diving. So if its zero then at full dive you drain no stam.
|
||||
// This stacks with `stamDrainPercRateGlide`
|
||||
// This is coerced between 1 and 0 or things would get weird.
|
||||
var stamDrainPerRateDive: 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(stamDrainHorzMod)
|
||||
buffer.writeNullableExpression(stamDrainPercRateGlide)
|
||||
buffer.writeNullableExpression(stamDrainPerRateDive)
|
||||
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()
|
||||
stamDrainHorzMod = buffer.readNullableExpression()
|
||||
stamDrainPercRateGlide = buffer.readNullableExpression()
|
||||
stamDrainPerRateDive = buffer.readNullableExpression()
|
||||
timeToRollCorrect = buffer.readNullableExpression()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -662,11 +700,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())
|
||||
}
|
||||
|
||||
@@ -681,7 +720,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 {
|
||||
@@ -695,7 +735,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 {
|
||||
@@ -704,3 +745,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()
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user