Compare commits
823
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78dfb30759 | ||
|
|
db10ae2183 | ||
|
|
548b8522a8 | ||
|
|
4791b146dc | ||
|
|
b7a9931393 | ||
|
|
9a35a11440 | ||
|
|
6d4a50cee2 | ||
|
|
818a637897 | ||
|
|
4c96ab9b28 | ||
|
|
d5a316b9ae | ||
|
|
87e32f0e9b | ||
|
|
53372e5400 | ||
|
|
2fbb1f354c | ||
|
|
bf06476558 | ||
|
|
2e3a742477 | ||
|
|
94c7542d2c | ||
|
|
fa2b6b5996 | ||
|
|
8e77dc70f6 | ||
|
|
51e3748adb | ||
|
|
d8cf2701c7 | ||
|
|
31b7c83369 | ||
|
|
a170123e2c | ||
|
|
c3c17a228e | ||
|
|
41d607ad56 | ||
|
|
58b3596948 | ||
|
|
ed4456d756 | ||
|
|
481734e6b1 | ||
|
|
636792fa1f | ||
|
|
ad65002b19 | ||
|
|
903f3b6d5a | ||
|
|
4456743e97 | ||
|
|
d0989e5ece | ||
|
|
ce3f703de2 | ||
|
|
180229db61 | ||
|
|
b6e10c016e | ||
|
|
8ed68742df | ||
|
|
ce09236dab | ||
|
|
0bb968dd73 | ||
|
|
f3d4cb34e0 | ||
|
|
16aa930735 | ||
|
|
55733e8ad5 | ||
|
|
67591efdad | ||
|
|
5d300c8157 | ||
|
|
3b6e1bd429 | ||
|
|
c77198a845 | ||
|
|
bd32c07cc1 | ||
|
|
1eb7354fc0 | ||
|
|
52ad6a3250 | ||
|
|
aa57bbbf34 | ||
|
|
26278eeb46 | ||
|
|
99cfc4f147 | ||
|
|
89281a1f6d | ||
|
|
ad044fb05c | ||
|
|
7088ce3828 | ||
|
|
fc6a712906 | ||
|
|
1d0397c399 | ||
|
|
8ad443a4c5 | ||
|
|
0d1f9a0093 | ||
|
|
7587344cb4 | ||
|
|
f0926d7524 | ||
|
|
7c3b17d841 | ||
|
|
263e3cd79f | ||
|
|
2ba8062915 | ||
|
|
edc298bf64 | ||
|
|
aa1158fcfe | ||
|
|
ab40ae81c4 | ||
|
|
b5df1878ed | ||
|
|
800965cb40 | ||
|
|
b00f1d9352 | ||
|
|
a588cfadf8 | ||
|
|
2eb4062f67 | ||
|
|
43321d8872 | ||
|
|
a5ea584b75 | ||
|
|
6a9edc2e67 | ||
|
|
12377deff7 | ||
|
|
583377d39b | ||
|
|
2c1da8df06 | ||
|
|
737ddd973b | ||
|
|
d749b04261 | ||
|
|
93deb3a669 | ||
|
|
dca45a95e9 | ||
|
|
0412f16460 | ||
|
|
efd4a72287 | ||
|
|
a62e5a6571 | ||
|
|
0ec337ee6c | ||
|
|
76df0a429d | ||
|
|
08ea9378de | ||
|
|
c2bd21ab96 | ||
|
|
5db8ccfad4 | ||
|
|
aa206d386a | ||
|
|
bba8a12a2a | ||
|
|
a235af6c01 | ||
|
|
a666a74a32 | ||
|
|
c285120ff4 | ||
|
|
dbd2890120 | ||
|
|
a5db46cdc5 | ||
|
|
765cf9b96a | ||
|
|
efe7952869 | ||
|
|
9e434de285 | ||
|
|
712fe2c250 | ||
|
|
5db5a2ed35 | ||
|
|
49c96e6fc8 | ||
|
|
d56c9a3f5a | ||
|
|
6c4c973ed8 | ||
|
|
a117303147 | ||
|
|
dbf248a508 | ||
|
|
56d75b14fa | ||
|
|
87311e2d7d | ||
|
|
1b525ca3a5 | ||
|
|
78ec9d3723 | ||
|
|
afa5d22c8e | ||
|
|
1cdf664dce | ||
|
|
bd63fb686d | ||
|
|
0f9a422d44 | ||
|
|
32d18b47f9 | ||
|
|
c618bf95fa | ||
|
|
18a8aa12d1 | ||
|
|
308ca6369d | ||
|
|
79fd9f4e04 | ||
|
|
38219bbca2 | ||
|
|
87de288777 | ||
|
|
b6f1d6c62e | ||
|
|
a7bd70e334 | ||
|
|
43310d3a3b | ||
|
|
d12bdc3745 | ||
|
|
08c8623951 | ||
|
|
6ae375cf4e | ||
|
|
29c65c18ff | ||
|
|
b90384fe08 | ||
|
|
d2ff7e3748 | ||
|
|
ea6452287b | ||
|
|
b1f7731563 | ||
|
|
5ad881235f | ||
|
|
05796998e8 | ||
|
|
aaf1aad9a4 | ||
|
|
b315fdcadc | ||
|
|
5b95f0a1c7 | ||
|
|
7641a839c2 | ||
|
|
6ed6ca6937 | ||
|
|
be106a465e | ||
|
|
e7c0374c83 | ||
|
|
7fd1a443ac | ||
|
|
31359077ed | ||
|
|
7f683bd618 | ||
|
|
012f15878e | ||
|
|
2ea5eb2823 | ||
|
|
f406b9695d | ||
|
|
f6dbe40246 | ||
|
|
a1f6e47f42 | ||
|
|
04f5dbfca0 | ||
|
|
fd940b5549 | ||
|
|
3e37c1af32 | ||
|
|
8b6b9ffede | ||
|
|
329a13867c | ||
|
|
e109b0fc9f | ||
|
|
5de7f96fef | ||
|
|
5c6632c3fb | ||
|
|
ebde125895 | ||
|
|
c45c74c508 | ||
|
|
cd6bc23825 | ||
|
|
3df95df1f9 | ||
|
|
ad3ee75c22 | ||
|
|
ce3935950a | ||
|
|
e1380df7d1 | ||
|
|
cf8cfb8dad | ||
|
|
3729728124 | ||
|
|
1f62f447b5 | ||
|
|
3092abfdd0 | ||
|
|
3150f33e9e | ||
|
|
40d7e7bccb | ||
|
|
cdbe61843f | ||
|
|
b13c168347 | ||
|
|
f77fa82e4f | ||
|
|
2e2b10311d | ||
|
|
2af5de8ce4 | ||
|
|
674e1df96d | ||
|
|
a4a2ae6133 | ||
|
|
66eea576f3 | ||
|
|
53a516266f | ||
|
|
f732442133 | ||
|
|
34cad13f57 | ||
|
|
e879a755a4 | ||
|
|
2ac9c827d4 | ||
|
|
a0ef46a18c | ||
|
|
9dc15c9096 | ||
|
|
346e2cd094 | ||
|
|
f7794d50dd | ||
|
|
3b8d67ce64 | ||
|
|
105d3a7d90 | ||
|
|
8a685cefc0 | ||
|
|
3ff536dc02 | ||
|
|
638ec03244 | ||
|
|
37438c9fdd | ||
|
|
cbc4b52193 | ||
|
|
9a760d8eae | ||
|
|
b7d26d99cd | ||
|
|
04c8b1e95b | ||
|
|
e4149b2a5e | ||
|
|
b446af166d | ||
|
|
3cdaa513f8 | ||
|
|
75e0da38c4 | ||
|
|
ca43fcf3a2 | ||
|
|
4f71e11383 | ||
|
|
5b56a5758a | ||
|
|
424889e820 | ||
|
|
6d3f00cd58 | ||
|
|
1d375bdffa | ||
|
|
e563bbee49 | ||
|
|
e2ccb20402 | ||
|
|
8fe2dc8304 | ||
|
|
4ddc167254 | ||
|
|
e3502ec320 | ||
|
|
d8d0ade350 | ||
|
|
e2ac4449b3 | ||
|
|
ecec7ccee3 | ||
|
|
228282f083 | ||
|
|
3bc7d39800 | ||
|
|
3dc5873642 | ||
|
|
a5c3d2f800 | ||
|
|
00d2bbbe42 | ||
|
|
8d6291c4c2 | ||
|
|
2b77d030e8 | ||
|
|
029927af6a | ||
|
|
bf791dc4d1 | ||
|
|
56ad481304 | ||
|
|
cc7a9ae44a | ||
|
|
664f79f3a7 | ||
|
|
9a2ed78b0d | ||
|
|
558907eb5c | ||
|
|
370b689ab4 | ||
|
|
e2740babc5 | ||
|
|
3393ab17d8 | ||
|
|
645029a9c7 | ||
|
|
f168744ccd | ||
|
|
67210cb5f9 | ||
|
|
779fbb4f38 | ||
|
|
ce04f546e7 | ||
|
|
a36437ea16 | ||
|
|
aa5af84283 | ||
|
|
e3031d2618 | ||
|
|
9e44ba1fb1 | ||
|
|
1af66d4175 | ||
|
|
144213cee8 | ||
|
|
cc95b4450e | ||
|
|
4bda306a3b | ||
|
|
2ee556dc31 | ||
|
|
4457df9aba | ||
|
|
6dfb073242 | ||
|
|
e1bd0d688f | ||
|
|
0bce2d8645 | ||
|
|
dfe240f1d3 | ||
|
|
f1ac0fe99f | ||
|
|
c2f575bf22 | ||
|
|
5f76966678 | ||
|
|
beddea1ecb | ||
|
|
04236fcf33 | ||
|
|
05e87b7faf | ||
|
|
e9d298834a | ||
|
|
ce113cbb80 | ||
|
|
61ebe7124a | ||
|
|
462c0b35b8 | ||
|
|
0b55c88ec4 | ||
|
|
942da6b2f6 | ||
|
|
224c38e0a7 | ||
|
|
056917f7df | ||
|
|
2aa9379987 | ||
|
|
3a6a3253a3 | ||
|
|
1580a06a8e | ||
|
|
e2bfdd3e76 | ||
|
|
1d24565aa6 | ||
|
|
2d80402688 | ||
|
|
40b351a303 | ||
|
|
dc32bd1845 | ||
|
|
a096485155 | ||
|
|
f1ae7c4476 | ||
|
|
bcfac2f68a | ||
|
|
ff1902770c | ||
|
|
ebb01d4fcc | ||
|
|
20614b6b07 | ||
|
|
b32b5a66a4 | ||
|
|
ad92804fce | ||
|
|
ca93955134 | ||
|
|
01d99676f9 | ||
|
|
2c8fd29cf1 | ||
|
|
a9eec5f568 | ||
|
|
e54eaa728b | ||
|
|
4e550151e5 | ||
|
|
67f5259742 | ||
|
|
eb783e13ba | ||
|
|
68237e7161 | ||
|
|
ec9f7665e2 | ||
|
|
f19ae0120e | ||
|
|
d674ff1d40 | ||
|
|
37d6f58536 | ||
|
|
2ce23e072d | ||
|
|
d37dfee032 | ||
|
|
ee567a63b6 | ||
|
|
868dd9cc44 | ||
|
|
3f9b0002a9 | ||
|
|
e07b15c8fc | ||
|
|
44c5c21fb5 | ||
|
|
9d13e86487 | ||
|
|
afa395cfeb | ||
|
|
831903b8e3 | ||
|
|
d382f48e2d | ||
|
|
2b2b44a535 | ||
|
|
ac9d85929e | ||
|
|
88c981bd6a | ||
|
|
f0baa21b1f | ||
|
|
1d5d78b953 | ||
|
|
2a62b49eab | ||
|
|
f771248936 | ||
|
|
35850ea996 | ||
|
|
caaccf0e67 | ||
|
|
e5de8bd26f | ||
|
|
9528d375f4 | ||
|
|
9aac7dc8e3 | ||
|
|
0bebd2a71e | ||
|
|
55874e6a84 | ||
|
|
cda0e10524 | ||
|
|
153f888cf5 | ||
|
|
6972b7d49d | ||
|
|
d7f442e2d0 | ||
|
|
cc15c0a487 | ||
|
|
a37828e288 | ||
|
|
91d00cb864 | ||
|
|
4ee628ea7a | ||
|
|
64c0001aff | ||
|
|
320b2ff528 | ||
|
|
cf8a1f5447 | ||
|
|
696d98f7cd | ||
|
|
909b90aea1 | ||
|
|
04296ae04e | ||
|
|
025dea4201 | ||
|
|
0b392c66eb | ||
|
|
5733074bd8 | ||
|
|
a0630ff738 | ||
|
|
5b5de641cb | ||
|
|
5a8101e535 | ||
|
|
054c841ec7 | ||
|
|
b02ac04fe3 | ||
|
|
57b4159691 | ||
|
|
41e3b61657 | ||
|
|
f48cd648fc | ||
|
|
76c457dcbc | ||
|
|
cae828dc48 | ||
|
|
39aa9d3ff1 | ||
|
|
43d4b127ae | ||
|
|
008c75d591 | ||
|
|
c32d8a5389 | ||
|
|
24bbfdf038 | ||
|
|
b3590eb49b | ||
|
|
0b51e0fe48 | ||
|
|
88ff4e37b8 | ||
|
|
68c144e366 | ||
|
|
506b0e8c70 | ||
|
|
5091abc8ef | ||
|
|
2a7503db06 | ||
|
|
1480bd97c2 | ||
|
|
11239ff856 | ||
|
|
eaca59a420 | ||
|
|
6e5a09d74d | ||
|
|
35b7a80501 | ||
|
|
91949414cc | ||
|
|
79d6f4de1f | ||
|
|
3e212a8eb1 | ||
|
|
1084f40282 | ||
|
|
e82609176f | ||
|
|
169464ca47 | ||
|
|
3a3a505e92 | ||
|
|
df727d8619 | ||
|
|
c44e2f43f8 | ||
|
|
7223bd77d9 | ||
|
|
59cb3017a2 | ||
|
|
1f00890cec | ||
|
|
c7e01ae07b | ||
|
|
f0fa87e06a | ||
|
|
b4c211ac08 | ||
|
|
82da0e8337 | ||
|
|
75bb5d8ac7 | ||
|
|
2ae3da859a | ||
|
|
ef3e49529b | ||
|
|
121afaf478 | ||
|
|
a6128cd36a | ||
|
|
c8cd9adb4f | ||
|
|
b2d5b9d6a2 | ||
|
|
ab6051f88a | ||
|
|
98800f695a | ||
|
|
fdebf4db73 | ||
|
|
5f21a50e7e | ||
|
|
0d233f109d | ||
|
|
467a6901c7 | ||
|
|
d4cd6b0ac6 | ||
|
|
ee7e0b2e91 | ||
|
|
1da1cdcb10 | ||
|
|
35c5e2fec6 | ||
|
|
efa1a9f383 | ||
|
|
6bb749d806 | ||
|
|
104755343b | ||
|
|
9e0ab5363f | ||
|
|
19d5903e2c | ||
|
|
dbd55b9382 | ||
|
|
3bb5ecd2b6 | ||
|
|
def44930e4 | ||
|
|
3d7b95d7fe | ||
|
|
0f44b40d21 | ||
|
|
987c1e2244 | ||
|
|
1b184af306 | ||
|
|
fc37cf403b | ||
|
|
ae160cca80 | ||
|
|
ea21596460 | ||
|
|
32233737c1 | ||
|
|
f0ab01ed0c | ||
|
|
3a8354fb1f | ||
|
|
25493b521e | ||
|
|
df5407e200 | ||
|
|
2bdc29873a | ||
|
|
6ca89e1091 | ||
|
|
92c488861c | ||
|
|
259bf693b6 | ||
|
|
43dbfc772e | ||
|
|
ab8dd5f477 | ||
|
|
f1d2145359 | ||
|
|
52a74bd182 | ||
|
|
ef9f84d127 | ||
|
|
d27a30d5f1 | ||
|
|
c1b4d98b6d | ||
|
|
e14e44a1c5 | ||
|
|
eb64aa3207 | ||
|
|
46cc276e2e | ||
|
|
d663461c51 | ||
|
|
8c1d4f9790 | ||
|
|
82bf374cad | ||
|
|
5c9c42705a | ||
|
|
4c77dba0b5 | ||
|
|
dcd431c699 | ||
|
|
165b137de2 | ||
|
|
e40a2386c2 | ||
|
|
8933bab24f | ||
|
|
b7b58019cd | ||
|
|
edffb938d6 | ||
|
|
3a074bff8a | ||
|
|
0e33023517 | ||
|
|
3874e21c65 | ||
|
|
10bfe3b29d | ||
|
|
ff88a3c756 | ||
|
|
5132b47427 | ||
|
|
526b1d145b | ||
|
|
70659cd818 | ||
|
|
acdd6692c8 | ||
|
|
5607eb0bd8 | ||
|
|
5634b94c35 | ||
|
|
272209c268 | ||
|
|
f26b3242d5 | ||
|
|
b6ea8f728e | ||
|
|
e76818d7d8 | ||
|
|
bcf379f218 | ||
|
|
6cb0f296ce | ||
|
|
433f4f6ca5 | ||
|
|
dc8d55a7d2 | ||
|
|
4d47cb8385 | ||
|
|
219bbbe077 | ||
|
|
70704b6330 | ||
|
|
7f511a391c | ||
|
|
8aba2bc279 | ||
|
|
932f366c83 | ||
|
|
434218dda3 | ||
|
|
e4dbba026c | ||
|
|
db1834f6bc | ||
|
|
dbbf96bb36 | ||
|
|
3a23f7a239 | ||
|
|
9f6de64089 | ||
|
|
e86ee8a7d4 | ||
|
|
7829e83ba0 | ||
|
|
ab3747a0c4 | ||
|
|
501d81e4a6 | ||
|
|
f62b566610 | ||
|
|
fe91e912d0 | ||
|
|
32a847af34 | ||
|
|
1d28672978 | ||
|
|
74a2a2b0ee | ||
|
|
354ef66d2a | ||
|
|
a6f1182f02 | ||
|
|
8825e7af3d | ||
|
|
bcf119bf5d | ||
|
|
d9cb59ca37 | ||
|
|
e33960effb | ||
|
|
a20eae7cd8 | ||
|
|
4adf40c5ec | ||
|
|
a27e0ffc7f | ||
|
|
3bea35eea2 | ||
|
|
3db7ecd71f | ||
|
|
723eccc88a | ||
|
|
75785e0706 | ||
|
|
f21da15591 | ||
|
|
1be44c75d5 | ||
|
|
cfea52f488 | ||
|
|
53e94365ad | ||
|
|
e393fd7a47 | ||
|
|
63eb0a1d9c | ||
|
|
680bc1d205 | ||
|
|
7a5908a0d3 | ||
|
|
c234291e8c | ||
|
|
b97115f1bd | ||
|
|
8720a682c3 | ||
|
|
fd0d811b47 | ||
|
|
520341dfd3 | ||
|
|
d66b8d04c7 | ||
|
|
5418407d1e | ||
|
|
4606d9e1c5 | ||
|
|
4f839a2589 | ||
|
|
257c362765 | ||
|
|
009a8c34e5 | ||
|
|
7e0ddb5b79 | ||
|
|
8b5ef32d93 | ||
|
|
6acf2655b8 | ||
|
|
0e40f0f3ad | ||
|
|
dfb2e21721 | ||
|
|
67cda56be5 | ||
|
|
b15545148b | ||
|
|
b37a70c5db | ||
|
|
0b9e0ac9c7 | ||
|
|
f5383665f8 | ||
|
|
35bef5a008 | ||
|
|
17c25def97 | ||
|
|
5f9088dbf5 | ||
|
|
8748ef6884 | ||
|
|
ee270f59e1 | ||
|
|
a2c5866026 | ||
|
|
ab408c4246 | ||
|
|
9e9a98b6f7 | ||
|
|
0b5fbb4d55 | ||
|
|
d7a8e0fe92 | ||
|
|
3bd5867143 | ||
|
|
c8ab370720 | ||
|
|
4a0ab9f092 | ||
|
|
a565f1cdb7 | ||
|
|
3a2075d00c | ||
|
|
9ef3caef53 | ||
|
|
c449b84fff | ||
|
|
bcf429589a | ||
|
|
4282445359 | ||
|
|
3e449ae1f5 | ||
|
|
8510290e87 | ||
|
|
c854fc5745 | ||
|
|
1a3a6443d8 | ||
|
|
b461dbce62 | ||
|
|
86e33b3ffd | ||
|
|
78c34fb7d0 | ||
|
|
2ce993718b | ||
|
|
eee3296120 | ||
|
|
9cf8b57457 | ||
|
|
208937defc | ||
|
|
3e9b6cffdb | ||
|
|
2cf253a1fe | ||
|
|
29bb481363 | ||
|
|
1f3589a592 | ||
|
|
66d697aa69 | ||
|
|
f93ebf19b0 | ||
|
|
b7a30d8f62 | ||
|
|
e75e54e7ff | ||
|
|
65707cbfde | ||
|
|
680bc25983 | ||
|
|
d0c4bd5a16 | ||
|
|
500069e5d8 | ||
|
|
f4a1e67f9b | ||
|
|
c269a808ea | ||
|
|
dbec251408 | ||
|
|
1f9466412b | ||
|
|
aa6038c704 | ||
|
|
0d5c84c396 | ||
|
|
dd9bc0d1d5 | ||
|
|
8aa00c1c31 | ||
|
|
eaec3ea2c8 | ||
|
|
efd32d2eee | ||
|
|
49580bd65b | ||
|
|
7b761bfa8e | ||
|
|
caa46e8bbf | ||
|
|
13ae174c66 | ||
|
|
047172e30f | ||
|
|
923d6f2a4d | ||
|
|
21646d4646 | ||
|
|
11f7158e8e | ||
|
|
5d503a90f7 | ||
|
|
116db0aa7c | ||
|
|
b90e700538 | ||
|
|
57372335b3 | ||
|
|
cd06bc9903 | ||
|
|
a2531d359a | ||
|
|
1ee12c592b | ||
|
|
f52e31c770 | ||
|
|
f5debdb14f | ||
|
|
25544681ae | ||
|
|
3bf777c2a9 | ||
|
|
529df4e8b4 | ||
|
|
d2bc8613c9 | ||
|
|
1706b7c516 | ||
|
|
c1263ad674 | ||
|
|
32ef93d79d | ||
|
|
5a06602503 | ||
|
|
24a68fefa7 | ||
|
|
b0dec54576 | ||
|
|
fb7712a5ce | ||
|
|
b5174686b4 | ||
|
|
26a7bc29b3 | ||
|
|
05cd37eb38 | ||
|
|
3ac3fcbcda | ||
|
|
fa07b93d54 | ||
|
|
27b3884f27 | ||
|
|
15e8f46702 | ||
|
|
e91b1061d3 | ||
|
|
f042d167d1 | ||
|
|
eff3bd91d7 | ||
|
|
50cb670eee | ||
|
|
cfd3eb6c4c | ||
|
|
1391bfee01 | ||
|
|
e8b6bee139 | ||
|
|
a98bc80005 | ||
|
|
06d25a043c | ||
|
|
6aa3b8b92f | ||
|
|
7381b47830 | ||
|
|
b2a704fa12 | ||
|
|
41ed476c00 | ||
|
|
44ddf097d6 | ||
|
|
cb1386fdb3 | ||
|
|
0dafe79032 | ||
|
|
51cd929d1c | ||
|
|
5468d5e888 | ||
|
|
0174b8bc67 | ||
|
|
e3b8fbb973 | ||
|
|
7103df0116 | ||
|
|
42fba85241 | ||
|
|
f9f11c7137 | ||
|
|
a51acead21 | ||
|
|
7a3ab17e57 | ||
|
|
d4e741c614 | ||
|
|
bee1797ff5 | ||
|
|
72fa7d42d5 | ||
|
|
7a4bf4cc5d | ||
|
|
ab66e13c8c | ||
|
|
eaeea493a1 | ||
|
|
0bfaedbf6e | ||
|
|
52f9f45608 | ||
|
|
dfa9dc1e2b | ||
|
|
c8f5538ae8 | ||
|
|
5af5a59ff1 | ||
|
|
eb37ecbe56 | ||
|
|
d7b528aef1 | ||
|
|
2908ca3a89 | ||
|
|
aa04dc077b | ||
|
|
c1c064e657 | ||
|
|
c0a509820f | ||
|
|
aa0b71aad7 | ||
|
|
55c5dfddc4 | ||
|
|
b40c69a1d9 | ||
|
|
ca20f432b3 | ||
|
|
63fe61b913 | ||
|
|
7605b526c8 | ||
|
|
189f39f1c1 | ||
|
|
8c718069c7 | ||
|
|
c2facdf5ce | ||
|
|
0c5dcb26f6 | ||
|
|
b88634691b | ||
|
|
df9dcdd622 | ||
|
|
15943fae47 | ||
|
|
78a71ba252 | ||
|
|
7f1acd16b5 | ||
|
|
be038d2348 | ||
|
|
b3b11012f4 | ||
|
|
095c466b89 | ||
|
|
4c3cd6fdc4 | ||
|
|
2379b22135 | ||
|
|
f36fa861da | ||
|
|
3b4f9d0fba | ||
|
|
42dabbf16a | ||
|
|
07abff7583 | ||
|
|
dbd9bfacc5 | ||
|
|
6ed1ea0cad | ||
|
|
5384fec57e | ||
|
|
1868bcf4d7 | ||
|
|
42059b9931 | ||
|
|
24890225a2 | ||
|
|
e4e32769dc | ||
|
|
3420422a4b | ||
|
|
18564baec7 | ||
|
|
b0c6d83913 | ||
|
|
5016b2b227 | ||
|
|
4727321d47 | ||
|
|
6e96e92d33 | ||
|
|
a8c92ce641 | ||
|
|
032d2e5c95 | ||
|
|
ec9d7d9dae | ||
|
|
217dab4dea | ||
|
|
452a92daa9 | ||
|
|
fb364deff9 | ||
|
|
ac824ce39c | ||
|
|
fa74a1c45a | ||
|
|
1cce1d1a44 | ||
|
|
b7f537e34c | ||
|
|
0311a8a4ef | ||
|
|
14ecc13cb3 | ||
|
|
5baca56d6d | ||
|
|
91c9692db1 | ||
|
|
bb7b1a8dee | ||
|
|
e6322e5fb7 | ||
|
|
3c5f04b52a | ||
|
|
5d78a5b573 | ||
|
|
4f58615065 | ||
|
|
32a4161a69 | ||
|
|
c5708145cd | ||
|
|
118f207adb | ||
|
|
a5826028b3 | ||
|
|
fa3f6053c3 | ||
|
|
bc60fe58b1 | ||
|
|
f9a5e9a9e4 | ||
|
|
6f6ee6f160 | ||
|
|
8a3dbd5e4a | ||
|
|
e052ad2281 | ||
|
|
8938ea13a4 | ||
|
|
907aed4c63 | ||
|
|
60798aae03 | ||
|
|
6f09957fbf | ||
|
|
6fc2344473 | ||
|
|
c82059ae72 | ||
|
|
a080db690f | ||
|
|
d82849ddd3 | ||
|
|
71a3fe6e48 | ||
|
|
5e934ac7a6 | ||
|
|
6aa5bde424 | ||
|
|
fee7488090 | ||
|
|
f5e5e3411e | ||
|
|
818f9365ec | ||
|
|
f81db9df3d | ||
|
|
8162e3a30d | ||
|
|
5a00bbf02d | ||
|
|
ff7fff52ec | ||
|
|
087fab4065 | ||
|
|
bed2897950 | ||
|
|
d51f0711b7 | ||
|
|
728860332d | ||
|
|
fe0ca14b3b | ||
|
|
7e57c3c862 | ||
|
|
0f4fdfcedd | ||
|
|
1cb77f35b5 | ||
|
|
9ac23be59b | ||
|
|
e469409a22 | ||
|
|
28db8e98d7 | ||
|
|
08bf3d4f8f | ||
|
|
88380bfb76 | ||
|
|
83d7cbbefa | ||
|
|
426d7c8e5e | ||
|
|
602d88db5e | ||
|
|
751d3be872 | ||
|
|
358a62d5b1 | ||
|
|
409814823b | ||
|
|
5d8f45b058 | ||
|
|
3001e8bf84 | ||
|
|
80de268987 | ||
|
|
b08004b6c6 | ||
|
|
2783a52495 | ||
|
|
3ae3f0db95 | ||
|
|
a15f026739 | ||
|
|
bc8ed8bb38 | ||
|
|
efa246272b | ||
|
|
e29d84a71a | ||
|
|
6c671b5e08 | ||
|
|
3ffd9ecc28 | ||
|
|
281131f5b3 | ||
|
|
3c67cd2a59 | ||
|
|
74f5c69bb5 | ||
|
|
8f82e1d960 | ||
|
|
c5497cfb2b | ||
|
|
94b6b3b948 | ||
|
|
8855c5ad7f | ||
|
|
5015a0a56c | ||
|
|
ec51fc0701 | ||
|
|
13287e6f76 | ||
|
|
a65dfcd3cd | ||
|
|
18183dd649 | ||
|
|
40c1f074bb | ||
|
|
5d1219e467 | ||
|
|
568773f1a8 | ||
|
|
f0c3cde213 | ||
|
|
def5d09903 | ||
|
|
ad5e2ddc41 | ||
|
|
38cfd7eca8 | ||
|
|
2d2f92c7a7 | ||
|
|
2ba2eea801 | ||
|
|
2bd4d1fb08 | ||
|
|
4c342c2047 | ||
|
|
24cee748d5 | ||
|
|
fbec6f03e7 | ||
|
|
8ab0288ab2 | ||
|
|
cc426a5ca9 | ||
|
|
e4f04b605a | ||
|
|
48441b1232 | ||
|
|
589d3614b8 | ||
|
|
fe386095de | ||
|
|
5640177c99 | ||
|
|
ff8c8b1bfa | ||
|
|
4bccd9b5aa | ||
|
|
8c1c5ac957 | ||
|
|
a20ec4d1c5 | ||
|
|
12b4e58dea | ||
|
|
940ec82182 | ||
|
|
67c01ce5f2 | ||
|
|
e603325eef | ||
|
|
afd09e93eb | ||
|
|
92775a4b8a | ||
|
|
6a2668171e | ||
|
|
5b7dc9f8f4 | ||
|
|
75dc76020f | ||
|
|
56289ce5bf | ||
|
|
e4ec7b0ae7 | ||
|
|
f38ef0b6b9 | ||
|
|
d5bb3e2d97 | ||
|
|
66f6555711 | ||
|
|
472ca9c748 | ||
|
|
054e4dffdc | ||
|
|
be5ee8a6a3 | ||
|
|
44c27be590 | ||
|
|
a704972dea | ||
|
|
0b43342a5c |
No files matched your search
@@ -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.
|
||||
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
### Additions
|
||||
- Added functionality for marks.
|
||||
- Added data for all marks and all title ribbons from Pokémon.
|
||||
- Added data for all marks and ribbons from the mainline Pokémon games.
|
||||
- Implemented fishing, personality, weather, and time related marks, that can rarely be granted to a wild-spawned Pokémon.
|
||||
- Added marks screen within the summary UI.
|
||||
- Added `/givemark <player> <partySlot> <mark>` command to give a mark to a Pokémon.
|
||||
- Added `/takemark <player> <partySlot> <mark>` command to remove a mark from a Pokémon.
|
||||
|
||||
+11
-2
@@ -1,19 +1,28 @@
|
||||
### Additions
|
||||
- Added ability to rename PC Boxes by clicking on the name of a box.
|
||||
- Added box options buttons for boxes, toggleable by clicking the right icon button in the bottom bar.
|
||||
- The button on the right allows for changing the box wallpaper.
|
||||
- The option buttons on the left side allow for sorting the box by name, level, Pokédex number, gender, and type. Shift clicking allows for sorting in reverse order.
|
||||
- The button on the right allows for changing the box wallpaper.
|
||||
- 10 additional color-based wallpapers have been added.
|
||||
- 5 unlockable biome-based wallpapers have been added, which can be unlocked by visiting:
|
||||
- Forest biome
|
||||
- Ocean biome
|
||||
- Cave Biome
|
||||
- The End
|
||||
- The Nether
|
||||
- Added filter functionality in PC UI, which supports `PokemonProperties` (e.g. `shiny=yes` shows all shiny Pokémon).
|
||||
- Names can be filtered by exact or partial matches, e.g. entering "cha" will show Charmander, Charmeleon, etc.
|
||||
- Added `/changewallpaper <player> <boxNumber> <wallpaper>` command to change a box wallpaper through commands.
|
||||
- Added `/renamebox <player> <boxNumber> <name>` command to rename a PC box through commands.
|
||||
- Added IVs and EVs stat displays in PC.
|
||||
- Added ability to change boxes by scrolling.
|
||||
- The displays can be cycled through by scrolling when the display is hovered over.
|
||||
- Added ability to cycle through boxes by scrolling when the box is hovered over.
|
||||
- PC will now open to last box viewed within a session.
|
||||
|
||||
### Changes
|
||||
- Slots in pastures will now have darker backgrounds and italicised Pokémon names if the Pokémon is not owned by the player.
|
||||
- The pasture slot will show the name of the Pokémon owner when hovered if the Pokémon is not owned.
|
||||
- Increased the default PC box count from 30 to 40 to accommodate the fact that we have way more Pokémon now. If you've changed this config option then you'll need to edit it again after starting up with 1.7!
|
||||
|
||||
### Developer
|
||||
- Added `RenamePCBoxEvent.Pre` and `RenamePCBoxEvent.Post` events to prevent players from renaming a box or changing their input.
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
### Fixes
|
||||
- Fixed the "enabled" property in spawn files not actually being respected. Where do they find these devs?
|
||||
- Fixed Terralith's shrubland not counting as plains for spawning purposes the way it was intended to.
|
||||
|
||||
### Developer
|
||||
- Spawning Influences now have the context of what the other buckets are when adjusting bucket weights. This will break existing influences that do bucket weight adjustment.
|
||||
|
||||
+312
-12
@@ -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.
|
||||
@@ -11,11 +12,11 @@
|
||||
- Pokémon's held items can now be rendered, with a visibility toggle in the Summary screen.
|
||||
- Added cosmetic item functionality for Pokémon. Certain cosmetic items can be given to applicable Pokémon via the interact menu.
|
||||
- Added the various log blocks as cosmetic items for Timburr and Komala.
|
||||
- Added cosmetics for Spoink, Gurdurr, Conkeldurr, Squirtle Line, Sneasler, Sandile line, Treecko line, Braixen, Delphox, and Dragonite.
|
||||
- Added Gilded Chest cosmetics for Chest Gimmighoul and Gholdengo
|
||||
- Added `visibility/hidden`, `visibility/hat` and `visibility/face` tags to control where and how certain items are rendered.
|
||||
- Added Pokémon markings, toggleable within the summary.
|
||||
- Added `/boxcount` command to change PC boxes amount
|
||||
- Added cosmetics for Gurdurr, Conkeldurr, Squirtle Line, Sneasler, Sandile line, Treecko line, Braixen, Delphox, and Dragonite.
|
||||
- Added cosmetics for Gurdurr, Conkeldurr, Squirtle Line, Sneasler, Sandle line, Treecko line, Braixen, Delphox, and Dragonite.
|
||||
- Added `/transformmodelpart (position|rotation|scale) <modelPart> <transform: x y z>` command that can add transformations to a pokemon's model part.
|
||||
- The player executing the command must be facing the target pokemon entity. Transformations are not persistent and will revert when resources are reloaded.
|
||||
- Added `legacy` and `special` sourced moves to Pokémon.
|
||||
@@ -23,7 +24,7 @@
|
||||
- Added `translucent_cull` boolean option into resolver's layer to allow for translucent textures with culling
|
||||
- Added [LambDynamicLights](https://modrinth.com/mod/lambdynamiclights) support for items held by Pokémon, evolution stone blocks, evolution stone items, Pokédex, Luminous Moss, Flame Orb, and Magmarizer.
|
||||
- Added the Clear Amulet, Grip Claw, Lagging Tail, Luminous Moss, Metal Alloy, Scroll of Darkness, Scroll of Waters
|
||||
- Added Recipes for Masterpiece Cup, Eject Pack
|
||||
- Added Recipes for Masterpiece Cup, Eject Pack.
|
||||
- Added modification to Minecraft Creative Inventory search to account for item names that contain `poké` when input contains `poke`.
|
||||
- Added Campfire Pot as well as loads of new food items (Poke Puffs, Ponigiri, Sinister Tea, etc)
|
||||
- Added Hearty Grains, a new crop used in the new cooking mechanic
|
||||
@@ -39,6 +40,7 @@
|
||||
- Pokémon with the ability Volt Absorb are immune to lightning damage and receive Regeneration II for a short duration
|
||||
- Ground type Pokémon are immune to lightning damage
|
||||
- Added functionality to Everstone when held by a Pokémon; suppresses evolution notification and hides evolve button in summary interface.
|
||||
- Added optional `filePath` parameter to MoLang functions to allow for global stores (ex. `../global/`). Path must end in a forward slash.
|
||||
- Added new optional property `attachment_options` for most EmitterShapes to be attached to the locator/entities scale, rotation, and/or position. Position is true by default.
|
||||
- Galarica Nut bushes now generate on beaches.
|
||||
- Some Pokémon now pitch their bodies in the direction they're moving.
|
||||
@@ -46,9 +48,11 @@
|
||||
- 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 [LambDynamicLights](https://modrinth.com/mod/lambdynamiclights) support for items held by Pokémon.
|
||||
- Fixed Moon Ball moon phase logic to actually work correctly
|
||||
- Added the Moves tab to the Pokédex, allowing players to view all of the move categories a form can obtain.
|
||||
- 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.
|
||||
|
||||
### Pokémon Added
|
||||
|
||||
@@ -64,6 +68,9 @@
|
||||
- Unown
|
||||
- Houndour
|
||||
- Houndoom
|
||||
- Ho-Oh
|
||||
- Lugia
|
||||
- Alola Bias Pichu (built-in resourcepack)
|
||||
|
||||
#### Gen 3
|
||||
- Azurill
|
||||
@@ -79,8 +86,6 @@
|
||||
- Glalie
|
||||
- Latias
|
||||
- Latios
|
||||
- Swablu
|
||||
- Altaria
|
||||
|
||||
#### Gen 4
|
||||
- Bronzor
|
||||
@@ -139,6 +144,8 @@
|
||||
- Dedenne
|
||||
- Noibat
|
||||
- Noivern
|
||||
- Furfrou
|
||||
- You can change Furfrou's form by using shears on it while it holds a certain dye in its cosmetic slot.
|
||||
|
||||
#### Gen 7
|
||||
- Yungoos
|
||||
@@ -193,9 +200,10 @@
|
||||
### Animation updates for the following Pokémon
|
||||
- Garchomp
|
||||
- Tropius
|
||||
- Torpius
|
||||
- Nosepass
|
||||
- Probopass
|
||||
- Sneasel
|
||||
- Weavile
|
||||
- Sneasler
|
||||
- Braixen
|
||||
- Delphox
|
||||
@@ -217,8 +225,79 @@
|
||||
- Porygon
|
||||
- Porygon2
|
||||
- Porygon-Z
|
||||
- Wattrel
|
||||
- Kilowattrel
|
||||
- Golurk
|
||||
- Spinarak
|
||||
- Ariados
|
||||
- Wyrdeer
|
||||
- Tyrantrum
|
||||
- Pidove
|
||||
- Tranquill
|
||||
- Unfezant
|
||||
- Beldum
|
||||
- Metang
|
||||
- Metagross
|
||||
- Plusle
|
||||
- Minun
|
||||
- Murkrow
|
||||
- Honchkrow
|
||||
- Larvesta
|
||||
- Volcarona
|
||||
- Dwebble
|
||||
- Crustle
|
||||
- Mr. Mime
|
||||
- Flygon
|
||||
- Pichu
|
||||
- Pikachu
|
||||
- Raichu
|
||||
- Alolan Raichu
|
||||
- Espathra
|
||||
- Abra
|
||||
- Kadabra
|
||||
- Alakazam
|
||||
- Deino
|
||||
- Zweilous
|
||||
- Hydreigon
|
||||
- Slaking
|
||||
- Klink
|
||||
- Klang
|
||||
- Klinklang
|
||||
- Baltoy
|
||||
- Claydol
|
||||
- Mamoswine
|
||||
- Rufflet
|
||||
- Braviary
|
||||
- Girafarig
|
||||
- Farigiraf
|
||||
- Rookidee
|
||||
- Corvisquire
|
||||
- Corviknight
|
||||
- Venipede
|
||||
- Whirlipede
|
||||
- Scolipede
|
||||
- Venusaur
|
||||
- Parasect
|
||||
- Bastiodon
|
||||
- Amaura
|
||||
- Varoom
|
||||
- Revavroom
|
||||
- Bouffalant
|
||||
- Lickilicky
|
||||
- Blaziken
|
||||
- Rhyhorn
|
||||
- Rhydon
|
||||
- Rhyperior
|
||||
- Tauros
|
||||
- Tauros - Paldea Aqua
|
||||
- Tauros - Paldea Blaze
|
||||
- Tauros - Paldea Combat
|
||||
- Dewgong
|
||||
|
||||
### Model updates for the following Pokémon
|
||||
- Cleffa
|
||||
- Clefairy
|
||||
- Clefable
|
||||
- Gyarados
|
||||
- Dragonite
|
||||
- Eevee
|
||||
@@ -279,6 +358,51 @@
|
||||
- Klink
|
||||
- Klank
|
||||
- Klinklang
|
||||
- Spinarak
|
||||
- Ariados
|
||||
- Pidove
|
||||
- Tranquill
|
||||
- Unfezant
|
||||
- Sobble
|
||||
- Drizzile
|
||||
- Inteleon
|
||||
- Plusle
|
||||
- Minun
|
||||
- Murkrow
|
||||
- Zorua
|
||||
- Zoroark
|
||||
- Mime.Jr
|
||||
- Mr. Mime
|
||||
- Pichu
|
||||
- Pikachu
|
||||
- Raichu
|
||||
- Alolan Raichu
|
||||
- Dusknoir
|
||||
- Deino
|
||||
- Zweilous
|
||||
- Hydreigon
|
||||
- Dreepy
|
||||
- Drakloak
|
||||
- Dragapult
|
||||
- Mamoswine
|
||||
- Rookidee
|
||||
- Corvisquire
|
||||
- Corviknight
|
||||
- Venipede
|
||||
- Whirlipede
|
||||
- Scolipede
|
||||
- Farigiraf
|
||||
- Staryu
|
||||
- Starmie
|
||||
- Gimmighoul
|
||||
- Gholdengo
|
||||
- Starly
|
||||
- Staravia
|
||||
- Staraptor
|
||||
- Varoom
|
||||
- Revavroom
|
||||
- Nickit
|
||||
- Thievul
|
||||
|
||||
### Changes
|
||||
- Changed pokemon caught and seen count to update based on the current pokedex being looked
|
||||
@@ -332,9 +456,17 @@
|
||||
- Updated Pokémon state icons shown in party interfaces
|
||||
- MoLang `face` function can now be run on any `PosableEntity` (Like Pokémon!) instead of just NPCs.
|
||||
- MoLang `run_action_effect` now works on Pokémon.
|
||||
- Changed MoLang entity function `is_standing_on` to allow for block tags in the list.
|
||||
- Changed MoLang entity function `is_standing_on` to allow for block tags in the list.
|
||||
- Added entity (as `q.entity`) that executed the command to the `executemolangscript` command.
|
||||
- Refactored the following events to `.Pre` and `.Post` for consistency:
|
||||
- `PokemonSentEvent`
|
||||
- `ExperienceGainedEvent`
|
||||
- `BattleStartedEvent`
|
||||
- Updated `PokemonSentEvent` parameters to include the Position and Level of the Pokémon being sent out.
|
||||
- Updated `EvolutionCompleteEvent` parameters to include the Source Pokemon that evolved.
|
||||
- Updated `HatchEggEvent.Post` to include the Pokemon that hatched.
|
||||
- Roseli Berry Trees now naturally generate in their preferred biomes.
|
||||
- Refactored dynamic lights compat to be more future proof. The dynamic light support is only tested with LambDynamicLights, on NeoForged use that mod in combination with Sinytra Connector
|
||||
|
||||
### Fixes
|
||||
- Fixed game crashing when removing national pokedex using datapacks
|
||||
@@ -409,6 +541,16 @@
|
||||
- Fixed a vulnerability that could cause party and PC rollbacks under specific circumstances.
|
||||
- Fixed a rare edge case where sorting your PC could be rolled back later.
|
||||
- Fixed BotanyPots built-in integration
|
||||
- Fixed shading of berries on berry trees.
|
||||
- Fixed static text cursor for nickname input in summary.
|
||||
- Fixed Cobblemon brewing recipes placing result into all slots, and not just slots with correct ingredients inside the brewing stand.
|
||||
- Fixed an issue where hoppers and brewing stands were not recognizing Cobblemon brewing recipes.
|
||||
- Fixed global species features... not working. Since they were created. Oops.
|
||||
- Fixed Pokémon with alternate forms being created with an incorrect 'forced' tag on their ability.
|
||||
- Fixed busted abilities and moves in Pokémon data due to removed datapacks etc. causing storage corruption. It now just rerolls their ability / uses Tackle.
|
||||
- Fixed singular Pokémon corruption causing entire storage corruption. Storages will now skip corrupted Pokémon and print an error to console.
|
||||
- Fixed species additions not being able to properly mark a species as implemented.
|
||||
- Fixed Pokémon item models not showing a glint when enchanted.
|
||||
|
||||
### Developer
|
||||
- A finished battle now has winners and losers set inside of `PokemonBattle` instead of them always being empty.
|
||||
@@ -422,7 +564,6 @@
|
||||
- Reworked observable handling in `Pokemon.kt` to cut down on RAM usage and clarify the file.
|
||||
- Note: This will break mods that used our observable functionality there or in MoveSet, IVs, EVs, or BenchedMoves.
|
||||
- Using `Pokemon#onChange()` is now the way to mark a Pokémon as needing a save.
|
||||
- Using `[Pokemon].changeObservable` is now the way to get an `Observable` for any save-worthy changes.
|
||||
- Updated NPCEntity beam positioning to properly account for the baseScale property.
|
||||
- Updated NPCEntity pokeball throw positioning to properly account for the baseScale property.
|
||||
- Fixed `[Pokemon].copyFrom` error causing forms, IVs, and EVs to not be applied properly when using `[Pokemon].loadFromJSON` or `[Pokemon].loadFromNBT`
|
||||
@@ -451,7 +592,6 @@
|
||||
- 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
|
||||
- Fixed `[SpawnPool].observable` not emitting when the spawn pool is reloaded.
|
||||
|
||||
### 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)
|
||||
@@ -504,6 +644,166 @@
|
||||
- Added `is_included`, `to_lower`, `to_upper`, and `string_length` as available Molang functions.
|
||||
- Fixed a crash that would occur during battles if the opponent wild Pokémon species comes from a namespace other than cobblemon
|
||||
- Fixed `clientActions` inside Dialogue pages being executed twice
|
||||
- Added `q.split_string(<text>, <delimiter/comma>)` Outputs an array with the divided text.
|
||||
- Added `q.spawn_npc(<x>, <y>, <z>, <npc>)` Spawns an NPC at the given coordinates and returns the NPC.
|
||||
- Added `q.player.seen_credits` Returns whether the player has seen the end credits.
|
||||
- Added `q.player.is_in_dialogue` Returns whether the player is currently in a dialogue.
|
||||
- Added `q.player.active_dialogue` Returns the active dialogue of the player, or null if none.
|
||||
- Added `q.player.is_spectator` Returns whether the player is in spectator mode.
|
||||
- Added `q.player.is_adventure` Returns whether the player is in adventure mode.
|
||||
- Added `q.player.is_creative` Returns whether the player is in creative mode.
|
||||
- Added `q.player.is_survival` Returns whether the player is in survival mode.
|
||||
- Added `q.player.set_battle_theme(<sound resource>)` Sets the player's battle theme to the given theme.
|
||||
- Added `q.player.battle_music(<sound_resource>, <volume>, <pitch>)` Plays the given battle music for the player.
|
||||
- Added `q.player.stop_battle_music()` Stops any currently playing battle music for the player. (With a fade out).
|
||||
- Added `q.player.riding_pokemon` Returns the Pokémon the player is currently riding.
|
||||
- Added `q.entity.is_player` Returns whether the entity is a player.
|
||||
- Added `q.entity.is_pokemon` Returns whether the entity is a Pokémon.
|
||||
- Added `q.entity.is_npc` Returns whether the entity is an NPC.
|
||||
- Added `q.entity.is_mob` Returns whether the entity is a vanilla mob.
|
||||
- Added `q.entity.is_animal` Returns whether the entity is an animal.
|
||||
- Added `q.entity.is_hostile` Returns whether the entity is a hostile mob.
|
||||
- Added `q.entity.is_baby` Returns whether the entity is a baby.
|
||||
- Added `q.entity.is_adult` Returns whether the entity is an adult.
|
||||
- Added `q.entity.is_tamable` Returns whether the entity is tamable.
|
||||
- Added `q.entity.is_tamed` Returns whether the entity is tamed.
|
||||
- Added `q.entity.add_effect(<effect>, <duration>, <amplifier>, <ambient> <show_particles>)` Adds a status effect to the entity.
|
||||
- Added `q.entity.remove_effect(<effect>)` Removes a status effect from the entity.
|
||||
- Added `q.entity.has_effect(<effect>)` Returns whether the entity has the given status effect.
|
||||
- Added `q.entity.is_looking_at(<entity>)` Returns whether the entity is looking at the given entity.
|
||||
- Added `q.entity.tags` Returns an array of the entity's tags.
|
||||
- Added `q.entity.add_tag(<tag>)` Adds the given tag to the entity.
|
||||
- Added `q.entity.remove_tag(<tag>)` Removes the given tag from the entity.
|
||||
- Added `q.entity.has_tag(<tag>)` Returns whether the entity has the given tag.
|
||||
- Added `q.entity.make_intangible(<true/false>)` Makes the entity intangible.
|
||||
- Added `q.pokemon.force_evolve(<index>)` Forces the Pokémon at the given index.
|
||||
- Added `q.pokemon.can_evolve` Returns whether the Pokémon at the given index can evolve.
|
||||
- Added `q.pokemon.is_busy` Returns whether the Pokémon is busy.
|
||||
- Added `q.pokemon.is_rideable` Returns whether the Pokémon is rideable.
|
||||
- Fixed `background` field and added `textColor` field for dialogues.
|
||||
- Added support to reload some data registries:
|
||||
- MoLang scripts
|
||||
- Callbacks
|
||||
- Spawn Detail Presets
|
||||
- Spawn Pools
|
||||
- Spawn Rules
|
||||
- Cosmetic Items
|
||||
- Dialogues
|
||||
- Fossils
|
||||
- Natural Materials
|
||||
- Action Effects
|
||||
- Mechanics
|
||||
- Unlockable Wallpapers
|
||||
- Starter Data
|
||||
|
||||
### Particles
|
||||
Added new/updated particles for the following moves:
|
||||
- Absorb
|
||||
- Acid
|
||||
- Acid Spray
|
||||
- Aerial Ace
|
||||
- Amnesia
|
||||
- Aurora Beam
|
||||
- Bite
|
||||
- Body Slam
|
||||
- Bubble
|
||||
- Bubble Beam
|
||||
- Bulldoze
|
||||
- Bullet Punch
|
||||
- Close Combat
|
||||
- Confusion
|
||||
- Crunch
|
||||
- Cotton Guard
|
||||
- Double Team
|
||||
- Dragon Claw
|
||||
- Egg Bomb
|
||||
- Ember
|
||||
- Eruption
|
||||
- Explosion
|
||||
- Fire Blast
|
||||
- Fire Punch
|
||||
- Fire Spin
|
||||
- Flame Burst
|
||||
- Flame Charge
|
||||
- Flamethrower
|
||||
- Flame Wheel
|
||||
- Frost Breath
|
||||
- Frustration
|
||||
- Giga Drain
|
||||
- Growl
|
||||
- Haze
|
||||
- Hidden Power
|
||||
- Horn Attack
|
||||
- Hyper Fang
|
||||
- Hypnosis
|
||||
- Ice Beam
|
||||
- Ice Punch
|
||||
- Ice Shard
|
||||
- Icy Wind
|
||||
- Infestation
|
||||
- Kinesis
|
||||
- Lava Plume
|
||||
- Leech Life
|
||||
- Leech Seed
|
||||
- Leer
|
||||
- Lick
|
||||
- Magical Leaf
|
||||
- Mega Drain
|
||||
- Minimize
|
||||
- Mist
|
||||
- Mud Bomb
|
||||
- MudSport
|
||||
- Mystical Fire
|
||||
- Nasty Plot
|
||||
- Poison Fang
|
||||
- Poison Gas
|
||||
- Poison Powder
|
||||
- Population Bomb
|
||||
- Pound
|
||||
- Powder
|
||||
- Powder Snow
|
||||
- Present
|
||||
- Protect
|
||||
- Psybeam
|
||||
- Psychic
|
||||
- Pursuit
|
||||
- Quick Attack
|
||||
- Rage Powder
|
||||
- Razor Leaf
|
||||
- Return
|
||||
- Rock Throw
|
||||
- Sand Attack
|
||||
- Scary Face
|
||||
- Scratch
|
||||
- Seed Bomb
|
||||
- Seismic Toss
|
||||
- Self Destruct
|
||||
- Shadow Ball
|
||||
- Shadow Claw
|
||||
- Sing
|
||||
- Sleep Powder
|
||||
- Sludge Bomb
|
||||
- Smokescreen
|
||||
- Spike
|
||||
- Spore
|
||||
- Stomp
|
||||
- String Shot
|
||||
- Stun Spore
|
||||
- Super Fang
|
||||
- Swords Dance
|
||||
- Tackle
|
||||
- Tail Whip
|
||||
- Thunder
|
||||
- Thunder Punch
|
||||
- Thunderbolt
|
||||
- Thunder Shock
|
||||
- Thunder Wave
|
||||
- Toxic
|
||||
- Water Gun
|
||||
- Water Pulse
|
||||
- Water Sport
|
||||
- Will-O-Wisp
|
||||
- Withdraw
|
||||
|
||||
## [1.6.1 (January 26th, 2025)](#1-6-1)
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ repositories {
|
||||
maven(url = "${rootProject.projectDir}/deps")
|
||||
maven(url = "https://api.modrinth.com/maven")
|
||||
maven(url = "https://maven.neoforged.net/releases")
|
||||
maven(url = "https://maven.gegy.dev")
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
@@ -44,6 +45,12 @@ dependencies {
|
||||
modCompileOnly(libs.bundles.common.integrations.compileOnly) {
|
||||
isTransitive = false
|
||||
}
|
||||
// LambDynamicLights is handled differently because we need the Mojang-mappings version of it.
|
||||
modCompileOnly(libs.lambDynamicLights) {
|
||||
capabilities {
|
||||
requireCapability("dev.lambdaurora.lambdynamiclights:api-mojmap")
|
||||
}
|
||||
}
|
||||
// Flywheel has no common dep so just pick one and don't use any platform specific code in common
|
||||
// modCompileOnly(libs.flywheelFabric)
|
||||
|
||||
@@ -94,4 +101,4 @@ sourceSets {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.compat;
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonEntities;
|
||||
import com.cobblemon.mod.common.client.render.layer.PokemonOnShoulderRenderer;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.cobblemon.mod.common.item.PokedexItem;
|
||||
import com.cobblemon.mod.common.pokemon.FormData;
|
||||
import com.cobblemon.mod.common.pokemon.lighthing.LightingData;
|
||||
import dev.lambdaurora.lambdynlights.LambDynLights;
|
||||
import dev.lambdaurora.lambdynlights.api.DynamicLightHandler;
|
||||
import dev.lambdaurora.lambdynlights.api.DynamicLightHandlers;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
|
||||
// Java class due to relying on entrypoint on Fabric side
|
||||
public class LambDynamicLightsCompat {
|
||||
|
||||
// This is all called on client side
|
||||
// These things are checked every tick so try to keep it as free as possible
|
||||
// That being said it's unlikely school Toshiba person is running dynamic lighting and shaders to enjoy this
|
||||
// Make sure not to call anything Fabric specific :)
|
||||
public static void hookCompat() {
|
||||
// Pokémon entities
|
||||
DynamicLightHandlers.registerDynamicLightHandler(
|
||||
CobblemonEntities.POKEMON,
|
||||
DynamicLightHandler.makeHandler(
|
||||
LambDynamicLightsCompat::resolvedPokemonLightLevel,
|
||||
pokemon -> false
|
||||
)
|
||||
);
|
||||
|
||||
// Shouldered Pokémon
|
||||
DynamicLightHandlers.registerDynamicLightHandler(
|
||||
EntityType.PLAYER,
|
||||
DynamicLightHandler.makeHandler(
|
||||
LambDynamicLightsCompat::resolvedPlayerLightLevel,
|
||||
player -> false
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private static int resolvedPokemonLightLevel(PokemonEntity pokemon) {
|
||||
var underwater = !pokemon.level().getFluidState(BlockPos.containing(pokemon.getX(), pokemon.getEyeY(), pokemon.getZ())).isEmpty();
|
||||
|
||||
ItemStack item = pokemon.getShownItem();
|
||||
int itemLightLevel = item.isEmpty()?0:LambDynLights.getLuminanceFromItemStack(item, underwater);
|
||||
int formLightLevel = extractFormLightLevel(pokemon.getForm(), underwater).orElse(0);
|
||||
|
||||
return Math.max(itemLightLevel, formLightLevel);
|
||||
}
|
||||
|
||||
private static int resolvedPlayerLightLevel(Player player) {
|
||||
var underwater = !player.level().getFluidState(BlockPos.containing(player.getX(), player.getEyeY(), player.getZ())).isEmpty();
|
||||
final int leftShoulderLightLevel = extractShoulderLightLevel(PokemonOnShoulderRenderer.shoulderDataFrom(player.getShoulderEntityLeft()), underwater);
|
||||
final int rightShoulderLightLevel = extractShoulderLightLevel(PokemonOnShoulderRenderer.shoulderDataFrom(player.getShoulderEntityRight()), underwater);
|
||||
final int itemLightLevel = customHeldItemLightLevel(player.getMainHandItem(), player.getOffhandItem());
|
||||
|
||||
return Math.max(itemLightLevel, Math.max(leftShoulderLightLevel, rightShoulderLightLevel));
|
||||
}
|
||||
|
||||
private static Optional<Integer> extractFormLightLevel(@NotNull FormData form, boolean underwater) {
|
||||
if (form.getLightingData() == null || !liquidGlowModeSupport(form.getLightingData().getLiquidGlowMode(), underwater)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(form.getLightingData().getLightLevel());
|
||||
}
|
||||
|
||||
private static int extractShoulderLightLevel(@Nullable PokemonOnShoulderRenderer.ShoulderData shoulderData, boolean underwater) {
|
||||
if (shoulderData == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ItemStack item = shoulderData.getShownItem();
|
||||
int itemLightLevel = item.isEmpty() ? 0 :LambDynLights.getLuminanceFromItemStack(item, underwater);
|
||||
int formLightLevel = extractFormLightLevel(shoulderData.getForm(), underwater).orElse(0);
|
||||
|
||||
return Math.max(itemLightLevel, formLightLevel);
|
||||
}
|
||||
|
||||
private static int customHeldItemLightLevel(ItemStack mainhand, ItemStack offhand) {
|
||||
return (mainhand.getItem() instanceof PokedexItem || offhand.getItem() instanceof PokedexItem) ? 13 : 0;
|
||||
}
|
||||
|
||||
private static boolean liquidGlowModeSupport(@NotNull LightingData.LiquidGlowMode liquidGlowMode, boolean underwater) {
|
||||
return underwater ? liquidGlowMode.getGlowsUnderwater() : liquidGlowMode.getGlowsInLand();
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.compat.lambdynamiclights;
|
||||
|
||||
import com.cobblemon.mod.common.pokemon.FormData;
|
||||
import com.cobblemon.mod.common.pokemon.lighthing.LightingData;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public abstract class CustomLuminance {
|
||||
protected static Optional<Integer> extractFormLightLevel(@NotNull FormData form, boolean underwater) {
|
||||
if (form.getLightingData() == null || !liquidGlowModeSupport(form.getLightingData().getLiquidGlowMode(), underwater)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(form.getLightingData().getLightLevel());
|
||||
}
|
||||
|
||||
protected static boolean liquidGlowModeSupport(@NotNull LightingData.LiquidGlowMode liquidGlowMode, boolean underwater) {
|
||||
return underwater ? liquidGlowMode.getGlowsUnderwater() : liquidGlowMode.getGlowsInLand();
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.compat.lambdynamiclights;
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon;
|
||||
import dev.lambdaurora.lambdynlights.api.DynamicLightsContext;
|
||||
import dev.lambdaurora.lambdynlights.api.DynamicLightsInitializer;
|
||||
import dev.lambdaurora.lambdynlights.api.entity.luminance.EntityLuminance;
|
||||
import dev.lambdaurora.lambdynlights.api.item.ItemLightSourceManager;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
|
||||
public class LambDynamicLightsInitializer implements DynamicLightsInitializer {
|
||||
public static final EntityLuminance.Type POKEMON_LUMINANCE = EntityLuminance.Type.registerSimple(
|
||||
ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, "pokemon"),
|
||||
PokemonLuminance.INSTANCE
|
||||
);
|
||||
public static final EntityLuminance.Type PLAYER_LUMINANCE = EntityLuminance.Type.registerSimple(
|
||||
ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, "shoulder"),
|
||||
PlayerLuminance.INSTANCE
|
||||
);
|
||||
|
||||
@Override
|
||||
public void onInitializeDynamicLights(DynamicLightsContext context) {
|
||||
Cobblemon.LOGGER.info("Lamb Dynamic Lights compatibility enabled");
|
||||
|
||||
context.entityLightSourceManager().onRegisterEvent().register(ctx -> {
|
||||
ctx.register(EntityType.PLAYER, PlayerLuminance.INSTANCE);
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings({"removal", "UnstableApiUsage"})
|
||||
@Override
|
||||
public void onInitializeDynamicLights(ItemLightSourceManager itemLightSourceManager) {}
|
||||
}
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.compat.lambdynamiclights;
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon;
|
||||
import com.cobblemon.mod.common.client.render.layer.PokemonOnShoulderRenderer;
|
||||
import dev.lambdaurora.lambdynlights.api.entity.luminance.EntityLuminance;
|
||||
import dev.lambdaurora.lambdynlights.api.item.ItemLightSourceManager;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Range;
|
||||
|
||||
public final class PlayerLuminance extends CustomLuminance implements EntityLuminance {
|
||||
public static final PlayerLuminance INSTANCE = new PlayerLuminance();
|
||||
private static final TagKey<Item> POKEDEX = TagKey.create(Registries.ITEM, ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, "pokedex"));
|
||||
|
||||
@Override
|
||||
public @NotNull Type type() {
|
||||
return LambDynamicLightsInitializer.PLAYER_LUMINANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Range(from = 0L, to = 15L) int getLuminance(@NotNull ItemLightSourceManager itemLightSourceManager, @NotNull Entity entity) {
|
||||
if (entity instanceof Player player) {
|
||||
final int shoulderLightLevel = shoulderLuminance(itemLightSourceManager, player);
|
||||
|
||||
final int itemLightLevel = player.isHolding(stack -> stack.is(POKEDEX)) ? 13 : 0;
|
||||
|
||||
return Math.max(shoulderLightLevel, itemLightLevel);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private @Range(from = 0L, to = 15L) static int shoulderLuminance(@NotNull ItemLightSourceManager itemLightSourceManager, @NotNull Player player) {
|
||||
final var leftShoulder = PokemonOnShoulderRenderer.shoulderDataFrom(player.getShoulderEntityLeft());
|
||||
final var rightShoulder = PokemonOnShoulderRenderer.shoulderDataFrom(player.getShoulderEntityRight());
|
||||
|
||||
if (leftShoulder == null && rightShoulder == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
final int leftShoulderLightLevel = extractShoulderLightLevel(leftShoulder, itemLightSourceManager, player.isUnderWater());
|
||||
final int rightShoulderLightLevel = extractShoulderLightLevel(rightShoulder, itemLightSourceManager, player.isUnderWater());
|
||||
return Math.max(leftShoulderLightLevel, rightShoulderLightLevel);
|
||||
}
|
||||
|
||||
private static int extractShoulderLightLevel(@Nullable PokemonOnShoulderRenderer.ShoulderData shoulderData, @NotNull ItemLightSourceManager itemLightSourceManager, boolean underwater) {
|
||||
if (shoulderData == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ItemStack item = shoulderData.getShownItem();
|
||||
int itemLightLevel = item.isEmpty() ? 0 : itemLightSourceManager.getLuminance(item, underwater);
|
||||
int formLightLevel = extractFormLightLevel(shoulderData.getForm(), underwater).orElse(0);
|
||||
|
||||
return Math.max(itemLightLevel, formLightLevel);
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.compat.lambdynamiclights;
|
||||
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import dev.lambdaurora.lambdynlights.api.entity.luminance.EntityLuminance;
|
||||
import dev.lambdaurora.lambdynlights.api.item.ItemLightSourceManager;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Range;
|
||||
|
||||
public final class PokemonLuminance extends CustomLuminance implements EntityLuminance {
|
||||
public static final PokemonLuminance INSTANCE = new PokemonLuminance();
|
||||
|
||||
private PokemonLuminance() {}
|
||||
|
||||
@Override
|
||||
public @NotNull Type type() {
|
||||
return LambDynamicLightsInitializer.POKEMON_LUMINANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Range(from = 0L, to = 15L) int getLuminance(
|
||||
@NotNull ItemLightSourceManager itemLightSourceManager,
|
||||
@NotNull Entity entity
|
||||
) {
|
||||
if (entity instanceof PokemonEntity pokemon) {
|
||||
boolean underwater = pokemon.isUnderWater();
|
||||
ItemStack item = pokemon.getShownItem();
|
||||
int itemLightLevel = item.isEmpty() ? 0 : itemLightSourceManager.getLuminance(item, underwater);
|
||||
int formLightLevel = extractFormLightLevel(pokemon.getForm(), underwater).orElse(0);
|
||||
|
||||
return Math.max(itemLightLevel, formLightLevel);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResultHolder;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.EnderpearlItem;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
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.Comparator;
|
||||
|
||||
@Mixin(EnderpearlItem.class)
|
||||
public abstract class EnderPearlItemMixin {
|
||||
@Inject(method = "use", at = @At(value = "HEAD"), cancellable = true)
|
||||
private void cobblemon$use(Level level, Player player, InteractionHand usedHand, CallbackInfoReturnable<InteractionResultHolder<ItemStack>> cir) {
|
||||
double range = player.entityInteractionRange();
|
||||
Entity closestEntity = level.getEntities(player, AABB.ofSize(player.position(), range, range, range)).stream()
|
||||
.filter(entity -> isLookingAt(player, entity))
|
||||
.min(Comparator.comparingDouble(entity -> entity.distanceTo(player)))
|
||||
.orElse(null);
|
||||
|
||||
if (closestEntity instanceof PokemonEntity pokemonEntity) {
|
||||
if (player.isCrouching() && pokemonEntity.getOwnerUUID() == player.getUUID()) {
|
||||
cir.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isLookingAt(Player player, Entity other) {
|
||||
float maxDistance = 10F;
|
||||
float stepDistance = 0.1F;
|
||||
double step = stepDistance;
|
||||
Vec3 startPos = player.getEyePosition();
|
||||
Vec3 direction = player.getLookAngle();
|
||||
|
||||
while (step <= maxDistance) {
|
||||
Vec3 location = startPos.add(direction.scale(step));
|
||||
step += stepDistance;
|
||||
|
||||
if (other.getBoundingBox().contains(location)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -58,11 +58,9 @@ public abstract class LivingEntityMixin extends Entity implements OrientationCon
|
||||
@Override
|
||||
public @NotNull HitResult pick(double hitDistance, float partialTicks, boolean hitFluids) {
|
||||
Entity vehicle = this.getVehicle();
|
||||
if (vehicle instanceof PokemonEntity pokemonEntity) {
|
||||
if (vehicle instanceof PokemonEntity pokemonEntity && pokemonEntity.getDelegate() instanceof PokemonClientDelegate delegate) {
|
||||
int seatIndex = pokemonEntity.getPassengers().indexOf(this);
|
||||
Seat seat = pokemonEntity.getSeats().get(seatIndex);
|
||||
|
||||
PokemonClientDelegate delegate = (PokemonClientDelegate) pokemonEntity.getDelegate();
|
||||
MatrixWrapper locator = delegate.getLocatorStates().get(seat.getLocator());
|
||||
|
||||
Vec3 locatorOffset = new Vec3(locator.getMatrix().getTranslation(new Vector3f()));
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+38
-1
@@ -15,9 +15,11 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.world.Containers;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.alchemy.PotionBrewing;
|
||||
import net.minecraft.world.item.crafting.RecipeHolder;
|
||||
import net.minecraft.world.item.crafting.RecipeManager;
|
||||
@@ -27,6 +29,8 @@ import net.minecraft.world.level.block.entity.BrewingStandBlockEntity;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -43,7 +47,9 @@ public class BrewingStandBlockEntityMixin {
|
||||
//copied from vanilla doBrew logic, but slightly tweaked to work for cobblemon, this was needed as replacing the loop was not quite possible inside doBrew
|
||||
ItemStack itemStack = slots.get(3);
|
||||
for(int i = 0; i < 3; ++i) {
|
||||
slots.set(i, recipe.getResult().copy());
|
||||
if (!slots.get(i).isEmpty()){
|
||||
slots.set(i, recipe.getResult().copy());
|
||||
}
|
||||
}
|
||||
|
||||
itemStack.shrink(1);
|
||||
@@ -91,4 +97,35 @@ public class BrewingStandBlockEntityMixin {
|
||||
|
||||
return recipeHolder.map(RecipeHolder::value).orElse(null);
|
||||
}
|
||||
|
||||
@Inject(method = "canPlaceItem", at = @At("HEAD"), cancellable = true)
|
||||
private void cobblemon$canPlaceItem(int slot, ItemStack stack, CallbackInfoReturnable<Boolean> cir) {
|
||||
if (stack == null || stack.isEmpty()) return;
|
||||
|
||||
BrewingStandBlockEntity self = (BrewingStandBlockEntity) (Object) this;
|
||||
Level level = self.getLevel();
|
||||
|
||||
if (level != null) {
|
||||
|
||||
var rm = level.getRecipeManager();
|
||||
|
||||
//Input slot
|
||||
if (slot == 3) {
|
||||
if (BrewingStandRecipe.Companion.isInput(stack, rm)) {
|
||||
cir.setReturnValue(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Output slots
|
||||
if (slot >= 0 && slot < 3) {
|
||||
if (BrewingStandRecipe.Companion.isBottle(stack, rm)) {
|
||||
if (self.getItem(slot).isEmpty()) {
|
||||
cir.setReturnValue(true);
|
||||
} else {
|
||||
cir.setReturnValue(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
-7
@@ -31,14 +31,15 @@ public class AbstractClientPlayerMixin {
|
||||
PokedexUsageContext usageContext = CobblemonClient.INSTANCE.getPokedexUsageContext();
|
||||
if (usageContext.getScanningGuiOpen()) {
|
||||
cir.setReturnValue(usageContext.getFovMultiplier());
|
||||
}
|
||||
} else {
|
||||
// Only modify fov through riding if the pokedex is not open
|
||||
LocalPlayer player = (LocalPlayer) (Object) this;
|
||||
if (player != null && player.isPassenger() && player.getVehicle() instanceof PokemonEntity) {
|
||||
PokemonEntity ride = (PokemonEntity) player.getVehicle();
|
||||
|
||||
LocalPlayer player = (LocalPlayer) (Object) this;
|
||||
if (player != null && player.isPassenger() && player.getVehicle() instanceof PokemonEntity) {
|
||||
PokemonEntity ride = (PokemonEntity) player.getVehicle();
|
||||
|
||||
//Return custom fov mult
|
||||
cir.setReturnValue(ride.rideFovMult());
|
||||
// Return custom fov mult for riding
|
||||
cir.setReturnValue(ride.rideFovMult());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.client.render.player.MountedPlayerRenderer;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import net.minecraft.client.model.AgeableListModel;
|
||||
import net.minecraft.client.model.HumanoidModel;
|
||||
import net.minecraft.client.model.Model;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
/**
|
||||
* This mixin adds to the head of the renderToBuffer function and allows us to detect if it is a HumanoidModel and
|
||||
* is being rendered as part of a player in the world who is riding a PokemonEntity. If so we need to obey any
|
||||
* root animations that have been applied earlier.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since May 16, 2025
|
||||
*/
|
||||
@Mixin(AgeableListModel.class)
|
||||
public class AgeableListModelMixin {
|
||||
@Inject(method = "renderToBuffer", at = @At("HEAD"))
|
||||
private void cobblemon$renderToBuffer(
|
||||
PoseStack poseStack,
|
||||
VertexConsumer vertexConsumer,
|
||||
int packedLight,
|
||||
int packedOverlay,
|
||||
int colour,
|
||||
CallbackInfo ci
|
||||
) {
|
||||
Model model = (Model) (Object) this;
|
||||
if (model instanceof HumanoidModel<?> && MountedPlayerRenderer.shouldApplyRootAnimation) {
|
||||
MountedPlayerRenderer.INSTANCE.animateRoot(poseStack);
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
-15
@@ -19,20 +19,17 @@ import net.minecraft.world.entity.LivingEntity;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
@Mixin(HumanoidModel.class)
|
||||
public class HumanoidModelMixin {
|
||||
@Unique HashMap<String, ModelPart> relevantPartsByName = new HashMap<>();
|
||||
|
||||
@Shadow @Final
|
||||
public ModelPart head;
|
||||
@Shadow @Final
|
||||
public ModelPart hat;
|
||||
@Shadow @Final
|
||||
public ModelPart rightArm;
|
||||
@Shadow @Final
|
||||
public ModelPart leftArm;
|
||||
@@ -40,6 +37,8 @@ public class HumanoidModelMixin {
|
||||
public ModelPart rightLeg;
|
||||
@Shadow @Final
|
||||
public ModelPart leftLeg;
|
||||
@Shadow @Final
|
||||
public ModelPart body;
|
||||
|
||||
@Inject(method = "setupAnim*", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/model/geom/ModelPart;copyFrom(Lnet/minecraft/client/model/geom/ModelPart;)V", shift = At.Shift.BEFORE))
|
||||
public void cobblemon$animateRiders(
|
||||
@@ -51,19 +50,23 @@ public class HumanoidModelMixin {
|
||||
float headPitch,
|
||||
CallbackInfo ci
|
||||
) {
|
||||
if (relevantPartsByName.isEmpty()) {
|
||||
relevantPartsByName.put("arm_left", leftArm);
|
||||
relevantPartsByName.put("arm_right", rightArm);
|
||||
relevantPartsByName.put("leg_left", leftLeg);
|
||||
relevantPartsByName.put("leg_right", rightLeg);
|
||||
relevantPartsByName.put("head", head);
|
||||
MountedPlayerRenderer.shouldApplyRootAnimation = false;
|
||||
|
||||
if (MountedPlayerRenderer.relevantPartsByName.size() <= 1) {
|
||||
MountedPlayerRenderer.relevantPartsByName.put("arm_left", leftArm);
|
||||
MountedPlayerRenderer.relevantPartsByName.put("arm_right", rightArm);
|
||||
MountedPlayerRenderer.relevantPartsByName.put("leg_left", leftLeg);
|
||||
MountedPlayerRenderer.relevantPartsByName.put("leg_right", rightLeg);
|
||||
MountedPlayerRenderer.relevantPartsByName.put("head", head);
|
||||
MountedPlayerRenderer.relevantPartsByName.put("hat", hat);
|
||||
MountedPlayerRenderer.relevantPartsByName.put("torso", body);
|
||||
}
|
||||
|
||||
Entity vehicle = entity.getVehicle();
|
||||
if (vehicle instanceof PokemonEntity pokemonEntity && entity instanceof OrientationControllable) {
|
||||
var shouldRotatePlayerHead = pokemonEntity.ifRidingAvailableSupply(false, (behaviour, settings, state) -> {
|
||||
return behaviour.shouldRotateRiderHead(settings, state, pokemonEntity);
|
||||
});
|
||||
var shouldRotatePlayerHead = pokemonEntity.ifRidingAvailableSupply(false, (behaviour, settings, state) ->
|
||||
behaviour.shouldRotateRiderHead(settings, state, pokemonEntity)
|
||||
);
|
||||
|
||||
if (!shouldRotatePlayerHead) {
|
||||
netHeadYaw = 0f;
|
||||
@@ -76,7 +79,8 @@ public class HumanoidModelMixin {
|
||||
|
||||
if (!(entity instanceof AbstractClientPlayer player)) return;
|
||||
|
||||
MountedPlayerRenderer.INSTANCE.animate(pokemonEntity, player, relevantPartsByName, netHeadYaw, headPitch, ageInTicks, limbSwing, limbSwingAmount);
|
||||
MountedPlayerRenderer.shouldApplyRootAnimation = true;
|
||||
MountedPlayerRenderer.INSTANCE.animate((HumanoidModel<AbstractClientPlayer>)(Object)this, pokemonEntity, player, netHeadYaw, headPitch, ageInTicks, limbSwing, limbSwingAmount);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -174,6 +174,16 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
|
||||
return this.cobblemon$isDoubleJumping;
|
||||
}
|
||||
|
||||
@Inject(method = "isHandsBusy", at = @At("HEAD"), cancellable = true)
|
||||
private void cobblemon$isHandsBusy(CallbackInfoReturnable<Boolean> cir) {
|
||||
Entity vehicle = this.getVehicle();
|
||||
if (vehicle instanceof PokemonEntity pokemonEntity) {
|
||||
int seatIndex = pokemonEntity.getPassengers().indexOf(this);
|
||||
Seat seat = pokemonEntity.getSeats().get(seatIndex);
|
||||
cir.setReturnValue(seat.getHandsBusy());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public HitResult pick(double hitDistance, float partialTicks, boolean hitFluids) {
|
||||
Entity vehicle = this.getVehicle();
|
||||
|
||||
@@ -117,6 +117,8 @@ public abstract class MouseHandlerMixin {
|
||||
@Local(argsOnly = true) double d,
|
||||
@Local(argsOnly = true) double movementTime
|
||||
) {
|
||||
boolean returnValue = true;
|
||||
|
||||
PokedexUsageContext usageContext = CobblemonClient.INSTANCE.getPokedexUsageContext();
|
||||
if (usageContext.getScanningGuiOpen()) {
|
||||
this.smoothTurnY.reset();
|
||||
@@ -127,7 +129,7 @@ public abstract class MouseHandlerMixin {
|
||||
var sensitivity = Mth.lerp(usageContext.getFovMultiplier(), spyglassSensitivity, lookSensitivity);
|
||||
var yRotationFlip = this.minecraft.options.invertYMouse().get() ? -1 : 1;
|
||||
player.turn(this.accumulatedDX * sensitivity, (this.accumulatedDY * sensitivity * yRotationFlip));
|
||||
return false;
|
||||
returnValue = false;
|
||||
}
|
||||
|
||||
// Clamp player rotation if riding and the vehicle demands it
|
||||
@@ -143,35 +145,33 @@ public abstract class MouseHandlerMixin {
|
||||
pitchSmoother.reset();
|
||||
rollSmoother.reset();
|
||||
yawSmoother.reset();
|
||||
return true;
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
//Send mouse input to be interpreted into rotation
|
||||
//deltas by the ride controller
|
||||
// Send mouse input to be interpreted into rotation
|
||||
// deltas by the ride controller
|
||||
Vec3 angVecMouse = cobblemon$getRideMouseRotation(cursorDeltaX, cursorDeltaY, movementTime);
|
||||
|
||||
//Perform Rotation using mouse influenced rotation deltas.
|
||||
// Perform Rotation using mouse influenced rotation deltas.
|
||||
controllable.getOrientationController().rotate(
|
||||
(float) angVecMouse.x,
|
||||
(float) angVecMouse.y,
|
||||
(float) angVecMouse.z
|
||||
);
|
||||
|
||||
//Gather and apply the current rotation deltas
|
||||
// Gather and apply the current rotation deltas
|
||||
var angRot = cobblemon$getAngularVelocity(movementTime);
|
||||
|
||||
//Apply smoothing if requested by the controller.
|
||||
//This Might be best if done by the controller itself?
|
||||
if(cobblemon$shouldUseAngVelSmoothing())
|
||||
{
|
||||
// Apply smoothing if requested by the controller.
|
||||
// This Might be best if done by the controller itself?
|
||||
if(cobblemon$shouldUseAngVelSmoothing()) {
|
||||
var yaw = yawSmoother.getNewDeltaValue(angRot.x * 0.5f, d);
|
||||
var pitch = pitchSmoother.getNewDeltaValue(angRot.y * 0.5f, d);
|
||||
var roll = rollSmoother.getNewDeltaValue(angRot.z * 0.5f, d);
|
||||
controllable.getOrientationController().rotate((float) yaw, (float) pitch, (float) roll);
|
||||
}
|
||||
//Otherwise simply apply the smoothing
|
||||
else
|
||||
{
|
||||
// Otherwise simply apply the smoothing
|
||||
else {
|
||||
controllable.getOrientationController().rotate((float) (angRot.x * 10 * d), (float) (angRot.y * 10 * d), (float) (angRot.z * 10 * d));
|
||||
}
|
||||
return false;
|
||||
|
||||
+3
-4
@@ -10,8 +10,7 @@ package com.cobblemon.mod.common.mixin.client.snapshots;
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBuildDetails;
|
||||
import com.cobblemon.mod.common.client.gui.snapshots.SnapshotWarningScreen;
|
||||
import com.cobblemon.mod.common.client.snapshots.SnapshotAcknowledgementData;
|
||||
import com.cobblemon.mod.common.client.snapshots.SnapshotAcknowledgementReader;
|
||||
import com.cobblemon.mod.common.client.persisted.ClientPersistedData;
|
||||
import kotlin.Unit;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.client.Minecraft;
|
||||
@@ -42,7 +41,7 @@ public final class SnapshotWarningMixin {
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
|
||||
SnapshotAcknowledgementReader.INSTANCE.write(new SnapshotAcknowledgementData(CobblemonBuildDetails.VERSION, dsa));
|
||||
ClientPersistedData.INSTANCE.writeSnapshotsData(new ClientPersistedData.SnapshotAcknowledgementData(CobblemonBuildDetails.VERSION, dsa));
|
||||
|
||||
runnable.run();
|
||||
return Unit.INSTANCE;
|
||||
@@ -54,7 +53,7 @@ public final class SnapshotWarningMixin {
|
||||
@Unique
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
private boolean cobblemon$isAcknowledged() {
|
||||
SnapshotAcknowledgementData data = SnapshotAcknowledgementReader.INSTANCE.read();
|
||||
ClientPersistedData.SnapshotAcknowledgementData data = ClientPersistedData.INSTANCE.readSnapshotsData();
|
||||
if (data == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,10 @@ package com.cobblemon.mod.common.mixin.entity;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
@@ -39,10 +42,17 @@ public abstract class EntityMixin {
|
||||
target = "Lnet/minecraft/world/entity/Entity;onGround()Z"
|
||||
)
|
||||
)
|
||||
public boolean cobblemon$forceOnGroundForStepUp(Entity entity, Operation<Boolean> original)
|
||||
{
|
||||
public boolean cobblemon$forceOnGroundForStepUp(Entity entity, Operation<Boolean> original) {
|
||||
if (entity instanceof PokemonEntity && entity.hasControllingPassenger()) {
|
||||
return true;
|
||||
BlockPos below = entity.blockPosition().below();
|
||||
Level level = entity.level();
|
||||
var blockStateBelow = level.getBlockState(below);
|
||||
boolean isAirOrLiquid = blockStateBelow.isAir() || !blockStateBelow.getFluidState().isEmpty();
|
||||
boolean canSupportEntity = blockStateBelow.isFaceSturdy(level, below, Direction.UP);
|
||||
boolean standingOnSolid = canSupportEntity && !isAirOrLiquid;
|
||||
if (standingOnSolid) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return original.call(entity);
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ import com.cobblemon.mod.common.config.starter.StarterConfig
|
||||
import com.cobblemon.mod.common.data.CobblemonDataProvider
|
||||
import com.cobblemon.mod.common.events.AdvancementHandler
|
||||
import com.cobblemon.mod.common.events.EntityCallbackHandler
|
||||
import com.cobblemon.mod.common.events.FlowHandler
|
||||
import com.cobblemon.mod.common.events.CallbackHandler
|
||||
import com.cobblemon.mod.common.events.PokedexHandler
|
||||
import com.cobblemon.mod.common.events.ServerTickHandler
|
||||
import com.cobblemon.mod.common.net.messages.client.settings.ServerSettingsPacket
|
||||
@@ -313,7 +313,7 @@ object Cobblemon {
|
||||
|
||||
CobblemonEvents.POKEMON_PROPERTY_INITIALISED.emit(Unit)
|
||||
|
||||
FlowHandler.setup()
|
||||
CallbackHandler.setup()
|
||||
EntityCallbackHandler.setup()
|
||||
|
||||
ifDedicatedServer {
|
||||
|
||||
@@ -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
|
||||
|
||||
+37
-31
@@ -18,72 +18,78 @@ import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
|
||||
import com.cobblemon.mod.common.net.messages.client.data.FlowRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.CallbackRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.endsWith
|
||||
import java.util.concurrent.ExecutionException
|
||||
import java.util.function.Predicate
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
import net.minecraft.server.packs.resources.ResourceManager
|
||||
|
||||
/**
|
||||
* Holds all the flows that are loaded from the server's data packs. A flow is foldered under the event identifier,
|
||||
* Holds all the callbacks that are loaded from the server's data packs. A callback is foldered under the event identifier,
|
||||
* which can be Cobblemon's namespace but can also be minecraft or any other namespace someone has added hooks for.
|
||||
* The value of the map is a list of MoLang scripts that will execute when that event occurs.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 24th, 2024
|
||||
*/
|
||||
object CobblemonFlows : DataRegistry {
|
||||
override val id = cobblemonResource("flows")
|
||||
override val observable = SimpleObservable<CobblemonFlows>()
|
||||
object CobblemonCallbacks : DataRegistry {
|
||||
override val id = cobblemonResource("callbacks")
|
||||
override val observable = SimpleObservable<CobblemonCallbacks>()
|
||||
override val type = PackType.SERVER_DATA
|
||||
override fun sync(player: ServerPlayer) {
|
||||
player.sendPacket(FlowRegistrySyncPacket(clientFlows.entries))
|
||||
player.sendPacket(CallbackRegistrySyncPacket(clientCallbacks.entries))
|
||||
}
|
||||
|
||||
val runtime by lazy { MoLangRuntime().setup() } // Lazy for if someone adds to generalFunctions in MoLangFunctions
|
||||
|
||||
val clientFlows = hashMapOf<ResourceLocation, MutableList<ExpressionLike>>()
|
||||
val flows = hashMapOf<ResourceLocation, MutableList<ExpressionLike>>()
|
||||
val clientCallbacks = hashMapOf<ResourceLocation, MutableList<ExpressionLike>>()
|
||||
val callbacks = hashMapOf<ResourceLocation, MutableList<ExpressionLike>>()
|
||||
|
||||
|
||||
override fun reload(manager: ResourceManager) {
|
||||
clientFlows.clear()
|
||||
flows.clear()
|
||||
clientCallbacks.clear()
|
||||
callbacks.clear()
|
||||
|
||||
val unsortedFlows = mutableMapOf<ResourceLocation, MutableList<Pair<String, ExpressionLike>>>()
|
||||
val unsortedCallbacks = mutableMapOf<ResourceLocation, MutableList<Pair<String, ExpressionLike>>>()
|
||||
val folderBeforeNameRegex = ".*\\/([^\\/]+)\\/[^\\/]+\$".toRegex()
|
||||
manager.listResources("flows") { path -> path.endsWith(CobblemonScripts.MOLANG_EXTENSION) }.forEach { (identifier, resource) ->
|
||||
resource.openAsReader().use { stream ->
|
||||
stream.buffered().use { reader ->
|
||||
try {
|
||||
val expression = reader.readText().asExpressionLike()
|
||||
val predicate: Predicate<ResourceLocation> = Predicate { path -> path.endsWith(CobblemonScripts.MOLANG_EXTENSION) }
|
||||
|
||||
val event = folderBeforeNameRegex.find(identifier.path)?.groupValues?.get(1)
|
||||
?: throw IllegalArgumentException("Invalid flow path: $identifier. Should have a folder structure that includes the name of the event being flowed.")
|
||||
manager.listResources("callbacks", predicate)
|
||||
.plus(manager.listResources("flows", predicate)) // old name
|
||||
.forEach { (identifier, resource) ->
|
||||
resource.openAsReader().use { stream ->
|
||||
stream.buffered().use { reader ->
|
||||
try {
|
||||
val expression = reader.readText().asExpressionLike()
|
||||
|
||||
val flowKey = ResourceLocation.fromNamespaceAndPath(identifier.namespace, event)
|
||||
unsortedFlows.putIfAbsent(flowKey, mutableListOf())
|
||||
unsortedFlows[flowKey]!!.add(identifier.path to expression)
|
||||
} catch (exception: Exception) {
|
||||
throw ExecutionException("Error loading MoLang script for flow: $identifier", exception)
|
||||
val event = folderBeforeNameRegex.find(identifier.path)?.groupValues?.get(1)
|
||||
?: throw IllegalArgumentException("Invalid callback path: $identifier. Should have a folder structure that includes the name of the event to callback.")
|
||||
|
||||
val callbackKey = ResourceLocation.fromNamespaceAndPath(identifier.namespace, event)
|
||||
unsortedCallbacks.putIfAbsent(callbackKey, mutableListOf())
|
||||
unsortedCallbacks[callbackKey]!!.add(identifier.path to expression)
|
||||
} catch (exception: Exception) {
|
||||
throw ExecutionException("Error loading MoLang script for callback: $identifier", exception)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsortedFlows.forEach { (key, value) ->
|
||||
unsortedCallbacks.forEach { (key, value) ->
|
||||
value.sortBy { it.first }
|
||||
if (key.path.startsWith("flows/client/")) {
|
||||
clientFlows.getOrPut(key) { mutableListOf() }.addAll(value.map { it.second })
|
||||
if (key.path.startsWith("flows/client/") || key.path.startsWith("callbacks/client/")) {
|
||||
clientCallbacks.getOrPut(key) { mutableListOf() }.addAll(value.map { it.second })
|
||||
} else {
|
||||
flows.getOrPut(key) { mutableListOf() }.addAll(value.map { it.second })
|
||||
callbacks.getOrPut(key) { mutableListOf() }.addAll(value.map { it.second })
|
||||
}
|
||||
}
|
||||
|
||||
Cobblemon.LOGGER.info("Loaded ${flows.size} flows and ${clientFlows.size} client flows")
|
||||
Cobblemon.LOGGER.info("Loaded ${callbacks.size} callbacks and ${clientCallbacks.size} client callbacks")
|
||||
observable.emit(this)
|
||||
}
|
||||
|
||||
@@ -93,7 +99,7 @@ object CobblemonFlows : DataRegistry {
|
||||
functions: Map<String, (MoParams) -> Any> = emptyMap(),
|
||||
cancelable: Cancelable? = null
|
||||
) {
|
||||
if (eventResourceLocation !in flows) return
|
||||
if (eventResourceLocation !in callbacks) return
|
||||
|
||||
if (cancelable == null) {
|
||||
runtime.environment.query.functions.remove("cancel")
|
||||
@@ -106,7 +112,7 @@ object CobblemonFlows : DataRegistry {
|
||||
|
||||
functions.forEach { (name, function) -> runtime.environment.query.addFunction(name, function) }
|
||||
|
||||
flows[eventResourceLocation]?.forEach {
|
||||
callbacks[eventResourceLocation]?.forEach {
|
||||
if (cancelable != null && cancelable.isCanceled) {
|
||||
return
|
||||
}
|
||||
@@ -26,10 +26,10 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
|
||||
.networkSynchronized(PokemonItemComponent.PACKET_CODEC)
|
||||
.build())
|
||||
|
||||
val HELD_ITEM_REP: DataComponentType<HeldItemCapableComponent> = DataComponentType.builder<HeldItemCapableComponent>()
|
||||
.persistent(HeldItemCapableComponent.CODEC)
|
||||
.networkSynchronized(HeldItemCapableComponent.PACKET_CODEC)
|
||||
.build()
|
||||
val HELD_ITEM_EFFECT: DataComponentType<HeldItemEffectComponent> = create("held_item_effect", DataComponentType.builder<HeldItemEffectComponent>()
|
||||
.persistent(HeldItemEffectComponent.CODEC)
|
||||
.networkSynchronized(HeldItemEffectComponent.PACKET_CODEC)
|
||||
.build())
|
||||
|
||||
val BAIT: DataComponentType<RodBaitComponent> = create("bait", DataComponentType.builder<RodBaitComponent>()
|
||||
.persistent(RodBaitComponent.CODEC)
|
||||
@@ -86,6 +86,7 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:ingredient"), INGREDIENT)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:food"), FOOD)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:mob_effects"), MOB_EFFECTS)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:held_item_effect"), HELD_ITEM_EFFECT)
|
||||
}
|
||||
|
||||
override val registry = BuiltInRegistries.DATA_COMPONENT_TYPE
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.cobblemon.mod.common.block.BerryBlock
|
||||
import com.cobblemon.mod.common.block.MintBlock
|
||||
import com.cobblemon.mod.common.block.MintBlock.MintType
|
||||
import com.cobblemon.mod.common.client.pokedex.PokedexType
|
||||
import com.cobblemon.mod.common.block.campfirepot.CampfirePotColor
|
||||
import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
|
||||
import com.cobblemon.mod.common.item.*
|
||||
import com.cobblemon.mod.common.item.armor.CobblemonArmorTrims
|
||||
@@ -192,19 +193,19 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
|
||||
val campfire_pots = mutableListOf<CampfirePotItem>()
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_RED = campfirePotItem(CobblemonBlocks.RED_CAMPFIRE_POT, "red")
|
||||
val CAMPFIRE_POT_RED = campfirePotItem(CobblemonBlocks.RED_CAMPFIRE_POT, CampfirePotColor.RED)
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_YELLOW = campfirePotItem(CobblemonBlocks.YELLOW_CAMPFIRE_POT, "yellow")
|
||||
val CAMPFIRE_POT_YELLOW = campfirePotItem(CobblemonBlocks.YELLOW_CAMPFIRE_POT, CampfirePotColor.YELLOW)
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_GREEN = campfirePotItem(CobblemonBlocks.GREEN_CAMPFIRE_POT, "green")
|
||||
val CAMPFIRE_POT_GREEN = campfirePotItem(CobblemonBlocks.GREEN_CAMPFIRE_POT, CampfirePotColor.GREEN)
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_BLUE = campfirePotItem(CobblemonBlocks.BLUE_CAMPFIRE_POT, "blue")
|
||||
val CAMPFIRE_POT_BLUE = campfirePotItem(CobblemonBlocks.BLUE_CAMPFIRE_POT, CampfirePotColor.BLUE)
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_PINK = campfirePotItem(CobblemonBlocks.PINK_CAMPFIRE_POT, "pink")
|
||||
val CAMPFIRE_POT_PINK = campfirePotItem(CobblemonBlocks.PINK_CAMPFIRE_POT, CampfirePotColor.PINK)
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_BLACK = campfirePotItem(CobblemonBlocks.BLACK_CAMPFIRE_POT, "black")
|
||||
val CAMPFIRE_POT_BLACK = campfirePotItem(CobblemonBlocks.BLACK_CAMPFIRE_POT, CampfirePotColor.BLACK)
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_WHITE = campfirePotItem(CobblemonBlocks.WHITE_CAMPFIRE_POT, "white")
|
||||
val CAMPFIRE_POT_WHITE = campfirePotItem(CobblemonBlocks.WHITE_CAMPFIRE_POT, CampfirePotColor.WHITE)
|
||||
|
||||
@JvmField
|
||||
val HEARTY_GRAINS = compostableItem("hearty_grains", ItemNameBlockItem(CobblemonBlocks.HEARTY_GRAINS, Properties().rarity(Rarity.COMMON)))
|
||||
@@ -400,7 +401,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val OLD_GATEAU = regionalFoodItem("old_gateau", 64, 9, 0.1335f, false)
|
||||
@JvmField
|
||||
val CASTELIACONE = regionalFoodItem("casteliacone", 64, 5, .28f, false)
|
||||
val CASTELIACONE = regionalFoodItem("casteliacone", 64, 5, 0.28f, false)
|
||||
@JvmField
|
||||
val LUMIOSE_GALETTE = regionalFoodItem("lumiose_galette", 64, 4, 0.4f, false)
|
||||
@JvmField
|
||||
@@ -695,7 +696,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)))
|
||||
val MOOMOO_MILK = create("moomoo_milk", CobblemonItem(Item.Properties().craftRemainder(Items.GLASS_BOTTLE).stacksTo((16))))
|
||||
|
||||
@JvmField
|
||||
val POTION = create("potion", PotionItem(PotionType.POTION))
|
||||
@@ -1500,8 +1501,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
}
|
||||
)
|
||||
|
||||
private fun campfirePotItem(block: Block, type: String): CampfirePotItem {
|
||||
val item = create("campfire_pot_${type}", CampfirePotItem(block))
|
||||
private fun campfirePotItem(block: Block, color: CampfirePotColor): CampfirePotItem {
|
||||
val item = create("campfire_pot_${color.suffix}", CampfirePotItem(block, color))
|
||||
campfire_pots.add(item)
|
||||
return item
|
||||
}
|
||||
@@ -1648,4 +1649,4 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
private fun foodItem(nutrition: Int, saturationModifier: Float): Item {
|
||||
return CobblemonItem(Item.Properties().food(FoodProperties.Builder().nutrition(nutrition).saturationModifier(saturationModifier).build()))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,7 @@ object CobblemonMechanics : DataRegistry {
|
||||
var potions = PotionsMechanic()
|
||||
|
||||
override fun sync(player: ServerPlayer) {}
|
||||
|
||||
override fun reload(manager: ResourceManager) {
|
||||
remedies = loadMechanic(manager, "remedies", RemediesMechanic::class.java)
|
||||
berries = loadMechanic(manager, "berries", BerriesMechanic::class.java)
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.client.net.OpenBehaviourEditorHandler
|
||||
import com.cobblemon.mod.common.client.net.CalculateSeatPositionsHandler
|
||||
import com.cobblemon.mod.common.client.net.OpenBehaviourEditorHandler
|
||||
import com.cobblemon.mod.common.client.net.PlayerInteractOptionsHandler
|
||||
import com.cobblemon.mod.common.client.net.SetClientPlayerDataHandler
|
||||
import com.cobblemon.mod.common.client.net.animation.PlayPosableAnimationHandler
|
||||
@@ -20,7 +20,7 @@ import com.cobblemon.mod.common.client.net.callback.party.OpenPartyCallbackHandl
|
||||
import com.cobblemon.mod.common.client.net.callback.partymove.OpenPartyMoveCallbackHandler
|
||||
import com.cobblemon.mod.common.client.net.cooking.ToggleCookingPotLidHandler
|
||||
import com.cobblemon.mod.common.client.net.data.DataRegistrySyncPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.data.UnlockReloadPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.data.RideSettingsSyncHandler
|
||||
import com.cobblemon.mod.common.client.net.dialogue.DialogueClosedHandler
|
||||
import com.cobblemon.mod.common.client.net.dialogue.DialogueOpenedHandler
|
||||
import com.cobblemon.mod.common.client.net.effect.PokeSnackBlockParticlesHandler
|
||||
@@ -65,8 +65,8 @@ import com.cobblemon.mod.common.client.net.trade.TradeProcessStartedHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeStartedHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeUpdatedHandler
|
||||
import com.cobblemon.mod.common.net.PacketRegisterInfo
|
||||
import com.cobblemon.mod.common.net.messages.client.OpenBehaviourEditorPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.CalculateSeatPositionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.OpenBehaviourEditorPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.PlayerInteractOptionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
|
||||
@@ -157,6 +157,7 @@ import com.cobblemon.mod.common.net.messages.server.callback.party.PartyPokemonS
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.party.PartySelectCancelledPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.partymove.PartyMoveSelectCancelledPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.partymove.PartyPokemonMoveSelectedPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.debug.ServerboundUpdateRidingSettingsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.debug.ServerboundUpdateRidingStatRangePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.debug.ServerboundUpdateRidingStatsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.dialogue.EscapeDialoguePacket
|
||||
@@ -170,11 +171,11 @@ import com.cobblemon.mod.common.net.messages.server.pasture.UnpasturePokemonPack
|
||||
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.FinishScanningPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.StartScanningPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.interact.InteractPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetItemHiddenPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetNicknamePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetActiveMarkPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetMarkingsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.ServerboundUpdateRidingStatePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetActiveMarkPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetItemHiddenPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetMarkingsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetNicknamePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.evolution.AcceptEvolutionPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.riding.DismountPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateDriverInputPacket
|
||||
@@ -208,6 +209,7 @@ import com.cobblemon.mod.common.net.serverhandling.callback.party.PartyPokemonSe
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.party.PartySelectCancelledHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.partymove.PartyMoveSelectCancelledHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.partymove.PartyPokemonMoveSelectedHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.debug.ServerboundUpdateRidingSettingsHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.debug.ServerboundUpdateRidingStatRangeHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.debug.ServerboundUpdateRidingStatsHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.dialogue.EscapeDialogueHandler
|
||||
@@ -222,11 +224,11 @@ import com.cobblemon.mod.common.net.serverhandling.pasture.UnpasturePokemonHandl
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.FinishScanningHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.StartScanningHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.interact.InteractPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetItemHiddenHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetNicknameHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetActiveMarkHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetMarkingsHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.ServerboundUpdateRidingStateHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetActiveMarkHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetItemHiddenHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetMarkingsHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetNicknameHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.riding.DismountPokemonPacketHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.riding.DriverInputPacketHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.starter.RequestStarterScreenHandler
|
||||
@@ -310,6 +312,7 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(MarksPotentialUpdatePacket.ID, MarksPotentialUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(MarkingsUpdatePacket.ID, MarkingsUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(RideBoostsUpdatePacket.ID, RideBoostsUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(RideStaminaUpdatePacket.ID, RideStaminaUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
|
||||
// Evolution start
|
||||
list.add(PacketRegisterInfo(AddEvolutionPacket.ID, AddEvolutionPacket::decode, PokemonUpdatePacketHandler()))
|
||||
@@ -388,7 +391,6 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(BerryRegistrySyncPacket.ID, BerryRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(SpeciesRegistrySyncPacket.ID, SpeciesRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(PropertiesCompletionRegistrySyncPacket.ID, PropertiesCompletionRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(UnlockReloadPacket.ID, UnlockReloadPacket::decode, UnlockReloadPacketHandler))
|
||||
list.add(PacketRegisterInfo(StandardSpeciesFeatureSyncPacket.ID, StandardSpeciesFeatureSyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(GlobalSpeciesFeatureSyncPacket.ID, GlobalSpeciesFeatureSyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(SpeciesFeatureAssignmentSyncPacket.ID, SpeciesFeatureAssignmentSyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
@@ -401,10 +403,11 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(DexEntrySyncPacket.ID, DexEntrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(SpawnBaitRegistrySyncPacket.ID, SpawnBaitRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(SeasoningRegistrySyncPacket.ID, SeasoningRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(FlowRegistrySyncPacket.ID, FlowRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(CallbackRegistrySyncPacket.ID, CallbackRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(CosmeticItemAssignmentSyncPacket.ID, CosmeticItemAssignmentSyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(BehaviourSyncPacket.ID, BehaviourSyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(MarkRegistrySyncPacket.ID, MarkRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(RideSettingsSyncPacket.ID, RideSettingsSyncPacket::decode, RideSettingsSyncHandler))
|
||||
|
||||
// Effects
|
||||
list.add(PacketRegisterInfo(SpawnSnowstormParticlePacket.ID, SpawnSnowstormParticlePacket::decode, SpawnSnowstormParticleHandler))
|
||||
@@ -578,6 +581,7 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateRidingStatePacket.ID, ServerboundUpdateRidingStatePacket::decode, ServerboundUpdateRidingStateHandler))
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateRidingStatsPacket.ID, ServerboundUpdateRidingStatsPacket::decode, ServerboundUpdateRidingStatsHandler))
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateRidingStatRangePacket.ID, ServerboundUpdateRidingStatRangePacket::decode, ServerboundUpdateRidingStatRangeHandler))
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateRidingSettingsPacket.ID, ServerboundUpdateRidingSettingsPacket::decode, ServerboundUpdateRidingSettingsHandler))
|
||||
list.add(PacketRegisterInfo(DismountPokemonPacket.ID, DismountPokemonPacket::decode, DismountPokemonPacketHandler))
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateDriverInputPacket.ID, ServerboundUpdateDriverInputPacket::decode, DriverInputPacketHandler))
|
||||
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.data.DataRegistry
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.BirdSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.GliderSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.HelicopterSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.HoverSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.JetSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.RocketSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.MinekartSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.VehicleSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BoatSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BurstSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.DolphinSettings
|
||||
import com.cobblemon.mod.common.net.messages.client.data.RideSettingsSyncPacket
|
||||
import com.cobblemon.mod.common.util.adapters.ExpressionAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.ExpressionLikeAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.FloatNumberRangeAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.GsonBuilder
|
||||
import net.minecraft.advancements.critereon.MinMaxBounds
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
import net.minecraft.server.packs.resources.ResourceManager
|
||||
|
||||
object CobblemonRideSettings : DataRegistry {
|
||||
override val id: ResourceLocation = cobblemonResource("ride_settings")
|
||||
override val type = PackType.SERVER_DATA
|
||||
override val observable = SimpleObservable<CobblemonRideSettings>()
|
||||
val gson = GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
|
||||
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
|
||||
.registerTypeAdapter(MinMaxBounds.Doubles::class.java, FloatNumberRangeAdapter)
|
||||
.create()
|
||||
|
||||
var bird = BirdSettings()
|
||||
var glider = GliderSettings()
|
||||
var helicopter = HelicopterSettings()
|
||||
var hover = HoverSettings()
|
||||
var jet = JetSettings()
|
||||
var rocket = RocketSettings()
|
||||
|
||||
var horse = HorseSettings()
|
||||
var minekart = MinekartSettings()
|
||||
var vehicle = VehicleSettings()
|
||||
|
||||
var boat = BoatSettings()
|
||||
var burst = BurstSettings()
|
||||
var dolphin = DolphinSettings()
|
||||
|
||||
override fun sync(player: ServerPlayer) {
|
||||
player.sendPacket(
|
||||
RideSettingsSyncPacket(
|
||||
bird = bird,
|
||||
glider = glider,
|
||||
helicopter = helicopter,
|
||||
hover = hover,
|
||||
jet = jet,
|
||||
rocket = rocket,
|
||||
horse = horse,
|
||||
minekart = minekart,
|
||||
vehicle = vehicle,
|
||||
boat = boat,
|
||||
burst = burst,
|
||||
dolphin = dolphin
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun reload(manager: ResourceManager) {
|
||||
bird = loadStyle(manager, "bird", BirdSettings::class.java)
|
||||
glider = loadStyle(manager, "glider", GliderSettings::class.java)
|
||||
helicopter = loadStyle(manager, "helicopter", HelicopterSettings::class.java)
|
||||
hover = loadStyle(manager, "hover", HoverSettings::class.java)
|
||||
jet = loadStyle(manager, "jet", JetSettings::class.java)
|
||||
rocket = loadStyle(manager, "rocket", RocketSettings::class.java)
|
||||
horse = loadStyle(manager, "horse", HorseSettings::class.java)
|
||||
minekart = loadStyle(manager, "minekart", MinekartSettings::class.java)
|
||||
vehicle = loadStyle(manager, "vehicle", VehicleSettings::class.java)
|
||||
boat = loadStyle(manager, "boat", BoatSettings::class.java)
|
||||
burst = loadStyle(manager, "burst", BurstSettings::class.java)
|
||||
dolphin = loadStyle(manager, "dolphin", DolphinSettings::class.java)
|
||||
}
|
||||
|
||||
private fun <T : RidingBehaviourSettings> loadStyle(manager: ResourceManager, name: String, clazz: Class<T>): T {
|
||||
manager.getResourceOrThrow(cobblemonResource("ride_settings/$name.json")).open().use {
|
||||
return gson.fromJson(it.reader(), clazz)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,6 +78,7 @@ object Abilities : DataRegistry {
|
||||
fun all() = this.abilityMap.values.toList()
|
||||
fun first() = this.abilityMap.values.first()
|
||||
fun get(name: String) = abilityMap[name.lowercase()]
|
||||
fun getOrDummy(name: String) = get(name) ?: DUMMY
|
||||
fun getOrException(name: String) = get(name) ?: throw IllegalArgumentException("Unable to find ability of name: $name")
|
||||
fun count() = this.abilityMap.size
|
||||
|
||||
|
||||
@@ -49,10 +49,9 @@ class AbilityTemplate(
|
||||
|
||||
@JvmStatic
|
||||
val CODEC: Codec<AbilityTemplate> = CodecUtils.createByStringCodec(
|
||||
Abilities::get,
|
||||
Abilities::getOrDummy,
|
||||
AbilityTemplate::name
|
||||
) { id -> "No ability for ID $id" }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -117,7 +117,7 @@ Example behaviour (`uses_healing_machine.json`):
|
||||
"configurations": [
|
||||
{
|
||||
"type": "add_tasks_to_activity",
|
||||
"activity": "idle",
|
||||
"activity": "minecraft:idle",
|
||||
"tasksByPriority": {
|
||||
"1": [
|
||||
"heal_using_healing_machine",
|
||||
|
||||
+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
|
||||
}
|
||||
|
||||
|
||||
+11
-10
@@ -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
|
||||
@@ -53,24 +52,25 @@ class FindHerdLeaderTaskConfig : SingleTaskConfig {
|
||||
it is PokemonEntity &&
|
||||
it != currentLeader &&
|
||||
it.pokemon.storeCoordinates.get()?.store !is PartyStore &&
|
||||
it.getHerdSize() < it.behaviour.herd.maxSize
|
||||
it.getHerdSize() < it.behaviour.herd.maxSize &&
|
||||
!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
|
||||
@@ -118,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,
|
||||
|
||||
+18
-26
@@ -9,44 +9,36 @@
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.ai.config.task.SharedEntityVariables.FLEE_DESIRED_DISTANCE
|
||||
import com.cobblemon.mod.common.api.ai.config.task.SharedEntityVariables.FLEE_SPEED_MULTIPLIER
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
|
||||
import com.cobblemon.mod.common.entity.ai.FleeFromAttackerTask
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.mojang.datafixers.util.Either
|
||||
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.SetWalkTargetAwayFrom
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.sensing.SensorType
|
||||
|
||||
class FleeAttackerTaskConfig : SingleTaskConfig {
|
||||
var condition = booleanVariable(SharedEntityVariables.FEAR_CATEGORY, "flee_attacker", true).asExpressible()
|
||||
var speedMultiplier = numberVariable(SharedEntityVariables.FEAR_CATEGORY, FLEE_SPEED_MULTIPLIER, 0.5).asExpressible()
|
||||
var desiredDistance = numberVariable(SharedEntityVariables.FEAR_CATEGORY, FLEE_DESIRED_DISTANCE, 9).asExpressible()
|
||||
val avoidDurationTicks: ExpressionOrEntityVariable = Either.left("600".asExpression())
|
||||
|
||||
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(
|
||||
condition,
|
||||
speedMultiplier,
|
||||
desiredDistance
|
||||
).asVariables()
|
||||
override fun getVariables(
|
||||
entity: LivingEntity,
|
||||
behaviourConfigurationContext: BehaviourConfigurationContext
|
||||
): List<MoLangConfigVariable> {
|
||||
return listOf(avoidDurationTicks).asVariables()
|
||||
}
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
behaviourConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
if (!condition.resolveBoolean(behaviourConfigurationContext.runtime) || entity !is PathfinderMob) return null
|
||||
|
||||
behaviourConfigurationContext.addMemories(MemoryModuleType.HURT_BY_ENTITY, MemoryModuleType.WALK_TARGET)
|
||||
behaviourConfigurationContext.addSensors(SensorType.HURT_BY)
|
||||
|
||||
val speedMultiplier = speedMultiplier.resolveFloat(behaviourConfigurationContext.runtime)
|
||||
val desiredDistance = desiredDistance.resolveInt(behaviourConfigurationContext.runtime)
|
||||
return WrapperLivingEntityTask(
|
||||
SetWalkTargetAwayFrom.entity(MemoryModuleType.HURT_BY_ENTITY, speedMultiplier, desiredDistance, false),
|
||||
PathfinderMob::class.java
|
||||
): BehaviorControl<in LivingEntity> {
|
||||
behaviourConfigurationContext.addMemories(
|
||||
MemoryModuleType.HURT_BY_ENTITY,
|
||||
MemoryModuleType.AVOID_TARGET
|
||||
)
|
||||
behaviourConfigurationContext.addSensors(SensorType.HURT_BY)
|
||||
return FleeFromAttackerTask.create(avoidDurationTicks.asExpression())
|
||||
}
|
||||
}
|
||||
+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
|
||||
|
||||
@@ -162,11 +162,11 @@ on the entity type and this piggy-backs off the Minecraft list of aggressive mob
|
||||
The 'calm_down' type creates a task that will make the entity calm down when it is no longer being hurt and is far from hostiles and its attacker.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
|
||||
### flee_attacker
|
||||
The 'flee_attacker' type creates a task that will make the entity flee from its attacker when it is hurt.
|
||||
### walk_away_from_avoid_target
|
||||
The 'flee_attacker' type creates a task that will make the entity flee from its avoid target.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
- `speedMultiplier`: A MoLang expression that determines the speed multiplier for fleeing from the attacker. Defaults to 0.5 which is an elevated speed to usual.
|
||||
- `desiredDistance`: A MoLang expression that determines the distance from the attacker that the entity should try to maintain. Defaults to 9.
|
||||
- `speedMultiplier`: A MoLang expression that determines the speed multiplier for fleeing from the avoid target. Defaults to 0.5 which is an elevated speed to usual.
|
||||
- `desiredDistance`: A MoLang expression that determines the distance from the target that the entity should try to maintain. Defaults to 9.
|
||||
|
||||
### flee_nearest_hostile
|
||||
The 'flee_nearest_hostile' type creates a task that will make the entity flee from the nearest hostile entity when it is hurt.
|
||||
|
||||
@@ -78,8 +78,9 @@ interface TaskConfig {
|
||||
cobblemonResource("switch_to_panic_when_hurt") to SwitchToPanicWhenHurtTaskConfig::class.java,
|
||||
cobblemonResource("switch_to_panic_when_hostiles_nearby") to SwitchToPanicWhenHostilesNearbyTaskConfig::class.java,
|
||||
cobblemonResource("calm_down") to CalmDownTaskConfig::class.java,
|
||||
cobblemonResource("flee_attacker") to FleeAttackerTaskConfig::class.java,
|
||||
cobblemonResource("walk_away_from_avoid_target") to WalkAwayFromAvoidTargetTaskConfig::class.java,
|
||||
cobblemonResource("flee_nearest_hostile") to FleeNearestHostileTaskConfig::class.java,
|
||||
cobblemonResource("flee_attacker") to FleeAttackerTaskConfig::class.java,
|
||||
cobblemonResource("fly_in_circles") to FlyInCirclesTaskConfig::class.java,
|
||||
cobblemonResource("run_script") to RunScript::class.java,
|
||||
cobblemonResource("look_in_direction") to LookInDirectionTaskConfig::class.java,
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
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.ai.config.task.SharedEntityVariables.FLEE_DESIRED_DISTANCE
|
||||
import com.cobblemon.mod.common.api.ai.config.task.SharedEntityVariables.FLEE_SPEED_MULTIPLIER
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.mojang.datafixers.util.Either
|
||||
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.SetWalkTargetAwayFrom
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.sensing.SensorType
|
||||
|
||||
class WalkAwayFromAvoidTargetTaskConfig : SingleTaskConfig {
|
||||
var condition: ExpressionOrEntityVariable = Either.left("true".asExpression())
|
||||
var speedMultiplier = numberVariable(SharedEntityVariables.FEAR_CATEGORY, FLEE_SPEED_MULTIPLIER, 0.7).asExpressible()
|
||||
var desiredDistance = numberVariable(SharedEntityVariables.FEAR_CATEGORY, FLEE_DESIRED_DISTANCE, 9).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(
|
||||
condition,
|
||||
speedMultiplier,
|
||||
desiredDistance
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
behaviourConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
if (!condition.resolveBoolean(behaviourConfigurationContext.runtime) || entity !is PathfinderMob) return null
|
||||
|
||||
behaviourConfigurationContext.addMemories(MemoryModuleType.HURT_BY_ENTITY, MemoryModuleType.WALK_TARGET)
|
||||
behaviourConfigurationContext.addSensors(SensorType.HURT_BY)
|
||||
|
||||
val speedMultiplier = speedMultiplier.resolveFloat(behaviourConfigurationContext.runtime)
|
||||
val desiredDistance = desiredDistance.resolveInt(behaviourConfigurationContext.runtime)
|
||||
return WrapperLivingEntityTask(
|
||||
SetWalkTargetAwayFrom.entity(MemoryModuleType.HURT_BY_ENTITY, speedMultiplier, desiredDistance, false),
|
||||
PathfinderMob::class.java
|
||||
)
|
||||
}
|
||||
}
|
||||
+64
-27
@@ -14,6 +14,8 @@ import com.cobblemon.mod.common.api.ai.CobblemonWanderControl
|
||||
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.entity.OmniPathingEntity
|
||||
import com.cobblemon.mod.common.entity.ai.CobblemonRandomSurfacePos
|
||||
import com.cobblemon.mod.common.entity.ai.CobblemonWalkTarget
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.mainThreadRuntime
|
||||
@@ -26,6 +28,7 @@ import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorUtils
|
||||
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
@@ -37,27 +40,31 @@ import net.minecraft.world.level.pathfinder.PathType
|
||||
class WanderTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val WANDER = "wander" // Category
|
||||
const val MAX_LOOK_DOWN_DISTANCE = 64
|
||||
}
|
||||
|
||||
val condition = booleanVariable(WANDER, "wanders", true).asExpressible()
|
||||
val wanderChance = numberVariable(WANDER, "wander_chance", 1/(20 * 6F)).asExpressible()
|
||||
val wanderChance = numberVariable(WANDER, "wander_chance", 1 / (20 * 6F)).asExpressible()
|
||||
val horizontalRange = numberVariable(WANDER, "horizontal_wander_range", 10).asExpressible()
|
||||
val verticalRange = numberVariable(WANDER, "vertical_wander_range", 5).asExpressible()
|
||||
val speedMultiplier = numberVariable(SharedEntityVariables.MOVEMENT_CATEGORY, SharedEntityVariables.WALK_SPEED, 0.35).asExpressible()
|
||||
val avoidTargetingAir: ExpressionOrEntityVariable = Either.left("true".asExpression()) // Whether to avoid air blocks when wandering
|
||||
val speedMultiplier =
|
||||
numberVariable(SharedEntityVariables.MOVEMENT_CATEGORY, SharedEntityVariables.WALK_SPEED, 0.35).asExpressible()
|
||||
val avoidTargetingAir: ExpressionOrEntityVariable =
|
||||
Either.left("true".asExpression()) // Whether to avoid air blocks when wandering
|
||||
val minimumHeight: ExpressionOrEntityVariable = Either.left("0".asExpression()) // Height off the ground
|
||||
val maximumHeight: ExpressionOrEntityVariable = Either.left("-1".asExpression()) // Height off the ground
|
||||
|
||||
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(
|
||||
condition,
|
||||
wanderChance,
|
||||
horizontalRange,
|
||||
verticalRange,
|
||||
speedMultiplier,
|
||||
avoidTargetingAir,
|
||||
minimumHeight,
|
||||
maximumHeight
|
||||
).asVariables()
|
||||
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) =
|
||||
listOf(
|
||||
condition,
|
||||
wanderChance,
|
||||
horizontalRange,
|
||||
verticalRange,
|
||||
speedMultiplier,
|
||||
avoidTargetingAir,
|
||||
minimumHeight,
|
||||
maximumHeight
|
||||
).asVariables()
|
||||
|
||||
private fun applyHeightConstraints(
|
||||
pos: BlockPos,
|
||||
@@ -73,10 +80,18 @@ class WanderTaskConfig : SingleTaskConfig {
|
||||
val altitude = if (!block.isAir) {
|
||||
0
|
||||
} else {
|
||||
(1..64).firstOrNull {
|
||||
(1..MAX_LOOK_DOWN_DISTANCE).firstOrNull {
|
||||
val newPos = pos.below(it)
|
||||
return@firstOrNull !world.getBlockState(newPos).isAir
|
||||
} ?: Int.MAX_VALUE
|
||||
if (!world.getBlockState(newPos).isAir) {
|
||||
return@firstOrNull true
|
||||
} else if (newPos.y <= world.minBuildHeight) {
|
||||
// Don't adjust downward into the void (Mostly if we're in the end)
|
||||
// This doesn't stop fliers from wandering off end islands,
|
||||
// but it does stop them from diving directly into the void.
|
||||
return pos
|
||||
}
|
||||
return@firstOrNull false
|
||||
} ?: MAX_LOOK_DOWN_DISTANCE
|
||||
}
|
||||
|
||||
if (altitude > maximumHeight && maximumHeight >= 0) {
|
||||
@@ -117,7 +132,7 @@ class WanderTaskConfig : SingleTaskConfig {
|
||||
it.registered(CobblemonMemories.WANDER_CONTROL)
|
||||
).apply(it) { walkTarget, lookTarget, pathCooldown, wanderControl ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob || entity.isUnderWater) {
|
||||
if (entity !is PathfinderMob || (entity.isUnderWater && !(entity.canBreatheUnderwater() && !((entity as OmniPathingEntity).canSwimInWater())))) {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
@@ -125,10 +140,9 @@ class WanderTaskConfig : SingleTaskConfig {
|
||||
mainThreadRuntime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
val avoidsTargetingAir = avoidTargetingAir.resolveBoolean(mainThreadRuntime)
|
||||
val wanderChance = mainThreadRuntime.resolveFloat(wanderChanceExpression)
|
||||
if (wanderChance <= 0 || world.random.nextFloat() > wanderChance || !wanderControl.allowLand) {
|
||||
if (wanderChance <= 0 || world.random.nextFloat() > wanderChance) {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
pathCooldown.setWithExpiry(true, wanderControl.pathCooldownTicks.toLong())
|
||||
val minimumHeight = minimumHeight.resolveInt(mainThreadRuntime)
|
||||
val maximumHeight = maximumHeight.resolveInt(mainThreadRuntime)
|
||||
@@ -141,21 +155,35 @@ class WanderTaskConfig : SingleTaskConfig {
|
||||
HoverRandomPos.getPos(
|
||||
entity,
|
||||
horizontalRange.resolveInt(mainThreadRuntime),
|
||||
maxOf(verticalRange.resolveInt(mainThreadRuntime), maximumHeight), // In case they fly up pretty high and need to find a way down
|
||||
verticalRange.resolveInt(mainThreadRuntime),
|
||||
entity.random.nextFloat() - 0.5,
|
||||
entity.random.nextFloat() - 0.5,
|
||||
Math.PI.toFloat(),
|
||||
maximumHeight,
|
||||
minimumHeight
|
||||
)
|
||||
} else if (entity.isUnderWater && entity.canBreatheUnderwater() && !((entity as OmniPathingEntity).canSwimInWater())) {
|
||||
BehaviorUtils.getRandomSwimmablePos(
|
||||
entity,
|
||||
horizontalRange.resolveInt(mainThreadRuntime),
|
||||
verticalRange.resolveInt(mainThreadRuntime)
|
||||
)
|
||||
} else if (entity is OmniPathingEntity && (entity.canWalkOnWater() || entity.canWalkOnLava())) {
|
||||
CobblemonRandomSurfacePos.getPos(
|
||||
entity,
|
||||
horizontalRange.resolveInt(mainThreadRuntime),
|
||||
verticalRange.resolveInt(mainThreadRuntime)
|
||||
)
|
||||
} else {
|
||||
LandRandomPos.getPos(
|
||||
entity,
|
||||
horizontalRange.resolveInt(mainThreadRuntime),
|
||||
verticalRange.resolveInt(mainThreadRuntime)
|
||||
)
|
||||
LandRandomPos.getPos(
|
||||
entity,
|
||||
horizontalRange.resolveInt(mainThreadRuntime),
|
||||
verticalRange.resolveInt(mainThreadRuntime)
|
||||
)
|
||||
} ?: continue
|
||||
|
||||
if (targetVec == null) continue
|
||||
|
||||
pos = applyHeightConstraints(
|
||||
pos = BlockPos.containing(targetVec),
|
||||
minimumHeight = minimumHeight,
|
||||
@@ -173,8 +201,17 @@ class WanderTaskConfig : SingleTaskConfig {
|
||||
pos = pos,
|
||||
speedModifier = speedMultiplier.resolveFloat(mainThreadRuntime),
|
||||
completionRange = 0,
|
||||
nodeTypeFilter = { nodeType -> nodeType !in listOf(PathType.WATER, PathType.WATER_BORDER) },
|
||||
destinationNodeTypeFilter = { nodeType -> !avoidsTargetingAir || nodeType !in listOf(PathType.OPEN) }
|
||||
nodeTypeFilter = { nodeType ->
|
||||
entity.canBreatheUnderwater() || nodeType !in listOf(
|
||||
PathType.WATER,
|
||||
PathType.WATER_BORDER
|
||||
)
|
||||
},
|
||||
destinationNodeTypeFilter = { nodeType ->
|
||||
!avoidsTargetingAir || nodeType !in listOf(
|
||||
PathType.OPEN
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
lookTarget.set(BlockPosTracker(pos.toVec3d().add(0.0, entity.eyeHeight.toDouble(), 0.0)))
|
||||
|
||||
+30
-1
@@ -31,15 +31,17 @@ 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()
|
||||
val wanderChance = numberVariable(WANDER, "water_wander_chance", 1/(20 * 3F)).asExpressible()
|
||||
val speedMultiplier = numberVariable(SharedEntityVariables.MOVEMENT_CATEGORY, SharedEntityVariables.WALK_SPEED, 0.35).asExpressible()
|
||||
val horizontalRange: ExpressionOrEntityVariable = Either.left("10.0".asExpression())
|
||||
val verticalRange: ExpressionOrEntityVariable = Either.left("3.0".asExpression())
|
||||
val verticalRange: ExpressionOrEntityVariable = Either.left("7.0".asExpression())
|
||||
|
||||
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext): List<MoLangConfigVariable> {
|
||||
return listOf(condition, wanderChance, speedMultiplier).asVariables()
|
||||
@@ -100,6 +102,33 @@ class WaterWanderTaskConfig : SingleTaskConfig {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
// Move target pos down so that we don't move out of water too much
|
||||
val requiredDepth = ceil(0.5 + entity.eyeHeight)
|
||||
// Get the current depth of the block
|
||||
var depth = 1
|
||||
var testPos = pos.above()
|
||||
var blockState = entity.level().getBlockState(testPos)
|
||||
while (blockState.fluidState.type == Fluids.WATER) {
|
||||
depth++
|
||||
testPos = testPos!!.above()
|
||||
blockState = entity.level().getBlockState(testPos)
|
||||
}
|
||||
|
||||
if (depth < requiredDepth) {
|
||||
// Iterate down until we reach the desired min depth or we reach a non water block
|
||||
pos = pos.below()
|
||||
blockState = entity.level().getBlockState(pos)
|
||||
while (blockState.fluidState.type == Fluids.WATER && depth < requiredDepth) {
|
||||
depth ++
|
||||
pos = pos!!.below()
|
||||
blockState = entity.level().getBlockState(pos)
|
||||
}
|
||||
}
|
||||
|
||||
if (depth < requiredDepth) {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
walkTarget.set(
|
||||
CobblemonWalkTarget(
|
||||
pos = pos,
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.cooking
|
||||
|
||||
import net.minecraft.ChatFormatting
|
||||
import net.minecraft.util.StringRepresentable
|
||||
|
||||
/**
|
||||
@@ -18,16 +17,16 @@ import net.minecraft.util.StringRepresentable
|
||||
* @author Licious
|
||||
* @since November 28th, 2022
|
||||
*/
|
||||
enum class Flavour(val colour: Int, val chatFormatting: ChatFormatting): StringRepresentable {
|
||||
SPICY(0x00FF00, ChatFormatting.RED),
|
||||
DRY(0xFF0000, ChatFormatting.DARK_AQUA),
|
||||
SWEET(0x0000FF, ChatFormatting.LIGHT_PURPLE),
|
||||
BITTER(0xFFFF00, ChatFormatting.GREEN),
|
||||
SOUR(0xFF00FF, ChatFormatting.YELLOW),
|
||||
MILD(0xFF00FF, ChatFormatting.DARK_PURPLE);
|
||||
enum class Flavour(val colour: Int): StringRepresentable {
|
||||
SPICY(0xFA795E),
|
||||
DRY(0x65AFF3),
|
||||
SWEET(0xFF82CF),
|
||||
BITTER(0x77C96A),
|
||||
SOUR(0xF3D465),
|
||||
MILD(0x9D99BF);
|
||||
|
||||
override fun getSerializedName() = name
|
||||
companion object {
|
||||
val CODEC = StringRepresentable.fromEnum<Flavour> { Flavour.entries.toTypedArray() }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,13 +9,11 @@
|
||||
package com.cobblemon.mod.common.api.cooking
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItemComponents
|
||||
import com.cobblemon.mod.common.item.PokePuffItem
|
||||
import com.cobblemon.mod.common.item.PokePuffItem.Companion.DISLIKED_FLAVOR_MULTIPLIER
|
||||
import com.cobblemon.mod.common.item.PokePuffItem.Companion.LIKED_FLAVOR_MULTIPLIER
|
||||
import com.cobblemon.mod.common.pokemon.Nature
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
object PokePuffUtils {
|
||||
|
||||
@@ -25,11 +23,12 @@ object PokePuffUtils {
|
||||
|
||||
val (baseFriendship) = calculateBaseFriendship(stack)
|
||||
|
||||
return when {
|
||||
dominantFlavour == nature.favouriteFlavour -> (baseFriendship / LIKED_FLAVOR_MULTIPLIER).toInt()
|
||||
dominantFlavour == Flavour.MILD && isNeutralNature -> (baseFriendship / 2.0).coerceAtLeast(1.0).toInt()
|
||||
else -> (-baseFriendship / DISLIKED_FLAVOR_MULTIPLIER).toInt()
|
||||
}
|
||||
val multiplier = if (
|
||||
dominantFlavour == nature.favouriteFlavour ||
|
||||
(dominantFlavour == Flavour.MILD && isNeutralNature)
|
||||
) LIKED_FLAVOR_MULTIPLIER else -DISLIKED_FLAVOR_MULTIPLIER
|
||||
|
||||
return (baseFriendship / multiplier).toInt()
|
||||
}
|
||||
|
||||
fun calculateBaseFriendship(stack: ItemStack): Pair<Double, Double> {
|
||||
@@ -77,8 +76,8 @@ object PokePuffUtils {
|
||||
Flavour.SPICY -> "attack"
|
||||
Flavour.DRY -> "special_attack"
|
||||
Flavour.SWEET -> "speed"
|
||||
Flavour.BITTER -> "special_defense"
|
||||
Flavour.SOUR -> "defense"
|
||||
Flavour.BITTER -> "special_defence"
|
||||
Flavour.SOUR -> "defence"
|
||||
Flavour.MILD -> "neutral"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,14 +18,13 @@ import net.minecraft.resources.ResourceLocation
|
||||
* @since August 5th, 2022
|
||||
*/
|
||||
interface DataProvider {
|
||||
|
||||
/**
|
||||
* Registers a [DataRegistry] to listen for updates.
|
||||
* The updates will automatically happen on the correct sides based on [DataRegistry.type].
|
||||
*
|
||||
* @param registry The [DataRegistry] being registered.
|
||||
*/
|
||||
fun <T : DataRegistry> register(registry: T): T
|
||||
fun <T : DataRegistry> register(registry: T, reloadable: Boolean): T
|
||||
|
||||
/**
|
||||
* Attempts to find a [DataRegistry] with the given [ResourceLocation].
|
||||
|
||||
@@ -29,6 +29,7 @@ class DialoguePage(
|
||||
var id: String = "",
|
||||
var speaker: String? = null,
|
||||
var lines: MutableList<DialogueText> = mutableListOf(),
|
||||
var textColor: String? = null,
|
||||
var input: DialogueInput = DialogueNoInput(),
|
||||
var background: ResourceLocation? = null,
|
||||
var gibber: DialogueGibber? = null,
|
||||
@@ -42,6 +43,7 @@ class DialoguePage(
|
||||
id: String = "",
|
||||
speaker: String? = null,
|
||||
lines: Iterable<MutableComponent>,
|
||||
textColor: String? = null,
|
||||
input: DialogueInput = DialogueNoInput(),
|
||||
gibber: DialogueGibber? = null,
|
||||
background: ResourceLocation? = null,
|
||||
@@ -53,6 +55,7 @@ class DialoguePage(
|
||||
id = id,
|
||||
speaker = speaker,
|
||||
lines = lines.map { WrappedDialogueText(it) }.toMutableList(),
|
||||
textColor = textColor,
|
||||
input = input,
|
||||
gibber = gibber,
|
||||
background = background,
|
||||
@@ -75,4 +78,4 @@ class DialoguePage(
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,7 @@ package com.cobblemon.mod.common.api.events
|
||||
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleFaintedEvent
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleFledEvent
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleStartedPostEvent
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleStartedPreEvent
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleStartedEvent
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleVictoryEvent
|
||||
import com.cobblemon.mod.common.api.events.battles.instruction.FormeChangeEvent
|
||||
import com.cobblemon.mod.common.api.events.battles.instruction.MegaEvolutionEvent
|
||||
@@ -22,11 +21,7 @@ import com.cobblemon.mod.common.api.events.berry.BerryMutationOfferEvent
|
||||
import com.cobblemon.mod.common.api.events.berry.BerryMutationResultEvent
|
||||
import com.cobblemon.mod.common.api.events.berry.BerryYieldCalculationEvent
|
||||
import com.cobblemon.mod.common.api.events.drops.LootDroppedEvent
|
||||
import com.cobblemon.mod.common.api.events.entity.PokemonEntityLoadEvent
|
||||
import com.cobblemon.mod.common.api.events.entity.PokemonEntitySaveEvent
|
||||
import com.cobblemon.mod.common.api.events.entity.PokemonEntitySaveToWorldEvent
|
||||
import com.cobblemon.mod.common.api.events.entity.SpawnBucketChosenEvent
|
||||
import com.cobblemon.mod.common.api.events.entity.SpawnEvent
|
||||
import com.cobblemon.mod.common.api.events.entity.*
|
||||
import com.cobblemon.mod.common.api.events.farming.ApricornHarvestEvent
|
||||
import com.cobblemon.mod.common.api.events.fishing.*
|
||||
import com.cobblemon.mod.common.api.events.item.LeftoversCreatedEvent
|
||||
@@ -43,11 +38,7 @@ import com.cobblemon.mod.common.api.events.pokemon.healing.PokemonHealedEvent
|
||||
import com.cobblemon.mod.common.api.events.pokemon.interaction.ExperienceCandyUseEvent
|
||||
import com.cobblemon.mod.common.api.events.pokemon.interaction.PokemonInteractionGUICreationEvent
|
||||
import com.cobblemon.mod.common.api.events.starter.StarterChosenEvent
|
||||
import com.cobblemon.mod.common.api.events.storage.ChangePCBoxWallpaperEvent
|
||||
import com.cobblemon.mod.common.api.events.storage.ReleasePokemonEvent
|
||||
import com.cobblemon.mod.common.api.events.storage.RenamePCBoxEvent
|
||||
import com.cobblemon.mod.common.api.events.storage.WallpaperCollectionEvent
|
||||
import com.cobblemon.mod.common.api.events.storage.WallpaperUnlockedEvent
|
||||
import com.cobblemon.mod.common.api.events.storage.*
|
||||
import com.cobblemon.mod.common.api.events.world.BigRootPropagatedEvent
|
||||
import com.cobblemon.mod.common.api.reactive.CancelableObservable
|
||||
import com.cobblemon.mod.common.api.reactive.EventObservable
|
||||
@@ -63,69 +54,109 @@ object CobblemonEvents {
|
||||
|
||||
@JvmField
|
||||
val POKEMON_PROPERTY_INITIALISED = SimpleObservable<Unit>()
|
||||
|
||||
@JvmField
|
||||
val COBBLEMON_INITIALISED = SimpleObservable<Unit>()
|
||||
|
||||
@JvmField
|
||||
val DATA_SYNCHRONIZED = SimpleObservable<ServerPlayer>()
|
||||
|
||||
@JvmField
|
||||
val SHOULDER_MOUNT = CancelableObservable<ShoulderMountEvent>()
|
||||
|
||||
@JvmField
|
||||
val FRIENDSHIP_UPDATED = EventObservable<FriendshipUpdatedEvent>()
|
||||
|
||||
@JvmField
|
||||
val FULLNESS_UPDATED = EventObservable<FullnessUpdatedEvent>()
|
||||
|
||||
@JvmField
|
||||
val POKEMON_FAINTED = EventObservable<PokemonFaintedEvent>()
|
||||
|
||||
@JvmField
|
||||
val EVOLUTION_ACCEPTED = CancelableObservable<EvolutionAcceptedEvent>()
|
||||
|
||||
@JvmField
|
||||
val EVOLUTION_DISPLAY = EventObservable<EvolutionDisplayEvent>()
|
||||
|
||||
@JvmField
|
||||
val EVOLUTION_TESTED = EventObservable<EvolutionTestedEvent>()
|
||||
|
||||
@JvmField
|
||||
val EVOLUTION_COMPLETE = EventObservable<EvolutionCompleteEvent>()
|
||||
|
||||
@JvmField
|
||||
val POKEMON_NICKNAMED = CancelableObservable<PokemonNicknamedEvent>()
|
||||
|
||||
@JvmField
|
||||
val THROWN_POKEBALL_HIT = CancelableObservable<ThrownPokeballHitEvent>()
|
||||
|
||||
@JvmField
|
||||
val POKEMON_CATCH_RATE = EventObservable<PokemonCatchRateEvent>()
|
||||
|
||||
@JvmField
|
||||
val POKE_BALL_CAPTURE_CALCULATED = EventObservable<PokeBallCaptureCalculatedEvent>()
|
||||
|
||||
@JvmField
|
||||
val POKEMON_CAPTURED = EventObservable<PokemonCapturedEvent>()
|
||||
|
||||
@JvmField
|
||||
val FOSSIL_REVIVED = EventObservable<FossilRevivedEvent>()
|
||||
|
||||
@JvmField
|
||||
val BATTLE_STARTED_PRE = CancelableObservable<BattleStartedPreEvent>()
|
||||
val BATTLE_STARTED_PRE = CancelableObservable<BattleStartedEvent.Pre>()
|
||||
|
||||
@JvmField
|
||||
val BATTLE_STARTED_POST = EventObservable<BattleStartedPostEvent>()
|
||||
val BATTLE_STARTED_POST = EventObservable<BattleStartedEvent.Post>()
|
||||
|
||||
@JvmField
|
||||
val BATTLE_FLED = EventObservable<BattleFledEvent>()
|
||||
|
||||
@JvmField
|
||||
val BATTLE_VICTORY = EventObservable<BattleVictoryEvent>()
|
||||
|
||||
@JvmField
|
||||
val BATTLE_FAINTED = EventObservable<BattleFaintedEvent>()
|
||||
|
||||
// instructions
|
||||
@JvmField
|
||||
val MEGA_EVOLUTION = EventObservable<MegaEvolutionEvent>()
|
||||
|
||||
@JvmField
|
||||
val TERASTALLIZATION = EventObservable<TerastallizationEvent>()
|
||||
|
||||
@JvmField
|
||||
val ZPOWER_USED = EventObservable<ZMoveUsedEvent>()
|
||||
|
||||
@JvmField
|
||||
val FORME_CHANGE = EventObservable<FormeChangeEvent>()
|
||||
|
||||
@JvmField
|
||||
val POKEMON_SENT_PRE = CancelableObservable<PokemonSentPreEvent>()
|
||||
@JvmField
|
||||
val POKEMON_SENT_POST = EventObservable<PokemonSentPostEvent>()
|
||||
@JvmField
|
||||
val POKEMON_RECALLED = EventObservable<PokemonRecalledEvent>()
|
||||
val POKEMON_SENT_PRE = CancelableObservable<PokemonSentEvent.Pre>()
|
||||
|
||||
@JvmField
|
||||
val TRADE_COMPLETED = EventObservable<TradeCompletedEvent>()
|
||||
val POKEMON_SENT_POST = EventObservable<PokemonSentEvent.Post>()
|
||||
|
||||
@JvmField
|
||||
val POKEMON_RECALL_PRE = CancelableObservable<PokemonRecallEvent.Pre>()
|
||||
|
||||
@JvmField
|
||||
val POKEMON_RECALL_POST = EventObservable<PokemonRecallEvent.Post>()
|
||||
|
||||
@JvmField
|
||||
val TRADE_EVENT_PRE = CancelableObservable<TradeEvent.Pre>()
|
||||
|
||||
@JvmField
|
||||
val TRADE_EVENT_POST = EventObservable<TradeEvent.Post>()
|
||||
|
||||
@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>()
|
||||
|
||||
@JvmField
|
||||
val LEVEL_UP_EVENT = EventObservable<LevelUpEvent>()
|
||||
@@ -134,16 +165,21 @@ object CobblemonEvents {
|
||||
val POKEMON_HEALED = CancelableObservable<PokemonHealedEvent>()
|
||||
|
||||
@JvmField
|
||||
/** CLIENT ONLY! */
|
||||
/** CLIENT ONLY! */
|
||||
val POKEMON_INTERACTION_GUI_CREATION = EventObservable<PokemonInteractionGUICreationEvent>()
|
||||
|
||||
@JvmField
|
||||
val POKEMON_ENTITY_SAVE = EventObservable<PokemonEntitySaveEvent>()
|
||||
|
||||
@JvmField
|
||||
val POKEMON_ENTITY_LOAD = CancelableObservable<PokemonEntityLoadEvent>()
|
||||
|
||||
@JvmField
|
||||
val POKEMON_ENTITY_SAVE_TO_WORLD = CancelableObservable<PokemonEntitySaveToWorldEvent>()
|
||||
|
||||
@JvmField
|
||||
val ENTITY_SPAWN = CancelableObservable<SpawnEvent<*>>()
|
||||
|
||||
@JvmField
|
||||
val SHINY_CHANCE_CALCULATION = EventObservable<ShinyChanceCalculationEvent>()
|
||||
|
||||
@@ -158,46 +194,56 @@ object CobblemonEvents {
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val EXPERIENCE_GAINED_EVENT_PRE = CancelableObservable<ExperienceGainedPreEvent>()
|
||||
val EXPERIENCE_GAINED_EVENT_PRE = CancelableObservable<ExperienceGainedEvent.Pre>()
|
||||
|
||||
@JvmField
|
||||
val EXPERIENCE_GAINED_EVENT_POST = EventObservable<ExperienceGainedPostEvent>()
|
||||
val EXPERIENCE_GAINED_EVENT_POST = EventObservable<ExperienceGainedEvent.Post>()
|
||||
|
||||
@JvmField
|
||||
val EXPERIENCE_CANDY_USE_PRE = CancelableObservable<ExperienceCandyUseEvent.Pre>()
|
||||
|
||||
@JvmField
|
||||
val EXPERIENCE_CANDY_USE_POST = EventObservable<ExperienceCandyUseEvent.Post>()
|
||||
|
||||
@JvmField
|
||||
val EV_GAINED_EVENT_PRE = CancelableObservable<EvGainedEvent.Pre>()
|
||||
|
||||
@JvmField
|
||||
val EV_GAINED_EVENT_POST = EventObservable<EvGainedEvent.Post>()
|
||||
|
||||
@JvmField
|
||||
val HYPER_TRAINED_IV_PRE = CancelableObservable<HyperTrainedIvEvent.Pre>()
|
||||
|
||||
@JvmField
|
||||
val HYPER_TRAINED_IV_POST = EventObservable<HyperTrainedIvEvent.Post>()
|
||||
|
||||
@JvmField
|
||||
val POKEMON_RELEASED_EVENT_PRE = CancelableObservable<ReleasePokemonEvent.Pre>()
|
||||
|
||||
@JvmField
|
||||
val POKEMON_RELEASED_EVENT_POST = EventObservable<ReleasePokemonEvent.Post>()
|
||||
|
||||
@JvmField
|
||||
val RENAME_PC_BOX_EVENT_PRE = CancelableObservable<RenamePCBoxEvent.Pre>()
|
||||
|
||||
@JvmField
|
||||
val RENAME_PC_BOX_EVENT_POST = EventObservable<RenamePCBoxEvent.Post>()
|
||||
|
||||
@JvmField
|
||||
val WALLPAPER_COLLECTION_EVENT = EventObservable<WallpaperCollectionEvent>()
|
||||
|
||||
@JvmField
|
||||
val WALLPAPER_UNLOCKED_EVENT = CancelableObservable<WallpaperUnlockedEvent>()
|
||||
|
||||
@JvmField
|
||||
val CHANGE_PC_BOX_WALLPAPER_EVENT_PRE = CancelableObservable<ChangePCBoxWallpaperEvent.Pre>()
|
||||
|
||||
@JvmField
|
||||
val CHANGE_PC_BOX_WALLPAPER_EVENT_POST = EventObservable<ChangePCBoxWallpaperEvent.Post>()
|
||||
|
||||
@JvmField
|
||||
val LOOT_DROPPED = CancelableObservable<LootDroppedEvent>()
|
||||
|
||||
@JvmField
|
||||
val STARTER_CHOSEN = CancelableObservable<StarterChosenEvent>()
|
||||
|
||||
@@ -206,58 +252,81 @@ object CobblemonEvents {
|
||||
|
||||
@JvmField
|
||||
val APRICORN_HARVESTED = EventObservable<ApricornHarvestEvent>()
|
||||
|
||||
// Berries
|
||||
@JvmField
|
||||
val BERRY_HARVEST = EventObservable<BerryHarvestEvent>()
|
||||
|
||||
@JvmField
|
||||
val BERRY_MUTATION_OFFER = EventObservable<BerryMutationOfferEvent>()
|
||||
|
||||
@JvmField
|
||||
val BERRY_MUTATION_RESULT = EventObservable<BerryMutationResultEvent>()
|
||||
|
||||
@JvmField
|
||||
val BERRY_YIELD = EventObservable<BerryYieldCalculationEvent>()
|
||||
|
||||
@JvmField
|
||||
val LEFTOVERS_CREATED = CancelableObservable<LeftoversCreatedEvent>()
|
||||
|
||||
@JvmField
|
||||
val BIG_ROOT_PROPAGATED = CancelableObservable<BigRootPropagatedEvent>()
|
||||
|
||||
@JvmField
|
||||
val HELD_ITEM_PRE = CancelableObservable<HeldItemEvent.Pre>()
|
||||
|
||||
@JvmField
|
||||
val HELD_ITEM_POST = EventObservable<HeldItemEvent.Post>()
|
||||
|
||||
@JvmField
|
||||
val COSMETIC_ITEM_PRE = CancelableObservable<HeldItemEvent.Pre>()
|
||||
|
||||
@JvmField
|
||||
val COSMETIC_ITEM_POST = EventObservable<HeldItemEvent.Post>()
|
||||
|
||||
@JvmField
|
||||
val POKEMON_GAINED = EventObservable<PokemonGainedEvent>()
|
||||
|
||||
@JvmField
|
||||
val POKEMON_SEEN = CancelableObservable<PokemonSeenEvent>()
|
||||
|
||||
@JvmField
|
||||
val POKEMON_ASPECTS_CHANGED = EventObservable<PokemonAspectsChangedEvent>()
|
||||
|
||||
@JvmField
|
||||
val POKEDEX_DATA_CHANGED_PRE = CancelableObservable<PokedexDataChangedEvent.Pre>()
|
||||
|
||||
@JvmField
|
||||
val POKEDEX_DATA_CHANGED_POST = EventObservable<PokedexDataChangedEvent.Post>()
|
||||
|
||||
// Fishing
|
||||
@JvmField
|
||||
val BAIT_SET = CancelableObservable<BaitSetEvent>()
|
||||
|
||||
@JvmField
|
||||
val BAIT_SET_PRE = CancelableObservable<BaitSetEvent>()
|
||||
|
||||
@JvmField
|
||||
val BAIT_CONSUMED = CancelableObservable<BaitConsumedEvent>()
|
||||
|
||||
@JvmField
|
||||
val POKEROD_CAST_PRE = CancelableObservable<PokerodCastEvent.Pre>()
|
||||
|
||||
@JvmField
|
||||
val POKEROD_CAST_POST = EventObservable<PokerodCastEvent.Post>()
|
||||
|
||||
@JvmField
|
||||
val POKEROD_REEL = CancelableObservable<PokerodReelEvent>()
|
||||
|
||||
@JvmField
|
||||
val BOBBER_SPAWN_POKEMON_PRE = CancelableObservable<BobberSpawnPokemonEvent.Pre>()
|
||||
|
||||
@JvmField
|
||||
val BOBBER_SPAWN_POKEMON_MODIFY = EventObservable<BobberSpawnPokemonEvent.Modify>()
|
||||
|
||||
@JvmField
|
||||
val BOBBER_SPAWN_POKEMON_POST = EventObservable<BobberSpawnPokemonEvent.Post>()
|
||||
|
||||
@JvmField
|
||||
val BAIT_EFFECT_REGISTRATION = EventObservable<BaitEffectFunctionRegistryEvent>()
|
||||
|
||||
@@ -271,8 +340,10 @@ object CobblemonEvents {
|
||||
|
||||
@JvmField
|
||||
val COLLECT_EGG = CancelableObservable<CollectEggEvent>()
|
||||
|
||||
@JvmField
|
||||
val HATCH_EGG_PRE = CancelableObservable<HatchEggEvent.Pre>()
|
||||
|
||||
@JvmField
|
||||
val HATCH_EGG_POST = EventObservable<HatchEggEvent.Post>()
|
||||
|
||||
|
||||
+62
-27
@@ -12,40 +12,75 @@ import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.cobblemon.mod.common.battles.ActiveBattlePokemon
|
||||
import com.cobblemon.mod.common.battles.BattleSide
|
||||
import com.cobblemon.mod.common.util.asArrayValue
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
|
||||
/**
|
||||
* Event fired before a [PokemonBattle] is started. Canceling this event prevents the battle from being
|
||||
* created and launched.
|
||||
* Events regarding the starting of a [PokemonBattle].
|
||||
*
|
||||
* @property reason The text used to inform the participants why the battle was canceled. Keep null for a default error.
|
||||
*
|
||||
* @author Segfault Guy
|
||||
* @author Segfault Guy/MeAlam
|
||||
* @since March 26th 2023
|
||||
*/
|
||||
data class BattleStartedPreEvent (override val battle: PokemonBattle, var reason: MutableComponent? = null) : BattleEvent, Cancelable() {
|
||||
val context = mutableMapOf(
|
||||
"battle" to battle.struct,
|
||||
"players" to battle.actors.filter { it.type == ActorType.PLAYER }.asArrayValue { it.struct },
|
||||
"npcs" to battle.actors.filter { it.type == ActorType.NPC }.asArrayValue { it.struct },
|
||||
"wild_pokemon" to battle.actors.filter { it.type == ActorType.WILD }.asArrayValue { it.struct }
|
||||
)
|
||||
interface BattleStartedEvent : BattleEvent {
|
||||
/**
|
||||
* The battle that is starting.
|
||||
*/
|
||||
override val battle: PokemonBattle
|
||||
|
||||
val functions = moLangFunctionMap(cancelFunc)
|
||||
}
|
||||
/**
|
||||
* Event fired before a [PokemonBattle] is started. Canceling this event prevents the battle from being
|
||||
* created and launched.
|
||||
*
|
||||
* @property reason The text used to inform the participants why the battle was canceled. Keep null for a default error.
|
||||
*
|
||||
* @author Segfault Guy/MeAlam
|
||||
* @since March 26th 2023
|
||||
*/
|
||||
data class Pre(
|
||||
override val battle: PokemonBattle,
|
||||
var reason: MutableComponent? = null
|
||||
) : BattleStartedEvent, Cancelable() {
|
||||
val context = mutableMapOf(
|
||||
"battle" to battle.struct,
|
||||
"players" to battle.actors.filter { it.type == ActorType.PLAYER }.asArrayValue { it.struct },
|
||||
"npcs" to battle.actors.filter { it.type == ActorType.NPC }.asArrayValue { it.struct },
|
||||
"wild_pokemon" to battle.actors.filter { it.type == ActorType.WILD }.asArrayValue { it.struct }
|
||||
)
|
||||
|
||||
val functions = moLangFunctionMap(cancelFunc)
|
||||
|
||||
fun getBattleSide(side: Int) : BattleSide? {
|
||||
return when (side) {
|
||||
1 -> battle.side1
|
||||
2 -> battle.side2
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun getPokemonOnSide(side: Int) : List<ActiveBattlePokemon>? {
|
||||
val battleSide = getBattleSide(side)
|
||||
val activePokemon = battleSide?.activePokemon
|
||||
return activePokemon
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event fired after a [PokemonBattle] starts.
|
||||
*
|
||||
* @author Segfault Guy/MeAlam
|
||||
* @since March 26th 2023
|
||||
*/
|
||||
data class Post(
|
||||
override val battle: PokemonBattle
|
||||
) : BattleStartedEvent {
|
||||
val context = mutableMapOf(
|
||||
"battle" to battle.struct,
|
||||
"players" to battle.actors.filter { it.type == ActorType.PLAYER }.asArrayValue { it.struct },
|
||||
"npcs" to battle.actors.filter { it.type == ActorType.NPC }.asArrayValue { it.struct },
|
||||
"wild_pokemon" to battle.actors.filter { it.type == ActorType.WILD }.asArrayValue { it.struct }
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Event fired after a [PokemonBattle] starts.
|
||||
*
|
||||
* @author Segfault Guy
|
||||
* @since March 26th 2023
|
||||
*/
|
||||
data class BattleStartedPostEvent (override val battle: PokemonBattle) : BattleEvent {
|
||||
val context = mutableMapOf(
|
||||
"battle" to battle.struct,
|
||||
"players" to battle.actors.filter { it.type == ActorType.PLAYER }.asArrayValue { it.struct },
|
||||
"npcs" to battle.actors.filter { it.type == ActorType.NPC }.asArrayValue { it.struct },
|
||||
"wild_pokemon" to battle.actors.filter { it.type == ActorType.WILD }.asArrayValue { it.struct }
|
||||
)
|
||||
}
|
||||
@@ -8,11 +8,9 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.fishing
|
||||
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.cobblemon.mod.common.api.reactive.CancelableObservable
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
@@ -22,10 +20,12 @@ import net.minecraft.world.item.ItemStack
|
||||
* @param bait The ItemStack of the bait that is set on the rod.
|
||||
*/
|
||||
class BaitSetEvent(val rod: ItemStack, val bait: ItemStack) : Cancelable() {
|
||||
val context = mutableMapOf(
|
||||
"rod" to rod.asMoLangValue(server()!!.registryAccess()),
|
||||
"bait" to bait.asMoLangValue(server()!!.registryAccess())
|
||||
)
|
||||
val context = server()?.let { server ->
|
||||
mutableMapOf(
|
||||
"rod" to rod.asMoLangValue(server.registryAccess()),
|
||||
"bait" to bait.asMoLangValue(server.registryAccess())
|
||||
)
|
||||
} ?: emptyMap()
|
||||
val functions = moLangFunctionMap(
|
||||
cancelFunc
|
||||
)
|
||||
|
||||
+77
-56
@@ -17,67 +17,88 @@ import com.cobblemon.mod.common.api.pokemon.experience.*
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
|
||||
/**
|
||||
* Event fired when experience is about to be gained. Cancelling this event prevents
|
||||
* any experience being added, and the amount of experience can be changed from [experience].
|
||||
* Event fired when experience will be gained.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since August 5th, 2022
|
||||
*/
|
||||
class ExperienceGainedPreEvent(
|
||||
val pokemon: Pokemon,
|
||||
val source: ExperienceSource,
|
||||
interface ExperienceGainedEvent {
|
||||
/**
|
||||
* The [Pokemon] gaining experience.
|
||||
*/
|
||||
val pokemon: Pokemon
|
||||
/**
|
||||
* The source of the experience being gained.
|
||||
*/
|
||||
val source: ExperienceSource
|
||||
/**
|
||||
* The amount of experience being gained.
|
||||
*/
|
||||
var experience: Int
|
||||
) : Cancelable() {
|
||||
val context = mutableMapOf(
|
||||
"pokemon" to pokemon.struct,
|
||||
"source" to StringValue(
|
||||
when (source) {
|
||||
is BattleExperienceSource -> "battle"
|
||||
is CandyExperienceSource -> "interaction"
|
||||
is CommandExperienceSource -> "command"
|
||||
is SidemodExperienceSource -> source.sidemodId
|
||||
else -> "unknown"
|
||||
}
|
||||
),
|
||||
"experience" to DoubleValue(experience.toDouble())
|
||||
)
|
||||
val functions = moLangFunctionMap(
|
||||
cancelFunc,
|
||||
"set_amount" to {
|
||||
experience = it.getInt(0)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Event fired when experience has been gained. Information about whether it leveled up or not is
|
||||
* available.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since August 5th, 2022
|
||||
*/
|
||||
class ExperienceGainedPostEvent(
|
||||
val pokemon: Pokemon,
|
||||
val source: ExperienceSource,
|
||||
val experience: Int,
|
||||
val previousLevel: Int,
|
||||
val currentLevel: Int,
|
||||
val learnedMoves: MutableSet<MoveTemplate>
|
||||
) {
|
||||
val context = mutableMapOf(
|
||||
"pokemon" to pokemon.struct,
|
||||
"source" to StringValue(
|
||||
when (source) {
|
||||
is BattleExperienceSource -> "battle"
|
||||
is CandyExperienceSource -> "interaction"
|
||||
is CommandExperienceSource -> "command"
|
||||
is SidemodExperienceSource -> source.sidemodId
|
||||
else -> "unknown"
|
||||
/**
|
||||
* Event fired when experience is about to be gained. Cancelling this event prevents
|
||||
* any experience being added, and the amount of experience can be changed from [experience].
|
||||
*
|
||||
* @author Hiroku/MeAlam
|
||||
* @since August 5th, 2022
|
||||
*/
|
||||
data class Pre(
|
||||
override val pokemon: Pokemon,
|
||||
override val source: ExperienceSource,
|
||||
override var experience: Int
|
||||
) : ExperienceGainedEvent, Cancelable() {
|
||||
val context = mutableMapOf(
|
||||
"pokemon" to pokemon.struct,
|
||||
"source" to StringValue(
|
||||
when (source) {
|
||||
is BattleExperienceSource -> "battle"
|
||||
is CandyExperienceSource -> "interaction"
|
||||
is CommandExperienceSource -> "command"
|
||||
is SidemodExperienceSource -> source.sidemodId
|
||||
else -> "unknown"
|
||||
}
|
||||
),
|
||||
"experience" to DoubleValue(experience.toDouble())
|
||||
)
|
||||
val functions = moLangFunctionMap(
|
||||
cancelFunc,
|
||||
"set_amount" to {
|
||||
experience = it.getInt(0)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
),
|
||||
"experience" to DoubleValue(experience.toDouble()),
|
||||
"previous_level" to DoubleValue(previousLevel.toDouble()),
|
||||
"current_level" to DoubleValue(currentLevel.toDouble())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Event fired when experience has been gained. Information about whether it leveled up or not is
|
||||
* available.
|
||||
*
|
||||
* @author Hiroku/MeAlam
|
||||
* @since August 5th, 2022
|
||||
*/
|
||||
data class Post(
|
||||
override val pokemon: Pokemon,
|
||||
override val source: ExperienceSource,
|
||||
override var experience: Int,
|
||||
val previousLevel: Int,
|
||||
val currentLevel: Int,
|
||||
val learnedMoves: MutableSet<MoveTemplate>
|
||||
) : ExperienceGainedEvent {
|
||||
val context = mutableMapOf(
|
||||
"pokemon" to pokemon.struct,
|
||||
"source" to StringValue(
|
||||
when (source) {
|
||||
is BattleExperienceSource -> "battle"
|
||||
is CandyExperienceSource -> "interaction"
|
||||
is CommandExperienceSource -> "command"
|
||||
is SidemodExperienceSource -> source.sidemodId
|
||||
else -> "unknown"
|
||||
}
|
||||
),
|
||||
"experience" to DoubleValue(experience.toDouble()),
|
||||
"previous_level" to DoubleValue(previousLevel.toDouble()),
|
||||
"current_level" to DoubleValue(currentLevel.toDouble())
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -13,14 +13,14 @@ import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
interface HatchEggEvent {
|
||||
val egg: PokemonProperties
|
||||
val player: ServerPlayer
|
||||
|
||||
data class Pre(override var egg : PokemonProperties, override var player: ServerPlayer) : HatchEggEvent, Cancelable() {
|
||||
data class Pre(var egg : PokemonProperties, override val player: ServerPlayer) : HatchEggEvent, Cancelable() {
|
||||
val context = mutableMapOf(
|
||||
"player" to (player.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO)
|
||||
)
|
||||
@@ -29,7 +29,7 @@ interface HatchEggEvent {
|
||||
)
|
||||
}
|
||||
|
||||
data class Post(override var egg : PokemonProperties, override var player: ServerPlayer) : HatchEggEvent {
|
||||
data class Post(override var player: ServerPlayer, val pokemon : Pokemon) : HatchEggEvent {
|
||||
val context = mutableMapOf(
|
||||
"player" to (player.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO)
|
||||
)
|
||||
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokemon
|
||||
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
|
||||
/**
|
||||
* Event fired when a [PokemonEntity] is recalled.
|
||||
*
|
||||
* @author Segfault Guy
|
||||
* @since March 25th, 2023
|
||||
*/
|
||||
interface PokemonRecallEvent {
|
||||
|
||||
val pokemon: Pokemon
|
||||
val oldEntity: PokemonEntity?
|
||||
|
||||
/**
|
||||
* Event fired before a [PokemonEntity] is recalled.
|
||||
*/
|
||||
data class Pre(
|
||||
override val pokemon: Pokemon,
|
||||
override val oldEntity: PokemonEntity?
|
||||
) : PokemonRecallEvent, Cancelable() {
|
||||
|
||||
val context = mutableMapOf(
|
||||
"pokemon" to pokemon.struct,
|
||||
"old_entity" to (oldEntity?.struct ?: StringValue("null"))
|
||||
)
|
||||
|
||||
val functions = moLangFunctionMap(
|
||||
cancelFunc
|
||||
)
|
||||
}
|
||||
|
||||
data class Post(
|
||||
override val pokemon: Pokemon,
|
||||
override val oldEntity: PokemonEntity?
|
||||
) : PokemonRecallEvent {
|
||||
val context = mutableMapOf(
|
||||
"pokemon" to pokemon.struct,
|
||||
"old_entity" to (oldEntity?.struct ?: StringValue("null"))
|
||||
)
|
||||
}
|
||||
}
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokemon
|
||||
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
|
||||
/**
|
||||
* Event fired when a [PokemonEntity] is recalled.
|
||||
*
|
||||
* @author Segfault Guy
|
||||
* @since March 25th, 2023
|
||||
*/
|
||||
data class PokemonRecalledEvent (
|
||||
val pokemon: Pokemon,
|
||||
val oldEntity: PokemonEntity?
|
||||
)
|
||||
+55
-30
@@ -17,39 +17,64 @@ import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
/**
|
||||
* Event fired when a party [Pokemon] is sent out. Cancelling this event prevents a corresponding
|
||||
* [PokemonEntity] from being instantiated and spawned into the world.
|
||||
* Event fired when a party [Pokemon] is sent out.
|
||||
*
|
||||
* @author Segfault Guy
|
||||
* @since March 25th, 2023
|
||||
*/
|
||||
data class PokemonSentPreEvent(
|
||||
val pokemon: Pokemon,
|
||||
val level: ServerLevel,
|
||||
val position: Vec3
|
||||
) : Cancelable() {
|
||||
val context = mutableMapOf(
|
||||
"pokemon" to pokemon.struct,
|
||||
"position" to StringValue(position.toString())
|
||||
)
|
||||
val functions = moLangFunctionMap(
|
||||
cancelFunc
|
||||
)
|
||||
}
|
||||
interface PokemonSentEvent {
|
||||
/**
|
||||
* The [Pokemon] being sent out.
|
||||
*/
|
||||
val pokemon: Pokemon
|
||||
|
||||
/**
|
||||
* Event fired after a [PokemonEntity] is spawned from a player's party and after its animations are finished.
|
||||
* Only fired for party [Pokemon] sent out with animations.
|
||||
*
|
||||
* @author Segfault Guy
|
||||
* @since March 25th, 2023
|
||||
*/
|
||||
data class PokemonSentPostEvent(
|
||||
val pokemon: Pokemon,
|
||||
val pokemonEntity: PokemonEntity
|
||||
) {
|
||||
val context = mutableMapOf(
|
||||
"pokemon" to pokemon.struct,
|
||||
"pokemon_entity" to pokemonEntity.struct
|
||||
)
|
||||
/**
|
||||
* The [ServerLevel] the [Pokemon] is being sent out into.
|
||||
*/
|
||||
val level: ServerLevel
|
||||
|
||||
/**
|
||||
* The [Vec3] position the [Pokemon] is being sent out at.
|
||||
*/
|
||||
val position: Vec3
|
||||
|
||||
/**
|
||||
* Event fired when a party [Pokemon] is sent out. Cancelling this event prevents a corresponding
|
||||
* [PokemonEntity] from being instantiated and spawned into the world.
|
||||
*
|
||||
* @author Segfault Guy/MeAlam
|
||||
* @since March 25th, 2023
|
||||
*/
|
||||
data class Pre(
|
||||
override val pokemon: Pokemon,
|
||||
override val level: ServerLevel,
|
||||
override val position: Vec3
|
||||
) : PokemonSentEvent, Cancelable() {
|
||||
val context = mutableMapOf(
|
||||
"pokemon" to pokemon.struct,
|
||||
"position" to StringValue(position.toString())
|
||||
)
|
||||
val functions = moLangFunctionMap(
|
||||
cancelFunc
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Event fired after a [PokemonEntity] is spawned from a player's party and after its animations are finished.
|
||||
* Only fired for party [Pokemon] sent out with animations.
|
||||
*
|
||||
* @author Segfault Guy/MeAlam
|
||||
* @since March 25th, 2023
|
||||
*/
|
||||
data class Post(
|
||||
override val pokemon: Pokemon,
|
||||
override val level: ServerLevel,
|
||||
override val position: Vec3,
|
||||
val pokemonEntity: PokemonEntity
|
||||
) : PokemonSentEvent {
|
||||
val context = mutableMapOf(
|
||||
"pokemon" to pokemon.struct,
|
||||
"pokemon_entity" to pokemonEntity.struct
|
||||
)
|
||||
}
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokemon
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
/**
|
||||
* The base for all events related to riding a Pokemon.
|
||||
*
|
||||
* @see [Pre]
|
||||
* @see [Post]
|
||||
*/
|
||||
interface RidePokemonEvent {
|
||||
val player: ServerPlayer
|
||||
val pokemon: PokemonEntity
|
||||
|
||||
class Pre(
|
||||
override val player: ServerPlayer,
|
||||
override val pokemon: PokemonEntity
|
||||
) : RidePokemonEvent, Cancelable() {
|
||||
val context = mutableMapOf(
|
||||
"player" to player.asMoLangValue(),
|
||||
"pokemon" to pokemon.struct
|
||||
)
|
||||
val functions = moLangFunctionMap(
|
||||
cancelFunc
|
||||
)
|
||||
}
|
||||
|
||||
class Post(
|
||||
override val player: ServerPlayer,
|
||||
override val pokemon: PokemonEntity
|
||||
) : RidePokemonEvent {
|
||||
val context = mutableMapOf(
|
||||
"player" to player.asMoLangValue(),
|
||||
"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
|
||||
}
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokemon
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.trade.TradeParticipant
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
|
||||
class TradeCompletedEvent(val tradeParticipant1 : TradeParticipant, val tradeParticipant1Pokemon : Pokemon, val tradeParticipant2 : TradeParticipant, val tradeParticipant2Pokemon : Pokemon) {
|
||||
val context = mutableMapOf(
|
||||
"trade_participant_1" to (tradeParticipant1.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
|
||||
"trade_participant_2" to (tradeParticipant2.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
|
||||
"trade_participant_1_pokemon" to tradeParticipant1Pokemon.struct,
|
||||
"trade_participant_2_pokemon" to tradeParticipant2Pokemon.struct
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokemon
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.trade.TradeParticipant
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
|
||||
/**
|
||||
* Event fired when a trade is initiated or completed.
|
||||
*
|
||||
* @author MeAlam
|
||||
* @since August 23th, 2025
|
||||
*/
|
||||
interface TradeEvent {
|
||||
|
||||
/**
|
||||
* The first [TradeParticipant].
|
||||
*/
|
||||
val tradeParticipant1: TradeParticipant
|
||||
|
||||
/**
|
||||
* The [Pokemon] being traded by the first participant.
|
||||
*/
|
||||
val tradeParticipant1Pokemon: Pokemon
|
||||
|
||||
/**
|
||||
* The second [TradeParticipant].
|
||||
*/
|
||||
val tradeParticipant2: TradeParticipant
|
||||
|
||||
/**
|
||||
* The [Pokemon] being traded by the second participant.
|
||||
*/
|
||||
val tradeParticipant2Pokemon: Pokemon
|
||||
|
||||
/**
|
||||
* Event fired when a trade is about to happen. Cancelling this event prevents the trade from occurring.
|
||||
*
|
||||
* @author MeAlam
|
||||
* @since August 23th, 2025
|
||||
*/
|
||||
data class Pre(
|
||||
override val tradeParticipant1: TradeParticipant,
|
||||
override val tradeParticipant1Pokemon: Pokemon,
|
||||
override val tradeParticipant2: TradeParticipant,
|
||||
override val tradeParticipant2Pokemon: Pokemon
|
||||
) : TradeEvent, Cancelable() {
|
||||
val context = mutableMapOf(
|
||||
"trade_participant_1" to (tradeParticipant1.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
|
||||
"trade_participant_2" to (tradeParticipant2.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
|
||||
"trade_participant_1_pokemon" to tradeParticipant1Pokemon.struct,
|
||||
"trade_participant_2_pokemon" to tradeParticipant2Pokemon.struct
|
||||
)
|
||||
|
||||
val functions = moLangFunctionMap(
|
||||
cancelFunc
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Event fired after a trade has occurred.
|
||||
*
|
||||
* @author MeAlam
|
||||
* @since August 23th, 2025
|
||||
*/
|
||||
data class Post(
|
||||
override val tradeParticipant1: TradeParticipant,
|
||||
override val tradeParticipant1Pokemon: Pokemon,
|
||||
override val tradeParticipant2: TradeParticipant,
|
||||
override val tradeParticipant2Pokemon: Pokemon
|
||||
) : TradeEvent {
|
||||
val context = mutableMapOf(
|
||||
"trade_participant_1" to (tradeParticipant1.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
|
||||
"trade_participant_2" to (tradeParticipant2.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
|
||||
"trade_participant_1_pokemon" to tradeParticipant1Pokemon.struct,
|
||||
"trade_participant_2_pokemon" to tradeParticipant2Pokemon.struct
|
||||
)
|
||||
}
|
||||
}
|
||||
+11
@@ -23,11 +23,22 @@ import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
* @since October 2nd, 2022
|
||||
*/
|
||||
data class EvolutionCompleteEvent(
|
||||
/**
|
||||
* The [Pokemon] resulting from the evolution.
|
||||
*/
|
||||
override val pokemon: Pokemon,
|
||||
/**
|
||||
* The [Pokemon] that was the source of the evolution.
|
||||
*/
|
||||
val sourcePokemon: Pokemon,
|
||||
/**
|
||||
* The [Evolution] that was used.
|
||||
*/
|
||||
override val evolution: Evolution
|
||||
) : EvolutionEvent {
|
||||
val context = mutableMapOf<String, MoValue>(
|
||||
"pokemon" to pokemon.struct,
|
||||
"source_pokemon" to sourcePokemon.struct,
|
||||
"evolution" to evolution.asMoLangValue()
|
||||
)
|
||||
}
|
||||
+16
-5
@@ -37,19 +37,28 @@ interface ChangePCBoxWallpaperEvent {
|
||||
|
||||
/**
|
||||
* The location of the wallpaper that is being changed to. Can be modified in the [Pre] event.
|
||||
* NOTE: Changing this to a wallpaper that does not exist on the client, will result in a purple/black fallback texture being displayed.
|
||||
* NOTE: Changing this to a wallpaper that does not exist on the client, will result in a default fallback texture being displayed.
|
||||
*/
|
||||
val wallpaper: ResourceLocation
|
||||
|
||||
/**
|
||||
* The location of the alternative wallpaper that is being changed to if available. Can be modified in the [Pre] event.
|
||||
* NOTE: Changing this to a wallpaper that does not exist on the client, will result in a default fallback texture being displayed.
|
||||
*/
|
||||
val altWallpaper: ResourceLocation?
|
||||
|
||||
class Pre(
|
||||
override val player: ServerPlayer,
|
||||
override val box: PCBox,
|
||||
override var wallpaper: ResourceLocation
|
||||
override var wallpaper: ResourceLocation,
|
||||
override var altWallpaper: ResourceLocation?
|
||||
|
||||
) : ChangePCBoxWallpaperEvent, Cancelable() {
|
||||
val context = mutableMapOf(
|
||||
"player" to player.asMoLangValue(),
|
||||
"box" to StringValue(box.toString()),
|
||||
"wallpaper" to StringValue(wallpaper.toString())
|
||||
"wallpaper" to StringValue(wallpaper.toString()),
|
||||
"altWallpaper" to StringValue(altWallpaper?.toString())
|
||||
)
|
||||
val functions = mapOf(
|
||||
cancelFunc
|
||||
@@ -59,12 +68,14 @@ interface ChangePCBoxWallpaperEvent {
|
||||
class Post(
|
||||
override val player: ServerPlayer,
|
||||
override val box: PCBox,
|
||||
override val wallpaper: ResourceLocation
|
||||
override val wallpaper: ResourceLocation,
|
||||
override val altWallpaper: ResourceLocation?
|
||||
) : ChangePCBoxWallpaperEvent {
|
||||
val context = mutableMapOf(
|
||||
"player" to player.asMoLangValue(),
|
||||
"box" to StringValue(box.toString()),
|
||||
"wallpaper" to StringValue(wallpaper.toString())
|
||||
"wallpaper" to StringValue(wallpaper.toString()),
|
||||
"altWallpaper" to StringValue(altWallpaper?.toString())
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -227,7 +227,7 @@ fun drawPosablePortrait(
|
||||
matrixStack.scale(scale, scale, -scale)
|
||||
matrixStack.translate(0.0, -PORTRAIT_DIAMETER / 18.0, 0.0)
|
||||
|
||||
val sprite = VaryingModelRepository.getSprite(identifier, state, SpriteType.PORTRAIT);
|
||||
val sprite = VaryingModelRepository.getSprite(identifier, state, SpriteType.PORTRAIT)
|
||||
|
||||
if (sprite == null) {
|
||||
val model = VaryingModelRepository.getPoser(identifier, state)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,7 @@ 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
|
||||
@@ -56,6 +57,8 @@ 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
|
||||
import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
|
||||
@@ -84,6 +87,7 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonBehaviourFlag
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.PokemonMoveControl
|
||||
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleMusicPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
|
||||
@@ -125,21 +129,28 @@ import net.minecraft.sounds.SoundSource
|
||||
import net.minecraft.tags.TagKey
|
||||
import net.minecraft.world.damagesource.DamageSource
|
||||
import net.minecraft.world.damagesource.DamageTypes
|
||||
import net.minecraft.world.effect.MobEffectInstance
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.entity.EntityDimensions
|
||||
import net.minecraft.world.entity.EntityType
|
||||
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
|
||||
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
|
||||
import net.minecraft.world.level.ClipContext
|
||||
import net.minecraft.world.level.GameType
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.Level.ExplosionInteraction
|
||||
import net.minecraft.world.level.biome.Biome
|
||||
@@ -149,6 +160,9 @@ 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
|
||||
@@ -199,6 +213,14 @@ object MoLangFunctions {
|
||||
"string_length" to java.util.function.Function { params ->
|
||||
return@Function DoubleValue(params.getString(0).length)
|
||||
},
|
||||
"split_string" to java.util.function.Function { params ->
|
||||
val text = params.getString(0)
|
||||
val delimiter = params.getString(1)
|
||||
val parts = text.split(delimiter).map { StringValue(it) }
|
||||
val struct = ArrayStruct(hashMapOf())
|
||||
parts.forEachIndexed { index, moValue -> struct.setDirectly("$index", moValue) }
|
||||
return@Function struct
|
||||
},
|
||||
"is_blank" to java.util.function.Function { params ->
|
||||
val arg = params.get<MoValue>(0)
|
||||
return@Function DoubleValue((arg is StringValue && (arg.value.isBlank() || arg.value.toDoubleOrNull() == 0.0)) || (arg is DoubleValue && arg.value == 0.0))
|
||||
@@ -466,6 +488,22 @@ object MoLangFunctions {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("spawn_npc") { params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
val npcClass = params.getStringOrNull(3)?.let { NPCClasses.getByIdentifier(it.asIdentifierDefaultingNamespace()) }
|
||||
val level = params.getInt(4)
|
||||
if(npcClass == null) return@put DoubleValue.ZERO
|
||||
val npc = NPCEntity(world)
|
||||
npc.moveTo(x, y, z, npc.yRot, npc.xRot)
|
||||
npc.npc = npcClass
|
||||
npc.initialize(level)
|
||||
if (world.addFreshEntity(npc)) {
|
||||
return@put npc.asMoLangValue()
|
||||
}
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
map.put("play_sound_on_server") { params ->
|
||||
val sound = params.getString(0).asResource()
|
||||
val soundSource = params.getString(1).uppercase()
|
||||
@@ -550,11 +588,62 @@ object MoLangFunctions {
|
||||
environment
|
||||
}
|
||||
map.put("is_player") { DoubleValue.ONE }
|
||||
map.put("riding_pokemon") {
|
||||
val vehicle = player.vehicle
|
||||
if (vehicle is PokemonEntity) {
|
||||
return@put vehicle.struct
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
if (player is ServerPlayer) {
|
||||
map.put("seen_credits") { _ ->
|
||||
DoubleValue( DoubleValue(player.seenCredits) )
|
||||
}
|
||||
map.put("is_in_dialogue") { _ ->
|
||||
DoubleValue( DoubleValue(player.isInDialogue) )
|
||||
}
|
||||
map.put("active_dialogue") { _ ->
|
||||
if (player.isInDialogue) {
|
||||
player.activeDialogue?.dialogueId.toString()
|
||||
return@put DoubleValue.ONE
|
||||
} else {
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("is_spectator") { DoubleValue(player.isSpectator) }
|
||||
map.put("is_creative") { DoubleValue(player.isCreative) }
|
||||
map.put("is_survival") { DoubleValue(player.gameMode.isSurvival) }
|
||||
map.put("is_adventure") { DoubleValue(player.gameMode.getGameModeForPlayer() == GameType.ADVENTURE) }
|
||||
map.put("run_command") { params ->
|
||||
val command = params.getString(0)
|
||||
player.server.commands.performPrefixedCommand(player.createCommandSourceStack(), command)
|
||||
}
|
||||
map.put("set_battle_theme") { params ->
|
||||
val soundId = params.getString(0).asResource()
|
||||
Cobblemon.playerDataManager.getGenericData(player).battleTheme = soundId
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("battle_music") { params ->
|
||||
val soundId = params.getString(0).asResource()
|
||||
val volume = params.getDoubleOrNull(1)?.toFloat() ?: 1.0f
|
||||
val pitch = params.getDoubleOrNull(2)?.toFloat() ?: 1.0f
|
||||
val restart = params.getBooleanOrNull(3) ?: true
|
||||
|
||||
val music = soundId
|
||||
if (music != null) {
|
||||
val packet = BattleMusicPacket(music, volume, pitch, restart)
|
||||
packet.sendToPlayer(player)
|
||||
return@put DoubleValue.ONE
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("stop_battle_music") { _ ->
|
||||
val packet = BattleMusicPacket(null)
|
||||
packet.sendToPlayer(player)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("play_sound_on_server") { params ->
|
||||
val sound = params.getString(0).asResource()
|
||||
val soundSource = SoundSource.valueOf(params.getString(1).uppercase())
|
||||
@@ -587,13 +676,13 @@ object MoLangFunctions {
|
||||
map.put("party") { player.party().struct }
|
||||
map.put("pc") { player.pc().struct }
|
||||
map.put("has_permission") { params -> DoubleValue(Cobblemon.permissionValidator.hasPermission(player, params.getString(0), params.getIntOrNull(1) ?: 4)) }
|
||||
map.put("data") { _ -> Cobblemon.molangData.load(player.uuid) }
|
||||
map.put("save_data") { _ -> Cobblemon.molangData.save(player.uuid) }
|
||||
map.put("data") { params -> Cobblemon.molangData.load(player.uuid, params.getStringOrNull(0)) }
|
||||
map.put("save_data") { params -> Cobblemon.molangData.save(player.uuid, params.getStringOrNull(0)) }
|
||||
map.put("in_battle") { DoubleValue(player.isInBattle()) }
|
||||
map.put("battle") { player.getBattleState()?.first?.struct ?: DoubleValue.ZERO }
|
||||
map.put("get_npc_data") { params ->
|
||||
val npcId = (params.get<MoValue>(0) as? ObjectValue<NPCEntity>)?.obj?.stringUUID ?: params.getString(0)
|
||||
val data = Cobblemon.molangData.load(player.uuid)
|
||||
val npcId = ((params.get<MoValue>(0) as? ObjectValue<*>)?.obj as? NPCEntity)?.stringUUID ?: params.getString(0)
|
||||
val data = Cobblemon.molangData.load(player.uuid, params.getStringOrNull(1))
|
||||
if (data.map.containsKey(npcId)) {
|
||||
return@put data.map[npcId]!!
|
||||
} else {
|
||||
@@ -603,9 +692,9 @@ object MoLangFunctions {
|
||||
}
|
||||
}
|
||||
map.put("get_npc_variable") { params ->
|
||||
val npcId = (params.get<MoValue>(0) as? ObjectValue<NPCEntity>)?.obj?.stringUUID ?: params.getString(0)
|
||||
val npcId = ((params.get<MoValue>(0) as? ObjectValue<*>)?.obj as? NPCEntity)?.stringUUID ?: params.getString(0)
|
||||
val variable = params.getString(1)
|
||||
val data = Cobblemon.molangData.load(player.uuid)
|
||||
val data = Cobblemon.molangData.load(player.uuid, params.getStringOrNull(2))
|
||||
if (data.map.containsKey(npcId)) {
|
||||
return@put (data.map[npcId] as VariableStruct).map[variable] ?: DoubleValue.ZERO
|
||||
} else {
|
||||
@@ -613,15 +702,16 @@ object MoLangFunctions {
|
||||
}
|
||||
}
|
||||
map.put("set_npc_variable") { params ->
|
||||
val npcId = (params.get<MoValue>(0) as? ObjectValue<NPCEntity>)?.obj?.stringUUID ?: params.getString(0)
|
||||
val npcId = ((params.get<MoValue>(0) as? ObjectValue<*>)?.obj as? NPCEntity)?.stringUUID ?: params.getString(0)
|
||||
val variable = params.getString(1)
|
||||
val value = params.get<MoValue>(2)
|
||||
val saveAfterwards = params.getBooleanOrNull(3) != false
|
||||
val data = Cobblemon.molangData.load(player.uuid)
|
||||
val path = params.getStringOrNull(4)
|
||||
val data = Cobblemon.molangData.load(player.uuid, path)
|
||||
val npcData = data.map.getOrPut(npcId) { VariableStruct() } as VariableStruct
|
||||
npcData.map[variable] = value
|
||||
if (saveAfterwards) {
|
||||
Cobblemon.molangData.save(player.uuid)
|
||||
Cobblemon.molangData.save(player.uuid, path)
|
||||
}
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
@@ -734,7 +824,26 @@ object MoLangFunctions {
|
||||
map.put("delta_movement") {
|
||||
return@put listOf(entity.deltaMovement.x, entity.deltaMovement.y, entity.deltaMovement.z).asArrayValue(::DoubleValue)
|
||||
}
|
||||
|
||||
map.put("tags") {
|
||||
val tags = entity.tags
|
||||
val array = ArrayStruct(hashMapOf())
|
||||
tags.forEachIndexed { index, tag -> array.setDirectly("$index", StringValue(tag)) }
|
||||
return@put array
|
||||
}
|
||||
map.put("add_tag") { params ->
|
||||
val tag = params.getString(0)
|
||||
entity.addTag(tag)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("remove_tag") { params ->
|
||||
val tag = params.getString(0)
|
||||
entity.removeTag(tag)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("has_tag") { params ->
|
||||
val tag = params.getString(0)
|
||||
return@put DoubleValue(entity.tags.contains(tag))
|
||||
}
|
||||
if (entity is MoLangScriptingEntity) {
|
||||
map.put("add_callback") { params ->
|
||||
val type = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
@@ -885,6 +994,9 @@ object MoLangFunctions {
|
||||
|
||||
return@put if (entity.isStandingOn(blockStrings, depth)) DoubleValue.ONE else DoubleValue.ZERO
|
||||
}
|
||||
map.put("discard") {
|
||||
entity.discard()
|
||||
}
|
||||
if (entity is PosableEntity) {
|
||||
map.put("play_animation") { params ->
|
||||
val animation = params.getString(0)
|
||||
@@ -942,6 +1054,11 @@ object MoLangFunctions {
|
||||
packet.sendToPlayer(player)
|
||||
}
|
||||
}
|
||||
map.put("make_intangible") { params ->
|
||||
val intangible = params.getBooleanOrNull(0) ?: true
|
||||
entity.noPhysics = intangible
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
return@mutableListOf map
|
||||
}
|
||||
)
|
||||
@@ -949,10 +1066,57 @@ object MoLangFunctions {
|
||||
val livingEntityFunctions: MutableList<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ entity ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("is_player") { _ -> DoubleValue(entity is Player) }
|
||||
map.put("is_npc") { _ -> DoubleValue(entity is NPCEntity) }
|
||||
map.put("is_mob") { _ -> DoubleValue(entity is Mob) }
|
||||
map.put("is_pokemon") { _ -> DoubleValue(entity is PokemonEntity) }
|
||||
map.put("is_animal") { _ -> DoubleValue(entity is Animal) }
|
||||
map.put("is_tamable") { _ -> DoubleValue(entity is TamableAnimal) }
|
||||
map.put("is_tamed") { _ -> DoubleValue(entity is TamableAnimal && entity.isTame) }
|
||||
map.put("is_hostile") { _ -> DoubleValue(entity is Monster) }
|
||||
map.put("is_baby") { _ -> DoubleValue(entity.isBaby) }
|
||||
map.put("is_adult") { _ -> DoubleValue(!entity.isBaby) }
|
||||
map.put("remove_effect") { params ->
|
||||
val effectId = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val effectHolder = BuiltInRegistries.MOB_EFFECT.getHolder(effectId).orElse(null)
|
||||
if (effectHolder != null) {
|
||||
entity.removeEffect(effectHolder)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
}
|
||||
map.put("add_effect") { params ->
|
||||
val effectId = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val duration = params.getInt(1)
|
||||
val amplifier = params.getIntOrNull(2) ?: 0
|
||||
val ambient = params.getBooleanOrNull(3) ?: false
|
||||
val visible = params.getBooleanOrNull(4) ?: true
|
||||
val effectHolder = BuiltInRegistries.MOB_EFFECT.getHolder(effectId).orElse(null)
|
||||
if (effectHolder != null) {
|
||||
entity.addEffect(MobEffectInstance(effectHolder, duration, amplifier, ambient, visible))
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
}
|
||||
map.put("has_effect") { params ->
|
||||
val effectId = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val effectHolder = BuiltInRegistries.MOB_EFFECT.getHolder(effectId).orElse(null)
|
||||
return@put DoubleValue(if (effectHolder != null) entity.hasEffect(effectHolder) else false)
|
||||
}
|
||||
map.put("heal") { params ->
|
||||
val amount = params.getDouble(0)
|
||||
entity.heal(amount.toFloat())
|
||||
}
|
||||
map.put("is_looking_at") { params ->
|
||||
val targetEntity = params.get<MoValue>(0)
|
||||
val maxDistance = params.getDoubleOrNull(1)?.toFloat() ?: 2.0F
|
||||
|
||||
val entity = if (targetEntity is ObjectValue<*> && targetEntity.obj is Entity) {
|
||||
targetEntity.obj as Entity
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
|
||||
return@put DoubleValue(entity.isLookingAt(entity, maxDistance))
|
||||
}
|
||||
map.put("is_living_entity") { DoubleValue.ONE }
|
||||
map.put("is_flying") { _ -> DoubleValue(entity.isFallFlying) }
|
||||
map.put("is_sleeping") { _ -> DoubleValue(entity.isSleeping) }
|
||||
@@ -1072,10 +1236,9 @@ 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
|
||||
}
|
||||
)
|
||||
@@ -1296,6 +1459,11 @@ object MoLangFunctions {
|
||||
map.put("has_party") { DoubleValue(npc.party != null) }
|
||||
map.put("is_npc") { DoubleValue.ONE }
|
||||
map.put("can_battle") { DoubleValue(npc.party?.any { it.currentHealth > 0 } == true || npc.npc.party?.isStatic == false) }
|
||||
map.put("set_battle_theme") { params ->
|
||||
val soundId = params.getString(0).asResource()
|
||||
npc.npc.battleTheme = soundId
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map
|
||||
}
|
||||
)
|
||||
@@ -1415,11 +1583,18 @@ object MoLangFunctions {
|
||||
}
|
||||
struct
|
||||
}
|
||||
map.put("ride_boosts") {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for (stat in RidingStat.entries) {
|
||||
struct.addFunction(stat.name.lowercase()) { DoubleValue(pokemon.getRideBoost(stat) ?: 0.0) }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map.put("natdex_number") {
|
||||
DoubleValue(pokemon.species.nationalPokedexNumber.toDouble())
|
||||
}
|
||||
map.put("types") {
|
||||
pokemon.form.types.map { it.toString() }.asArrayValue(::StringValue)
|
||||
pokemon.form.types.map { it.toString() }.asArrayValue(::StringValue)
|
||||
}
|
||||
map.put("gender_ratio") {
|
||||
DoubleValue(pokemon.form.maleRatio.toDouble())
|
||||
@@ -1620,6 +1795,21 @@ object MoLangFunctions {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("set_ride_boost") { params ->
|
||||
val statName = params.getString(0).uppercase()
|
||||
val stat = RidingStat.entries.find { it.name.equals(statName, ignoreCase = true) } ?: return@put DoubleValue.ZERO
|
||||
val value = params.getDoubleOrNull(1)?.toFloat() ?: return@put DoubleValue.ZERO
|
||||
|
||||
pokemon.setRideBoost(stat, value)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("add_ride_boost") { params ->
|
||||
val statName = params.getString(0).uppercase()
|
||||
val stat = RidingStat.entries.find { it.name.equals(statName, ignoreCase = true) } ?: return@put DoubleValue.ZERO
|
||||
val value = params.getDoubleOrNull(1)?.toFloat() ?: return@put DoubleValue.ZERO
|
||||
|
||||
return@put DoubleValue(if (pokemon.addRideBoost(stat, value)) 1.0 else 0.0)
|
||||
}
|
||||
map.put("initialize_moveset") { params ->
|
||||
val preferLatest = params.getBooleanOrNull(0) ?: true
|
||||
pokemon.initializeMoveset(preferLatest)
|
||||
@@ -1664,7 +1854,11 @@ object MoLangFunctions {
|
||||
val moveTemplate = Moves.getByName(moveName) ?: return@put DoubleValue.ZERO
|
||||
val includeLegacy = params.getBooleanOrNull(1) ?: true
|
||||
|
||||
val canLearn = LearnsetQuery.ANY.canLearn(moveTemplate, pokemon.form.moves)
|
||||
val canLearn = if (includeLegacy) {
|
||||
LearnsetQuery.ANY.canLearn(moveTemplate, pokemon.form.moves)
|
||||
} else {
|
||||
LearnsetQuery.LEGAL.canLearn(moveTemplate, pokemon.form.moves)
|
||||
}
|
||||
return@put DoubleValue(if (canLearn) 1.0 else 0.0)
|
||||
}
|
||||
map.put("unlearn_move") { params ->
|
||||
@@ -1677,6 +1871,15 @@ object MoLangFunctions {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("can_evolve") { _ ->
|
||||
DoubleValue(pokemon.evolutions.any())
|
||||
}
|
||||
map.put("force_evolve") { params ->
|
||||
val idx = params.getInt(0)
|
||||
val evolution = if (idx >= 0) pokemon.evolutions.elementAtOrNull(idx) else return@put DoubleValue.ZERO
|
||||
evolution?.forceEvolve(pokemon)
|
||||
StringValue(evolution.toString())
|
||||
}
|
||||
map
|
||||
}
|
||||
)
|
||||
@@ -1684,10 +1887,10 @@ object MoLangFunctions {
|
||||
val pokemonEntityFunctions = mutableListOf<(PokemonEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ pokemonEntity ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("is_busy") { DoubleValue(pokemonEntity.isBusy) }
|
||||
map.put("in_battle") { DoubleValue(pokemonEntity.isBattling) }
|
||||
map.put("is_moving") { DoubleValue((pokemonEntity.moveControl as? PokemonMoveControl)?.hasWanted() == true) }
|
||||
map.put("is_flying") { DoubleValue(pokemonEntity.getBehaviourFlag(PokemonBehaviourFlag.FLYING)) }
|
||||
map.put("is_flying") { DoubleValue(pokemonEntity.getBehaviourFlag(PokemonBehaviourFlag.FLYING)) }
|
||||
map.put("get_alt_pose") { StringValue(pokemonEntity.getAltPose()) }
|
||||
map.put("is_gliding") { DoubleValue(pokemonEntity.isUsingAltPose(cobblemonResource("gliding"))) }
|
||||
map.put("is_sprinting") { DoubleValue(pokemonEntity.isUsingAltPose(cobblemonResource("sprinting"))) }
|
||||
@@ -1941,12 +2144,17 @@ object MoLangFunctions {
|
||||
}
|
||||
|
||||
map.put("data") { params ->
|
||||
val data = Cobblemon.molangData.load(UUID(0L, 0L))
|
||||
return@put data
|
||||
Cobblemon.molangData.load(
|
||||
UUID(0L, 0L),
|
||||
params.getStringOrNull(0)
|
||||
)
|
||||
}
|
||||
|
||||
map.put("save_data") {
|
||||
Cobblemon.molangData.save(UUID(0L, 0L))
|
||||
map.put("save_data") { params ->
|
||||
Cobblemon.molangData.save(
|
||||
UUID(0L, 0L),
|
||||
params.getStringOrNull(index = 0)
|
||||
)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
|
||||
@@ -2450,3 +2658,4 @@ object MoLangFunctions {
|
||||
}
|
||||
|
||||
fun Either<ResourceLocation, ExpressionLike>.runScript(runtime: MoLangRuntime) = map({ CobblemonScripts.run(it, runtime) }, { it.resolve(runtime) })
|
||||
|
||||
@@ -117,7 +117,7 @@ class BenchedMoves : Iterable<BenchedMove> {
|
||||
.xmap(
|
||||
{ moveList ->
|
||||
val benchedMoves = BenchedMoves()
|
||||
benchedMoves.addAll(moveList)
|
||||
benchedMoves.addAll(moveList.filter { it.moveTemplate !is MoveTemplate.Dummy })
|
||||
return@xmap benchedMoves
|
||||
},
|
||||
BenchedMoves::toList
|
||||
|
||||
@@ -213,7 +213,10 @@ class MoveSet : Iterable<Move> {
|
||||
.xmap(
|
||||
{ moveList ->
|
||||
val moveSet = MoveSet()
|
||||
moveList.forEach(moveSet::add)
|
||||
moveList.filter { it.template !is MoveTemplate.Dummy }.forEach(moveSet::add)
|
||||
if (moveSet.moves.all { it == null }) {
|
||||
moveSet.add(Moves.getExceptional().create())
|
||||
}
|
||||
return@xmap moveSet
|
||||
},
|
||||
{ moveSet -> moveSet.toList() }
|
||||
|
||||
@@ -96,7 +96,7 @@ open class MoveTemplate(
|
||||
|
||||
@JvmStatic
|
||||
val BY_STRING_CODEC: Codec<MoveTemplate> = CodecUtils.createByStringCodec(
|
||||
Moves::getByName,
|
||||
Moves::getByNameOrDummy,
|
||||
MoveTemplate::name
|
||||
) { id -> "No MoveTemplate for ID $id" }
|
||||
}
|
||||
|
||||
+7
-2
@@ -15,7 +15,9 @@ 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
|
||||
|
||||
open class OrientationController(val entity: LivingEntity) {
|
||||
|
||||
@@ -112,8 +114,11 @@ open class OrientationController(val entity: LivingEntity) {
|
||||
|
||||
fun applyGlobalPitch(deltaPitchDegrees: Float) = updateOrientation { original ->
|
||||
val currQuat = Quaternionf().setFromUnnormalized(original)
|
||||
val horzLeftVector = Vector3f(this.leftVector.x, 0.0f, this.leftVector.z)
|
||||
val globalPitch = Quaternionf().fromAxisAngleRad(horzLeftVector.normalize(), -deltaPitchDegrees.toRadians())
|
||||
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
|
||||
|
||||
val globalPitch = Quaternionf().fromAxisAngleRad(pitchAxis.normalize(), -deltaPitchDegrees.toRadians())
|
||||
val resultQuat = globalPitch.mul(currQuat)
|
||||
return@updateOrientation Matrix3f().set(resultQuat)
|
||||
}
|
||||
|
||||
@@ -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,11 +399,12 @@ 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) }
|
||||
ability?.let { this.createAbility(it, pokemon.form)?.let(pokemon::updateAbility) }
|
||||
status?.let { Statuses.getStatus(it)?.let { status -> if (status is PersistentStatus) pokemon.applyStatus(status) } }
|
||||
customProperties.forEach { it.apply(pokemon) }
|
||||
ivs?.let { ivs ->
|
||||
ivs.forEach { stat ->
|
||||
pokemon.setIV(stat.key, stat.value)
|
||||
@@ -464,7 +467,7 @@ open class PokemonProperties {
|
||||
val stack = ItemStack(result.item)
|
||||
stack.applyComponents(result.components)
|
||||
if (stack.isEmpty) return@let
|
||||
pokemon.swapHeldItem(stack)
|
||||
pokemon.swapHeldItem(stack, decrement = true, aiCanDrop = false)
|
||||
}
|
||||
pokemon.updateAspects()
|
||||
}
|
||||
@@ -504,6 +507,7 @@ open class PokemonProperties {
|
||||
nickname?.takeIf { it.string != pokemon.nickname?.string }?.let { return false }
|
||||
form?.takeIf { !it.equals(pokemon.form.name, true) }?.let { return false }
|
||||
friendship?.takeIf { it != pokemon.friendship }?.let { return false }
|
||||
fullness?.takeIf { it != pokemon.currentFullness }?.let { return false }
|
||||
pokeball?.takeIf { it != pokemon.caughtBall.name.toString() }?.let { return false }
|
||||
nature?.takeIf { it != pokemon.nature.name.toString() }?.let { return false }
|
||||
ability?.takeIf { it != pokemon.ability.name }?.let { return false }
|
||||
@@ -588,6 +592,7 @@ open class PokemonProperties {
|
||||
originalTrainer?.takeIf { it != properties.originalTrainer }?.let{ return false }
|
||||
originalTrainerType?.takeIf { it != properties.originalTrainerType }?.let{ return false }
|
||||
moves?.takeIf { it.any { move -> properties.moves?.none { it == move } == true } }?.let { return false }
|
||||
fullness?.takeIf { it != properties.fullness }?.let { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -636,6 +641,7 @@ open class PokemonProperties {
|
||||
nickname?.let { nbt.putString(DataKeys.POKEMON_NICKNAME, Component.Serializer.toJson(it, registryLookup)) }
|
||||
form?.let { nbt.putString(DataKeys.POKEMON_FORM_ID, it) }
|
||||
friendship?.let { nbt.putInt(DataKeys.POKEMON_FRIENDSHIP, it) }
|
||||
fullness?.let { nbt.putInt(DataKeys.POKEMON_FULLNESS, it) }
|
||||
pokeball?.let { nbt.putString(DataKeys.POKEMON_CAUGHT_BALL, it) }
|
||||
nature?.let { nbt.putString(DataKeys.POKEMON_NATURE, it) }
|
||||
ability?.let { nbt.putString(DataKeys.POKEMON_ABILITY, it) }
|
||||
@@ -667,6 +673,7 @@ open class PokemonProperties {
|
||||
nickname = if (tag.contains(DataKeys.POKEMON_NICKNAME)) Component.Serializer.fromJson(tag.getString(DataKeys.POKEMON_NICKNAME), registryLookup) else null
|
||||
form = if (tag.contains(DataKeys.POKEMON_FORM_ID)) tag.getString(DataKeys.POKEMON_FORM_ID) else null
|
||||
friendship = if (tag.contains(DataKeys.POKEMON_FRIENDSHIP)) tag.getInt(DataKeys.POKEMON_FRIENDSHIP) else null
|
||||
fullness = if (tag.contains(DataKeys.POKEMON_FULLNESS)) tag.getInt(DataKeys.POKEMON_FULLNESS) else null
|
||||
pokeball = if (tag.contains(DataKeys.POKEMON_CAUGHT_BALL)) tag.getString(DataKeys.POKEMON_CAUGHT_BALL) else null
|
||||
nature = if (tag.contains(DataKeys.POKEMON_NATURE)) tag.getString(DataKeys.POKEMON_NATURE) else null
|
||||
ability = if (tag.contains(DataKeys.POKEMON_ABILITY)) tag.getString(DataKeys.POKEMON_ABILITY) else null
|
||||
@@ -700,6 +707,7 @@ open class PokemonProperties {
|
||||
//nickname?.let { json.addProperty(DataKeys.POKEMON_NICKNAME, Text.Serialization.toJsonString(it)) }
|
||||
form?.let { json.addProperty(DataKeys.POKEMON_FORM_ID, it) }
|
||||
friendship?.let { json.addProperty(DataKeys.POKEMON_FRIENDSHIP, it) }
|
||||
fullness?.let {json.addProperty(DataKeys.POKEMON_FULLNESS, it)}
|
||||
pokeball?.let { json.addProperty(DataKeys.POKEMON_CAUGHT_BALL, it) }
|
||||
nature?.let { json.addProperty(DataKeys.POKEMON_NATURE, it) }
|
||||
ability?.let { json.addProperty(DataKeys.POKEMON_ABILITY, it) }
|
||||
@@ -732,6 +740,7 @@ open class PokemonProperties {
|
||||
//nickname = json.get(DataKeys.POKEMON_NICKNAME)?.asString?.let { Text.Serialization.fromJson(it) }
|
||||
form = json.get(DataKeys.POKEMON_FORM_ID)?.asString
|
||||
friendship = json.get(DataKeys.POKEMON_FRIENDSHIP)?.asInt
|
||||
fullness = json.get(DataKeys.POKEMON_FULLNESS)?.asInt
|
||||
pokeball = json.get(DataKeys.POKEMON_CAUGHT_BALL)?.asString
|
||||
nature = json.get(DataKeys.POKEMON_NATURE)?.asString
|
||||
ability = json.get(DataKeys.POKEMON_ABILITY)?.asString
|
||||
@@ -763,6 +772,7 @@ open class PokemonProperties {
|
||||
shiny?.let { pieces.add("shiny=$it") }
|
||||
gender?.let { pieces.add("gender=$it")}
|
||||
friendship?.let { pieces.add("friendship=$it") }
|
||||
fullness?.let {pieces.add("fullness=$it")}
|
||||
pokeball?.let { pieces.add("pokeball=$it") }
|
||||
nature?.let { pieces.add("nature=$it") }
|
||||
ability?.let { pieces.add("ability=$it") }
|
||||
|
||||
@@ -43,7 +43,6 @@ import com.cobblemon.mod.common.api.riding.stats.RidingStatDefinition
|
||||
import com.cobblemon.mod.common.api.spawning.TimeRange
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.api.types.adapters.ElementalTypeAdapter
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
@@ -54,10 +53,8 @@ import com.cobblemon.mod.common.pokemon.ai.ObtainableItemConditionAdapter
|
||||
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonEvolutionAdapter
|
||||
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonPreEvolutionAdapter
|
||||
import com.cobblemon.mod.common.pokemon.evolution.adapters.LegacyItemConditionWrapperAdapter
|
||||
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
|
||||
import com.cobblemon.mod.common.util.adapters.*
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.ifClient
|
||||
import com.google.common.collect.HashBasedTable
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
@@ -65,13 +62,11 @@ import com.google.gson.reflect.TypeToken
|
||||
import com.mojang.datafixers.util.Either
|
||||
import net.minecraft.advancements.critereon.ItemPredicate
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
import net.minecraft.tags.TagKey
|
||||
import net.minecraft.world.effect.MobEffect
|
||||
import net.minecraft.world.entity.EntityDimensions
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
@@ -151,24 +146,17 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
|
||||
val species: Collection<Species>
|
||||
get() = this.speciesByIdentifier.values
|
||||
val implemented = mutableListOf<Species>()
|
||||
val implemented: List<Species>
|
||||
get() = this.species.filter { it.implemented }
|
||||
|
||||
init {
|
||||
SpeciesAdditions.observable.subscribe {
|
||||
implemented.clear()
|
||||
this.species.forEach(Species::initialize)
|
||||
this.species.forEach {
|
||||
if (it.implemented) {
|
||||
this.implemented.add(it)
|
||||
}
|
||||
}
|
||||
this.species.forEach(Species::resolveEvolutionMoves)
|
||||
Cobblemon.showdownThread.queue {
|
||||
it.resetRegistryData("species")
|
||||
it.sendRegistryData(allShowdownSpecies(), "species")
|
||||
it.indicateSpeciesInitialized()
|
||||
// Reload this with the mod
|
||||
CobblemonHeldItemManager.load()
|
||||
Cobblemon.LOGGER.info("Loaded {} Pokémon species", this.speciesByIdentifier.size)
|
||||
this.observable.emit(this)
|
||||
}
|
||||
@@ -233,7 +221,6 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
|
||||
override fun reload(data: Map<ResourceLocation, Species>) {
|
||||
this.speciesByIdentifier.clear()
|
||||
this.implemented.clear()
|
||||
this.speciesByDex.clear()
|
||||
data.forEach { (identifier, species) ->
|
||||
species.resourceIdentifier = identifier
|
||||
@@ -241,11 +228,6 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
this.speciesByDex.remove(old.resourceIdentifier.namespace, old.nationalPokedexNumber)
|
||||
}
|
||||
this.speciesByDex.put(species.resourceIdentifier.namespace, species.nationalPokedexNumber, species)
|
||||
ifClient {
|
||||
if (species.implemented) {
|
||||
this.implemented.add(species)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -199,6 +199,8 @@ interface Evolution : EvolutionLike {
|
||||
// (e.g., Oshawott evolving at level 17 knowing Razor Shell, while Dewott only learns it at level 18).
|
||||
val previousSpeciesLearnableMoves = pokemon.relearnableMoves
|
||||
|
||||
val sourcePokemon = pokemon.clone()
|
||||
|
||||
this.result.apply(pokemon)
|
||||
|
||||
val movesToLearn = previousSpeciesLearnableMoves + this.learnableMoves
|
||||
@@ -238,7 +240,7 @@ interface Evolution : EvolutionLike {
|
||||
)
|
||||
}
|
||||
|
||||
CobblemonEvents.EVOLUTION_COMPLETE.post(EvolutionCompleteEvent(pokemon, this))
|
||||
CobblemonEvents.EVOLUTION_COMPLETE.post(EvolutionCompleteEvent(pokemon, sourcePokemon, this))
|
||||
CobblemonEvents.POKEMON_GAINED.post(PokemonGainedEvent(pokemon.getOwnerUUID()!!, pokemon))
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -70,7 +70,7 @@ object SpeciesFeatures : JsonDataRegistry<SpeciesFeatureProvider<*>> {
|
||||
|
||||
fun getCodeFeature(name: String) = codeFeatures[name]
|
||||
fun getResourceFeature(name: String) = resourceFeatures[name]
|
||||
fun getFeature(name: String) = getCodeFeature(name) ?: getResourceFeature(name)
|
||||
fun getFeature(name: String) = getCodeFeature(name) ?: getResourceFeature(name) ?: GlobalSpeciesFeatures.getFeature(name)
|
||||
fun loadOnClient(entries: Collection<Map.Entry<String, SpeciesFeatureProvider<*>>>) {
|
||||
codeFeatures.putAll(entries.map { it.toPair() })
|
||||
}
|
||||
|
||||
@@ -109,17 +109,7 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
|
||||
}
|
||||
|
||||
// We only sync level up moves atm
|
||||
override fun shouldSynchronize(other: Learnset): Boolean {
|
||||
return when {
|
||||
other.levelUpMoves.size != this.levelUpMoves.size -> true
|
||||
other.eggMoves.size != this.eggMoves.size -> true
|
||||
other.tutorMoves.size != this.tutorMoves.size -> true
|
||||
other.tmMoves.size != this.tmMoves.size -> true
|
||||
other.evolutionMoves.size != this.evolutionMoves.size -> true
|
||||
other.formChangeMoves.size != this.formChangeMoves.size -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
override fun shouldSynchronize(other: Learnset) = other.levelUpMoves != this.levelUpMoves
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
this.levelUpMoves.clear()
|
||||
@@ -131,16 +121,6 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
|
||||
}
|
||||
levelUpMoves[level] = moves
|
||||
}
|
||||
this.eggMoves.clear()
|
||||
this.eggMoves.addAll(readMoves(buffer))
|
||||
this.tutorMoves.clear()
|
||||
this.tutorMoves.addAll(readMoves(buffer))
|
||||
this.tmMoves.clear()
|
||||
this.tmMoves.addAll(readMoves(buffer))
|
||||
this.evolutionMoves.clear()
|
||||
this.evolutionMoves.addAll(readMoves(buffer))
|
||||
this.formChangeMoves.clear()
|
||||
this.formChangeMoves.addAll(readMoves(buffer))
|
||||
}
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
@@ -152,25 +132,5 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
|
||||
buffer.writeInt(move.num)
|
||||
}
|
||||
}
|
||||
writeMoves(buffer, this.eggMoves)
|
||||
writeMoves(buffer, this.tutorMoves)
|
||||
writeMoves(buffer, this.tmMoves)
|
||||
writeMoves(buffer, this.evolutionMoves)
|
||||
writeMoves(buffer, this.formChangeMoves)
|
||||
}
|
||||
|
||||
private fun writeMoves(buffer: RegistryFriendlyByteBuf, moves: Collection<MoveTemplate>) {
|
||||
buffer.writeSizedInt(IntSize.U_SHORT, moves.size)
|
||||
for (move in moves) {
|
||||
buffer.writeInt(move.num)
|
||||
}
|
||||
}
|
||||
|
||||
private fun readMoves(buffer: RegistryFriendlyByteBuf): MutableList<MoveTemplate> {
|
||||
val moves = mutableListOf<MoveTemplate>()
|
||||
repeat(times = buffer.readSizedInt(IntSize.U_SHORT)) {
|
||||
Moves.getByNumericalId(buffer.readInt())?.let(moves::add)
|
||||
}
|
||||
return moves
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -18,6 +18,7 @@ import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import kotlin.math.abs
|
||||
|
||||
/**
|
||||
* Represents the behaviour of a Pokemon when being ridden.
|
||||
@@ -113,7 +114,7 @@ interface RidingBehaviour<Settings : RidingBehaviourSettings, State : RidingBeha
|
||||
* and 1.0 represents x is at or above it.
|
||||
*/
|
||||
internal fun scaleToRange(x: Double, min: Double, max: Double): Double {
|
||||
return ((x - min) / (max - min)).coerceIn(0.0, 1.0)
|
||||
return if ((max - min) < 0.01) 0.0 else ((x - min) / (max - min)).coerceIn(0.0, 1.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-7
@@ -10,10 +10,8 @@ package com.cobblemon.mod.common.api.riding.behaviour
|
||||
|
||||
import com.cobblemon.mod.common.api.net.Decodable
|
||||
import com.cobblemon.mod.common.api.net.Encodable
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
/**
|
||||
* Represents static settings of a riding behaviour.
|
||||
@@ -21,17 +19,17 @@ import net.minecraft.world.phys.Vec3
|
||||
* Typically this will be initialized for each pokemon form during deserialization
|
||||
* to determine how they should ride.
|
||||
*
|
||||
* These also exist in a datapacked folder, ride_settings, which are used for the fallback
|
||||
* values if non-stat settings are omitted in the pokemon form JSON.
|
||||
*
|
||||
* @author landonjw
|
||||
*/
|
||||
interface RidingBehaviourSettings: Encodable, Decodable {
|
||||
val key: ResourceLocation
|
||||
val stats: MutableMap<RidingStat, IntRange>
|
||||
|
||||
fun calculate(stat: RidingStat, boosts: Int): Float {
|
||||
fun calculate(stat: RidingStat, boostAmount: Float): Float {
|
||||
val range = stats[stat] ?: return 0F
|
||||
val boostQuotient = boosts / 255F
|
||||
return range.first + (range.last - range.first) * boostQuotient
|
||||
return range.first + boostAmount
|
||||
}
|
||||
|
||||
fun hasStat(stat: RidingStat) = stats.containsKey(stat)
|
||||
}
|
||||
+1
-1
@@ -18,7 +18,7 @@ import net.minecraft.world.phys.Vec3
|
||||
* This is intended to contain mutable state that is passed between the client and server
|
||||
* or temporary state that is used during the riding process.
|
||||
*
|
||||
* This will be destroyed when the a user dismounts the Pokemon.
|
||||
* This will be destroyed when the main rider dismounts the Pokemon.
|
||||
*
|
||||
* @author landonjw
|
||||
*/
|
||||
|
||||
+14
@@ -8,13 +8,17 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.DoubleJump
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.client.MountedCameraTypeHandler
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.tags.FluidTags
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import kotlin.collections.get
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.entity.player.Player
|
||||
|
||||
/**
|
||||
* Small wrapper around a RidingBehaviour to provide sane defaults in the event that the behaviour is not active.
|
||||
@@ -49,6 +53,15 @@ class RidingController(
|
||||
if (newTransition != null && behaviourSettings != null) {
|
||||
val newState = RidingBehaviours.get(behaviourSettings.key).createDefaultState(behaviourSettings)
|
||||
context?.let { newState.stamina.set(it.state.stamina.get(), forced = true) }
|
||||
context?.let { newState.rideVelocity.set(it.state.rideVelocity.get(), forced = true) }
|
||||
val previousKey = context?.behaviour
|
||||
val newKey = behaviourSettings.key
|
||||
for (passenger in entity.passengers) {
|
||||
if (passenger is Player && passenger !is ServerPlayer) {
|
||||
MountedCameraTypeHandler.handleTransition(previousKey ?: continue, newKey)
|
||||
}
|
||||
}
|
||||
|
||||
context = ActiveRidingContext(behaviourSettings.key, behaviourSettings, newState, newTransition)
|
||||
lastTransitionAge = entity.ticksLived
|
||||
} else {
|
||||
@@ -107,6 +120,7 @@ class RidingController(
|
||||
private fun canTransitionToAir(driver: LivingEntity?): Boolean {
|
||||
if (driver != null) {
|
||||
if (driver !is DoubleJump) return false
|
||||
if (behaviours[RidingStyle.AIR] == null) return false
|
||||
// check if style is water and player is submerged
|
||||
val isEyeInLiquid = entity.isEyeInFluid(FluidTags.WATER) || entity.isEyeInFluid(FluidTags.LAVA)
|
||||
if (context?.style == RidingStyle.LIQUID && isEyeInLiquid) return false
|
||||
|
||||
+290
-140
@@ -11,7 +11,9 @@ 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
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
@@ -25,12 +27,13 @@ import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
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 org.joml.Vector3f
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import kotlin.math.*
|
||||
|
||||
class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
@@ -47,8 +50,22 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
val poseProvider = PoseProvider<BirdSettings, BirdState>(PoseType.HOVER)
|
||||
.with(PoseOption(PoseType.FLY) { _, state, _ -> state.rideVelocity.get().z > 0.2 })
|
||||
|
||||
val globalBird: BirdSettings
|
||||
get() = CobblemonRideSettings.bird
|
||||
|
||||
override fun isActive(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity): Boolean {
|
||||
return true
|
||||
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
|
||||
//Need to check other fluids
|
||||
if (vehicle.isInWater || vehicle.isUnderWater) {
|
||||
return@any false
|
||||
}
|
||||
//This might not actually work, depending on what the yPos actually is. yPos of the middle of the entity? the feet?
|
||||
if (it.y.toDouble() == (vehicle.position().y)) {
|
||||
val blockState = vehicle.level().getBlockState(it.below())
|
||||
return@any !(!blockState.isAir && blockState.fluidState.isEmpty)
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun pose(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity): PoseType {
|
||||
@@ -64,6 +81,57 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
|
||||
override fun tick(
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
) {
|
||||
if(vehicle.level().isClientSide) {
|
||||
tickStamina(settings, state, vehicle, driver)
|
||||
checkUpsideDown(state, vehicle, driver)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkUpsideDown(
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
) {
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
if (cos(controller.roll.toRadians()) < 0.0) {
|
||||
val upsideDownRate = 1.0 / (2.0 * 20.0) // 2 seconds to max out upsideDown force
|
||||
state.currUpsideDownForce.set( min(1.0, state.currUpsideDownForce.get() + upsideDownRate * abs(cos(controller.roll.toRadians()))))
|
||||
} else if (state.stamina.get() == 0.0f) { // checks stamina depletion and forces dive if depleted
|
||||
val upsideDownRate = 1.0 / (2.0 * 20.0) // 1 seconds to max out
|
||||
state.currUpsideDownForce.set( min(1.0, state.currUpsideDownForce.get() + upsideDownRate))
|
||||
} else { // Its right side up or has stamina so reset it
|
||||
state.currUpsideDownForce.set(0.0)
|
||||
}
|
||||
}
|
||||
|
||||
fun tickStamina(
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
) {
|
||||
val stam = state.stamina.get()
|
||||
|
||||
// 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 ?: globalBird.staminaExpr!!) * 20.0f
|
||||
val stamDrainRate = (1.0f / totalStam).toFloat()
|
||||
|
||||
val newStam = if (driver.zza > 0.0) max(0.0f,stam - stamDrainRate)
|
||||
else if (!state.gliding.get()) max(0.0f,stam - stamDrainRate * 0.5f) // if hovering half the stam drain
|
||||
else min(1.0f,stam + stamDrainRate * 0.2f)
|
||||
|
||||
state.stamina.set(newStam)
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
@@ -80,8 +148,6 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
var yVel = 0.0
|
||||
var leftForce = 0.0
|
||||
var upForce = 0.0
|
||||
var forwardForce = 0.0
|
||||
|
||||
@@ -100,14 +166,10 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
forwardForce += sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().y
|
||||
}
|
||||
|
||||
// The downward force used to encourage players to stop flying upside down.
|
||||
val extraDownwardForce = state.currUpsideDownForce.get().pow(2) * -0.3 // 6 blocks a second downward
|
||||
|
||||
//Bring the ride out of the sky when stamina is depleted.
|
||||
if (state.stamina.get() <= 0.0) {
|
||||
upForce -= 0.3
|
||||
}
|
||||
|
||||
|
||||
val velocity = Vec3(state.rideVelocity.get().x , upForce, forwardForce)
|
||||
val velocity = Vec3(state.rideVelocity.get().x , upForce + extraDownwardForce, forwardForce)
|
||||
return velocity
|
||||
}
|
||||
|
||||
@@ -115,20 +177,25 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
* Calculates the change in the ride space vector due to player input and ride state
|
||||
*/
|
||||
fun calculateRideSpaceVel(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity, driver: Player) {
|
||||
//retrieve stats
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val glideTopSpeed = vehicle.runtime.resolveDouble(settings.glidespeedExpr)
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
|
||||
val staminaStat = vehicle.runtime.resolveDouble(settings.staminaExpr)
|
||||
// retrieve stats
|
||||
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
|
||||
|
||||
var newVelocity = Vec3(state.rideVelocity.get().x, state.rideVelocity.get().y, state.rideVelocity.get().z)
|
||||
|
||||
// Account for collision
|
||||
if (vehicle.verticalCollision || vehicle.horizontalCollision) {
|
||||
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) {
|
||||
//make sure it can't exceed top speed
|
||||
val forwardInput = when {
|
||||
state.stamina.get() == 0.0f -> 0.0
|
||||
driver.zza > 0 && newVelocity.z > topSpeed -> 0.0
|
||||
driver.zza < 0 && newVelocity.z < (-topSpeed / 3.0) -> 0.0
|
||||
else -> driver.zza.sign
|
||||
@@ -148,7 +215,6 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
glideSpeedChange = sin(Math.toRadians(controller.pitch.toDouble()))
|
||||
glideSpeedChange = glideSpeedChange * 0.25
|
||||
|
||||
//TODO: Possibly create a deadzone around parallel where glide doesn't affect speed?
|
||||
if (glideSpeedChange <= 0.0) {
|
||||
//Ensures that a propelling force is still able to be applied when
|
||||
//climbing in height
|
||||
@@ -176,6 +242,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
//Vertical movement based on driver input.
|
||||
val vertTopSpeed = topSpeed / 2.0
|
||||
val vertInput = when {
|
||||
state.stamina.get() == 0.0f -> 0.0
|
||||
driver.jumping && state.rideVelocity.get().length() < 0.3 -> 1.0
|
||||
driver.isShiftKeyDown && state.rideVelocity.get().length() < 0.3 -> -1.0
|
||||
else -> 0.0
|
||||
@@ -197,28 +264,13 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Reintroduce stamina drain once stats start to be polished and tweaked
|
||||
// //Only perform stamina logic if the ride does not have infinite stamina
|
||||
// if (!vehicle.runtime.resolveBoolean(settings.infiniteStamina)) {
|
||||
// if (activeInput) {
|
||||
// state.stamina.set(state.stamina.get() - (0.05 / staminaStat).toFloat())
|
||||
// }
|
||||
//
|
||||
// //Lose a base amount of stamina just for being airborne
|
||||
// state.stamina.set(state.stamina.get() - (0.01 / staminaStat).toFloat())
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// state.stamina.set(1.0f)
|
||||
// }
|
||||
|
||||
//air resistance
|
||||
if( abs(newVelocity.z) > 0) {
|
||||
newVelocity = newVelocity.subtract(0.0, 0.0, min(0.0015 * newVelocity.z.sign, newVelocity.z))
|
||||
@@ -233,39 +285,92 @@ 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
|
||||
|
||||
//limit rolling based on handling and current speed.
|
||||
//modulated by speed so that when flapping idle in air you are ont wobbling around to look around
|
||||
//TODO: Tie handling into yaw rate
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
//This rotation limit will be used to know if the ride has exceeded it and its needs correcting.
|
||||
val rotMin = 15.0
|
||||
val rotLimit = max(handling * sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed)), rotMin)
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalBird.handlingExpr!!)
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalBird.speedExpr!!) / 20.0
|
||||
|
||||
var yawDeltaDeg = deltaTime * handling * sin(Math.toRadians(controller.roll.toDouble())) //sin(Math.toRadians(controller.roll.toDouble())) * abs(cos(Math.toRadians(controller.pitch.toDouble())))
|
||||
val trueYawDelt = yawDeltaDeg * abs(cos(Math.toRadians(controller.pitch.toDouble())))
|
||||
//val pitchedYawDelt = yawDeltaDeg - trueYawDelt
|
||||
val yawDeltaDeg = deltaTime * handling * sin(Math.toRadians(controller.roll.toDouble())) //* sin(Math.toRadians(controller.roll.toDouble())).sign
|
||||
val trueYawDelt = yawDeltaDeg * abs(cos(Math.toRadians(controller.pitch.toDouble())))// * sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed))
|
||||
|
||||
controller.applyGlobalYaw(trueYawDelt.toFloat())
|
||||
// Dampen yaw when upside down
|
||||
val yawDampen = (1 - abs(min(cos(controller.roll.toRadians()),0.0f)).pow(2))
|
||||
controller.applyGlobalYaw(trueYawDelt.toFloat() * yawDampen)
|
||||
|
||||
//Calculate correcting roll force.
|
||||
val correctionRate = 5.0
|
||||
if(abs(controller.roll) > rotLimit && state.rideVelocity.get().length() < 0.2) {
|
||||
// Correct orientation when at low speeds
|
||||
correctOrientation(settings, state, vehicle, controller, deltaTime)
|
||||
|
||||
controller.applyGlobalPitch(state.currPitchCorrectionForce.get().toRadians())
|
||||
|
||||
//yaw, pitch, roll
|
||||
return Vec3(0.0, 0.0, state.currRollCorrectionForce.get())
|
||||
}
|
||||
|
||||
private fun correctOrientation(
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity,
|
||||
controller: OrientationController,
|
||||
deltaTime: Double
|
||||
) {
|
||||
// Calculate correcting roll force.
|
||||
// Mult by cos(pitch) to ensure that it doesn't roll correct at when diving or climbing
|
||||
val rollCorrectionTimer = vehicle.runtime.resolveDouble(settings.timeToRollCorrect ?: globalBird.timeToRollCorrect!!)
|
||||
val maxRollCorrectionRate = if (state.rideVelocity.get().length() < 0.1) 15.0f else 8.0f * (cos(controller.pitch.toRadians())).pow(2) * ((state.noInputTimeRoll.get() - rollCorrectionTimer)).coerceIn(0.0, 1.0).toFloat()
|
||||
val maxRollForce = maxRollCorrectionRate / 40.0
|
||||
val rollArrivalDeg = 90.0
|
||||
if(state.rideVelocity.get().length() < 0.1 || state.noInputTimeRoll.get() > rollCorrectionTimer) {
|
||||
val arrivalInfluence = (min(rollArrivalDeg,abs(controller.roll).toDouble()) / rollArrivalDeg)
|
||||
val desiredRollForce = (0 - controller.roll).sign * maxRollCorrectionRate * arrivalInfluence
|
||||
var steeredRollForce = desiredRollForce - state.currRollCorrectionForce.get()
|
||||
|
||||
// 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() + ((abs(controller.roll) - rotLimit) / 180 - rotLimit) * controller.roll.sign * correctionRate * deltaTime * 0.01
|
||||
state.currRollCorrectionForce.get() + steeredRollForce
|
||||
)
|
||||
} else {
|
||||
state.currRollCorrectionForce.set(
|
||||
//reduce rollCorrection gradually
|
||||
lerp(state.currRollCorrectionForce.get(), 0.0, deltaTime * 0.98)
|
||||
lerp(state.currRollCorrectionForce.get(), 0.0, deltaTime * 0.96)
|
||||
)
|
||||
}
|
||||
|
||||
//yaw, pitch, roll
|
||||
return Vec3(0.0, 0.0, state.currRollCorrectionForce.get())
|
||||
//Calculate correcting pitch force.
|
||||
val maxPitchCorrectionRate = if (state.currUpsideDownForce.get() != 0.0) 8f * state.currUpsideDownForce.get() else 15f
|
||||
val maxPitchForce = maxPitchCorrectionRate.toFloat() / 40.0f
|
||||
val pitchArrivalDeg = 45.0f
|
||||
var desiredPitch = if (state.currUpsideDownForce.get() != 0.0) 90.0 else 0.0
|
||||
// if (cos(controller.roll.toRadians()) < 0.0) {
|
||||
// desiredPitch *= -1 // If not rolled over then flip the pitch force. There are better ways to do this :(
|
||||
// }
|
||||
|
||||
if((state.rideVelocity.get().length() < 0.1 && controller.pitch < 0.0) || state.currUpsideDownForce.get() != 0.0) {
|
||||
val arrivalInfluence = (min(pitchArrivalDeg,abs(Mth.wrapDegrees(desiredPitch.toFloat().angleDifference(controller.pitch)))) / pitchArrivalDeg)
|
||||
val desiredPitchForce = maxPitchCorrectionRate.toFloat() * arrivalInfluence
|
||||
var steeredPitchForce = (desiredPitchForce - state.currPitchCorrectionForce.get())
|
||||
|
||||
// Ensure the 'steering' correction force doesn't exceed our maxForce
|
||||
if (abs(steeredPitchForce) > maxPitchForce) {
|
||||
steeredPitchForce = steeredPitchForce.sign * maxPitchForce
|
||||
}
|
||||
|
||||
// Apply the 'steered' force (its really not a steering force I need a better name)
|
||||
state.currPitchCorrectionForce.set(
|
||||
state.currPitchCorrectionForce.get() + steeredPitchForce
|
||||
)
|
||||
} else {
|
||||
state.currPitchCorrectionForce.set(
|
||||
//reduce rollCorrection gradually
|
||||
lerp(state.currPitchCorrectionForce.get(), 0.0, deltaTime * 0.98)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
@@ -280,6 +385,25 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
// Begin the roll correction time counter if not enough mouse input
|
||||
// is detected
|
||||
if (mouseX < 1 && abs(mouseY) < 1) {
|
||||
state.noInputTimeRoll.set(state.noInputTimeRoll.get() + deltaTime)
|
||||
} else if (cos(controller.roll.toRadians()) < 0.1) {
|
||||
state.noInputTimeRoll.set(state.noInputTimeRoll.get() + deltaTime * 0.5)
|
||||
}
|
||||
else {
|
||||
state.noInputTimeRoll.set(0.0)
|
||||
}
|
||||
|
||||
if (abs(mouseY) < 1) {
|
||||
state.noInputTimePitch.set(state.noInputTimePitch.get() + deltaTime)
|
||||
} else {
|
||||
state.noInputTimePitch.set(0.0)
|
||||
}
|
||||
|
||||
return when {
|
||||
Cobblemon.config.disableRoll -> noRollRotation(
|
||||
settings,
|
||||
@@ -326,24 +450,23 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
// Set roll to zero if transitioning to noroll config
|
||||
controller.rotateRoll(controller.roll * -1.0f)
|
||||
|
||||
//TODO: hook up handling in parity with normal bird
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalBird.handlingExpr!!)
|
||||
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult ?: globalBird.mouseInputMult!!)
|
||||
|
||||
//Smooth out mouse input.
|
||||
val smoothingSpeed = 4.0
|
||||
val mouseXc = (mouseX).coerceIn(-60.0, 60.0)
|
||||
val mouseYc = (mouseY).coerceIn(-60.0, 60.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 * ( 1.0 - min(sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed)), 0.5))
|
||||
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())
|
||||
controller.applyGlobalYaw(yawForce.toFloat() * abs(cos(controller.pitch.toRadians())).pow(2))
|
||||
|
||||
var pitchRot = yInput
|
||||
var pitchRot = yInput * handling
|
||||
|
||||
// Pitch up globally
|
||||
if (abs(controller.pitch + pitchRot) >= 89.5 ) {
|
||||
@@ -372,62 +495,48 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalBird.speedExpr!!) / 20.0
|
||||
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult ?: globalBird.mouseInputMult!!)
|
||||
|
||||
//Smooth out mouse input.
|
||||
val smoothingSpeed = 4.0
|
||||
val mouseXc = (mouseX).coerceIn(-60.0, 60.0)
|
||||
val mouseYc = (mouseY).coerceIn(-60.0, 60.0)
|
||||
val smoothingSpeed = 2.0
|
||||
val mouseXc = (mouseX).coerceIn(-45.0, 45.0) * mouseInputMult
|
||||
// When upside down apply a pitching force to nudge the player towards a dive. This reduces the 'weirdness'
|
||||
// of being upside down
|
||||
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 currSpeed = state.rideVelocity.get().length()
|
||||
val currHorzSpeed = state.rideVelocity.get().horizontalDistance()
|
||||
val hoverSpeed = 0.1
|
||||
|
||||
//limit rolling based on handling and current speed.
|
||||
//modulated by speed so that when flapping idle in air you are ont wobbling around to look around
|
||||
val rotMin = 15.0
|
||||
var rollForce = xInput
|
||||
val rotLimit = max(90 * sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed)), rotMin)
|
||||
// Yaw locally when at or below hoverSpeed but not when pitched up or rolled over
|
||||
val yawDampen = abs(cos(controller.pitch.toRadians())).pow(2) * abs(cos(controller.roll.toRadians())).pow(2)
|
||||
val localYaw = if (currHorzSpeed < hoverSpeed) xInput * (1 - sqrt(RidingBehaviour.scaleToRange(currHorzSpeed, hoverSpeed, topSpeed))).coerceIn(0.0, 1.0)
|
||||
else 0.0 //xInput * 0.4 * cos(controller.roll.toRadians()).coerceIn(0.0f, 1.0f) * yawDampen
|
||||
|
||||
//Limit roll by non linearly decreasing inputs towards
|
||||
// a rotation limit based on the current distance from
|
||||
// that rotation limit
|
||||
if (abs(controller.roll + rollForce) < rotLimit) {
|
||||
if (sign(rollForce) == sign(controller.roll).toDouble()) {
|
||||
//Grab how far the current roll is away from the roll limit
|
||||
val d = abs(abs(controller.roll) - rotLimit)
|
||||
rollForce *= (d.pow(2)) / (rotLimit.pow(2))
|
||||
}
|
||||
} else { //if (sign(rollForce) == sign(controller.roll).toDouble()) {
|
||||
rollForce *= max(abs(sin(controller.pitch.toRadians())),0.1f)
|
||||
}
|
||||
// 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 ?: 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 || yInput > 0.0) RidingBehaviour.scaleToRange(currSpeed, 0.0, topSpeed).coerceIn(0.2, 1.0)
|
||||
else 0.0
|
||||
|
||||
//Give the ability to yaw with x mouse input when at low speeds.
|
||||
val yawForce = xInput * ( 1.0 - sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed)))
|
||||
|
||||
// //Yaw locally a bit when up or down so that its more intuitive to make it out of a dive or a straight vertical
|
||||
// //climb
|
||||
// val yawForcePitched = xInput * sin(Math.toRadians(abs(controller.pitch.toDouble()))) * 0.25
|
||||
|
||||
|
||||
//Apply yaw globally as we don't want roll or pitch changes due to local yaw when looking up or down.
|
||||
if (controller.upVector.dot(Vector3f(0f,1f,0f)) > 0) {
|
||||
controller.applyGlobalYaw(yawForce.toFloat())
|
||||
}
|
||||
|
||||
|
||||
// Pitch up globally
|
||||
controller.applyGlobalPitch(-1 * yInput.toFloat())
|
||||
// roll to 0 if pitching and not upside down. This is to prevent odd back and forth wobbling when
|
||||
// pitching globally and experiencing axis changes
|
||||
if (controller.upVector.dot(Vector3f(0f,1f,0f)) > 0) {
|
||||
// correct roll equal to the amount the ride is pitching (yInput)
|
||||
// correct roll only when near to horizontal (cos(pitch))
|
||||
// correct roll proportional to how rolled the ride currently is
|
||||
rollForce += controller.roll * -0.01 * abs(yInput) * abs(cos(controller.pitch.toRadians())) * abs(sin(controller.roll.toRadians()))
|
||||
}
|
||||
// Roll less when slow and not at all when at hoverSpeed or lower.
|
||||
// Apply a roll righting force when trying to pitch hard while rolled sideways. This nudges the player towards
|
||||
// pitching globally
|
||||
val pitchInfluencedRollCorrection = 0.0
|
||||
val howRolledAmI = (( cos(controller.roll.toRadians()) - 1).coerceIn(-1.0f, 0.0f) * -2).coerceIn(0.0f, 0.7f) //Coerce into how much roll you want to let in in the deadzone
|
||||
val inputInRolledDir = if (xInput.sign.toFloat() == controller.roll.sign) 1.0 else -cos(controller.roll.toRadians()).coerceIn(-1.0f, 0.0f).toDouble()
|
||||
val rollDampen = (1 - howRolledAmI * abs(cos(controller.pitch.toRadians())).pow(2) * inputInRolledDir)
|
||||
val rollForce = if (currHorzSpeed > hoverSpeed) xInput * sqrt(abs(RidingBehaviour.scaleToRange(currHorzSpeed, hoverSpeed, topSpeed)).coerceIn(0.0,1.0)) * rollDampen + pitchInfluencedRollCorrection
|
||||
else 0.0
|
||||
|
||||
//yaw, pitch, roll
|
||||
return Vec3(0.0, 0.0, rollForce)
|
||||
return Vec3(localYaw, localPitch, rollForce)
|
||||
}
|
||||
|
||||
|
||||
@@ -474,13 +583,14 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val glideTopSpeed = vehicle.runtime.resolveDouble(settings.glidespeedExpr)
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalBird.speedExpr!!) / 20.0
|
||||
val glideTopSpeed = topSpeed * vehicle.runtime.resolveDouble(settings.glideSpeedExpr ?: globalBird.glideSpeedExpr!!)
|
||||
val currSpeed = state.rideVelocity.get().length()
|
||||
|
||||
//Must I ensure that topspeed is greater than minimum?
|
||||
val normalizedGlideSpeed = RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), topSpeed, glideTopSpeed)
|
||||
// Must I ensure that topspeed is greater than minimum?
|
||||
val normalizedGlideSpeed = RidingBehaviour.scaleToRange(currSpeed, topSpeed, glideTopSpeed)
|
||||
|
||||
//Only ever want the fov change to be a max of 0.2 and for it to have non linear scaling.
|
||||
// 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
|
||||
}
|
||||
|
||||
@@ -536,6 +646,17 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun damageOnCollision(
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity,
|
||||
impactVec: Vec3
|
||||
): Boolean {
|
||||
if (!state.gliding.get()) return false
|
||||
val impactSpeed = impactVec.horizontalDistance().toFloat() * 10f
|
||||
return vehicle.causeFallDamage(impactSpeed, 1f, vehicle.damageSources().flyIntoWall())
|
||||
}
|
||||
|
||||
override fun getRideSounds(
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
@@ -551,60 +672,70 @@ 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
|
||||
|
||||
//max y level for the ride
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 200.0, 128.0)".asExpression()
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 135.0, 45.0)".asExpression()
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 1.0, 0.35)".asExpression()
|
||||
var accelerationExpr: Expression = "q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 3.0)), (1.0 / (20.0 * 8.0)))".asExpression()
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 120.0, 20.0)".asExpression()
|
||||
var timeToRollCorrect: Expression? = null
|
||||
private set
|
||||
|
||||
var glidespeedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 2.0, 1.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? = null
|
||||
var staminaExpr: Expression? = null
|
||||
|
||||
var glideSpeedExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(infiniteAltitude)
|
||||
buffer.writeExpression(infiniteStamina)
|
||||
buffer.writeExpression(glidespeedExpr)
|
||||
buffer.writeExpression(jumpExpr)
|
||||
buffer.writeExpression(handlingExpr)
|
||||
buffer.writeExpression(speedExpr)
|
||||
buffer.writeExpression(accelerationExpr)
|
||||
buffer.writeExpression(staminaExpr)
|
||||
buffer.writeNullableExpression(infiniteAltitude)
|
||||
buffer.writeNullableExpression(infiniteStamina)
|
||||
buffer.writeNullableExpression(glideSpeedExpr)
|
||||
buffer.writeNullableExpression(handlingExpr)
|
||||
buffer.writeNullableExpression(horizontalPitchExpr)
|
||||
buffer.writeNullableExpression(mouseInputMult)
|
||||
buffer.writeNullableExpression(speedExpr)
|
||||
buffer.writeNullableExpression(accelerationExpr)
|
||||
buffer.writeNullableExpression(staminaExpr)
|
||||
buffer.writeNullableExpression(timeToRollCorrect)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
infiniteAltitude = buffer.readExpression()
|
||||
infiniteStamina = buffer.readExpression()
|
||||
glidespeedExpr = buffer.readExpression()
|
||||
jumpExpr = buffer.readExpression()
|
||||
handlingExpr = buffer.readExpression()
|
||||
speedExpr = buffer.readExpression()
|
||||
accelerationExpr = buffer.readExpression()
|
||||
staminaExpr = buffer.readExpression()
|
||||
infiniteAltitude = buffer.readNullableExpression()
|
||||
infiniteStamina = buffer.readNullableExpression()
|
||||
glideSpeedExpr = buffer.readNullableExpression()
|
||||
handlingExpr = buffer.readNullableExpression()
|
||||
horizontalPitchExpr = buffer.readNullableExpression()
|
||||
mouseInputMult = buffer.readNullableExpression()
|
||||
speedExpr = buffer.readNullableExpression()
|
||||
accelerationExpr = buffer.readNullableExpression()
|
||||
staminaExpr = buffer.readNullableExpression()
|
||||
timeToRollCorrect = buffer.readNullableExpression()
|
||||
}
|
||||
}
|
||||
|
||||
class BirdState : RidingBehaviourState() {
|
||||
var gliding = ridingState(false, Side.CLIENT)
|
||||
var gliding = ridingState(false, Side.BOTH)
|
||||
var lastGlide = ridingState(-100L, Side.CLIENT)
|
||||
var currRollCorrectionForce = ridingState(0.0, Side.CLIENT)
|
||||
var currPitchCorrectionForce = 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())
|
||||
}
|
||||
|
||||
@@ -618,6 +749,9 @@ class BirdState : RidingBehaviourState() {
|
||||
gliding.set(false, forced = true)
|
||||
lastGlide.set(-100L, forced = true)
|
||||
currRollCorrectionForce.set(0.0, forced = true)
|
||||
currPitchCorrectionForce.set(0.0, forced = true)
|
||||
noInputTimeRoll.set(0.0, forced = true)
|
||||
noInputTimePitch.set(0.0, forced = true)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
@@ -630,6 +764,9 @@ class BirdState : RidingBehaviourState() {
|
||||
it.gliding.set(this.gliding.get(), forced = true)
|
||||
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.noInputTimeRoll.set(this.noInputTimeRoll.get(), forced = true)
|
||||
it.noInputTimePitch.set(this.noInputTimePitch.get(), forced = true)
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
@@ -638,3 +775,16 @@ class BirdState : RidingBehaviourState() {
|
||||
return super.shouldSync(previous)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the shortest difference between two angles.
|
||||
* The result will be in the range [-180, 180].
|
||||
* A positive result indicates a clockwise direction from this angle to the other,
|
||||
* a negative result indicates a counter-clockwise direction.
|
||||
*/
|
||||
fun Float.angleDifference(other: Float): Float {
|
||||
var diff = other - this
|
||||
while (diff <= -180F) diff += 360F
|
||||
while (diff > 180F) diff -= 360F
|
||||
return diff
|
||||
}
|
||||
+16
-14
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.air
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
|
||||
@@ -37,6 +38,9 @@ class GliderBehaviour : RidingBehaviour<GliderSettings, RidingBehaviourState> {
|
||||
|
||||
override val key = KEY
|
||||
|
||||
val globalGlider: GliderSettings
|
||||
get() = CobblemonRideSettings.glider
|
||||
|
||||
override fun getRidingStyle(settings: GliderSettings, state: RidingBehaviourState): RidingStyle {
|
||||
return RidingStyle.AIR
|
||||
}
|
||||
@@ -53,7 +57,7 @@ class GliderBehaviour : RidingBehaviour<GliderSettings, RidingBehaviourState> {
|
||||
}
|
||||
|
||||
override fun speed(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity, driver: Player): Float {
|
||||
return vehicle.runtime.resolveFloat(settings.speed)
|
||||
return vehicle.runtime.resolveFloat(settings.speed ?: globalGlider.speed!!)
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
@@ -72,10 +76,9 @@ class GliderBehaviour : RidingBehaviour<GliderSettings, RidingBehaviourState> {
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
val xVector = if (vehicle.runtime.resolveBoolean(settings.canStrafe)) driver.xxa.toDouble() else 0.0
|
||||
val yVector = -vehicle.runtime.resolveDouble(settings.glideSpeed)
|
||||
val xVector = if (vehicle.runtime.resolveBoolean(settings.canStrafe ?: globalGlider.canStrafe!!)) driver.xxa.toDouble() else 0.0
|
||||
val yVector = -vehicle.runtime.resolveDouble(settings.glideSpeed ?: globalGlider.glideSpeed!!)
|
||||
val zVector = driver.zza.toDouble()
|
||||
val speedStat = settings.calculate(RidingStat.SPEED, 0)
|
||||
return Vec3(xVector, yVector, zVector)
|
||||
}
|
||||
|
||||
@@ -196,31 +199,30 @@ class GliderSettings : RidingBehaviourSettings {
|
||||
override val key = GliderBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var glideSpeed: Expression = "0.1".asExpression()
|
||||
var glideSpeed: Expression? = null
|
||||
private set
|
||||
|
||||
var speed: Expression = "1.0".asExpression()
|
||||
var speed: Expression? = null
|
||||
private set
|
||||
|
||||
var canStrafe: Expression = "false".asExpression()
|
||||
var canStrafe: Expression? = null
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(glideSpeed)
|
||||
buffer.writeExpression(speed)
|
||||
buffer.writeExpression(canStrafe)
|
||||
buffer.writeNullableExpression(glideSpeed)
|
||||
buffer.writeNullableExpression(speed)
|
||||
buffer.writeNullableExpression(canStrafe)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
glideSpeed = buffer.readExpression()
|
||||
speed = buffer.readExpression()
|
||||
canStrafe = buffer.readExpression()
|
||||
glideSpeed = buffer.readNullableExpression()
|
||||
speed = buffer.readNullableExpression()
|
||||
canStrafe = buffer.readNullableExpression()
|
||||
}
|
||||
}
|
||||
+27
-19
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.air
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
|
||||
@@ -21,7 +22,16 @@ import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.readNullableExpression
|
||||
import com.cobblemon.mod.common.util.readRidingStats
|
||||
import com.cobblemon.mod.common.util.resolveDouble
|
||||
import com.cobblemon.mod.common.util.writeNullableExpression
|
||||
import com.cobblemon.mod.common.util.writeRidingStats
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sin
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.SmoothDouble
|
||||
@@ -30,9 +40,6 @@ import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sin
|
||||
|
||||
class HelicopterBehaviour : RidingBehaviour<HelicopterSettings, RidingBehaviourState> {
|
||||
companion object {
|
||||
@@ -41,6 +48,8 @@ class HelicopterBehaviour : RidingBehaviour<HelicopterSettings, RidingBehaviourS
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
val globalHelicopter: HelicopterSettings
|
||||
get() = CobblemonRideSettings.helicopter
|
||||
|
||||
override fun getRidingStyle(settings: HelicopterSettings, state: RidingBehaviourState): RidingStyle {
|
||||
return RidingStyle.AIR
|
||||
@@ -93,8 +102,8 @@ class HelicopterBehaviour : RidingBehaviour<HelicopterSettings, RidingBehaviourS
|
||||
val runtime = vehicle.runtime
|
||||
|
||||
//If the player is not modulating height then hover
|
||||
var yVel = if (driver.jumping) runtime.resolveDouble(settings.verticalVelocity)
|
||||
else if (driver.isShiftKeyDown) -runtime.resolveDouble(settings.verticalVelocity)
|
||||
var yVel = if (driver.jumping) runtime.resolveDouble(settings.verticalVelocity ?: globalHelicopter.verticalVelocity!!)
|
||||
else if (driver.isShiftKeyDown) -runtime.resolveDouble(settings.verticalVelocity ?: globalHelicopter.verticalVelocity!!)
|
||||
else (0.0)
|
||||
|
||||
yVel *= 0.25
|
||||
@@ -290,36 +299,35 @@ class HelicopterSettings : RidingBehaviourSettings {
|
||||
override val key = HelicopterBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var gravity: Expression = "1.0".asExpression()
|
||||
var gravity: Expression? = null
|
||||
private set
|
||||
|
||||
var horizontalAcceleration: Expression = "0.1".asExpression()
|
||||
var horizontalAcceleration: Expression? = null
|
||||
private set
|
||||
|
||||
var verticalVelocity: Expression = "1.0".asExpression()
|
||||
var verticalVelocity: Expression? = null
|
||||
private set
|
||||
|
||||
var speed: Expression = "1.0".asExpression()
|
||||
var speed: Expression? = null
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(gravity)
|
||||
buffer.writeExpression(horizontalAcceleration)
|
||||
buffer.writeExpression(verticalVelocity)
|
||||
buffer.writeExpression(speed)
|
||||
buffer.writeNullableExpression(gravity)
|
||||
buffer.writeNullableExpression(horizontalAcceleration)
|
||||
buffer.writeNullableExpression(verticalVelocity)
|
||||
buffer.writeNullableExpression(speed)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
gravity = buffer.readExpression()
|
||||
horizontalAcceleration = buffer.readExpression()
|
||||
verticalVelocity = buffer.readExpression()
|
||||
speed = buffer.readExpression()
|
||||
gravity = buffer.readNullableExpression()
|
||||
horizontalAcceleration = buffer.readNullableExpression()
|
||||
verticalVelocity = buffer.readNullableExpression()
|
||||
speed = buffer.readNullableExpression()
|
||||
}
|
||||
}
|
||||
+187
-179
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types.air
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
@@ -19,13 +20,16 @@ import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.level.ClipContext
|
||||
import net.minecraft.world.phys.HitResult
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
@@ -38,6 +42,8 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
val globalHover: HoverSettings
|
||||
get() = CobblemonRideSettings.hover
|
||||
|
||||
override fun getRidingStyle(settings: HoverSettings, state: HoverState): RidingStyle {
|
||||
return RidingStyle.AIR
|
||||
@@ -81,7 +87,57 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
return state.speed.get().toFloat()
|
||||
}
|
||||
|
||||
override fun tick(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
) {
|
||||
if(vehicle.level().isClientSide) {
|
||||
tickStamina(settings, state, vehicle)
|
||||
checkTooHigh(settings, state, vehicle)
|
||||
}
|
||||
}
|
||||
|
||||
fun checkTooHigh(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity,
|
||||
) {
|
||||
val heightLimit = vehicle.runtime.resolveDouble(settings.jumpExpr ?: globalHover.jumpExpr!!)
|
||||
val pos = vehicle.position()
|
||||
val level = Minecraft.getInstance().player?.level() ?: return
|
||||
val hit = level.clip(
|
||||
ClipContext(
|
||||
pos,
|
||||
pos.subtract(0.0, heightLimit, 0.0),
|
||||
ClipContext.Block.COLLIDER,
|
||||
ClipContext.Fluid.NONE,
|
||||
vehicle
|
||||
)
|
||||
)
|
||||
|
||||
state.tooHigh.set(hit.type == HitResult.Type.MISS)
|
||||
}
|
||||
|
||||
fun tickStamina(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity
|
||||
) {
|
||||
if (vehicle.runtime.resolveBoolean(settings.infiniteStamina ?: globalHover.infiniteStamina!!)) {
|
||||
return
|
||||
}
|
||||
val stam = state.stamina.get()
|
||||
val stamDrainRate = (1.0f / vehicle.runtime.resolveDouble(settings.staminaExpr ?: globalHover.staminaExpr!!)).toFloat() / 20.0f
|
||||
val stamReplenishRate = (1.0f / vehicle.runtime.resolveDouble(settings.stamReplenishTimeSeconds ?: globalHover.stamReplenishTimeSeconds!!) ).toFloat() / 20.0f // 2 seconds to replenish a full bar of stamina
|
||||
val newStam = if (state.tooHigh.get()) max(0.0f, stam - stamDrainRate) else min(1.0f,stam + stamReplenishRate)
|
||||
|
||||
state.stamina.set(newStam)
|
||||
}
|
||||
|
||||
override fun updatePassengerRotation(
|
||||
@@ -90,24 +146,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
) {
|
||||
|
||||
//Take the inverse so that it cancels out how
|
||||
//much the entity rotates.
|
||||
val turnAmount = calcRotAmount(settings, state, vehicle, driver)
|
||||
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val maxYawDiff = 90.0f
|
||||
|
||||
//Normalize the current rotation diff
|
||||
val rotMod = Mth.wrapDegrees(driver.yRot - vehicle.yRot) / maxYawDiff
|
||||
|
||||
val rotAmount = 10.0f
|
||||
|
||||
//Take the inverse so that you turn more at higher speeds
|
||||
val normSpeed = 1.0f - 0.5f*RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed).toFloat()
|
||||
|
||||
//driver.yRot += (entity.riding.deltaRotation.y - turnAmount)
|
||||
//driver.setYHeadRot(driver.yHeadRot + (entity.riding.deltaRotation.y) - turnAmount)
|
||||
return
|
||||
}
|
||||
|
||||
override fun clampPassengerRotation(
|
||||
@@ -116,12 +155,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
) {
|
||||
val f = Mth.wrapDegrees(driver.yRot - vehicle.yRot)
|
||||
val lookYawLimit = 90.0f
|
||||
val g = Mth.clamp(f, -lookYawLimit, lookYawLimit)
|
||||
driver.yRotO += g - f
|
||||
driver.yRot = driver.yRot + g - f
|
||||
driver.setYHeadRot(driver.yRot)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -132,9 +166,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
driver: LivingEntity
|
||||
): Vec2 {
|
||||
val turnAmount = calcRotAmount(settings, state, vehicle, driver)
|
||||
|
||||
return Vec2(driver.xRot, vehicle.yRot + turnAmount )
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -149,27 +181,19 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Float {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
//In degrees per second
|
||||
val handling = 220.0
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalHover.speedExpr!!) / 20.0
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalHover.handlingExpr!!) / 20.0
|
||||
val maxYawDiff = 90.0f
|
||||
|
||||
//Normalize the current rotation diff
|
||||
val rotDiff = Mth.wrapDegrees(driver.yRot - vehicle.yRot)
|
||||
val rotDiffNorm = rotDiff / maxYawDiff
|
||||
// Normalize the current rotation diff
|
||||
val rotDiff = Mth.wrapDegrees(driver.yRot - vehicle.yRot).coerceIn(-maxYawDiff,maxYawDiff)
|
||||
val rotDiffInfl = sqrt(abs(rotDiff / maxYawDiff)) * rotDiff.sign
|
||||
|
||||
//Take the square root so that the ride levels out quicker when at lower differences between entity
|
||||
//y and driver y
|
||||
//This influences the speed of the turn based on how far in one direction you're looking
|
||||
val rotDiffMod = (sqrt(abs(rotDiffNorm)) * rotDiffNorm.sign)
|
||||
var turnRate = (handling.toFloat()) * rotDiffInfl
|
||||
turnRate -= (turnRate * 0.5f) * (RidingBehaviour.scaleToRange(vehicle.deltaMovement.length(), 0.0, topSpeed).coerceIn(0.0, 1.0)).pow(2).toFloat()
|
||||
|
||||
//Take the inverse so that you turn less at higher speeds
|
||||
val normSpeed = 1.0f // = 1.0f - 0.5f*RidingBehaviour.scaleToRange(state.rideVelocityocity.length(), 0.0, topSpeed).toFloat()
|
||||
|
||||
val turnRate = (handling.toFloat() / 20.0f)
|
||||
|
||||
//Ensure you only ever rotate as much difference as there is between the angles.
|
||||
val turnSpeed = turnRate * rotDiffMod * normSpeed
|
||||
// Ensure you only ever rotate as much difference as there is between the angles.
|
||||
val turnSpeed = turnRate
|
||||
val rotAmount = turnSpeed.coerceIn(-abs(rotDiff), abs(rotDiff))
|
||||
|
||||
return rotAmount
|
||||
@@ -182,128 +206,77 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
|
||||
// if (Minecraft.getInstance().options.keySprint.isDown) {
|
||||
// val belowBox = vehicle.boundingBox.expandTowards(0.0,-1.5, 0.0).inflate(5.0, 10.0,5.0)
|
||||
//
|
||||
// val level = vehicle.level()
|
||||
// val toBeSucked = level.getEntities(vehicle, belowBox)
|
||||
// for (suckee in toBeSucked) {
|
||||
// if (suckee != null) {
|
||||
// abductify(suckee, vehicle.position().add(0.0,-2.0,0.0))
|
||||
// suckee.deltaMovement = suckee.deltaMovement.add(0.0, 0.5,0.0)
|
||||
//
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
//
|
||||
// if (firstEntity != null) {
|
||||
// // Example action: print the entity name
|
||||
// println("Found nearby entity: ${firstEntity.name.string}")
|
||||
//
|
||||
// firstEntity.deltaMovement = firstEntity.deltaMovement.add(0.0, 0.1,0.0)
|
||||
// }
|
||||
val retVel = calculateRideSpaceVel(settings, state, vehicle, driver)
|
||||
//state.rideVelocity.set(retVel)
|
||||
return retVel
|
||||
}
|
||||
|
||||
private fun abductify(abductee: Entity, pos: Vec3) {
|
||||
val diff = pos.subtract(abductee.position()).scale(1.0)
|
||||
abductee.deltaMovement = abductee.deltaMovement.add(diff)
|
||||
}
|
||||
|
||||
private fun calculateRideSpaceVel(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Vec3 {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
|
||||
val speed = vehicle.deltaMovement.length()
|
||||
// Convert these to their tick values
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalHover.speedExpr!!) / 20.0
|
||||
val accel = topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalHover.accelerationExpr!!) * 20.0)
|
||||
|
||||
//Flag for determining if player is actively inputting
|
||||
var activeInput = false
|
||||
var newVelocity = state.rideVelocity.get()
|
||||
|
||||
val minSpeed = 0.0
|
||||
|
||||
var newVelocity = vehicle.deltaMovement
|
||||
// Check for collisions and if found, update the speed to reflect it
|
||||
if (vehicle.verticalCollision || vehicle.horizontalCollision) {
|
||||
state.speed.set(vehicle.deltaMovement.length())
|
||||
}
|
||||
|
||||
// align the velocity vector to be in local vehicle space
|
||||
val yawAligned = Matrix3f().rotateY(vehicle.yRot.toRadians())
|
||||
newVelocity = (newVelocity.toVector3f().mul(yawAligned)).toVec3d()
|
||||
|
||||
//air Resistance
|
||||
//newVelocity = newVelocity.lerp(Vec3.ZERO,(((topSpeed - speed) / topSpeed))*0.001)
|
||||
|
||||
|
||||
//speed up and slow down based on input
|
||||
if (driver.zza != 0.0f && state.stamina.get() > 0.0) {
|
||||
//make sure it can't exceed top speed
|
||||
val forwardInput = when {
|
||||
speed > topSpeed && (driver.zza.sign == newVelocity.z.sign.toFloat()) -> 0.0
|
||||
else -> driver.zza.sign
|
||||
}
|
||||
|
||||
newVelocity = Vec3(
|
||||
newVelocity.x,
|
||||
newVelocity.y,
|
||||
(newVelocity.z + (accel * forwardInput.toDouble())))
|
||||
|
||||
} else {
|
||||
// newVelocity = Vec3(
|
||||
// newVelocity.x,
|
||||
// newVelocity.y,
|
||||
// newVelocity.z - (((topSpeed - speed) / topSpeed)*0.001)
|
||||
// )
|
||||
}
|
||||
|
||||
//speed up and slow down based on input
|
||||
if (driver.xxa != 0.0f && state.stamina.get() > 0.0) {
|
||||
//make sure it can't exceed top speed
|
||||
val lateralInput = when {
|
||||
speed > topSpeed && (driver.xxa.sign == newVelocity.x.sign.toFloat()) -> 0.0
|
||||
else -> driver.xxa.sign
|
||||
}
|
||||
|
||||
newVelocity = Vec3(
|
||||
newVelocity.x + (accel * lateralInput.toDouble()),
|
||||
newVelocity.y,
|
||||
(newVelocity.z ))
|
||||
|
||||
//air resistance
|
||||
// if (lateralInput == 0.0) {
|
||||
// newVelocity = Vec3(
|
||||
// newVelocity.x - (((topSpeed - speed) / topSpeed)*0.001),
|
||||
// newVelocity.y,
|
||||
// newVelocity.z
|
||||
// )
|
||||
// }
|
||||
}
|
||||
|
||||
//Vertical movement based on driver input.
|
||||
// Vertical movement based on driver input.
|
||||
val vertInput = when {
|
||||
driver.jumping && !(speed > topSpeed && (newVelocity.y.sign.toFloat() > 0.0))-> 1.0
|
||||
driver.isShiftKeyDown && !(speed > topSpeed && (newVelocity.y.sign.toFloat() < 0.0))-> -1.0
|
||||
state.stamina.get() == 0.0f -> -1.0
|
||||
driver.jumping -> 1.0
|
||||
driver.isShiftKeyDown -> -1.0
|
||||
else -> 0.0
|
||||
}
|
||||
|
||||
//air resistance
|
||||
// if (vertInput == 0.0) {
|
||||
// newVelocity = Vec3(
|
||||
// newVelocity.x,
|
||||
// newVelocity.y - (((topSpeed - speed) / topSpeed)*0.001),
|
||||
// newVelocity.z
|
||||
// )
|
||||
// }
|
||||
// Normalize and scale to accel so that diagonal movement
|
||||
// doesn't speed up the ride more than input in one direction
|
||||
var inputVel = Vec3(
|
||||
driver.xxa.sign.toDouble(),
|
||||
vertInput,
|
||||
driver.zza.sign.toDouble()
|
||||
).normalize()
|
||||
|
||||
if (state.stamina.get() > 0.0) {
|
||||
newVelocity = Vec3(
|
||||
newVelocity.x,
|
||||
(newVelocity.y + (accel * vertInput)), //.coerceIn(-vertTopSpeed, vertTopSpeed),
|
||||
newVelocity.z)
|
||||
}
|
||||
// Air Friction/Resistance
|
||||
// When the dot between friction and input is -1 we want to apply none of it.
|
||||
// In short we want to avoid applying friction that opposes our input force.
|
||||
// otherwise we would not be able to reach our top speed when acceleration is
|
||||
// low.
|
||||
val frictionConst = 0.02
|
||||
var frictionForce = newVelocity.scale(-1.0).scale(frictionConst)
|
||||
val frictionApplied = if(inputVel.lengthSqr() != 0.0) 1 - (0.5*(1 + -1.0 * (inputVel.normalize().dot(frictionForce.normalize()))))
|
||||
else 1.0
|
||||
frictionForce = frictionForce.scale((frictionApplied))
|
||||
newVelocity = newVelocity.add(frictionForce)
|
||||
|
||||
// Ensure that if you're not applying force where the ride is facing then you apply a portion of debuff
|
||||
val strafeDebuff = 0.5
|
||||
val vehicleForwardVec = Vec3(0.0, 0.0, 1.0)
|
||||
val playerForwardVec = inputVel //.yRot(vehicle.yRot)
|
||||
val inputStrength = accel - (accel * strafeDebuff*(1 - vehicleForwardVec.dot(playerForwardVec).coerceIn(0.0, 1.0)))
|
||||
inputVel = inputVel.scale(inputStrength)
|
||||
|
||||
newVelocity = newVelocity.add(inputVel)
|
||||
|
||||
// Set speed to new magnitude to avoid the horrible things minecraft friction
|
||||
// does to our vectors :(
|
||||
state.speed.set(min(topSpeed, newVelocity.length()))
|
||||
|
||||
// Check to see if this new velocity will exceed top speed and if it will then cap it
|
||||
newVelocity = if (newVelocity.length() > topSpeed) newVelocity.normalize().scale(topSpeed) else newVelocity
|
||||
|
||||
val revertYawAligned = Matrix3f().rotateY(-vehicle.yRot.toRadians())
|
||||
state.rideVelocity.set((newVelocity.toVector3f().mul(revertYawAligned)).toVec3d())
|
||||
|
||||
return newVelocity
|
||||
}
|
||||
@@ -330,13 +303,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
//Smooth out mouse input.
|
||||
val smoothingSpeed = 4
|
||||
val xInput = mouseXSmoother.getNewDeltaValue(mouseX * 0.1, deltaTime * smoothingSpeed);
|
||||
val yInput = mouseYSmoother.getNewDeltaValue(mouseY * 0.1, deltaTime * smoothingSpeed);
|
||||
|
||||
//yaw, pitch, roll
|
||||
return Vec3(0.0, yInput, xInput)
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
@@ -383,7 +350,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return 1.0f
|
||||
return if (state.isBoosting.get()) 1.1f else 1.0f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(
|
||||
@@ -466,66 +433,107 @@ class HoverSettings : RidingBehaviourSettings {
|
||||
override val key = HoverBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var rideSound: ResourceLocation = "ride.loop.saucer".asIdentifierDefaultingNamespace()
|
||||
var volumeExpr: Expression = "math.pow(math.min(q.ride_velocity() / 0.8, 1.0),2)".asExpression()
|
||||
var pitchExpr: Expression = "math.pow(math.min(1.0 + 0.2*(q.ride_velocity() / 0.8), 1.2),2)".asExpression()
|
||||
|
||||
var canJump = "true".asExpression()
|
||||
var infiniteStamina: Expression? = null
|
||||
private set
|
||||
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 0.65, 0.3)".asExpression()
|
||||
var canJump: Expression? = null
|
||||
private set
|
||||
|
||||
// Max accel is a whole 1.0 in 1 second. The conversion in the function below is to convert seconds to ticks
|
||||
var accelerationExpr: Expression =
|
||||
"q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 1.5)), (1.0 / (20.0 * 3.0)))".asExpression()
|
||||
var stamReplenishTimeSeconds: Expression? = null
|
||||
private set
|
||||
|
||||
// Between 30 seconds and 10 seconds at the lowest when at full speed.
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 30.0, 10.0)".asExpression()
|
||||
// Speed in block per second
|
||||
var speedExpr: Expression? = null
|
||||
private set
|
||||
|
||||
//Between a one block jump and a ten block jump
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 10.0, 1.0)".asExpression()
|
||||
// Acceleration in number of seconds to top speed
|
||||
var accelerationExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 140.0, 20.0)".asExpression()
|
||||
// Amount of seconds between boosts
|
||||
var staminaExpr: Expression? = null
|
||||
private set
|
||||
|
||||
// Blocks before stamina drain
|
||||
var jumpExpr: Expression? = null
|
||||
private set
|
||||
|
||||
// Turn rate in degrees per second when stationary
|
||||
var handlingExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(speedExpr)
|
||||
buffer.writeExpression(accelerationExpr)
|
||||
buffer.writeExpression(staminaExpr)
|
||||
buffer.writeExpression(jumpExpr)
|
||||
buffer.writeExpression(handlingExpr)
|
||||
buffer.writeNullableExpression(infiniteStamina)
|
||||
buffer.writeNullableExpression(canJump)
|
||||
buffer.writeNullableExpression(stamReplenishTimeSeconds)
|
||||
buffer.writeNullableExpression(speedExpr)
|
||||
buffer.writeNullableExpression(accelerationExpr)
|
||||
buffer.writeNullableExpression(staminaExpr)
|
||||
buffer.writeNullableExpression(jumpExpr)
|
||||
buffer.writeNullableExpression(handlingExpr)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
speedExpr = buffer.readExpression()
|
||||
accelerationExpr = buffer.readExpression()
|
||||
staminaExpr = buffer.readExpression()
|
||||
jumpExpr = buffer.readExpression()
|
||||
handlingExpr = buffer.readExpression()
|
||||
infiniteStamina = buffer.readNullableExpression()
|
||||
canJump = buffer.readNullableExpression()
|
||||
stamReplenishTimeSeconds = buffer.readNullableExpression()
|
||||
speedExpr = buffer.readNullableExpression()
|
||||
accelerationExpr = buffer.readNullableExpression()
|
||||
staminaExpr = buffer.readNullableExpression()
|
||||
jumpExpr = buffer.readNullableExpression()
|
||||
handlingExpr = buffer.readNullableExpression()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class HoverState : RidingBehaviourState() {
|
||||
var speed = ridingState(0.0, Side.CLIENT)
|
||||
var isBoosting = ridingState(false, Side.BOTH)
|
||||
var boostVec = ridingState(Vec3.ZERO, Side.CLIENT)
|
||||
var boostTicks = ridingState(0, Side.CLIENT)
|
||||
var tooHigh = ridingState(false, Side.CLIENT)
|
||||
|
||||
override fun encode(buffer: FriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
buffer.writeDouble(speed.get())
|
||||
buffer.writeBoolean(isBoosting.get())
|
||||
buffer.writeBoolean(tooHigh.get())
|
||||
}
|
||||
|
||||
override fun decode(buffer: FriendlyByteBuf) {
|
||||
super.decode(buffer)
|
||||
speed.set(buffer.readDouble(), forced = true)
|
||||
isBoosting.set(buffer.readBoolean(), forced = true)
|
||||
tooHigh.set(buffer.readBoolean(), forced = true)
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
super.reset()
|
||||
speed.set(0.0, forced = true)
|
||||
isBoosting.set(false, forced = true)
|
||||
boostVec.set(Vec3.ZERO, forced = true)
|
||||
boostTicks.set(0, forced = true)
|
||||
tooHigh.set(false, forced = true)
|
||||
}
|
||||
|
||||
override fun copy() = HoverState().also {
|
||||
it.rideVelocity.set(this.rideVelocity.get(), forced = true)
|
||||
it.stamina.set(this.stamina.get(), forced = true)
|
||||
it.isBoosting.set(this.isBoosting.get(), forced = true)
|
||||
it.boostVec.set(this.boostVec.get(), forced = true)
|
||||
it.boostTicks.set(this.boostTicks.get(), forced = true)
|
||||
it.tooHigh.set(this.tooHigh.get(), forced = true)
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
if (previous !is HoverState) return false
|
||||
if (previous.isBoosting.get() != isBoosting.get()) return true
|
||||
return super.shouldSync(previous)
|
||||
}
|
||||
}
|
||||
+207
-109
@@ -11,19 +11,35 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.air
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.MoLangMath.lerp
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.Side
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.ridingState
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.config.CobblemonConfig
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import com.cobblemon.mod.common.util.readNullableExpression
|
||||
import com.cobblemon.mod.common.util.readRidingStats
|
||||
import com.cobblemon.mod.common.util.resolveDouble
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import com.cobblemon.mod.common.util.writeNullableExpression
|
||||
import com.cobblemon.mod.common.util.writeRidingStats
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
@@ -31,7 +47,7 @@ import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import kotlin.math.*
|
||||
import org.joml.Vector3f
|
||||
|
||||
class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
companion object {
|
||||
@@ -44,6 +60,9 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
return RidingStyle.AIR
|
||||
}
|
||||
|
||||
val globalJet: JetSettings
|
||||
get() = CobblemonRideSettings.jet
|
||||
|
||||
val poseProvider = PoseProvider<JetSettings, JetState>(PoseType.HOVER)
|
||||
.with(PoseOption(PoseType.FLY) { _, state, _ -> state.rideVelocity.get().z > 0.1 })
|
||||
|
||||
@@ -67,35 +86,65 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
}
|
||||
|
||||
override fun speed(settings: JetSettings, state: JetState, vehicle: PokemonEntity, driver: Player): Float {
|
||||
//retrieve stats
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val staminaStat = vehicle.runtime.resolveDouble(settings.staminaExpr)
|
||||
|
||||
//retrieve minSpeed
|
||||
val minSpeed = vehicle.runtime.resolveDouble(settings.minSpeed)
|
||||
|
||||
//TODO: Reintroduce stamina drain once stats start to be polished and tweaked
|
||||
state.stamina.set(1.0f)
|
||||
// //Reduce stamina unless stamina is infinite
|
||||
// if (!vehicle.runtime.resolveBoolean(settings.infiniteStamina)) {
|
||||
// //Calculate stamina loss due to speed
|
||||
// //At max speed it will tick down 0.1 a second so the stamina will last ten seconds
|
||||
// //There has got to be a better way to express this equation. It interpolates between 0.5 and 1.0
|
||||
// var staminaRate = (RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), minSpeed, topSpeed))
|
||||
//
|
||||
// //interpolate between 0.25 and 1.0 so that you always have at least a min of 0.25 stam loss
|
||||
// staminaRate = 0.25 + (0.75 * staminaRate.pow(3))
|
||||
//
|
||||
// //Calculate stamina loss in seconds achievable at top speed
|
||||
// val staminaLoss = staminaRate * (1.0 / (20.0 * staminaStat))
|
||||
// state.stamina.set(max(state.stamina.get() - staminaLoss, 0.0).toFloat())
|
||||
// } else {
|
||||
// state.stamina.set(1.0f)
|
||||
// }
|
||||
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
|
||||
override fun tick(settings: JetSettings, state: JetState, vehicle: PokemonEntity, driver: Player, input: Vec3) {
|
||||
if(vehicle.level().isClientSide) {
|
||||
handleBoosting(state)
|
||||
tickStamina(settings, state, vehicle)
|
||||
}
|
||||
}
|
||||
|
||||
fun handleBoosting(
|
||||
state: JetState,
|
||||
) {
|
||||
//If the forward key is not held then it cannot be boosting
|
||||
val boostKeyPressed = Minecraft.getInstance().options.keySprint.isDown()
|
||||
|
||||
if(state.stamina.get() != 0.0f && boostKeyPressed) {
|
||||
if (state.stamina.get() >= 0.0f) {
|
||||
//If on the previous tick the boost key was held then don't change if the ride is boosting
|
||||
if(state.boostIsToggleable.get()) {
|
||||
//flip the boosting state if boost key is pressed
|
||||
state.boosting.set(!state.boosting.get())
|
||||
}
|
||||
//If the boost key is not held then next tick boosting is toggleable
|
||||
state.boostIsToggleable.set(false)
|
||||
}
|
||||
} else {
|
||||
//Turn off boost and reset boost params
|
||||
state.boostIsToggleable.set(true)
|
||||
state.boosting.set(false)
|
||||
state.canSpeedBurst.set(true)
|
||||
}
|
||||
}
|
||||
|
||||
fun tickStamina(
|
||||
settings: JetSettings,
|
||||
state: JetState,
|
||||
vehicle: PokemonEntity,
|
||||
) {
|
||||
val stam = state.stamina.get()
|
||||
var newStam = stam
|
||||
val stamDrainRate = (1.0f / vehicle.runtime.resolveDouble(settings.staminaExpr ?: globalJet.staminaExpr!!)).toFloat() / 20.0f
|
||||
|
||||
if (state.boosting.get()) {
|
||||
newStam = max(0.0f,stam - stamDrainRate * 1.5f)
|
||||
} else {
|
||||
newStam = max(0.0f,stam - stamDrainRate)
|
||||
}
|
||||
|
||||
// If out of stamina then increase noStamTickCnt
|
||||
if (newStam == 0.0f) {
|
||||
state.noStamTickCnt.set(state.noStamTickCnt.get() + 1)
|
||||
} else {
|
||||
state.noStamTickCnt.get()
|
||||
}
|
||||
|
||||
state.stamina.set(newStam)
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
settings: JetSettings,
|
||||
state: JetState,
|
||||
@@ -112,26 +161,26 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
var upForce = 0.0
|
||||
var forwardForce = 0.0
|
||||
|
||||
val controller = (driver as? OrientationControllable)?.orientationController
|
||||
if (controller == null || controller.orientation == null) return Vec3.ZERO
|
||||
|
||||
//Calculate ride space velocity
|
||||
calculateRideSpaceVel(settings, state, vehicle, driver)
|
||||
|
||||
//Translate ride space velocity to world space velocity.
|
||||
if (controller != null) {
|
||||
upForce = -1.0 * sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().z
|
||||
forwardForce = cos(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().z
|
||||
}
|
||||
// The downward force used to encourage players to stop flying upside down.
|
||||
val maxDownwardForceTickCnt = 20.0
|
||||
val extraDownwardForce = if(state.stamina.get() == 0.0f) -0.3 * (state.noStamTickCnt.get() / maxDownwardForceTickCnt).coerceIn(0.0, 1.0) else 0.0 // 6 blocks a second downward
|
||||
|
||||
//If stamina has run out then initiate forced glide down.
|
||||
upForce = if (state.stamina.get() > 0.0) upForce else -0.7
|
||||
// Convert the local velocity vector into a world vector
|
||||
val localVelVec = Vector3f(
|
||||
state.rideVelocity.get().x.toFloat(),
|
||||
(state.rideVelocity.get().y).toFloat(),
|
||||
state.rideVelocity.get().z.toFloat() * -1.0f // Flip the z axis to make this left handed? orr.. right handed? idk, flip it though
|
||||
)
|
||||
var worldVelVec = localVelVec.mul(controller.orientation).toVec3d().yRot(vehicle.yRot.toRadians()) // Unrotate preemptively as this vector gets rotate later down the line in MC logic.
|
||||
worldVelVec = worldVelVec.add(0.0, extraDownwardForce, 0.0) // Add the stamina depletion force to bring the ride down.
|
||||
|
||||
val velocity = Vec3(0.0, upForce, forwardForce)
|
||||
|
||||
return velocity
|
||||
return worldVelVec
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -143,13 +192,36 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
) {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
|
||||
val minSpeed = vehicle.runtime.resolveDouble(settings.minSpeed)
|
||||
val speed = state.rideVelocity.get().length()
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalJet.speedExpr!!) / 20.0
|
||||
val accel = topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalJet.accelerationExpr!!) * 20.0)
|
||||
val deccel = vehicle.runtime.resolveDouble(settings.deccelRate ?: globalJet.deccelRate!!)//0.005
|
||||
val minSpeed = topSpeed * vehicle.runtime.resolveDouble(settings.minSpeedFactor ?: globalJet.minSpeedFactor!!)
|
||||
val speed = state.rideVelocity.get().z
|
||||
val boostMult = vehicle.runtime.resolveDouble(settings.jumpExpr ?: globalJet.jumpExpr!!)
|
||||
|
||||
val boostTopSpeed = topSpeed * boostMult
|
||||
val boostAccel = accel * boostMult
|
||||
|
||||
//speed up and slow down based on input
|
||||
if (driver.zza > 0.0 && speed < topSpeed && state.stamina.get() > 0.0f) {
|
||||
if (state.stamina.get() == 0.0f) {
|
||||
// Decelerate currently always a constant half of max acceleration.
|
||||
state.rideVelocity.set(
|
||||
Vec3(
|
||||
state.rideVelocity.get().x,
|
||||
state.rideVelocity.get().y,
|
||||
max(state.rideVelocity.get().z - (deccel * 1.3), 0.3 * 0.5)
|
||||
)
|
||||
)
|
||||
}
|
||||
else if (state.boosting.get() && speed < boostTopSpeed) {
|
||||
state.rideVelocity.set(
|
||||
Vec3(
|
||||
state.rideVelocity.get().x,
|
||||
state.rideVelocity.get().y,
|
||||
min(state.rideVelocity.get().z + (boostAccel), boostTopSpeed)
|
||||
)
|
||||
)
|
||||
} else if ((speed < minSpeed) || (driver.zza > 0.0 && speed < topSpeed)) {
|
||||
//modify acceleration to be slower when at closer speeds to top speed
|
||||
val accelMod = max(-(RidingBehaviour.scaleToRange(speed, minSpeed, topSpeed)) + 1, 0.0)
|
||||
state.rideVelocity.set(
|
||||
@@ -159,27 +231,31 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
min(state.rideVelocity.get().z + (accel * accelMod), topSpeed)
|
||||
)
|
||||
)
|
||||
} else if (driver.zza >= 0.0 && (state.stamina.get() == 0.0f)) {
|
||||
} else if (driver.zza < 0.0 && speed > minSpeed) {
|
||||
// Decelerate currently always a constant half of max acceleration.
|
||||
state.rideVelocity.set(
|
||||
Vec3(
|
||||
state.rideVelocity.get().x,
|
||||
state.rideVelocity.get().y,
|
||||
max(state.rideVelocity.get().z - ((accel) / 4), minSpeed)
|
||||
max(state.rideVelocity.get().z - (deccel), minSpeed)
|
||||
)
|
||||
)
|
||||
} else if (driver.zza < 0.0 && speed > minSpeed) {
|
||||
//modify deccel to be slower when at closer speeds to minimum speed
|
||||
val deccelMod = max((RidingBehaviour.scaleToRange(speed, minSpeed, topSpeed) - 1).pow(2) * 4, 0.1)
|
||||
|
||||
//Decelerate currently always a constant half of max acceleration.
|
||||
} else if (speed > topSpeed) {
|
||||
state.rideVelocity.set(
|
||||
Vec3(
|
||||
state.rideVelocity.get().x,
|
||||
state.rideVelocity.get().y,
|
||||
max(state.rideVelocity.get().z - ((accel * deccelMod) / 2), minSpeed)
|
||||
)
|
||||
state.rideVelocity.get().scale(0.98)
|
||||
)
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Kill strafing velocities carried over from controller transition. Don't
|
||||
* kill immediately to give some momentum to transitions.
|
||||
***************************************************************************/
|
||||
state.rideVelocity.set(Vec3(
|
||||
lerp(state.rideVelocity.get().x, 0.0, 0.03),
|
||||
lerp(state.rideVelocity.get().y, 0.0, 0.03),
|
||||
state.rideVelocity.get().z
|
||||
))
|
||||
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
@@ -196,7 +272,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
val cappedDeltaTime = min(deltaTime, 0.2)
|
||||
|
||||
//Get handling in degrees per second
|
||||
val yawRotRate = vehicle.runtime.resolveDouble(settings.handlingYawExpr)
|
||||
val yawRotRate = vehicle.runtime.resolveDouble(settings.handlingYawExpr ?: globalJet.handlingYawExpr!!)
|
||||
|
||||
//Base the change off of deltatime.
|
||||
var handlingYaw = yawRotRate * (cappedDeltaTime)
|
||||
@@ -276,11 +352,10 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
state.currMouseYForce.set((state.currMouseYForce.get() + (0.0015 * mouseY)).coerceIn(-1.0, 1.0))
|
||||
|
||||
//Get handling in degrees per second
|
||||
var handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
|
||||
val handlingDebuff = if(state.stamina.get() == 0.0f) 0.5 else 1.0
|
||||
var handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalJet.handlingExpr!!) * handlingDebuff
|
||||
//convert it to delta time
|
||||
handling *= (cappedDeltaTime)
|
||||
//apply stamina debuff if applicable
|
||||
handling *= if (state.stamina.get() > 0.0) 1.0 else 0.5
|
||||
|
||||
var pitchRot = handling * state.currMouseYForce.get()
|
||||
|
||||
@@ -331,13 +406,12 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
state.currMouseYForce.set((state.currMouseYForce.get() + (0.0015 * mouseY)).coerceIn(-1.0, 1.0))
|
||||
|
||||
//Get handling in degrees per second
|
||||
var handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
|
||||
val handlingDebuff = if(state.stamina.get() == 0.0f) 0.5 else 1.0
|
||||
var handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalJet.handlingExpr!!) * handlingDebuff
|
||||
|
||||
//convert it to delta time
|
||||
handling *= (cappedDeltaTime)
|
||||
|
||||
//apply stamina debuff if applicable
|
||||
handling *= if (state.stamina.get() > 0.0) 1.0 else 0.5
|
||||
|
||||
val poke = driver.vehicle as? PokemonEntity
|
||||
|
||||
@@ -409,15 +483,12 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val minSpeed = vehicle.runtime.resolveDouble(settings.minSpeed)
|
||||
|
||||
//Must I ensure that topspeed is greater than minimum?
|
||||
val normalizedSpeed = RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), minSpeed, topSpeed)
|
||||
|
||||
//TODO: Determine if this should be based on max possible speed instead of top speed.
|
||||
//Only ever want the fov change to be a max of 0.2 and for it to have non linear scaling.
|
||||
return 1.0f + normalizedSpeed.pow(2).toFloat() * 0.2f
|
||||
val maxDownwardForceTickCnt = 20.0f
|
||||
return when {
|
||||
state.noStamTickCnt.get() != 0 -> 1f - 0.2f*(state.noStamTickCnt.get() / maxDownwardForceTickCnt).coerceIn(0.0f,1.0f)
|
||||
state.boosting.get() -> 1.2f
|
||||
else -> 1.0f
|
||||
}
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(settings: JetSettings, state: JetState, vehicle: PokemonEntity): Boolean {
|
||||
@@ -490,60 +561,70 @@ class JetSettings : RidingBehaviourSettings {
|
||||
override val key = JetBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var gravity: Expression = "0".asExpression()
|
||||
var gravity: Expression? = null
|
||||
private set
|
||||
|
||||
var minSpeed: Expression = "1.2".asExpression()
|
||||
var deccelRate: Expression? = null
|
||||
private set
|
||||
|
||||
var handlingYawExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 50.0, 25.0)".asExpression()
|
||||
// Mult to top speed in order to derive minSpeed
|
||||
var minSpeedFactor: Expression? = null
|
||||
private set
|
||||
|
||||
var handlingYawExpr: Expression? = null
|
||||
private set
|
||||
|
||||
// Make configurable by json
|
||||
var infiniteStamina: Expression = "false".asExpression()
|
||||
var infiniteStamina: Expression? = null
|
||||
private set
|
||||
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 300.0, 128.0)".asExpression()
|
||||
// Boost power. Mult for top speed and accel while boosting
|
||||
var jumpExpr: Expression? = null
|
||||
private set
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 140.0, 20.0)".asExpression()
|
||||
|
||||
// Turn rate in degrees per second
|
||||
var handlingExpr: Expression? = null
|
||||
private set
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 1.8, 1.0)".asExpression()
|
||||
// Top Speed in blocks per second
|
||||
var speedExpr: Expression? = null
|
||||
private set
|
||||
var accelerationExpr: Expression =
|
||||
"q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 1.0)), (1.0 / (20.0 * 5.0)))".asExpression()
|
||||
// Seconds to get to top speed
|
||||
var accelerationExpr: Expression? = null
|
||||
private set
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 60.0, 10.0)".asExpression()
|
||||
// Time in seconds to drain full bar of stamina flying
|
||||
var staminaExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(gravity)
|
||||
buffer.writeExpression(minSpeed)
|
||||
buffer.writeExpression(handlingYawExpr)
|
||||
buffer.writeExpression(infiniteStamina)
|
||||
buffer.writeExpression(jumpExpr)
|
||||
buffer.writeExpression(handlingExpr)
|
||||
buffer.writeExpression(speedExpr)
|
||||
buffer.writeExpression(accelerationExpr)
|
||||
buffer.writeExpression(staminaExpr)
|
||||
buffer.writeNullableExpression(gravity)
|
||||
buffer.writeNullableExpression(deccelRate)
|
||||
buffer.writeNullableExpression(minSpeedFactor)
|
||||
buffer.writeNullableExpression(handlingYawExpr)
|
||||
buffer.writeNullableExpression(infiniteStamina)
|
||||
buffer.writeNullableExpression(jumpExpr)
|
||||
buffer.writeNullableExpression(handlingExpr)
|
||||
buffer.writeNullableExpression(speedExpr)
|
||||
buffer.writeNullableExpression(accelerationExpr)
|
||||
buffer.writeNullableExpression(staminaExpr)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
gravity = buffer.readExpression()
|
||||
minSpeed = buffer.readExpression()
|
||||
handlingYawExpr = buffer.readExpression()
|
||||
infiniteStamina = buffer.readExpression()
|
||||
jumpExpr = buffer.readExpression()
|
||||
handlingExpr = buffer.readExpression()
|
||||
speedExpr = buffer.readExpression()
|
||||
accelerationExpr = buffer.readExpression()
|
||||
staminaExpr = buffer.readExpression()
|
||||
gravity = buffer.readNullableExpression()
|
||||
deccelRate = buffer.readNullableExpression()
|
||||
minSpeedFactor = buffer.readNullableExpression()
|
||||
handlingYawExpr = buffer.readNullableExpression()
|
||||
infiniteStamina = buffer.readNullableExpression()
|
||||
jumpExpr = buffer.readNullableExpression()
|
||||
handlingExpr = buffer.readNullableExpression()
|
||||
speedExpr = buffer.readNullableExpression()
|
||||
accelerationExpr = buffer.readNullableExpression()
|
||||
staminaExpr = buffer.readNullableExpression()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -551,16 +632,26 @@ class JetState : RidingBehaviourState() {
|
||||
var currSpeed = ridingState(0.0, Side.CLIENT)
|
||||
var currMouseXForce = ridingState(0.0, Side.CLIENT)
|
||||
var currMouseYForce = ridingState(0.0, Side.CLIENT)
|
||||
var boosting = ridingState(false, Side.BOTH)
|
||||
var boostIsToggleable = ridingState(false, Side.BOTH)
|
||||
var canSpeedBurst = ridingState(false, Side.BOTH)
|
||||
var noStamTickCnt = ridingState(0, Side.CLIENT) // Value that is increased for every tick you are out of stamina
|
||||
|
||||
override fun encode(buffer: FriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
buffer.writeDouble(currSpeed.get())
|
||||
buffer.writeFloat(stamina.get())
|
||||
buffer.writeBoolean(boosting.get())
|
||||
buffer.writeBoolean(boostIsToggleable.get())
|
||||
buffer.writeBoolean(canSpeedBurst.get())
|
||||
}
|
||||
|
||||
override fun decode(buffer: FriendlyByteBuf) {
|
||||
super.decode(buffer)
|
||||
currSpeed.set(buffer.readDouble(), forced = true)
|
||||
boosting.set(buffer.readBoolean(), forced = true)
|
||||
boostIsToggleable.set(buffer.readBoolean(), forced = true)
|
||||
canSpeedBurst.set(buffer.readBoolean(), forced = true)
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
@@ -568,12 +659,19 @@ class JetState : RidingBehaviourState() {
|
||||
currSpeed.set(0.0, forced = true)
|
||||
currMouseXForce.set(0.0, forced = true)
|
||||
currMouseYForce.set(0.0, forced = true)
|
||||
boosting.set(false, forced = true)
|
||||
boostIsToggleable.set(false, forced = true)
|
||||
canSpeedBurst.set(true, forced = true)
|
||||
noStamTickCnt.set(0, forced = true)
|
||||
}
|
||||
|
||||
override fun copy() = JetState().also {
|
||||
it.currSpeed.set(currSpeed.get(), forced = true)
|
||||
it.stamina.set(stamina.get(), forced = true)
|
||||
it.rideVelocity.set(rideVelocity.get(), forced = true)
|
||||
it.boosting.set(this.boosting.get(), forced = true)
|
||||
it.boostIsToggleable.set(this.boosting.get(), forced = true)
|
||||
it.canSpeedBurst.set(this.canSpeedBurst.get(), forced = true)
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
|
||||
+136
-66
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.air
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.MoLangMath.lerp
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
@@ -40,6 +41,8 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
val globalRocket: RocketSettings
|
||||
get() = CobblemonRideSettings.rocket
|
||||
|
||||
override fun getRidingStyle(settings: RocketSettings, state: RocketState): RidingStyle {
|
||||
return RidingStyle.AIR
|
||||
@@ -84,30 +87,59 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
|
||||
driver: Player
|
||||
): Float {
|
||||
// Use this as a "tick" function and check to see if the driver is "boosting"
|
||||
if(vehicle.level().isClientSide) {
|
||||
if (vehicle.level().isClientSide) {
|
||||
handleBoosting(settings, state, vehicle, driver)
|
||||
tickStamina(settings, state, vehicle, driver)
|
||||
}
|
||||
|
||||
//If the forward key is not held then it cannot be boosting
|
||||
if(Minecraft.getInstance().options.keyUp.isDown()) {
|
||||
val boostKeyPressed = Minecraft.getInstance().options.keySprint.isDown()
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
|
||||
fun handleBoosting(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
) {
|
||||
//If the forward key is not held then it cannot be boosting
|
||||
if(Minecraft.getInstance().options.keyUp.isDown() && state.stamina.get() != 0.0f) {
|
||||
val boostKeyPressed = Minecraft.getInstance().options.keySprint.isDown()
|
||||
//If on the previous tick the boost key was held then don't change if the ride is boosting
|
||||
if(state.boostIsToggleable.get() && boostKeyPressed) {
|
||||
//flip the boosting state if boost key is pressed
|
||||
state.boosting.set(!state.boosting.get())
|
||||
}
|
||||
|
||||
//If the boost key is not held then next tick boosting is toggleable
|
||||
state.boostIsToggleable.set(!boostKeyPressed)
|
||||
} else {
|
||||
//Turn off boost and reset boost params
|
||||
state.boostIsToggleable.set(true)
|
||||
state.boosting.set(false)
|
||||
state.canSpeedBurst.set(true)
|
||||
}
|
||||
} else {
|
||||
//Turn off boost and reset boost params
|
||||
state.boostIsToggleable.set(true)
|
||||
state.boosting.set(false)
|
||||
state.canSpeedBurst.set(true)
|
||||
}
|
||||
}
|
||||
|
||||
fun tickStamina(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
) {
|
||||
val stam = state.stamina.get()
|
||||
|
||||
if (vehicle.runtime.resolveBoolean(settings.infiniteStamina ?: globalRocket.infiniteStamina!!)) {
|
||||
return
|
||||
}
|
||||
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
// Grab the boost time in seconds and convert to ticks. Then calculate the drain rate as inversely
|
||||
// proportional to the number of ticks of boost thus making a full boost take x ticks
|
||||
// in short: "Stamina drains slower at higher values and also replenishes slower"
|
||||
val boostTime = vehicle.runtime.resolveDouble(settings.staminaExpr ?: globalRocket.staminaExpr!!) * 20.0f
|
||||
val stamDrainRate = (1.0f / boostTime).toFloat()
|
||||
|
||||
val newStam = if(state.boosting.get() || driver.jumping) max(0.0f,stam - stamDrainRate) else stam
|
||||
|
||||
state.stamina.set(newStam)
|
||||
}
|
||||
|
||||
override fun updatePassengerRotation(
|
||||
@@ -153,22 +185,23 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
|
||||
): Vec2 {
|
||||
|
||||
var newMomentum = state.turnMomentum.get().toDouble()
|
||||
|
||||
// Grab turningAcceleration and divide 20 twice to get
|
||||
val turningAcceleration = (vehicle.runtime.resolveDouble(settings.handlingExpr) / 20.0f) / 20.0f * 2
|
||||
// Degrees per tick
|
||||
val maxTurnMomentum = vehicle.runtime.resolveDouble(settings.maxTurnRate ?: globalRocket.maxTurnRate!!) / 20.0f
|
||||
// Turn rate increase per tick. Based off number of seconds to get to max turn rate
|
||||
val turningAcceleration = maxTurnMomentum / (vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalRocket.handlingExpr!!) * 20.0f)
|
||||
val turnInput = (driver.xxa *-1.0f) * turningAcceleration
|
||||
|
||||
val maxTurnMomentum = 80.0f / 20.0f
|
||||
|
||||
// Base boost stats off of normal turning stats
|
||||
val boostMaxTurnMomentum = maxTurnMomentum * 0.1f
|
||||
val boostTurnInput = turnInput * 0.15f
|
||||
val boostHandlingMod = vehicle.runtime.resolveDouble(settings.boostHandlingMod ?: globalRocket.boostHandlingMod!!)
|
||||
val boostMaxTurnMomentum = maxTurnMomentum * boostHandlingMod
|
||||
val boostTurnInput = turnInput * boostHandlingMod
|
||||
|
||||
if(state.boosting.get()) {
|
||||
if(driver.xxa != 0.0f && abs(newMomentum + turnInput) < (boostMaxTurnMomentum)) { //If max momentum will not be exceeded then modulate
|
||||
newMomentum += boostTurnInput
|
||||
} else {
|
||||
newMomentum = lerp(newMomentum, 0.0, 0.05)
|
||||
newMomentum = newMomentum.coerceIn(-boostMaxTurnMomentum, boostMaxTurnMomentum)
|
||||
}
|
||||
} else {
|
||||
if(driver.xxa == 0.0f) { //If no turning input then lerp to 0
|
||||
@@ -204,8 +237,9 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Vec3 {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) * 2
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr) * 0.5 * 2
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalRocket.speedExpr!!)
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalRocket.accelerationExpr!!)
|
||||
val jump = vehicle.runtime.resolveDouble(settings.jumpExpr ?: globalRocket.jumpExpr!!)
|
||||
|
||||
var newVelocity = vehicle.deltaMovement
|
||||
|
||||
@@ -213,8 +247,10 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
|
||||
val yawAligned = Matrix3f().rotateY(vehicle.yRot.toRadians())
|
||||
newVelocity = (newVelocity.toVector3f().mul(yawAligned)).toVec3d()
|
||||
|
||||
//speed up and slow down based on input
|
||||
if (driver.zza != 0.0f && state.stamina.get() > 0.0 && !state.boosting.get()) {
|
||||
/******************************************************
|
||||
* Speed up and slow down based on input
|
||||
*****************************************************/
|
||||
if (driver.zza != 0.0f && !state.boosting.get()) {
|
||||
//make sure it can't exceed top speed
|
||||
val forwardInput = when {
|
||||
vehicle.deltaMovement.horizontalDistance() > topSpeed && (driver.zza.sign == newVelocity.z.sign.toFloat()) -> 0.0
|
||||
@@ -226,14 +262,16 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
|
||||
newVelocity.y,
|
||||
(newVelocity.z + (accel * forwardInput.toDouble())))
|
||||
|
||||
} else if ((state.stamina.get() > 0.0 && state.boosting.get())) {
|
||||
val boostSpeed = topSpeed * 5
|
||||
val boostAccel = if(newVelocity.length() < boostSpeed) accel * 3 else 0.0
|
||||
} else if ((state.boosting.get())) {
|
||||
val boostMod = vehicle.runtime.resolveDouble(settings.boostSpeedMod ?: globalRocket.boostSpeedMod!!)
|
||||
val boostSpeed = topSpeed * boostMod
|
||||
val boostAccel = if(newVelocity.length() < boostSpeed) accel * (boostMod/1.5) else 0.0
|
||||
val forwardInput = 1.0f
|
||||
var burst = 0.0f
|
||||
|
||||
if(state.canSpeedBurst.get()) {
|
||||
burst = 0.25f
|
||||
burst = 0.5f
|
||||
state.stamina.set(max(0.0f,state.stamina.get() - 0.05f))
|
||||
state.canSpeedBurst.set(false)
|
||||
}
|
||||
|
||||
@@ -244,12 +282,13 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
|
||||
}
|
||||
|
||||
//Vertical movement based on driver input.
|
||||
val maxVertSpeed = 0.5f
|
||||
val maxVertSpeed = jump
|
||||
val boostMaxVertSpeed = 0.5f * 0.25f
|
||||
var vertInput = 0.0
|
||||
|
||||
//If boosting then don't lose altitude
|
||||
//otherwise propel up or fall
|
||||
if (state.boosting.get() && !driver.jumping && vehicle.deltaMovement.y > (-maxVertSpeed * 0.25)) {
|
||||
if (state.boosting.get() && !driver.jumping && vehicle.deltaMovement.y > (-boostMaxVertSpeed)) {
|
||||
vertInput = -0.7
|
||||
newVelocity = Vec3(
|
||||
newVelocity.x,
|
||||
@@ -257,28 +296,33 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
|
||||
newVelocity.z)
|
||||
} else if(state.boosting.get() && vehicle.deltaMovement.y < maxVertSpeed) {
|
||||
vertInput = when {
|
||||
driver.jumping -> 2.0
|
||||
driver.jumping -> 1.0
|
||||
else -> 0.0
|
||||
}
|
||||
newVelocity = Vec3(
|
||||
newVelocity.x,
|
||||
(newVelocity.y + (accel * 0.5 * vertInput)),
|
||||
(newVelocity.y + (accel * vertInput)),
|
||||
newVelocity.z)
|
||||
} else {
|
||||
if (driver.jumping && !(vehicle.deltaMovement.y > maxVertSpeed && (newVelocity.y.sign.toFloat() > 0.0))) {
|
||||
vertInput = 1.0
|
||||
|
||||
// More force if traveling downwards to allow for quicker fall stops
|
||||
vertInput = (if (state.stamina.get() == 0.0f) -1.0f else if(newVelocity.y < 0) jump.toFloat() * 3.0f else jump.toFloat()).toDouble()
|
||||
// Reset falldistance if upward motion is detected
|
||||
vehicle.resetFallDistance()
|
||||
} else {
|
||||
vertInput = -2.5
|
||||
}
|
||||
|
||||
if (state.stamina.get() > 0.0) {
|
||||
newVelocity = Vec3(
|
||||
newVelocity.x,
|
||||
(newVelocity.y + (accel * vertInput)),
|
||||
newVelocity.z)
|
||||
} else {
|
||||
/******************************************************
|
||||
* Gravity logic
|
||||
*****************************************************/
|
||||
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.6
|
||||
val terminalVel = 2.0
|
||||
|
||||
val fallingForce = gravity
|
||||
newVelocity = Vec3(newVelocity.x, max(newVelocity.y - fallingForce, -terminalVel), newVelocity.z)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,11 +405,9 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
|
||||
driver: Player
|
||||
): Float {
|
||||
if (state.boosting.get()) {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
//val exceededSpeed = max(vehicle.deltaMovement.length() - topSpeed, 0.0)
|
||||
|
||||
//TODO: Remove this magic number and get a better comparison for boost top speed
|
||||
val normalizedBoostSpeed = RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), topSpeed, topSpeed * 3)
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalRocket.speedExpr!!)
|
||||
val boostMod = vehicle.runtime.resolveDouble(settings.boostSpeedMod ?: globalRocket.boostSpeedMod!!)
|
||||
val normalizedBoostSpeed = RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), topSpeed, topSpeed * boostMod)
|
||||
return 1.0f + normalizedBoostSpeed.pow(2).toFloat() * 0.2f
|
||||
} else {
|
||||
return 1.0f
|
||||
@@ -437,6 +479,18 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
override fun damageOnCollision(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity,
|
||||
impactVec: Vec3
|
||||
): Boolean {
|
||||
if (!state.boosting.get()) return false
|
||||
val impactSpeed = impactVec.horizontalDistance().toFloat() * 10f
|
||||
return vehicle.causeFallDamage(impactSpeed, 1f, vehicle.damageSources().flyIntoWall())
|
||||
}
|
||||
|
||||
override fun getRideSounds(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
@@ -452,51 +506,67 @@ class RocketSettings : RidingBehaviourSettings {
|
||||
override val key = RocketBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 0.65, 0.3) * 0.25".asExpression()
|
||||
var infiniteStamina: Expression? = null
|
||||
private set
|
||||
// Boost multiplier for speed
|
||||
var boostSpeedMod: Expression? = null
|
||||
private set
|
||||
|
||||
// Boost multiplier for handling
|
||||
var boostHandlingMod: Expression? = null
|
||||
private set
|
||||
|
||||
// Max turn rate in degrees per second
|
||||
var maxTurnRate: Expression? = null
|
||||
private set
|
||||
|
||||
var speedExpr: Expression? = null
|
||||
private set
|
||||
|
||||
// Max accel is a whole 1.0 in 1 second. The conversion in the function below is to convert seconds to ticks
|
||||
var accelerationExpr: Expression =
|
||||
"q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 1.5)), (1.0 / (20.0 * 3.0))) * 0.5".asExpression()
|
||||
var accelerationExpr: Expression? = null
|
||||
private set
|
||||
|
||||
// Between 30 seconds and 10 seconds at the lowest when at full speed.
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 30.0, 10.0)".asExpression()
|
||||
// air time in seconds
|
||||
var staminaExpr: Expression? = null
|
||||
private set
|
||||
|
||||
//Between a one block jump and a ten block jump
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 10.0, 1.0)".asExpression()
|
||||
var jumpExpr: Expression? = null
|
||||
private set
|
||||
|
||||
// Controls the acceleration of the turning speed since the max turning speed is locked at 60 degrees/second
|
||||
// So this is a range of 60 degrees/second per second to 20. Meaning at max stats when holding a direction it
|
||||
// takes a full second to be turning at max speed.
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 60.0, 20.0)".asExpression()
|
||||
// How long it takes to get to max turn rate from rest (in seconds)
|
||||
var handlingExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(speedExpr)
|
||||
buffer.writeExpression(accelerationExpr)
|
||||
buffer.writeExpression(staminaExpr)
|
||||
buffer.writeExpression(jumpExpr)
|
||||
buffer.writeExpression(handlingExpr)
|
||||
buffer.writeNullableExpression(infiniteStamina)
|
||||
buffer.writeNullableExpression(boostSpeedMod)
|
||||
buffer.writeNullableExpression(boostHandlingMod)
|
||||
buffer.writeNullableExpression(maxTurnRate)
|
||||
buffer.writeNullableExpression(speedExpr)
|
||||
buffer.writeNullableExpression(accelerationExpr)
|
||||
buffer.writeNullableExpression(staminaExpr)
|
||||
buffer.writeNullableExpression(jumpExpr)
|
||||
buffer.writeNullableExpression(handlingExpr)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
speedExpr = buffer.readExpression()
|
||||
accelerationExpr = buffer.readExpression()
|
||||
staminaExpr = buffer.readExpression()
|
||||
jumpExpr = buffer.readExpression()
|
||||
handlingExpr = buffer.readExpression()
|
||||
infiniteStamina = buffer.readNullableExpression()
|
||||
boostSpeedMod = buffer.readNullableExpression()
|
||||
boostHandlingMod = buffer.readNullableExpression()
|
||||
maxTurnRate = buffer.readNullableExpression()
|
||||
speedExpr = buffer.readNullableExpression()
|
||||
accelerationExpr = buffer.readNullableExpression()
|
||||
staminaExpr = buffer.readNullableExpression()
|
||||
jumpExpr = buffer.readNullableExpression()
|
||||
handlingExpr = buffer.readNullableExpression()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class RocketState : RidingBehaviourState() {
|
||||
|
||||
+2
-1
@@ -387,9 +387,10 @@ open class CompositeSettings : RidingBehaviourSettings {
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeRidingStats(stats)
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeResourceLocation(transitionStrategy)
|
||||
buffer.writeResourceLocation(defaultBehaviour.key)
|
||||
defaultBehaviour.encode(buffer)
|
||||
buffer.writeResourceLocation(alternateBehaviour.key)
|
||||
alternateBehaviour.encode(buffer)
|
||||
}
|
||||
|
||||
|
||||
+4
@@ -41,6 +41,10 @@ interface CompositeRidingStrategy<T : CompositeSettings> {
|
||||
toState: RidingBehaviourState,
|
||||
toSettings: RidingBehaviourSettings
|
||||
) {
|
||||
if (vehicle.level().isClientSide) {
|
||||
val fromSettings = if (settings.defaultBehaviour == toSettings) settings.alternateBehaviour else settings.defaultBehaviour
|
||||
|
||||
}
|
||||
toState.stamina.set(fromState.stamina.get())
|
||||
toState.rideVelocity.set(fromState.rideVelocity.get())
|
||||
fromState.reset()
|
||||
|
||||
+130
-56
@@ -9,15 +9,34 @@
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types.land
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.Side
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.ridingState
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.readNullableExpression
|
||||
import com.cobblemon.mod.common.util.readRidingStats
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
import com.cobblemon.mod.common.util.resolveDouble
|
||||
import com.cobblemon.mod.common.util.resolveFloat
|
||||
import com.cobblemon.mod.common.util.writeNullableExpression
|
||||
import com.cobblemon.mod.common.util.writeRidingStats
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sign
|
||||
import kotlin.math.sqrt
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
@@ -30,7 +49,6 @@ import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import kotlin.math.*
|
||||
|
||||
class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
companion object {
|
||||
@@ -38,6 +56,8 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
val globalHorse: HorseSettings
|
||||
get() = CobblemonRideSettings.horse
|
||||
|
||||
override fun getRidingStyle(settings: HorseSettings, state: HorseState): RidingStyle {
|
||||
return RidingStyle.LAND
|
||||
@@ -81,13 +101,21 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
// Use this as a "tick" function and calculate sprinting and inAir state here
|
||||
handleSprinting(settings, state, vehicle, driver)
|
||||
inAirCheck(state, vehicle)
|
||||
tickStamina(settings, state, vehicle, driver)
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
|
||||
override fun tick(
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
) {
|
||||
handleSprinting(state, driver)
|
||||
inAirCheck(state, vehicle)
|
||||
tickStamina(settings, state, vehicle)
|
||||
}
|
||||
|
||||
fun inAirCheck(
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
@@ -100,16 +128,13 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
val canSupportEntity = blockStateBelow.isFaceSturdy(vehicle.level(), posBelow, Direction.UP)
|
||||
val standingOnSolid = canSupportEntity && !isAirOrLiquid
|
||||
|
||||
|
||||
// inAir if not on the ground
|
||||
val inAir = !(vehicle.deltaMovement.y == 0.0 || standingOnSolid)
|
||||
state.inAir.set(inAir)
|
||||
}
|
||||
|
||||
fun handleSprinting(
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
) {
|
||||
if (state.sprinting.get()) {
|
||||
@@ -125,11 +150,15 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
) {
|
||||
val stam = state.stamina.get()
|
||||
|
||||
if (vehicle.runtime.resolveBoolean(settings.infiniteStamina ?: globalHorse.infiniteStamina!!)) {
|
||||
return
|
||||
}
|
||||
|
||||
var newStam = stam
|
||||
val stamDrainRate = (1.0f / vehicle.runtime.resolveDouble(settings.staminaExpr)).toFloat() / 20.0f
|
||||
val stamDrainRate = (1.0f / vehicle.runtime.resolveDouble(settings.staminaExpr ?: globalHorse.staminaExpr!!)).toFloat() / 20.0f
|
||||
|
||||
if (state.sprinting.get()) {
|
||||
newStam = max(0.0f,stam - stamDrainRate)
|
||||
@@ -179,10 +208,10 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Float {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalHorse.speedExpr!!)
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalHorse.handlingExpr!!)
|
||||
val walkHandlingBoost = 5
|
||||
val maxYawDiff = vehicle.runtime.resolveFloat(settings.lookYawLimit)
|
||||
val maxYawDiff = vehicle.runtime.resolveFloat(settings.lookYawLimit ?: globalHorse.lookYawLimit!!)
|
||||
|
||||
// Normalize the current rotation diff
|
||||
val rotDiff = Mth.wrapDegrees(driver.yRot - vehicle.yRot).coerceIn(-maxYawDiff,maxYawDiff)
|
||||
@@ -226,24 +255,37 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Vec3 {
|
||||
val canSprint = vehicle.runtime.resolveBoolean(settings.canSprint)
|
||||
val canJump = vehicle.runtime.resolveBoolean(settings.canJump)
|
||||
val jumpForce = vehicle.runtime.resolveDouble(settings.jumpExpr)
|
||||
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val canSprint = vehicle.runtime.resolveBoolean(settings.canSprint ?: globalHorse.canSprint!!)
|
||||
val canJump = vehicle.runtime.resolveBoolean(settings.canJump ?: globalHorse.canJump!!)
|
||||
val jumpForce = vehicle.runtime.resolveDouble(settings.jumpExpr ?: globalHorse.jumpExpr!!) * 0.75
|
||||
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalHorse.speedExpr!!)
|
||||
val walkSpeed = getWalkSpeed(vehicle)
|
||||
val topSpeed = if(canSprint && state.sprinting.get()) rideTopSpeed else walkSpeed
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
|
||||
val accel = topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalHorse.accelerationExpr!!) * 20.0)
|
||||
var activeInput = false
|
||||
|
||||
/******************************************************
|
||||
* Gather the previous velocity and set the horizontal
|
||||
* fields of the velocity vector to the magnitude of the
|
||||
* ones from deltaMovement. This is important as when a
|
||||
* collision happens the Deltamovement will reflect it
|
||||
* but rideVelocity won't.
|
||||
* Gather the previous velocity and check for horizontal
|
||||
* collisions
|
||||
*****************************************************/
|
||||
var newVelocity = Vec3(state.rideVelocity.get().x, 0.0, state.rideVelocity.get().z).normalize().scale(vehicle.deltaMovement.horizontalDistance())
|
||||
newVelocity = newVelocity.add(0.0, state.rideVelocity.get().y, 0.0)
|
||||
val dmSpeed = vehicle.deltaMovement.length()
|
||||
val rvSpeed = state.rideVelocity.get().length()
|
||||
var newVelocity = state.rideVelocity.get() //.normalize().scale(vehicle.deltaMovement.horizontalDistance())
|
||||
|
||||
/******************************************************
|
||||
* Gather the previous velocity and check for horizontal
|
||||
* collisions
|
||||
*****************************************************/
|
||||
// if (dmSpeed > rvSpeed) {
|
||||
// // align the velocity vector to be in local vehicle space
|
||||
// newVelocity = vehicle.deltaMovement
|
||||
// val yawAligned = Matrix3f().rotateY(-vehicle.yRot.toRadians())
|
||||
// newVelocity = (newVelocity.toVector3f().mul(yawAligned)).toVec3d()
|
||||
// }
|
||||
|
||||
if (vehicle.horizontalCollision) {
|
||||
newVelocity = newVelocity.normalize().scale(vehicle.deltaMovement.length())
|
||||
}
|
||||
|
||||
/******************************************************
|
||||
* Speed up and slow down based on input
|
||||
@@ -252,7 +294,7 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
|
||||
// If on a tight turn then do not speed up past half of top speed in order to turn quicker
|
||||
// Also determine how fast to be slowing down based on how far turned you are
|
||||
val lookYawLimit = vehicle.runtime.resolveFloat(settings.lookYawLimit)
|
||||
val lookYawLimit = vehicle.runtime.resolveFloat(settings.lookYawLimit ?: globalHorse.lookYawLimit!!)
|
||||
val percOfMaxTurnSpeed = abs(Mth.wrapDegrees(driver.yRot - vehicle.yRot) / lookYawLimit) * 100.0f
|
||||
val turnPercThresh = 0.0f
|
||||
val s = min(((percOfMaxTurnSpeed - turnPercThresh) / (100.0f - turnPercThresh)).pow(1),1.0f)
|
||||
@@ -282,12 +324,14 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
/******************************************************
|
||||
* Gravity logic
|
||||
*****************************************************/
|
||||
if (vehicle.onGround()) { newVelocity = Vec3(newVelocity.x, 0.0, newVelocity.z) }
|
||||
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.6
|
||||
val terminalVel = 2.0
|
||||
if (!vehicle.onGround() && state.jumpTicks.get() <= 0) {
|
||||
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.6
|
||||
val terminalVel = 2.0
|
||||
newVelocity = Vec3(newVelocity.x, max(newVelocity.y - gravity, -terminalVel), newVelocity.z)
|
||||
} else if(vehicle.onGround()) {
|
||||
newVelocity = Vec3(newVelocity.x, 0.0, newVelocity.z)
|
||||
}
|
||||
|
||||
val fallingForce = gravity
|
||||
newVelocity = Vec3(newVelocity.x, max(newVelocity.y - fallingForce, -terminalVel), newVelocity.z)
|
||||
|
||||
/******************************************************
|
||||
* Ground Friction
|
||||
@@ -299,8 +343,21 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
/******************************************************
|
||||
* Jump Logic
|
||||
*****************************************************/
|
||||
if (driver.jumping && vehicle.onGround() && canJump && driver.deltaMovement.y <= 0.1) {
|
||||
newVelocity = Vec3(newVelocity.x, jumpForce, newVelocity.z)
|
||||
if (state.jumpTicks.get() > 0 || (state.jumpTicks.get() >= 0 && driver.jumping && vehicle.onGround() && canJump && driver.deltaMovement.y <= 0.1)) {
|
||||
// Spread out jumpforce so that variable height jumps are possible
|
||||
val jumpInputTicks = 6
|
||||
if (driver.jumping && (state.jumpTicks.get() >= 0 && state.jumpTicks.get() < jumpInputTicks)) {
|
||||
val appliedJumpForce = ((jumpForce*1.5) / jumpInputTicks) //* (1 - state.jumpTicks.get() / jumpInputTicks)
|
||||
newVelocity = Vec3(newVelocity.x, newVelocity.y + appliedJumpForce, newVelocity.z)
|
||||
state.jumpTicks.set(state.jumpTicks.get() + 1)
|
||||
} else {
|
||||
// Set delay before next jump is possible after hitting ground
|
||||
val tickJumpDelay = 3
|
||||
state.jumpTicks.set(-tickJumpDelay)
|
||||
}
|
||||
} else if (vehicle.onGround() && state.jumpTicks.get() < 0) {
|
||||
// Tick off the delay once reaching the ground
|
||||
state.jumpTicks.set(state.jumpTicks.get() + 1)
|
||||
}
|
||||
|
||||
//Zero out lateral velocity possibly picked up from a controller transition
|
||||
@@ -479,78 +536,94 @@ class HorseSettings : RidingBehaviourSettings {
|
||||
override val key = HorseBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var canJump = "true".asExpression()
|
||||
var infiniteStamina: Expression? = null
|
||||
private set
|
||||
var canJump: Expression? = null
|
||||
private set
|
||||
|
||||
var canSprint = "true".asExpression()
|
||||
var canSprint: Expression? = null
|
||||
private set
|
||||
|
||||
var lookYawLimit = "90.0f".asExpression()
|
||||
var lookYawLimit: Expression? = null
|
||||
private set
|
||||
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'LAND', 1.0, 0.4)".asExpression()
|
||||
var speedExpr: Expression? = null
|
||||
private set
|
||||
|
||||
// Max accel is a whole 1.0 in 1 second. The conversion in the function below is to convert seconds to ticks
|
||||
var accelerationExpr: Expression =
|
||||
"q.get_ride_stats('ACCELERATION', 'LAND', (1.0 / (20.0 * 0.8)), (1.0 / (20.0 * 3.0)))".asExpression()
|
||||
var accelerationExpr: Expression? = null
|
||||
private set
|
||||
|
||||
// Between 30 seconds and 10 seconds at the lowest when at full speed.
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'LAND', 30.0, 10.0)".asExpression()
|
||||
var staminaExpr: Expression? = null
|
||||
private set
|
||||
|
||||
//Between a one block jump and a ten block jump
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'LAND', 1.5, 0.8)".asExpression()
|
||||
//Between a one block jump and a six block jump
|
||||
var jumpExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'LAND', 180.0, 40.0)".asExpression()
|
||||
var handlingExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(speedExpr)
|
||||
buffer.writeExpression(accelerationExpr)
|
||||
buffer.writeExpression(staminaExpr)
|
||||
buffer.writeExpression(jumpExpr)
|
||||
buffer.writeExpression(handlingExpr)
|
||||
buffer.writeNullableExpression(infiniteStamina)
|
||||
buffer.writeNullableExpression(canJump)
|
||||
buffer.writeNullableExpression(canSprint)
|
||||
buffer.writeNullableExpression(lookYawLimit)
|
||||
buffer.writeNullableExpression(speedExpr)
|
||||
buffer.writeNullableExpression(accelerationExpr)
|
||||
buffer.writeNullableExpression(staminaExpr)
|
||||
buffer.writeNullableExpression(jumpExpr)
|
||||
buffer.writeNullableExpression(handlingExpr)
|
||||
buffer.writeNullableExpression(canJump)
|
||||
buffer.writeNullableExpression(canSprint)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
speedExpr = buffer.readExpression()
|
||||
accelerationExpr = buffer.readExpression()
|
||||
staminaExpr = buffer.readExpression()
|
||||
jumpExpr = buffer.readExpression()
|
||||
handlingExpr = buffer.readExpression()
|
||||
infiniteStamina = buffer.readNullableExpression()
|
||||
canJump = buffer.readNullableExpression()
|
||||
canSprint = buffer.readNullableExpression()
|
||||
lookYawLimit = buffer.readNullableExpression()
|
||||
speedExpr = buffer.readNullableExpression()
|
||||
accelerationExpr = buffer.readNullableExpression()
|
||||
staminaExpr = buffer.readNullableExpression()
|
||||
jumpExpr = buffer.readNullableExpression()
|
||||
handlingExpr = buffer.readNullableExpression()
|
||||
canJump = buffer.readNullableExpression()
|
||||
canSprint = buffer.readNullableExpression()
|
||||
}
|
||||
}
|
||||
|
||||
class HorseState : RidingBehaviourState() {
|
||||
var sprinting = ridingState(false, Side.CLIENT)
|
||||
var inAir = ridingState(false, Side.CLIENT)
|
||||
var jumpTicks = ridingState(0, Side.CLIENT)
|
||||
|
||||
override fun encode(buffer: FriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
buffer.writeBoolean(sprinting.get())
|
||||
buffer.writeBoolean(inAir.get())
|
||||
buffer.writeInt(jumpTicks.get())
|
||||
}
|
||||
|
||||
override fun decode(buffer: FriendlyByteBuf) {
|
||||
super.decode(buffer)
|
||||
sprinting.set(buffer.readBoolean(), forced = true)
|
||||
inAir.set(buffer.readBoolean(), forced = true)
|
||||
jumpTicks.set(buffer.readInt(), forced = true)
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
super.reset()
|
||||
sprinting.set(false, forced = true)
|
||||
inAir.set(false, forced = true)
|
||||
jumpTicks.set(0, forced = true)
|
||||
}
|
||||
|
||||
override fun copy() = HorseState().also {
|
||||
@@ -558,6 +631,7 @@ class HorseState : RidingBehaviourState() {
|
||||
it.stamina.set(this.stamina.get(), forced = true)
|
||||
it.sprinting.set(this.sprinting.get(), forced = true)
|
||||
it.inAir.set(this.inAir.get(), forced = true)
|
||||
it.jumpTicks.set(this.jumpTicks.get(), forced = true)
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
|
||||
+54
-28
@@ -10,18 +10,36 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.land
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.MoLangMath.lerp
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.Side
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.SidedRidingState
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.ridingState
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import com.cobblemon.mod.common.util.readNullableExpression
|
||||
import com.cobblemon.mod.common.util.readRidingStats
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
import com.cobblemon.mod.common.util.resolveDouble
|
||||
import com.cobblemon.mod.common.util.resolveFloat
|
||||
import com.cobblemon.mod.common.util.writeNullableExpression
|
||||
import com.cobblemon.mod.common.util.writeRidingStats
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sign
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
@@ -32,12 +50,6 @@ import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import org.joml.Matrix3f
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sign
|
||||
|
||||
class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
|
||||
companion object {
|
||||
@@ -45,6 +57,8 @@ class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
val globalMinekart: MinekartSettings
|
||||
get() = CobblemonRideSettings.minekart
|
||||
|
||||
override fun getRidingStyle(settings: MinekartSettings, state: MinekartState): RidingStyle {
|
||||
return RidingStyle.LAND
|
||||
@@ -84,7 +98,7 @@ class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
|
||||
driver: Player
|
||||
): Float {
|
||||
|
||||
val boostMax = vehicle.runtime.resolveFloat(settings.boostLimit)
|
||||
val boostMax = vehicle.runtime.resolveFloat(settings.boostLimit ?: globalMinekart.boostLimit!!)
|
||||
//TODO: should this be by how much turn has accumulated or simply time turning... or both?
|
||||
val boostPerSecond = 20.0f
|
||||
val boostLoss = 40.0f
|
||||
@@ -142,8 +156,8 @@ class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
|
||||
var newMomentum = state.turnMomentum.get().toDouble()
|
||||
|
||||
//TODO: maybe tie this to max turnspeeds rather than the turn acceleration
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val turningAcceleration = (vehicle.runtime.resolveDouble(settings.handlingExpr) * 1.5 / 20.0f) / 20.0f * 4.0
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalMinekart.speedExpr!!)
|
||||
val turningAcceleration = (vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalMinekart.handlingExpr!!) * 1.5 / 20.0f) / 20.0f * 4.0
|
||||
val turnInput = (driver.xxa *-1.0f) * turningAcceleration
|
||||
val driftInput = turnInput * 0.25f
|
||||
|
||||
@@ -202,9 +216,9 @@ class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
|
||||
): Vec3 {
|
||||
|
||||
// Check to see if the ride should be walking or sprinting
|
||||
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) * 0.6 * 2.0
|
||||
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalMinekart.speedExpr!!) * 0.6 * 2.0
|
||||
val topSpeed = if (state.drifting.get()) rideTopSpeed * 0.7 else rideTopSpeed
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr) * 0.3 * 3 * 2.0
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalMinekart.accelerationExpr!!) * 0.3 * 3 * 2.0
|
||||
val driftAngle = 20.0f
|
||||
|
||||
//Align the direction of this vector with the vehicles current heading.
|
||||
@@ -327,7 +341,7 @@ class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return vehicle.runtime.resolveBoolean(settings.canJump)
|
||||
return vehicle.runtime.resolveBoolean(settings.canJump ?: globalMinekart.canJump!!)
|
||||
}
|
||||
|
||||
override fun setRideBar(
|
||||
@@ -447,45 +461,57 @@ class MinekartSettings : RidingBehaviourSettings {
|
||||
override val key = MinekartBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var canJump = "true".asExpression()
|
||||
var infiniteStamina: Expression? = null
|
||||
private set
|
||||
var canJump: Expression? = null
|
||||
private set
|
||||
|
||||
var boostLimit = "100.0f".asExpression()
|
||||
var boostLimit: Expression? = null
|
||||
private set
|
||||
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'LAND', 1.0, 0.3)".asExpression()
|
||||
var speedExpr: Expression? = null
|
||||
private set
|
||||
|
||||
// Max accel is a whole 1.0 in 1 second. The conversion in the function below is to convert seconds to ticks
|
||||
var accelerationExpr: Expression =
|
||||
"q.get_ride_stats('ACCELERATION', 'LAND', (1.0 / (20.0 * 1.5)), (1.0 / (20.0 * 5.0)))".asExpression()
|
||||
var accelerationExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'LAND', 30.0, 10.0)".asExpression()
|
||||
var staminaExpr: Expression? = null
|
||||
private set
|
||||
|
||||
//Between a one block jump and a ten block jump
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'LAND', 10.0, 1.0)".asExpression()
|
||||
var jumpExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'LAND', 140.0, 20.0)".asExpression()
|
||||
var handlingExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(canJump)
|
||||
buffer.writeExpression(boostLimit)
|
||||
buffer.writeNullableExpression(infiniteStamina)
|
||||
buffer.writeNullableExpression(canJump)
|
||||
buffer.writeNullableExpression(boostLimit)
|
||||
buffer.writeNullableExpression(speedExpr)
|
||||
buffer.writeNullableExpression(accelerationExpr)
|
||||
buffer.writeNullableExpression(staminaExpr)
|
||||
buffer.writeNullableExpression(jumpExpr)
|
||||
buffer.writeNullableExpression(handlingExpr)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
canJump = buffer.readExpression()
|
||||
boostLimit = buffer.readExpression()
|
||||
infiniteStamina = buffer.readNullableExpression()
|
||||
canJump = buffer.readNullableExpression()
|
||||
boostLimit = buffer.readNullableExpression()
|
||||
speedExpr = buffer.readNullableExpression()
|
||||
accelerationExpr = buffer.readNullableExpression()
|
||||
staminaExpr = buffer.readNullableExpression()
|
||||
jumpExpr = buffer.readNullableExpression()
|
||||
handlingExpr = buffer.readNullableExpression()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+66
-38
@@ -10,18 +10,35 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.land
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.MoLangMath.lerp
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.Side
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.SidedRidingState
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.ridingState
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import com.cobblemon.mod.common.util.readNullableExpression
|
||||
import com.cobblemon.mod.common.util.readRidingStats
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
import com.cobblemon.mod.common.util.resolveDouble
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import com.cobblemon.mod.common.util.writeNullableExpression
|
||||
import com.cobblemon.mod.common.util.writeRidingStats
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.sign
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
@@ -33,10 +50,6 @@ import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import org.joml.Matrix3f
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.sign
|
||||
|
||||
class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
companion object {
|
||||
@@ -44,6 +57,8 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
val globalVehicle: VehicleSettings
|
||||
get() = CobblemonRideSettings.vehicle
|
||||
|
||||
override fun getRidingStyle(settings: VehicleSettings, state: VehicleState): RidingStyle {
|
||||
return RidingStyle.LAND
|
||||
@@ -126,7 +141,7 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
var newMomentum = state.turnMomentum.get().toDouble()
|
||||
|
||||
// Grab turningAcceleration and divide 20 twice to get
|
||||
val turningAcceleration = (vehicle.runtime.resolveDouble(settings.handlingExpr) * 1.5 / 20.0f) / 20.0f * 4.0
|
||||
val turningAcceleration = (vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalVehicle.handlingExpr!!) * 1.5 / 20.0f) / 20.0f * 4.0
|
||||
val turnInput = (driver.xxa *-1.0f) * turningAcceleration
|
||||
|
||||
val maxTurnMomentum = 80.0f / 20.0f
|
||||
@@ -137,7 +152,7 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
|
||||
if(state.drifting.get() || state.inAir.get()) {
|
||||
if( newMomentum < driftMaxTurnMomentum) {
|
||||
driftTurnInput = driftTurnInput * (vehicle.deltaMovement.horizontalDistance() / vehicle.runtime.resolveDouble(settings.speedExpr))
|
||||
driftTurnInput = driftTurnInput * (vehicle.deltaMovement.horizontalDistance() / vehicle.runtime.resolveDouble(settings.speedExpr ?: globalVehicle.speedExpr!!))
|
||||
newMomentum += driftTurnInput
|
||||
}
|
||||
newMomentum = lerp(newMomentum, 0.0, 0.03)
|
||||
@@ -177,10 +192,10 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
|
||||
// Check to see if the ride should be walking or sprinting
|
||||
//val walkSpeed = getWalkSpeed(vehicle)
|
||||
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) * 0.6 * 2.0
|
||||
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalVehicle.speedExpr!!) * 0.6 * 2.0
|
||||
val topSpeed = if (state.drifting.get()) rideTopSpeed * 0.5 else rideTopSpeed
|
||||
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr) * 0.3 * 3 * 2.0
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalVehicle.accelerationExpr!!) * 0.3 * 3 * 2.0
|
||||
|
||||
//Flag for determining if player is actively inputting
|
||||
var activeInput = false
|
||||
@@ -250,7 +265,7 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
}
|
||||
|
||||
//TODO: Change this so its tied to a jumping stat and representative of the amount of jumps
|
||||
val canJump = vehicle.runtime.resolveBoolean(settings.canJump)
|
||||
val canJump = vehicle.runtime.resolveBoolean(settings.canJump ?: globalVehicle.canJump!!)
|
||||
//Jump the thang!
|
||||
if (driver.jumping && vehicle.onGround() && canJump) {
|
||||
val jumpForce = 0.5
|
||||
@@ -296,7 +311,7 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return vehicle.runtime.resolveBoolean(settings.canJump)
|
||||
return vehicle.runtime.resolveBoolean(settings.canJump ?: globalVehicle.canJump!!)
|
||||
}
|
||||
|
||||
override fun setRideBar(
|
||||
@@ -412,66 +427,79 @@ class VehicleSettings : RidingBehaviourSettings {
|
||||
override val key = VehicleBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var canJump = "true".asExpression()
|
||||
var infiniteStamina: Expression? = null
|
||||
private set
|
||||
|
||||
var speed = "0.3".asExpression()
|
||||
var canJump: Expression? = null
|
||||
private set
|
||||
|
||||
var driveFactor = "1.0".asExpression()
|
||||
var speed: Expression? = null
|
||||
private set
|
||||
|
||||
var reverseDriveFactor = "0.25".asExpression()
|
||||
var driveFactor: Expression? = null
|
||||
private set
|
||||
|
||||
var rotationSpeed = "45/20".asExpression()
|
||||
var reverseDriveFactor: Expression? = null
|
||||
private set
|
||||
|
||||
var lookYawLimit = "101".asExpression()
|
||||
var rotationSpeed: Expression? = null
|
||||
private set
|
||||
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'LAND', 1.0, 0.3)".asExpression()
|
||||
var lookYawLimit: Expression? = null
|
||||
private set
|
||||
|
||||
var speedExpr: Expression? = null
|
||||
private set
|
||||
|
||||
// Max accel is a whole 1.0 in 1 second. The conversion in the function below is to convert seconds to ticks
|
||||
var accelerationExpr: Expression =
|
||||
"q.get_ride_stats('ACCELERATION', 'LAND', (1.0 / (20.0 * 1.5)), (1.0 / (20.0 * 5.0)))".asExpression()
|
||||
var accelerationExpr: Expression? = null
|
||||
private set
|
||||
|
||||
// Between 30 seconds and 10 seconds at the lowest when at full speed.
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'LAND', 30.0, 10.0)".asExpression()
|
||||
var staminaExpr: Expression? = null
|
||||
private set
|
||||
|
||||
//Between a one block jump and a ten block jump
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'LAND', 10.0, 1.0)".asExpression()
|
||||
var jumpExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'LAND', 140.0, 20.0)".asExpression()
|
||||
var handlingExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(canJump)
|
||||
buffer.writeExpression(speed)
|
||||
buffer.writeExpression(driveFactor)
|
||||
buffer.writeExpression(reverseDriveFactor)
|
||||
buffer.writeExpression(rotationSpeed)
|
||||
buffer.writeExpression(lookYawLimit)
|
||||
buffer.writeNullableExpression(infiniteStamina)
|
||||
buffer.writeNullableExpression(canJump)
|
||||
buffer.writeNullableExpression(speed)
|
||||
buffer.writeNullableExpression(driveFactor)
|
||||
buffer.writeNullableExpression(reverseDriveFactor)
|
||||
buffer.writeNullableExpression(rotationSpeed)
|
||||
buffer.writeNullableExpression(lookYawLimit)
|
||||
buffer.writeNullableExpression(speedExpr)
|
||||
buffer.writeNullableExpression(accelerationExpr)
|
||||
buffer.writeNullableExpression(staminaExpr)
|
||||
buffer.writeNullableExpression(jumpExpr)
|
||||
buffer.writeNullableExpression(handlingExpr)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
canJump = buffer.readExpression()
|
||||
speed = buffer.readExpression()
|
||||
driveFactor = buffer.readExpression()
|
||||
reverseDriveFactor = buffer.readExpression()
|
||||
rotationSpeed = buffer.readExpression()
|
||||
lookYawLimit = buffer.readExpression()
|
||||
infiniteStamina = buffer.readNullableExpression()
|
||||
canJump = buffer.readNullableExpression()
|
||||
speed = buffer.readNullableExpression()
|
||||
driveFactor = buffer.readNullableExpression()
|
||||
reverseDriveFactor = buffer.readNullableExpression()
|
||||
rotationSpeed = buffer.readNullableExpression()
|
||||
lookYawLimit = buffer.readNullableExpression()
|
||||
speedExpr = buffer.readNullableExpression()
|
||||
accelerationExpr = buffer.readNullableExpression()
|
||||
staminaExpr = buffer.readNullableExpression()
|
||||
jumpExpr = buffer.readNullableExpression()
|
||||
handlingExpr = buffer.readNullableExpression()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+141
-46
@@ -8,17 +8,34 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types.liquid
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.Side
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.ridingState
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.readNullableExpression
|
||||
import com.cobblemon.mod.common.util.readRidingStats
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
import com.cobblemon.mod.common.util.resolveDouble
|
||||
import com.cobblemon.mod.common.util.resolveFloat
|
||||
import com.cobblemon.mod.common.util.writeNullableExpression
|
||||
import com.cobblemon.mod.common.util.writeRidingStats
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.Mth
|
||||
@@ -28,24 +45,24 @@ import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("liquid/boat")
|
||||
val TOP_SPEED = 2.0
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
val globalBoat: BoatSettings
|
||||
get() = CobblemonRideSettings.boat
|
||||
|
||||
override fun getRidingStyle(settings: BoatSettings, state: BoatState): RidingStyle {
|
||||
return RidingStyle.LIQUID
|
||||
}
|
||||
|
||||
val poseProvider = PoseProvider<BoatSettings, BoatState>(PoseType.FLOAT)
|
||||
.with(PoseOption(PoseType.SWIM) { _, _, entity -> entity.isSwimming && entity.entityData.get(PokemonEntity.MOVING) })
|
||||
val poseProvider = PoseProvider<BoatSettings, BoatState>(PoseType.STAND)
|
||||
.with(PoseOption(PoseType.WALK) { _, state, _ ->
|
||||
abs(state.rideVelocity.get().horizontalDistance()) > 0.2
|
||||
})
|
||||
|
||||
override fun isActive(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity): Boolean {
|
||||
if (state.jumpBuffer.get() != -1) {
|
||||
@@ -76,14 +93,22 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
|
||||
state.jumpBuffer.set(state.jumpBuffer.get() + 1)
|
||||
}
|
||||
|
||||
if (driver.zza == 0f && (vehicle.isInWater || vehicle.isUnderWater)) {
|
||||
// Only start sprinting on vehicle if the state has changed and the pokemon has atleast 1/3 stamina
|
||||
if (driver.isSprinting && !state.isVehicleSprinting.get()) {
|
||||
state.isVehicleSprinting.set(state.stamina.get() > 0.33f)
|
||||
}
|
||||
else if (!driver.isSprinting) {
|
||||
state.isVehicleSprinting.set(false)
|
||||
}
|
||||
|
||||
if (!state.isVehicleSprinting.get()) {
|
||||
if (state.staminaBuffer.get() >= 20) {
|
||||
val staminaIncrease = vehicle.getRideStat(RidingStat.STAMINA, RidingStyle.LIQUID, 1.0, 2.0)
|
||||
val staminaIncrease = vehicle.runtime.resolveDouble(settings.staminaIncreaseExpr ?: globalBoat.staminaIncreaseExpr!!)
|
||||
state.stamina.set(min(1.0f, state.stamina.get() + 0.01f * staminaIncrease.toFloat()))
|
||||
}
|
||||
}
|
||||
else if (abs(state.rideVelocity.get().z) > 0.1) {
|
||||
consumeStamina(vehicle, state, 0.01f)
|
||||
else if (abs(state.rideVelocity.get().z) > 0.1 && state.isVehicleSprinting.get()) {
|
||||
consumeStamina(vehicle, driver, settings, state, 0.01f)
|
||||
}
|
||||
state.staminaBuffer.set(state.staminaBuffer.get() + 1)
|
||||
super.tick(settings, state, vehicle, driver, input)
|
||||
@@ -114,67 +139,79 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
applyStrafeRotation(vehicle, driver, state)
|
||||
applyStrafeRotation(vehicle, driver, settings, state)
|
||||
|
||||
var velocity = state.rideVelocity.get()
|
||||
velocity = applyVelocityFromInput(velocity, vehicle, driver, state)
|
||||
velocity = applyVelocityFromInput(velocity, vehicle, driver, settings, state)
|
||||
velocity = applyGravity(velocity, vehicle, settings, state)
|
||||
velocity = applyJump(velocity, vehicle, driver, state)
|
||||
velocity = applyJump(velocity, vehicle, driver, settings, state)
|
||||
state.rideVelocity.set(velocity)
|
||||
return state.rideVelocity.get()
|
||||
}
|
||||
|
||||
private fun applyVelocityFromInput(velocity: Vec3, vehicle: PokemonEntity, driver: Player, state: BoatState): Vec3 {
|
||||
val speed = vehicle.getRideStat(RidingStat.SPEED, RidingStyle.LIQUID, 0.8, TOP_SPEED)
|
||||
val acceleration = vehicle.getRideStat(RidingStat.ACCELERATION, RidingStyle.LIQUID, 0.05, 0.5)
|
||||
private fun applyVelocityFromInput(velocity: Vec3, vehicle: PokemonEntity, driver: Player, settings: BoatSettings, state: BoatState): Vec3 {
|
||||
val speed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalBoat.speedExpr!!)
|
||||
val acceleration = speed / (vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalBoat.accelerationExpr!!) * 20)
|
||||
|
||||
if (state.jumpBuffer.get() != -1 || !(vehicle.isInWater || vehicle.isUnderWater)) {
|
||||
return velocity
|
||||
}
|
||||
|
||||
val sprintModifier = vehicle.runtime.resolveDouble(settings.sprintSpeedModifier ?: globalBoat.sprintSpeedModifier!!)
|
||||
val activeSprintModifier = if (state.isVehicleSprinting.get() && hasStamina(state)) sprintModifier else 1.0
|
||||
val forwardInput = driver.zza.toDouble()
|
||||
val delta = when {
|
||||
!hasStamina(state) || forwardInput == 0.0 -> -velocity.z * 0.04
|
||||
forwardInput == 0.0 -> -velocity.z * 0.04
|
||||
forwardInput < 0.0 -> when {
|
||||
velocity.z < 0.0 -> forwardInput * acceleration * 0.5
|
||||
else -> min(forwardInput * acceleration * 0.5, -velocity.z * 0.04) // We never want it to be slower to reverse than no input
|
||||
}
|
||||
else -> forwardInput * acceleration
|
||||
else -> forwardInput * acceleration * activeSprintModifier
|
||||
}
|
||||
|
||||
return Vec3(
|
||||
velocity.x,
|
||||
velocity.y,
|
||||
Mth.clamp(velocity.z + delta, -speed * 0.25, speed)
|
||||
Mth.clamp(velocity.z + delta, -speed * 0.25, speed * activeSprintModifier)
|
||||
)
|
||||
}
|
||||
|
||||
private fun applyJump(velocity: Vec3, vehicle: PokemonEntity, driver: Player, state: BoatState): Vec3 {
|
||||
private fun applyJump(velocity: Vec3, vehicle: PokemonEntity, driver: Player, settings: BoatSettings, state: BoatState): Vec3 {
|
||||
if (!driver.jumping) return velocity // Not jumping
|
||||
if (state.jumpBuffer.get() != -1) return velocity // Already jumped very recently
|
||||
if (!hasStamina(state)) return velocity // Has no stamina
|
||||
|
||||
val jumpStrength = vehicle.getRideStat(RidingStat.JUMP, RidingStyle.LIQUID, 1.0, 2.5)
|
||||
val jumpStrength = vehicle.runtime.resolveDouble(settings.jumpStrengthExpr ?: globalBoat.jumpStrengthExpr!!)
|
||||
state.jumpBuffer.set(0)
|
||||
consumeStamina(vehicle, state, 0.05f)
|
||||
return Vec3(velocity.x, velocity.y + jumpStrength, velocity.z)
|
||||
}
|
||||
|
||||
private fun applyGravity(velocity: Vec3, vehicle: PokemonEntity, settings: BoatSettings, state: BoatState): Vec3 {
|
||||
if (state.jumpBuffer.get() == -1 && vehicle.isInWater || vehicle.isUnderWater) {
|
||||
return Vec3(velocity.x, -0.05, velocity.z)
|
||||
if (state.jumpBuffer.get() == -1 && (vehicle.isInWater || vehicle.isUnderWater)) {
|
||||
if (shouldFloatHigher(vehicle, settings)) {
|
||||
return Vec3(velocity.x, 0.5, velocity.z)
|
||||
}
|
||||
else {
|
||||
return Vec3(velocity.x, 0.0, velocity.z)
|
||||
}
|
||||
}
|
||||
val terminalVelocity = vehicle.runtime.resolveDouble(settings.terminalVelocity)
|
||||
val terminalVelocity = vehicle.runtime.resolveDouble(settings.terminalVelocity ?: globalBoat.terminalVelocity!!)
|
||||
val gravity = (9.8 / ( 20.0)) * 0.2
|
||||
return Vec3(velocity.x, max(velocity.y - gravity, terminalVelocity), velocity.z)
|
||||
}
|
||||
|
||||
private fun applyStrafeRotation(vehicle: PokemonEntity, driver: Player, state: BoatState) {
|
||||
val skill = vehicle.getRideStat(RidingStat.SKILL, RidingStyle.LIQUID, 0.5, 1.5)
|
||||
private fun shouldFloatHigher(vehicle: PokemonEntity, settings: BoatSettings): Boolean {
|
||||
val surfaceOffset = vehicle.runtime.resolveFloat(settings.surfaceLevelOffset ?: globalBoat.surfaceLevelOffset!!)
|
||||
val blockPos = BlockPos.containing(vehicle.x, vehicle.eyeY + surfaceOffset, vehicle.z)
|
||||
val fluidState = vehicle.level().getFluidState(blockPos)
|
||||
return !fluidState.isEmpty
|
||||
}
|
||||
|
||||
private fun applyStrafeRotation(vehicle: PokemonEntity, driver: Player, settings: BoatSettings, state: BoatState) {
|
||||
val skillModifier = vehicle.runtime.resolveDouble(settings.rotationSpeedModifierExpr ?: globalBoat.rotationSpeedModifierExpr!!)
|
||||
val strafe = driver.xxa
|
||||
if (abs(strafe) > 0) {
|
||||
val increase = if (strafe > 0) -1 else 1
|
||||
state.deltaRotation.set(state.deltaRotation.get() + increase * skill)
|
||||
state.deltaRotation.set(state.deltaRotation.get() + increase * skillModifier)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,9 +219,16 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
|
||||
return state.stamina.get() != 0f
|
||||
}
|
||||
|
||||
private fun consumeStamina(vehicle: PokemonEntity, state: BoatState, drain: Float) {
|
||||
val stamina = vehicle.getRideStat(RidingStat.STAMINA, RidingStyle.LIQUID, 1.0, 5.0)
|
||||
state.stamina.set(max(0f, state.stamina.get() - drain / stamina.toFloat()))
|
||||
private fun consumeStamina(vehicle: PokemonEntity, driver: Player, settings: BoatSettings, state: BoatState, drain: Float) {
|
||||
val staminaModifier = vehicle.runtime.resolveFloat(settings.staminaDecreaseExpr ?: globalBoat.staminaDecreaseExpr!!)
|
||||
if (staminaModifier == 0F) {
|
||||
return
|
||||
}
|
||||
state.stamina.set(max(0f, state.stamina.get() - drain / staminaModifier))
|
||||
if (state.stamina.get() == 0f) {
|
||||
state.isVehicleSprinting.set(false)
|
||||
driver.isSprinting = false
|
||||
}
|
||||
state.staminaBuffer.set(0)
|
||||
}
|
||||
|
||||
@@ -285,10 +329,8 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
if (state.rideVelocity.get().z > TOP_SPEED * 0.8) {
|
||||
return Mth.lerp((state.rideVelocity.get().z - (TOP_SPEED * 0.8)) / (TOP_SPEED * 0.2), 1.0, 1.2).toFloat()
|
||||
}
|
||||
return 1.0f
|
||||
val sprintFov = vehicle.runtime.resolveFloat(settings.sprintFovModifier ?: globalBoat.sprintFovModifier!!)
|
||||
return if (state.isVehicleSprinting.get()) sprintFov else 1.0f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity): Boolean {
|
||||
@@ -301,7 +343,14 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): ResourceLocation {
|
||||
return cobblemonResource("no_pose")
|
||||
val isInWater = Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
|
||||
if (vehicle.isInWater || vehicle.isUnderWater) {
|
||||
return@any true
|
||||
}
|
||||
val blockState = vehicle.level().getBlockState(it)
|
||||
return@any !blockState.fluidState.isEmpty
|
||||
}
|
||||
return if (!isInWater) cobblemonResource("in_air") else cobblemonResource("no_pose")
|
||||
}
|
||||
|
||||
override fun inertia(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity): Double {
|
||||
@@ -325,7 +374,7 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
|
||||
state: BoatState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return vehicle.runtime.resolveBoolean(settings.rotatePokemonHead)
|
||||
return vehicle.runtime.resolveBoolean(settings.rotatePokemonHead ?: globalBoat.rotatePokemonHead!!)
|
||||
}
|
||||
|
||||
override fun shouldRotateRiderHead(
|
||||
@@ -355,23 +404,67 @@ class BoatSettings : RidingBehaviourSettings {
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
var terminalVelocity = "-2.0".asExpression()
|
||||
var terminalVelocity: Expression? = null
|
||||
private set
|
||||
|
||||
var rotatePokemonHead = "true".asExpression()
|
||||
var rotatePokemonHead: Expression? = null
|
||||
private set
|
||||
|
||||
var staminaIncreaseExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var staminaDecreaseExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var rotationSpeedModifierExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var jumpStrengthExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var speedExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var accelerationExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var sprintSpeedModifier: Expression? = null
|
||||
private set
|
||||
|
||||
var sprintFovModifier: Expression? = null
|
||||
|
||||
var surfaceLevelOffset: Expression? = null
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(terminalVelocity)
|
||||
buffer.writeNullableExpression(terminalVelocity)
|
||||
buffer.writeNullableExpression(rotatePokemonHead)
|
||||
buffer.writeNullableExpression(staminaIncreaseExpr)
|
||||
buffer.writeNullableExpression(staminaDecreaseExpr)
|
||||
buffer.writeNullableExpression(rotationSpeedModifierExpr)
|
||||
buffer.writeNullableExpression(jumpStrengthExpr)
|
||||
buffer.writeNullableExpression(speedExpr)
|
||||
buffer.writeNullableExpression(accelerationExpr)
|
||||
buffer.writeNullableExpression(sprintSpeedModifier)
|
||||
buffer.writeNullableExpression(sprintFovModifier)
|
||||
buffer.writeNullableExpression(surfaceLevelOffset)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
terminalVelocity = buffer.readExpression()
|
||||
terminalVelocity = buffer.readNullableExpression()
|
||||
rotatePokemonHead = buffer.readNullableExpression()
|
||||
staminaIncreaseExpr = buffer.readNullableExpression()
|
||||
staminaDecreaseExpr = buffer.readNullableExpression()
|
||||
rotationSpeedModifierExpr = buffer.readNullableExpression()
|
||||
jumpStrengthExpr = buffer.readNullableExpression()
|
||||
speedExpr = buffer.readNullableExpression()
|
||||
accelerationExpr = buffer.readNullableExpression()
|
||||
sprintSpeedModifier = buffer.readNullableExpression()
|
||||
sprintFovModifier = buffer.readNullableExpression()
|
||||
surfaceLevelOffset = buffer.readNullableExpression()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,6 +472,7 @@ class BoatState : RidingBehaviourState() {
|
||||
val deltaRotation = ridingState(0.0, Side.CLIENT)
|
||||
val jumpBuffer = ridingState(-1, Side.CLIENT)
|
||||
val staminaBuffer = ridingState(0, Side.CLIENT)
|
||||
val isVehicleSprinting = ridingState(false, Side.CLIENT)
|
||||
|
||||
override fun copy() = BoatState().also {
|
||||
it.deltaRotation.set(deltaRotation.get(), true)
|
||||
@@ -386,6 +480,7 @@ class BoatState : RidingBehaviourState() {
|
||||
it.stamina.set(stamina.get(), true)
|
||||
it.jumpBuffer.set(jumpBuffer.get(), true)
|
||||
it.staminaBuffer.set(staminaBuffer.get(), true)
|
||||
it.isVehicleSprinting.set(isVehicleSprinting.get(), true)
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
|
||||
+14
-8
@@ -8,10 +8,14 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types.liquid
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.Side
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.ridingState
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
@@ -21,6 +25,7 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.readRidingStats
|
||||
import com.cobblemon.mod.common.util.writeNullable
|
||||
import com.cobblemon.mod.common.util.writeRidingStats
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
@@ -38,6 +43,8 @@ class BurstBehaviour : RidingBehaviour<BurstSettings, BurstState> {
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
val globalBurst: BurstSettings
|
||||
get() = CobblemonRideSettings.burst
|
||||
|
||||
override fun getRidingStyle(settings: BurstSettings, state: BurstState): RidingStyle {
|
||||
return RidingStyle.LIQUID
|
||||
@@ -67,7 +74,7 @@ class BurstBehaviour : RidingBehaviour<BurstSettings, BurstState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
if(state.dashing.get()) {
|
||||
if (state.dashing.get()) {
|
||||
state.ticks.set(state.ticks.get() + 1)
|
||||
if(state.ticks.get() >= DASH_TICKS) {
|
||||
state.dashing.set(false)
|
||||
@@ -77,7 +84,7 @@ class BurstBehaviour : RidingBehaviour<BurstSettings, BurstState> {
|
||||
}
|
||||
|
||||
state.dashing.set(true)
|
||||
return settings.dashSpeed
|
||||
return settings.dashSpeed ?: globalBurst.dashSpeed!!
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
@@ -239,22 +246,21 @@ class BurstSettings : RidingBehaviourSettings {
|
||||
override val key = BurstBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var dashSpeed = 1F
|
||||
var dashSpeed: Float? = null
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeFloat(dashSpeed)
|
||||
buffer.writeNullable(dashSpeed) { buf, speed -> buf.writeFloat(speed) }
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
dashSpeed = buffer.readFloat()
|
||||
dashSpeed = buffer.readNullable { buf -> buf.readFloat() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+352
-160
@@ -9,34 +9,44 @@
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types.liquid
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
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
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.JetSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.JetState
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.Side
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.ridingState
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.math.geometry.toDegrees
|
||||
import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import com.cobblemon.mod.common.util.readNullableExpression
|
||||
import com.cobblemon.mod.common.util.readRidingStats
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
import com.cobblemon.mod.common.util.resolveDouble
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import com.cobblemon.mod.common.util.writeNullableExpression
|
||||
import com.cobblemon.mod.common.util.writeRidingStats
|
||||
import kotlin.math.*
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.min
|
||||
import kotlin.math.sin
|
||||
import kotlin.math.sqrt
|
||||
|
||||
class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
companion object {
|
||||
@@ -44,6 +54,8 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
val globalDolphin: DolphinSettings
|
||||
get() = CobblemonRideSettings.dolphin
|
||||
|
||||
override fun getRidingStyle(settings: DolphinSettings, state: DolphinState): RidingStyle {
|
||||
return RidingStyle.LIQUID
|
||||
@@ -53,14 +65,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
.with(PoseOption(PoseType.SWIM) { _, _, entity -> entity.deltaMovement.length() > 0.1 })
|
||||
|
||||
override fun isActive(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity): Boolean {
|
||||
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
|
||||
if (it.y.toDouble() == (vehicle.position().y)) {
|
||||
val blockState = vehicle.level().getBlockState(it.below())
|
||||
return@any (blockState.isAir || !blockState.fluidState.isEmpty ) ||
|
||||
(vehicle.isInWater || vehicle.isUnderWater)
|
||||
}
|
||||
true
|
||||
}
|
||||
return vehicle.isInWater || !vehicle.onGround()
|
||||
}
|
||||
|
||||
override fun pose(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity): PoseType {
|
||||
@@ -68,7 +73,66 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
}
|
||||
|
||||
override fun speed(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity, driver: Player): Float {
|
||||
return vehicle.runtime.resolveFloat(settings.speed)
|
||||
return state.speed.get().toFloat()
|
||||
}
|
||||
|
||||
override fun tick(
|
||||
settings: DolphinSettings,
|
||||
state: DolphinState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
) {
|
||||
if(vehicle.level().isClientSide) {
|
||||
handleBoosting(state, vehicle)
|
||||
tickStamina(settings, state, vehicle)
|
||||
}
|
||||
}
|
||||
|
||||
fun handleBoosting(
|
||||
state: DolphinState,
|
||||
vehicle: PokemonEntity
|
||||
) {
|
||||
//If the forward key is not held then it cannot be boosting
|
||||
if(Minecraft.getInstance().options.keyUp.isDown() && state.stamina.get() != 0.0f && (vehicle.isInWater || vehicle.isUnderWater)) {
|
||||
val boostKeyPressed = Minecraft.getInstance().options.keySprint.isDown()
|
||||
if (state.stamina.get() >= 0.25f) {
|
||||
//If on the previous tick the boost key was held then don't change if the ride is boosting
|
||||
if(state.boostIsToggleable.get() && boostKeyPressed) {
|
||||
//flip the boosting state if boost key is pressed
|
||||
state.boosting.set(!state.boosting.get())
|
||||
}
|
||||
//If the boost key is not held then next tick boosting is toggleable
|
||||
state.boostIsToggleable.set(!boostKeyPressed)
|
||||
}
|
||||
} else {
|
||||
//Turn off boost and reset boost params
|
||||
state.boostIsToggleable.set(true)
|
||||
state.boosting.set(false)
|
||||
}
|
||||
}
|
||||
|
||||
fun tickStamina(
|
||||
settings: DolphinSettings,
|
||||
state: DolphinState,
|
||||
vehicle: PokemonEntity
|
||||
) {
|
||||
val stam = state.stamina.get()
|
||||
|
||||
if (vehicle.runtime.resolveBoolean(settings.infiniteStamina ?: globalDolphin.infiniteStamina!!)) {
|
||||
return
|
||||
}
|
||||
|
||||
// Grab the boost time in seconds and convert to ticks. Then calculate the drain rate as inversely
|
||||
// proportional to the number of ticks of boost thus making a full boost take x ticks
|
||||
// in short: "Stamina drains slower at higher values and also replenishes slower"
|
||||
val boostTime = vehicle.runtime.resolveDouble(settings.staminaExpr ?: globalDolphin.staminaExpr!!) * 20.0f
|
||||
val stamDrainRate = (1.0f / boostTime).toFloat()
|
||||
|
||||
val newStam = if (state.boosting.get()) max(0.0f,stam - stamDrainRate)
|
||||
else min(1.0f,stam + stamDrainRate)
|
||||
|
||||
state.stamina.set(newStam)
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
@@ -87,39 +151,71 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
val runtime = vehicle.runtime
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
|
||||
val runtime = vehicle.runtime
|
||||
val boostMod = if (state.boosting.get()) runtime.resolveDouble(settings.jumpExpr ?: globalDolphin.jumpExpr!!) else 1.0
|
||||
val topSpeed = (vehicle.runtime.resolveDouble(settings.speedExpr ?: globalDolphin.speedExpr!!) / 20.0) * boostMod
|
||||
val accel = (topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalDolphin.accelerationExpr!!) * 20.0)) * boostMod
|
||||
|
||||
/*********************************************
|
||||
* Breaching logic
|
||||
*********************************************/
|
||||
if (!vehicle.isInWater && !vehicle.isUnderWater)
|
||||
{
|
||||
state.lastVelocity.set(Vec3(state.lastVelocity.get().x, state.lastVelocity.get().y - 0.035, state.lastVelocity.get().z))
|
||||
val currVel = state.lastVelocity.get()
|
||||
val breachBoost = 1.5
|
||||
if (!state.hasBreached.get()) {
|
||||
// Apply an upwards breaching force if boosting. This will enhance jumping out of water to feel more
|
||||
// fun
|
||||
if (state.boosting.get()) {
|
||||
state.lastVelocity.set(Vec3(currVel.x, currVel.y + breachBoost*sin(-controller.pitch.toRadians()).coerceIn(0.0f,0.5f), currVel.z))
|
||||
}
|
||||
state.hasBreached.set(true)
|
||||
} else {
|
||||
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.4
|
||||
val terminalVel = 2.0
|
||||
state.lastVelocity.set(Vec3(currVel.x, max(-terminalVel, currVel.y - gravity), currVel.z)) //.yRot(vehicle.yRot.toRadians()))
|
||||
}
|
||||
return state.lastVelocity.get()
|
||||
} else {
|
||||
state.lastVelocity.set(vehicle.deltaMovement.yRot(vehicle.yRot.toRadians()))
|
||||
state.hasBreached.set(false)
|
||||
}
|
||||
|
||||
val driveFactor = runtime.resolveFloat(settings.driveFactor)
|
||||
val strafeFactor = runtime.resolveFloat(settings.strafeFactor)
|
||||
val f = driver.xxa * strafeFactor
|
||||
var g = driver.zza * driveFactor
|
||||
if (g <= 0.0f) {
|
||||
g *= runtime.resolveFloat(settings.reverseDriveFactor)
|
||||
/*********************************************
|
||||
* Handle collisions
|
||||
*********************************************/
|
||||
if (vehicle.horizontalCollision || vehicle.verticalCollision) {
|
||||
// not actually friction
|
||||
val frictionLimit = 0.1
|
||||
val postColSpeed = min(max(vehicle.deltaMovement.length(), frictionLimit), state.speed.get())
|
||||
state.speed.set(postColSpeed)
|
||||
}
|
||||
|
||||
var yComp = 0.0
|
||||
|
||||
//Get roll to add a left and right strafe during roll
|
||||
//Not sure if this is desired? Will need to mess around with this
|
||||
//a bit more
|
||||
var zComp = 0.0
|
||||
val controller = (driver as? OrientationControllable)?.orientationController
|
||||
if (controller != null)
|
||||
{
|
||||
//Can be used again if I better figure out how to deadzone it near the tops?
|
||||
zComp = -1.0 * g.toDouble() * sin(Math.toRadians(controller.roll.toDouble()))
|
||||
yComp = -1.0 * g.toDouble() * sin(Math.toRadians(controller.pitch.toDouble()))
|
||||
val currSpeed = state.speed.get()
|
||||
val strafeFactor = runtime.resolveDouble(settings.strafeFactor ?: globalDolphin.strafeFactor!!)
|
||||
val reverseDriveFactor = runtime.resolveDouble(settings.reverseDriveFactor ?: globalDolphin.reverseDriveFactor!!)
|
||||
val f = -driver.xxa * strafeFactor
|
||||
when {
|
||||
driver.zza > 0.0 -> state.speed.set(min(topSpeed, currSpeed + accel))
|
||||
driver.zza < 0.0 -> state.speed.set(max( -topSpeed * reverseDriveFactor, currSpeed - accel))
|
||||
else -> state.speed.set( lerp(currSpeed, 0.0, 0.05))
|
||||
}
|
||||
val vertInput = when {
|
||||
driver.jumping && state.stamina.get() != 0.0f -> 1.0
|
||||
driver.isShiftKeyDown -> -1.0
|
||||
else -> 0.0
|
||||
} * strafeFactor
|
||||
|
||||
val currVelocity = Vec3(0.0, yComp , g.toDouble())
|
||||
state.lastVelocity.set(currVelocity)
|
||||
return currVelocity
|
||||
var newVelocity = Vec3(f, vertInput,-state.speed.get())
|
||||
|
||||
// Align to the ride
|
||||
newVelocity = controller.orientation?.transform(newVelocity.toVector3f())?.toVec3d() ?: Vec3.ZERO
|
||||
newVelocity = newVelocity.yRot(vehicle.yRot.toRadians())
|
||||
val desiredVec = newVelocity.subtract(vehicle.deltaMovement)
|
||||
return vehicle.deltaMovement.add(desiredVec)
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
@@ -129,30 +225,93 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (!vehicle.isInWater && !vehicle.isUnderWater)
|
||||
{
|
||||
return Vec3(0.0, 0.0, 0.0)
|
||||
if (Cobblemon.config.disableRoll) return Vec3.ZERO
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
|
||||
val currSpeed = vehicle.deltaMovement.length()
|
||||
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalDolphin.handlingExpr!!)
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalDolphin.speedExpr!!) / 20.0
|
||||
|
||||
val yawDeltaDeg = deltaTime * handling * sin(Math.toRadians(controller.roll.toDouble())).pow(2) * sin(Math.toRadians(controller.roll.toDouble())).sign
|
||||
val trueYawDelt = yawDeltaDeg * abs(cos(Math.toRadians(controller.pitch.toDouble()))) * sqrt(RidingBehaviour.scaleToRange(currSpeed, 0.0, topSpeed))
|
||||
|
||||
// Dampen yaw when upside down
|
||||
val yawDampen = (1 - abs(min(cos(controller.roll.toRadians()),0.0f)))
|
||||
controller.applyGlobalYaw(trueYawDelt.toFloat() * yawDampen)
|
||||
|
||||
// Correct orientation when at low speeds
|
||||
correctOrientation(settings, state, vehicle, controller, driver, deltaTime)
|
||||
|
||||
//yaw, pitch, roll
|
||||
return Vec3(0.0, state.currPitchCorrectionForce.get(), state.currRollCorrectionForce.get())
|
||||
}
|
||||
|
||||
private fun correctOrientation(
|
||||
settings: DolphinSettings,
|
||||
state: DolphinState,
|
||||
vehicle: PokemonEntity,
|
||||
controller: OrientationController,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
) {
|
||||
// Calculate correcting roll force.
|
||||
// Mult by cos(pitch) to ensure that it doesn't roll correct at when diving or climbing
|
||||
val currSpeed = vehicle.deltaMovement.length()
|
||||
val rollCorrectionTimer = vehicle.runtime.resolveDouble(settings.timeToRollCorrect ?: globalDolphin.timeToRollCorrect!!)
|
||||
val maxRollCorrectionRate = if (!(vehicle.isInWater || vehicle.isUnderWater)) 15.0f
|
||||
else 10.0f * (cos(controller.pitch.toRadians())).pow(2) * ((state.noInputTime.get() - rollCorrectionTimer) * 2).coerceIn(0.0, 1.0).toFloat()
|
||||
val maxRollForce = maxRollCorrectionRate / 40.0
|
||||
val rollArrivalDeg = 90.0
|
||||
|
||||
if((state.speed.get() < 0.1 && driver.zza.toDouble() == 0.0) || state.noInputTime.get() > rollCorrectionTimer || !(vehicle.isInWater || vehicle.isUnderWater)) {
|
||||
val arrivalInfluence = (min(rollArrivalDeg,abs(controller.roll).toDouble()) / rollArrivalDeg)
|
||||
val desiredRollForce = (0 - controller.roll).sign * maxRollCorrectionRate * arrivalInfluence
|
||||
var steeredRollForce = desiredRollForce - state.currRollCorrectionForce.get()
|
||||
|
||||
// Ensure the 'steering' correction force doesn't exceed our maxForce
|
||||
if (abs(steeredRollForce) > maxRollForce) {
|
||||
steeredRollForce = steeredRollForce.sign * maxRollForce
|
||||
}
|
||||
|
||||
// Apply the 'steered' force (its really not a steering force I need a better name)
|
||||
state.currRollCorrectionForce.set(
|
||||
state.currRollCorrectionForce.get() + steeredRollForce
|
||||
)
|
||||
} else {
|
||||
state.currRollCorrectionForce.set(
|
||||
//reduce rollCorrection gradually
|
||||
lerp(state.currRollCorrectionForce.get(), 0.0, deltaTime * 0.96)
|
||||
)
|
||||
}
|
||||
|
||||
val controller = (driver as? OrientationControllable)?.orientationController
|
||||
//Calculate correcting pitch force.
|
||||
val maxPitchCorrectionRate = if (!(vehicle.isInWater || vehicle.isUnderWater)) 15.0 else 5.0f * ((state.noInputTime.get() - rollCorrectionTimer) * 2).coerceIn(0.0, 1.0).toFloat() * (1 - (currSpeed * 0.5).coerceIn(0.0, 1.0))
|
||||
val maxPitchForce = maxPitchCorrectionRate / 40.0
|
||||
val pitchArrivalDeg = 45.0
|
||||
val targetPitch = if ((vehicle.isInWater || vehicle.isUnderWater)) 0.0f else (-asin(vehicle.deltaMovement.y / vehicle.deltaMovement.length()).toDegrees()).coerceIn(-45.0f, 45.0f)
|
||||
|
||||
//this should be changed to be speed maybe?
|
||||
val movingForce = driver.zza
|
||||
if (controller != null) {
|
||||
var yawAngVel = 3 * sin(Math.toRadians(controller.roll.toDouble())).toFloat()
|
||||
var pitchAngVel = -1 * Math.abs(sin(Math.toRadians(controller.roll.toDouble())).toFloat())
|
||||
if((state.noInputTime.get() > rollCorrectionTimer && driver.zza.toDouble() == 0.0) || !(vehicle.isInWater || vehicle.isUnderWater)) {
|
||||
val arrivalInfluence = min(rollArrivalDeg, abs(Mth.wrapDegrees(targetPitch - controller.pitch.toDouble()))) / pitchArrivalDeg
|
||||
val desiredPitchForce = (targetPitch - controller.pitch).sign * maxPitchCorrectionRate * arrivalInfluence
|
||||
var steeredPitchForce = desiredPitchForce - state.currPitchCorrectionForce.get()
|
||||
|
||||
//limit rotation modulation when pitched up heavily or pitched down heavily
|
||||
yawAngVel *= (abs(cos(Math.toRadians(controller.pitch.toDouble())))).toFloat()
|
||||
pitchAngVel *= (abs(cos(Math.toRadians(controller.pitch.toDouble())))).toFloat()
|
||||
//if you are not pressing forward then don't turn
|
||||
//yawAngVel *= movingForce
|
||||
//Ignore x,y,z its angular velocity:
|
||||
//yaw, pitch, roll
|
||||
return Vec3(yawAngVel.toDouble(), pitchAngVel.toDouble(), 0.0)
|
||||
// Ensure the 'steering' correction force doesn't exceed our maxForce
|
||||
if (abs(steeredPitchForce) > maxPitchForce) {
|
||||
steeredPitchForce = steeredPitchForce.sign * maxPitchForce
|
||||
}
|
||||
|
||||
// Apply the 'steered' force (its really not a steering force I need a better name)
|
||||
state.currPitchCorrectionForce.set(
|
||||
state.currPitchCorrectionForce.get() + steeredPitchForce
|
||||
)
|
||||
} else {
|
||||
state.currPitchCorrectionForce.set(
|
||||
//reduce rollCorrection gradually
|
||||
lerp(state.currPitchCorrectionForce.get(), 0.0, deltaTime * 0.98)
|
||||
)
|
||||
}
|
||||
|
||||
return Vec3(0.0, 0.0, 0.0)
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
@@ -167,7 +326,13 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
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 && (vehicle.isInWater || vehicle.isUnderWater)) {
|
||||
state.noInputTime.set(state.noInputTime.get() + deltaTime)
|
||||
} else {
|
||||
state.noInputTime.set(0.0)
|
||||
}
|
||||
return when {
|
||||
Cobblemon.config.disableRoll -> noRollRotation(
|
||||
settings,
|
||||
@@ -211,41 +376,34 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
|
||||
//TODO:hook up handling
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
// Set roll to zero if transitioning to noroll config
|
||||
controller.rotateRoll(controller.roll * -1.0f)
|
||||
|
||||
//Interpret mouse input
|
||||
val boostingHandleDebuff = if (state.boosting.get()) 0.2 else 1.0
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalDolphin.handlingExpr!!) * boostingHandleDebuff
|
||||
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult ?: globalDolphin.mouseInputMult!!)
|
||||
|
||||
//Smooth out mouse input.
|
||||
val smoothingSpeed = 4.0
|
||||
val mouseXc = (mouseX).coerceIn(-60.0, 60.0)
|
||||
val mouseYc = (mouseY).coerceIn(-60.0, 60.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);
|
||||
val rollRot = xInput
|
||||
val pitchRot = yInput
|
||||
|
||||
// Roll
|
||||
// if (abs(controller.pitch + rollRot) >= 89.5 ) {
|
||||
// mouseXSmoother.reset()
|
||||
// } else {
|
||||
//
|
||||
// }
|
||||
//Give the ability to yaw with x mouse input when at low speeds.
|
||||
val yawForce = xInput * handling
|
||||
|
||||
controller.rotateRoll(rollRot.toFloat())
|
||||
|
||||
// Yaw
|
||||
// TODO:move all magic numbers to handling expression
|
||||
val yawSpeed = handling * deltaTime * 0.25
|
||||
val yawForce = sin(controller.roll.toRadians()) * yawSpeed * ( 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())
|
||||
controller.applyGlobalYaw(yawForce.toFloat() * abs(cos(controller.pitch.toRadians())).pow(2))
|
||||
|
||||
// Pitch
|
||||
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)
|
||||
controller.rotatePitch(pitchRot.toFloat())
|
||||
controller.applyGlobalPitch(pitchRot.toFloat() * -1.0f)
|
||||
}
|
||||
|
||||
//yaw, pitch, roll
|
||||
@@ -266,46 +424,39 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
): Vec3 {
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalDolphin.speedExpr!!) / 20.0
|
||||
val boostingHandleDebuff = if (state.boosting.get()) 0.2 else 1.0
|
||||
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult ?: globalDolphin.mouseInputMult!!) * boostingHandleDebuff
|
||||
|
||||
//TODO:hook up handling
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
|
||||
//Interpret mouse input
|
||||
//Smooth out mouse input.
|
||||
val smoothingSpeed = 4.0
|
||||
val mouseXc = (mouseX).coerceIn(-60.0, 60.0)
|
||||
val mouseYc = (mouseY).coerceIn(-60.0, 60.0)
|
||||
val mouseXc = (mouseX).coerceIn(-45.0, 45.0) * mouseInputMult
|
||||
val mouseYc = (mouseY).coerceIn(-45.0, 45.0) * mouseInputMult
|
||||
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1, deltaTime * smoothingSpeed);
|
||||
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1, deltaTime * smoothingSpeed);
|
||||
val rollRot = xInput
|
||||
val pitchRot = yInput
|
||||
val currSpeed = vehicle.deltaMovement.length()
|
||||
|
||||
// Roll
|
||||
// if (abs(controller.pitch + rollRot) >= 89.5 ) {
|
||||
// mouseXSmoother.reset()
|
||||
// } else {
|
||||
//
|
||||
// }
|
||||
// Yaw locally
|
||||
// Dampen yaw when rolled or pitched substantially
|
||||
val yawDampen = abs(cos(controller.pitch.toRadians())).pow(2) * abs(cos(controller.roll.toRadians())).pow(2)
|
||||
val localYaw = xInput * (1 - (RidingBehaviour.scaleToRange(currSpeed, 0.0, topSpeed))).coerceIn(0.4, 1.0) * yawDampen
|
||||
|
||||
controller.rotateRoll(rollRot.toFloat())
|
||||
// Pitch locally up or down depending upon a number of factors:
|
||||
// - Reduce pitch substantially when rolled and in a steep yaw. This prevents the player from ignoring a slow handling stat
|
||||
// - Do not pitch at all when at or below hover speed
|
||||
// - pitch faster at higher speeds
|
||||
val p = 1.0 - vehicle.runtime.resolveDouble(settings.horizontalPitchExpr ?: globalDolphin.horizontalPitchExpr!!)
|
||||
val localPitch = yInput * (1 - abs(sin(controller.roll.toRadians())) * p * hypot(controller.forwardVector.x.toDouble(), controller.forwardVector.z.toDouble()).toFloat())
|
||||
// localPitch *= if (currSpeed > hoverSpeed) RidingBehaviour.scaleToRange(currSpeed, hoverSpeed, topSpeed)
|
||||
// else 0.0
|
||||
|
||||
// Yaw
|
||||
// TODO:move all magic numbers to handling expression
|
||||
val yawSpeed = handling * deltaTime * 0.25
|
||||
val yawForce = sin(controller.roll.toRadians()) * yawSpeed * ( 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())
|
||||
|
||||
// Pitch
|
||||
if (abs(controller.pitch + pitchRot) >= 89.5 ) {
|
||||
mouseYSmoother.reset()
|
||||
} else {
|
||||
//controller.applyGlobalPitch(pitchRot.toFloat() * -1.0f)
|
||||
controller.rotatePitch(pitchRot.toFloat())
|
||||
}
|
||||
// Roll less when slow and not at all when at hoverSpeed or lower.
|
||||
// Apply a roll righting force when trying to pitch hard while rolled sideways. This nudges the player towards
|
||||
// pitching globally
|
||||
val rollForce = xInput * ((RidingBehaviour.scaleToRange(currSpeed, 0.0, topSpeed))).coerceIn(0.2, 1.0) //+ pitchInfluencedRollCorrection
|
||||
|
||||
//yaw, pitch, roll
|
||||
return Vec3.ZERO
|
||||
return Vec3(localYaw, localPitch, rollForce)
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
@@ -314,7 +465,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return vehicle.runtime.resolveBoolean(settings.canJump)
|
||||
return false
|
||||
}
|
||||
|
||||
override fun setRideBar(
|
||||
@@ -323,7 +474,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return 0.0f
|
||||
return (state.stamina.get() / 1.0f)
|
||||
}
|
||||
|
||||
override fun jumpForce(
|
||||
@@ -333,10 +484,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
driver: Player,
|
||||
jumpStrength: Int
|
||||
): Vec3 {
|
||||
val runtime = vehicle.runtime
|
||||
runtime.environment.query.addFunction("jump_strength") { DoubleValue(jumpStrength.toDouble()) }
|
||||
val jumpVector = settings.jumpVector.map { runtime.resolveFloat(it) }
|
||||
return Vec3(jumpVector[0].toDouble(), jumpVector[1].toDouble(), jumpVector[2].toDouble())
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun gravity(
|
||||
@@ -345,7 +493,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
vehicle: PokemonEntity,
|
||||
regularGravity: Double
|
||||
): Double {
|
||||
return 0.0
|
||||
return 0.0 //if (!vehicle.isInWater && !vehicle.isUnderWater) 1.0 else 0.0
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(
|
||||
@@ -354,7 +502,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return 1.0f
|
||||
return if (state.boosting.get()) 1.2f else 1.0f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity): Boolean {
|
||||
@@ -371,7 +519,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
}
|
||||
|
||||
override fun inertia(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity): Double {
|
||||
return 0.05
|
||||
return if (!vehicle.isInWater && !vehicle.isUnderWater) 1.0 else 0.1
|
||||
}
|
||||
|
||||
override fun shouldRoll(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity): Boolean {
|
||||
@@ -379,7 +527,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
}
|
||||
|
||||
override fun turnOffOnGround(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity): Boolean {
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
override fun dismountOnShift(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity): Boolean {
|
||||
@@ -418,68 +566,99 @@ class DolphinSettings : RidingBehaviourSettings {
|
||||
override val key = DolphinBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var canJump = "true".asExpression()
|
||||
var infiniteStamina: Expression? = null
|
||||
private set
|
||||
|
||||
var jumpVector = listOf("0".asExpression(), "0.3".asExpression(), "0".asExpression())
|
||||
var canJump: Expression? = null
|
||||
private set
|
||||
|
||||
var speed = "1.0".asExpression()
|
||||
var reverseDriveFactor: Expression? = null
|
||||
private set
|
||||
|
||||
var driveFactor = "1.0".asExpression()
|
||||
var strafeFactor: Expression? = null
|
||||
private set
|
||||
|
||||
var reverseDriveFactor = "0.25".asExpression()
|
||||
var timeToRollCorrect: Expression? = null
|
||||
private set
|
||||
|
||||
var strafeFactor = "0.2".asExpression()
|
||||
private set
|
||||
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 200.0, 128.0)".asExpression()
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 135.0, 45.0)".asExpression()
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 1.0, 0.35)".asExpression()
|
||||
var accelerationExpr: Expression = "q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 3.0)), (1.0 / (20.0 * 8.0)))".asExpression()
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 120.0, 20.0)".asExpression()
|
||||
// Boost influence when boosting
|
||||
var jumpExpr: Expression? = null
|
||||
// Yaw rate in degrees per second
|
||||
var handlingExpr: Expression? = null
|
||||
var horizontalPitchExpr: Expression? = null
|
||||
var mouseInputMult: Expression? = null
|
||||
// Top speed in Bl/s
|
||||
var speedExpr: Expression? = null
|
||||
// Seconds to get to top speed
|
||||
var accelerationExpr: Expression? = null
|
||||
// Seconds of boost
|
||||
var staminaExpr: Expression? = null
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(canJump)
|
||||
buffer.writeExpression(jumpVector[0])
|
||||
buffer.writeExpression(jumpVector[1])
|
||||
buffer.writeExpression(jumpVector[2])
|
||||
buffer.writeExpression(speed)
|
||||
buffer.writeExpression(driveFactor)
|
||||
buffer.writeExpression(reverseDriveFactor)
|
||||
buffer.writeExpression(strafeFactor)
|
||||
buffer.writeNullableExpression(infiniteStamina)
|
||||
buffer.writeNullableExpression(canJump)
|
||||
buffer.writeNullableExpression(reverseDriveFactor)
|
||||
buffer.writeNullableExpression(strafeFactor)
|
||||
buffer.writeNullableExpression(jumpExpr)
|
||||
buffer.writeNullableExpression(handlingExpr)
|
||||
buffer.writeNullableExpression(horizontalPitchExpr)
|
||||
buffer.writeNullableExpression(mouseInputMult)
|
||||
buffer.writeNullableExpression(speedExpr)
|
||||
buffer.writeNullableExpression(accelerationExpr)
|
||||
buffer.writeNullableExpression(staminaExpr)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
canJump = buffer.readExpression()
|
||||
jumpVector = listOf(
|
||||
buffer.readExpression(),
|
||||
buffer.readExpression(),
|
||||
buffer.readExpression()
|
||||
)
|
||||
speed = buffer.readExpression()
|
||||
driveFactor = buffer.readExpression()
|
||||
reverseDriveFactor = buffer.readExpression()
|
||||
strafeFactor = buffer.readExpression()
|
||||
infiniteStamina = buffer.readNullableExpression()
|
||||
canJump = buffer.readNullableExpression()
|
||||
reverseDriveFactor = buffer.readNullableExpression()
|
||||
strafeFactor = buffer.readNullableExpression()
|
||||
jumpExpr = buffer.readNullableExpression()
|
||||
handlingExpr = buffer.readNullableExpression()
|
||||
horizontalPitchExpr = buffer.readNullableExpression()
|
||||
mouseInputMult = buffer.readNullableExpression()
|
||||
speedExpr = buffer.readNullableExpression()
|
||||
accelerationExpr = buffer.readNullableExpression()
|
||||
staminaExpr = buffer.readNullableExpression()
|
||||
}
|
||||
}
|
||||
|
||||
class DolphinState : RidingBehaviourState() {
|
||||
var lastVelocity = ridingState(Vec3.ZERO, Side.BOTH)
|
||||
var currRollCorrectionForce = ridingState(0.0, Side.CLIENT)
|
||||
var currPitchCorrectionForce = ridingState(0.0, Side.CLIENT)
|
||||
var noInputTime = ridingState(0.0, Side.CLIENT)
|
||||
var boosting = ridingState(false, Side.BOTH)
|
||||
var boostIsToggleable = ridingState(false, Side.CLIENT)
|
||||
var speed = ridingState(0.0, Side.CLIENT)
|
||||
var hasBreached = ridingState(false, Side.CLIENT)
|
||||
|
||||
override fun encode(buffer: FriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
buffer.writeBoolean(boosting.get())
|
||||
}
|
||||
|
||||
override fun decode(buffer: FriendlyByteBuf) {
|
||||
super.decode(buffer)
|
||||
boosting.set(buffer.readBoolean(), forced = true)
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
super.reset()
|
||||
lastVelocity.set(Vec3.ZERO, forced = true)
|
||||
currRollCorrectionForce.set(0.0, forced = true)
|
||||
currPitchCorrectionForce.set(0.0, forced = true)
|
||||
noInputTime.set(0.0, forced = true)
|
||||
boosting.set(false, forced = true)
|
||||
boostIsToggleable.set(true, forced = true)
|
||||
speed.set(0.0, forced = true)
|
||||
hasBreached.set(false, forced = true)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
@@ -490,5 +669,18 @@ class DolphinState : RidingBehaviourState() {
|
||||
it.stamina.set(stamina.get(), forced = true)
|
||||
it.rideVelocity.set(rideVelocity.get(), forced = true)
|
||||
it.lastVelocity.set(lastVelocity.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.boosting.set(this.boosting.get(), forced = true)
|
||||
it.boostIsToggleable.set(this.boosting.get(), forced = true)
|
||||
it.speed.set(this.speed.get(), forced = true)
|
||||
it.hasBreached.set(this.hasBreached.get(), forced = true)
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
if (previous !is DolphinState) return false
|
||||
if (previous.boosting.get() != boosting.get()) return true
|
||||
return super.shouldSync(previous)
|
||||
}
|
||||
}
|
||||
@@ -42,9 +42,10 @@ class RideLoopSound(val ride: PokemonEntity, val soundSettings: RideSoundSetting
|
||||
var fade: Float = 0.0f
|
||||
var shouldMuffle: Boolean = soundSettings.muffleEnabled
|
||||
var rideAttenuation: RideAttenuationModel = soundSettings.attenuationModel
|
||||
var muffleAmount: Float = 1.0f // Currently slightly misleading. 1.0 is on muffle and 0.0 is full hFgain muting
|
||||
// TODO: revisit below flag once multi passenger support is enabled
|
||||
var isPassenger: Boolean = Minecraft.getInstance().player?.id == ride.controllingPassenger?.id
|
||||
var muffleAmount: Float = 1.0f // Currently slightly misleading. 1.0 is no muffle and 0.0 is full hFgain muting
|
||||
var isPassenger: Boolean = ride.passengers.any { passenger ->
|
||||
Minecraft.getInstance().player?.id == passenger.id
|
||||
}
|
||||
|
||||
init {
|
||||
this.looping = true
|
||||
|
||||
+18
-8
@@ -6,16 +6,16 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.sound;
|
||||
package com.cobblemon.mod.common.api.riding.sound
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.api.net.Encodable
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.readExpression
|
||||
import com.cobblemon.mod.common.util.writeExpression
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* Class to store data for looping sounds played during riding.
|
||||
@@ -25,31 +25,41 @@ import net.minecraft.resources.ResourceLocation;
|
||||
*/
|
||||
data class RideSoundSettings(
|
||||
val soundLocation: ResourceLocation,
|
||||
val volumeExpr: Expression = "1.0".asExpression(),
|
||||
val pitchExpr: Expression = "1.0".asExpression(),
|
||||
@SerializedName("volumeExpr") private val _volumeExpr: Expression? = "1.0".asExpression(),
|
||||
@SerializedName("pitchExpr") private val _pitchExpr: Expression? = "1.0".asExpression(),
|
||||
val playForNonPassengers: Boolean = false,
|
||||
val muffleEnabled: Boolean = false,
|
||||
val attenuationModel: RideAttenuationModel = RideAttenuationModel.NONE
|
||||
@SerializedName("attenuationModel") private val _attenuationModel: RideAttenuationModel? = RideAttenuationModel.NONE
|
||||
): Encodable {
|
||||
|
||||
//this is needed because GSON does NOT support default constructor parameters and will just happily throw nulls all over the place
|
||||
val volumeExpr: Expression
|
||||
get() = _volumeExpr ?: "1.0".asExpression()
|
||||
|
||||
val pitchExpr: Expression
|
||||
get() = _pitchExpr ?: "1.0".asExpression()
|
||||
|
||||
val attenuationModel: RideAttenuationModel
|
||||
get() = _attenuationModel ?: RideAttenuationModel.NONE
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(soundLocation)
|
||||
buffer.writeExpression(volumeExpr)
|
||||
buffer.writeExpression(pitchExpr)
|
||||
buffer.writeBoolean(playForNonPassengers)
|
||||
buffer.writeBoolean(muffleEnabled)
|
||||
buffer.writeEnum(attenuationModel)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun decode(buffer: RegistryFriendlyByteBuf): RideSoundSettings {
|
||||
|
||||
// Try to read if available, fallback to default otherwise
|
||||
val soundLocation = buffer.readResourceLocation()
|
||||
val volumeExpr = buffer.readExpression()
|
||||
val pitchExpr = buffer.readExpression()
|
||||
val playForNonPassengers = buffer.readBoolean()
|
||||
val muffleEnabled = buffer.readBoolean()
|
||||
val attenuationModel = RideAttenuationModel.EXPONENTIAL
|
||||
val attenuationModel = buffer.readEnum(RideAttenuationModel::class.java)
|
||||
|
||||
return RideSoundSettings(
|
||||
soundLocation,
|
||||
|
||||
@@ -21,11 +21,11 @@ import net.minecraft.network.chat.Component
|
||||
enum class RidingStat(
|
||||
val flavour: Flavour
|
||||
) {
|
||||
SPEED(Flavour.SWEET),
|
||||
ACCELERATION(Flavour.SPICY),
|
||||
SKILL(Flavour.DRY),
|
||||
JUMP(Flavour.BITTER),
|
||||
STAMINA(Flavour.SOUR);
|
||||
SPEED(Flavour.SWEET),
|
||||
STAMINA(Flavour.SOUR),
|
||||
JUMP(Flavour.BITTER);
|
||||
|
||||
companion object {
|
||||
fun getByName(name: String) = RidingStat.entries.firstOrNull { it.name.equals(name, ignoreCase = true) }
|
||||
|
||||
Loaded 100 of 2530 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user