forked from mirrors/cobblemon
Compare commits
769
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee8133356e | ||
|
|
d78e114b3b | ||
|
|
14ab259eee | ||
|
|
8022bfefc8 | ||
|
|
eb82d6d6a9 | ||
|
|
5be8fdd03d | ||
|
|
0a0532c8cc | ||
|
|
e1551ba70b | ||
|
|
9e05ef3e8c | ||
|
|
ea1608c8d2 | ||
|
|
045ddb30ca | ||
|
|
bf3e26874f | ||
|
|
bfbdadb519 | ||
|
|
6b2bd64eec | ||
|
|
59e300582c | ||
|
|
dd75b898a6 | ||
|
|
b3bd2e26cd | ||
|
|
475f8de2e6 | ||
|
|
096785d7ec | ||
|
|
c09c85ffd8 | ||
|
|
9f5acfbf62 | ||
|
|
d0ddf3bb11 | ||
|
|
50624a4e34 | ||
|
|
19733e53c9 | ||
|
|
277e72912c | ||
|
|
23c80bb82c | ||
|
|
143f9dba3c | ||
|
|
2fb019ce8a | ||
|
|
487b59f7b5 | ||
|
|
b06c2253ae | ||
|
|
457c2ab84d | ||
|
|
972d09e920 | ||
|
|
c0bef49e2c | ||
|
|
44c9ca3c6b | ||
|
|
d02fe990f6 | ||
|
|
53e133ab94 | ||
|
|
7d7c4fa3e2 | ||
|
|
3c5c4b9cd0 | ||
|
|
a83546a1da | ||
|
|
af6405b078 | ||
|
|
ae5021a851 | ||
|
|
ebbe4bb0e1 | ||
|
|
ebe93f531c | ||
|
|
e489bcde07 | ||
|
|
64a3f2dd28 | ||
|
|
ba15ca3d02 | ||
|
|
4b79d51f7b | ||
|
|
4c68b0f837 | ||
|
|
eaf0d7ec80 | ||
|
|
d9770b17f0 | ||
|
|
b9b4b1c2e7 | ||
|
|
13f33f935c | ||
|
|
3fbcfe842d | ||
|
|
d5503f7fa9 | ||
|
|
fd3e599636 | ||
|
|
a70a258e64 | ||
|
|
8121cb4e87 | ||
|
|
a1ef50a8ec | ||
|
|
c9c5488be7 | ||
|
|
f356dd98b2 | ||
|
|
ce7e465d99 | ||
|
|
463d341965 | ||
|
|
b5a0866b31 | ||
|
|
6b1d17ea5d | ||
|
|
49fe33f9db | ||
|
|
32ff4557a7 | ||
|
|
e4b3136bc9 | ||
|
|
0815a93ee0 | ||
|
|
9414cc096a | ||
|
|
7f7774ce90 | ||
|
|
6fde014c67 | ||
|
|
ba54401c49 | ||
|
|
2d4723d981 | ||
|
|
d91292cfb6 | ||
|
|
7156c99b92 | ||
|
|
7320e3b9cc | ||
|
|
c217d6b754 | ||
|
|
87af7745ae | ||
|
|
91005cf217 | ||
|
|
e888012b8b | ||
|
|
43fef815ab | ||
|
|
0f55a28c65 | ||
|
|
0e724118ff | ||
|
|
88e843b46c | ||
|
|
06a5e67617 | ||
|
|
7b5eb7c869 | ||
|
|
cc283b200f | ||
|
|
44779c68f5 | ||
|
|
f43cd175ce | ||
|
|
a6f725df02 | ||
|
|
538bf148bc | ||
|
|
df67a6659b | ||
|
|
84d5634b79 | ||
|
|
f313e82004 | ||
|
|
c9320ca019 | ||
|
|
58b932e35e | ||
|
|
db9634c254 | ||
|
|
9ff9329c21 | ||
|
|
b06628e6ba | ||
|
|
e74012e2a7 | ||
|
|
b5cfcd5999 | ||
|
|
eccba938c7 | ||
|
|
d6f71abb51 | ||
|
|
92f55cd23f | ||
|
|
6dbd668856 | ||
|
|
d762ee2e1b | ||
|
|
ae87bb2423 | ||
|
|
7fd7abf7d6 | ||
|
|
13367df87b | ||
|
|
2eb7dfdfa2 | ||
|
|
bbb031d484 | ||
|
|
ad7bb84d5c | ||
|
|
69eb396229 | ||
|
|
06abdaf683 | ||
|
|
79e0c2f1bf | ||
|
|
d0b50bc2a8 | ||
|
|
dbee413af5 | ||
|
|
21a1d839ae | ||
|
|
353f7f6d33 | ||
|
|
1c5863877b | ||
|
|
374a6fa321 | ||
|
|
33a350089f | ||
|
|
35b28075e4 | ||
|
|
8066823186 | ||
|
|
d2e95ceefd | ||
|
|
c36f6933aa | ||
|
|
54112f02b5 | ||
|
|
e98fc55dcb | ||
|
|
2f907988fb | ||
|
|
8b350ddb4b | ||
|
|
cbf03289a6 | ||
|
|
57db94eb00 | ||
|
|
70850d546d | ||
|
|
5fe1dfbfff | ||
|
|
c81494bec2 | ||
|
|
cd965961ae | ||
|
|
52238abe35 | ||
|
|
7594582520 | ||
|
|
a240e4e48c | ||
|
|
4dc279b664 | ||
|
|
8b371127ed | ||
|
|
b3e59b5151 | ||
|
|
abfaec1d05 | ||
|
|
e503ddbc69 | ||
|
|
643b43d8bc | ||
|
|
5c5c236d1b | ||
|
|
ee4e9dd250 | ||
|
|
27cb0dc449 | ||
|
|
7efd4e182d | ||
|
|
5781c18868 | ||
|
|
b43e5e180e | ||
|
|
be439dfc24 | ||
|
|
913d11cccc | ||
|
|
d6f208366e | ||
|
|
d82c4ed444 | ||
|
|
6ddf2ee5f9 | ||
|
|
4f537a2b37 | ||
|
|
5060762894 | ||
|
|
dbf42cb102 | ||
|
|
db75ceefa8 | ||
|
|
abaec8778b | ||
|
|
f186f03f54 | ||
|
|
ad2949ceb1 | ||
|
|
ff79a17299 | ||
|
|
ac169120d6 | ||
|
|
3f6a4bbae4 | ||
|
|
6100252b3e | ||
|
|
27be7c6eaf | ||
|
|
18b3cf4236 | ||
|
|
194528b78d | ||
|
|
8c7478d00a | ||
|
|
5c18bfdad3 | ||
|
|
0e328b2166 | ||
|
|
e9fcb80d8c | ||
|
|
01b3b2952d | ||
|
|
f28db32356 | ||
|
|
dd5f2c2821 | ||
|
|
51a65e6cec | ||
|
|
df1dc67cbb | ||
|
|
dd81579d72 | ||
|
|
a2b62bea48 | ||
|
|
c4323f79ba | ||
|
|
f1ac43c9c1 | ||
|
|
ce581de613 | ||
|
|
176b5143f5 | ||
|
|
be94e1c8ad | ||
|
|
bed13364c6 | ||
|
|
1fe392e57b | ||
|
|
4391b5947e | ||
|
|
499778dd21 | ||
|
|
9c7af03837 | ||
|
|
db740d7b5d | ||
|
|
58a4123db2 | ||
|
|
e10ccf2d1b | ||
|
|
25138beb88 | ||
|
|
d72332a3cc | ||
|
|
8986220597 | ||
|
|
e04e1db9d4 | ||
|
|
142ded7f61 | ||
|
|
a9eaeed47f | ||
|
|
de08562607 | ||
|
|
675855a22a | ||
|
|
da24a40b7e | ||
|
|
2049bf68b3 | ||
|
|
1aeb1e207b | ||
|
|
a37edfaa19 | ||
|
|
2d43bb518f | ||
|
|
e70b7a4d6e | ||
|
|
8f16e236e7 | ||
|
|
fce23a6898 | ||
|
|
483589dbec | ||
|
|
0177860ebf | ||
|
|
58b8c3676e | ||
|
|
32f6db33ae | ||
|
|
d8af56cab1 | ||
|
|
e04437f3d3 | ||
|
|
79aed2ac43 | ||
|
|
db6e604420 | ||
|
|
9b7856520f | ||
|
|
388b3891e5 | ||
|
|
50742f298d | ||
|
|
9cf4177f31 | ||
|
|
550383e275 | ||
|
|
4f0f08213d | ||
|
|
91042b5bb7 | ||
|
|
88261125c0 | ||
|
|
eb528e6534 | ||
|
|
2982ef1c30 | ||
|
|
813c949216 | ||
|
|
4ba2563e92 | ||
|
|
356f668067 | ||
|
|
85e2ccef63 | ||
|
|
8b34bb0229 | ||
|
|
c4999dc328 | ||
|
|
e484159a18 | ||
|
|
4ca850d2e7 | ||
|
|
3447caea22 | ||
|
|
f66a94649b | ||
|
|
c495e967af | ||
|
|
745bae1451 | ||
|
|
5fabc1bfaf | ||
|
|
3aa6969fba | ||
|
|
5ed69a17a4 | ||
|
|
73c9504d63 | ||
|
|
94f0796c44 | ||
|
|
63ea4c1261 | ||
|
|
63c352e5ca | ||
|
|
9a8c4a8630 | ||
|
|
0f9df28a82 | ||
|
|
886242e13b | ||
|
|
303ef76bd0 | ||
|
|
bfc6d8b446 | ||
|
|
af24f5acbe | ||
|
|
3986781a74 | ||
|
|
a16d374ff3 | ||
|
|
f224f32495 | ||
|
|
d31e65ea93 | ||
|
|
9e27c1ec85 | ||
|
|
b02edb7f78 | ||
|
|
04b0915290 | ||
|
|
aaa1338150 | ||
|
|
e2243feee1 | ||
|
|
c26bd59c21 | ||
|
|
b83f605da3 | ||
|
|
f7d2776f7d | ||
|
|
18ad6006ca | ||
|
|
46ba44545c | ||
|
|
cc446cb36e | ||
|
|
91e5ad88ab | ||
|
|
89a8a702c1 | ||
|
|
7a6a374acc | ||
|
|
60d18e6522 | ||
|
|
fc438520ba | ||
|
|
00836d8e51 | ||
|
|
e1290682f8 | ||
|
|
d82998bbeb | ||
|
|
3cb5b6fd3f | ||
|
|
e692afe86a | ||
|
|
61680b9f21 | ||
|
|
0a52e70027 | ||
|
|
8a06113b5e | ||
|
|
77397929cb | ||
|
|
402973f814 | ||
|
|
89d5660a5f | ||
|
|
8d513618d3 | ||
|
|
f52ce88756 | ||
|
|
a5b7e335db | ||
|
|
b9f46447b5 | ||
|
|
f57050e4e3 | ||
|
|
e504ba4dc7 | ||
|
|
52036deb06 | ||
|
|
08f3adceb0 | ||
|
|
115493c3a4 | ||
|
|
c160545cf1 | ||
|
|
66d9e0ad9b | ||
|
|
9e7e116528 | ||
|
|
89fa5c7837 | ||
|
|
8bd9d113dc | ||
|
|
d7718b7f3e | ||
|
|
49944a073b | ||
|
|
90183cb81e | ||
|
|
1f81c42294 | ||
|
|
a7ed6400b2 | ||
|
|
188b504575 | ||
|
|
e59d5ee6f3 | ||
|
|
ea4d170756 | ||
|
|
d7c3107635 | ||
|
|
ef8a5ce76b | ||
|
|
85d2d4aca8 | ||
|
|
72a9b82bd9 | ||
|
|
adbfa16076 | ||
|
|
8055aed5ab | ||
|
|
95c0055932 | ||
|
|
d553257060 | ||
|
|
3069ae9722 | ||
|
|
ed05d7b754 | ||
|
|
2c546f00b3 | ||
|
|
f407051c4b | ||
|
|
9f179bb573 | ||
|
|
36d8c3520d | ||
|
|
6105d15a03 | ||
|
|
1176af0641 | ||
|
|
ad958f91fe | ||
|
|
e580d5abb1 | ||
|
|
f9821ad698 | ||
|
|
ad35bf1f8a | ||
|
|
02baa3f44f | ||
|
|
1cc4be5d28 | ||
|
|
f10e617203 | ||
|
|
a77a6d21eb | ||
|
|
8dc3b7be42 | ||
|
|
58286d8bb5 | ||
|
|
e41a6d7c7d | ||
|
|
3528f0bec0 | ||
|
|
dff6c3dc5c | ||
|
|
96b234a313 | ||
|
|
c97b94dcc2 | ||
|
|
87c8ae373b | ||
|
|
ed94d2bd1a | ||
|
|
d7ac0128a7 | ||
|
|
4c35714dfe | ||
|
|
660636034b | ||
|
|
f54ed2924d | ||
|
|
a432854023 | ||
|
|
40760f2c74 | ||
|
|
ae65b2c2b1 | ||
|
|
d917e81804 | ||
|
|
67a468a006 | ||
|
|
9ad39ffd8f | ||
|
|
e96958e1b7 | ||
|
|
f168e319a2 | ||
|
|
bc23445cbc | ||
|
|
58fed2361f | ||
|
|
5e13da3e65 | ||
|
|
19a6bf28e4 | ||
|
|
03bc8a1b40 | ||
|
|
7ea2746c2b | ||
|
|
38b6322649 | ||
|
|
e37e73f530 | ||
|
|
4f2fefe2bd | ||
|
|
a1d4f499e0 | ||
|
|
e7389bf129 | ||
|
|
79a6cb5a1d | ||
|
|
9e4720b233 | ||
|
|
426cd50bb1 | ||
|
|
51bc9b9c67 | ||
|
|
9439070325 | ||
|
|
cbe9356723 | ||
|
|
60baf23baa | ||
|
|
be6d4f80d8 | ||
|
|
df660329df | ||
|
|
0d33618e83 | ||
|
|
0803906f30 | ||
|
|
2c26046a3c | ||
|
|
641c91d335 | ||
|
|
bdab3d04cb | ||
|
|
98eb43d13d | ||
|
|
ca839432aa | ||
|
|
59488974d6 | ||
|
|
df6f200b63 | ||
|
|
3f0561456f | ||
|
|
5279767829 | ||
|
|
e3f8aacd9e | ||
|
|
d381dd0d21 | ||
|
|
23f98f3a7d | ||
|
|
02f3d87bc6 | ||
|
|
2dad3c3197 | ||
|
|
ea8e70d0fa | ||
|
|
64717be354 | ||
|
|
bc9314b9d9 | ||
|
|
541f2dac40 | ||
|
|
adba69c675 | ||
|
|
ab831a2d9d | ||
|
|
faa939986a | ||
|
|
b651bf1ded | ||
|
|
8eb77cfc97 | ||
|
|
a51c8ae6d1 | ||
|
|
5809b07443 | ||
|
|
3fb4ca05fe | ||
|
|
19323143e5 | ||
|
|
89de0c6392 | ||
|
|
ddddfec219 | ||
|
|
b3c6d205ab | ||
|
|
b1e2eec156 | ||
|
|
46bb33a95f | ||
|
|
39b169082c | ||
|
|
c13e9fb35d | ||
|
|
5cc7dfa353 | ||
|
|
d4dab7b88a | ||
|
|
6b697a7be1 | ||
|
|
90fbe3be49 | ||
|
|
50dd1b154d | ||
|
|
ea3e389c41 | ||
|
|
10ed864325 | ||
|
|
0e9013c75e | ||
|
|
e54b22409a | ||
|
|
65afff1c3c | ||
|
|
230a2db160 | ||
|
|
0fdadc90e8 | ||
|
|
e03a3abe1d | ||
|
|
497f832995 | ||
|
|
5aeae2b350 | ||
|
|
ecd71fe077 | ||
|
|
9c5a04495d | ||
|
|
6d3ddece03 | ||
|
|
b60fc26a97 | ||
|
|
c1a95d0f3f | ||
|
|
99a0e83962 | ||
|
|
7572530a36 | ||
|
|
8ee0d904ed | ||
|
|
291196eda7 | ||
|
|
b94e6d65b9 | ||
|
|
7f9ed14be7 | ||
|
|
f3e6e5a94c | ||
|
|
325c4093eb | ||
|
|
2510b23401 | ||
|
|
d4b6f6e55e | ||
|
|
3d38dc2c99 | ||
|
|
6cc384abbb | ||
|
|
3fd04bcdb4 | ||
|
|
205ec6a228 | ||
|
|
943625056a | ||
|
|
06e0bbc502 | ||
|
|
d83d5cd3da | ||
|
|
6215f8afa5 | ||
|
|
60fdd3325e | ||
|
|
eedcdafcbe | ||
|
|
b39068283f | ||
|
|
34c4cc1579 | ||
|
|
b2e1558e03 | ||
|
|
34595e01a9 | ||
|
|
60b9ca7b7f | ||
|
|
83aea117c6 | ||
|
|
90bd717d7b | ||
|
|
78b348329c | ||
|
|
b64013c995 | ||
|
|
a945bad5b5 | ||
|
|
edde3b16bf | ||
|
|
85003c9829 | ||
|
|
d9133e854a | ||
|
|
ffaee877a7 | ||
|
|
186f7dbbce | ||
|
|
4ae9b25d35 | ||
|
|
b35fe23831 | ||
|
|
16165a2e68 | ||
|
|
cdec0e5640 | ||
|
|
05afe191e6 | ||
|
|
87db8aadfe | ||
|
|
36921f439c | ||
|
|
e3b5067f31 | ||
|
|
bfa2d470f0 | ||
|
|
53c0b28deb | ||
|
|
5e9208d898 | ||
|
|
23a91ec2c9 | ||
|
|
9998a328e3 | ||
|
|
9f07747cac | ||
|
|
a7998bcec5 | ||
|
|
5983b29e9c | ||
|
|
749f441913 | ||
|
|
dabbcceb9c | ||
|
|
11f8313fc3 | ||
|
|
32b813620b | ||
|
|
8147067b2e | ||
|
|
24c6544e83 | ||
|
|
a2fe6364cd | ||
|
|
b422ee4d29 | ||
|
|
a83867e153 | ||
|
|
523973d2c0 | ||
|
|
746160fa18 | ||
|
|
837ba9679b | ||
|
|
db7a319441 | ||
|
|
e284032c7e | ||
|
|
3db9dd02f2 | ||
|
|
e680ec8790 | ||
|
|
dc4f831e02 | ||
|
|
b3a00c40b5 | ||
|
|
f56fb0f50a | ||
|
|
ec732bc03d | ||
|
|
229abd4a13 | ||
|
|
7e92c17178 | ||
|
|
77aa3208a0 | ||
|
|
a03090d0da | ||
|
|
d9d5229a1f | ||
|
|
34e64da0c6 | ||
|
|
a8f78e3fd6 | ||
|
|
b2ea715195 | ||
|
|
7327ae14fd | ||
|
|
35e4aafe99 | ||
|
|
eab6fa9b40 | ||
|
|
ea4af39853 | ||
|
|
1440ed692e | ||
|
|
444657a468 | ||
|
|
8140dd773e | ||
|
|
23717a634c | ||
|
|
c966e93f22 | ||
|
|
f49c01916a | ||
|
|
98de423f91 | ||
|
|
89c2fb49cd | ||
|
|
ae05be2ce2 | ||
|
|
5f2984a3d0 | ||
|
|
de41fdf871 | ||
|
|
af491f02a2 | ||
|
|
e40a3331e2 | ||
|
|
6dac494a0f | ||
|
|
246ddc7a54 | ||
|
|
f7c560db78 | ||
|
|
bae7ad5ce1 | ||
|
|
2e59fa4f68 | ||
|
|
474cf1c6db | ||
|
|
8cd46a5a49 | ||
|
|
382bf67866 | ||
|
|
c7d93ebfc5 | ||
|
|
ef4fbecad6 | ||
|
|
96075ed34a | ||
|
|
e7cd9237f6 | ||
|
|
84e22c68c7 | ||
|
|
300f2fc53c | ||
|
|
2dfb88467b | ||
|
|
589aab3b1a | ||
|
|
e17ca3a422 | ||
|
|
0153846c7b | ||
|
|
f598f8d645 | ||
|
|
8e3e047646 | ||
|
|
551858df57 | ||
|
|
a7e3f4a7a1 | ||
|
|
94d64bb5c6 | ||
|
|
002e68b599 | ||
|
|
2ac2a57e12 | ||
|
|
b84002d142 | ||
|
|
e569acc648 | ||
|
|
7c374b6e29 | ||
|
|
3c128d9009 | ||
|
|
f5321a34c3 | ||
|
|
f96ea92a4c | ||
|
|
a1e10bf3b1 | ||
|
|
eccb4bb7af | ||
|
|
254d5e1343 | ||
|
|
a3cb3e8d9e | ||
|
|
69354fac58 | ||
|
|
d40f3599b9 | ||
|
|
531ed8b827 | ||
|
|
fc844fca9e | ||
|
|
23bfa090d2 | ||
|
|
2754bfc525 | ||
|
|
3d1aecce13 | ||
|
|
3bd5d4849c | ||
|
|
9519b51c8e | ||
|
|
c78e884e9e | ||
|
|
e98ba597dd | ||
|
|
7c93834b2c | ||
|
|
38cd081ad6 | ||
|
|
54a14ab805 | ||
|
|
633b45287c | ||
|
|
3619f42090 | ||
|
|
695453ffdb | ||
|
|
b67bfca556 | ||
|
|
0fe8caf88f | ||
|
|
1b4360f0c6 | ||
|
|
b5bb385106 | ||
|
|
3c2bf0e2b3 | ||
|
|
ac695b1092 | ||
|
|
dd7066e8fa | ||
|
|
416f003927 | ||
|
|
3be3dc3de2 | ||
|
|
a8f273737d | ||
|
|
a185805b3f | ||
|
|
defab6eaff | ||
|
|
72fc1f1fb9 | ||
|
|
f96c69ab54 | ||
|
|
d51039dbb8 | ||
|
|
6d69b91243 | ||
|
|
a12aee34d9 | ||
|
|
7bcf47fc35 | ||
|
|
f450d9d2d6 | ||
|
|
0752ab3cac | ||
|
|
96ae054aae | ||
|
|
80666530db | ||
|
|
b5e912a13f | ||
|
|
fe35a63769 | ||
|
|
17cab186d9 | ||
|
|
e00136052d | ||
|
|
b23237ca9a | ||
|
|
95d238ada0 | ||
|
|
36456bfb0d | ||
|
|
d9661c356f | ||
|
|
a66fad69cf | ||
|
|
95488ac54b | ||
|
|
19f2b71273 | ||
|
|
ba6e136a0b | ||
|
|
28ac437987 | ||
|
|
a4935f19bb | ||
|
|
c3e470f1de | ||
|
|
945f6b5f84 | ||
|
|
bf6c668850 | ||
|
|
4139337f85 | ||
|
|
25bc2dea93 | ||
|
|
9dc3884ea6 | ||
|
|
89d95c6ab8 | ||
|
|
7b70aa5a1e | ||
|
|
8191b4d9b6 | ||
|
|
84534fd041 | ||
|
|
e6e1446eb1 | ||
|
|
39fbadc4fb | ||
|
|
3c2a9c1dfd | ||
|
|
921fcbe477 | ||
|
|
c90b6c86ab | ||
|
|
3ecf25502b | ||
|
|
b808112e17 | ||
|
|
1b2d31db03 | ||
|
|
c9f8445d14 | ||
|
|
acd879ebef | ||
|
|
f6889fd8ea | ||
|
|
21ccbef6c9 | ||
|
|
9349e6a668 | ||
|
|
28a7fe6a04 | ||
|
|
18e1ed79ef | ||
|
|
aafdb7688a | ||
|
|
73b53f9702 | ||
|
|
33317a57c7 | ||
|
|
72d8f26cdb | ||
|
|
aea1afe145 | ||
|
|
0fec45b7ea | ||
|
|
af98b823aa | ||
|
|
07d9da2ca9 | ||
|
|
c2c86b2ef1 | ||
|
|
87a36020f6 | ||
|
|
22733e7117 | ||
|
|
117afcea32 | ||
|
|
3f89750ee5 | ||
|
|
d88be7ae05 | ||
|
|
24245331db | ||
|
|
d6d10d33e3 | ||
|
|
4d88c9b383 | ||
|
|
a3f597c589 | ||
|
|
2a3d88c12e | ||
|
|
9561f47337 | ||
|
|
f02c92db22 | ||
|
|
181ef5d50e | ||
|
|
870397ebb9 | ||
|
|
0ed413070d | ||
|
|
f66f5ffdf9 | ||
|
|
ca280a7b3e | ||
|
|
85284bff9d | ||
|
|
fa4d33f725 | ||
|
|
4ce42cdf6d | ||
|
|
1b8562ead8 | ||
|
|
b9d9b906fb | ||
|
|
e9d079a223 | ||
|
|
e2e42ee91c | ||
|
|
4b0208201b | ||
|
|
bec3f97733 | ||
|
|
16cfe37dac | ||
|
|
7381da863c | ||
|
|
b0902825fa | ||
|
|
31c489cba0 | ||
|
|
6a2f21d75a | ||
|
|
e2c42a57c7 | ||
|
|
8592b53216 | ||
|
|
49dcf0049f | ||
|
|
076bee923f | ||
|
|
aacdb33827 | ||
|
|
53e9ece930 | ||
|
|
87f08d9bab | ||
|
|
a50a6326af | ||
|
|
3c0eec833e | ||
|
|
5b0cfdffcb | ||
|
|
d788e0402e | ||
|
|
a347dc5e2a | ||
|
|
b2fe158a7b | ||
|
|
faab7e8df0 | ||
|
|
9070e198f1 | ||
|
|
45b2a0db4d | ||
|
|
65d39050ea | ||
|
|
5da16df5ae | ||
|
|
24fc4b05ab | ||
|
|
173902feb1 | ||
|
|
fd5f08fe84 | ||
|
|
d430ad6ca1 | ||
|
|
1309c18292 | ||
|
|
b6deebac97 | ||
|
|
83711f2dcb | ||
|
|
18e1a767af | ||
|
|
6ae3efb461 | ||
|
|
84e1934134 | ||
|
|
9aed9342ee | ||
|
|
e4d0bf39ca | ||
|
|
847fa6842e | ||
|
|
08aa4f29bd | ||
|
|
539218381b | ||
|
|
9dff39b4d7 | ||
|
|
1e578e94e5 | ||
|
|
ec35c9b9bb | ||
|
|
0deaeaeb7d | ||
|
|
ec3d9e1a56 | ||
|
|
814f84ae42 | ||
|
|
904be475ef | ||
|
|
5edecbe81b | ||
|
|
f357033404 | ||
|
|
4800ae6649 | ||
|
|
60b0690c1e | ||
|
|
82e31dd766 | ||
|
|
130e767e75 | ||
|
|
b1a1d509ec | ||
|
|
0698cf495e | ||
|
|
6047660405 | ||
|
|
aaf3495f83 | ||
|
|
ae6529c9fd | ||
|
|
45a96b2a42 | ||
|
|
4212903c67 | ||
|
|
be47dbcede | ||
|
|
9556c57c69 | ||
|
|
f72b65bfac | ||
|
|
6121700c9b | ||
|
|
91c968e743 | ||
|
|
8fed6f4823 | ||
|
|
34e4b39d75 | ||
|
|
a21c70ce46 | ||
|
|
8865a71f4c | ||
|
|
142cadc187 | ||
|
|
66f4c2531d | ||
|
|
3f8ddd5f7d | ||
|
|
c076e14cbd | ||
|
|
d724eb068b | ||
|
|
d3ac263e29 | ||
|
|
a6b7fe1f17 | ||
|
|
ec714ccb84 | ||
|
|
ab0a129ba1 | ||
|
|
a3f272b2aa | ||
|
|
77d91ed979 | ||
|
|
9907ba17c1 | ||
|
|
a6f0e19a44 | ||
|
|
7ee064b04d | ||
|
|
f2e4c735f7 | ||
|
|
5345bd321f | ||
|
|
eab8d0063b | ||
|
|
6877977234 | ||
|
|
60cb81362e | ||
|
|
088acb8e86 | ||
|
|
77f59f06cb | ||
|
|
a0882c6105 | ||
|
|
d50eb52172 | ||
|
|
5e5cadd314 | ||
|
|
75b06f82f0 | ||
|
|
2ab050aa60 | ||
|
|
6d2f645e4a | ||
|
|
9f937e29f2 | ||
|
|
abc61a96c8 | ||
|
|
b1d10822d4 | ||
|
|
09d984456d |
@@ -36,9 +36,11 @@ common/src/main/resources/assets/cobblemon/showdown
|
||||
# Generated files
|
||||
*/generated/resources/*
|
||||
/fabric/src/generated/resources/cobblemon-common.accesswidener
|
||||
/fabric/src/main/generated/*
|
||||
/.ignored/
|
||||
common/.fabric
|
||||
.kotlin/
|
||||
|
||||
# MacOS
|
||||
.DS_Store
|
||||
/common/config/*
|
||||
|
||||
+217
-19
@@ -1,53 +1,251 @@
|
||||
# Changelog
|
||||
## [1.7.4 (MONTH Xth, 2026)](#1-7-4)
|
||||
## [1.8.0 (MONTH Xth, 2026)](#1-8-0)
|
||||
|
||||
### Additions
|
||||
- Added trades for Saccharine Saplings, Hearty Grains, Chipped Pot, and Masterpiece Teacup to the Wandering Trader.
|
||||
- Added brewing recipe for Throat Spray.
|
||||
- Added Berry Tree scanning for Pokedex
|
||||
- Added Datapackable Pokedex Scanning for Block Entities
|
||||
- Added config options to award experience to fainted Pokémon for opponents they helped defeat (`awardExperienceToFaintedPokemon`) and to still award experience for defeated enemies even after battle loss/forfeit (`awardExperienceOnBattleLoss`)
|
||||
- Party Overlay Exp Gained Animation will now display the amount of EXP the Pokémon gained
|
||||
- Added a toast to notify players of how they can scroll through their party. Did you know you can hold R and scroll instead of using arrow keys??!
|
||||
- Added new layer property that makes a layer scroll in a direction. "scrolling": {"speedU": 0.1, "speedV": 0.1}
|
||||
- Added a new layer property that makes a layer scroll in a direction. "scrolling": {"speedU": 0.1, "speedV": 0.1}
|
||||
- Added missing crops to Botany Pots integration
|
||||
- Added a way to obtain Porygon by inserting the Upgrade or Dubious Disc into a data monitor
|
||||
- Added a new `cobblemon:structure`, which is a feature without any restrictions allowing the Builders to place certain structures in much more specific manner.
|
||||
- Added Configs for Infinite Use TMs and Unlocking all TMs
|
||||
- Added Type Gem blocks
|
||||
- Added Type Gem Clusters
|
||||
- Added TMs
|
||||
- Added TM Machine
|
||||
- Added Deepslate Core Block
|
||||
- Added /givetm Command
|
||||
- Added advancement triggers for learning TMs: `cobblemon:has_learn_specific_tm` (per-TM), `cobblemon:has_learn_all_tm` (all TMs learned), and `cobblemon:tms_learned` (count-based), backed by a new `totalTMLearnedCount` on player advancement data.
|
||||
- Added `cobblemon:catch_alpha_pokemon` advancement trigger and `totalAlphaCaptureCount` tracking for capturing Alpha Pokémon.
|
||||
- Added Move Dex to Pokédex
|
||||
- Added the Habitat Block, a spawner block for servers and adventure maps that controls spawning in an area.
|
||||
- Added Disk Rack for storing disks and being an audio sequencer for Noteblocks
|
||||
- Added intrinsic scaling to all mons (+-5%). Some rendering cases, such as shoulder mounting, revert to 1.0 scaling to preserve animation accuracy.
|
||||
- Added scaling to "baby" Pokémon (smaller (-20%) at level 1 → normally sized at level 10)
|
||||
- Added conditional seats for riding. Seats can be gated by molang such as "is_alpha".
|
||||
- Added Alpha Pokémon
|
||||
- Added alpha scaling (alphas are larger than their normal counterparts)
|
||||
- Alphas will always lead herds and will spawn in herds
|
||||
- Alphas spawn with two random TM moves from their learnset
|
||||
- Alpha Pokémon will always defend themselves from players that attack them
|
||||
- Added alpha level matching and stat boost (based on level)
|
||||
- Added pitch-down and reverb to alpha cries
|
||||
- Added alpha Pokémon bait effect
|
||||
- Added alpha eye rendering including eye overlay, bloom, eye trail, and particles.
|
||||
- Added cobweb block slowness immunity for spider Pokémon
|
||||
- Added 49 new habitat structures. 34 as typical Minecraft structures and 15 as structures that get placed as biome features to allow for more natural placement in generation. Structures placed as biome features will not be able to be located with the `/locate structure` command.
|
||||
- Vanilla village house template pools have been adjusted to account for the new abandoned village structure habitat. Weights have changed within the JSON file for every piece to ensure too many habitats don't generate in a single village.
|
||||
- Added 10 new ruin structures.
|
||||
- rooted_arch_ruins & crumbling_arch_ruins have been transferred over to biome features, allowing for better placement in generation.
|
||||
- All ruin structures that did not generate with a gimmighoul/gilded chest before will now generate with one.
|
||||
- All ruin structures gilded chest loot tables have been updated to include a guaranteed TM not learned naturally by any Pokémon through their level-up moveset. Each ruin gets its own specific TM, so you can now go hunting ruins to complete your move dex!
|
||||
- All fossil structures have been transferred over to biome features, allowing for better placement in generation.
|
||||
- The prehistoric_underwater_fissure & prehistoric_submerged_impact fossil structures now use the biome tag `#cobblemon:is_temperate_ocean`.
|
||||
- A new Biome tag called `#cobblemon:is_stony_beach` has been added.
|
||||
- Gimmighoul towers now generate at ground level rather than sinking one block into the ground.
|
||||
- 1 New shipwreck cove structure called the "magma_shipwreck_cove" has been added.
|
||||
- Shipwreck coves now generate slightly more frequently.
|
||||
- Added equipment loot tables for shipwreck cove enemies.
|
||||
- Maps from abandoned fishing boats leading to shipwreck coves now will skip explored chunks, allowing the player to find more shipwreck coves easier.
|
||||
- The lush shipwreck cove's phantom mob spawner has been replaced with breezes.
|
||||
- The lush shipwreck cove structure itself has been slightly cleaned up to be less noisy with the blocks it is built out of.
|
||||
- All shipwreck coves' spawners have been adjusted to give compatible mobs enchanted iron or diamond armour and weapons while in its ominous state. The armour given to the mobs will also use the automaton trim. We upped the difficulty because it was too easy now that your Pokémon can fight for you.
|
||||
- One TM of the elemental hyper beams has been added as guaranteed loot to the main treasure chest in every shipwreck cove. (Magma cove gets blast burn, lush cove gets frenzy plant, and submerged cove gets hydro cannon)
|
||||
- Added 7 new advancements
|
||||
|
||||
### New rideable Pokémon
|
||||
- Drampa
|
||||
- Archeops
|
||||
|
||||
### Pokémon Added
|
||||
- Pancham (+ Cosmetic with Black Glasses)
|
||||
- Pangoro (+ Cosmetic with Black Glasses)
|
||||
- Skitty
|
||||
- Delcatty
|
||||
- Wurmple
|
||||
- Silcoon
|
||||
- Beautifly
|
||||
- Cascoon
|
||||
- Dustox
|
||||
- Audino
|
||||
- Rockruff
|
||||
- Lycanroc
|
||||
- Snom
|
||||
- Frosmoth
|
||||
- Applin
|
||||
- Flapple
|
||||
- Appletun
|
||||
- Dipplin
|
||||
- Hydrapple
|
||||
- Sandshrew (Alolan)
|
||||
- Sandslash (Alolan)
|
||||
- Seviper
|
||||
- Zangoose
|
||||
- Stunky
|
||||
- Skuntank
|
||||
- Pawniard
|
||||
- Bisharp
|
||||
- Kingambit
|
||||
|
||||
### Model updates for the following Pokémon
|
||||
- Goomy
|
||||
- Sliggoo
|
||||
- Goodra
|
||||
- Porygon
|
||||
- Porygon2
|
||||
- Porygon-Z
|
||||
- Zubat
|
||||
- Golbat
|
||||
- Crobat
|
||||
- Pidgey
|
||||
- Pidgeotto
|
||||
- Pidgeot
|
||||
- Sandslash
|
||||
- Nidoking
|
||||
- Sableye
|
||||
- Mawile
|
||||
- Nacli
|
||||
- Naclestack
|
||||
- Garganacl
|
||||
- Skarmory
|
||||
|
||||
### Animation updates for the following Pokémon
|
||||
- Drampa
|
||||
- Archeops
|
||||
- Skarmory
|
||||
- Riolu
|
||||
- Lucario
|
||||
- Shellos
|
||||
- Skitty
|
||||
- Joltik
|
||||
- Purrloin
|
||||
- Liepard
|
||||
- Flygon
|
||||
|
||||
#### Added sounds to the following moves
|
||||
- Absorb
|
||||
- Aurora Beam
|
||||
- Double Team
|
||||
- Fire Punch
|
||||
- Fire Spin
|
||||
- Flame Charge
|
||||
- Flame Wheel
|
||||
- Giga Drain
|
||||
- Ice Punch
|
||||
- Leech Life
|
||||
- Magical Leaf
|
||||
- Mega Drain
|
||||
- Minimize
|
||||
- Poison Gas
|
||||
- Rock Throw
|
||||
- Scary Face
|
||||
- Seed bomb
|
||||
- Smokescreen
|
||||
- String Shot
|
||||
- Tail Whip
|
||||
|
||||
### Changes
|
||||
- Dragonite's mailbag cosmetic can now also be obtained with a chest in the cosmetic slot
|
||||
- Sharp Beak can now be crafted
|
||||
- Metal Alloy can now be crafted
|
||||
- Ability Capsules can now be brewed
|
||||
- The following items can now be obtained by brushing the following Pokémon:
|
||||
- Pink Wool - Wigglytuff
|
||||
- White Wool - Swablu, Altaria
|
||||
- Shed Shell - Ekans, Arbok, Dratini, Dragonair, Dunsparce, Treecko, Grovyle, Sceptile, Seviper, Scraggy, Scrafty, Helioptile, Heliolisk, Salandit, Salazzle, Sobble, Drizzile, Inteleon, Silicoblra, Sandaconda, Toxel, Toxtricity, Orthworm, Dudunsparce
|
||||
- Phantom Membrane - Dreepy, Drakloak, Dragapult
|
||||
- The following items can now be obtained by evolving the following Pokémon into any of their evolutions:
|
||||
- Turtle Scute - Squirtle, Wartortle, Turtwig, Grotle, Tirtouga, Chewtle
|
||||
- Shed Shell - Metapod, Kakuna, Ekans, Paras, Venonat, Krabby, Scyther, Kabuto, Dratini, Dragonair, Ledyba, Spinarak, Yanma, Pineco, Dunsparce, GLigar, Pupitar, Treecko, Grovyle, Silcoon, Cascoon, Surskit, Nincada, Trapinch, Vibrava, Corphish, Anorith, Bagon, Shelgon, Kricketot, Burmy, Combee, Skorupi, Swadloon, Whirlipede, Dwebble, Scraggy, Karrablast, Joltik, Larvesta, Spewpa, Clauncher, Helioptile, Charjabug, Crabrawler, Cutiefly, Dewpider, Salandit, Wimpod, Poipole, Sobble, Drizzile, Dottler, Silicobra, Toxel, Sizzlipede, Snom, Tarountula, Nymble, Rellor
|
||||
- Saccharine Leaves now drop Tart, Sweet, or Syrupy Apples, depending on their honey coating stage, instead of vanilla Apples
|
||||
- Fixed the height of the Tumblestone Bud items when held in hand, placed on head, viewed in a GUI, and placed in an Item Frame
|
||||
- Refactored all advancements, removing several deemed unnecessary. Due to this refactor, all previously obtained advancements will be reset besides the root (for picking a starter Pokémon). We are sorry for the inconvience, but it makes the tab cleaner and simplifies future advancements.
|
||||
- Restructured Botany Pots integration folder structure
|
||||
- Decrease default pastured mon wander radius to 32.
|
||||
- Factor in the weight of pokemon when calculating pushing forces between hitboxes.
|
||||
- Decrease the default pastured mon wander radius to 32.
|
||||
- Factor in the weight of Pokémon when calculating pushing forces between hitboxes.
|
||||
- Remade the Starter Selection Screen with new assets
|
||||
- Refactored berry trees to be less impactful on TPS (more performant)
|
||||
- Changed the bait effects of the following items: (Vanilla) Sweet & Glow Berries, Golden & Enchanted Golden Apples (Cobblemon) Custap, Eggant, Hopo & Micle Berries, Sweet Hearts
|
||||
- Wild Pokémon now reliably have at least one offensive move if they can have offensive moves at that level.
|
||||
- Data Monitor can accept Disks (TMs, Music Disks, Upgrade, Dubious Disk, etc.) to change what's being displayed on the screen
|
||||
- Rider positioning is now client-driven (Remove poseOffsets and offset per seat/pose) in favour of sending real-time passenger position.
|
||||
- Cleaned up seat data. Removed all now unused fields from seats in all species files. Also made locator seat names explicit in the species files (however, this doesn't break the legacy format)
|
||||
- Marks can now provide aspects.
|
||||
- Added new shiny and Alpha chance configs
|
||||
|
||||
### Fixes
|
||||
- Fixes https://gitlab.com/cable-mc/cobblemon/-/issues/1943
|
||||
- Fix occasional crash on retrieving revived fossils
|
||||
- Fixed Croagunk's walkspeed
|
||||
- Fixed Blitzle's base scale being `1` rather than the intended `0.8`
|
||||
- Fixed Magnezone's emissive layer for the shiny having the wrong name, thus displaying wrongly
|
||||
- Fixed Litleo not blinking
|
||||
- Fixed Pokédex layout for genderless variations
|
||||
- Fixed odd stacking behaviour with the poke_bait item.
|
||||
- Fixed a compatibility issue with World Game Rules which caused some log spam in specific cases.
|
||||
- Fixed improper validation of Pokémon when performing a trade.
|
||||
- Fixed issue where particle like beam can disappear when you don't see the beginning of the beam or you look away
|
||||
- Fixes https://gitlab.com/cable-mc/cobblemon/-/issues/1943
|
||||
- Fix occasional crash on retrieving revived fossils
|
||||
- Fix brushing not working on Neoforge
|
||||
- [Various Submarine Ride Fixes](https://gitlab.com/cable-mc/cobblemon/-/merge_requests/2157)
|
||||
- Fix Pastured mons despawning and running away.
|
||||
- Fix Apricorn trees crashing when other mods bonemeal it
|
||||
- [Various UFO Ride Improvements](https://gitlab.com/cable-mc/cobblemon/-/merge_requests/2156)
|
||||
- Fixed NeoForge brewing stand unexpectedly crashing users when shift clicking items within it's menu
|
||||
- Fixed NPCs switching pokemon very often during battles
|
||||
- Fixed an issue where particle like beam can disappear when you don't see the beginning of the beam, or you look away
|
||||
- Fixed an occasional crash on retrieving revived fossils
|
||||
- Fixed brushing not working on Neoforge
|
||||
- Fixed braking not working for submarines (whoops).
|
||||
- Fixed strafing not having the same friction as forward movement (funny whoops).
|
||||
- Fixed submarine's surface-water animations not playing when supposed to.
|
||||
- Fixed submarine camera freaking out when looking up or down on the surface.
|
||||
- Fixed pastured mons despawning and running away.
|
||||
- Fixed Apricorn trees crashing when other mods bonemeal it
|
||||
- Fixed the issue that caused Hover Behaviour rides without a ground style to be rendered useless upon touching the ground on a server.
|
||||
- Removed the "inAir" penalty for block destruction speed when on a hover mount.
|
||||
- Fixed horizontal and vertical collisions with hover mounts so they no longer accumulate or retain momentum
|
||||
- Fixed NeoForge brewing stand unexpectedly crashing users when shift clicking items within its menu
|
||||
- Fixed NPCs switching Pokémon very often during battles
|
||||
- Fixed Campfire pot causing crashes under certain circumstances
|
||||
- Fixed Type Gems not being consumed upon use
|
||||
- Fixed Ancient Pokéball's to their respective modifiers
|
||||
- Fixed a Server warning when recalling Pokémon
|
||||
- Fixed sliding particles incorrectly handling block collision
|
||||
- Fixed Full Heal not curing status conditions in battle
|
||||
- Fixed a rare issue with battles locking up when you are forced to switch, with the switch menu appearing instantly.
|
||||
- Fixed the combined effects of Pledge moves having broken Showdown message handling and localisation
|
||||
- Fixed rare Pokémon being far too likely to spawn multiple times in a single spawn cycle. The dev told me that the exact bug is too complicated to explain.
|
||||
- Resolved the niche issue where Structures would spawn in an incorrect location, creating very funky world gen.
|
||||
- Fixed Galarian Ponyta missing its cry.
|
||||
- Fixed various log warnings caused by redundant files and sound events.
|
||||
- Traded Pokémon will now retain the original friendship level when traded back to its OT
|
||||
|
||||
### Developer
|
||||
- Added `PokeSnackSpawnerFactory` which allows influence registration on PokeSnack encounters
|
||||
- Added `FishingSpawnerFactory` which allows influence registration on fishing encounters
|
||||
- Added `BattleFleeAttemptEvent` fired from `PokemonBattle.checkFleeAttempt`, allowing developers to intercept and control flee attempts
|
||||
- Tweaked the spawning selector interface to take a bucket function rather than a bucket directly.
|
||||
- Added a new `cobblemon:height_range` processor type to allow developers to restrict structure-spawning ranges.
|
||||
- Separated all the functions of MoLangFunctions.kt into their own files.
|
||||
|
||||
### Molang & Datapacks
|
||||
- q.item.is_enchanted()
|
||||
- q.item.has_enchantment(minecraft:sharpness, 3)
|
||||
- Added `moveset_builders` datapack folder for different styles of movesets.
|
||||
- Added `party_pools` datapack folder for defining groups of Pokémon for complex NPC party definitions.
|
||||
- Added `party_compositions` datapack folder for arranging parties on NPCs that rely on party pools.
|
||||
- Added `composed_pool` NPC party provider type which combines a party pool with a party composition to create more complex NPC parties.
|
||||
- Added basic chatter NPC Behaviour
|
||||
- Added `q.item.is_enchanted()`
|
||||
- Added `q.item.has_enchantment(minecraft:sharpness, 3)`
|
||||
- Added `has_chosen_starter` & `get_starter_uuid` as available Molang functions
|
||||
- Added `marks`, `has_mark`, & `remove_marks` as available Molang functions for pokemon
|
||||
- Added `marks`, `has_mark`, & `remove_marks` as available Molang functions for Pokémon
|
||||
- Added Data Component support for species drops, eg `"components": {"minecraft:custom_model_data": 123}`
|
||||
- Added the new `sortOrder` field to mark entries, (defaults to 0 if not specified) which controls the order in which marks are rendered on the HUD, with higher numbers being rendered on top of lower numbers
|
||||
- Added optional `order` field to starter categories. It will allow explicitly sorting categories in the starter selection screen
|
||||
- SpeciesAdditions append vs override now matches wiki documentation
|
||||
- Added `weighted_choice` as a new feature type. It requires setting weights for each choice and allows developers to simplify the process of assigning random aspect choice based on marble chance
|
||||
- Added `q.world.spawn_loot_table_items(loot_table_id, x, y, z)` which spawns the items from the loot table at the position. (Use type: "minecraft:chest" for the loot table.)
|
||||
- Added `q.pokemon.entity` to access the entity of Pokémon.
|
||||
- Flamethrower and Seismic Toss had their action effect JSON cleaned up
|
||||
- Ride sounds can now be set as exclusive to passengers
|
||||
- Ride sounds no longer play when submerged unless specified with a new setting
|
||||
- NPC Trainers can now call a `command` animation when issuing a command in battle.
|
||||
|
||||
### Particles
|
||||
|
||||
### Localization
|
||||
- Updated translations for:
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
- Some kind of particle effect for them? they're meant to have glowing eyes, so maybe some red particles on the eye locator
|
||||
--- global posers? stacked posers?
|
||||
--- red texture seems to be what some people wanted.
|
||||
|
||||
/- shoulder mounts part of it
|
||||
- omni boost
|
||||
- roar when player comes within reach, adjust levels
|
||||
|
||||
- Riding settings to control what the min scale is for a pokemon to be ridable.
|
||||
@@ -55,7 +55,7 @@ dependencies {
|
||||
// modCompileOnly(libs.flywheelFabric)
|
||||
|
||||
// Showdown
|
||||
modCompileOnly(libs.graal)
|
||||
modCompileOnly(libs.graal.core)
|
||||
|
||||
// Data Storage
|
||||
modCompileOnly(libs.bundles.mongo)
|
||||
|
||||
@@ -13,5 +13,6 @@ public interface ChannelDuck {
|
||||
void cobblemon$inverseAttenuation(float rolloffFactor);
|
||||
void cobblemon$clearFilters();
|
||||
|
||||
// void cobblemon$exponentialAttenuation(float rolloff, float refDistance, float maxDistance);
|
||||
void cobblemon$applyReverb(float decayTime, float gain, float diffusion, float density);
|
||||
void cobblemon$clearReverb();
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.mixin;
|
||||
import com.cobblemon.mod.common.duck.ChannelDuck;
|
||||
import com.mojang.blaze3d.audio.Channel;
|
||||
import org.lwjgl.openal.AL10;
|
||||
import org.lwjgl.openal.AL11;
|
||||
import org.lwjgl.openal.EXTEfx;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
@@ -21,8 +22,10 @@ public abstract class ChannelMixin implements ChannelDuck {
|
||||
@Shadow
|
||||
private int source;
|
||||
|
||||
// Id of filter applied when sound is obstructed
|
||||
// Ids of effects applied to the channel
|
||||
private int cobblemon$lowPassFilterId = 0;
|
||||
private int cobblemon$reverbEffectId = 0;
|
||||
private int cobblemon$reverbSlotId = 0;
|
||||
|
||||
@Override
|
||||
public void cobblemon$applyLowPassFilter(float gain, float hfGain) {
|
||||
@@ -67,4 +70,54 @@ public abstract class ChannelMixin implements ChannelDuck {
|
||||
cobblemon$lowPassFilterId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cobblemon$applyReverb(float decayTime, float gain, float diffusion, float density) {
|
||||
// Clean up existing reverb first if it exists
|
||||
cobblemon$clearReverb();
|
||||
|
||||
// Create the effect object
|
||||
int effectId = EXTEfx.alGenEffects();
|
||||
if (!EXTEfx.alIsEffect(effectId)) {
|
||||
System.err.println("Failed to create OpenAL reverb effect");
|
||||
return;
|
||||
}
|
||||
|
||||
EXTEfx.alEffecti(effectId, EXTEfx.AL_EFFECT_TYPE, EXTEfx.AL_EFFECT_REVERB);
|
||||
EXTEfx.alEffectf(effectId, EXTEfx.AL_REVERB_DECAY_TIME, decayTime);
|
||||
EXTEfx.alEffectf(effectId, EXTEfx.AL_REVERB_GAIN, gain);
|
||||
EXTEfx.alEffectf(effectId, EXTEfx.AL_REVERB_DIFFUSION, diffusion);
|
||||
EXTEfx.alEffectf(effectId, EXTEfx.AL_REVERB_DENSITY, density);
|
||||
|
||||
// Create auxiliary effect slot and load effect into it
|
||||
int slotId = EXTEfx.alGenAuxiliaryEffectSlots();
|
||||
if (!EXTEfx.alIsAuxiliaryEffectSlot(slotId)) {
|
||||
System.err.println("Failed to create OpenAL auxiliary effect slot");
|
||||
EXTEfx.alDeleteEffects(effectId);
|
||||
return;
|
||||
}
|
||||
|
||||
EXTEfx.alAuxiliaryEffectSloti(slotId, EXTEfx.AL_EFFECTSLOT_EFFECT, effectId);
|
||||
|
||||
// Route source through the slot — send 0, no direct filter
|
||||
AL11.alSource3i(source, EXTEfx.AL_AUXILIARY_SEND_FILTER, slotId, 0, EXTEfx.AL_FILTER_NULL);
|
||||
|
||||
cobblemon$reverbEffectId = effectId;
|
||||
cobblemon$reverbSlotId = slotId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cobblemon$clearReverb() {
|
||||
if (cobblemon$reverbSlotId != 0) {
|
||||
// Detach source from slot
|
||||
AL11.alSource3i(source, EXTEfx.AL_AUXILIARY_SEND_FILTER,
|
||||
EXTEfx.AL_EFFECTSLOT_NULL, 0, EXTEfx.AL_FILTER_NULL);
|
||||
EXTEfx.alDeleteAuxiliaryEffectSlots(cobblemon$reverbSlotId);
|
||||
cobblemon$reverbSlotId = 0;
|
||||
}
|
||||
if (cobblemon$reverbEffectId != 0) {
|
||||
EXTEfx.alDeleteEffects(cobblemon$reverbEffectId);
|
||||
cobblemon$reverbEffectId = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,15 +36,15 @@ public class ClientPacketListenerMixin {
|
||||
for (int passenger : packet.getPassengers()) {
|
||||
passengers.add(this.level.getEntity(passenger));
|
||||
}
|
||||
Arrays
|
||||
.stream(pokemon.getOccupiedSeats())
|
||||
pokemon.getOccupiedSeats().values().stream()
|
||||
.filter(x -> !passengers.contains(x))
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(Entity::stopRiding);
|
||||
|
||||
var occupiedSeats = Arrays.asList(pokemon.getOccupiedSeats());
|
||||
// Mount passengers not yet in the map
|
||||
var currentPassengers = new ArrayList<>(pokemon.getOccupiedSeats().values());
|
||||
passengers.stream()
|
||||
.filter(x -> !occupiedSeats.contains(x))
|
||||
.filter(x -> !currentPassengers.contains(x))
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(x -> x.startRiding(pokemon, true));
|
||||
ci.cancel();
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.block.entity.DiscShelfBlockEntity;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.NoteBlock;
|
||||
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(NoteBlock.class)
|
||||
public abstract class NoteBlockMixin {
|
||||
@Inject(method = "triggerEvent", at = @At("HEAD"), cancellable = true)
|
||||
private void cobblemon$triggerTMShelfSequencer(BlockState state, Level level, BlockPos pos, int type, int data, CallbackInfoReturnable<Boolean> cir) {
|
||||
final BlockPos shelfPos = pos.below();
|
||||
final BlockEntity blockEntity = level.getBlockEntity(shelfPos);
|
||||
if (!(blockEntity instanceof DiscShelfBlockEntity discShelfBlockEntity)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!level.isClientSide) {
|
||||
final BlockPos instrumentPos = shelfPos.below();
|
||||
final SoundEvent soundEvent;
|
||||
if (level.getBlockState(instrumentPos).isAir()) {
|
||||
soundEvent = SoundEvents.NOTE_BLOCK_BIT.value();
|
||||
} else {
|
||||
final NoteBlockInstrument instrument = level.getBlockState(instrumentPos).instrument();
|
||||
final Holder<SoundEvent> holder = instrument.getSoundEvent();
|
||||
soundEvent = holder.value();
|
||||
}
|
||||
discShelfBlockEntity.onNoteBlockPulse(level, pos, soundEvent);
|
||||
}
|
||||
|
||||
// Suppress vanilla note-block playback while it is on top of a TM Shelf.
|
||||
cir.setReturnValue(false);
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,10 @@ import com.cobblemon.mod.common.mixin.accessor.TrackedEntityAccessor;
|
||||
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateDriverInputPacket;
|
||||
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateOrientationPacket;
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.ClientboundUpdateRidingStatePacket;
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.ClientboundSeatAssignmentPacket;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.game.ClientGamePacketListener;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ServerEntity;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
@@ -37,6 +39,7 @@ 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.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -53,12 +56,15 @@ public abstract class ServerEntityMixin {
|
||||
private Matrix3f cobblemon$lastSentOrientation;
|
||||
@Unique
|
||||
private boolean cobblemon$lastSentActive;
|
||||
@Unique
|
||||
private List<Entity> cobblemon$lastSeatPassengers = List.of();
|
||||
|
||||
@Inject(method = "sendChanges", at = @At("TAIL"))
|
||||
private void cobblemon$sendChanges(CallbackInfo ci) {
|
||||
cobblemon$sendOrientationChanges();
|
||||
cobblemon$sendRidingStateChanges();
|
||||
cobblemon$sendDriverInput();
|
||||
cobblemon$sendOccupiedSeatsChange();
|
||||
}
|
||||
|
||||
private void cobblemon$sendRidingStateChanges() {
|
||||
@@ -101,6 +107,24 @@ public abstract class ServerEntityMixin {
|
||||
cobblemon$broadcast(new ClientboundUpdateDriverInputPacket(driverInput, entity.getId()));
|
||||
}
|
||||
|
||||
private void cobblemon$sendOccupiedSeatsChange() {
|
||||
if (!(this.entity instanceof PokemonEntity pokemon)) return;
|
||||
|
||||
List<Entity> currentPassengers = pokemon.getPassengers();
|
||||
if (currentPassengers.equals(cobblemon$lastSeatPassengers)) return;
|
||||
cobblemon$lastSeatPassengers = List.copyOf(currentPassengers);
|
||||
|
||||
pokemon.getOccupiedSeats().forEach((seat, passenger) -> {
|
||||
if (seat.getLocator() == null) return;
|
||||
if (passenger == null) return;
|
||||
cobblemon$broadcast(new ClientboundSeatAssignmentPacket(
|
||||
passenger.getId(),
|
||||
pokemon.getId(),
|
||||
seat.getLocator()
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
private void cobblemon$broadcast(NetworkPacket<?> packet) {
|
||||
if (!(entity.level() instanceof ServerLevel level)) return;
|
||||
ChunkMap chunkMap = level.getChunkSource().chunkMap;
|
||||
@@ -116,4 +140,22 @@ public abstract class ServerEntityMixin {
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "sendPairingData", at = @At("TAIL"))
|
||||
private void cobblemon$sendPairingSeatAssignments(
|
||||
ServerPlayer player,
|
||||
Consumer<Packet<ClientGamePacketListener>> consumer,
|
||||
CallbackInfo ci
|
||||
) {
|
||||
if (!(entity instanceof PokemonEntity pokemon)) return;
|
||||
if (pokemon.getOccupiedSeats().isEmpty()) return;
|
||||
|
||||
pokemon.getOccupiedSeats().forEach((seat, passenger) -> {
|
||||
if (seat.getLocator() == null) return;
|
||||
CobblemonNetwork.INSTANCE.sendPacketToPlayer(
|
||||
player,
|
||||
new ClientboundSeatAssignmentPacket(passenger.getId(), pokemon.getId(), seat.getLocator())
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import net.minecraft.client.sounds.ChannelAccess;
|
||||
import net.minecraft.client.sounds.SoundEngine;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
@@ -32,6 +33,7 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
|
||||
import java.util.Iterator;
|
||||
@@ -108,6 +110,17 @@ public abstract class SoundEngineMixin implements SoundEngineDuck {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TM Shelf can intentionally push note-block pitches above vanilla's 2.0f ceiling for octave-up playback.
|
||||
* Keep vanilla clamping for all other sounds.
|
||||
*/
|
||||
@Inject(method = "calculatePitch", at = @At("HEAD"), cancellable = true)
|
||||
private void cobblemon$allowExtendedNoteblockPitch(SoundInstance sound, CallbackInfoReturnable<Float> cir) {
|
||||
if (sound.getLocation().getPath().startsWith("block.note_block.")) {
|
||||
cir.setReturnValue(Mth.clamp(sound.getPitch(), 0.5F, 4.0F));
|
||||
}
|
||||
}
|
||||
|
||||
/** Different behavior for resuming SoundInstances while a BattleMusicInstance is being played. We do not want to resume filtered sounds. */
|
||||
@Inject(method = "resume()V", at = @At("HEAD"), cancellable = true)
|
||||
public void resume(CallbackInfo cb) {
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.world.CobblemonStructureIDs;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.levelgen.RandomState;
|
||||
import net.minecraft.world.level.levelgen.structure.Structure;
|
||||
import net.minecraft.world.level.levelgen.structure.StructurePiece;
|
||||
import net.minecraft.world.level.levelgen.structure.StructureStart;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
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.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@Mixin(Structure.class)
|
||||
public abstract class StructureMixin {
|
||||
// Define a set of structures that should not spawn below Sea Level
|
||||
@Unique
|
||||
private static final Set<ResourceLocation> RESTRICTED_STRUCTURES = new HashSet<>(Arrays.asList(
|
||||
CobblemonStructureIDs.STONJOURNER_HENGE,
|
||||
CobblemonStructureIDs.LUNA_HENGE,
|
||||
CobblemonStructureIDs.SOL_HENGE
|
||||
));
|
||||
|
||||
@Inject(method = "generate", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/levelgen/structure/StructureStart;isValid()Z"),
|
||||
cancellable = true)
|
||||
public void cobblemon$isValid(RegistryAccess registryAccess, ChunkGenerator chunkGenerator, BiomeSource biomeSource,
|
||||
RandomState randomState, StructureTemplateManager structureTemplateManager, long seed,
|
||||
ChunkPos chunkPos, int references, LevelHeightAccessor heightAccessor,
|
||||
Predicate<Holder<Biome>> validBiome,
|
||||
CallbackInfoReturnable<StructureStart> cir,
|
||||
@Local StructureStart structureStart) {
|
||||
ResourceLocation structureKey = registryAccess.registryOrThrow(Registries.STRUCTURE).getKey((Structure) (Object) this);
|
||||
if(!RESTRICTED_STRUCTURES.contains(structureKey)) {
|
||||
return;
|
||||
}
|
||||
for (StructurePiece piece : structureStart.getPieces()) {
|
||||
if (piece.getBoundingBox().minY() < chunkGenerator.getSeaLevel()) {
|
||||
cir.setReturnValue(StructureStart.INVALID_START);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,10 +9,13 @@
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon;
|
||||
import com.cobblemon.mod.common.CobblemonItemComponents;
|
||||
import com.cobblemon.mod.common.CobblemonItems;
|
||||
import com.cobblemon.mod.common.ModAPI;
|
||||
import com.cobblemon.mod.common.item.PokeBallItem;
|
||||
import com.cobblemon.mod.common.item.PokedexItem;
|
||||
import com.cobblemon.mod.common.item.WearableItem;
|
||||
import com.cobblemon.mod.common.item.components.TMMoveComponent;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.renderer.ItemModelShaper;
|
||||
@@ -54,11 +57,17 @@ public abstract class ItemRendererMixin {
|
||||
cancellable = true
|
||||
)
|
||||
private void cobblemon$overrideItemModel(ItemStack stack, ItemDisplayContext renderMode, boolean leftHanded, PoseStack matrices, MultiBufferSource multiBufferSource, int light, int overlay, BakedModel model, CallbackInfo ci) {
|
||||
boolean shouldBe2d = renderMode == ItemDisplayContext.GUI || renderMode == ItemDisplayContext.FIXED;
|
||||
boolean shouldBe2d = renderMode == ItemDisplayContext.GUI || renderMode == ItemDisplayContext.FIXED || stack.getItem() == CobblemonItems.TECHNICAL_MACHINE;
|
||||
ResourceLocation resourceLocation = null;
|
||||
if (shouldBe2d) {
|
||||
if (stack.getItem() instanceof PokeBallItem pokeBallItem) resourceLocation = pokeBallItem.getPokeBall().getModel2d();
|
||||
else if (stack.getItem() instanceof PokedexItem pokedexItem) resourceLocation = pokedexItem.getType().getItemSpritePath();
|
||||
else if (stack.getItem() == CobblemonItems.TECHNICAL_MACHINE) {
|
||||
TMMoveComponent component = stack.get(CobblemonItemComponents.TM_MOVE);
|
||||
if (component != null) {
|
||||
resourceLocation = new ResourceLocation(Cobblemon.MODID, "tm/" + component.getMove().getElementalType().showdownId() + "_tm");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (renderMode != ItemDisplayContext.HEAD && stack.getItem() instanceof WearableItem wearableItem) resourceLocation = wearableItem.getModel2d();
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.cobblemon.mod.common.duck.RidePassenger;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.cobblemon.mod.common.net.messages.server.orientation.ServerboundUpdateOrientationPacket;
|
||||
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateDriverInputPacket;
|
||||
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdatePassengerPositionPacket;
|
||||
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateRiderRotationPacket;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.client.CameraType;
|
||||
@@ -148,10 +149,22 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
|
||||
cobblemon$lastRideYRot = rideYRot;
|
||||
cobblemon$lastRideEyePos = rideEyePos;
|
||||
|
||||
CobblemonNetwork.INSTANCE.sendToServer(new ServerboundUpdateRiderRotationPacket(rideXRot, rideYRot, rideEyePos));
|
||||
CobblemonNetwork.INSTANCE.sendToServer(
|
||||
new ServerboundUpdateRiderRotationPacket(rideXRot, rideYRot, rideEyePos)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "rideTick", at = @At("TAIL"))
|
||||
private void cobblemon$updatePassengerPosition(CallbackInfo ci) {
|
||||
if (Minecraft.getInstance().player != (Object)this) return;
|
||||
if (!(this.getVehicle() instanceof PokemonEntity pokemonEntity)) return;
|
||||
if (pokemonEntity.getSeatForPassenger(this) == null) return;
|
||||
|
||||
var offset = this.position().subtract(pokemonEntity.position());
|
||||
CobblemonNetwork.INSTANCE.sendToServer(
|
||||
new ServerboundUpdatePassengerPositionPacket(this.getId(), offset)
|
||||
);
|
||||
}
|
||||
|
||||
@Unique
|
||||
@@ -217,9 +230,10 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
|
||||
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());
|
||||
Seat seat = pokemonEntity.getSeatForPassenger(this);;
|
||||
if (seat != null) {
|
||||
cir.setReturnValue(seat.getHandsBusy());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,7 @@
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.client.CobblemonBakingOverrides;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.color.block.BlockColors;
|
||||
import net.minecraft.client.renderer.block.model.BlockModel;
|
||||
import net.minecraft.client.resources.model.BlockStateModelLoader;
|
||||
@@ -18,29 +17,76 @@ import net.minecraft.client.resources.model.ModelBakery;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.client.resources.model.UnbakedModel;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.resources.Resource;
|
||||
import net.minecraft.util.profiling.ProfilerFiller;
|
||||
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.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(ModelBakery.class)
|
||||
public abstract class ModelLoaderMixin {
|
||||
|
||||
@Shadow abstract UnbakedModel getModel(ResourceLocation resourceLocation);
|
||||
|
||||
@Shadow protected abstract void registerModel(ModelResourceLocation modelId, UnbakedModel unbakedModel);
|
||||
|
||||
@Shadow protected abstract void loadItemModelAndDependencies(ResourceLocation resourceLocation);
|
||||
|
||||
@Unique
|
||||
List<String> dynamicFolders = Arrays.asList("egg", "tm");
|
||||
|
||||
@Inject(
|
||||
method = "<init>",
|
||||
at = @At("TAIL")
|
||||
method = "<init>",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/util/profiling/ProfilerFiller;popPush(Ljava/lang/String;)V",
|
||||
ordinal = 1
|
||||
)
|
||||
)
|
||||
public void loadCustomItemModels(BlockColors blockColors,
|
||||
ProfilerFiller profiler,
|
||||
Map<ResourceLocation, BlockModel> jsonUnbakedModels,
|
||||
Map<ResourceLocation, List<BlockStateModelLoader.LoadedJson>> blockStates,
|
||||
CallbackInfo ci
|
||||
) {
|
||||
/*
|
||||
* The models/item/*.json's only get loaded if there's an item in the registry that actually asks for it. It
|
||||
* naively looks for things in the root with an item ID and calls upon it, and some of those JSONs reference
|
||||
* other model JSONs and so it cascades a little bit. Used for block models but more importantly here, sprites.
|
||||
*
|
||||
* The thing about the eggs (and TMs) is, we really want the eggs to be addon-compatible in a way that doesn't restrict
|
||||
* the species that can have eggs, so we can't pre-plan all of them. This mixin looks for the things inside the
|
||||
* egg subfolder (which won't get loaded automagically) and manually inserts them into the unbaked model list so
|
||||
* Minecraft loads them for us. We'll then dynamically call these models from the ItemRendererMixin; so it's
|
||||
* hacky at the init stage and hacky at the render stage. Pretty cool in the gameplay stage though.
|
||||
*
|
||||
* - Hiro
|
||||
*/
|
||||
|
||||
dynamicFolders.forEach(folder -> {
|
||||
Map<ResourceLocation, Resource> map = Minecraft.getInstance().getResourceManager().listResources("models/item/" + folder, (t) -> true);
|
||||
map.forEach((key, value) -> {
|
||||
ResourceLocation resourceLocation = new ResourceLocation(key.getNamespace(), key.getPath().replace("models/item/", "").replace(".json", ""));
|
||||
this.loadItemModelAndDependencies(resourceLocation);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "<init>",
|
||||
at = @At("TAIL")
|
||||
)
|
||||
public void init(BlockColors blockColors,
|
||||
ProfilerFiller profiler,
|
||||
Map<ResourceLocation, BlockModel> jsonUnbakedModels,
|
||||
Map<ResourceLocation, List<BlockStateModelLoader.LoadedJson>> blockStates,
|
||||
CallbackInfo ci) {
|
||||
ProfilerFiller profiler,
|
||||
Map<ResourceLocation, BlockModel> jsonUnbakedModels,
|
||||
Map<ResourceLocation, List<BlockStateModelLoader.LoadedJson>> blockStates,
|
||||
CallbackInfo ci) {
|
||||
CobblemonBakingOverrides.INSTANCE.getModels().forEach(bakingOverride -> {
|
||||
var unbakedModel = this.getModel(bakingOverride.getModelLocation());
|
||||
this.registerModel(bakingOverride.getModelIdentifier(), unbakedModel);
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.cobblemon.mod.common.api.drop.EvolutionItemDropEntry
|
||||
import com.cobblemon.mod.common.api.drop.ItemDropEntry
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.DATA_SYNCHRONIZED
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatPools
|
||||
import com.cobblemon.mod.common.api.interaction.RequestManager
|
||||
import com.cobblemon.mod.common.api.molang.MoLangLoadedFilesCache
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
@@ -33,6 +34,7 @@ import com.cobblemon.mod.common.api.pokemon.feature.ChoiceSpeciesFeatureProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.FlagSpeciesFeatureProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.IntSpeciesFeatureProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.WeightedChoiceSpeciesFeatureProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.helditem.HeldItemProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.EvCalculator
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Generation8EvCalculator
|
||||
@@ -62,6 +64,8 @@ import com.cobblemon.mod.common.api.storage.player.adapter.DexDataMongoBackend
|
||||
import com.cobblemon.mod.common.api.storage.player.adapter.DexDataNbtBackend
|
||||
import com.cobblemon.mod.common.api.storage.player.adapter.PlayerDataJsonBackend
|
||||
import com.cobblemon.mod.common.api.storage.player.adapter.PlayerDataMongoBackend
|
||||
import com.cobblemon.mod.common.api.storage.player.adapter.TMMoveMongoBackend
|
||||
import com.cobblemon.mod.common.api.storage.player.adapter.TMMoveNbtBackend
|
||||
import com.cobblemon.mod.common.api.storage.player.factory.CachedPlayerDataStoreFactory
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonEntityTypeTags
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
@@ -89,10 +93,14 @@ import com.cobblemon.mod.common.events.StatHandler
|
||||
import com.cobblemon.mod.common.net.messages.client.settings.ServerSettingsPacket
|
||||
import com.cobblemon.mod.common.permission.LaxPermissionValidator
|
||||
import com.cobblemon.mod.common.platform.events.PlatformEvents
|
||||
import com.cobblemon.mod.common.api.pokedex.scan.BlockScanProviderRegistry
|
||||
import com.cobblemon.mod.common.pokedex.scanner.block.BerryBlockScanProvider
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.aspects.ALPHA_ASPECT
|
||||
import com.cobblemon.mod.common.pokemon.aspects.CHARACTERISTIC_RAINBOW_ASPECT
|
||||
import com.cobblemon.mod.common.pokemon.aspects.COSMETIC_SLOT_ASPECT
|
||||
import com.cobblemon.mod.common.pokemon.aspects.GENDER_ASPECT
|
||||
import com.cobblemon.mod.common.pokemon.aspects.MARK_ASPECT
|
||||
import com.cobblemon.mod.common.pokemon.aspects.SHINY_ASPECT
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.BlockClickEvolution
|
||||
import com.cobblemon.mod.common.pokemon.feature.SlowpokeTailRegrowthSpeciesFeature
|
||||
@@ -109,13 +117,7 @@ import com.cobblemon.mod.common.pokemon.properties.UnaspectPropertyType
|
||||
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
|
||||
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
|
||||
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.ifDedicatedServer
|
||||
import com.cobblemon.mod.common.util.isLaterVersion
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import com.cobblemon.mod.common.util.requestWallpapers
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.world.feature.CobblemonPlacedFeatures
|
||||
import com.cobblemon.mod.common.world.feature.ore.CobblemonOrePlacedFeatures
|
||||
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
|
||||
@@ -244,6 +246,8 @@ object Cobblemon {
|
||||
|
||||
DATA_SYNCHRONIZED.subscribe {
|
||||
storage.onPlayerDataSync(it)
|
||||
it.tmList().scheduleFullSyncFromStores(it.party(), it.pc())
|
||||
it.pokedex().scheduleFullSyncFromStores(it.party(), it.pc())
|
||||
playerDataManager.syncAllToPlayer(it)
|
||||
starterHandler.handleJoin(it)
|
||||
it.requestWallpapers()
|
||||
@@ -306,6 +310,7 @@ object Cobblemon {
|
||||
this.config.maxPokemonLevel,
|
||||
this.config.maxPokemonFriendship,
|
||||
this.config.maxDynamaxLevel,
|
||||
this.config.unlockAllMoveDexMovesByDefault
|
||||
).sendToPlayer(player)
|
||||
}
|
||||
|
||||
@@ -319,8 +324,11 @@ object Cobblemon {
|
||||
GENDER_ASPECT.register()
|
||||
COSMETIC_SLOT_ASPECT.register()
|
||||
CHARACTERISTIC_RAINBOW_ASPECT.register()
|
||||
ALPHA_ASPECT.register()
|
||||
MARK_ASPECT.register()
|
||||
|
||||
SpeciesFeatures.types["choice"] = ChoiceSpeciesFeatureProvider::class.java
|
||||
SpeciesFeatures.types["weighted_choice"] = WeightedChoiceSpeciesFeatureProvider::class.java
|
||||
SpeciesFeatures.types["flag"] = FlagSpeciesFeatureProvider::class.java
|
||||
SpeciesFeatures.types["integer"] = IntSpeciesFeatureProvider::class.java
|
||||
|
||||
@@ -345,6 +353,8 @@ object Cobblemon {
|
||||
|
||||
CobblemonEvents.POKEMON_PROPERTY_INITIALISED.emit(Unit)
|
||||
|
||||
BlockScanProviderRegistry.register(cobblemonResource("berry"), BerryBlockScanProvider)
|
||||
|
||||
CallbackHandler.setup()
|
||||
EntityCallbackHandler.setup()
|
||||
|
||||
@@ -363,6 +373,7 @@ object Cobblemon {
|
||||
val server = event.server
|
||||
MoLangLoadedFilesCache.initialize(server)
|
||||
playerDataManager = PlayerInstancedDataStoreManager().also { it.setup(server) }
|
||||
HabitatPools.onServerLoading(server)
|
||||
|
||||
val mongoClient: MongoClient?
|
||||
|
||||
@@ -375,8 +386,12 @@ object Cobblemon {
|
||||
val pokedexNbtFactory = CachedPlayerDataStoreFactory(DexDataNbtBackend())
|
||||
pokedexNbtFactory.setup(server)
|
||||
|
||||
val tmMoveFactory = CachedPlayerDataStoreFactory(TMMoveNbtBackend())
|
||||
tmMoveFactory.setup(server)
|
||||
|
||||
playerDataManager.setFactory(generalJsonFactory, PlayerInstancedDataStoreTypes.GENERAL)
|
||||
playerDataManager.setFactory(pokedexNbtFactory, PlayerInstancedDataStoreTypes.POKEDEX)
|
||||
playerDataManager.setFactory(tmMoveFactory, PlayerInstancedDataStoreTypes.TM_MOVES)
|
||||
|
||||
if (config.storageFormat == "nbt") {
|
||||
NBTStoreAdapter(pokemonStoreRoot.absolutePath, useNestedFolders = true, folderPerClass = true)
|
||||
@@ -403,8 +418,14 @@ object Cobblemon {
|
||||
val pokedexMongoFactory = CachedPlayerDataStoreFactory(DexDataMongoBackend(mongoClient, config.mongoDBDatabaseName, "PokeDexCollection"))
|
||||
pokedexMongoFactory.setup(server)
|
||||
|
||||
val tmMovesMongoFactory = CachedPlayerDataStoreFactory(
|
||||
TMMoveMongoBackend(mongoClient, config.mongoDBDatabaseName, "TMMovesCollection")
|
||||
)
|
||||
tmMovesMongoFactory.setup(server)
|
||||
|
||||
playerDataManager.setFactory(generalMongoFactory, PlayerInstancedDataStoreTypes.GENERAL)
|
||||
playerDataManager.setFactory(pokedexMongoFactory, PlayerInstancedDataStoreTypes.POKEDEX)
|
||||
playerDataManager.setFactory(tmMovesMongoFactory, PlayerInstancedDataStoreTypes.TM_MOVES)
|
||||
MongoDBStoreAdapter(mongoClient, config.mongoDBDatabaseName)
|
||||
} catch (e: ClassNotFoundException) {
|
||||
LOGGER.error("MongoDB driver not found.")
|
||||
@@ -588,6 +609,7 @@ object Cobblemon {
|
||||
this.implementation.registerCommandArgument(cobblemonResource("mark"), MarkArgumentType::class, SingletonArgumentInfo.contextFree(MarkArgumentType::mark))
|
||||
this.implementation.registerCommandArgument(cobblemonResource("transform_type"), TransformTypeArgumentType::class, SingletonArgumentInfo.contextFree(TransformTypeArgumentType::transformType))
|
||||
this.implementation.registerCommandArgument(cobblemonResource("model_part"), ModelPartArgumentType::class, SingletonArgumentInfo.contextFree(ModelPartArgumentType::modelPart))
|
||||
this.implementation.registerCommandArgument(cobblemonResource("tm"), TmArgumentType::class, SingletonArgumentInfo.contextFree(TmArgumentType::tm))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,22 +8,15 @@
|
||||
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.HealingMachineBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.PCBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.PokemonPastureBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.FossilAnalyzerBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.FossilMultiblockEntity
|
||||
import com.cobblemon.mod.common.block.entity.RestorationTankBlockEntity
|
||||
import com.cobblemon.mod.common.block.multiblock.FossilMultiblockBuilder
|
||||
import com.cobblemon.mod.common.block.entity.*
|
||||
import com.cobblemon.mod.common.block.entity.DisplayCaseBlockEntity
|
||||
import com.cobblemon.mod.common.block.habitat.HabitatBlockEntity
|
||||
import com.cobblemon.mod.common.block.multiblock.FossilMultiblockBuilder
|
||||
import com.cobblemon.mod.common.platform.PlatformRegistry
|
||||
import net.minecraft.core.Registry
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
import net.minecraft.resources.ResourceKey
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
|
||||
object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, ResourceKey<Registry<BlockEntityType<*>>>, BlockEntityType<*>>() {
|
||||
|
||||
@@ -39,6 +32,9 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
|
||||
@JvmField
|
||||
val LECTERN: BlockEntityType<LecternBlockEntity> = this.create("lectern", BlockEntityType.Builder.of(::LecternBlockEntity, CobblemonBlocks.LECTERN).build(null))
|
||||
|
||||
@JvmField
|
||||
val DISC_SHELF: BlockEntityType<DiscShelfBlockEntity> = this.create("disc_shelf", BlockEntityType.Builder.of(::DiscShelfBlockEntity, CobblemonBlocks.DISC_SHELF).build(null))
|
||||
|
||||
@JvmField
|
||||
val BERRY = this.create("berry", BlockEntityType.Builder.of(::BerryBlockEntity, *CobblemonBlocks.berries().values.toTypedArray()).build(null))
|
||||
|
||||
@@ -48,6 +44,13 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
|
||||
val SIGN: BlockEntityType<CobblemonSignBlockEntity> = this.create("sign", BlockEntityType.Builder.of(::CobblemonSignBlockEntity, CobblemonBlocks.APRICORN_SIGN, CobblemonBlocks.APRICORN_WALL_SIGN, CobblemonBlocks.SACCHARINE_SIGN, CobblemonBlocks.SACCHARINE_WALL_SIGN).build(null))
|
||||
@JvmField
|
||||
val HANGING_SIGN: BlockEntityType<CobblemonHangingSignBlockEntity> = this.create("hanging_sign", BlockEntityType.Builder.of(::CobblemonHangingSignBlockEntity, CobblemonBlocks.APRICORN_HANGING_SIGN, CobblemonBlocks.APRICORN_WALL_HANGING_SIGN, CobblemonBlocks.SACCHARINE_HANGING_SIGN, CobblemonBlocks.SACCHARINE_WALL_HANGING_SIGN).build(null))
|
||||
@JvmField
|
||||
val TM_MACHINE: BlockEntityType<TMMachineBlockEntity> = this.create("tm_machine", BlockEntityType.Builder.of(::TMMachineBlockEntity, CobblemonBlocks.TM_MACHINE).build(null))
|
||||
|
||||
@JvmField
|
||||
val HABITAT_BLOCK: BlockEntityType<HabitatBlockEntity> = this.create("habitat_block",
|
||||
BlockEntityType.Builder.of(::HabitatBlockEntity, CobblemonBlocks.HABITAT_BLOCK).build(null)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val GILDED_CHEST: BlockEntityType<GildedChestBlockEntity> = this.create("chest", BlockEntityType.Builder.of(::GildedChestBlockEntity,
|
||||
|
||||
@@ -11,20 +11,27 @@ package com.cobblemon.mod.common
|
||||
import com.cobblemon.mod.common.api.apricorn.Apricorn
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.block.*
|
||||
import com.cobblemon.mod.common.block.campfirepot.CampfireBlock
|
||||
import com.cobblemon.mod.common.block.LecternBlock
|
||||
import com.cobblemon.mod.common.block.MintBlock.MintType
|
||||
import com.cobblemon.mod.common.block.campfirepot.CampfireBlock
|
||||
import com.cobblemon.mod.common.block.campfirepot.CampfirePotBlock
|
||||
import com.cobblemon.mod.common.block.chest.GildedChestBlock
|
||||
import com.cobblemon.mod.common.block.general.BaleBlock
|
||||
import com.cobblemon.mod.common.block.general.HorizontalRotationCarpetBlock
|
||||
import com.cobblemon.mod.common.block.general.HorizontalRotationalBlock
|
||||
import com.cobblemon.mod.common.block.grower.SaccharineTreeGrower
|
||||
import com.cobblemon.mod.common.block.habitat.HabitatBlock
|
||||
import com.cobblemon.mod.common.block.sign.CobblemonHangingSignBlock
|
||||
import com.cobblemon.mod.common.block.sign.CobblemonSignBlock
|
||||
import com.cobblemon.mod.common.block.sign.CobblemonWallHangingSignBlock
|
||||
import com.cobblemon.mod.common.block.sign.CobblemonWallSignBlock
|
||||
import com.cobblemon.mod.common.block.general.HorizontalRotationCarpetBlock
|
||||
import com.cobblemon.mod.common.block.general.HorizontalRotationalBlock
|
||||
import com.cobblemon.mod.common.mixin.invoker.*
|
||||
import com.cobblemon.mod.common.block.tmmachine.TMMachineBlock
|
||||
import com.cobblemon.mod.common.mixin.invoker.BlocksInvoker
|
||||
import com.cobblemon.mod.common.mixin.invoker.DoorBlockInvoker
|
||||
import com.cobblemon.mod.common.mixin.invoker.FireBlockInvoker
|
||||
import com.cobblemon.mod.common.mixin.invoker.PressurePlateBlockInvoker
|
||||
import com.cobblemon.mod.common.mixin.invoker.StairsBlockInvoker
|
||||
import com.cobblemon.mod.common.mixin.invoker.TrapdoorBlockInvoker
|
||||
import com.cobblemon.mod.common.platform.PlatformRegistry
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.core.BlockPos
|
||||
@@ -46,7 +53,6 @@ import net.minecraft.world.level.block.state.properties.NoteBlockInstrument
|
||||
import net.minecraft.world.level.block.state.properties.WoodType
|
||||
import net.minecraft.world.level.material.MapColor
|
||||
import net.minecraft.world.level.material.PushReaction
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
|
||||
@Suppress("SameParameterValue", "HasPlatformType", "MemberVisibilityCanBePrivate", "unused")
|
||||
object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<Block>>, Block>() {
|
||||
@@ -121,19 +127,19 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
val APRICORN_PLANKS = this.create("apricorn_planks",
|
||||
Block(
|
||||
BlockBehaviour.Properties.of().mapColor(MapColor.DIRT).instrument(NoteBlockInstrument.BASS)
|
||||
.strength(2.0f, 3.0f).sound(SoundType.WOOD)
|
||||
.strength(2.0F, 3.0F).sound(SoundType.WOOD)
|
||||
)
|
||||
)
|
||||
@JvmField
|
||||
val APRICORN_LEAVES = leaves("apricorn_leaves")
|
||||
@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)))
|
||||
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).ignitedByLava().forceSolidOn()))
|
||||
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
|
||||
val APRICORN_PRESSURE_PLATE = this.create("apricorn_pressure_plate", PressurePlateBlockInvoker.`cobblemon$create`(APRICORN_BLOCK_SET_TYPE, BlockBehaviour.Properties.of().mapColor(APRICORN_PLANKS.defaultMapColor()).noCollission().strength(0.5f).sound(SoundType.WOOD)))
|
||||
val APRICORN_PRESSURE_PLATE = this.create("apricorn_pressure_plate", PressurePlateBlockInvoker.`cobblemon$create`(APRICORN_BLOCK_SET_TYPE, BlockBehaviour.Properties.of().mapColor(APRICORN_PLANKS.defaultMapColor()).noCollission().strength(0.5F).sound(SoundType.WOOD)))
|
||||
@JvmField
|
||||
val APRICORN_SIGN = this.create("apricorn_sign", CobblemonSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_SIGN), APRICORN_WOOD_TYPE))
|
||||
|
||||
@@ -150,7 +156,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val APRICORN_WALL_HANGING_SIGN = this.create("apricorn_wall_hanging_sign", CobblemonWallHangingSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_HANGING_SIGN), APRICORN_WOOD_TYPE))
|
||||
@JvmField
|
||||
val APRICORN_SLAB = this.create("apricorn_slab", SlabBlock(BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
|
||||
val APRICORN_SLAB = this.create("apricorn_slab", SlabBlock(BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).strength(2.0F, 3.0F).sound(SoundType.WOOD)))
|
||||
@JvmField
|
||||
val APRICORN_STAIRS = this.create("apricorn_stairs", StairsBlockInvoker.`cobblemon$create`(APRICORN_PLANKS.defaultBlockState(), BlockBehaviour.Properties.ofFullCopy(APRICORN_PLANKS)))
|
||||
@JvmField
|
||||
@@ -187,12 +193,12 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val STRIPPED_SACCHARINE_WOOD = this.create("stripped_saccharine_wood", RotatedPillarBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_LIGHT_BLUE).instrument(NoteBlockInstrument.BASS).strength(2.0F).sound(SoundType.WOOD).ignitedByLava())) //log("stripped_saccharine_log")
|
||||
@JvmField
|
||||
val SACCHARINE_PLANKS = this.create("saccharine_planks", Block(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_LIGHT_BLUE).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
|
||||
val SACCHARINE_PLANKS = this.create("saccharine_planks", Block(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_LIGHT_BLUE).instrument(NoteBlockInstrument.BASS).strength(2.0F, 3.0F).sound(SoundType.WOOD)))
|
||||
@JvmField
|
||||
val SACCHARINE_LEAVES = this.create("saccharine_leaves", SaccharineLeafBlock(
|
||||
BlockBehaviour.Properties.of().mapColor(
|
||||
MapColor.PLANT
|
||||
).strength(0.2f).randomTicks().sound(SoundType.GRASS).noOcclusion()
|
||||
).strength(0.2F).randomTicks().sound(SoundType.GRASS).noOcclusion()
|
||||
.isValidSpawn { _: BlockState?, _: BlockGetter?, _: BlockPos?, entity: EntityType<*>? ->
|
||||
entity === EntityType.OCELOT || entity === EntityType.PARROT
|
||||
}.isSuffocating { _: BlockState?, _: BlockGetter?, _: BlockPos? ->
|
||||
@@ -204,13 +210,13 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
false
|
||||
}))
|
||||
@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)))
|
||||
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).ignitedByLava().forceSolidOn()))
|
||||
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
|
||||
val SACCHARINE_PRESSURE_PLATE = this.create("saccharine_pressure_plate", PressurePlateBlockInvoker.`cobblemon$create`(SACCHARINE_BLOCK_SET_TYPE, BlockBehaviour.Properties.of().mapColor(SACCHARINE_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).noCollission().strength(0.5f).sound(SoundType.WOOD)))
|
||||
val SACCHARINE_PRESSURE_PLATE = this.create("saccharine_pressure_plate", PressurePlateBlockInvoker.`cobblemon$create`(SACCHARINE_BLOCK_SET_TYPE, BlockBehaviour.Properties.of().mapColor(SACCHARINE_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).noCollission().strength(0.5F).sound(SoundType.WOOD)))
|
||||
@JvmField
|
||||
val SACCHARINE_SIGN = this.create("saccharine_sign", CobblemonSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_SIGN), SACCHARINE_WOOD_TYPE))
|
||||
@JvmField
|
||||
@@ -220,7 +226,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val SACCHARINE_WALL_HANGING_SIGN = this.create("saccharine_wall_hanging_sign", CobblemonWallHangingSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_HANGING_SIGN), SACCHARINE_WOOD_TYPE))
|
||||
@JvmField
|
||||
val SACCHARINE_SLAB = this.create("saccharine_slab", SlabBlock(BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
|
||||
val SACCHARINE_SLAB = this.create("saccharine_slab", SlabBlock(BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).strength(2.0F, 3.0F).sound(SoundType.WOOD)))
|
||||
@JvmField
|
||||
val SACCHARINE_STAIRS = this.create("saccharine_stairs", StairsBlockInvoker.`cobblemon$create`(SACCHARINE_PLANKS.defaultBlockState(), BlockBehaviour.Properties.ofFullCopy(SACCHARINE_PLANKS)))
|
||||
@JvmField
|
||||
@@ -249,31 +255,31 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
val POKE_SNACK = this.create("poke_snack", PokeSnackBlock(BlockBehaviour.Properties.of().forceSolidOn().strength(0.5F).sound(SoundType.WOOL).pushReaction(PushReaction.DESTROY).lightLevel { if (it.getValue(BlockStateProperties.LIT)) 3 else 0 }, true))
|
||||
|
||||
@JvmField
|
||||
val TUMBLESTONE_CLUSTER = tumblestoneBlock("tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
|
||||
val TUMBLESTONE_CLUSTER = tumblestoneBlock("tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null)
|
||||
@JvmField
|
||||
val LARGE_BUDDING_TUMBLESTONE = tumblestoneBlock("large_budding_tumblestone", GrowableStoneBlock.STAGE_2, 5, 3, TUMBLESTONE_CLUSTER)
|
||||
val LARGE_BUDDING_TUMBLESTONE = tumblestoneBlock("large_budding_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, TUMBLESTONE_CLUSTER)
|
||||
@JvmField
|
||||
val MEDIUM_BUDDING_TUMBLESTONE = tumblestoneBlock("medium_budding_tumblestone", GrowableStoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_TUMBLESTONE)
|
||||
val MEDIUM_BUDDING_TUMBLESTONE = tumblestoneBlock("medium_budding_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_TUMBLESTONE)
|
||||
@JvmField
|
||||
val SMALL_BUDDING_TUMBLESTONE = tumblestoneBlock("small_budding_tumblestone", GrowableStoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_TUMBLESTONE)
|
||||
val SMALL_BUDDING_TUMBLESTONE = tumblestoneBlock("small_budding_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_TUMBLESTONE)
|
||||
|
||||
@JvmField
|
||||
val SKY_TUMBLESTONE_CLUSTER = tumblestoneBlock("sky_tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
|
||||
val SKY_TUMBLESTONE_CLUSTER = tumblestoneBlock("sky_tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null)
|
||||
@JvmField
|
||||
val LARGE_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("large_budding_sky_tumblestone", GrowableStoneBlock.STAGE_2, 5, 3, SKY_TUMBLESTONE_CLUSTER)
|
||||
val LARGE_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("large_budding_sky_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, SKY_TUMBLESTONE_CLUSTER)
|
||||
@JvmField
|
||||
val MEDIUM_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("medium_budding_sky_tumblestone", GrowableStoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_SKY_TUMBLESTONE)
|
||||
val MEDIUM_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("medium_budding_sky_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_SKY_TUMBLESTONE)
|
||||
@JvmField
|
||||
val SMALL_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("small_budding_sky_tumblestone", GrowableStoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_SKY_TUMBLESTONE)
|
||||
val SMALL_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("small_budding_sky_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_SKY_TUMBLESTONE)
|
||||
|
||||
@JvmField
|
||||
val BLACK_TUMBLESTONE_CLUSTER = tumblestoneBlock("black_tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
|
||||
val BLACK_TUMBLESTONE_CLUSTER = tumblestoneBlock("black_tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null)
|
||||
@JvmField
|
||||
val LARGE_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("large_budding_black_tumblestone", GrowableStoneBlock.STAGE_2, 5, 3, BLACK_TUMBLESTONE_CLUSTER)
|
||||
val LARGE_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("large_budding_black_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, BLACK_TUMBLESTONE_CLUSTER)
|
||||
@JvmField
|
||||
val MEDIUM_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("medium_budding_black_tumblestone", GrowableStoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_BLACK_TUMBLESTONE)
|
||||
val MEDIUM_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("medium_budding_black_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_BLACK_TUMBLESTONE)
|
||||
@JvmField
|
||||
val SMALL_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("small_budding_black_tumblestone", GrowableStoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_BLACK_TUMBLESTONE)
|
||||
val SMALL_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("small_budding_black_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_BLACK_TUMBLESTONE)
|
||||
|
||||
@JvmField
|
||||
val TUMBLESTONE_BLOCK = this.create("tumblestone_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_ORANGE).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
|
||||
@@ -282,6 +288,87 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val BLACK_TUMBLESTONE_BLOCK = this.create("black_tumblestone_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
|
||||
|
||||
@JvmField
|
||||
val TYPE_GEM_CORE = this.create("deepslate_crystal_core", TypeGemCoreBlock(BlockBehaviour.Properties.of().strength(4.0F, 10.0F).requiresCorrectToolForDrops()))
|
||||
|
||||
private val typeGemBlocks = mutableMapOf<ResourceLocation, Block>()
|
||||
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_NORMAL = typeGemBlock("normal_gem_block")
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_FIRE = typeGemBlock("fire_gem_block")
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_WATER = typeGemBlock("water_gem_block")
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_ELECTRIC = typeGemBlock("electric_gem_block")
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_GRASS = typeGemBlock("grass_gem_block")
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_ICE = typeGemBlock("ice_gem_block")
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_FIGHTING = typeGemBlock("fighting_gem_block")
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_POISON = typeGemBlock("poison_gem_block")
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_GROUND = typeGemBlock("ground_gem_block")
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_FLYING = typeGemBlock("flying_gem_block")
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_PSYCHIC = typeGemBlock("psychic_gem_block")
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_BUG = typeGemBlock("bug_gem_block")
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_ROCK = typeGemBlock("rock_gem_block")
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_GHOST = typeGemBlock("ghost_gem_block")
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_DRAGON = typeGemBlock("dragon_gem_block")
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_DARK = typeGemBlock("dark_gem_block")
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_STEEL = typeGemBlock("steel_gem_block")
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_FAIRY = typeGemBlock("fairy_gem_block")
|
||||
|
||||
private val typeGemClusters = mutableMapOf<ResourceLocation, TypeGemClusterBlock>()
|
||||
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_NORMAL = typeGemCluster("normal_gem_cluster", TYPE_GEM_BLOCK_NORMAL, cobblemonResource("normal_gem"))
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_FIRE = typeGemCluster("fire_gem_cluster", TYPE_GEM_BLOCK_FIRE, cobblemonResource("fire_gem"))
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_WATER = typeGemCluster("water_gem_cluster", TYPE_GEM_BLOCK_WATER, cobblemonResource("water_gem"))
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_ELECTRIC = typeGemCluster("electric_gem_cluster", TYPE_GEM_BLOCK_ELECTRIC, cobblemonResource("electric_gem"))
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_GRASS = typeGemCluster("grass_gem_cluster", TYPE_GEM_BLOCK_GRASS, cobblemonResource("grass_gem"))
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_ICE = typeGemCluster("ice_gem_cluster", TYPE_GEM_BLOCK_ICE, cobblemonResource("ice_gem"))
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_FIGHTING = typeGemCluster("fighting_gem_cluster", TYPE_GEM_BLOCK_FIGHTING, cobblemonResource("fighting_gem"))
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_POISON = typeGemCluster("poison_gem_cluster", TYPE_GEM_BLOCK_POISON, cobblemonResource("poison_gem"))
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_GROUND = typeGemCluster("ground_gem_cluster", TYPE_GEM_BLOCK_GROUND, cobblemonResource("ground_gem"))
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_FLYING = typeGemCluster("flying_gem_cluster", TYPE_GEM_BLOCK_FLYING, cobblemonResource("flying_gem"))
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_PSYCHIC = typeGemCluster("psychic_gem_cluster", TYPE_GEM_BLOCK_PSYCHIC, cobblemonResource("psychic_gem"))
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_BUG = typeGemCluster("bug_gem_cluster", TYPE_GEM_BLOCK_BUG, cobblemonResource("bug_gem"))
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_ROCK = typeGemCluster("rock_gem_cluster", TYPE_GEM_BLOCK_ROCK, cobblemonResource("rock_gem"))
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_GHOST = typeGemCluster("ghost_gem_cluster", TYPE_GEM_BLOCK_GHOST, cobblemonResource("ghost_gem"))
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_DRAGON = typeGemCluster("dragon_gem_cluster", TYPE_GEM_BLOCK_DRAGON, cobblemonResource("dragon_gem"))
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_DARK = typeGemCluster("dark_gem_cluster", TYPE_GEM_BLOCK_DARK, cobblemonResource("dark_gem"))
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_STEEL = typeGemCluster("steel_gem_cluster", TYPE_GEM_BLOCK_STEEL, cobblemonResource("steel_gem"))
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_FAIRY = typeGemCluster("fairy_gem_cluster", TYPE_GEM_BLOCK_FAIRY, cobblemonResource("fairy_gem"))
|
||||
|
||||
@JvmField
|
||||
val POLISHED_TUMBLESTONE = this.create("polished_tumblestone", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_ORANGE).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
|
||||
@JvmField
|
||||
@@ -450,6 +537,18 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val HABITAT_BLOCK = this.create(
|
||||
name = "habitat_block",
|
||||
entry = HabitatBlock(
|
||||
properties = BlockBehaviour.Properties.of()
|
||||
.mapColor(MapColor.STONE)
|
||||
.strength(1.5F, Blocks.OBSIDIAN.explosionResistance)
|
||||
.pushReaction(PushReaction.BLOCK)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@JvmField
|
||||
val CAMPFIRE = create("campfire", CampfireBlock(BlockBehaviour.Properties.of().sound(SoundType.WOOD).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.PODZOL).strength(2.0F).lightLevel{14}, false))
|
||||
|
||||
@@ -459,7 +558,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val RELIC_COIN_POUCH = create("relic_coin_pouch", CoinPouchBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.RELIC_COIN_POUCH_SOUNDS).pushReaction(PushReaction.DESTROY).noOcclusion(), true))
|
||||
@JvmField
|
||||
val RELIC_COIN_SACK = create("relic_coin_sack", CoinPouchBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.RELIC_COIN_SACK_SOUNDS).pushReaction(PushReaction.DESTROY).strength(0.4f), false))
|
||||
val RELIC_COIN_SACK = create("relic_coin_sack", CoinPouchBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.RELIC_COIN_SACK_SOUNDS).pushReaction(PushReaction.DESTROY).strength(0.4F), false))
|
||||
|
||||
@JvmField
|
||||
val GILDED_CHEST = create("gilded_chest", GildedChestBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.CHEST).noOcclusion().sound(CobblemonSounds.GILDED_CHEST_SOUNDS), GildedChestBlock.Type.RED))
|
||||
@@ -512,14 +611,14 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
val PINK_PLAQUE = create("pink_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
|
||||
|
||||
@JvmField
|
||||
val CLEANSE_TAG = create("cleanse_tag", WallAttachedStackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.WOOD).mapColor(MapColor.NONE).pushReaction(PushReaction.DESTROY).noOcclusion().noCollission(), WallAttachedStackableItemBlock.WallStackableItemBlockType.TAG))
|
||||
val CLEANSE_TAG = create("cleanse_tag", WallAttachedStackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_PAPER_SMALL_SOUNDS).mapColor(MapColor.NONE).pushReaction(PushReaction.DESTROY).noOcclusion().noCollission(), WallAttachedStackableItemBlock.WallStackableItemBlockType.TAG))
|
||||
@JvmField
|
||||
val SPELL_TAG = create("spell_tag", WallAttachedStackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.WOOD).mapColor(MapColor.NONE).pushReaction(PushReaction.DESTROY).noOcclusion().noCollission(), WallAttachedStackableItemBlock.WallStackableItemBlockType.TAG))
|
||||
val SPELL_TAG = create("spell_tag", WallAttachedStackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_PAPER_SMALL_SOUNDS).mapColor(MapColor.NONE).pushReaction(PushReaction.DESTROY).noOcclusion().noCollission(), WallAttachedStackableItemBlock.WallStackableItemBlockType.TAG))
|
||||
|
||||
@JvmField
|
||||
val BLUNDER_POLICY = create("blunder_policy", WallAttachedDirectionalShapeBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion().noCollission(), 14, 12, 1))
|
||||
val BLUNDER_POLICY = create("blunder_policy", WallAttachedDirectionalShapeBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_PAPER_LARGE_SOUNDS).mapColor(MapColor.NONE).noOcclusion().noCollission(), 14, 12, 1))
|
||||
@JvmField
|
||||
val WEAKNESS_POLICY = create("weakness_policy", WallAttachedDirectionalShapeBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion().noCollission(), 14, 12, 1))
|
||||
val WEAKNESS_POLICY = create("weakness_policy", WallAttachedDirectionalShapeBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_PAPER_LARGE_SOUNDS).mapColor(MapColor.NONE).noOcclusion().noCollission(), 14, 12, 1))
|
||||
|
||||
@JvmField
|
||||
val FULL_HEAL = create("full_heal", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
|
||||
@@ -574,25 +673,43 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val MONITOR = create("monitor", MonitorBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).requiresCorrectToolForDrops().strength(5.0F, 6.0F).lightLevel { if (it.getValue(MonitorBlock.SCREEN) != MonitorBlock.MonitorScreen.OFF) 13 else 0 }))
|
||||
@JvmField
|
||||
val DAMAGED_MONITOR = create("damaged_monitor", DamagedMonitorBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).requiresCorrectToolForDrops().strength(5.0F, 6.0F).lightLevel { when (it.getValue(DamagedMonitorBlock.SCREEN)) { DamagedMonitorBlock.BrokenMonitorScreen.GLITCHING -> 10; else -> 0 } }))
|
||||
@JvmField
|
||||
val FOSSIL_ANALYZER = create("fossil_analyzer", FossilAnalyzerBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).requiresCorrectToolForDrops().strength(5.0F, 6.0F).noOcclusion()))
|
||||
@JvmField
|
||||
val RESTORATION_TANK = create("restoration_tank", RestorationTankBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.GLASS).pushReaction(PushReaction.BLOCK).requiresCorrectToolForDrops().strength(5.0F, 6.0F).noOcclusion()))
|
||||
|
||||
@JvmField
|
||||
val HEALING_MACHINE = create("healing_machine", HealingMachineBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).strength(2f).noOcclusion().lightLevel { if (it.getValue(HealingMachineBlock.CHARGE_LEVEL) >= HealingMachineBlock.MAX_CHARGE_LEVEL) 12 else 5 }))
|
||||
val HEALING_MACHINE = create("healing_machine", HealingMachineBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).strength(2F).noOcclusion().lightLevel { if (it.getValue(HealingMachineBlock.CHARGE_LEVEL) >= HealingMachineBlock.MAX_CHARGE_LEVEL) 12 else 5 }))
|
||||
|
||||
@JvmField
|
||||
val PC = create("pc", PCBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).strength(2F).noOcclusion().lightLevel { if ((it.getValue(PCBlock.ON) as Boolean) && (it.getValue(PCBlock.PART) == PCBlock.PCPart.TOP)) 13 else 0 }))
|
||||
|
||||
val TM_MACHINE = create(
|
||||
"tm_machine",
|
||||
TMMachineBlock(
|
||||
BlockBehaviour.Properties.of()
|
||||
.mapColor(MapColor.COLOR_GREEN)
|
||||
.sound(SoundType.METAL)
|
||||
.pushReaction(PushReaction.BLOCK)
|
||||
.strength(2F)
|
||||
.noOcclusion()
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@JvmField
|
||||
val LECTERN = create("lectern", LecternBlock(BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).sound(SoundType.WOOD).strength(2.5F).ignitedByLava().lightLevel { if (it.getValue(LecternBlock.EMIT_LIGHT) as Boolean) 13 else 0 }))
|
||||
|
||||
@JvmField
|
||||
val DISC_SHELF = create("disc_shelf", DiscShelfBlock(BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).sound(SoundType.METAL).strength(2.5F)))
|
||||
|
||||
@JvmField
|
||||
val DISPLAY_CASE = create("display_case", DisplayCaseBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.DISPLAY_CASE_SOUNDS).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.STONE).strength(0.3F).requiresCorrectToolForDrops()))
|
||||
|
||||
// TODO after 1.7
|
||||
// @JvmField
|
||||
// val INCENSE_SWEET = create("incense_sweet", SweetIncenseBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.DISPLAY_CASE_SOUNDS).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.STONE).strength(0.3f)))
|
||||
// val INCENSE_SWEET = create("incense_sweet", SweetIncenseBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.DISPLAY_CASE_SOUNDS).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.STONE).strength(0.3F)))
|
||||
|
||||
@JvmField
|
||||
val PASTURE = create("pasture", PastureBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_BROWN).sound(SoundType.WOOD).strength(2F).noOcclusion().pushReaction(PushReaction.BLOCK).lightLevel { if ((it.getValue(PastureBlock.ON) as Boolean) && (it.getValue(PastureBlock.PART) == PastureBlock.PasturePart.TOP)) 13 else 0 }))
|
||||
@@ -890,4 +1007,28 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
val block = BlocksInvoker.createLeavesBlock(SoundType.GRASS)
|
||||
return this.create(name, block)
|
||||
}
|
||||
|
||||
private fun typeGemBlock(name: String): Block {
|
||||
val typeGemBlock = this.create(name, Block(BlockBehaviour.Properties.of().strength(3.0F, 6.0F).sound(CobblemonSounds.TYPE_GEM_BLOCK_SOUNDS)))
|
||||
|
||||
val identifier = cobblemonResource(name)
|
||||
typeGemBlocks[identifier] = typeGemBlock
|
||||
|
||||
return typeGemBlock
|
||||
}
|
||||
|
||||
fun typeGemBlocks() = typeGemBlocks.toMap()
|
||||
|
||||
private fun typeGemCluster(name: String, gemBlock: Block, dropItemId: ResourceLocation): Block {
|
||||
val gemClusterBlock = this.create(name, TypeGemClusterBlock(BlockBehaviour.Properties.of().strength(2.0F, 3.0F).sound(CobblemonSounds.TYPE_GEM_CLUSTER_SOUNDS).pushReaction(PushReaction.DESTROY), gemBlock, dropItemId))
|
||||
|
||||
TypeGemClusterBlock.gemToClusterMap[gemBlock] = gemClusterBlock
|
||||
|
||||
val identifier = cobblemonResource(name)
|
||||
typeGemClusters[identifier] = gemClusterBlock
|
||||
|
||||
return gemClusterBlock
|
||||
}
|
||||
|
||||
fun typeGemClusters() = typeGemClusters.toMap()
|
||||
}
|
||||
@@ -71,10 +71,11 @@ object CobblemonCommands {
|
||||
MarkGiveAllCommand.register(dispatcher)
|
||||
PcSearchCommand.register(dispatcher)
|
||||
PcTakeCommand.register(dispatcher)
|
||||
GiveTmCommand.register(dispatcher)
|
||||
TmCommand.register(dispatcher)
|
||||
|
||||
// Possibly lock down registration if and only if under dev environment or running in an environment
|
||||
// with a certain system environment variable set
|
||||
CobblemonInfoCommand.register(dispatcher)
|
||||
CalculateSeatPositionCommand.register(dispatcher)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
import net.minecraft.server.packs.resources.PreparableReloadListener
|
||||
import net.minecraft.tags.TagKey
|
||||
import net.minecraft.world.inventory.MenuType
|
||||
import net.minecraft.world.level.GameRules
|
||||
import net.minecraft.world.level.ItemLike
|
||||
import net.minecraft.world.level.biome.Biome
|
||||
|
||||
@@ -69,6 +69,12 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
|
||||
.networkSynchronized(FoodColourComponent.PACKET_CODEC)
|
||||
.build())
|
||||
|
||||
@JvmField
|
||||
val TM_MOVE: DataComponentType<TMMoveComponent> = create("tm_move", DataComponentType.builder<TMMoveComponent>()
|
||||
.persistent(TMMoveComponent.CODEC)
|
||||
.networkSynchronized(TMMoveComponent.PACKET_CODEC)
|
||||
.build())
|
||||
|
||||
@JvmField
|
||||
val INGREDIENT: DataComponentType<IngredientComponent> = create("ingredient", DataComponentType.builder<IngredientComponent>()
|
||||
.persistent(IngredientComponent.CODEC)
|
||||
@@ -101,6 +107,7 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:flavour"), FLAVOUR)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:ride_boosts"), RIDE_BOOST)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:food_colour"), FOOD_COLOUR)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:tm_move"), TM_MOVE)
|
||||
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)
|
||||
|
||||
@@ -21,8 +21,8 @@ import com.cobblemon.mod.common.api.text.gray
|
||||
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.client.pokedex.PokedexType
|
||||
import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
|
||||
import com.cobblemon.mod.common.item.*
|
||||
import com.cobblemon.mod.common.item.armor.CobblemonArmorTrims
|
||||
@@ -35,7 +35,6 @@ import com.cobblemon.mod.common.item.berry.HealingBerryItem
|
||||
import com.cobblemon.mod.common.item.berry.PPRestoringBerryItem
|
||||
import com.cobblemon.mod.common.item.berry.PortionHealingBerryItem
|
||||
import com.cobblemon.mod.common.item.berry.StatusCuringBerryItem
|
||||
import com.cobblemon.mod.common.item.interactive.PonigiriItem
|
||||
import com.cobblemon.mod.common.item.food.SinisterTeaItem
|
||||
import com.cobblemon.mod.common.item.interactive.*
|
||||
import com.cobblemon.mod.common.item.interactive.ability.AbilityChangeItem
|
||||
@@ -453,10 +452,16 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val MONITOR = blockItem("monitor", CobblemonBlocks.MONITOR)
|
||||
@JvmField
|
||||
val DAMAGED_MONITOR = blockItem("damaged_monitor", CobblemonBlocks.DAMAGED_MONITOR)
|
||||
@JvmField
|
||||
val HEALING_MACHINE = blockItem("healing_machine", CobblemonBlocks.HEALING_MACHINE, Rarity.UNCOMMON)
|
||||
@JvmField
|
||||
val PC = blockItem("pc", CobblemonBlocks.PC)
|
||||
@JvmField
|
||||
val TM_MACHINE = blockItem("tm_machine", CobblemonBlocks.TM_MACHINE)
|
||||
@JvmField
|
||||
val DISC_SHELF = blockItem("disc_shelf", CobblemonBlocks.DISC_SHELF)
|
||||
@JvmField
|
||||
val PASTURE = blockItem("pasture", CobblemonBlocks.PASTURE)
|
||||
@JvmField
|
||||
val DISPLAY_CASE = blockItem("display_case", CobblemonBlocks.DISPLAY_CASE)
|
||||
@@ -599,6 +604,9 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
|
||||
@JvmField val BERRY_JUICE = this.create("berry_juice", BerryJuiceItem())
|
||||
|
||||
@JvmField
|
||||
val HABITAT_BLOCK = blockItem("habitat_block", CobblemonBlocks.HABITAT_BLOCK)
|
||||
|
||||
@JvmField
|
||||
val GALARICA_NUTS = compostableItemNameBlockItem("galarica_nuts", CobblemonBlocks.GALARICA_NUT_BUSH)
|
||||
|
||||
@@ -819,7 +827,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField val DIRE_HIT = create("dire_hit", DireHitItem(CobblemonBlocks.DIRE_HIT))
|
||||
@JvmField val GUARD_SPEC = create("guard_spec", GuardSpecItem(CobblemonBlocks.GUARD_SPEC))
|
||||
|
||||
@JvmField val FULL_HEAL = create("full_heal", StatusCureItem("item.cobblemon.full_heal", block = CobblemonBlocks.FULL_HEAL))
|
||||
@JvmField val FULL_HEAL = create("full_heal", StatusCureItem("item.cobblemon.full_heal", *Statuses.getPersistentStatuses().toTypedArray(), block = CobblemonBlocks.FULL_HEAL))
|
||||
@JvmField val ANTIDOTE = create("antidote", StatusCureItem("item.cobblemon.antidote", Statuses.POISON, Statuses.POISON_BADLY, block = CobblemonBlocks.ANTIDOTE))
|
||||
@JvmField val AWAKENING = create("awakening", StatusCureItem("item.cobblemon.awakening", Statuses.SLEEP, block = CobblemonBlocks.AWAKENING))
|
||||
@JvmField val BURN_HEAL = create("burn_heal", StatusCureItem("item.cobblemon.burn_heal", Statuses.BURN, block = CobblemonBlocks.BURN_HEAL))
|
||||
@@ -1208,6 +1216,83 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val BLACK_TUMBLESTONE_BLOCK = blockItem("black_tumblestone_block", CobblemonBlocks.BLACK_TUMBLESTONE_BLOCK)
|
||||
|
||||
@JvmField
|
||||
val TYPE_GEM_CORE = blockItem("deepslate_crystal_core", CobblemonBlocks.TYPE_GEM_CORE)
|
||||
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_NORMAL = blockItem("normal_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_NORMAL)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_FIRE = blockItem("fire_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_FIRE)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_WATER = blockItem("water_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_WATER)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_ELECTRIC = blockItem("electric_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_ELECTRIC)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_GRASS = blockItem("grass_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_GRASS)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_ICE = blockItem("ice_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_ICE)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_FIGHTING = blockItem("fighting_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_FIGHTING)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_POISON = blockItem("poison_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_POISON)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_GROUND = blockItem("ground_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_GROUND)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_FLYING = blockItem("flying_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_FLYING)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_PSYCHIC = blockItem("psychic_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_PSYCHIC)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_BUG = blockItem("bug_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_BUG)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_ROCK = blockItem("rock_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_ROCK)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_GHOST = blockItem("ghost_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_GHOST)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_DRAGON = blockItem("dragon_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_DRAGON)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_DARK = blockItem("dark_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_DARK)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_STEEL = blockItem("steel_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_STEEL)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_FAIRY = blockItem("fairy_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_FAIRY)
|
||||
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_NORMAL = blockItem("normal_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_NORMAL)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_FIRE = blockItem("fire_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_FIRE)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_WATER = blockItem("water_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_WATER)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_ELECTRIC = blockItem("electric_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_ELECTRIC)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_GRASS = blockItem("grass_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_GRASS)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_ICE = blockItem("ice_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_ICE)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_FIGHTING = blockItem("fighting_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_FIGHTING)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_POISON = blockItem("poison_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_POISON)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_GROUND = blockItem("ground_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_GROUND)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_FLYING = blockItem("flying_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_FLYING)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_PSYCHIC = blockItem("psychic_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_PSYCHIC)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_BUG = blockItem("bug_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_BUG)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_ROCK = blockItem("rock_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_ROCK)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_GHOST = blockItem("ghost_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_GHOST)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_DRAGON = blockItem("dragon_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_DRAGON)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_DARK = blockItem("dark_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_DARK)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_STEEL = blockItem("steel_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_STEEL)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_FAIRY = blockItem("fairy_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_FAIRY)
|
||||
|
||||
@JvmField
|
||||
val POKEROD_SMITHING_TEMPLATE = create("pokerod_smithing_template", SmithingTemplateItem(
|
||||
Component.translatable("item.minecraft.fishing_rod").blue(),
|
||||
@@ -1432,44 +1517,49 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
val RELIC_COIN_POUCH = blockItem("relic_coin_pouch", CobblemonBlocks.RELIC_COIN_POUCH)
|
||||
@JvmField
|
||||
val RELIC_COIN_SACK = blockItem("relic_coin_sack", CobblemonBlocks.RELIC_COIN_SACK)
|
||||
@JvmField
|
||||
val TECHNICAL_MACHINE = this.create("technical_machine", TechnicalMachineItem(Properties()))
|
||||
@JvmField
|
||||
val BLANK_TM = this.create("blank_tm", CobblemonItem(Item.Properties()))
|
||||
|
||||
// Type Gems
|
||||
@JvmField
|
||||
val NORMAL_GEM = noSettingsItem("normal_gem")
|
||||
val NORMAL_GEM = this.create("normal_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_NORMAL))
|
||||
@JvmField
|
||||
val FIRE_GEM = noSettingsItem("fire_gem")
|
||||
val FIRE_GEM = this.create("fire_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_FIRE))
|
||||
@JvmField
|
||||
val WATER_GEM = noSettingsItem("water_gem")
|
||||
val WATER_GEM = this.create("water_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_WATER))
|
||||
@JvmField
|
||||
val GRASS_GEM = noSettingsItem("grass_gem")
|
||||
val GRASS_GEM = this.create("grass_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_GRASS))
|
||||
@JvmField
|
||||
val ELECTRIC_GEM = noSettingsItem("electric_gem")
|
||||
val ELECTRIC_GEM = this.create("electric_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_ELECTRIC))
|
||||
@JvmField
|
||||
val ICE_GEM = noSettingsItem("ice_gem")
|
||||
val ICE_GEM = this.create("ice_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_ICE))
|
||||
@JvmField
|
||||
val FIGHTING_GEM = noSettingsItem("fighting_gem")
|
||||
val FIGHTING_GEM = this.create("fighting_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_FIGHTING))
|
||||
@JvmField
|
||||
val POISON_GEM = noSettingsItem("poison_gem")
|
||||
val POISON_GEM = this.create("poison_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_POISON))
|
||||
@JvmField
|
||||
val GROUND_GEM = noSettingsItem("ground_gem")
|
||||
val GROUND_GEM = this.create("ground_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_GROUND))
|
||||
@JvmField
|
||||
val FLYING_GEM = noSettingsItem("flying_gem")
|
||||
val FLYING_GEM = this.create("flying_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_FLYING))
|
||||
@JvmField
|
||||
val PSYCHIC_GEM = noSettingsItem("psychic_gem")
|
||||
val PSYCHIC_GEM = this.create("psychic_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_PSYCHIC))
|
||||
@JvmField
|
||||
val BUG_GEM = noSettingsItem("bug_gem")
|
||||
val BUG_GEM = this.create("bug_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_BUG))
|
||||
@JvmField
|
||||
val ROCK_GEM = noSettingsItem("rock_gem")
|
||||
val ROCK_GEM = this.create("rock_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_ROCK))
|
||||
@JvmField
|
||||
val GHOST_GEM = noSettingsItem("ghost_gem")
|
||||
val GHOST_GEM = this.create("ghost_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_GHOST))
|
||||
@JvmField
|
||||
val DRAGON_GEM = noSettingsItem("dragon_gem")
|
||||
val DRAGON_GEM = this.create("dragon_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_DRAGON))
|
||||
@JvmField
|
||||
val DARK_GEM = noSettingsItem("dark_gem")
|
||||
val DARK_GEM = this.create("dark_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_DARK))
|
||||
@JvmField
|
||||
val STEEL_GEM = noSettingsItem("steel_gem")
|
||||
val STEEL_GEM = this.create("steel_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_STEEL))
|
||||
@JvmField
|
||||
val FAIRY_GEM = noSettingsItem("fairy_gem")
|
||||
val FAIRY_GEM = this.create("fairy_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_FAIRY))
|
||||
|
||||
//@JvmField
|
||||
//val BINDING_SOIL = blockItem("binding_soil", CobblemonBlocks.BINDING_SOIL)
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common
|
||||
|
||||
|
||||
import com.cobblemon.mod.common.block.campfirepot.CookingPotMenu
|
||||
import com.cobblemon.mod.common.block.tmmachine.TMMachineMenu
|
||||
import com.cobblemon.mod.common.platform.PlatformRegistry
|
||||
import net.minecraft.core.Registry
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
@@ -19,7 +20,8 @@ import net.minecraft.world.inventory.MenuType
|
||||
|
||||
object CobblemonMenuType : PlatformRegistry<Registry<MenuType<*>>, ResourceKey<Registry<MenuType<*>>>, MenuType<*>>() {
|
||||
|
||||
val COOKING_POT = MenuType.register("cooking_pot", ::CookingPotMenu)
|
||||
val COOKING_POT = MenuType.register("cooking_pot_menu", ::CookingPotMenu)
|
||||
val TM_MACHINE = MenuType.register("tm_machine_menu", ::TMMachineMenu)
|
||||
|
||||
override val registry: Registry<MenuType<*>>
|
||||
get() = BuiltInRegistries.MENU
|
||||
|
||||
@@ -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
|
||||
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.moves.MoveSelector
|
||||
import com.cobblemon.mod.common.api.moves.MovesetBuilder
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.MovesetBuilderAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.StringToObjectAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
|
||||
object CobblemonMovesetBuilders : JsonDataRegistry<MovesetBuilder> {
|
||||
override val gson: Gson = GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.registerTypeAdapter(MovesetBuilder::class.java, MovesetBuilderAdapter)
|
||||
.registerTypeAdapter(MoveSelector::class.java, StringToObjectAdapter(MoveSelector.selectors))
|
||||
.create()
|
||||
|
||||
override val typeToken = TypeToken.get(MovesetBuilder::class.java)
|
||||
override val resourcePath = "moveset_builders"
|
||||
override val id: ResourceLocation = cobblemonResource("moveset_builders")
|
||||
override val type = PackType.SERVER_DATA
|
||||
override val observable = SimpleObservable<CobblemonMovesetBuilders>()
|
||||
|
||||
val movesetBuilders = mutableMapOf<ResourceLocation, MovesetBuilder>()
|
||||
|
||||
fun getOrThrow(id: ResourceLocation): MovesetBuilder {
|
||||
return movesetBuilders[id]
|
||||
?: throw IllegalArgumentException("Unknown MovesetBuilder id: $id")
|
||||
}
|
||||
|
||||
override fun sync(player: ServerPlayer) { /* These don't sync. But what if they did? Nah probably shouldn't sync. */ }
|
||||
|
||||
override fun reload(data: Map<ResourceLocation, MovesetBuilder>) {
|
||||
movesetBuilders.clear()
|
||||
data.forEach { (id, value) -> value.id = id }
|
||||
movesetBuilders.putAll(data)
|
||||
observable.emit(this)
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.client.net.CalculateSeatPositionsHandler
|
||||
import com.cobblemon.mod.common.client.net.OpenBehaviourEditorHandler
|
||||
import com.cobblemon.mod.common.client.net.PlayerInteractOptionsHandler
|
||||
import com.cobblemon.mod.common.client.net.SetClientPlayerDataHandler
|
||||
@@ -33,7 +32,10 @@ import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormParticleHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.ExpGainedDataPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.InteractPokemonUIPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.PokedexUIPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.SetActiveTMPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.SetTMMachineContainerDataPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.SummaryUIPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.habitat.OpenHabitatBlockEditorHandler
|
||||
import com.cobblemon.mod.common.client.net.npc.CloseNPCEditorHandler
|
||||
import com.cobblemon.mod.common.client.net.npc.OpenNPCEditorHandler
|
||||
import com.cobblemon.mod.common.client.net.orientation.ClientboundUpdateOrientationHandler
|
||||
@@ -43,6 +45,7 @@ import com.cobblemon.mod.common.client.net.pasture.PokemonPasturedHandler
|
||||
import com.cobblemon.mod.common.client.net.pasture.PokemonUnpasturedHandler
|
||||
import com.cobblemon.mod.common.client.net.pasture.UpdatePastureConflictFlagHandler
|
||||
import com.cobblemon.mod.common.client.net.pokedex.ServerConfirmedRegisterHandler
|
||||
import com.cobblemon.mod.common.client.net.pokemon.update.ClientboundSeatAssignmentPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.pokemon.update.ClientboundUpdateRidingStateHandler
|
||||
import com.cobblemon.mod.common.client.net.pokemon.update.PokemonUpdatePacketHandler
|
||||
import com.cobblemon.mod.common.client.net.riding.ClientboundUpdateDriverInputHandler
|
||||
@@ -68,7 +71,6 @@ import com.cobblemon.mod.common.client.net.trade.TradeProcessStartedHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeStartedHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeUpdatedHandler
|
||||
import com.cobblemon.mod.common.net.PacketRegisterInfo
|
||||
import com.cobblemon.mod.common.net.messages.client.CalculateSeatPositionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.OpenBehaviourEditorPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.PlayerInteractOptionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
|
||||
@@ -88,6 +90,7 @@ import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacke
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SaccharineLogBlockParticlesPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.habitat.OpenHabitatBlockEditorPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.npc.CloseNPCEditorPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.npc.OpenNPCEditorPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateDriverInputPacket
|
||||
@@ -101,6 +104,7 @@ import com.cobblemon.mod.common.net.messages.client.pokedex.ServerConfirmedRegis
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.*
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.AddEvolutionPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.ClearEvolutionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.ClientboundSeatAssignmentPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.RemoveEvolutionPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.settings.OpenCobblemonConfigScreenPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.settings.ServerSettingsPacket
|
||||
@@ -139,8 +143,16 @@ import com.cobblemon.mod.common.net.messages.client.trade.TradeUpdatedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.ExpGainedDataPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.InteractPokemonUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.PokedexUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.SetActiveTMPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.SetTMMachineContainerDataPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.SummaryUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.*
|
||||
import com.cobblemon.mod.common.net.messages.server.BattleChallengePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.BattleChallengeResponsePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.BenchMovePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.RequestMoveSwapPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.RequestPlayerInteractionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.SelectStarterPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.SendOutPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.BattleSelectActionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.BattleTeamLeavePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.BattleTeamRequestPacket
|
||||
@@ -150,6 +162,7 @@ import com.cobblemon.mod.common.net.messages.server.battle.SpectateBattlePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.behaviour.DamageOnCollisionPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.behaviour.SetEntityBehaviourPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.block.AdjustBlockEntityViewerCountPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.block.TMMachineTeachMovePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectCancelledPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectedPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.party.PartyPokemonSelectedPacket
|
||||
@@ -162,6 +175,7 @@ import com.cobblemon.mod.common.net.messages.server.debug.ServerboundUpdateRidin
|
||||
import com.cobblemon.mod.common.net.messages.server.debug.ServerboundUpdateRidingStatsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.dialogue.EscapeDialoguePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.dialogue.InputToDialoguePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.habitat.SaveHabitatBlockSettingsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.npc.SaveNPCPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.orientation.ServerboundUpdateOrientationPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pasture.PasturePokemonPacket
|
||||
@@ -179,6 +193,7 @@ import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetNicknamePa
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.evolution.AcceptEvolutionPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.riding.DismountPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateDriverInputPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdatePassengerPositionPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateRiderRotationPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.starter.RequestStarterScreenPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.SwapPCPartyPokemonPacket
|
||||
@@ -205,6 +220,7 @@ import com.cobblemon.mod.common.net.serverhandling.battle.TeamRequestResponseHan
|
||||
import com.cobblemon.mod.common.net.serverhandling.behaviour.DamageOnCollisionPacketHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.behaviour.SetEntityBehaviourHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.block.AdjustBlockEntityViewerCountHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.block.TMMachineTeachMoveHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.move.MoveSelectCancelledHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.move.MoveSelectedHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.party.PartyPokemonSelectedHandler
|
||||
@@ -218,9 +234,9 @@ import com.cobblemon.mod.common.net.serverhandling.debug.ServerboundUpdateRiding
|
||||
import com.cobblemon.mod.common.net.serverhandling.dialogue.EscapeDialogueHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.dialogue.InputToDialogueHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.evolution.AcceptEvolutionHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.habitat.SaveHabitatBlockSettingsHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.npc.SaveNPCHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.orientation.ServerboundUpdateOrientationHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.party.PartyTutorialPacketHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pasture.PasturePokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pasture.SetPastureConflictHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pasture.UnpastureAllPokemonHandler
|
||||
@@ -235,6 +251,7 @@ import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetMarkingsHan
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetNicknameHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.riding.DismountPokemonPacketHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.riding.DriverInputPacketHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.riding.ServerboundUpdatePassengerPositionHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.riding.ServerboundUpdateRiderRotationHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.starter.RequestStarterScreenHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.starter.SelectStarterPacketHandler
|
||||
@@ -243,6 +260,7 @@ import com.cobblemon.mod.common.net.serverhandling.storage.RequestMoveSwapHandle
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.SendOutPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.SwapPCPartyPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.party.MovePartyPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.party.PartyTutorialPacketHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.party.ReleasePCPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.party.SwapPartyPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.pc.*
|
||||
@@ -321,6 +339,8 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(MarkingsUpdatePacket.ID, MarkingsUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(RideBoostsUpdatePacket.ID, RideBoostsUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(RideStaminaUpdatePacket.ID, RideStaminaUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(AlphaUpdatePacket.ID, AlphaUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(ScaleModifierUpdatePacket.ID, ScaleModifierUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
|
||||
// Evolution start
|
||||
list.add(PacketRegisterInfo(AddEvolutionPacket.ID, AddEvolutionPacket::decode, PokemonUpdatePacketHandler()))
|
||||
@@ -481,16 +501,18 @@ object CobblemonNetwork {
|
||||
// Pokédex scanning
|
||||
list.add(PacketRegisterInfo(ServerConfirmedRegisterPacket.ID, ServerConfirmedRegisterPacket::decode, ServerConfirmedRegisterHandler))
|
||||
|
||||
// Debug / cheats
|
||||
list.add(PacketRegisterInfo(CalculateSeatPositionsPacket.ID, CalculateSeatPositionsPacket::decode, CalculateSeatPositionsHandler))
|
||||
|
||||
// Riding
|
||||
list.add(PacketRegisterInfo(ClientboundUpdateRidingStatePacket.ID, ClientboundUpdateRidingStatePacket::decode, ClientboundUpdateRidingStateHandler))
|
||||
list.add(PacketRegisterInfo(ClientboundUpdateDriverInputPacket.ID, ClientboundUpdateDriverInputPacket::decode, ClientboundUpdateDriverInputHandler))
|
||||
list.add(PacketRegisterInfo(ClientboundSeatAssignmentPacket.ID, ClientboundSeatAssignmentPacket::decode, ClientboundSeatAssignmentPacketHandler))
|
||||
|
||||
// Habitat blocks
|
||||
list.add(PacketRegisterInfo(OpenHabitatBlockEditorPacket.ID, OpenHabitatBlockEditorPacket::decode, OpenHabitatBlockEditorHandler))
|
||||
|
||||
// Debug
|
||||
list.add(PacketRegisterInfo(OpenRidingStatsDebugGUIPacket.ID, OpenRidingStatsDebugGUIPacket::decode, OpenRidingStatsDebugGUIHandler))
|
||||
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
@@ -570,6 +592,7 @@ object CobblemonNetwork {
|
||||
|
||||
// Block entity
|
||||
list.add(PacketRegisterInfo(AdjustBlockEntityViewerCountPacket.ID, AdjustBlockEntityViewerCountPacket::decode, AdjustBlockEntityViewerCountHandler))
|
||||
list.add(PacketRegisterInfo(TMMachineTeachMovePacket.ID, TMMachineTeachMovePacket::decode, TMMachineTeachMoveHandler))
|
||||
|
||||
// Move select packets
|
||||
list.add(PacketRegisterInfo(MoveSelectedPacket.ID, MoveSelectedPacket::decode, MoveSelectedHandler))
|
||||
@@ -602,17 +625,26 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(DismountPokemonPacket.ID, DismountPokemonPacket::decode, DismountPokemonPacketHandler))
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateDriverInputPacket.ID, ServerboundUpdateDriverInputPacket::decode, DriverInputPacketHandler))
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateRiderRotationPacket.ID, ServerboundUpdateRiderRotationPacket::decode, ServerboundUpdateRiderRotationHandler))
|
||||
list.add(PacketRegisterInfo(ServerboundUpdatePassengerPositionPacket.ID, ServerboundUpdatePassengerPositionPacket::decode, ServerboundUpdatePassengerPositionHandler))
|
||||
|
||||
// Cooking
|
||||
list.add(PacketRegisterInfo(ToggleCookingPotLidPacket.ID, ToggleCookingPotLidPacket::decode, ToggleCookingPotLidHandler))
|
||||
|
||||
// TM Machine
|
||||
list.add(PacketRegisterInfo(SetActiveTMPacket.ID, SetActiveTMPacket::decode, SetActiveTMPacketHandler))
|
||||
list.add(PacketRegisterInfo(SetTMMachineContainerDataPacket.ID, SetTMMachineContainerDataPacket::decode, SetTMMachineContainerDataPacketHandler))
|
||||
|
||||
// Behaviour Packets
|
||||
list.add(PacketRegisterInfo(SetEntityBehaviourPacket.ID, SetEntityBehaviourPacket::decode, SetEntityBehaviourHandler))
|
||||
list.add(PacketRegisterInfo(DamageOnCollisionPacket.ID, DamageOnCollisionPacket::decode, DamageOnCollisionPacketHandler))
|
||||
|
||||
// Debug
|
||||
// Habitat blocks
|
||||
list.add(PacketRegisterInfo(SaveHabitatBlockSettingsPacket.ID, SaveHabitatBlockSettingsPacket::decode, SaveHabitatBlockSettingsHandler))
|
||||
|
||||
// Debug / cheats
|
||||
list.add(PacketRegisterInfo(RequestOpenRidingStatsDebugGUIPacket.ID, RequestOpenRidingStatsDebugGUIPacket::decode, RequestOpenRidingStatsDebugGUIHandler))
|
||||
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.npc.PartyComposition
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
|
||||
object CobblemonPartyCompositions : JsonDataRegistry<PartyComposition> {
|
||||
override val id: ResourceLocation = cobblemonResource("party_compositions")
|
||||
override val type = PackType.SERVER_DATA
|
||||
override val typeToken: TypeToken<PartyComposition> = TypeToken.get(PartyComposition::class.java)
|
||||
override val resourcePath: String = "party_compositions"
|
||||
override val observable = SimpleObservable<CobblemonPartyCompositions>()
|
||||
override val gson: Gson = GsonBuilder()
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.setPrettyPrinting()
|
||||
.create()
|
||||
|
||||
val partyCompositions = mutableMapOf<ResourceLocation, PartyComposition>()
|
||||
|
||||
override fun sync(player: ServerPlayer) {
|
||||
// probably worth syncing which exist, yeah. the deeper details probably not. I'll get to this eventually.
|
||||
}
|
||||
|
||||
override fun reload(data: Map<ResourceLocation, PartyComposition>) {
|
||||
data.entries.forEach { it.value.id = it.key }
|
||||
partyCompositions.clear()
|
||||
partyCompositions.putAll(data)
|
||||
observable.emit(this)
|
||||
Cobblemon.LOGGER.info("Loaded ${data.size} party compositions.")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.moves.MovesetBuilder
|
||||
import com.cobblemon.mod.common.api.npc.PartyPool
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.util.ScriptableIntRange
|
||||
import com.cobblemon.mod.common.util.adapters.ExpressionLikeAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IntRangeAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.MovesetBuilderReferenceAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.PokemonPropertiesAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.ScriptableIntRangeAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
|
||||
object CobblemonPartyPools : JsonDataRegistry<PartyPool> {
|
||||
override val id: ResourceLocation = cobblemonResource("party_pools")
|
||||
override val type = PackType.SERVER_DATA
|
||||
override val typeToken: TypeToken<PartyPool> = TypeToken.get(PartyPool::class.java)
|
||||
override val resourcePath: String = "party_pools"
|
||||
override val observable = SimpleObservable<CobblemonPartyPools>()
|
||||
override val gson: Gson = GsonBuilder()
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.registerTypeAdapter(PokemonProperties::class.java, PokemonPropertiesAdapter(saveLong = false))
|
||||
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
|
||||
.registerTypeAdapter(ScriptableIntRange::class.java, ScriptableIntRangeAdapter)
|
||||
.registerTypeAdapter(MovesetBuilder::class.java, MovesetBuilderReferenceAdapter)
|
||||
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
|
||||
.setPrettyPrinting()
|
||||
.create()
|
||||
|
||||
val partyPools = mutableMapOf<ResourceLocation, PartyPool>()
|
||||
|
||||
override fun sync(player: ServerPlayer) {
|
||||
// probably worth syncing. Knowing the details might help exploit the server? lot of effort for little though.
|
||||
}
|
||||
|
||||
override fun reload(data: Map<ResourceLocation, PartyPool>) {
|
||||
data.entries.forEach { it.value.id = it.key }
|
||||
partyPools.clear()
|
||||
partyPools.putAll(data)
|
||||
observable.emit(this)
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,10 @@ object CobblemonPoiTypes: PlatformRegistry<Registry<PoiType>, ResourceKey<Regist
|
||||
// TODO after 1.7
|
||||
// @JvmField
|
||||
// val INCENSE_SWEET_KEY = createKey("incense_sweet", CobblemonBlocks.INCENSE_SWEET, 0, 1)
|
||||
@JvmField
|
||||
val HABITAT_BLOCK_KEY: ResourceKey<PoiType> = createKey("habitat_block", CobblemonBlocks.HABITAT_BLOCK, 0, 1)
|
||||
@JvmField
|
||||
val HABITAT_BLOCK = create(HABITAT_BLOCK_KEY.location().path, PoiType(getBlockStates(CobblemonBlocks.HABITAT_BLOCK), 0, 1))
|
||||
|
||||
private fun createKey(string: String, block: Block, maxTickets: Int, validRange: Int ): ResourceKey<PoiType> =
|
||||
ResourceKey.create(Registries.POINT_OF_INTEREST_TYPE, cobblemonResource(string)).also { resourceKey ->
|
||||
|
||||
@@ -37,6 +37,9 @@ object CobblemonSensors {
|
||||
@JvmField
|
||||
val POKEMON_DEFEND_OWNER = register("pokemon_owner_under_attack", ::DefendOwnerSensor)
|
||||
|
||||
@JvmField
|
||||
val ALPHA_LEVEL_MATCHING = register("alpha_level_matching", ::AlphaLevelMatchingSensor)
|
||||
|
||||
@JvmField
|
||||
val NEARBY_GROWABLE_CROPS = register("nearby_growable_crops", ::PokemonGrowableCropSensor)
|
||||
|
||||
|
||||
@@ -136,6 +136,13 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
@JvmField
|
||||
val MULCH_REMOVE = this.create("block.mulch.remove")
|
||||
|
||||
@JvmField
|
||||
val MONITOR_BREAK = this.create("block.monitor.break")
|
||||
@JvmField
|
||||
val MONITOR_INSERT = this.create("block.monitor.insert")
|
||||
@JvmField
|
||||
val MONITOR_LOADING = this.create("block.monitor.loading")
|
||||
|
||||
@JvmField
|
||||
val FOSSIL_MACHINE_ACTIVATE = this.create("block.fossil_machine.activate")
|
||||
@JvmField
|
||||
@@ -200,6 +207,22 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
@JvmField
|
||||
val TUMBLESTONE_STEP = this.create("block.tumblestone.step")
|
||||
|
||||
@JvmField
|
||||
val TM_USE = this.create("item.tm.use")
|
||||
@JvmField
|
||||
val TM_MACHINE_CRAFT = this.create("block.tm_machine.craft")
|
||||
@JvmField
|
||||
val TM_MACHINE_OFF = this.create("block.tm_machine.off")
|
||||
@JvmField
|
||||
val TM_MACHINE_ON = this.create("block.tm_machine.on")
|
||||
@JvmField
|
||||
val TM_MACHINE_OPEN = this.create("block.tm_machine.open")
|
||||
@JvmField
|
||||
val TM_MACHINE_CLOSE = this.create("block.tm_machine.close")
|
||||
@JvmField
|
||||
val TM_MACHINE_PLACE_DISC = this.create("block.tm_machine.place_disc")
|
||||
|
||||
|
||||
@JvmField
|
||||
val EVOLUTION_STONE_BLOCK_BREAK = this.create("block.evolution_stone_block.break")
|
||||
@JvmField
|
||||
@@ -312,9 +335,13 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val RIDE_LOOP_LEATHER = create("ride.loop.leather")
|
||||
val RIDE_LOOP_LEATHER_MONO = create("ride.loop.leather.mono")
|
||||
@JvmField
|
||||
val RIDE_LOOP_PLUMAGE = create("ride.loop.plumage")
|
||||
val RIDE_LOOP_PLUMAGE_MONO = create("ride.loop.plumage.mono")
|
||||
@JvmField
|
||||
val RIDE_LOOP_LEATHER_STEREO = create("ride.loop.leather.stereo")
|
||||
@JvmField
|
||||
val RIDE_LOOP_PLUMAGE_STEREO = create("ride.loop.plumage.stereo")
|
||||
|
||||
@JvmField
|
||||
val MOVE_QUICKATTACK_TARGET = this.create("move.quickattack.target")
|
||||
@@ -380,6 +407,35 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
TUMBLESTONE_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_BREAK = this.create("block.type_gem_cluster.break")
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_BREAK = this.create("block.type_gem_block.break")
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_HIT = this.create("block.type_gem_block.hit")
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_PLACE = this.create("block.type_gem_block.place")
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_STEP = this.create("block.type_gem_block.step")
|
||||
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_SOUNDS = SoundType(1f, 1.2f,
|
||||
TYPE_GEM_CLUSTER_BREAK,
|
||||
TYPE_GEM_BLOCK_STEP,
|
||||
TYPE_GEM_BLOCK_PLACE,
|
||||
TYPE_GEM_BLOCK_HIT,
|
||||
TYPE_GEM_BLOCK_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_SOUNDS = SoundType(1f, 1.1f,
|
||||
TYPE_GEM_BLOCK_BREAK,
|
||||
TYPE_GEM_BLOCK_STEP,
|
||||
TYPE_GEM_BLOCK_PLACE,
|
||||
TYPE_GEM_BLOCK_HIT,
|
||||
TYPE_GEM_BLOCK_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val EVOLUTION_STONE_BLOCK_SOUNDS = SoundType(1f, 1.1f,
|
||||
EVOLUTION_STONE_BLOCK_BREAK,
|
||||
@@ -561,5 +617,31 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
CAMPFIRE_POT_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val ITEM_BLOCK_PAPER_SMALL_PLACE = this.create("block.item_block.paper_small.place")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_PAPER_SMALL_BREAK = this.create("block.item_block.paper_small.break")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_PAPER_SMALL_SOUNDS = SoundType(1f, 1.1f,
|
||||
ITEM_BLOCK_PAPER_SMALL_BREAK,
|
||||
SoundEvents.WOOL_STEP,
|
||||
ITEM_BLOCK_PAPER_SMALL_PLACE,
|
||||
SoundEvents.WOOL_HIT,
|
||||
SoundEvents.WOOL_FALL
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val ITEM_BLOCK_PAPER_LARGE_PLACE = this.create("block.item_block.paper_large.place")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_PAPER_LARGE_BREAK = this.create("block.item_block.paper_large.break")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_PAPER_LARGE_SOUNDS = SoundType(1f, 1.1f,
|
||||
ITEM_BLOCK_PAPER_LARGE_BREAK,
|
||||
SoundEvents.WOOL_STEP,
|
||||
ITEM_BLOCK_PAPER_LARGE_PLACE,
|
||||
SoundEvents.WOOL_HIT,
|
||||
SoundEvents.WOOL_FALL
|
||||
)
|
||||
|
||||
private fun create(name: String): SoundEvent = this.create(name, SoundEvent.createVariableRangeEvent(cobblemonResource(name)))
|
||||
}
|
||||
|
||||
@@ -32,6 +32,9 @@ object CobblemonCriteria : PlatformRegistry<Registry<CriterionTrigger<*>>, Resou
|
||||
@JvmField
|
||||
val CATCH_SHINY_POKEMON = this.create("catch_shiny_pokemon", SimpleCriterionTrigger(CountableCriterion.CODEC))
|
||||
|
||||
@JvmField
|
||||
val CATCH_ALPHA_POKEMON = this.create("catch_alpha_pokemon", SimpleCriterionTrigger(CountableCriterion.CODEC))
|
||||
|
||||
@JvmField
|
||||
val EGG_COLLECT = this.create("eggs_collected", SimpleCriterionTrigger(CountableCriterion.CODEC))
|
||||
|
||||
@@ -78,8 +81,20 @@ object CobblemonCriteria : PlatformRegistry<Registry<CriterionTrigger<*>>, Resou
|
||||
@JvmField
|
||||
val PLANT_TUMBLESTONE = this.create("plant_tumblestone", SimpleCriterionTrigger(PlantTumblestoneCriterion.CODEC))
|
||||
|
||||
@JvmField
|
||||
val PLANT_TYPE_GEM = this.create("plant_type_gem", SimpleCriterionTrigger(PlantTypeGemCriterion.CODEC))
|
||||
|
||||
val RIDING_STAT_BOOST = this.create("riding_stat_boost", SimpleCriterionTrigger(RidingStatBoostCriterion.CODEC))
|
||||
|
||||
@JvmField
|
||||
val LEARN_TM = this.create("has_learn_specific_tm", SimpleCriterionTrigger(LearnTMCriterion.CODEC))
|
||||
|
||||
@JvmField
|
||||
val LEARN_ALL_TM = this.create("has_learn_all_tm", SimpleCriterionTrigger(LearnAllTMCriterion.CODEC))
|
||||
|
||||
@JvmField
|
||||
val LEARN_TM_COUNT = this.create("tms_learned", SimpleCriterionTrigger(CountableCriterion.CODEC))
|
||||
|
||||
override val registry = BuiltInRegistries.TRIGGER_TYPES
|
||||
override val resourceKey = Registries.TRIGGER_TYPE
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.advancement.criterion
|
||||
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.advancements.critereon.ContextAwarePredicate
|
||||
import net.minecraft.advancements.critereon.EntityPredicate
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import java.util.Optional
|
||||
|
||||
class LearnTMContext(val tm: ResourceLocation)
|
||||
|
||||
class LearnTMCriterion(
|
||||
playerCtx: Optional<ContextAwarePredicate>,
|
||||
val tm: String
|
||||
) : SimpleCriterionCondition<LearnTMContext>(playerCtx) {
|
||||
companion object {
|
||||
val CODEC: Codec<LearnTMCriterion> = RecordCodecBuilder.create { it.group(
|
||||
EntityPredicate.ADVANCEMENT_CODEC.optionalFieldOf("player").forGetter(LearnTMCriterion::playerCtx),
|
||||
Codec.STRING.optionalFieldOf("tm", "any").forGetter(LearnTMCriterion::tm)
|
||||
).apply(it, ::LearnTMCriterion) }
|
||||
}
|
||||
|
||||
override fun matches(player: ServerPlayer, context: LearnTMContext): Boolean {
|
||||
return tm == "any" || context.tm == tm.asIdentifierDefaultingNamespace()
|
||||
}
|
||||
}
|
||||
|
||||
class LearnAllTMContext
|
||||
|
||||
class LearnAllTMCriterion(
|
||||
playerCtx: Optional<ContextAwarePredicate>
|
||||
) : SimpleCriterionCondition<LearnAllTMContext>(playerCtx) {
|
||||
companion object {
|
||||
val CODEC: Codec<LearnAllTMCriterion> = RecordCodecBuilder.create { it.group(
|
||||
EntityPredicate.ADVANCEMENT_CODEC.optionalFieldOf("player").forGetter(LearnAllTMCriterion::playerCtx)
|
||||
).apply(it, ::LearnAllTMCriterion) }
|
||||
}
|
||||
|
||||
override fun matches(player: ServerPlayer, context: LearnAllTMContext) = true
|
||||
}
|
||||
+3
-39
@@ -14,7 +14,7 @@ import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.advancements.critereon.ContextAwarePredicate
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import java.util.Optional
|
||||
import java.util.*
|
||||
|
||||
class PlantTumblestoneContext(val pos: BlockPos, val block: TumblestoneBlock)
|
||||
|
||||
@@ -29,42 +29,6 @@ class PlantTumblestoneCriterion(
|
||||
}
|
||||
|
||||
override fun matches(player: ServerPlayer, context: PlantTumblestoneContext): Boolean {
|
||||
return context.block.canGrow(context.pos, player.level())
|
||||
return context.block.canGrow(player.level().getBlockState(context.pos), context.pos, player.level())
|
||||
}
|
||||
}
|
||||
|
||||
///**
|
||||
// * A criterion condition for when a tumblestone block is planted
|
||||
// * @param id The identifier of the criterion
|
||||
// * @param predicate The predicate for the criterion
|
||||
// *
|
||||
// * @author Aethen
|
||||
// * @since 02/28/2024
|
||||
// */
|
||||
//class PlantTumblestoneCriterionCondition(id: Identifier, predicate: LootContextPredicate) :
|
||||
// SimpleCriterionCondition<PlantTumblestoneContext>(id, predicate) {
|
||||
// override fun toJson(json: JsonObject) {
|
||||
// // Add properties to json if needed. None needed for this criterion
|
||||
// }
|
||||
//
|
||||
// override fun fromJson(json: JsonObject) {
|
||||
// // Parse properties from json if needed. None needed for this criterion
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Checks if the tumblestone block can grow at the given position
|
||||
// * @param player The player that planted the tumblestone block
|
||||
// * @param context The context of the criterion
|
||||
// * @return True if the tumblestone block can grow at the given position, false otherwise
|
||||
// */
|
||||
// override fun matches(player: ServerPlayer, context: PlantTumblestoneContext): Boolean {
|
||||
// return context.tumbleStoneBlock.canGrow(context.pos, player.level())
|
||||
// }
|
||||
//}
|
||||
//
|
||||
///**
|
||||
// * The context of the [PlantTumblestoneCriterionCondition]
|
||||
// * @param pos The position of the tumblestone block
|
||||
// * @param tumbleStoneBlock The tumblestone block
|
||||
// */
|
||||
//open class PlantTumblestoneContext(var pos: BlockPos, var tumbleStoneBlock: TumblestoneBlock)
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.advancement.criterion
|
||||
|
||||
import com.cobblemon.mod.common.block.TypeGemClusterBlock
|
||||
import com.cobblemon.mod.common.block.TypeGemCoreBlock
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.advancements.critereon.ContextAwarePredicate
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import java.util.*
|
||||
|
||||
class PlantTypeGemContext(val pos: BlockPos, val block: TypeGemClusterBlock)
|
||||
|
||||
class PlantTypeGemCriterion(
|
||||
playerCtx: Optional<ContextAwarePredicate>
|
||||
): SimpleCriterionCondition<PlantTypeGemContext>(playerCtx) {
|
||||
|
||||
companion object {
|
||||
val CODEC: Codec<PlantTypeGemCriterion> = RecordCodecBuilder.create { it.group(
|
||||
ContextAwarePredicate.CODEC.optionalFieldOf("player").forGetter(PlantTypeGemCriterion::playerCtx)
|
||||
).apply(it, ::PlantTypeGemCriterion) }
|
||||
}
|
||||
|
||||
override fun matches(player: ServerPlayer, context: PlantTypeGemContext): Boolean {
|
||||
return player.level().getBlockState(context.pos).block is TypeGemCoreBlock
|
||||
}
|
||||
}
|
||||
+38
-15
@@ -22,18 +22,21 @@ import com.cobblemon.mod.common.api.battles.model.actor.EntityBackedBattleActor
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.FleeableBattleActor
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleFledEvent
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleFleeAttemptEvent
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.BattleEvSource
|
||||
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.api.text.white
|
||||
import com.cobblemon.mod.common.api.text.yellow
|
||||
import com.cobblemon.mod.common.battles.ActiveBattlePokemon
|
||||
import com.cobblemon.mod.common.battles.BattleCaptureAction
|
||||
import com.cobblemon.mod.common.battles.BattleFormat
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
import com.cobblemon.mod.common.battles.BattleSide
|
||||
import com.cobblemon.mod.common.battles.FleeAttemptActionResponse
|
||||
import com.cobblemon.mod.common.battles.ForfeitActionResponse
|
||||
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
|
||||
import com.cobblemon.mod.common.battles.dispatch.BattleDispatch
|
||||
@@ -48,7 +51,9 @@ import com.cobblemon.mod.common.entity.npc.NPCBattleActor
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleEndPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleMakeChoicePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleMessagePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleQueueRequestPacket
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.DefeatEvolutionProgress
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.LastBattleCriticalHitsEvolutionProgress
|
||||
import com.cobblemon.mod.common.pokemon.requirements.DefeatRequirement
|
||||
@@ -132,11 +137,13 @@ open class PokemonBattle(
|
||||
|
||||
var dispatchResult = GO
|
||||
val dispatches = ConcurrentLinkedDeque<BattleDispatch>()
|
||||
val afterDispatches = mutableListOf<() -> Unit>()
|
||||
|
||||
val captureActions = mutableListOf<BattleCaptureAction>()
|
||||
|
||||
val majorBattleActions = hashMapOf<UUID, BattleMessage>()
|
||||
|
||||
/** Monotonically increasing counter to track faint ordering within this battle. */
|
||||
var faintCounter = 0
|
||||
val minorBattleActions = hashMapOf<UUID, BattleMessage>()
|
||||
val contextManager = ContextManager()
|
||||
|
||||
@@ -244,12 +251,23 @@ open class PokemonBattle(
|
||||
|
||||
fun end() {
|
||||
ended = true
|
||||
val awardToFainted = Cobblemon.config.awardExperienceToFaintedPokemon
|
||||
val awardOnLoss = Cobblemon.config.awardExperienceOnBattleLoss
|
||||
this.actors.forEach { actor ->
|
||||
val faintedPokemons = actor.pokemonList.filter { it.health <= 0 }
|
||||
actor.getSide().getOppositeSide().actors.forEach { opponent ->
|
||||
val opponentNonFaintedPokemons = opponent.pokemonList.filter { it.health > 0 }
|
||||
val opponentPokemons = if (awardToFainted) opponent.pokemonList else opponent.pokemonList.filter { it.health > 0 }
|
||||
faintedPokemons.forEach { faintedPokemon ->
|
||||
for (opponentPokemon in opponentNonFaintedPokemons) {
|
||||
for (opponentPokemon in opponentPokemons) {
|
||||
// Faint-order check: if awarding XP to fainted pokemon, only award for enemies that fainted before this pokemon did
|
||||
if (awardToFainted) {
|
||||
val enemyFaintedAt = faintedPokemon.faintedAt ?: continue
|
||||
val opponentFaintedAt = opponentPokemon.faintedAt
|
||||
if (opponentFaintedAt != null && opponentFaintedAt <= enemyFaintedAt) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
val facedFainted = opponentPokemon.facedOpponents.contains(faintedPokemon)
|
||||
val pokemon = opponentPokemon.effectedPokemon
|
||||
if (facedFainted) {
|
||||
@@ -270,7 +288,8 @@ open class PokemonBattle(
|
||||
else -> continue
|
||||
}
|
||||
val experience = Cobblemon.experienceCalculator.calculate(opponentPokemon, faintedPokemon, multiplier)
|
||||
if (experience > 0 && actor.pokemonList.all { it.health <= 0 }) {
|
||||
val enemyTeamWiped = actor.pokemonList.all { it.health <= 0 }
|
||||
if (experience > 0 && (enemyTeamWiped || awardOnLoss)) {
|
||||
opponent.awardExperience(opponentPokemon, experience)
|
||||
}
|
||||
Cobblemon.evYieldCalculator.calculate(opponentPokemon, faintedPokemon).forEach { (stat, amount) ->
|
||||
@@ -380,12 +399,10 @@ open class PokemonBattle(
|
||||
|
||||
fun dispatch(dispatcher: () -> DispatchResult) {
|
||||
dispatches.add(BattleDispatch { dispatcher() })
|
||||
|
||||
}
|
||||
|
||||
fun dispatchToFront(dispatcher: () -> DispatchResult) {
|
||||
dispatches.addFirst(BattleDispatch { dispatcher() })
|
||||
|
||||
}
|
||||
|
||||
fun dispatchWaitingToFront(delaySeconds: Float = 1F, dispatcher: () -> Unit) {
|
||||
@@ -436,21 +453,12 @@ open class PokemonBattle(
|
||||
dispatches.addFirst(dispatcher)
|
||||
}
|
||||
|
||||
fun doWhenClear(action: () -> Unit) {
|
||||
afterDispatches.add(action)
|
||||
}
|
||||
|
||||
fun tick() {
|
||||
try {
|
||||
while (dispatchResult.canProceed()) {
|
||||
val dispatch = dispatches.poll() ?: break
|
||||
dispatchResult = dispatch(this)
|
||||
}
|
||||
|
||||
if (dispatches.isEmpty()) {
|
||||
afterDispatches.toList().forEach { it() }
|
||||
afterDispatches.clear()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
LOGGER.error("Exception while ticking a battle. Saving battle log.", e)
|
||||
val message = battleLang("crash").red()
|
||||
@@ -505,6 +513,7 @@ open class PokemonBattle(
|
||||
|
||||
fun checkForInputDispatch() {
|
||||
if (checkForfeit()) return // ignore actors that are still choosing, their choices don't matter anymore
|
||||
if (checkFleeAttempt()) return
|
||||
val readyToInput = (actors.any { !it.mustChoose && it.responses.isNotEmpty() } && actors.none { it.mustChoose })
|
||||
if (readyToInput && captureActions.isEmpty()) {
|
||||
actors.filter { it.responses.isNotEmpty() }.forEach { it.writeShowdownResponse() }
|
||||
@@ -512,6 +521,20 @@ open class PokemonBattle(
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkFleeAttempt(): Boolean {
|
||||
val runner = actors.find { it.responses.any { it is FleeAttemptActionResponse } } as? PlayerBattleActor ?: return false
|
||||
CobblemonEvents.BATTLE_FLEE_ATTEMPT.post(BattleFleeAttemptEvent(this, runner))
|
||||
runner.responses.clear()
|
||||
|
||||
runner.mustChoose = true
|
||||
runner.request?.let { runner.sendUpdate(BattleQueueRequestPacket(it)) }
|
||||
runner.sendUpdate(BattleMakeChoicePacket())
|
||||
|
||||
runner.entity?.sendSystemMessage(battleLang("run_prompt").white())
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/** Forces Showdown to end the battle when a [BattleActor] chooses to forfeit. */
|
||||
private fun checkForfeit(): Boolean {
|
||||
val forfeit = actors.find { it.responses.any { it is ForfeitActionResponse } }
|
||||
|
||||
+8
-5
@@ -8,8 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.battles.model.actor
|
||||
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
@@ -76,7 +74,7 @@ abstract class BattleActor(
|
||||
fun turn() {
|
||||
val request = request ?: return
|
||||
responses.clear()
|
||||
if(activePokemon.any { it.isAlive() }) {
|
||||
if (activePokemon.any { it.isAlive() }) {
|
||||
mustChoose = true
|
||||
sendUpdate(BattleMakeChoicePacket())
|
||||
}
|
||||
@@ -90,15 +88,15 @@ abstract class BattleActor(
|
||||
}
|
||||
}
|
||||
|
||||
fun upkeep() {
|
||||
fun postUpdate() {
|
||||
val request = request ?: return
|
||||
val forceSwitchPokemon = request.forceSwitch.mapIndexedNotNull { index, b -> if (b) activePokemon[index] else null }
|
||||
if (forceSwitchPokemon.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
mustChoose = true // This must happen before the sendUpdate, not after, otherwise AI-driven actors will get locked (they may reply instantly)
|
||||
sendUpdate(BattleMakeChoicePacket())
|
||||
mustChoose = true
|
||||
}
|
||||
|
||||
fun setActionResponses(responses: List<ShowdownActionResponse>) {
|
||||
@@ -154,6 +152,11 @@ abstract class BattleActor(
|
||||
open fun sendMessage(component: Component) {
|
||||
sendUpdate(BattleMessagePacket(component))
|
||||
}
|
||||
/**
|
||||
* Called when this actor's Pokemon uses a move. Default implementation does nothing.
|
||||
* Subclasses (NPC-backed actors, players, etc) may override to trigger animations or UI.
|
||||
*/
|
||||
open fun onUseMove(user: BattlePokemon, target: BattlePokemon?) {}
|
||||
open fun awardExperience(battlePokemon: BattlePokemon, experience: Int) {}
|
||||
open fun sendUpdate(packet: NetworkPacket<*>) {}
|
||||
|
||||
|
||||
@@ -14,9 +14,11 @@ import com.cobblemon.mod.common.api.text.green
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.cobblemon.mod.common.util.toBlockPos
|
||||
import net.minecraft.core.component.DataComponentMap
|
||||
import com.google.gson.JsonElement
|
||||
import com.mojang.serialization.JsonOps
|
||||
import net.minecraft.core.component.DataComponentPatch
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.resources.RegistryOps
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
@@ -40,7 +42,7 @@ open class ItemDropEntry : DropEntry {
|
||||
override var maxSelectableTimes = 1
|
||||
open val dropMethod: ItemDropMethod? = null
|
||||
open var item = ResourceLocation.parse("minecraft:fish")
|
||||
open val components: DataComponentMap? = null
|
||||
open val components: JsonElement? = null
|
||||
|
||||
override fun drop(entity: LivingEntity?, world: ServerLevel, pos: Vec3, player: ServerPlayer?) {
|
||||
val item = world.registryAccess().registryOrThrow(Registries.ITEM).get(item) ?: return LOGGER.error("Unable to load drop item: $item")
|
||||
@@ -53,11 +55,15 @@ open class ItemDropEntry : DropEntry {
|
||||
it
|
||||
}
|
||||
}
|
||||
val builder = DataComponentPatch.builder()
|
||||
components?.forEach {
|
||||
builder.set(it)
|
||||
if (components != null) {
|
||||
val registryOps = RegistryOps.create(JsonOps.INSTANCE, world.registryAccess())
|
||||
val decodeResult = DataComponentPatch.CODEC.parse(registryOps, components)
|
||||
decodeResult.ifSuccess { patch ->
|
||||
stack.applyComponentsAndValidate(patch)
|
||||
}.ifError { error ->
|
||||
LOGGER.error("Unable to parse components for drop item $item: ${error.message()}")
|
||||
}
|
||||
}
|
||||
stack.applyComponentsAndValidate(builder.build())
|
||||
|
||||
if (dropMethod == ItemDropMethod.ON_PLAYER && player != null) {
|
||||
world.addFreshEntity(ItemEntity(player.level(), player.x, player.y, player.z, stack))
|
||||
|
||||
@@ -10,6 +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.BattleFleeAttemptEvent
|
||||
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
|
||||
@@ -22,9 +23,21 @@ 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.cooking.PokeSnackSpawnPokemonEvent
|
||||
import com.cobblemon.mod.common.api.events.drops.LootDroppedEvent
|
||||
import com.cobblemon.mod.common.api.events.entity.*
|
||||
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.farming.ApricornHarvestEvent
|
||||
import com.cobblemon.mod.common.api.events.fishing.*
|
||||
import com.cobblemon.mod.common.api.events.fishing.BaitConsumedEvent
|
||||
import com.cobblemon.mod.common.api.events.fishing.BaitEffectFunctionRegistryEvent
|
||||
import com.cobblemon.mod.common.api.events.fishing.BaitSetEvent
|
||||
import com.cobblemon.mod.common.api.events.fishing.BaitSpawnPokemonEvent
|
||||
import com.cobblemon.mod.common.api.events.fishing.BobberSpawnPokemonEvent
|
||||
import com.cobblemon.mod.common.api.events.fishing.PokerodCastEvent
|
||||
import com.cobblemon.mod.common.api.events.fishing.PokerodReelEvent
|
||||
import com.cobblemon.mod.common.api.events.habitats.HabitatSpawnActivatedEvent
|
||||
import com.cobblemon.mod.common.api.events.habitats.SaveHabitatBlockSettingsEvent
|
||||
import com.cobblemon.mod.common.api.events.item.LeftoversCreatedEvent
|
||||
import com.cobblemon.mod.common.api.events.pokeball.PokeBallCaptureCalculatedEvent
|
||||
import com.cobblemon.mod.common.api.events.pokeball.PokemonCatchRateEvent
|
||||
@@ -39,7 +52,11 @@ 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.*
|
||||
import com.cobblemon.mod.common.api.events.storage.ChangePCBoxWallpaperEvent
|
||||
import com.cobblemon.mod.common.api.events.storage.ReleasePokemonEvent
|
||||
import com.cobblemon.mod.common.api.events.storage.RenamePCBoxEvent
|
||||
import com.cobblemon.mod.common.api.events.storage.WallpaperCollectionEvent
|
||||
import com.cobblemon.mod.common.api.events.storage.WallpaperUnlockedEvent
|
||||
import com.cobblemon.mod.common.api.events.world.BigRootPropagatedEvent
|
||||
import com.cobblemon.mod.common.api.reactive.CancelableObservable
|
||||
import com.cobblemon.mod.common.api.reactive.EventObservable
|
||||
@@ -110,6 +127,9 @@ object CobblemonEvents {
|
||||
@JvmField
|
||||
val BATTLE_STARTED_POST = EventObservable<BattleStartedEvent.Post>()
|
||||
|
||||
@JvmField
|
||||
val BATTLE_FLEE_ATTEMPT = EventObservable<BattleFleeAttemptEvent>()
|
||||
|
||||
@JvmField
|
||||
val BATTLE_FLED = EventObservable<BattleFledEvent>()
|
||||
|
||||
@@ -361,4 +381,12 @@ object CobblemonEvents {
|
||||
// -------------------------------------------------------------------------------------
|
||||
@JvmField
|
||||
val SELECT_DRIVER = EventObservable<SelectDriverEvent>()
|
||||
|
||||
// Habitats
|
||||
@JvmField
|
||||
val HABITAT_SPAWN_ACTIVATED = CancelableObservable<HabitatSpawnActivatedEvent>()
|
||||
@JvmField
|
||||
val HABITAT_SETTINGS_SAVED_PRE = CancelableObservable<SaveHabitatBlockSettingsEvent.Pre>()
|
||||
@JvmField
|
||||
val HABITAT_SETTINGS_SAVED_POST = EventObservable<SaveHabitatBlockSettingsEvent.Post>()
|
||||
}
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.events.battles
|
||||
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
|
||||
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
|
||||
import com.cobblemon.mod.common.util.asArrayValue
|
||||
|
||||
/**
|
||||
* Event fired when a [PokemonBattle] is fled by a [PlayerBattleActor].
|
||||
*
|
||||
* @author saultsc
|
||||
* @since March 31th 2026
|
||||
*/
|
||||
|
||||
data class BattleFleeAttemptEvent(
|
||||
override val battle: PokemonBattle,
|
||||
val player: PlayerBattleActor
|
||||
) : BattleEvent {
|
||||
val context = mutableMapOf<String, MoValue>(
|
||||
"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 }
|
||||
)
|
||||
}
|
||||
@@ -10,7 +10,9 @@ package com.cobblemon.mod.common.api.events.entity
|
||||
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.spawning.BestSpawner
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnCause
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
import net.minecraft.world.entity.Entity
|
||||
|
||||
/**
|
||||
@@ -21,4 +23,9 @@ import net.minecraft.world.entity.Entity
|
||||
* @author Hiroku
|
||||
* @since April 22nd, 2023
|
||||
*/
|
||||
class SpawnEvent<T : Entity>(val entity: T, val spawnablePosition: SpawnablePosition) : Cancelable()
|
||||
class SpawnEvent<T : Entity>(val entity: T, val spawnablePosition: SpawnablePosition) : Cancelable() {
|
||||
val cause: SpawnCause
|
||||
get() = spawnablePosition.cause
|
||||
val spawner: Spawner
|
||||
get() = spawnablePosition.spawner
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.events.habitats
|
||||
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.habitats.spawningstyle.ActivatedHabitatSpawning
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnCause
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.FixedAreaSpawner
|
||||
import com.cobblemon.mod.common.block.habitat.HabitatBlockEntity
|
||||
|
||||
/**
|
||||
* Event that fires when a habitat is about to attempt to spawn Pokémon. Cancelling this event will prevent
|
||||
* the habitat block from attempting to spawn anything for this activation.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 15th,2026
|
||||
*/
|
||||
class HabitatSpawnActivatedEvent(
|
||||
/** The habitat block entity that is responsible for the spawn. */
|
||||
val habitatBlockEntity: HabitatBlockEntity,
|
||||
/** The activation settings. You could have got it from the block entity but this is easier. */
|
||||
val activatedHabitatSpawning: ActivatedHabitatSpawning,
|
||||
/** The [SpawnCause] that will be used in the spawn action. This may contain a player that is nearest to the block. */
|
||||
var cause: SpawnCause,
|
||||
/** The maximum number of Pokémon the block will consider spawning (provided there is space). */
|
||||
var maxSpawns: Int
|
||||
) : Cancelable() {
|
||||
/** The spawner representing the habitat. */
|
||||
val spawner: FixedAreaSpawner
|
||||
get() = activatedHabitatSpawning.spawner
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.events.habitats
|
||||
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.habitats.dto.HabitatSettingsDTO
|
||||
import com.cobblemon.mod.common.block.habitat.HabitatBlockEntity
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
interface SaveHabitatBlockSettingsEvent {
|
||||
val player: ServerPlayer
|
||||
val world: ServerLevel
|
||||
val blockPos: BlockPos
|
||||
val habitatBlockEntity: HabitatBlockEntity
|
||||
val settings: HabitatSettingsDTO
|
||||
|
||||
class Pre(
|
||||
override val player: ServerPlayer,
|
||||
override val world: ServerLevel,
|
||||
override val blockPos: BlockPos,
|
||||
override val habitatBlockEntity: HabitatBlockEntity,
|
||||
override val settings: HabitatSettingsDTO
|
||||
) : SaveHabitatBlockSettingsEvent, Cancelable()
|
||||
|
||||
class Post(
|
||||
override val player: ServerPlayer,
|
||||
override val world: ServerLevel,
|
||||
override val blockPos: BlockPos,
|
||||
override val habitatBlockEntity: HabitatBlockEntity,
|
||||
override val settings: HabitatSettingsDTO
|
||||
) : SaveHabitatBlockSettingsEvent
|
||||
}
|
||||
@@ -68,11 +68,14 @@ class SpawnBait(
|
||||
val TYPING = cobblemonResource("typing")
|
||||
val EGG_GROUP = cobblemonResource("egg_group")
|
||||
val SHINY_REROLL = cobblemonResource("shiny_reroll")
|
||||
val MARK_CHANCE = cobblemonResource("mark_chance")
|
||||
val DROPS_REROLL = cobblemonResource("drops_reroll")
|
||||
val HIDDEN_ABILITY_CHANCE = cobblemonResource("ha_chance")
|
||||
val ALPHA_CHANCE = cobblemonResource("alpha_chance")
|
||||
val POKEMON_CHANCE = cobblemonResource("pokemon_chance")
|
||||
val FRIENDSHIP = cobblemonResource("friendship")
|
||||
val RARITY_BUCKET = cobblemonResource("rarity_bucket")
|
||||
val SIZE = cobblemonResource("size")
|
||||
|
||||
fun registerEffect(type: ResourceLocation, effect: (PokemonEntity, Effect) -> Unit) {
|
||||
EFFECT_FUNCTIONS[type] = effect
|
||||
@@ -86,11 +89,14 @@ class SpawnBait(
|
||||
registerEffect(NATURE) { entity, effect -> FishingSpawnCause.alterNatureAttempt(entity, effect) }
|
||||
registerEffect(IV) { entity, effect -> FishingSpawnCause.alterIVAttempt(entity, effect) }
|
||||
registerEffect(SHINY_REROLL) { entity, effect -> FishingSpawnCause.shinyReroll(entity, effect) }
|
||||
registerEffect(MARK_CHANCE) { entity, effect -> FishingSpawnCause.alterMarksAttempt(entity, effect) }
|
||||
registerEffect(DROPS_REROLL) { entity, effect -> FishingSpawnCause.saveDropsReroll(entity, effect) }
|
||||
registerEffect(GENDER_CHANCE) { entity, effect -> FishingSpawnCause.alterGenderAttempt(entity, effect) }
|
||||
registerEffect(LEVEL_RAISE) { entity, effect -> FishingSpawnCause.alterLevelAttempt(entity, effect) }
|
||||
registerEffect(HIDDEN_ABILITY_CHANCE) { entity, _ -> FishingSpawnCause.alterHAAttempt(entity) }
|
||||
registerEffect(ALPHA_CHANCE) { entity, effect -> FishingSpawnCause.alterAlphaAttempt(entity) }
|
||||
registerEffect(FRIENDSHIP) { entity, effect -> FishingSpawnCause.alterFriendshipAttempt(entity, effect) }
|
||||
registerEffect(SIZE) { entity, effect -> FishingSpawnCause.alterSize(entity, effect) }
|
||||
CobblemonEvents.BAIT_EFFECT_REGISTRATION.post(BaitEffectFunctionRegistryEvent()) { event ->
|
||||
event.functions.forEach { (type, function) -> registerEffect(type, function) }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.habitats
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* A [HabitatPool] of [ActivatedHabitatSpawn]s. Mainly just to help with deserialization and ensuring
|
||||
* a single type for everything that's applied to a habitat block.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 13th, 2026
|
||||
*/
|
||||
class ActivatedHabitatPool : HabitatPool<ActivatedHabitatSpawn>() {
|
||||
override val type: ResourceLocation = ActivatedHabitatSpawn.TYPE
|
||||
override var spawns: List<ActivatedHabitatSpawn> = listOf()
|
||||
|
||||
companion object {
|
||||
fun default() = ActivatedHabitatPool().apply {
|
||||
id = ResourceLocation("cobblemon", "custom__default_activated_pool")
|
||||
name = "Custom"
|
||||
}
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf, buckets: List<SpawnBucket>) {
|
||||
super.decode(buffer, buckets)
|
||||
spawns = buffer.readList { ActivatedHabitatSpawn().apply { decode(buffer, buckets) } }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.habitats
|
||||
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
|
||||
/**
|
||||
* A basic [HabitatSpawn] that will be triggered via activated spawning in a habitat. Nothing to it, really.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 13th, 2026
|
||||
*/
|
||||
class ActivatedHabitatSpawn : HabitatSpawn() {
|
||||
companion object {
|
||||
val TYPE = cobblemonResource("activated")
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.habitats
|
||||
|
||||
import com.cobblemon.mod.common.api.habitats.spawningstyle.ActivatedHabitatSpawning
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import net.minecraft.world.entity.Entity
|
||||
|
||||
/**
|
||||
* Influence added to the activated habitat spawner blocks so that they can track the entity and apply block modifiers.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 15th, 2026
|
||||
*/
|
||||
class ActivatedHabitatSpawningInfluence(val activatedHabitatSpawning: ActivatedHabitatSpawning) : SpawningInfluence {
|
||||
override fun affectSpawn(action: SpawnAction<*>, entity: Entity) {
|
||||
if (entity is PokemonEntity) {
|
||||
activatedHabitatSpawning.habitatBlockEntity.modifiers.apply(entity)
|
||||
activatedHabitatSpawning.habitatBlockEntity.spawnedEntityIDs.add(entity.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.habitats
|
||||
|
||||
/**
|
||||
* Controls how habitats change through all the possible phases.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 23rd, 2026
|
||||
*/
|
||||
enum class HabitatPhaseOrder {
|
||||
/** Literally in ascending order, 1-2-3-4. */
|
||||
SIMPLE,
|
||||
/** It randomly chooses an order then uses that order predictably. 3-1-4-2-3-1-4-2 for example */
|
||||
FIXED_RANDOM,
|
||||
/** Completely random phase each day. Like, 1-1-2-4-1-2-1-2-4-1-3 */
|
||||
FULL_RANDOM
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.habitats
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.block.habitat.HabitatBlockEntity
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* A pool of [HabitatSpawn]s. This is either filled with [NaturalHabitatSpawn]s or [ActivatedHabitatSpawn]s depending
|
||||
* on whether the pool is a [NaturalHabitatPool] or an [ActivatedHabitatPool]. The pool is responsible for converting
|
||||
* its spawns into [PokemonSpawnDetail]s.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 13th, 2026
|
||||
*/
|
||||
abstract class HabitatPool<T : HabitatSpawn> {
|
||||
companion object {
|
||||
val types = mutableMapOf(
|
||||
ActivatedHabitatSpawn.TYPE to ActivatedHabitatPool::class.java,
|
||||
NaturalHabitatSpawn.TYPE to NaturalHabitatPool::class.java
|
||||
)
|
||||
}
|
||||
|
||||
lateinit var id: ResourceLocation
|
||||
var name: String = ""
|
||||
abstract val type: ResourceLocation
|
||||
abstract var spawns: List<T>
|
||||
|
||||
/** Whether a pool is editable is just down to whether it's not from a datapack. */
|
||||
@Transient
|
||||
var editable: Boolean = false
|
||||
|
||||
open fun validate() {
|
||||
for (spawn in spawns) {
|
||||
if (SpawnablePosition.getByName(spawn.spawnablePositionType) == null) {
|
||||
throw IllegalArgumentException("$id mentions non-existent spawnable position type ${spawn.spawnablePositionType} on spawn for ${spawn.species}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createSpawnDetails(habitatBlockEntity: HabitatBlockEntity): List<PokemonSpawnDetail> {
|
||||
return spawns.mapNotNull { it.createSpawnDetail(habitatBlockEntity) }
|
||||
}
|
||||
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(id)
|
||||
buffer.writeUtf(name)
|
||||
buffer.writeCollection(spawns) { _, it -> it.encode(buffer) }
|
||||
}
|
||||
|
||||
open fun decode(buffer: RegistryFriendlyByteBuf, buckets: List<SpawnBucket>) {
|
||||
id = buffer.readResourceLocation()
|
||||
name = buffer.readUtf()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.habitats
|
||||
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.spawning.IntRanges
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.TimeRange
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePositionType
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
import com.cobblemon.mod.common.util.adapters.HabitatPoolAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IntRangeAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IntRangesAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.RegisteredSpawnablePositionAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.SpawnBucketAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.SpeciesAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.pokemonPropertiesShortAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.fromJson
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import java.io.File
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
import net.minecraft.world.level.storage.LevelResource
|
||||
|
||||
object HabitatPools : JsonDataRegistry<HabitatPool<*>> {
|
||||
override val id = cobblemonResource("habitat_pools")
|
||||
override val type = PackType.SERVER_DATA
|
||||
override val observable = SimpleObservable<HabitatPools>()
|
||||
override val typeToken: TypeToken<HabitatPool<*>> = TypeToken.get(HabitatPool::class.java)
|
||||
override val resourcePath = "habitat_pools"
|
||||
|
||||
override val gson: Gson = GsonBuilder()
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.registerTypeAdapter(HabitatPool::class.java, HabitatPoolAdapter)
|
||||
.registerTypeAdapter(SpawnablePositionType::class.java, RegisteredSpawnablePositionAdapter)
|
||||
.registerTypeAdapter(SpawnBucket::class.java, SpawnBucketAdapter)
|
||||
.registerTypeAdapter(Species::class.java, SpeciesAdapter)
|
||||
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
|
||||
.registerTypeAdapter(TimeRange::class.java, TimeRange.adapter)
|
||||
.registerTypeAdapter(IntRanges::class.java, IntRangesAdapter.basic)
|
||||
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
|
||||
.setPrettyPrinting()
|
||||
.create()
|
||||
|
||||
private val datapacked = mutableMapOf<ResourceLocation, HabitatPool<*>>()
|
||||
private val worldSaved = mutableMapOf<ResourceLocation, HabitatPool<*>>()
|
||||
|
||||
val habitatPoolsById = mutableMapOf<ResourceLocation, HabitatPool<*>>()
|
||||
|
||||
private lateinit var worldSavedPoolsFolder: File
|
||||
|
||||
override fun sync(player: ServerPlayer) {}
|
||||
|
||||
fun onServerLoading(server: MinecraftServer) {
|
||||
worldSavedPoolsFolder = server.getWorldPath(LevelResource.ROOT).resolve("habitat_pools").toFile()
|
||||
if (!worldSavedPoolsFolder.exists()) {
|
||||
return
|
||||
}
|
||||
|
||||
worldSavedPoolsFolder.listFiles { it.endsWith(".json") }.forEach {
|
||||
val identifier = ResourceLocation.parse(it.nameWithoutExtension)
|
||||
val habitat = gson.fromJson<HabitatPool<*>>(it.reader())
|
||||
habitat.id = identifier
|
||||
habitat.validate()
|
||||
habitat.editable = true
|
||||
worldSaved[identifier] = habitat
|
||||
}
|
||||
setupHabitatPoolsById()
|
||||
}
|
||||
|
||||
override fun reload(data: Map<ResourceLocation, HabitatPool<*>>) {
|
||||
datapacked.clear()
|
||||
datapacked.putAll(data)
|
||||
data.entries.forEach { (identifier, habitat) ->
|
||||
habitat.id = identifier
|
||||
habitat.validate()
|
||||
habitat.editable = false
|
||||
}
|
||||
setupHabitatPoolsById()
|
||||
}
|
||||
|
||||
fun setupHabitatPoolsById() {
|
||||
habitatPoolsById.clear()
|
||||
habitatPoolsById.putAll(datapacked)
|
||||
habitatPoolsById.putAll(worldSaved)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* 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.habitats
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.spawning.BestSpawner
|
||||
import com.cobblemon.mod.common.api.spawning.IntRanges
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.TimeRange
|
||||
import com.cobblemon.mod.common.api.spawning.condition.AppendageCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.block.habitat.HabitatBlockEntity
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.adapters.IntRangeAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IntRangesAdapter
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.math.intersection
|
||||
import com.cobblemon.mod.common.util.math.intersects
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
/**
|
||||
* A kind of simplified spawn definition for the purposes of spawning from [HabitatBlockEntity] instances. This is a
|
||||
* very shallow class tree, with [ActivatedHabitatSpawn] being a trivial subclass but the other subclass being
|
||||
* [NaturalHabitatSpawn] which introduces the need to supply a spawn bucket.
|
||||
*
|
||||
* These have only a small subset of the properties that a regular [PokemonSpawnDetail] would have to align with the
|
||||
* fact that these are configured from a simple GUI. There are limits to how many properties and conditions could be
|
||||
* exposed in a GUI before it becomes too difficult for the average user to understand what's going on.
|
||||
*
|
||||
* [HabitatSpawn]s are loaded from datapacks as pools of spawns but can also be modified from in-game, though those
|
||||
* made from in-game are stored separately to datapacks. Only non-datapacked habitat spawns can be modified.
|
||||
*
|
||||
* Upon edits to a habitat block, the spawn details are regenerated based off of these definitions.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 13th, 2026
|
||||
*/
|
||||
sealed class HabitatSpawn {
|
||||
/** The species that will be spawned. */
|
||||
lateinit var species: Species
|
||||
/** The kind of position the Pokémon will spawn in. Things like "grounded", "seafloor", "fishing". */
|
||||
lateinit var spawnablePositionType: String
|
||||
|
||||
/** Selection weight. */
|
||||
var weight: Float = 1F
|
||||
/** The possible levels for this spawn. */
|
||||
var levelRange: IntRange = 1..Int.MAX_VALUE // I can't access the up-to-date config value here.
|
||||
/** Additional modifiers to apply to the Pokémon, such as alolan=true, ability=intimidate, etc. */
|
||||
var modifiers: PokemonProperties? = null
|
||||
/** Which phases of the habitat this spawn will run in. See [HabitatBlockEntity] for more information. */
|
||||
var phases: IntRanges? = null
|
||||
/** The Minecraft time ranges for this spawn. */
|
||||
var timeRange: TimeRange? = null
|
||||
/** The minimum light level that the Pokémon can spawn in. */
|
||||
var minLight: Int? = null
|
||||
/** The maximum light level that the Pokémon can spawn in. */
|
||||
var maxLight: Int? = null
|
||||
|
||||
open fun writeToNBT(nbt: CompoundTag) {
|
||||
nbt.putString(DataKeys.HABITAT_POOL_SPAWN_SPECIES, species.resourceIdentifier.toString())
|
||||
nbt.putString(DataKeys.HABITAT_POOL_SPAWN_POSITION_TYPE, spawnablePositionType)
|
||||
nbt.putFloat(DataKeys.HABITAT_POOL_SPAWN_WEIGHT, weight)
|
||||
nbt.putString(DataKeys.HABITAT_POOL_SPAWN_LEVEL_RANGE, IntRangeAdapter.serialize(levelRange))
|
||||
modifiers?.let { nbt.putString(DataKeys.HABITAT_POOL_SPAWN_MODIFIERS, it.asString()) }
|
||||
phases?.let { nbt.putString(DataKeys.HABITAT_POOL_SPAWN_PHASES, IntRangesAdapter.basic.serialize(it)) }
|
||||
timeRange?.let { nbt.putString(DataKeys.HABITAT_POOL_SPAWN_TIMES, TimeRange.adapter.serialize(it)) }
|
||||
minLight?.let { nbt.putInt(DataKeys.HABITAT_POOL_SPAWN_MIN_LIGHT, it) }
|
||||
maxLight?.let { nbt.putInt(DataKeys.HABITAT_POOL_SPAWN_MAX_LIGHT, it) }
|
||||
}
|
||||
|
||||
open fun readFromNBT(nbt: CompoundTag) {
|
||||
species = PokemonSpecies.getByIdentifier(nbt.getString(DataKeys.HABITAT_POOL_SPAWN_SPECIES).asIdentifierDefaultingNamespace())!!
|
||||
spawnablePositionType = nbt.getString(DataKeys.HABITAT_POOL_SPAWN_POSITION_TYPE)
|
||||
weight = nbt.getFloat(DataKeys.HABITAT_POOL_SPAWN_WEIGHT)
|
||||
levelRange = IntRangeAdapter.deserialize(nbt.getString(DataKeys.HABITAT_POOL_SPAWN_LEVEL_RANGE))
|
||||
modifiers = if (nbt.contains(DataKeys.HABITAT_POOL_SPAWN_MODIFIERS)) PokemonProperties.parse(nbt.getString(DataKeys.HABITAT_POOL_SPAWN_MODIFIERS)) else null
|
||||
phases = if (nbt.contains(DataKeys.HABITAT_POOL_SPAWN_PHASES)) IntRangesAdapter.basic.deserialize(nbt.getString(DataKeys.HABITAT_POOL_SPAWN_PHASES)) else null
|
||||
timeRange = if (nbt.contains(DataKeys.HABITAT_POOL_SPAWN_TIMES)) TimeRange.adapter.deserialize(nbt.getString(DataKeys.HABITAT_POOL_SPAWN_TIMES)) else null
|
||||
minLight = if (nbt.contains(DataKeys.HABITAT_POOL_SPAWN_MIN_LIGHT)) nbt.getInt(DataKeys.HABITAT_POOL_SPAWN_MIN_LIGHT) else null
|
||||
maxLight = if (nbt.contains(DataKeys.HABITAT_POOL_SPAWN_MAX_LIGHT)) nbt.getInt(DataKeys.HABITAT_POOL_SPAWN_MAX_LIGHT) else null
|
||||
}
|
||||
|
||||
open fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(species.resourceIdentifier)
|
||||
buffer.writeUtf(spawnablePositionType)
|
||||
buffer.writeFloat(weight)
|
||||
buffer.writeUtf(IntRangeAdapter.serialize(levelRange))
|
||||
buffer.writeNullable(modifiers) { _, value -> buffer.writeUtf(value.asString()) }
|
||||
buffer.writeNullable(phases) { _, value -> buffer.writeUtf(IntRangesAdapter.basic.serialize(value)) }
|
||||
buffer.writeNullable(timeRange) { _, value -> buffer.writeUtf(TimeRange.adapter.serialize(value)) }
|
||||
buffer.writeNullable(minLight) { _, value -> buffer.writeInt(value) }
|
||||
buffer.writeNullable(maxLight) { _, value -> buffer.writeInt(value) }
|
||||
}
|
||||
|
||||
open fun decode(
|
||||
buffer: RegistryFriendlyByteBuf,
|
||||
buckets: List<SpawnBucket>
|
||||
) {
|
||||
species = PokemonSpecies.getByIdentifier(buffer.readResourceLocation())!!
|
||||
spawnablePositionType = buffer.readUtf()
|
||||
weight = buffer.readFloat()
|
||||
levelRange = IntRangeAdapter.deserialize(buffer.readUtf())
|
||||
modifiers = buffer.readNullable { _ -> PokemonProperties.parse(buffer.readUtf()) }
|
||||
phases = buffer.readNullable { _ -> IntRangesAdapter.basic.deserialize(buffer.readUtf()) }
|
||||
timeRange = buffer.readNullable { _ -> TimeRange.adapter.deserialize(buffer.readUtf()) }
|
||||
minLight = buffer.readNullable { _ -> buffer.readInt() }
|
||||
maxLight = buffer.readNullable { _ -> buffer.readInt() }
|
||||
}
|
||||
|
||||
/** Creates the [PokemonSpawnDetail] that will be used in the spawner. This is done per habitat block. */
|
||||
open fun createSpawnDetail(habitatBlockEntity: HabitatBlockEntity): PokemonSpawnDetail? {
|
||||
val spawnDetail = PokemonSpawnDetail()
|
||||
|
||||
// If the habitat block specifies a level range, it restricts how and if this spawn exists
|
||||
if (!levelRange.intersects(habitatBlockEntity.levelRange)) {
|
||||
return null
|
||||
}
|
||||
|
||||
val spawnablePositionType = SpawnablePosition.getByName(this.spawnablePositionType) ?: return null // How'd they get bad data into here
|
||||
|
||||
val levelRange = levelRange.intersection(habitatBlockEntity.levelRange)
|
||||
|
||||
val condition = createCondition(habitatBlockEntity)
|
||||
val properties = modifiers?.copy() ?: PokemonProperties()
|
||||
properties.species = species.resourceIdentifier.path
|
||||
|
||||
spawnDetail.apply {
|
||||
this.id = "habitat_${species.resourceIdentifier}"
|
||||
this.pokemon = properties
|
||||
this.bucket = BestSpawner.config.buckets.first { it.weight > 0 }
|
||||
this.weight = this@HabitatSpawn.weight
|
||||
this.levelRange = levelRange
|
||||
this.conditions = mutableListOf(condition)
|
||||
this.spawnablePositionType = spawnablePositionType
|
||||
}
|
||||
|
||||
return spawnDetail
|
||||
}
|
||||
|
||||
fun createCondition(habitatBlockEntity: HabitatBlockEntity): SpawningCondition<*> {
|
||||
val clazz = SpawningCondition.getByName(SpawnablePosition.getByName(this.spawnablePositionType)!!.defaultCondition)!!
|
||||
val condition = clazz.getConstructor().newInstance()
|
||||
condition.timeRange = timeRange
|
||||
condition.minLight = minLight
|
||||
condition.maxLight = maxLight
|
||||
val phases = phases
|
||||
if (phases != null) {
|
||||
condition.appendages.add(PhaseAppendageCondition(habitatBlockEntity, phases))
|
||||
}
|
||||
return condition
|
||||
}
|
||||
|
||||
class PhaseAppendageCondition(val habitatBlockEntity: HabitatBlockEntity, val phases: IntRanges) : AppendageCondition {
|
||||
override fun fits(spawnablePosition: SpawnablePosition) = habitatBlockEntity.currentPhase in phases
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.habitats
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* A [HabitatPool] of [NaturalHabitatSpawn]s. Mainly just to help with deserialization and ensuring
|
||||
* a single type for everything that's applied to a habitat block.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 13th, 2026
|
||||
*/
|
||||
class NaturalHabitatPool : HabitatPool<NaturalHabitatSpawn>() {
|
||||
override val type: ResourceLocation = NaturalHabitatSpawn.TYPE
|
||||
override var spawns: List<NaturalHabitatSpawn> = listOf()
|
||||
|
||||
companion object {
|
||||
fun default() = NaturalHabitatPool().apply {
|
||||
id = ResourceLocation("cobblemon", "custom__default_natural_pool")
|
||||
name = "Custom"
|
||||
}
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf, buckets: List<SpawnBucket>) {
|
||||
super.decode(buffer, buckets)
|
||||
spawns = buffer.readList { NaturalHabitatSpawn().apply { decode(buffer, buckets) } }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.habitats
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
|
||||
import com.cobblemon.mod.common.block.habitat.HabitatBlockEntity
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
/**
|
||||
* A type of [HabitatSpawn] that spawns Pokémon as part of natural world spawning, and therefore
|
||||
* needs to indicate the [SpawnBucket] it will be part of.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 13th, 2026
|
||||
*/
|
||||
class NaturalHabitatSpawn : HabitatSpawn() {
|
||||
companion object {
|
||||
val TYPE = cobblemonResource("natural")
|
||||
}
|
||||
|
||||
lateinit var bucket: SpawnBucket
|
||||
|
||||
override fun createSpawnDetail(habitatBlockEntity: HabitatBlockEntity): PokemonSpawnDetail? {
|
||||
val spawnDetail = super.createSpawnDetail(habitatBlockEntity)
|
||||
spawnDetail?.bucket = bucket
|
||||
return spawnDetail
|
||||
}
|
||||
|
||||
override fun writeToNBT(nbt: CompoundTag) {
|
||||
super.writeToNBT(nbt)
|
||||
nbt.putString(DataKeys.HABITAT_POOL_SPAWN_BUCKET, bucket.name)
|
||||
}
|
||||
|
||||
override fun readFromNBT(nbt: CompoundTag) {
|
||||
super.readFromNBT(nbt)
|
||||
bucket = Cobblemon.bestSpawner.config.buckets.first { it.name == nbt.getString(DataKeys.HABITAT_POOL_SPAWN_BUCKET) }
|
||||
}
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
buffer.writeUtf(bucket.name)
|
||||
}
|
||||
|
||||
override fun decode(
|
||||
buffer: RegistryFriendlyByteBuf,
|
||||
buckets: List<SpawnBucket>
|
||||
) {
|
||||
super.decode(buffer, buckets)
|
||||
val bucketName = buffer.readUtf()
|
||||
bucket = buckets.first { it.name == bucketName }
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.habitats.dto
|
||||
|
||||
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.spawningstyle.ActivatedHabitatSpawning
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
class ActivatedHabitatSettingsDTO() {
|
||||
var chance = 1F
|
||||
var trigger: ActivatedHabitatSpawning.Trigger = ActivatedHabitatSpawning.Trigger.REDSTONE
|
||||
var cancelledNaturalSpawningRange = -1
|
||||
var spawnRange = 16
|
||||
var maxSpawns = -1
|
||||
var maxSpawnsPerActivation = 1
|
||||
var habitatPool: HabitatPoolSettingsDTO = HabitatPoolSettingsDTO(ActivatedHabitatPool.default())
|
||||
|
||||
constructor(activatedHabitatSpawning: ActivatedHabitatSpawning): this() {
|
||||
this.chance = activatedHabitatSpawning.chance
|
||||
this.trigger = activatedHabitatSpawning.trigger
|
||||
this.cancelledNaturalSpawningRange = activatedHabitatSpawning.cancelledNaturalSpawningRange
|
||||
this.spawnRange = activatedHabitatSpawning.spawnRange
|
||||
this.maxSpawns = activatedHabitatSpawning.maxSpawns
|
||||
this.maxSpawnsPerActivation = activatedHabitatSpawning.maxSpawnsPerActivation
|
||||
this.habitatPool = HabitatPoolSettingsDTO(activatedHabitatSpawning.pool)
|
||||
}
|
||||
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeFloat(chance)
|
||||
buffer.writeEnum(trigger)
|
||||
buffer.writeInt(cancelledNaturalSpawningRange)
|
||||
buffer.writeInt(spawnRange)
|
||||
buffer.writeInt(maxSpawns)
|
||||
buffer.writeInt(maxSpawnsPerActivation)
|
||||
habitatPool.encode(buffer)
|
||||
}
|
||||
|
||||
fun decode(
|
||||
buffer: RegistryFriendlyByteBuf,
|
||||
buckets: List<SpawnBucket>
|
||||
) {
|
||||
this.chance = buffer.readFloat()
|
||||
this.trigger = buffer.readEnum(ActivatedHabitatSpawning.Trigger::class.java)
|
||||
this.cancelledNaturalSpawningRange = buffer.readInt()
|
||||
this.spawnRange = buffer.readInt()
|
||||
this.maxSpawns = buffer.readInt()
|
||||
this.maxSpawnsPerActivation = buffer.readInt()
|
||||
this.habitatPool = HabitatPoolSettingsDTO()
|
||||
habitatPool.decode(buffer, buckets)
|
||||
}
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.habitats.dto
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatSpawn
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatPools
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatSpawn
|
||||
import com.cobblemon.mod.common.api.habitats.NaturalHabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.NaturalHabitatSpawn
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
class HabitatPoolSettingsDTO() {
|
||||
lateinit var id: ResourceLocation
|
||||
var isReference = false
|
||||
var isActivated = false
|
||||
|
||||
// Below properties are only there when it's not a reference, otherwise the handler will have to look them up from the id
|
||||
var name: String? = null
|
||||
var spawns: MutableList<HabitatSpawn>? = null
|
||||
|
||||
constructor(habitatPool: HabitatPool<*>): this() {
|
||||
this.id = habitatPool.id
|
||||
this.isReference = !id.path.startsWith("custom__")
|
||||
this.isActivated = habitatPool is ActivatedHabitatPool
|
||||
if (!this.isReference) {
|
||||
this.name = habitatPool.name
|
||||
this.spawns = habitatPool.spawns.toMutableList()
|
||||
}
|
||||
}
|
||||
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(id)
|
||||
buffer.writeBoolean(isReference)
|
||||
buffer.writeBoolean(isActivated)
|
||||
if (!isReference) {
|
||||
buffer.writeUtf(name!!)
|
||||
buffer.writeCollection(spawns!!) { _, spawn ->
|
||||
spawn.encode(buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun decode(
|
||||
buffer: RegistryFriendlyByteBuf,
|
||||
buckets: List<SpawnBucket>
|
||||
) {
|
||||
this.id = buffer.readResourceLocation()
|
||||
this.isReference = buffer.readBoolean()
|
||||
this.isActivated = buffer.readBoolean()
|
||||
if (!isReference) {
|
||||
this.name = buffer.readUtf()
|
||||
this.spawns = buffer.readList {
|
||||
val spawn = if (isActivated) {
|
||||
ActivatedHabitatSpawn()
|
||||
} else {
|
||||
NaturalHabitatSpawn()
|
||||
}
|
||||
spawn.decode(buffer, buckets)
|
||||
spawn
|
||||
}.toMutableList()
|
||||
}
|
||||
}
|
||||
|
||||
fun toHabitatPool(): HabitatPool<*>? {
|
||||
if (isReference) {
|
||||
val pool = HabitatPools.habitatPoolsById[id]
|
||||
if (pool == null) {
|
||||
Cobblemon.LOGGER.error("Could not find habitat pool with id $id")
|
||||
} else if (pool is ActivatedHabitatPool && !isActivated) {
|
||||
Cobblemon.LOGGER.error("Pool with id $id is an activated habitat pool but was deserialized as a natural pool")
|
||||
} else if (pool !is ActivatedHabitatPool && isActivated) {
|
||||
Cobblemon.LOGGER.error("Pool with id $id is a natural habitat pool but was deserialized as an activated pool")
|
||||
}
|
||||
return pool
|
||||
}
|
||||
return if (isActivated) {
|
||||
val pool = ActivatedHabitatPool()
|
||||
pool.id = id
|
||||
pool.name = name!!
|
||||
pool.spawns = spawns!!.map { it as ActivatedHabitatSpawn }
|
||||
pool
|
||||
} else {
|
||||
val pool = NaturalHabitatPool()
|
||||
pool.id = id
|
||||
pool.name = name!!
|
||||
pool.spawns = spawns!!.map { it as NaturalHabitatSpawn }
|
||||
pool
|
||||
}
|
||||
}
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.habitats.dto
|
||||
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatPhaseOrder
|
||||
import com.cobblemon.mod.common.api.habitats.spawningstyle.ActivatedHabitatSpawning
|
||||
import com.cobblemon.mod.common.api.habitats.spawningstyle.NaturalHabitatSpawning
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.block.habitat.HabitatBlockEntity
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
class HabitatSettingsDTO() {
|
||||
var numberOfPhases = 1
|
||||
var phaseOrder = HabitatPhaseOrder.SIMPLE
|
||||
lateinit var levelRange: IntRange
|
||||
var modifiers: String = ""
|
||||
lateinit var mimicId: ResourceLocation
|
||||
var isActivatedSpawning = false
|
||||
var activatedSettings: ActivatedHabitatSettingsDTO? = null
|
||||
var naturalSettings: NaturalHabitatSettingsDTO? = null
|
||||
|
||||
constructor(habitatBlockEntity: HabitatBlockEntity) : this() {
|
||||
this.numberOfPhases = habitatBlockEntity.numberOfPhases
|
||||
this.phaseOrder = habitatBlockEntity.phaseOrder
|
||||
this.levelRange = habitatBlockEntity.levelRange
|
||||
this.modifiers = habitatBlockEntity.modifiers.asString()
|
||||
this.mimicId = habitatBlockEntity.mimicId
|
||||
val style = habitatBlockEntity.spawningStyle
|
||||
if (style is ActivatedHabitatSpawning) {
|
||||
this.isActivatedSpawning = true
|
||||
this.activatedSettings = ActivatedHabitatSettingsDTO(style)
|
||||
} else if (style is NaturalHabitatSpawning) {
|
||||
this.naturalSettings = NaturalHabitatSettingsDTO(style)
|
||||
}
|
||||
}
|
||||
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeInt(numberOfPhases)
|
||||
buffer.writeEnum(phaseOrder)
|
||||
buffer.writeInt(levelRange.first)
|
||||
buffer.writeInt(levelRange.last)
|
||||
buffer.writeUtf(modifiers)
|
||||
buffer.writeResourceLocation(mimicId)
|
||||
buffer.writeBoolean(isActivatedSpawning)
|
||||
if (isActivatedSpawning) {
|
||||
activatedSettings?.encode(buffer)
|
||||
} else {
|
||||
naturalSettings?.encode(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
fun decode(
|
||||
buffer: RegistryFriendlyByteBuf,
|
||||
buckets: List<SpawnBucket>
|
||||
) {
|
||||
this.numberOfPhases = buffer.readInt()
|
||||
this.phaseOrder = buffer.readEnum(HabitatPhaseOrder::class.java)
|
||||
val levelRangeStart = buffer.readInt()
|
||||
val levelRangeEnd = buffer.readInt()
|
||||
this.levelRange = levelRangeStart..levelRangeEnd
|
||||
this.modifiers = buffer.readUtf()
|
||||
this.mimicId = buffer.readResourceLocation()
|
||||
if (buffer.readBoolean()) {
|
||||
this.isActivatedSpawning = true
|
||||
this.naturalSettings = null
|
||||
val activatedSettings = ActivatedHabitatSettingsDTO()
|
||||
activatedSettings.decode(buffer, buckets)
|
||||
this.activatedSettings = activatedSettings
|
||||
} else {
|
||||
this.activatedSettings = null
|
||||
val naturalSettings = NaturalHabitatSettingsDTO()
|
||||
naturalSettings.decode(buffer, buckets)
|
||||
this.naturalSettings = naturalSettings
|
||||
}
|
||||
}
|
||||
}
|
||||
+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.api.habitats.dto
|
||||
|
||||
import com.cobblemon.mod.common.api.habitats.NaturalHabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.spawningstyle.NaturalHabitatSpawning
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
class NaturalHabitatSettingsDTO() {
|
||||
var replaceSpawns = false
|
||||
var rangeOfInfluence = 16
|
||||
var habitatPool: HabitatPoolSettingsDTO = HabitatPoolSettingsDTO(NaturalHabitatPool.default())
|
||||
|
||||
constructor(naturalHabitatSpawning: NaturalHabitatSpawning): this() {
|
||||
this.replaceSpawns = naturalHabitatSpawning.replaceSpawns
|
||||
this.rangeOfInfluence = naturalHabitatSpawning.rangeOfInfluence
|
||||
this.habitatPool = HabitatPoolSettingsDTO(naturalHabitatSpawning.pool)
|
||||
}
|
||||
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeBoolean(replaceSpawns)
|
||||
buffer.writeInt(rangeOfInfluence)
|
||||
habitatPool.encode(buffer)
|
||||
}
|
||||
|
||||
fun decode(
|
||||
buffer: RegistryFriendlyByteBuf,
|
||||
buckets: List<SpawnBucket>
|
||||
) {
|
||||
this.replaceSpawns = buffer.readBoolean()
|
||||
this.rangeOfInfluence = buffer.readInt()
|
||||
this.habitatPool = HabitatPoolSettingsDTO()
|
||||
habitatPool.decode(buffer, buckets)
|
||||
}
|
||||
}
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* 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.habitats.spawningstyle
|
||||
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.habitats.HabitatSpawnActivatedEvent
|
||||
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatSpawn
|
||||
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatSpawningInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnCause
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.FixedAreaSpawner
|
||||
import com.cobblemon.mod.common.block.habitat.HabitatBlockEntity
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
|
||||
/**
|
||||
* A habitat spawning style defined by being triggered by something. The activation trigger, [trigger], is either
|
||||
* a redstone pulse, every tick, or every random tick.
|
||||
*
|
||||
* The spawn details are generated from the [pool] every time there is some change to the habitat block's settings,
|
||||
* then those spawn details are used for spawning Pokémon when the habitat block is triggered.
|
||||
*
|
||||
* The [chance] property is applied after the trigger logic so that not every activation necessarily results in a spawn
|
||||
* attempt.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 14th, 2026
|
||||
*/
|
||||
class ActivatedHabitatSpawning(val habitatBlockEntity: HabitatBlockEntity) : HabitatSpawningStyle {
|
||||
enum class Trigger {
|
||||
/** Requires a redstone pulse. */
|
||||
REDSTONE,
|
||||
/** Tries every tick. */
|
||||
TICK,
|
||||
/** Tries every random tick. */
|
||||
RANDOM_TICK
|
||||
}
|
||||
|
||||
override val type = ActivatedHabitatSpawn.TYPE
|
||||
|
||||
/** The chance that an activation by the [trigger] will result in a spawn attempt. The spawn attempt may still fail. */
|
||||
var chance = 1F
|
||||
/** The thing that will activate the habitat block into attempting to spawn something. */
|
||||
var trigger = Trigger.REDSTONE
|
||||
/** What range, in blocks, around the habitat block in which natural spawning will not occur. */
|
||||
var cancelledNaturalSpawningRange = -1
|
||||
/** The range, in blocks, around the habitat block in which spawning will be attempted. */
|
||||
var spawnRange = 16
|
||||
/** The maximum number of Pokémon that can exist from this block's spawning before it pauses. -1 for infinite. */
|
||||
var maxSpawns = -1
|
||||
/** The maximum number of Pokémon that can spawn in a single activation. This will still be capped by [maxSpawns]. */
|
||||
var maxSpawnsPerActivation = 1
|
||||
/** The pool of spawns to choose from. */
|
||||
var pool: ActivatedHabitatPool = ActivatedHabitatPool.default()
|
||||
|
||||
// This is cached here so that it doesn't have to be regenerated every time the block is triggered, updated when settings change.
|
||||
var spawnDetails: List<SpawnDetail> = listOf()
|
||||
lateinit var spawner: FixedAreaSpawner
|
||||
|
||||
override fun writeToNBT(nbt: CompoundTag) {
|
||||
nbt.putString(DataKeys.HABITAT_SPAWNING_STYLE, type.toString())
|
||||
nbt.putFloat(DataKeys.HABITAT_ACTIVATED_CHANCE, chance)
|
||||
nbt.putString(DataKeys.HABITAT_ACTIVATED_TRIGGER, trigger.name)
|
||||
nbt.putInt(DataKeys.HABITAT_ACTIVATED_CANCEL_RANGE, cancelledNaturalSpawningRange)
|
||||
nbt.putInt(DataKeys.HABITAT_ACTIVATED_SPAWN_RANGE, spawnRange)
|
||||
nbt.putInt(DataKeys.HABITAT_ACTIVATED_MAX_SPAWNS, maxSpawns)
|
||||
nbt.putInt(DataKeys.HABITAT_ACTIVATED_MAX_SPAWNS_PER_ACTIVATION, maxSpawnsPerActivation)
|
||||
|
||||
val poolNBT = CompoundTag()
|
||||
writePoolToNBT(pool, poolNBT)
|
||||
nbt.put(DataKeys.HABITAT_POOL, poolNBT)
|
||||
}
|
||||
|
||||
override fun readFromNBT(nbt: CompoundTag) {
|
||||
chance = nbt.getFloat(DataKeys.HABITAT_ACTIVATED_CHANCE)
|
||||
trigger = Trigger.valueOf(nbt.getString(DataKeys.HABITAT_ACTIVATED_TRIGGER) ?: Trigger.REDSTONE.name)
|
||||
cancelledNaturalSpawningRange = nbt.getInt(DataKeys.HABITAT_ACTIVATED_CANCEL_RANGE)
|
||||
spawnRange = nbt.getInt(DataKeys.HABITAT_ACTIVATED_SPAWN_RANGE)
|
||||
maxSpawns = nbt.getInt(DataKeys.HABITAT_ACTIVATED_MAX_SPAWNS)
|
||||
maxSpawnsPerActivation = nbt.getInt(DataKeys.HABITAT_ACTIVATED_MAX_SPAWNS_PER_ACTIVATION)
|
||||
|
||||
pool = HabitatSpawningStyle.readPoolFromNBT(
|
||||
nbt = nbt.getCompound(DataKeys.HABITAT_POOL),
|
||||
poolInitializer = ::ActivatedHabitatPool,
|
||||
spawnInitializer = ::ActivatedHabitatSpawn,
|
||||
defaultPool = ActivatedHabitatPool::default
|
||||
)
|
||||
}
|
||||
|
||||
fun generateSpawnDetails(world: ServerLevel, pos: BlockPos) {
|
||||
spawnDetails = pool.createSpawnDetails(habitatBlockEntity)
|
||||
spawner = FixedAreaSpawner(
|
||||
name = "habitat_spawner_$pos",
|
||||
world = world,
|
||||
position = pos,
|
||||
spawnPool = SpawnPool("habitat_block").also {
|
||||
it.details.addAll(spawnDetails)
|
||||
it.precalculate()
|
||||
},
|
||||
maxPokemonPerChunk = 16F,
|
||||
verticalRadius = spawnRange,
|
||||
horizontalRadius = spawnRange
|
||||
)
|
||||
spawner.influences.add(ActivatedHabitatSpawningInfluence(this))
|
||||
}
|
||||
|
||||
fun activate(world: ServerLevel, pos: BlockPos) {
|
||||
// Check if the spawn attempt should even happen based on chance.
|
||||
if (chance < 1F && world.random.nextFloat() >= chance) {
|
||||
return
|
||||
} else if (spawnDetails.isEmpty()) {
|
||||
// If there are no spawn details, then there's no point in trying to spawn anything.
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the max spawns has been reached.
|
||||
if (maxSpawns != -1) {
|
||||
val existingSpawns = habitatBlockEntity.spawnedEntityIDs.size
|
||||
if (existingSpawns >= maxSpawns) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
val cause = SpawnCause(
|
||||
spawner = spawner,
|
||||
entity = world.getNearestPlayer(
|
||||
pos.x.toDouble(),
|
||||
pos.y.toDouble(),
|
||||
pos.z.toDouble(),
|
||||
64.0,
|
||||
true
|
||||
)
|
||||
)
|
||||
|
||||
val remainingUntilMaxSpawns = if (maxSpawns == -1) Int.MAX_VALUE else maxSpawns - habitatBlockEntity.spawnedEntityIDs.size
|
||||
val maxSpawnsForActivation = if (maxSpawnsPerActivation == -1) Int.MAX_VALUE else maxSpawnsPerActivation
|
||||
val maxSpawns = minOf(remainingUntilMaxSpawns, maxSpawnsForActivation)
|
||||
|
||||
val event = HabitatSpawnActivatedEvent(
|
||||
habitatBlockEntity = habitatBlockEntity,
|
||||
activatedHabitatSpawning = this,
|
||||
cause = cause,
|
||||
maxSpawns = maxSpawns
|
||||
)
|
||||
|
||||
CobblemonEvents.HABITAT_SPAWN_ACTIVATED.postThen(event = event) {
|
||||
spawner.run(
|
||||
cause = event.cause,
|
||||
maxSpawns = event.maxSpawns
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.habitats.spawningstyle
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatPools
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatSpawn
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.ListTag
|
||||
import net.minecraft.nbt.Tag
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* Holds habitat block specific details about the methodology behind how the habitat block will spawn Pokémon. This is
|
||||
* either an [ActivatedHabitatSpawning] or a [NaturalHabitatSpawning].
|
||||
*
|
||||
* @see ActivatedHabitatSpawning
|
||||
* @see NaturalHabitatSpawning
|
||||
* @author Hiroku
|
||||
* @since February 14th, 2026
|
||||
*/
|
||||
sealed interface HabitatSpawningStyle {
|
||||
val type: ResourceLocation
|
||||
|
||||
fun writeToNBT(nbt: CompoundTag)
|
||||
fun readFromNBT(nbt: CompoundTag)
|
||||
|
||||
fun writePoolToNBT(pool: HabitatPool<*>, nbt: CompoundTag) {
|
||||
nbt.putString(DataKeys.HABITAT_POOL_ID, pool.id.toString())
|
||||
if (pool.id.path.startsWith("custom__")) {
|
||||
nbt.putString(DataKeys.HABITAT_POOL_NAME, pool.name)
|
||||
val spawnsListNBT = ListTag()
|
||||
for (spawn in pool.spawns) {
|
||||
val spawnTag = CompoundTag()
|
||||
spawn.writeToNBT(spawnTag)
|
||||
spawnsListNBT.add(spawnTag)
|
||||
}
|
||||
nbt.put(DataKeys.HABITAT_POOL_SPAWNS, spawnsListNBT)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun <T : HabitatSpawn, P : HabitatPool<T>> readPoolFromNBT(nbt: CompoundTag, poolInitializer: () -> P, spawnInitializer: () -> T, defaultPool: () -> P): P {
|
||||
var pool: P
|
||||
val poolId = nbt.getString(DataKeys.HABITAT_POOL_ID).asIdentifierDefaultingNamespace()
|
||||
if (nbt.contains(DataKeys.HABITAT_POOL_NAME)) {
|
||||
// It's one that was stored on the block
|
||||
pool = poolInitializer()
|
||||
pool.id = poolId
|
||||
pool.name = nbt.getString(DataKeys.HABITAT_POOL_NAME)
|
||||
val spawnsListNBT = nbt.getList(DataKeys.HABITAT_POOL_SPAWNS, Tag.TAG_COMPOUND.toInt())
|
||||
val spawns = mutableListOf<T>()
|
||||
spawnsListNBT.forEach { spawnTag ->
|
||||
try {
|
||||
val spawn = spawnInitializer().apply { readFromNBT(spawnTag as CompoundTag) }
|
||||
spawns.add(spawn)
|
||||
} catch (_: Exception) {
|
||||
// If there's an error reading a spawn, skip it and log the error.
|
||||
Cobblemon.LOGGER.error("Error reading activated habitat spawn from NBT for habitat block, skipping.")
|
||||
}
|
||||
}
|
||||
pool.spawns = spawns
|
||||
} else {
|
||||
pool = HabitatPools.habitatPoolsById[poolId] as? P ?: defaultPool()
|
||||
}
|
||||
|
||||
return pool
|
||||
}
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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.habitats.spawningstyle
|
||||
|
||||
import com.cobblemon.mod.common.api.habitats.NaturalHabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.NaturalHabitatSpawn
|
||||
import com.cobblemon.mod.common.api.habitats.spawningstyle.HabitatSpawningStyle.Companion.readPoolFromNBT
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.block.habitat.HabitatBlockEntity
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* A habitat spawning style defined by tapping into regular spawning methods. This works by adding to the spawn list
|
||||
* for spawning that's occurring nearby. When [replaceSpawns] is set to true, the original spawns will be
|
||||
* removed completely in favour of the spawns provided in this habitat's [pool].
|
||||
*
|
||||
* The spawn details are generated from the [pool] every time there is some change to the habitat block's settings,
|
||||
* then those spawn details are used for spawning Pokémon when world spawning is occurring nearby.
|
||||
*
|
||||
* Whether or not this interactions with overworld or fishing spawns is determined by whether the habitat pool has
|
||||
* overworld or fishing spawns respectively.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 22nd, 2026
|
||||
*/
|
||||
class NaturalHabitatSpawning(val habitatBlockEntity: HabitatBlockEntity) : HabitatSpawningStyle {
|
||||
@Transient
|
||||
override val type: ResourceLocation = NaturalHabitatSpawn.TYPE
|
||||
|
||||
var pool: NaturalHabitatPool = NaturalHabitatPool.default()
|
||||
var replaceSpawns = false
|
||||
var rangeOfInfluence = 16
|
||||
|
||||
@Transient
|
||||
var affectsFishing = false
|
||||
@Transient
|
||||
var affectsOverworld = false
|
||||
@Transient
|
||||
var spawnDetails: List<SpawnDetail> = listOf()
|
||||
|
||||
fun generateSpawnDetails() {
|
||||
spawnDetails = pool.createSpawnDetails(habitatBlockEntity = habitatBlockEntity)
|
||||
affectsFishing = spawnDetails.any { it.spawnablePositionType.name == "fishing" }
|
||||
affectsOverworld = spawnDetails.any { it.spawnablePositionType.name != "fishing" }
|
||||
}
|
||||
|
||||
override fun writeToNBT(nbt: CompoundTag) {
|
||||
nbt.putBoolean(DataKeys.HABITAT_NATURAL_REPLACE_SPAWNS, replaceSpawns)
|
||||
nbt.putInt(DataKeys.HABITAT_NATURAL_RANGE_OF_INFLUENCE, rangeOfInfluence)
|
||||
|
||||
val poolNBT = CompoundTag()
|
||||
writePoolToNBT(pool, poolNBT)
|
||||
nbt.put(DataKeys.HABITAT_POOL, poolNBT)
|
||||
}
|
||||
|
||||
override fun readFromNBT(nbt: CompoundTag) {
|
||||
replaceSpawns = nbt.getBoolean(DataKeys.HABITAT_NATURAL_REPLACE_SPAWNS)
|
||||
rangeOfInfluence = nbt.getInt(DataKeys.HABITAT_NATURAL_RANGE_OF_INFLUENCE)
|
||||
|
||||
pool = readPoolFromNBT(
|
||||
nbt = nbt.getCompound(DataKeys.HABITAT_POOL),
|
||||
poolInitializer = ::NaturalHabitatPool,
|
||||
spawnInitializer = ::NaturalHabitatSpawn,
|
||||
defaultPool = NaturalHabitatPool::default
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,9 @@ class Mark(
|
||||
val replace: List<ResourceLocation>?,
|
||||
val group: String?,
|
||||
val chance: Float = 0F,
|
||||
val indexNumber: Int?
|
||||
val indexNumber: Int?,
|
||||
val aspects: List<String>?,
|
||||
val sortOrder: Int = 0,
|
||||
): StringRepresentable {
|
||||
|
||||
companion object {
|
||||
@@ -42,7 +44,9 @@ class Mark(
|
||||
buffer.readNullable { buffer.readList { buffer.readResourceLocation() } },
|
||||
buffer.readNullable { buffer.readString() },
|
||||
buffer.readFloat(),
|
||||
buffer.readNullable { buffer.readInt() }
|
||||
buffer.readNullable { buffer.readInt() },
|
||||
buffer.readNullable { buffer.readList { buffer.readString() } },
|
||||
buffer.readInt(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -83,5 +87,7 @@ class Mark(
|
||||
buffer.writeNullable(group) { _, v -> buffer.writeString(v) }
|
||||
buffer.writeFloat(chance)
|
||||
buffer.writeNullable(indexNumber) { _, v -> buffer.writeInt(v) }
|
||||
buffer.writeNullable(aspects) { _, v -> buffer.writeCollection(v) { _, aspect -> buffer.writeString(aspect) } }
|
||||
buffer.writeInt(sortOrder)
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.molang.function
|
||||
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
|
||||
|
||||
/**
|
||||
* Abstract MoLang function holder
|
||||
* Effectively allows to attach molang functions to almost anything by extending it
|
||||
*
|
||||
* @sample BattleActorMoLangFunctions
|
||||
* @author Gito
|
||||
*/
|
||||
abstract class AbstractMoLangFunctionHolder<T> {
|
||||
val custom: MutableList<(T) -> Map<String, (MoParams) -> Any>> = mutableListOf()
|
||||
|
||||
protected abstract fun T.moLangFunctions(): MutableMap<String, (MoParams) -> Any>
|
||||
|
||||
fun attach(value: T): Map<String, (MoParams) -> Any> {
|
||||
return value.moLangFunctions().also { functions ->
|
||||
custom.forEach { definition ->
|
||||
functions.putAll(definition(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+46
@@ -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.api.molang.function
|
||||
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
|
||||
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
|
||||
import com.cobblemon.mod.common.entity.npc.NPCBattleActor
|
||||
|
||||
|
||||
object BattleActorMoLangFunctions : AbstractMoLangFunctionHolder<BattleActor>() {
|
||||
override fun BattleActor.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val actor = this
|
||||
val map = mutableMapOf<String, (MoParams) -> Any>(
|
||||
"is_npc" to { DoubleValue(actor.type == ActorType.NPC) },
|
||||
"is_player" to { DoubleValue(actor.type == ActorType.PLAYER) },
|
||||
"is_wild" to { DoubleValue(actor.type == ActorType.WILD) }
|
||||
)
|
||||
|
||||
when (actor) {
|
||||
is NPCBattleActor -> {
|
||||
map["npc"] = { actor.entity.struct }
|
||||
}
|
||||
|
||||
is PlayerBattleActor -> {
|
||||
map["player"] = { actor.entity?.asMoLangValue() ?: DoubleValue.ZERO }
|
||||
}
|
||||
|
||||
is PokemonBattleActor -> {
|
||||
map["pokemon"] = { actor.entity?.asMoLangValue() ?: DoubleValue.ZERO }
|
||||
}
|
||||
}
|
||||
|
||||
return map
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.molang.function
|
||||
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.util.getStringOrNull
|
||||
|
||||
object BattleMessageMoLangFunctions : AbstractMoLangFunctionHolder<BattleMessage>() {
|
||||
override fun BattleMessage.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val battleMessage = this
|
||||
return mutableMapOf(
|
||||
"has_argument_at" to { params ->
|
||||
val index = params.getInt(0)
|
||||
val value = params.getStringOrNull(1) ?: ""
|
||||
DoubleValue(value == battleMessage.argumentAt(index))
|
||||
},
|
||||
"has_argument" to { params ->
|
||||
val argumentName = params.getString(0)
|
||||
val value = params.getStringOrNull(1) ?: ""
|
||||
DoubleValue(value == battleMessage.optionalArgument(argumentName))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.molang.function
|
||||
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.util.asArrayValue
|
||||
import java.util.*
|
||||
|
||||
object BattleMoLangFunctions : AbstractMoLangFunctionHolder<PokemonBattle>() {
|
||||
override fun PokemonBattle.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val battle = this
|
||||
return mutableMapOf(
|
||||
"environment" to { battle.runtime.environment },
|
||||
"battle_id" to { StringValue(battle.battleId.toString()) },
|
||||
"battle_type" to { StringValue(battle.format.toString()) },
|
||||
"is_pvn" to { DoubleValue(battle.isPvN) },
|
||||
"is_pvp" to { DoubleValue(battle.isPvP) },
|
||||
"is_pvw" to { DoubleValue(battle.isPvW) },
|
||||
"stop" to { battle.stop() },
|
||||
"actors" to { battle.actors.toList().asArrayValue { it.struct } },
|
||||
"get_actor" to getActor@{ params ->
|
||||
val uuid = UUID.fromString(params.getString(0))
|
||||
val actor = battle.actors.find { it.uuid == uuid } ?: return@getActor DoubleValue.ZERO
|
||||
actor.struct
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.molang.function
|
||||
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.world.level.biome.Biome
|
||||
|
||||
object BiomeMoLangFunctions: AbstractMoLangFunctionHolder<Holder<Biome>>() {
|
||||
override fun Holder<Biome>.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
return mutableMapOf()
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.molang.function
|
||||
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.world.level.block.Block
|
||||
|
||||
object BlockMoLangFunctions: AbstractMoLangFunctionHolder<Holder<Block>>() {
|
||||
override fun Holder<Block>.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
return mutableMapOf()
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.molang.function
|
||||
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.world.level.dimension.DimensionType
|
||||
|
||||
object DimensionTypeMoLangFunctions : AbstractMoLangFunctionHolder<Holder<DimensionType>>() {
|
||||
override fun Holder<DimensionType>.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
return mutableMapOf()
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.molang.function
|
||||
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.api.drop.DropEntry
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.getOrNull
|
||||
|
||||
object DropEntryMoLangFunctions : AbstractMoLangFunctionHolder<DropEntry>() {
|
||||
override fun DropEntry.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val drop = this
|
||||
val map = mutableMapOf<String, (MoParams) -> Any>()
|
||||
|
||||
map["percentage"] = { DoubleValue(drop.percentage.toDouble()) }
|
||||
map["quantity"] = { DoubleValue(drop.quantity.toDouble()) }
|
||||
map["max_selectable_times"] = { DoubleValue(drop.maxSelectableTimes.toDouble()) }
|
||||
map["can_drop"] = { params ->
|
||||
val pokemon = params.getOrNull<ObjectValue<Pokemon>>(0)?.obj
|
||||
DoubleValue(if (drop.canDrop(pokemon)) 1.0 else 0.0)
|
||||
}
|
||||
return map
|
||||
}
|
||||
}
|
||||
+313
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
* 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.molang.function
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.struct.ArrayStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.dialogue.ReferenceDialogueFaceProvider
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asBiomeMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asWorldMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.scheduling.Schedulable
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.entity.MoLangScriptingEntity
|
||||
import com.cobblemon.mod.common.entity.PosableEntity
|
||||
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
|
||||
import com.cobblemon.mod.common.util.asArrayValue
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.asResource
|
||||
import com.cobblemon.mod.common.util.asUUID
|
||||
import com.cobblemon.mod.common.util.cloneFrom
|
||||
import com.cobblemon.mod.common.util.effectiveName
|
||||
import com.cobblemon.mod.common.util.getBlockStatesWithPos
|
||||
import com.cobblemon.mod.common.util.getBooleanOrNull
|
||||
import com.cobblemon.mod.common.util.getDoubleOrNull
|
||||
import com.cobblemon.mod.common.util.getIsSubmerged
|
||||
import com.cobblemon.mod.common.util.getOrNull
|
||||
import com.cobblemon.mod.common.util.getStringOrNull
|
||||
import com.cobblemon.mod.common.util.isStandingOn
|
||||
import com.cobblemon.mod.common.util.resolve
|
||||
import com.cobblemon.mod.common.util.worldRegistry
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.tags.TagKey
|
||||
import net.minecraft.world.damagesource.DamageSource
|
||||
import net.minecraft.world.damagesource.DamageTypes
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.entity.TamableAnimal
|
||||
import net.minecraft.world.phys.AABB
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import java.util.UUID
|
||||
import kotlin.math.sqrt
|
||||
|
||||
object EntityMoLangFunctions : AbstractMoLangFunctionHolder<Entity>() {
|
||||
override fun Entity.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val thisEntity = this
|
||||
val map = mutableMapOf<String, (MoParams) -> Any>(
|
||||
"uuid" to { StringValue(thisEntity.uuid.toString()) },
|
||||
"name" to { _ -> StringValue(thisEntity.effectiveName().string) },
|
||||
"yaw" to { _ -> DoubleValue(thisEntity.yRot.toDouble()) },
|
||||
"pitch" to { _ -> DoubleValue(thisEntity.xRot.toDouble()) },
|
||||
"x" to { _ -> DoubleValue(thisEntity.x) },
|
||||
"y" to { _ -> DoubleValue(thisEntity.y) },
|
||||
"z" to { _ -> DoubleValue(thisEntity.z) },
|
||||
"velocity_x" to { _ -> DoubleValue(thisEntity.deltaMovement.x) },
|
||||
"velocity_y" to { _ -> DoubleValue(thisEntity.deltaMovement.y) },
|
||||
"velocity_z" to { _ -> DoubleValue(thisEntity.deltaMovement.z) },
|
||||
"width" to { DoubleValue(thisEntity.boundingBox.xsize) },
|
||||
"height" to { DoubleValue(thisEntity.boundingBox.ysize) },
|
||||
"this_size" to { DoubleValue(thisEntity.boundingBox.run { if (xsize > ysize) xsize else ysize }) },
|
||||
"this_width" to { DoubleValue(thisEntity.boundingBox.xsize) },
|
||||
"this_height" to { DoubleValue(thisEntity.boundingBox.ysize) },
|
||||
"id_modulo" to { params -> DoubleValue(thisEntity.uuid.hashCode() % params.getDouble(0)) },
|
||||
"horizontal_velocity" to { _ -> DoubleValue(thisEntity.deltaMovement.horizontalDistance()) },
|
||||
"vertical_velocity" to { DoubleValue(thisEntity.deltaMovement.y) },
|
||||
"is_on_ground" to { _ -> DoubleValue(thisEntity.onGround()) },
|
||||
"world" to { _ -> thisEntity.level().worldRegistry.wrapAsHolder(thisEntity.level()).asWorldMoLangValue() },
|
||||
"biome" to { _ -> thisEntity.level().getBiome(thisEntity.blockPosition()).asBiomeMoLangValue() },
|
||||
"is_passenger" to { DoubleValue(thisEntity.isPassenger) },
|
||||
"discard" to { thisEntity.discard() },
|
||||
"is_sneaking" to { _ -> DoubleValue(thisEntity.isShiftKeyDown) },
|
||||
"is_sprinting" to { _ -> DoubleValue(thisEntity.isSprinting) },
|
||||
"is_in_water" to { _ -> DoubleValue(thisEntity.isUnderWater) },
|
||||
"is_in_rain" to { _ -> DoubleValue(thisEntity.isInWaterOrRain && !thisEntity.isInWater) },
|
||||
"is_touching_water_or_rain" to { _ -> DoubleValue(thisEntity.isInWaterRainOrBubble) },
|
||||
"is_touching_water" to { _ -> DoubleValue(thisEntity.isInWater) },
|
||||
"is_underwater" to { DoubleValue(thisEntity.getIsSubmerged()) },
|
||||
"is_in_lava" to { _ -> DoubleValue(thisEntity.isInLava) },
|
||||
"is_on_fire" to { _ -> DoubleValue(thisEntity.isOnFire) },
|
||||
"is_invisible" to { _ -> DoubleValue(thisEntity.isInvisible) },
|
||||
"is_riding" to { _ -> DoubleValue(thisEntity.isPassenger) },
|
||||
"set_name" to { params ->
|
||||
val name = params.getString(0)
|
||||
thisEntity.customName = name.text()
|
||||
DoubleValue.ONE
|
||||
},
|
||||
"damage" to { params ->
|
||||
val amount = params.getDouble(0)
|
||||
val source = DamageSource(
|
||||
thisEntity.level().registryAccess().registryOrThrow(Registries.DAMAGE_TYPE)
|
||||
.getHolder(DamageTypes.GENERIC).get()
|
||||
)
|
||||
thisEntity.hurt(source, amount.toFloat())
|
||||
},
|
||||
"distance_to_owner" to distanceToOwner@{
|
||||
if (thisEntity !is TamableAnimal) {
|
||||
return@distanceToOwner DoubleValue.ZERO
|
||||
}
|
||||
val owner = thisEntity.owner ?: return@distanceToOwner DoubleValue.ZERO
|
||||
DoubleValue(thisEntity.distanceTo(owner))
|
||||
},
|
||||
"owner" to owner@{
|
||||
if (thisEntity !is TamableAnimal) return@owner DoubleValue.ZERO
|
||||
val owner = thisEntity.owner
|
||||
owner?.asMostSpecificMoLangValue() ?: DoubleValue.ZERO
|
||||
},
|
||||
"delta_movement" to {
|
||||
listOf(thisEntity.deltaMovement.x, thisEntity.deltaMovement.y, thisEntity.deltaMovement.z)
|
||||
.asArrayValue(::DoubleValue)
|
||||
},
|
||||
"tags" to {
|
||||
val tags = thisEntity.tags
|
||||
val array = ArrayStruct(hashMapOf())
|
||||
tags.forEachIndexed { index, tag -> array.setDirectly("$index", StringValue(tag)) }
|
||||
array
|
||||
},
|
||||
"add_tag" to { params ->
|
||||
val tag = params.getString(0)
|
||||
thisEntity.addTag(tag)
|
||||
DoubleValue.ONE
|
||||
},
|
||||
"remove_tag" to { params ->
|
||||
val tag = params.getString(0)
|
||||
thisEntity.removeTag(tag)
|
||||
DoubleValue.ONE
|
||||
},
|
||||
"has_tag" to { params ->
|
||||
val tag = params.getString(0)
|
||||
DoubleValue(thisEntity.tags.contains(tag))
|
||||
},
|
||||
"distance_to_pos" to { params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
DoubleValue(sqrt(thisEntity.distanceToSqr(Vec3(x, y, z))))
|
||||
},
|
||||
"type" to { _ ->
|
||||
thisEntity.registryAccess().registry(Registries.ENTITY_TYPE).get().getKey(thisEntity.type)?.toString()?.let {
|
||||
StringValue(it)
|
||||
} ?: DoubleValue.ZERO
|
||||
},
|
||||
"find_nearby_block" to findNearbyBlock@{ params ->
|
||||
val input = params.getString(0)
|
||||
val isTag = input.contains("#")
|
||||
val type = input.replace("#", "").asIdentifierDefaultingNamespace(namespace = "minecraft")
|
||||
val range = params.getDoubleOrNull(1) ?: 10
|
||||
val blockPos = thisEntity.level().getBlockStatesWithPos(
|
||||
AABB.ofSize(
|
||||
thisEntity.position(),
|
||||
range.toDouble(),
|
||||
range.toDouble(),
|
||||
range.toDouble()
|
||||
)
|
||||
)
|
||||
.filter { blockPosPair ->
|
||||
blockPosPair.first.blockHolder.let {
|
||||
if (isTag) it.`is`(
|
||||
TagKey.create(
|
||||
Registries.BLOCK,
|
||||
type
|
||||
)
|
||||
) else it.`is`(type)
|
||||
}
|
||||
}
|
||||
.minByOrNull { it.second.distSqr(thisEntity.blockPosition()) }
|
||||
?.second
|
||||
if (blockPos != null) {
|
||||
return@findNearbyBlock ArrayStruct(
|
||||
mapOf(
|
||||
"0" to DoubleValue(blockPos.x),
|
||||
"1" to DoubleValue(blockPos.y),
|
||||
"2" to DoubleValue(blockPos.z)
|
||||
)
|
||||
)
|
||||
}
|
||||
return@findNearbyBlock DoubleValue.ZERO
|
||||
},
|
||||
"get_nearby_entities" to { params ->
|
||||
val distance = params.getDouble(0)
|
||||
val entities =
|
||||
thisEntity.level().getEntities(thisEntity, AABB.ofSize(thisEntity.position(), distance, distance, distance))
|
||||
|
||||
entities
|
||||
.filterIsInstance<Entity>()
|
||||
.map { it.asMostSpecificMoLangValue() }
|
||||
.asArrayValue()
|
||||
},
|
||||
"is_standing_on_blocks" to { params ->
|
||||
val depth = params.getDouble(0).toInt()
|
||||
val blockStrings: MutableSet<String> = mutableSetOf()
|
||||
for (blockIndex in 1..<params.params.size) {
|
||||
blockStrings.add(params.getString(blockIndex))
|
||||
}
|
||||
|
||||
if (thisEntity.isStandingOn(blockStrings, depth)) DoubleValue.ONE else DoubleValue.ZERO
|
||||
},
|
||||
|
||||
// q.entity.spawn_bedrock_particles(effect, locator, [player])
|
||||
// sends to everyone nearby or just to the player if they're set.
|
||||
// Locator is necessary even if unused on non-posables.
|
||||
"spawn_bedrock_particles" to { params ->
|
||||
val particle = params.getString(0).asResource()
|
||||
val locator = params.getString(1)
|
||||
val player = params.getOrNull<MoValue>(2)?.let {
|
||||
when (it) {
|
||||
is StringValue -> thisEntity.level().getPlayerByUUID(UUID.fromString(it.value))
|
||||
is ObjectValue<*> -> it.obj
|
||||
else -> null
|
||||
}
|
||||
} as? ServerPlayer
|
||||
|
||||
val packet = SpawnSnowstormEntityParticlePacket(particle, thisEntity.id, listOf(locator))
|
||||
if (player == null) {
|
||||
packet.sendToPlayersAround(thisEntity.x, thisEntity.y, thisEntity.z, 64.0, thisEntity.level().dimension())
|
||||
} else {
|
||||
packet.sendToPlayer(player)
|
||||
}
|
||||
},
|
||||
"make_intangible" to { params ->
|
||||
val intangible = params.getBooleanOrNull(0) ?: true
|
||||
thisEntity.noPhysics = intangible
|
||||
DoubleValue.ONE
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
if (thisEntity is PosableEntity) {
|
||||
map["play_animation"] = playAnimation@{ params ->
|
||||
val animation = params.getString(0)
|
||||
val packet = PlayPosableAnimationPacket(thisEntity.id, setOf(animation), emptyList())
|
||||
val target = params.getStringOrNull(1)
|
||||
if (target != null) {
|
||||
val targetPlayer = if (target.asUUID != null) {
|
||||
thisEntity.level().getPlayerByUUID(target.asUUID!!) as ServerPlayer
|
||||
} else if (thisEntity.level() is ServerLevel) {
|
||||
thisEntity.level().server!!.playerList.getPlayerByName(target)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
if (targetPlayer != null) {
|
||||
packet.sendToPlayer(targetPlayer)
|
||||
return@playAnimation DoubleValue.ONE
|
||||
} else {
|
||||
return@playAnimation DoubleValue.ZERO
|
||||
}
|
||||
} else {
|
||||
packet.sendToPlayersAround(thisEntity.x, thisEntity.y, thisEntity.z, 64.0, thisEntity.level().dimension())
|
||||
return@playAnimation DoubleValue.ONE
|
||||
}
|
||||
}
|
||||
|
||||
map["face"] = { params ->
|
||||
ObjectValue(ReferenceDialogueFaceProvider(thisEntity.id, params.getBooleanOrNull(0) != false))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (thisEntity is Schedulable) {
|
||||
map["run_molang_after"] = { params ->
|
||||
val expression = params.getString(0).asExpressionLike()
|
||||
val delayInSeconds = params.getDouble(1).toFloat()
|
||||
val runtime = MoLangRuntime()
|
||||
runtime.environment.cloneFrom(params.environment)
|
||||
thisEntity.after(delayInSeconds) {
|
||||
runtime.resolve(expression)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (thisEntity is MoLangScriptingEntity) {
|
||||
map["add_callback"] = { params ->
|
||||
val type = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val callback = params.getString(1).asIdentifierDefaultingNamespace()
|
||||
val allowDuplicates = params.getBooleanOrNull(2) ?: false
|
||||
thisEntity.callbacks.addCallback(type, callback, allowDuplicates)
|
||||
}
|
||||
map["remove_callback"] = { params ->
|
||||
val type = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val callback = params.getString(1).asIdentifierDefaultingNamespace()
|
||||
thisEntity.callbacks.removeCallback(type, callback)
|
||||
}
|
||||
map["has_callback"] = { params ->
|
||||
val type = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val callback = params.getString(1).asIdentifierDefaultingNamespace()
|
||||
DoubleValue(callback in (thisEntity.callbacks[type] ?: emptySet()))
|
||||
}
|
||||
map["clear_callbacks"] = { params ->
|
||||
val type = params.getStringOrNull(0)?.asIdentifierDefaultingNamespace()
|
||||
if (type != null) {
|
||||
thisEntity.callbacks[type]?.clear()
|
||||
} else {
|
||||
thisEntity.callbacks.clear()
|
||||
}
|
||||
DoubleValue.ONE
|
||||
}
|
||||
}
|
||||
|
||||
return map
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.molang.function
|
||||
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolution
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.LevelUpEvolution
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.TradeEvolution
|
||||
|
||||
object EvolutionMoLangFunctions : AbstractMoLangFunctionHolder<Evolution>() {
|
||||
override fun Evolution.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val evolution = this
|
||||
val map = hashMapOf<String, (MoParams) -> Any>()
|
||||
|
||||
map["result"] = { evolution.result.asMoLangValue() }
|
||||
map["is_optional"] = { DoubleValue(evolution.optional) }
|
||||
map["consumes_held_item"] = { DoubleValue(evolution.consumeHeldItem) }
|
||||
|
||||
when (evolution) {
|
||||
is LevelUpEvolution -> {
|
||||
map["is_level_up"] = { DoubleValue.ONE }
|
||||
}
|
||||
|
||||
is TradeEvolution -> {
|
||||
map["is_trade"] = { DoubleValue.ONE }
|
||||
}
|
||||
|
||||
is ItemInteractionEvolution -> {
|
||||
map["is_item"] = { DoubleValue.ONE }
|
||||
}
|
||||
}
|
||||
|
||||
return map
|
||||
}
|
||||
}
|
||||
+311
@@ -0,0 +1,311 @@
|
||||
/*
|
||||
* 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.molang.function
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.struct.ArrayStruct
|
||||
import com.bedrockk.molang.runtime.struct.ContextStruct
|
||||
import com.bedrockk.molang.runtime.struct.VariableStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.Environment
|
||||
import com.cobblemon.mod.common.api.molang.MoLangLoadedFilesCache
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.api.npc.partyproviders.SimplePartyProvider
|
||||
import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker
|
||||
import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
|
||||
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.WaveFunctions
|
||||
import com.cobblemon.mod.common.pokemon.ai.ObtainableItem
|
||||
import com.cobblemon.mod.common.pokemon.ai.ObtainableItemCondition
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.getDoubleOrNull
|
||||
import com.cobblemon.mod.common.util.getIntOrNull
|
||||
import com.cobblemon.mod.common.util.getOrNull
|
||||
import com.cobblemon.mod.common.util.isInt
|
||||
import com.cobblemon.mod.common.util.resolve
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import java.text.ParseException
|
||||
|
||||
object GeneralMoLangFunctions {
|
||||
val holder: HashMap<String, (MoParams) -> Any> = hashMapOf(
|
||||
"print" to { params ->
|
||||
val message = params.get<MoValue>(0).asString()
|
||||
LOGGER.info(message)
|
||||
},
|
||||
"delete_variable" to deleteVar@{ params ->
|
||||
val struct = params.get<VariableStruct>(0)
|
||||
val variable = params.getString(1)
|
||||
struct.map.remove(variable)
|
||||
return@deleteVar DoubleValue.ONE
|
||||
},
|
||||
"delete_variables" to { params ->
|
||||
val struct = params.get<VariableStruct>(0)
|
||||
struct.map.clear()
|
||||
DoubleValue.ONE
|
||||
},
|
||||
"get_variable" to { params ->
|
||||
val struct = params.get<VariableStruct>(0)
|
||||
val variable = params.getString(1)
|
||||
struct.map[variable] ?: DoubleValue.ZERO
|
||||
},
|
||||
"set_variable" to { params ->
|
||||
val struct = params.get<VariableStruct>(0)
|
||||
val variable = params.getString(1)
|
||||
val value = params.get<MoValue>(2)
|
||||
struct.map[variable] = value
|
||||
value
|
||||
},
|
||||
"set_query" to { params ->
|
||||
val variable = params.getString(0)
|
||||
val value = params.get<MoValue>(1)
|
||||
params.environment.query.addFunction(variable) { value }
|
||||
value
|
||||
},
|
||||
"replace" to { params ->
|
||||
val text = params.getString(0)
|
||||
val search = params.getString(1)
|
||||
val replace = params.getString(2)
|
||||
StringValue(text.replace(search, replace))
|
||||
},
|
||||
"is_included" to { params ->
|
||||
val text = params.getString(0)
|
||||
val search = params.getString(1)
|
||||
DoubleValue(text.contains(search))
|
||||
},
|
||||
"to_lower" to { params ->
|
||||
StringValue(params.getString(0).lowercase())
|
||||
},
|
||||
"to_upper" to { params ->
|
||||
StringValue(params.getString(0).uppercase())
|
||||
},
|
||||
"string_length" to { params ->
|
||||
DoubleValue(params.getString(0).length)
|
||||
},
|
||||
"split_string" to { 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) }
|
||||
struct
|
||||
},
|
||||
"is_blank" to { params ->
|
||||
val arg = params.get<MoValue>(0)
|
||||
DoubleValue((arg is StringValue && (arg.value.isBlank() || arg.value.toDoubleOrNull() == 0.0)) || (arg is DoubleValue && arg.value == 0.0))
|
||||
},
|
||||
"run_command" to { params ->
|
||||
val command = params.getString(0)
|
||||
try {
|
||||
server()!!.commands.performPrefixedCommand(server()!!.createCommandSourceStack(), command)
|
||||
} catch (_: NullPointerException) {
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
},
|
||||
"is_int" to { params -> DoubleValue(params.get<MoValue>(0).asString().isInt()) },
|
||||
"is_number" to { params ->
|
||||
DoubleValue(
|
||||
params.get<MoValue>(0).asString().toDoubleOrNull() != null
|
||||
)
|
||||
},
|
||||
"to_number" to { params ->
|
||||
DoubleValue(
|
||||
params.get<MoValue>(0).asString().toDoubleOrNull() ?: 0.0
|
||||
)
|
||||
},
|
||||
"to_int" to { params ->
|
||||
DoubleValue(
|
||||
params.get<MoValue>(0).asString().toIntOrNull() ?: 0
|
||||
)
|
||||
},
|
||||
"to_string" to { params -> StringValue(params.get<MoValue>(0).asString()) },
|
||||
"do_effect_walks" to { _ ->
|
||||
DoubleValue(Cobblemon.config.walkingInBattleAnimations)
|
||||
},
|
||||
"random" to { params ->
|
||||
val options = mutableListOf<MoValue>()
|
||||
var index = 0
|
||||
while (params.contains(index)) {
|
||||
options.add(params.get(index))
|
||||
index++
|
||||
}
|
||||
options.random() // Can throw an exception if they specified no args. They'd be idiots though.
|
||||
},
|
||||
"curve" to { params ->
|
||||
val curveName = params.getString(0)
|
||||
val curve =
|
||||
WaveFunctions.functions[curveName] ?: throw IllegalArgumentException("Unknown curve: $curveName")
|
||||
ObjectValue(curve)
|
||||
},
|
||||
"array" to { params ->
|
||||
val values = params.params
|
||||
val array = ArrayStruct(hashMapOf())
|
||||
values.forEachIndexed { index, moValue -> array.setDirectly("$index", moValue) }
|
||||
array
|
||||
},
|
||||
"length" to { params ->
|
||||
val array = (params.getOrNull<MoValue>(0) as? VariableStruct)
|
||||
if (array == null) {
|
||||
DoubleValue.ZERO
|
||||
} else {
|
||||
DoubleValue(array.map.size.toDouble())
|
||||
}
|
||||
},
|
||||
"append" to { params ->
|
||||
val array = params.get<ArrayStruct>(0)
|
||||
val value = params.get<MoValue>(1)
|
||||
val nextIndex = array.map.size
|
||||
array.setDirectly("$nextIndex", value)
|
||||
array
|
||||
},
|
||||
"insert" to { params ->
|
||||
val array = params.get<ArrayStruct>(0)
|
||||
val index = params.getInt(1)
|
||||
val value = params.get<MoValue>(2)
|
||||
val size = array.map.size
|
||||
|
||||
// Shift elements at and after index up by 1
|
||||
for (i in (size - 1) downTo index) {
|
||||
val current = array.map[i.toString()]
|
||||
if (current != null) {
|
||||
array.map[(i + 1).toString()] = current
|
||||
}
|
||||
}
|
||||
array.map[index.toString()] = value
|
||||
array
|
||||
},
|
||||
"delete" to { params ->
|
||||
val array = params.get<ArrayStruct>(0)
|
||||
val index = params.getInt(1)
|
||||
if (index in 0 until array.map.size) {
|
||||
array.map.remove(index.toString())
|
||||
// Re-index the array to keep keys sequential in numerical order
|
||||
val newMap = hashMapOf<String, MoValue>()
|
||||
array.map.keys.mapNotNull { it.toIntOrNull() }
|
||||
.sorted()
|
||||
.forEachIndexed { i, k -> newMap[i.toString()] = array.map[k.toString()]!! }
|
||||
array.map.clear()
|
||||
array.map.putAll(newMap)
|
||||
}
|
||||
array
|
||||
},
|
||||
"run_script" to { params ->
|
||||
val runtime = MoLangRuntime()
|
||||
runtime.environment.query = params.environment.query
|
||||
runtime.environment.variable = params.environment.variable
|
||||
val args = params.params.subList(1, params.params.size)
|
||||
runtime.environment.context = ContextStruct(
|
||||
(params.environment.context?.map
|
||||
?: emptyMap()) + args.mapIndexed { index, value -> "arg_${index + 1}" to value }.toMap()
|
||||
)
|
||||
val script = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
// store the args in the
|
||||
CobblemonScripts.run(script, runtime) ?: DoubleValue.ZERO
|
||||
},
|
||||
"run_molang" to { params ->
|
||||
val runtime = MoLangRuntime()
|
||||
runtime.environment.query = params.environment.query
|
||||
runtime.environment.variable = params.environment.variable
|
||||
runtime.environment.context = params.environment.context
|
||||
val expression = params.getString(0).asExpressionLike()
|
||||
val delayInSeconds = params.getDoubleOrNull(1)?.toFloat() ?: 0.0f
|
||||
|
||||
fun evaluate(): MoValue {
|
||||
return try {
|
||||
runtime.resolve(expression)
|
||||
} catch (ex: Exception) {
|
||||
LOGGER.warn(
|
||||
"Failed to evaluate MoLang expression${if (delayInSeconds > 0.0f) " (delayed)" else ""}: $expression",
|
||||
ex
|
||||
)
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
|
||||
if (delayInSeconds > 0.0f) {
|
||||
val tracker =
|
||||
if (Cobblemon.implementation.environment() == Environment.SERVER) ServerTaskTracker else ClientTaskTracker
|
||||
tracker.after(delayInSeconds) {
|
||||
evaluate()
|
||||
}
|
||||
DoubleValue.ONE
|
||||
} else {
|
||||
evaluate()
|
||||
}
|
||||
},
|
||||
"system_time_millis" to { _ ->
|
||||
DoubleValue(System.currentTimeMillis())
|
||||
},
|
||||
// the rest of the world use dd/MM/yyyy grow up america (this comment was generated by copilot)
|
||||
"date_local_time" to { _ ->
|
||||
val time = System.currentTimeMillis()
|
||||
val date = java.util.Date(time)
|
||||
val formatted = java.text.SimpleDateFormat("dd/MM/yyyy").format(date)
|
||||
StringValue(formatted)
|
||||
},
|
||||
"date_of" to { params ->
|
||||
val time = params.getDouble(0).toLong()
|
||||
val date = java.util.Date(time)
|
||||
val formatted = java.text.SimpleDateFormat("dd/MM/yyyy").format(date)
|
||||
StringValue(formatted)
|
||||
},
|
||||
"date_is_after" to { params ->
|
||||
val dateA = params.getString(0)
|
||||
val dateB = params.getString(1)
|
||||
val format = java.text.SimpleDateFormat("dd/MM/yyyy")
|
||||
val a = try {
|
||||
format.parse(dateA)
|
||||
} catch (_: ParseException) {
|
||||
throw RuntimeException("Cannot parse date ${dateA}, expected it to be in dd/MM/yyyy format, other provided")
|
||||
}
|
||||
val b = try {
|
||||
format.parse(dateB)
|
||||
} catch (_: ParseException) {
|
||||
throw RuntimeException("Cannot parse date ${dateB}, expected it to be in dd/MM/yyyy format, other provided")
|
||||
}
|
||||
DoubleValue(a.after(b))
|
||||
},
|
||||
"get_move_from_id" to { params ->
|
||||
val moveId = params.getString(0)
|
||||
val moveTemplate = Moves.getByName(moveId)
|
||||
moveTemplate?.struct ?: DoubleValue.ZERO
|
||||
},
|
||||
"create_simple_party_provider" to { _ ->
|
||||
val partyProvider = SimplePartyProvider()
|
||||
partyProvider.struct
|
||||
},
|
||||
"create_pickup_item" to { params ->
|
||||
val item = params.getOrNull<MoValue>(0)?.asString()?.let(ObtainableItemCondition::parseFromString)
|
||||
val pickupPriority = params.getIntOrNull(1) ?: 0
|
||||
val pickupItem = ObtainableItem(item = item, pickupPriority = pickupPriority)
|
||||
pickupItem.struct
|
||||
},
|
||||
"create_itemstack" to { params ->
|
||||
val itemId = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
try {
|
||||
val item = BuiltInRegistries.ITEM.get(itemId)
|
||||
val count = params.getIntOrNull(1) ?: 1
|
||||
val itemStack = ItemStack(item, count)
|
||||
ObjectValue(itemStack)
|
||||
} catch (_: NullPointerException) {
|
||||
LOGGER.error("Cannot create item stack from identifier: $itemId, item with that ID does not exist. Possible typo?")
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
},
|
||||
"file" to { MoLangLoadedFilesCache.struct }
|
||||
)
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.molang.function
|
||||
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.getIntOrNull
|
||||
import net.minecraft.core.RegistryAccess
|
||||
import net.minecraft.core.component.DataComponents
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.tags.TagKey
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.enchantment.EnchantmentHelper
|
||||
|
||||
object ItemStackMoLangFunctions : AbstractMoLangFunctionHolder<Pair<ItemStack, RegistryAccess>>() {
|
||||
override fun Pair<ItemStack, RegistryAccess>.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val stack = this.first
|
||||
val registryAccess = this.second
|
||||
val itemRegistry = registryAccess.registryOrThrow(Registries.ITEM)
|
||||
val holder = itemRegistry.wrapAsHolder(stack.item)
|
||||
|
||||
val map = hashMapOf<String, (MoParams) -> Any>()
|
||||
map["item"] = { _ -> holder.asMoLangValue(Registries.ITEM) }
|
||||
map["count"] = { _ -> DoubleValue(stack.count.toDouble()) }
|
||||
map["damage_value"] = { _ -> DoubleValue(stack.damageValue) }
|
||||
map["max_damage"] = { _ -> DoubleValue(stack.maxDamage) }
|
||||
map["is_empty"] = { _ -> DoubleValue(stack.isEmpty) }
|
||||
map["shrink"] = { params -> stack.shrink(params.getInt(0)) }
|
||||
map["grow"] = { params -> stack.grow(params.getInt(0)) }
|
||||
map["is_of"] = { params ->
|
||||
DoubleValue(
|
||||
holder.`is`(
|
||||
params.getString(0).asIdentifierDefaultingNamespace()
|
||||
)
|
||||
)
|
||||
}
|
||||
map["is_in"] = { params ->
|
||||
DoubleValue(
|
||||
holder.`is`(
|
||||
TagKey.create(
|
||||
Registries.ITEM,
|
||||
params.getString(0).replace("#", "").asIdentifierDefaultingNamespace()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
map["is_food"] = { _ -> DoubleValue(stack.has(DataComponents.FOOD)) }
|
||||
map["is_enchanted"] = { _ -> DoubleValue(stack.hasFoil()) }
|
||||
map["has_enchantment"] = put@{ params ->
|
||||
val enchantmentId = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val minLevel = params.getIntOrNull(1) ?: 1
|
||||
val enchantmentRegistry = registryAccess.registryOrThrow(Registries.ENCHANTMENT)
|
||||
val holder =
|
||||
enchantmentRegistry.getHolder(enchantmentId).orElse(null) ?: return@put DoubleValue.ZERO
|
||||
val level = EnchantmentHelper.getItemEnchantmentLevel(holder, stack)
|
||||
return@put DoubleValue(level >= minLevel)
|
||||
}
|
||||
return map
|
||||
}
|
||||
}
|
||||
+278
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
* 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.molang.function
|
||||
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.struct.VariableStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.CobblemonBlockPosTracker
|
||||
import com.cobblemon.mod.common.api.ai.CobblemonWanderControl
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.asUUID
|
||||
import com.cobblemon.mod.common.util.getBooleanOrNull
|
||||
import com.cobblemon.mod.common.util.getDoubleOrNull
|
||||
import com.cobblemon.mod.common.util.getIntOrNull
|
||||
import com.cobblemon.mod.common.util.getMemorySafely
|
||||
import com.cobblemon.mod.common.util.hasMemoryFromString
|
||||
import com.cobblemon.mod.common.util.isDoingActivityFromString
|
||||
import com.cobblemon.mod.common.util.isLookingAt
|
||||
import com.cobblemon.mod.common.util.traceEntityCollision
|
||||
import net.minecraft.core.Position
|
||||
import net.minecraft.core.Vec3i
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.world.effect.MobEffectInstance
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.Mob
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
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.monster.Monster
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.level.ClipContext
|
||||
import net.minecraft.world.level.pathfinder.PathType
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import java.util.*
|
||||
|
||||
object LivingEntityMoLangFunctions : AbstractMoLangFunctionHolder<LivingEntity>() {
|
||||
override fun LivingEntity.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val entity = this
|
||||
val map = mutableMapOf<String, (MoParams) -> Any>()
|
||||
map["is_player"] = { _ -> DoubleValue(entity is Player) }
|
||||
map["is_npc"] = { _ -> DoubleValue(entity is NPCEntity) }
|
||||
map["is_mob"] = { _ -> DoubleValue(entity is Mob) }
|
||||
map["is_pokemon"] = { _ -> DoubleValue(entity is PokemonEntity) }
|
||||
map["is_animal"] = { _ -> DoubleValue(entity is Animal) }
|
||||
map["is_tamable"] = { _ -> DoubleValue(entity is TamableAnimal) }
|
||||
map["is_tamed"] = { _ -> DoubleValue(entity is TamableAnimal && entity.isTame) }
|
||||
map["is_hostile"] = { _ -> DoubleValue(entity is Monster) }
|
||||
map["is_baby"] = { _ -> DoubleValue(entity.isBaby) }
|
||||
map["is_adult"] = { _ -> DoubleValue(!entity.isBaby) }
|
||||
map["remove_effect"] = put@{ 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["add_effect"] = put@{ 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["has_effect"] = put@{ 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["heal"] = { params ->
|
||||
val amount = params.getDouble(0)
|
||||
entity.heal(amount.toFloat())
|
||||
}
|
||||
map["is_looking_at"] = put@{ 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["is_living_entity"] = { DoubleValue.ONE }
|
||||
map["is_flying"] = { _ -> DoubleValue(entity.isFallFlying) }
|
||||
map["is_sleeping"] = { _ -> DoubleValue(entity.isSleeping) }
|
||||
map["health"] = { _ -> DoubleValue(entity.health) }
|
||||
map["max_health"] = { _ -> DoubleValue(entity.maxHealth) }
|
||||
map["look_at_position"] = { params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
val duration = params.getIntOrNull(3) ?: 20
|
||||
val flags = params.params.subList(4, params.params.size).map { it.asString() }
|
||||
val brain = entity.brain
|
||||
brain.setMemoryWithExpiry(
|
||||
MemoryModuleType.LOOK_TARGET,
|
||||
CobblemonBlockPosTracker(Vec3(x, y, z), flags.toSet()),
|
||||
duration.toLong()
|
||||
)
|
||||
}
|
||||
map["get_wander_control_memory"] = put@{
|
||||
val value = entity.brain.getMemorySafely(CobblemonMemories.WANDER_CONTROL).orElse(null)
|
||||
?: CobblemonWanderControl()
|
||||
if (
|
||||
!entity.brain.checkMemory(CobblemonMemories.WANDER_CONTROL, MemoryStatus.VALUE_PRESENT)
|
||||
&& entity.brain.checkMemory(CobblemonMemories.WANDER_CONTROL, MemoryStatus.REGISTERED)
|
||||
) {
|
||||
entity.brain.setMemory(CobblemonMemories.WANDER_CONTROL, value)
|
||||
}
|
||||
return@put value.struct
|
||||
}
|
||||
map["get_position_memory"] = put@{ params ->
|
||||
val id = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val memoryType = BuiltInRegistries.MEMORY_MODULE_TYPE.get(id)
|
||||
if (entity.brain.checkMemory(memoryType, MemoryStatus.VALUE_PRESENT)) {
|
||||
return@put when (val memory = entity.brain.getMemory(memoryType).get()) {
|
||||
is Vec3i -> VariableStruct(
|
||||
mapOf(
|
||||
"x" to DoubleValue(memory.x),
|
||||
"y" to DoubleValue(memory.y),
|
||||
"z" to DoubleValue(memory.z)
|
||||
)
|
||||
)
|
||||
|
||||
is Position -> VariableStruct(
|
||||
mapOf(
|
||||
"x" to DoubleValue(memory.x()),
|
||||
"y" to DoubleValue(memory.y()),
|
||||
"z" to DoubleValue(memory.z())
|
||||
)
|
||||
)
|
||||
|
||||
else -> DoubleValue.ZERO
|
||||
}
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map["set_uuid_memory"] = put@{ params ->
|
||||
val memory = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
.let(BuiltInRegistries.MEMORY_MODULE_TYPE::get) as MemoryModuleType<UUID>
|
||||
val uuid = params.getString(1).asUUID ?: return@put DoubleValue.ZERO
|
||||
val expiry = params.getIntOrNull(2) ?: -1
|
||||
if (expiry != -1) {
|
||||
entity.brain.setMemoryWithExpiry(memory, uuid, expiry.toLong())
|
||||
} else {
|
||||
entity.brain.setMemory(memory, uuid)
|
||||
}
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["erase_memory"] = put@{ params ->
|
||||
val memories = params.params.map { it.asString().asIdentifierDefaultingNamespace() }
|
||||
.map(BuiltInRegistries.MEMORY_MODULE_TYPE::get)
|
||||
memories.forEach { entity.brain.eraseMemory(it) }
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["has_memory_value"] = put@{ params ->
|
||||
for (param in params.params) {
|
||||
if (!entity.hasMemoryFromString(param.asString())) {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["lacks_memory_value"] = put@{ params ->
|
||||
for (param in params.params) {
|
||||
if (entity.hasMemoryFromString(param.asString())) {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["is_doing_activity"] = put@{ params ->
|
||||
for (param in params.params) {
|
||||
if (entity.isDoingActivityFromString(param.asString())) {
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
}
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
map["can_see"] = put@{ params ->
|
||||
val target = params.get<MoValue>(0)
|
||||
val range = params.getDoubleOrNull(1) ?: 32.0
|
||||
if (target is ObjectValue<*>) {
|
||||
val targetEntity = target.obj as? LivingEntity ?: return@put DoubleValue.ZERO
|
||||
val vector = targetEntity.eyePosition.subtract(entity.eyePosition).normalize()
|
||||
val trace = entity.traceEntityCollision(
|
||||
maxDistance = range.toFloat(),
|
||||
stepDistance = 0.1F,
|
||||
direction = vector,
|
||||
entityClass = targetEntity.javaClass,
|
||||
collideBlock = ClipContext.Fluid.NONE
|
||||
)
|
||||
return@put DoubleValue(targetEntity in (trace?.entities ?: emptyList()))
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
|
||||
if (entity is PathfinderMob) {
|
||||
map["walk_to"] = { params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
val speedMultiplier = params.getDoubleOrNull(3) ?: 0.35
|
||||
|
||||
if (entity.brain.checkMemory(MemoryModuleType.WALK_TARGET, MemoryStatus.REGISTERED)) {
|
||||
entity.brain.setMemory(
|
||||
MemoryModuleType.WALK_TARGET,
|
||||
WalkTarget(Vec3(x, y, z), speedMultiplier.toFloat(), 1)
|
||||
)
|
||||
entity.brain.setMemory(
|
||||
MemoryModuleType.LOOK_TARGET,
|
||||
BlockPosTracker(Vec3(x, y + entity.eyeHeight, z))
|
||||
)
|
||||
} else {
|
||||
entity.navigation.moveTo(x, y, z, speedMultiplier)
|
||||
entity.lookControl.setLookAt(Vec3(x, y + entity.eyeHeight, z))
|
||||
}
|
||||
}
|
||||
|
||||
map["has_walk_target"] = { _ ->
|
||||
DoubleValue(entity.brain.getMemorySafely(MemoryModuleType.WALK_TARGET).isPresent || entity.isPathFinding)
|
||||
}
|
||||
|
||||
map["set_can_float"] = { params ->
|
||||
val canFloat = params.getBooleanOrNull(0) != false
|
||||
entity.navigation.setCanFloat(canFloat)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
|
||||
map["get_pathfinding_malus"] = put@{ params ->
|
||||
val type = PathType.entries.find { it.name == params.getString(0).uppercase() }
|
||||
if (type != null) {
|
||||
return@put DoubleValue(entity.getPathfindingMalus(type))
|
||||
}
|
||||
Cobblemon.LOGGER.error("Unknown pathfinding type: ${params.getString(0)}")
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
map["set_pathfinding_malus"] = put@{ params ->
|
||||
val type = PathType.entries.find { it.name == params.getString(0).uppercase() }
|
||||
val malus = params.getDouble(1).toFloat()
|
||||
if (type != null) {
|
||||
entity.setPathfindingMalus(type, malus)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
Cobblemon.LOGGER.error("Unknown pathfinding type: ${params.getString(0)}")
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
return map
|
||||
}
|
||||
}
|
||||
+272
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
* 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.molang.function
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.struct.ArrayStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonActivities
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
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.npc.NPCClasses
|
||||
import com.cobblemon.mod.common.api.npc.configuration.interaction.DialogueNPCInteractionConfiguration
|
||||
import com.cobblemon.mod.common.api.npc.configuration.interaction.ScriptNPCInteractionConfiguration
|
||||
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
|
||||
import com.cobblemon.mod.common.api.storage.party.NPCPartyStore
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
|
||||
import com.cobblemon.mod.common.util.asArrayValue
|
||||
import com.cobblemon.mod.common.util.asBlockPos
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.asResource
|
||||
import com.cobblemon.mod.common.util.cloneFrom
|
||||
import com.cobblemon.mod.common.util.getBooleanOrNull
|
||||
import com.cobblemon.mod.common.util.getDoubleOrNull
|
||||
import com.cobblemon.mod.common.util.getStringOrNull
|
||||
import com.cobblemon.mod.common.util.withNPCValue
|
||||
import net.minecraft.commands.arguments.EntityAnchorArgument
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.entity.EntityDimensions
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import kotlin.collections.any
|
||||
|
||||
object NPCMoLangFunctions : AbstractMoLangFunctionHolder<NPCEntity>() {
|
||||
override fun NPCEntity.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val npc = this
|
||||
val map = hashMapOf<String, (MoParams) -> Any>()
|
||||
map["class"] = { StringValue(npc.npc.id.toString()) }
|
||||
map["name"] = { StringValue(npc.name.string) }
|
||||
map["level"] = { DoubleValue(npc.level) }
|
||||
map["has_aspect"] = { params -> DoubleValue(npc.aspects.contains(params.getString(0))) }
|
||||
map["in_battle"] = { DoubleValue(npc.isInBattle()) }
|
||||
map["battles"] = {
|
||||
ArrayStruct(npc.battleIds.mapNotNull { BattleRegistry.getBattle(it)?.struct }
|
||||
.mapIndexed { index, value -> "$index" to value }.toMap())
|
||||
}
|
||||
map["stop_battles"] = { _ -> npc.battleIds.forEach { BattleRegistry.getBattle(it)?.stop() } }
|
||||
map["run_script_on_client"] = { params ->
|
||||
val world = npc.level()
|
||||
if (world is ServerLevel) {
|
||||
val script = params.getString(0)
|
||||
val packet = RunPosableMoLangPacket(npc.id, setOf("q.run_script('$script')"))
|
||||
packet.sendToPlayers(world.players().toList())
|
||||
}
|
||||
Unit
|
||||
}
|
||||
map["run_script"] = { params ->
|
||||
val script = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val runtime = MoLangRuntime()
|
||||
runtime.environment.cloneFrom(params.environment)
|
||||
CobblemonScripts.run(script, runtime) ?: DoubleValue(0)
|
||||
}
|
||||
map["set_movable"] = put@{ params ->
|
||||
val movable = params.getBooleanOrNull(0) != false
|
||||
npc.isMovable = movable
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["set_invulnerable"] = put@{ params ->
|
||||
val invulnerable = params.getBooleanOrNull(0) != false
|
||||
npc.isInvulnerable = invulnerable
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["set_leashable"] = put@{ params ->
|
||||
val leashable = params.getBooleanOrNull(0) != false
|
||||
npc.isLeashable = leashable
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["set_allow_projectile_hits"] = put@{ params ->
|
||||
val allowProjectileHits = params.getBooleanOrNull(0) != false
|
||||
npc.allowProjectileHits = allowProjectileHits
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["set_name_tag_visible"] = put@{ params ->
|
||||
val nameTagVisible = params.getBooleanOrNull(0) != false
|
||||
npc.hideNameTag = !nameTagVisible
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["unset_interaction"] = put@{
|
||||
npc.interaction = null
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["set_dialogue_interaction"] = put@{ params ->
|
||||
val dialogue = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
npc.interaction = DialogueNPCInteractionConfiguration().also {
|
||||
it.dialogue = dialogue
|
||||
}
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["set_script_interaction"] = put@{ params ->
|
||||
val script = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
npc.interaction = ScriptNPCInteractionConfiguration().also {
|
||||
it.script = script
|
||||
}
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["set_player_texture"] = put@{ params ->
|
||||
val username = params.getString(0)
|
||||
// Re-applying it would be unnecessarily laggy.
|
||||
if (username == npc.data.map["player_texture_username"]?.asString()) {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
npc.loadTextureFromGameProfileName(username)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["unset_player_texture"] = put@{
|
||||
npc.unloadTexture()
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["set_resource_identifier"] = put@{ params ->
|
||||
val identifier = params.getStringOrNull(0)?.asIdentifierDefaultingNamespace()
|
||||
npc.forcedResourceIdentifier = identifier
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["unset_resource_identifier"] = put@{
|
||||
npc.forcedResourceIdentifier = null
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["set_class"] = put@{ params ->
|
||||
val identifier = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val npcClass = NPCClasses.getByIdentifier(identifier)
|
||||
if (npcClass != null) {
|
||||
npc.npc = npcClass
|
||||
return@put DoubleValue.ONE
|
||||
} else {
|
||||
Cobblemon.LOGGER.error("Unknown NPC class: $identifier")
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map["set_render_scale"] = put@{ params ->
|
||||
val scale = params.getDouble(0)
|
||||
npc.renderScale = scale.toFloat()
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["render_scale"] = { _ -> DoubleValue(npc.renderScale) }
|
||||
map["set_hitbox_scale"] = put@{ params ->
|
||||
val scale = params.getDouble(0).toFloat()
|
||||
npc.hitboxScale = scale
|
||||
npc.refreshDimensions()
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["hitbox_scale"] = { _ -> DoubleValue(npc.hitboxScale) }
|
||||
map["set_hitbox"] = put@{ params ->
|
||||
if (params.params.isEmpty()) {
|
||||
npc.hitbox = null
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
val width = params.getDouble(0).toFloat()
|
||||
val height = params.getDouble(1).toFloat()
|
||||
val eyeHeight = params.getDoubleOrNull(2)?.toFloat() ?: (height * 0.85F)
|
||||
npc.hitbox = EntityDimensions.scalable(width, height).withEyeHeight(eyeHeight)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["unset_hitbox"] = put@{
|
||||
npc.hitbox = null
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["aspects"] = put@{
|
||||
val aspects = npc.aspects
|
||||
return@put aspects.asArrayValue { StringValue(it) }
|
||||
}
|
||||
map["add_aspect"] = put@{ params ->
|
||||
val aspects = params.params.map { it.asString() }
|
||||
npc.appliedAspects.addAll(aspects)
|
||||
npc.updateAspects()
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["remove_aspect"] = put@{ params ->
|
||||
val aspects = params.params.map { it.asString() }
|
||||
npc.appliedAspects.removeAll(aspects.toSet())
|
||||
npc.updateAspects()
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["party"] = put@{
|
||||
val party = npc.party ?: return@put DoubleValue.ZERO
|
||||
return@put party.asMoLangValue()
|
||||
}
|
||||
map["create_npc_party"] = put@{
|
||||
val party = NPCPartyStore(npc)
|
||||
return@put party.asMoLangValue()
|
||||
}
|
||||
map["set_npc_party"] = { params ->
|
||||
val party = params.get<MoValue>(0)
|
||||
if (party is ObjectValue<*>) {
|
||||
npc.party = party.obj as NPCPartyStore
|
||||
} else {
|
||||
npc.party = null
|
||||
}
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["run_action_effect"] = put@{ params ->
|
||||
val runtime = MoLangRuntime().setup()
|
||||
runtime.environment.cloneFrom(params.environment)
|
||||
runtime.withNPCValue(value = npc)
|
||||
val actionEffect = ActionEffects.actionEffects[params.getString(0).asIdentifierDefaultingNamespace()]
|
||||
if (actionEffect != null) {
|
||||
val context = ActionEffectContext(
|
||||
actionEffect = actionEffect,
|
||||
providers = mutableListOf(NPCProvider(npc)),
|
||||
runtime = runtime,
|
||||
level = npc.level()
|
||||
)
|
||||
npc.actionEffect = context
|
||||
npc.brain.setMemory(CobblemonMemories.ACTIVE_ACTION_EFFECT, context)
|
||||
npc.brain.setActiveActivityIfPossible(CobblemonActivities.ACTION_EFFECT)
|
||||
actionEffect.run(context).thenRun {
|
||||
val npcActionEffect = npc.brain.getMemory(CobblemonMemories.ACTIVE_ACTION_EFFECT).orElse(null)
|
||||
if (npcActionEffect == context && npc.brain.isActive(CobblemonActivities.ACTION_EFFECT)) {
|
||||
npc.brain.eraseMemory(CobblemonMemories.ACTIVE_ACTION_EFFECT)
|
||||
npc.actionEffect = null
|
||||
}
|
||||
}
|
||||
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
map["put_pokemon_in_healer"] = put@{ params ->
|
||||
val healer = npc.level()
|
||||
.getBlockEntity(params.get<ArrayStruct>(0).asBlockPos(), CobblemonBlockEntities.HEALING_MACHINE)
|
||||
.orElse(null) ?: return@put DoubleValue.ZERO
|
||||
val party = npc.party ?: return@put DoubleValue.ZERO
|
||||
if (healer.canHeal(party)) {
|
||||
healer.activate(npc.uuid, party)
|
||||
return@put DoubleValue.ONE
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map["look_at_position"] = { params ->
|
||||
val pos = Vec3(params.getDouble(0), params.getDouble(1), params.getDouble(2))
|
||||
npc.lookAt(EntityAnchorArgument.Anchor.EYES, pos)
|
||||
}
|
||||
map["environment"] = { _ -> npc.runtime.environment }
|
||||
map["party"] = { npc.party?.struct ?: DoubleValue.ZERO }
|
||||
map["has_party"] = { DoubleValue(npc.party != null) }
|
||||
map["is_npc"] = { DoubleValue.ONE }
|
||||
map["can_battle"] =
|
||||
{ DoubleValue(npc.party?.any { it.currentHealth > 0 } == true || npc.npc.party?.isStatic == false) }
|
||||
map["set_battle_theme"] = put@{ params ->
|
||||
val soundId = params.getString(0).asResource()
|
||||
npc.npc.battleTheme = soundId
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
return map
|
||||
}
|
||||
}
|
||||
+62
@@ -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.api.molang.function
|
||||
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.CobblemonUnlockableWallpapers
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.storage.pc.PCPosition
|
||||
import com.cobblemon.mod.common.api.storage.pc.PCStore
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.asArrayValue
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.getBooleanOrNull
|
||||
|
||||
object PCMoLangFunctions : AbstractMoLangFunctionHolder<PCStore>() {
|
||||
override fun PCStore.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val pc = this
|
||||
val map = mutableMapOf<String, (MoParams) -> Any>()
|
||||
|
||||
map["get_pokemon"] = getPokemon@{ params ->
|
||||
val box = params.getInt(0)
|
||||
val slot = params.getInt(1)
|
||||
val pokemon = pc[PCPosition(box, slot)] ?: return@getPokemon DoubleValue.ZERO
|
||||
return@getPokemon pokemon.struct
|
||||
}
|
||||
map["set_pokemon"] = { params ->
|
||||
val box = params.getInt(0)
|
||||
val slot = params.getInt(1)
|
||||
val pokemon = params.get<ObjectValue<Pokemon>>(2).obj
|
||||
pc[PCPosition(box, slot)] = pokemon
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["resize"] = { params ->
|
||||
val newSize = params.getInt(0)
|
||||
val lockNewSize = params.getBooleanOrNull(1) == true
|
||||
pc.resize(newSize, lockNewSize)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["get_box_count"] = { DoubleValue(pc.boxes.size.toDouble()) }
|
||||
map["has_unlocked_wallpaper"] = { params ->
|
||||
val wallpaper = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
DoubleValue(pc.unlockedWallpapers.contains(wallpaper))
|
||||
}
|
||||
map["get_unlocked_wallpapers"] = { pc.unlockedWallpapers.asArrayValue { StringValue(it.toString()) } }
|
||||
map["unlock_wallpaper"] = unlockWallpaper@{
|
||||
val wallpaper = it.getString(0).asIdentifierDefaultingNamespace()
|
||||
val playSound = it.getBooleanOrNull(1) != false
|
||||
CobblemonUnlockableWallpapers.unlockableWallpapers[wallpaper] ?: return@unlockWallpaper DoubleValue.ZERO
|
||||
return@unlockWallpaper DoubleValue(pc.unlockWallpaper(wallpaper, playSound))
|
||||
}
|
||||
|
||||
return map
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.molang.function
|
||||
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.storage.party.PartyStore
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
|
||||
object PartyMoLangFunctions: AbstractMoLangFunctionHolder<PartyStore>() {
|
||||
override fun PartyStore.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val party = this
|
||||
val map = hashMapOf<String, (MoParams) -> Any>()
|
||||
map["get_pokemon"] = getPokemon@{ params ->
|
||||
val index = params.getInt(0)
|
||||
val pokemon = party.get(index) ?: return@getPokemon DoubleValue.ZERO
|
||||
pokemon.struct
|
||||
}
|
||||
map["set_pokemon"] = { params ->
|
||||
val index = params.getInt(0)
|
||||
val pokemon = params.get<ObjectValue<Pokemon>>(1).obj
|
||||
party.set(index, pokemon)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
return map
|
||||
}
|
||||
}
|
||||
+386
@@ -0,0 +1,386 @@
|
||||
/*
|
||||
* 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.molang.function
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangEnvironment
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.struct.ArrayStruct
|
||||
import com.bedrockk.molang.runtime.struct.VariableStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.api.dialogue.PlayerDialogueFaceProvider
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.battles.BattleBuilder
|
||||
import com.cobblemon.mod.common.battles.BattleFormat
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleMusicPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.sound.UnvalidatedPlaySoundS2CPacket
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.activeDialogue
|
||||
import com.cobblemon.mod.common.util.asBlockPos
|
||||
import com.cobblemon.mod.common.util.asResource
|
||||
import com.cobblemon.mod.common.util.asUUID
|
||||
import com.cobblemon.mod.common.util.getBattleState
|
||||
import com.cobblemon.mod.common.util.getBooleanOrNull
|
||||
import com.cobblemon.mod.common.util.getDoubleOrNull
|
||||
import com.cobblemon.mod.common.util.getIntOrNull
|
||||
import com.cobblemon.mod.common.util.getOrNull
|
||||
import com.cobblemon.mod.common.util.getStringOrNull
|
||||
import com.cobblemon.mod.common.util.isInBattle
|
||||
import com.cobblemon.mod.common.util.isInDialogue
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import com.cobblemon.mod.common.util.pc
|
||||
import com.cobblemon.mod.common.util.pokedex
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.sounds.SoundSource
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.GameType
|
||||
import kotlin.collections.iterator
|
||||
|
||||
object PlayerMoLangFunctions : AbstractMoLangFunctionHolder<Player>() {
|
||||
override fun Player.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val player = this
|
||||
val map = mutableMapOf<String, (MoParams) -> Any>()
|
||||
map["username"] = { _ -> StringValue(player.gameProfile.name) }
|
||||
map["uuid"] = { _ -> StringValue(player.gameProfile.id.toString()) }
|
||||
map["main_held_item"] = { _ -> player.mainHandItem.asMoLangValue(player.registryAccess()) }
|
||||
map["off_held_item"] = { _ -> player.offhandItem.asMoLangValue(player.registryAccess()) }
|
||||
map["inventory"] = put@{ _ ->
|
||||
val inventory = player.inventory
|
||||
val items = ArrayStruct(hashMapOf())
|
||||
for (i in 0 until inventory.containerSize) {
|
||||
items.setDirectly("$i", inventory.getItem(i).asMoLangValue(player.registryAccess()))
|
||||
}
|
||||
return@put items
|
||||
}
|
||||
map["has_inventory_space"] = put@{ _ ->
|
||||
val inventory = player.inventory
|
||||
return@put if (inventory.getFreeSlot() != -1) DoubleValue.ONE else DoubleValue.ZERO
|
||||
}
|
||||
map["set_inventory_slot"] = put@{ params ->
|
||||
val slot = params.getInt(0)
|
||||
val inventory = player.inventory
|
||||
if (slot !in 0 until inventory.containerSize) return@put DoubleValue.ZERO
|
||||
|
||||
val value = params.getOrNull<MoValue>(1) ?: return@put DoubleValue.ZERO
|
||||
|
||||
val stack: ItemStack? = when (value) {
|
||||
is ObjectValue<*> -> value.obj as? ItemStack
|
||||
is StringValue -> {
|
||||
val id = ResourceLocation.parse(value.value)
|
||||
val item = player.registryAccess().registryOrThrow(Registries.ITEM).get(id)
|
||||
?: return@put DoubleValue.ZERO
|
||||
ItemStack(item)
|
||||
}
|
||||
|
||||
else -> null
|
||||
}
|
||||
|
||||
if (stack == null) return@put DoubleValue.ZERO
|
||||
|
||||
inventory.setItem(slot, stack)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["give_item"] = put@{ params ->
|
||||
val inventory = player.inventory
|
||||
val freeSlot = inventory.getFreeSlot()
|
||||
val value = params.getOrNull<MoValue>(0) ?: return@put DoubleValue.ZERO
|
||||
val dropIfFull = params.getBooleanOrNull(1) ?: false
|
||||
|
||||
val stack: ItemStack? = when (value) {
|
||||
is ObjectValue<*> -> value.obj as? ItemStack
|
||||
is StringValue -> {
|
||||
val id = ResourceLocation.parse(value.value)
|
||||
val item = player.registryAccess().registryOrThrow(Registries.ITEM).get(id)
|
||||
?: return@put DoubleValue.ZERO
|
||||
ItemStack(item)
|
||||
}
|
||||
|
||||
else -> null
|
||||
}
|
||||
|
||||
if (stack == null) return@put DoubleValue.ZERO
|
||||
|
||||
if (freeSlot == -1) {
|
||||
if (dropIfFull) {
|
||||
player.drop(stack, false)
|
||||
return@put DoubleValue.ONE
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
|
||||
inventory.setItem(freeSlot, stack)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["face"] = { params ->
|
||||
ObjectValue(
|
||||
PlayerDialogueFaceProvider(
|
||||
player.uuid,
|
||||
params.getBooleanOrNull(0) != false
|
||||
)
|
||||
)
|
||||
}
|
||||
map["swing_hand"] = { _ -> player.swing(player.usedItemHand) }
|
||||
map["food_level"] = { _ -> DoubleValue(player.foodData.foodLevel) }
|
||||
map["saturation_level"] = { _ -> DoubleValue(player.foodData.saturationLevel) }
|
||||
map["tell"] = { params ->
|
||||
val message = params.getString(0).text()
|
||||
val overlay = params.getBooleanOrNull(1) == true
|
||||
player.displayClientMessage(message, overlay)
|
||||
}
|
||||
map["teleport"] = { params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
val playParticleOptionss = params.getBooleanOrNull(3) ?: false
|
||||
player.randomTeleport(x, y, z, playParticleOptionss)
|
||||
}
|
||||
map["heal"] = { params ->
|
||||
val amount = params.getDoubleOrNull(0) ?: player.maxHealth
|
||||
player.heal(amount.toFloat())
|
||||
}
|
||||
map["environment"] = {
|
||||
val environment = MoLangEnvironment()
|
||||
environment.query = player.asMoLangValue()
|
||||
environment
|
||||
}
|
||||
map["is_player"] = { DoubleValue.ONE }
|
||||
map["riding_pokemon"] = put@{
|
||||
val vehicle = player.vehicle
|
||||
if (vehicle is PokemonEntity) {
|
||||
return@put vehicle.struct
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
if (player is ServerPlayer) {
|
||||
map["seen_credits"] = { _ ->
|
||||
DoubleValue(player.seenCredits)
|
||||
}
|
||||
map["is_in_dialogue"] = { _ ->
|
||||
DoubleValue(player.isInDialogue)
|
||||
}
|
||||
map["active_dialogue"] = put@{ _ ->
|
||||
if (player.isInDialogue) {
|
||||
return@put player.activeDialogue?.toMoLangStruct() ?: DoubleValue.ZERO
|
||||
} else {
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map["is_spectator"] = { DoubleValue(player.isSpectator) }
|
||||
map["is_creative"] = { DoubleValue(player.isCreative) }
|
||||
map["is_survival"] = { DoubleValue(player.gameMode.isSurvival) }
|
||||
map["is_adventure"] = { DoubleValue(player.gameMode.gameModeForPlayer == GameType.ADVENTURE) }
|
||||
map["run_command"] = { params ->
|
||||
val command = params.getString(0)
|
||||
player.server.commands.performPrefixedCommand(player.createCommandSourceStack(), command)
|
||||
}
|
||||
map["set_battle_theme"] = put@{ params ->
|
||||
val soundId = params.getString(0).asResource()
|
||||
Cobblemon.playerDataManager.getGenericData(player).battleTheme = soundId
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["battle_music"] = put@{ 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
|
||||
|
||||
if (soundId != null) {
|
||||
val packet = BattleMusicPacket(soundId, volume, pitch, restart)
|
||||
packet.sendToPlayer(player)
|
||||
return@put DoubleValue.ONE
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map["stop_battle_music"] = put@{ _ ->
|
||||
val packet = BattleMusicPacket(null)
|
||||
packet.sendToPlayer(player)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["play_sound_on_server"] = { params ->
|
||||
val sound = params.getString(0).asResource()
|
||||
val soundSource = SoundSource.valueOf(params.getString(1).uppercase())
|
||||
val volume = params.getDoubleOrNull(2)?.toFloat() ?: 1.0f
|
||||
val pitch = params.getDoubleOrNull(3)?.toFloat() ?: 1.0f
|
||||
|
||||
val packet =
|
||||
UnvalidatedPlaySoundS2CPacket(sound, soundSource, player.x, player.y, player.z, volume, pitch)
|
||||
packet.sendToPlayer(player)
|
||||
}
|
||||
map["is_party_at_full_health"] = { _ ->
|
||||
DoubleValue(player.party().none(Pokemon::canBeHealed))
|
||||
}
|
||||
map["can_heal_at_healer"] = put@{ params ->
|
||||
val pos = params.get<ArrayStruct>(0).asBlockPos()
|
||||
val healer = player.level().getBlockEntity(pos, CobblemonBlockEntities.HEALING_MACHINE).orElse(null)
|
||||
?: return@put DoubleValue.ZERO
|
||||
val party = player.party()
|
||||
return@put DoubleValue(healer.canHeal(party))
|
||||
}
|
||||
map["put_pokemon_in_healer"] = put@{ params ->
|
||||
val healer = player.level()
|
||||
.getBlockEntity(params.get<ArrayStruct>(0).asBlockPos(), CobblemonBlockEntities.HEALING_MACHINE)
|
||||
.orElse(null) ?: return@put DoubleValue.ZERO
|
||||
val party = player.party()
|
||||
if (healer.canHeal(party)) {
|
||||
healer.activate(player.uuid, party)
|
||||
return@put DoubleValue.ONE
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map["party"] = { player.party().struct }
|
||||
map["pc"] = { player.pc().struct }
|
||||
map["has_permission"] = { params ->
|
||||
DoubleValue(
|
||||
Cobblemon.permissionValidator.hasPermission(
|
||||
player,
|
||||
params.getString(0),
|
||||
params.getIntOrNull(1) ?: 4
|
||||
)
|
||||
)
|
||||
}
|
||||
map["data"] = { params -> Cobblemon.molangData.load(player.uuid, params.getStringOrNull(0)) }
|
||||
map["save_data"] = { params -> Cobblemon.molangData.save(player.uuid, params.getStringOrNull(0)) }
|
||||
map["in_battle"] = { DoubleValue(player.isInBattle()) }
|
||||
map["battle"] = { player.getBattleState()?.first?.struct ?: DoubleValue.ZERO }
|
||||
map["get_npc_data"] = put@{ params ->
|
||||
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 {
|
||||
val vars = VariableStruct()
|
||||
data.map[npcId] = vars
|
||||
return@put vars
|
||||
}
|
||||
}
|
||||
map["get_npc_variable"] = put@{ params ->
|
||||
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, params.getStringOrNull(2))
|
||||
if (data.map.containsKey(npcId)) {
|
||||
return@put (data.map[npcId] as VariableStruct).map[variable] ?: DoubleValue.ZERO
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map["set_npc_variable"] = put@{ params ->
|
||||
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 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, path)
|
||||
}
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["pokedex"] = { player.pokedex().struct }
|
||||
map["has_advancement"] = put@{ params ->
|
||||
val requiredAdvancement = ResourceLocation.parse(params.getString(0))
|
||||
for (entry in player.advancements.progress) {
|
||||
if (entry.key.id == requiredAdvancement && entry.value.isDone) {
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
}
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
map["start_battle"] = put@{ params ->
|
||||
val opponentValue = params.get<MoValue>(0)
|
||||
val opponent = if (opponentValue is ObjectValue<*>) {
|
||||
opponentValue.obj as ServerPlayer
|
||||
} else {
|
||||
val paramString = opponentValue.asString()
|
||||
val playerUUID = paramString.asUUID
|
||||
if (playerUUID != null) {
|
||||
server()?.playerList?.getPlayer(playerUUID) ?: return@put DoubleValue.ZERO
|
||||
} else {
|
||||
server()?.playerList?.getPlayerByName(paramString) ?: return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
val format = params.getStringOrNull(1)
|
||||
?.let(BattleFormat::fromFormatIdentifier)
|
||||
?: BattleFormat.GEN_9_SINGLES
|
||||
|
||||
val setLevel = params.getIntOrNull(2) ?: -1
|
||||
format.adjustLevel = setLevel
|
||||
|
||||
val rules = params.getStringOrNull(5)
|
||||
?.split(",")
|
||||
?.toSet()
|
||||
?: emptySet()
|
||||
|
||||
val modifiedBattleFormat = BattleFormat.setBattleRules(
|
||||
battleFormat = format,
|
||||
rules = rules
|
||||
)
|
||||
|
||||
val cloneParties = (setLevel != -1) || (params.getBooleanOrNull(3) ?: false)
|
||||
val healFirst = params.getBooleanOrNull(4) ?: false
|
||||
|
||||
val battleStartResult = BattleBuilder.pvp1v1(
|
||||
player1 = player,
|
||||
player2 = opponent,
|
||||
battleFormat = modifiedBattleFormat,
|
||||
cloneParties = cloneParties,
|
||||
healFirst = healFirst
|
||||
)
|
||||
var returnValue: MoValue = DoubleValue.ZERO
|
||||
battleStartResult.ifSuccessful { returnValue = it.struct }
|
||||
return@put returnValue
|
||||
}
|
||||
map["get_custom_stat"] = put@{ params ->
|
||||
val statName = params.getString(0)
|
||||
|
||||
val resourceLocation = ResourceLocation.tryParse(statName) ?: return@put DoubleValue.ZERO
|
||||
val statResourceLocation =
|
||||
BuiltInRegistries.CUSTOM_STAT.get(resourceLocation) ?: return@put DoubleValue.ZERO
|
||||
|
||||
val exists = net.minecraft.stats.Stats.CUSTOM.contains(statResourceLocation)
|
||||
if (!exists) return@put DoubleValue.ZERO
|
||||
|
||||
val stat = net.minecraft.stats.Stats.CUSTOM.get(statResourceLocation)
|
||||
val value = player.stats.getValue(stat)
|
||||
|
||||
DoubleValue(value)
|
||||
}
|
||||
map["get_starter_uuid"] = { _ ->
|
||||
Cobblemon.playerDataManager.getGenericData(player).starterUUID?.let {
|
||||
StringValue(
|
||||
it.toString()
|
||||
)
|
||||
} ?: DoubleValue.ZERO
|
||||
}
|
||||
map["has_chosen_starter"] = { _ -> DoubleValue(Cobblemon.playerDataManager.getGenericData(player).starterSelected) }
|
||||
}
|
||||
|
||||
return map
|
||||
}
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.molang.function
|
||||
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.pokedex.AbstractPokedexManager
|
||||
import com.cobblemon.mod.common.api.pokedex.CaughtCount
|
||||
import com.cobblemon.mod.common.api.pokedex.CaughtPercent
|
||||
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
|
||||
import com.cobblemon.mod.common.api.pokedex.PokedexManager
|
||||
import com.cobblemon.mod.common.api.pokedex.SeenCount
|
||||
import com.cobblemon.mod.common.api.pokedex.SeenPercent
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.getStringOrNull
|
||||
|
||||
object PokedexMoLangFunctions : AbstractMoLangFunctionHolder<AbstractPokedexManager>() {
|
||||
override fun AbstractPokedexManager.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val pokedex = this
|
||||
val map = mutableMapOf<String, (MoParams) -> Any>()
|
||||
map["get_species_record"] = { params ->
|
||||
val speciesId = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
pokedex.speciesRecords[speciesId]?.struct ?: QueryStruct(hashMapOf())
|
||||
}
|
||||
|
||||
map["has_seen"] = put@{ params ->
|
||||
val speciesId = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val formName = params.getStringOrNull(1)
|
||||
|
||||
if (formName == null) {
|
||||
return@put DoubleValue(pokedex.getHighestKnowledgeForSpecies(speciesId).ordinal >= PokedexEntryProgress.SEEN.ordinal)
|
||||
} else {
|
||||
return@put DoubleValue(
|
||||
(pokedex.getSpeciesRecord(speciesId)?.getFormRecord(formName)?.knowledge?.ordinal
|
||||
?: 0) >= PokedexEntryProgress.SEEN.ordinal
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
map["has_caught"] = put@{ params ->
|
||||
val speciesId = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val formName = params.getStringOrNull(1)
|
||||
if (formName == null) {
|
||||
return@put DoubleValue(pokedex.getHighestKnowledgeForSpecies(speciesId) == PokedexEntryProgress.OWNED)
|
||||
} else {
|
||||
return@put DoubleValue(
|
||||
pokedex.getSpeciesRecord(speciesId)
|
||||
?.getFormRecord(formName)?.knowledge == PokedexEntryProgress.OWNED
|
||||
)
|
||||
}
|
||||
}
|
||||
map["caught_count"] = { DoubleValue(pokedex.getGlobalCalculatedValue(CaughtCount)) }
|
||||
map["seen_count"] = { DoubleValue(pokedex.getGlobalCalculatedValue(SeenCount)) }
|
||||
map["caught_percent"] = { DoubleValue(pokedex.getGlobalCalculatedValue(CaughtPercent)) }
|
||||
map["seen_percent"] = { DoubleValue(pokedex.getGlobalCalculatedValue(SeenPercent)) }
|
||||
|
||||
if (pokedex is PokedexManager) {
|
||||
map["player_id"] = { StringValue(pokedex.uuid.toString()) }
|
||||
map["see"] = put@{ params ->
|
||||
val pokemon = params.get<ObjectValue<Pokemon>>(0).obj
|
||||
pokedex.encounter(pokemon)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["catch"] = put@{ params ->
|
||||
val pokemon = params.get<ObjectValue<Pokemon>>(0).obj
|
||||
pokedex.obtain(pokemon)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
}
|
||||
|
||||
return map
|
||||
}
|
||||
}
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* 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.molang.function
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.CobblemonActivities
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
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.TargetsProvider
|
||||
import com.cobblemon.mod.common.api.storage.party.PartyStore
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
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.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.cloneFrom
|
||||
import com.cobblemon.mod.common.util.getStringOrNull
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
|
||||
object PokemonEntityMoLangFunctions : AbstractMoLangFunctionHolder<PokemonEntity>() {
|
||||
override fun PokemonEntity.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val pokemonEntity = this
|
||||
val map = mutableMapOf<String, (MoParams) -> Any>()
|
||||
map["is_busy"] = { DoubleValue(pokemonEntity.isBusy) }
|
||||
map["in_battle"] = { DoubleValue(pokemonEntity.isBattling) }
|
||||
map["is_moving"] = { DoubleValue((pokemonEntity.moveControl as? PokemonMoveControl)?.hasWanted() == true) }
|
||||
map["is_flying"] = { DoubleValue(pokemonEntity.getBehaviourFlag(PokemonBehaviourFlag.FLYING)) }
|
||||
map["get_riding_state"] = { params ->
|
||||
val name = params.getStringOrNull(0)
|
||||
pokemonEntity.ifRidingAvailableSupply(DoubleValue.ZERO) { behaviour, settings, state ->
|
||||
val moValue = behaviour.asMoLangValue(settings, state, pokemonEntity)
|
||||
return@ifRidingAvailableSupply moValue.functions[name]?.apply(MoParams.EMPTY) as? MoValue ?: moValue
|
||||
}
|
||||
}
|
||||
map["is_gliding"] = {
|
||||
pokemonEntity.ifRidingAvailableSupply(DoubleValue.ZERO) { behaviour, settings, state ->
|
||||
val moValue = behaviour.asMoLangValue(settings, state, pokemonEntity)
|
||||
return@ifRidingAvailableSupply moValue.functions["gliding"]?.apply(MoParams.EMPTY) as? MoValue
|
||||
?: DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map["is_sprinting"] = {
|
||||
pokemonEntity.ifRidingAvailableSupply(DoubleValue.ZERO) { behaviour, settings, state ->
|
||||
val moValue = behaviour.asMoLangValue(settings, state, pokemonEntity)
|
||||
return@ifRidingAvailableSupply moValue.functions["sprinting"]?.apply(MoParams.EMPTY) as? MoValue
|
||||
?: DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map["is_drifting"] = {
|
||||
pokemonEntity.ifRidingAvailableSupply(DoubleValue.ZERO) { behaviour, settings, state ->
|
||||
val moValue = behaviour.asMoLangValue(settings, state, pokemonEntity)
|
||||
return@ifRidingAvailableSupply moValue.functions["drifting"]?.apply(MoParams.EMPTY) as? MoValue
|
||||
?: DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map["is_powered_drifting"] = {
|
||||
pokemonEntity.ifRidingAvailableSupply(DoubleValue.ZERO) { behaviour, settings, state ->
|
||||
val moValue = behaviour.asMoLangValue(settings, state, pokemonEntity)
|
||||
return@ifRidingAvailableSupply moValue.functions["powered_drifting"]?.apply(MoParams.EMPTY) as? MoValue
|
||||
?: DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map["in_air"] = {
|
||||
pokemonEntity.ifRidingAvailableSupply(DoubleValue.ZERO) { behaviour, settings, state ->
|
||||
val moValue = behaviour.asMoLangValue(settings, state, pokemonEntity)
|
||||
return@ifRidingAvailableSupply moValue.functions["in_air"]?.apply(MoParams.EMPTY) as? MoValue
|
||||
?: DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map["is_wild"] = { DoubleValue(pokemonEntity.ownerUUID == null) }
|
||||
map["is_in_party"] = { DoubleValue(pokemonEntity.pokemon.storeCoordinates.get()?.store is PartyStore) }
|
||||
map["is_ridden"] = { DoubleValue(pokemonEntity.hasControllingPassenger()) }
|
||||
map["has_aspect"] = { DoubleValue(it.getString(0) in pokemonEntity.aspects) }
|
||||
map["is_pokemon"] = { DoubleValue.ONE }
|
||||
map["is_holding_item"] = {
|
||||
DoubleValue(!pokemonEntity.entityData.get(PokemonEntity.SHOWN_HELD_ITEM).let {
|
||||
it.isEmpty || it.`is`(CobblemonItemTags.WEARABLE_HAT_ITEMS) || it.`is`(CobblemonItemTags.WEARABLE_FACE_ITEMS)
|
||||
})
|
||||
}
|
||||
map["riding_style"] = {
|
||||
StringValue(pokemonEntity.ifRidingAvailableSupply("") { behaviour, settings, state ->
|
||||
behaviour.getRidingStyle(settings, state).name
|
||||
})
|
||||
}
|
||||
map["is_wearing_hat"] = {
|
||||
DoubleValue(
|
||||
pokemonEntity.entityData
|
||||
.get(PokemonEntity.SHOWN_HELD_ITEM)
|
||||
.`is`(CobblemonItemTags.WEARABLE_HAT_ITEMS)
|
||||
)
|
||||
}
|
||||
map["is_wearing_face"] = {
|
||||
DoubleValue(
|
||||
pokemonEntity.entityData
|
||||
.get(PokemonEntity.SHOWN_HELD_ITEM)
|
||||
.`is`(CobblemonItemTags.WEARABLE_FACE_ITEMS)
|
||||
)
|
||||
}
|
||||
map["is_pastured"] = {
|
||||
DoubleValue((pokemonEntity.tethering != null))
|
||||
}
|
||||
map["pasture_conflict_enabled"] = {
|
||||
DoubleValue(pokemonEntity.getBehaviourFlag(PokemonBehaviourFlag.PASTURE_CONFLICT))
|
||||
}
|
||||
map["run_action_effect"] = runActionEffect@{ params ->
|
||||
val runtime = MoLangRuntime().setup()
|
||||
runtime.environment.cloneFrom(params.environment)
|
||||
runtime.withQueryValue("entity", pokemonEntity.struct)
|
||||
val actionEffect = ActionEffects.actionEffects[params.getString(0).asIdentifierDefaultingNamespace()]
|
||||
if (actionEffect != null) {
|
||||
val context = ActionEffectContext(
|
||||
actionEffect = actionEffect,
|
||||
providers = mutableListOf(TargetsProvider(pokemonEntity)),
|
||||
runtime = runtime,
|
||||
level = pokemonEntity.level()
|
||||
)
|
||||
// This was taken from NPC run_action_effect but this variable isn't used, so I just commented it out.
|
||||
// pokemonEntity.actionEffect = context
|
||||
pokemonEntity.brain.setMemory(CobblemonMemories.ACTIVE_ACTION_EFFECT, context)
|
||||
pokemonEntity.brain.setActiveActivityIfPossible(CobblemonActivities.ACTION_EFFECT)
|
||||
actionEffect.run(context).thenRun {
|
||||
val pokemonActionEffect =
|
||||
pokemonEntity.brain.getMemory(CobblemonMemories.ACTIVE_ACTION_EFFECT).orElse(null)
|
||||
if (pokemonActionEffect == context && pokemonEntity.brain.isActive(CobblemonActivities.ACTION_EFFECT)) {
|
||||
pokemonEntity.brain.eraseMemory(CobblemonMemories.ACTIVE_ACTION_EFFECT)
|
||||
//pokemonEntity.actionEffect = null
|
||||
}
|
||||
}
|
||||
|
||||
return@runActionEffect DoubleValue.ONE
|
||||
}
|
||||
return@runActionEffect DoubleValue.ZERO
|
||||
}
|
||||
return map
|
||||
}
|
||||
}
|
||||
+457
@@ -0,0 +1,457 @@
|
||||
/*
|
||||
* 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.molang.function
|
||||
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonMovesetBuilders
|
||||
import com.cobblemon.mod.common.api.mark.Marks
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.moves.BenchedMove
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
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.stats.RidingStat
|
||||
import com.cobblemon.mod.common.api.storage.party.PartyStore
|
||||
import com.cobblemon.mod.common.pokemon.IVs
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.asArrayValue
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.getBooleanOrNull
|
||||
import com.cobblemon.mod.common.util.getDoubleOrNull
|
||||
import com.cobblemon.mod.common.util.getIntOrNull
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import com.cobblemon.mod.common.util.toProperties
|
||||
import kotlin.random.Random
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
object PokemonMoLangFunctions : AbstractMoLangFunctionHolder<Pokemon>() {
|
||||
override fun Pokemon.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val pokemon = this
|
||||
val map = mutableMapOf<String, (MoParams) -> Any>()
|
||||
map["id"] = { StringValue(pokemon.uuid.toString()) }
|
||||
map["nickname"] = { StringValue(pokemon.nickname.toString()) }
|
||||
map["level"] = { DoubleValue(pokemon.level.toDouble()) }
|
||||
map["max_hp"] = { DoubleValue(pokemon.maxHealth.toDouble()) }
|
||||
map["current_hp"] = { DoubleValue(pokemon.currentHealth.toDouble()) }
|
||||
map["friendship"] = { DoubleValue(pokemon.friendship.toDouble()) }
|
||||
map["max_fullness"] = { DoubleValue(pokemon.getMaxFullness().toDouble()) }
|
||||
map["fullness"] = { DoubleValue(pokemon.currentFullness.toDouble()) }
|
||||
map["lose_fullness"] = { params ->
|
||||
val amount = params.getDouble(0)
|
||||
pokemon.loseFullness(amount.toInt())
|
||||
}
|
||||
map["feed_pokemon"] = { params ->
|
||||
val amount = params.getDouble(0)
|
||||
val playSound = params.getBooleanOrNull(1) ?: true
|
||||
pokemon.feedPokemon(amount.toInt(), playSound)
|
||||
}
|
||||
map["behaviour"] = { pokemon.form.behaviour.struct }
|
||||
map["behavior"] = { pokemon.form.behaviour.struct } // Inferior
|
||||
map["pokeball"] = { StringValue(pokemon.caughtBall.toString()) }
|
||||
map["ability"] = { StringValue(pokemon.ability.name) }
|
||||
map["has_learned"] = put@{ params ->
|
||||
val moveName = params.getString(0)
|
||||
val move = pokemon.allAccessibleMoves.find { it.name == moveName }
|
||||
if (move != null) {
|
||||
return@put DoubleValue.ONE
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map["moveset"] = {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for ((index, move) in pokemon.moveSet.withIndex()) {
|
||||
struct.addFunction(index.toString()) { move.struct }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map["evs"] = {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for (stat in Stats.PERMANENT) {
|
||||
struct.addFunction(stat.showdownId) { DoubleValue(pokemon.evs.getOrDefault(stat).toDouble()) }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map["ivs"] = {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for (stat in Stats.PERMANENT) {
|
||||
struct.addFunction(stat.showdownId) { DoubleValue(pokemon.ivs.getOrDefault(stat).toDouble()) }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map["hyper_trained_ivs"] = {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for (stat in Stats.PERMANENT) {
|
||||
struct.addFunction(stat.showdownId) { DoubleValue(pokemon.ivs.hyperTrainedIVs[stat] ?: -1.0) }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map["ride_boosts"] = {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for (stat in RidingStat.entries) {
|
||||
struct.addFunction(stat.name.lowercase()) { DoubleValue(pokemon.getRideBoost(stat)) }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map["natdex_number"] = {
|
||||
DoubleValue(pokemon.species.nationalPokedexNumber.toDouble())
|
||||
}
|
||||
map["types"] = {
|
||||
pokemon.form.types.map { it.toString() }.asArrayValue(::StringValue)
|
||||
}
|
||||
map["gender_ratio"] = {
|
||||
DoubleValue(pokemon.form.maleRatio.toDouble())
|
||||
}
|
||||
map["ev_yield"] = {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for (stat in Stats.PERMANENT) {
|
||||
struct.addFunction(stat.showdownId) { DoubleValue(pokemon.form.evYield[stat]?.toDouble()) }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map["base_stats"] = {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for (stat in Stats.PERMANENT) {
|
||||
struct.addFunction(stat.showdownId) { DoubleValue(pokemon.form.baseStats[stat]?.toDouble()) }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map["catch_rate"] = {
|
||||
DoubleValue(pokemon.form.catchRate.toDouble())
|
||||
}
|
||||
map["base_experience_yield"] = {
|
||||
DoubleValue(pokemon.form.baseExperienceYield.toDouble())
|
||||
}
|
||||
map["drops"] = {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for ((index, drop) in pokemon.form.drops.entries.withIndex()) {
|
||||
struct.addFunction(index.toString()) { drop }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map["tm_learnset"] = {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for ((index, move) in pokemon.form.moves.tmMoves.withIndex()) {
|
||||
struct.addFunction(index.toString()) { move.struct }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map["egg_learnset"] = {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for ((index, move) in pokemon.form.moves.eggMoves.withIndex()) {
|
||||
struct.addFunction(index.toString()) { move.struct }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map["tutor_learnset"] = {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for ((index, move) in pokemon.form.moves.tutorMoves.withIndex()) {
|
||||
struct.addFunction(index.toString()) { move.struct }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map["level_learnset"] = {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for ((index, move) in pokemon.form.moves.levelUpMoves) {
|
||||
struct.addFunction(index.toString()) { move }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map["ability_pool"] = {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for ((index, ability) in pokemon.form.abilities.withIndex()) {
|
||||
struct.addFunction(index.toString()) { StringValue(ability.toString()) }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map["egg_groups"] = {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for ((index, group) in pokemon.form.eggGroups.withIndex()) {
|
||||
struct.addFunction(index.toString()) { StringValue(group.toString()) }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map["egg_cycles"] = {
|
||||
DoubleValue(pokemon.species.eggCycles.toDouble())
|
||||
}
|
||||
map["labels"] = {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for ((index, label) in pokemon.form.labels.withIndex()) {
|
||||
struct.addFunction(index.toString()) { StringValue(label) }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map["aspects"] = {
|
||||
val aspects = pokemon.aspects
|
||||
aspects.asArrayValue { StringValue(it) }
|
||||
}
|
||||
map["form_aspects"] = {
|
||||
val aspects = pokemon.form.aspects
|
||||
aspects.asArrayValue { StringValue(it) }
|
||||
}
|
||||
map["form_name"] = {
|
||||
StringValue(pokemon.form.name)
|
||||
}
|
||||
// Yes, this is the lazy call for a single hardcoded pre-evolution.
|
||||
// Lol. Lmao, even.
|
||||
// TO-DO: Subscribe to [PokemonSpecies.observable].
|
||||
// Gito: surely this is just a typo, right? not touching the function name just in case
|
||||
map["prevolution"] = preEvolution@{
|
||||
val preEvolution = pokemon.species.preEvolution ?: return@preEvolution DoubleValue.ZERO
|
||||
return@preEvolution preEvolution
|
||||
}
|
||||
map["nature"] = { StringValue(pokemon.nature.toString()) }
|
||||
map["is_wild"] = { DoubleValue(pokemon.entity?.let { it.ownerUUID == null } == true) }
|
||||
map["is_shiny"] = { DoubleValue(pokemon.shiny) }
|
||||
map["is_in_party"] = { DoubleValue(pokemon.storeCoordinates.get()?.store is PartyStore) }
|
||||
map["species"] = { pokemon.species.struct }
|
||||
map["form"] = { StringValue(pokemon.form.name) }
|
||||
map["weight"] = { DoubleValue(pokemon.species.weight.toDouble()) }
|
||||
map["matches"] = { params -> DoubleValue(params.getString(0).toProperties().matches(pokemon)) }
|
||||
map["apply"] = { params ->
|
||||
params.getString(0).toProperties().apply(pokemon)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["owner"] = { pokemon.getOwnerPlayer()?.asMoLangValue() ?: DoubleValue.ZERO }
|
||||
map["held_item"] = { pokemon.heldItem().asMoLangValue(server()!!.registryAccess()) }
|
||||
map["remove_held_item"] = { _ ->
|
||||
pokemon.removeHeldItem()
|
||||
}
|
||||
map["add_aspects"] = { params ->
|
||||
for (aspect in params.params) pokemon.forcedAspects += aspect.asString()
|
||||
pokemon.updateAspects()
|
||||
}
|
||||
map["remove_aspects"] = { params ->
|
||||
for (aspect in params.params) pokemon.forcedAspects -= aspect.asString()
|
||||
pokemon.updateAspects()
|
||||
}
|
||||
map["cosmetic_item"] = {
|
||||
pokemon.cosmeticItem().asMoLangValue(server()!!.registryAccess())
|
||||
}
|
||||
map["remove_cosmetic_item"] = { _ ->
|
||||
pokemon.removeCosmeticItem()
|
||||
}
|
||||
map["marks"] = { _ -> pokemon.marks.asArrayValue { StringValue(it.identifier.toString()) } }
|
||||
map["has_mark"] = { params ->
|
||||
var hasMark = false
|
||||
val identifier = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val mark = Marks.getByIdentifier(identifier)
|
||||
if (mark != null) {
|
||||
hasMark = pokemon.marks.contains(mark)
|
||||
}
|
||||
|
||||
DoubleValue(hasMark)
|
||||
}
|
||||
map["remove_marks"] = { params ->
|
||||
var removedMark = false
|
||||
for (param in params.params) {
|
||||
val identifier = param.asString().asIdentifierDefaultingNamespace()
|
||||
val mark = Marks.getByIdentifier(identifier)
|
||||
if (mark != null) {
|
||||
pokemon.exchangeMark(mark, false)
|
||||
removedMark = true
|
||||
}
|
||||
}
|
||||
|
||||
DoubleValue(removedMark)
|
||||
}
|
||||
map["add_marks"] = { params ->
|
||||
var appliedMark = false
|
||||
for (param in params.params) {
|
||||
val identifier = param.asString().asIdentifierDefaultingNamespace()
|
||||
val mark = Marks.getByIdentifier(identifier)
|
||||
if (mark != null) {
|
||||
pokemon.exchangeMark(mark, true)
|
||||
appliedMark = true
|
||||
}
|
||||
}
|
||||
|
||||
DoubleValue(appliedMark)
|
||||
}
|
||||
map["add_marks_with_chance"] = { params ->
|
||||
var appliedMark = false
|
||||
for (param in params.params) {
|
||||
val identifier = param.asString().asIdentifierDefaultingNamespace()
|
||||
val mark = Marks.getByIdentifier(identifier)
|
||||
|
||||
mark?.let {
|
||||
val probability = it.chance.coerceIn(0F, 1F) * 100
|
||||
val randomValue = Random.nextDouble(0.0, 100.0)
|
||||
if (randomValue < probability) {
|
||||
pokemon.exchangeMark(it, true)
|
||||
appliedMark = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DoubleValue(appliedMark)
|
||||
}
|
||||
map["add_potential_marks"] = { params ->
|
||||
for (param in params.params) {
|
||||
val identifier = param.asString().asIdentifierDefaultingNamespace()
|
||||
val mark = Marks.getByIdentifier(identifier)
|
||||
if (mark != null) pokemon.addPotentialMark(mark)
|
||||
}
|
||||
}
|
||||
map["apply_potential_marks"] = put@{
|
||||
return@put DoubleValue(pokemon.applyPotentialMarks())
|
||||
}
|
||||
map["hyper_train_iv"] = hyperTrainIv@{ params ->
|
||||
val statId = params.getString(0)
|
||||
val stat = Stats.getStat(statId)
|
||||
val value = params.getIntOrNull(1) ?: IVs.MAX_VALUE
|
||||
|
||||
if (Stats.PERMANENT.contains(stat)) {
|
||||
pokemon.hyperTrainIV(stat, value)
|
||||
return@hyperTrainIv DoubleValue.ONE
|
||||
} else {
|
||||
Cobblemon.LOGGER.error("Unknown or non-permanent stat: $stat")
|
||||
return@hyperTrainIv DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map["add_exp"] = { params ->
|
||||
val exp = params.getDouble(0).toInt()
|
||||
pokemon.addExperience(SidemodExperienceSource("molang"), exp)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["set_iv"] = setIv@{ params ->
|
||||
val statId = params.getString(0)
|
||||
val stat = Stats.getStat(statId)
|
||||
val value = params.getIntOrNull(1)?.coerceIn(0, IVs.MAX_VALUE) ?: IVs.MAX_VALUE
|
||||
|
||||
if (Stats.PERMANENT.contains(stat)) {
|
||||
pokemon.setIV(stat, value)
|
||||
return@setIv DoubleValue.ONE
|
||||
} else {
|
||||
return@setIv DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map["set_ev"] = setEv@{ params ->
|
||||
val statId = params.getString(0)
|
||||
val stat = Stats.getStat(statId)
|
||||
val value = (params.getIntOrNull(1) ?: 0).coerceIn(0, 255)
|
||||
|
||||
if (Stats.PERMANENT.contains(stat)) {
|
||||
pokemon.setEV(stat, value)
|
||||
return@setEv DoubleValue.ONE
|
||||
} else {
|
||||
return@setEv DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map["set_ride_boost"] = setRideBoost@{ params ->
|
||||
val statName = params.getString(0).uppercase()
|
||||
val stat = RidingStat.entries.find { it.name.equals(statName, ignoreCase = true) }
|
||||
?: return@setRideBoost DoubleValue.ZERO
|
||||
val value = params.getDoubleOrNull(1)?.toFloat() ?: return@setRideBoost DoubleValue.ZERO
|
||||
|
||||
pokemon.setRideBoost(stat, value)
|
||||
return@setRideBoost DoubleValue.ONE
|
||||
}
|
||||
map["add_ride_boost"] = addRideBoost@{ params ->
|
||||
val statName = params.getString(0).uppercase()
|
||||
val stat = RidingStat.entries.find { it.name.equals(statName, ignoreCase = true) }
|
||||
?: return@addRideBoost DoubleValue.ZERO
|
||||
val value = params.getDoubleOrNull(1)?.toFloat() ?: return@addRideBoost DoubleValue.ZERO
|
||||
|
||||
return@addRideBoost DoubleValue(if (pokemon.addRideBoost(stat, value)) 1.0 else 0.0)
|
||||
}
|
||||
map["initialize_moveset"] = put@{ params ->
|
||||
val param = params.get<MoValue>(0) ?: StringValue(pokemon.form.defaultWildMovesetBuilder.toString())
|
||||
if (param is DoubleValue) {
|
||||
pokemon.initializeMoveset(param == DoubleValue.ONE)
|
||||
} else {
|
||||
val movesetBuilderId = ResourceLocation.parse(param.asString())
|
||||
val movesetBuilder = CobblemonMovesetBuilders.movesetBuilders[movesetBuilderId]
|
||||
?: run {
|
||||
Cobblemon.LOGGER.error("Tried initializing moveset from moveset builder ${param.asString()} but it does not exist.")
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
pokemon.initializeMovesetFrom(movesetBuilder)
|
||||
}
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["validate_moveset"] = { params ->
|
||||
val includeLegacy = params.getBooleanOrNull(0) ?: true
|
||||
pokemon.validateMoveset(includeLegacy)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["teach_learnable_moves"] = { params ->
|
||||
val includeLegacy = params.getBooleanOrNull(0) ?: true
|
||||
pokemon.teachLearnableMoves(includeLegacy)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["teach_move"] = teachMove@{ params ->
|
||||
val moveName = params.getString(0)
|
||||
val moveTemplate = Moves.getByName(moveName) ?: return@teachMove DoubleValue.ZERO
|
||||
val bypass = params.getBooleanOrNull(1) ?: false
|
||||
|
||||
val canLearn = bypass || LearnsetQuery.ANY.canLearn(moveTemplate, pokemon.form.moves)
|
||||
if (!canLearn) {
|
||||
return@teachMove DoubleValue.ZERO
|
||||
}
|
||||
|
||||
val alreadyKnows = pokemon.moveSet.getMoves().any { it.template == moveTemplate } ||
|
||||
pokemon.benchedMoves.any { it.moveTemplate == moveTemplate }
|
||||
if (alreadyKnows) {
|
||||
return@teachMove DoubleValue.ZERO
|
||||
}
|
||||
|
||||
if (pokemon.moveSet.hasSpace()) {
|
||||
pokemon.moveSet.add(moveTemplate.create())
|
||||
} else {
|
||||
pokemon.benchedMoves.add(BenchedMove(moveTemplate, 0))
|
||||
}
|
||||
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["can_learn_move"] = canLearnMove@{ params ->
|
||||
val moveName = params.getString(0)
|
||||
val moveTemplate = Moves.getByName(moveName) ?: return@canLearnMove DoubleValue.ZERO
|
||||
val includeLegacy = params.getBooleanOrNull(1) ?: true
|
||||
|
||||
val canLearn = if (includeLegacy) {
|
||||
LearnsetQuery.ANY.canLearn(moveTemplate, pokemon.form.moves)
|
||||
} else {
|
||||
LearnsetQuery.LEGAL.canLearn(moveTemplate, pokemon.form.moves)
|
||||
}
|
||||
return@canLearnMove DoubleValue(if (canLearn) 1.0 else 0.0)
|
||||
}
|
||||
map["unlearn_move"] = unlearnMove@{ params ->
|
||||
val moveName = params.getString(0)
|
||||
val moveTemplate = Moves.getByName(moveName)
|
||||
if (moveTemplate != null) {
|
||||
pokemon.unlearnMove(moveTemplate)
|
||||
return@unlearnMove DoubleValue.ONE
|
||||
} else {
|
||||
return@unlearnMove DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map["can_evolve"] = { _ ->
|
||||
DoubleValue(pokemon.evolutions.any())
|
||||
}
|
||||
map["force_evolve"] = forceEvolve@{ params ->
|
||||
val idx = params.getInt(0)
|
||||
val evolution = if (idx >= 0) pokemon.evolutions.elementAtOrNull(idx) else return@forceEvolve DoubleValue.ZERO
|
||||
evolution?.forceEvolve(pokemon)
|
||||
StringValue(evolution.toString())
|
||||
}
|
||||
|
||||
map["entity"] = { pokemon.entity?.asMoLangValue() ?: DoubleValue.ZERO }
|
||||
map["is_alpha"] = { DoubleValue(pokemon.isAlpha) }
|
||||
map["size_category"] = { _ -> StringValue(pokemon.getSizeCategory().name) }
|
||||
|
||||
return map
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.molang.function
|
||||
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.pokemon.Gender
|
||||
import com.cobblemon.mod.common.util.getBooleanOrNull
|
||||
import com.cobblemon.mod.common.util.getIntOrNull
|
||||
import com.cobblemon.mod.common.util.getStringOrNull
|
||||
|
||||
object PokemonPropertiesMoLangFunctions : AbstractMoLangFunctionHolder<PokemonProperties>() {
|
||||
override fun PokemonProperties.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val props = this
|
||||
val map = mutableMapOf<String, (MoParams) -> Any>()
|
||||
|
||||
map["level"] = { DoubleValue(props.level?.toDouble() ?: 0) }
|
||||
map["set_level"] = { params ->
|
||||
props.level = params.getIntOrNull(0)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["shiny"] = { DoubleValue(props.shiny) }
|
||||
map["set_shiny"] = { params ->
|
||||
props.shiny = params.getBooleanOrNull(0)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["species"] = { props.species?.let { StringValue(it) } ?: DoubleValue.ZERO }
|
||||
map["set_species"] = { params ->
|
||||
props.species = params.getStringOrNull(0)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["gender"] = { props.gender?.let { StringValue(it.name) } ?: DoubleValue.ZERO }
|
||||
map["set_gender"] = { params ->
|
||||
props.gender = params.getStringOrNull(0)?.let { Gender.valueOf(it) }
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["form"] = { props.form?.let { StringValue(it) } ?: DoubleValue.ZERO }
|
||||
map["ivs"] = ivs@{
|
||||
val ivs = props.ivs ?: return@ivs DoubleValue.ZERO
|
||||
|
||||
return@ivs ivs.struct
|
||||
}
|
||||
map["evs"] = evs@{
|
||||
val evs = props.evs ?: return@evs DoubleValue.ZERO
|
||||
|
||||
return@evs evs.struct
|
||||
}
|
||||
map["friendship"] = { DoubleValue(props.friendship?.toDouble() ?: DoubleValue.ZERO) }
|
||||
map["set_friendship"] = { params ->
|
||||
props.friendship = params.getIntOrNull(0)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["create"] = {
|
||||
val pokemon = props.create()
|
||||
pokemon.struct
|
||||
}
|
||||
map["to_string"] = { params ->
|
||||
val separator = params.getStringOrNull(0) ?: " "
|
||||
StringValue(props.asString(separator = separator))
|
||||
}
|
||||
return map
|
||||
}
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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.molang.function
|
||||
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.struct.ArrayStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asStruct
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.storage.PokemonStore
|
||||
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.asArrayValue
|
||||
import com.cobblemon.mod.common.util.asUUID
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import com.cobblemon.mod.common.util.toProperties
|
||||
|
||||
object PokemonStoreMoLangFunctions: AbstractMoLangFunctionHolder<PokemonStore<*>>() {
|
||||
override fun PokemonStore<*>.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val store = this
|
||||
val map = mutableMapOf<String, (MoParams) -> Any>()
|
||||
map["uuid"] = { StringValue(store.uuid.toString()) }
|
||||
map["add"] = { params ->
|
||||
val pokemon = params.get<ObjectValue<Pokemon>>(0)
|
||||
DoubleValue(store.add(pokemon.obj))
|
||||
}
|
||||
map["add_by_properties"] = { params ->
|
||||
val props = params.getString(0).toProperties()
|
||||
val player = (store as? PlayerPartyStore)?.playerUUID?.getPlayer() // Only really know for sure when it's a player party store
|
||||
val pokemon = props.create(player = player)
|
||||
DoubleValue(store.add(pokemon))
|
||||
}
|
||||
map["find_by_properties"] = { params ->
|
||||
val props = params.getString(0).toProperties()
|
||||
val pokemon = store.find { props.matches(it) }
|
||||
pokemon?.asStruct() ?: DoubleValue.ZERO
|
||||
}
|
||||
map["find_all_by_properties"] = { params ->
|
||||
val props = params.getString(0).toProperties()
|
||||
val pokemon = store.filter { props.matches(it) }
|
||||
ArrayStruct(pokemon.mapIndexed { index, value -> "$index" to value.asStruct() }.toMap())
|
||||
}
|
||||
map["find_by_id"] = { params ->
|
||||
val id = params.getString(0).asUUID
|
||||
val pokemon = store.find { it.uuid == id }
|
||||
pokemon?.asStruct() ?: DoubleValue.ZERO
|
||||
}
|
||||
map["remove_by_id"] = removeById@{ params ->
|
||||
val id = params.getString(0).asUUID
|
||||
val pokemon = store.find { it.uuid == id } ?: return@removeById DoubleValue.ZERO
|
||||
return@removeById DoubleValue(store.remove(pokemon))
|
||||
}
|
||||
map["average_level"] = averageLevel@{ _ ->
|
||||
var numberOfPokemon = 0
|
||||
var totalLevel = 0
|
||||
for (pokemon in store) {
|
||||
totalLevel += pokemon.level
|
||||
numberOfPokemon++
|
||||
}
|
||||
if (numberOfPokemon == 0) {
|
||||
return@averageLevel DoubleValue.ZERO
|
||||
}
|
||||
return@averageLevel DoubleValue(totalLevel.toDouble() / numberOfPokemon)
|
||||
}
|
||||
map["count"] = { _ -> DoubleValue(store.count()) }
|
||||
map["count_by_properties"] = { params ->
|
||||
val props = params.getString(0).toProperties()
|
||||
DoubleValue(store.count { props.matches(it) })
|
||||
}
|
||||
map["highest_level"] = {
|
||||
val highest = store.maxOfOrNull { it.level } ?: 0
|
||||
DoubleValue(highest)
|
||||
}
|
||||
map["lowest_level"] = {
|
||||
val lowest = store.minOfOrNull { it.level } ?: 0
|
||||
DoubleValue(lowest)
|
||||
}
|
||||
map["heal"] = {
|
||||
for (pokemon in store) {
|
||||
pokemon.heal()
|
||||
}
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["healing_remainder_percent"] = { _ ->
|
||||
var totalPercent = 0.0f
|
||||
for (pokemon in store) {
|
||||
totalPercent += (1.0f - (pokemon.currentHealth.toFloat() / pokemon.maxHealth))
|
||||
}
|
||||
DoubleValue(totalPercent)
|
||||
}
|
||||
map["has_usable_pokemon"] = { _ -> DoubleValue(store.any { !it.isFainted() }) }
|
||||
map["pokemon"] = {
|
||||
store.map { it.asStruct() }.asArrayValue()
|
||||
}
|
||||
return map
|
||||
}
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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.molang.function
|
||||
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asWorldMoLangValue
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.getBooleanOrNull
|
||||
import com.cobblemon.mod.common.util.getStringOrNull
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.resources.ResourceKey
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import java.util.*
|
||||
|
||||
object ServerMoLangFunctions : AbstractMoLangFunctionHolder<MinecraftServer>() {
|
||||
override fun MinecraftServer.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val server = this
|
||||
val map = mutableMapOf<String, (MoParams) -> Any>()
|
||||
|
||||
map["get_world"] = getWorld@{ params ->
|
||||
val world = server.getLevel(
|
||||
ResourceKey.create(
|
||||
Registries.DIMENSION,
|
||||
params.getString(0).asIdentifierDefaultingNamespace(namespace = "minecraft")
|
||||
)
|
||||
) ?: return@getWorld DoubleValue.ZERO
|
||||
|
||||
return@getWorld world
|
||||
.registryAccess()
|
||||
.registryOrThrow(Registries.DIMENSION)
|
||||
.wrapAsHolder(world)
|
||||
.asWorldMoLangValue()
|
||||
}
|
||||
|
||||
map["broadcast"] = { params ->
|
||||
val message = params.getString(0)
|
||||
server.playerList.broadcastSystemMessage(message.text(), params.getBooleanOrNull(1) == true)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
|
||||
map["get_player_by_uuid"] = getPlayerByUuid@{ params ->
|
||||
val uuid = UUID.fromString(params.getString(0))
|
||||
val player = server.playerList.getPlayer(uuid) ?: return@getPlayerByUuid DoubleValue.ZERO
|
||||
return@getPlayerByUuid player.asMoLangValue()
|
||||
}
|
||||
|
||||
map["get_player_by_username"] = getPlayerByUsername@{ params ->
|
||||
val username = params.getString(0)
|
||||
val player = server.playerList.getPlayerByName(username) ?: return@getPlayerByUsername DoubleValue.ZERO
|
||||
return@getPlayerByUsername player.asMoLangValue()
|
||||
}
|
||||
|
||||
map["data"] = { params ->
|
||||
Cobblemon.molangData.load(
|
||||
UUID(0L, 0L),
|
||||
params.getStringOrNull(0)
|
||||
)
|
||||
}
|
||||
|
||||
map["save_data"] = { params ->
|
||||
Cobblemon.molangData.save(
|
||||
UUID(0L, 0L),
|
||||
params.getStringOrNull(index = 0)
|
||||
)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
|
||||
// Maybe later...
|
||||
// map.put("stop") { params ->
|
||||
// Cobblemon.LOGGER.warn("Server is being stopped from a MoLang script.")
|
||||
// server.stopServer()
|
||||
// }
|
||||
|
||||
return map
|
||||
}
|
||||
}
|
||||
+46
@@ -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.api.molang.function
|
||||
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asBiomeMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asWorldMoLangValue
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
object SpawnablePositionMoLangFunctions : AbstractMoLangFunctionHolder<SpawnablePosition>() {
|
||||
override fun SpawnablePosition.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val spawningContext = this
|
||||
val map = mutableMapOf<String, (MoParams) -> Any>()
|
||||
val worldValue = spawningContext.world.registryAccess().registryOrThrow(Registries.DIMENSION)
|
||||
.wrapAsHolder(spawningContext.world).asWorldMoLangValue()
|
||||
val biomeValue = spawningContext.biomeHolder.asBiomeMoLangValue()
|
||||
map["biome"] = { _ -> biomeValue }
|
||||
map["world"] = { _ -> worldValue }
|
||||
map["light"] = { _ -> DoubleValue(spawningContext.light.toDouble()) }
|
||||
map["x"] = { _ -> DoubleValue(spawningContext.position.x.toDouble()) }
|
||||
map["y"] = { _ -> DoubleValue(spawningContext.position.y.toDouble()) }
|
||||
map["z"] = { _ -> DoubleValue(spawningContext.position.z.toDouble()) }
|
||||
map["moon_phase"] = { _ -> DoubleValue(spawningContext.moonPhase.toDouble()) }
|
||||
map["can_see_sky"] = { _ -> DoubleValue(spawningContext.canSeeSky) }
|
||||
map["sky_light"] = { _ -> DoubleValue(spawningContext.skyLight.toDouble()) }
|
||||
map["player"] = put@{ _ ->
|
||||
val causeEntity = spawningContext.cause.entity ?: return@put DoubleValue.ZERO
|
||||
if (causeEntity is ServerPlayer) {
|
||||
return@put causeEntity.asMoLangValue()
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
return map
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.molang.function
|
||||
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
import com.cobblemon.mod.common.util.asArrayValue
|
||||
|
||||
object SpeciesMoLangFunctions : AbstractMoLangFunctionHolder<Species>() {
|
||||
override fun Species.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val species = this
|
||||
val map = mutableMapOf<String, (MoParams) -> Any>(
|
||||
"identifier" to { StringValue(species.resourceIdentifier.toString()) },
|
||||
"name" to { StringValue(species.name) },
|
||||
"primary_type" to { StringValue(species.primaryType.showdownId) },
|
||||
"secondary_type" to { StringValue(species.secondaryType?.showdownId ?: "null") },
|
||||
"experience_group" to { StringValue(species.experienceGroup.name) },
|
||||
"height" to { DoubleValue(species.height) },
|
||||
"weight" to { DoubleValue(species.weight) },
|
||||
"base_scale" to { DoubleValue(species.baseScale) },
|
||||
"hitbox_width" to { DoubleValue(species.hitbox.width) },
|
||||
"hitbox_height" to { DoubleValue(species.hitbox.height) },
|
||||
"hitbox_fixed" to { DoubleValue(species.hitbox.fixed) },
|
||||
"catch_rate" to { DoubleValue(species.catchRate) },
|
||||
"labels" to { species.labels.asArrayValue { StringValue(it) } },
|
||||
"has_label" to { params -> DoubleValue(species.labels.contains(params.getString(0))) }
|
||||
)
|
||||
|
||||
return map
|
||||
}
|
||||
}
|
||||
+293
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
* 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.molang.function
|
||||
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.struct.ArrayStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.npc.NPCClasses
|
||||
import com.cobblemon.mod.common.api.spawning.TimeRange
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.sound.UnvalidatedPlaySoundS2CPacket
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import java.util.UUID
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.resources.ResourceKey
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.sounds.SoundSource
|
||||
import net.minecraft.world.entity.EntityType
|
||||
import net.minecraft.world.entity.LightningBolt
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.Level.ExplosionInteraction
|
||||
import net.minecraft.world.level.biome.Biome
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.levelgen.Heightmap
|
||||
import net.minecraft.world.level.storage.loot.LootParams
|
||||
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets
|
||||
import net.minecraft.world.level.storage.loot.parameters.LootContextParams
|
||||
import net.minecraft.world.phys.AABB
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
object WorldMoLangFunctions : AbstractMoLangFunctionHolder<Holder<Level>>() {
|
||||
override fun Holder<Level>.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
|
||||
val world = this.value()
|
||||
val map = mutableMapOf<String, (MoParams) -> Any>(
|
||||
)
|
||||
|
||||
map["is_time_of_day"] = put@{ params ->
|
||||
|
||||
val timeOfDay = TimeRange.timeRanges[params.getString(0).lowercase()]
|
||||
?: return@put DoubleValue.ZERO
|
||||
val time = world.dayTime % 24000
|
||||
return@put DoubleValue(timeOfDay.contains(time.toInt()))
|
||||
}
|
||||
map["game_time"] = { _ -> DoubleValue(world.gameTime.toDouble()) }
|
||||
map["time_of_day"] = put@{
|
||||
val time = world.dayTime % 24000
|
||||
return@put DoubleValue(time.toDouble())
|
||||
}
|
||||
map["server"] = { _ -> server()?.asMoLangValue() ?: DoubleValue.ZERO }
|
||||
map["is_raining_at"] = put@{ params ->
|
||||
val x = params.getInt(0)
|
||||
val y = params.getInt(1)
|
||||
val z = params.getInt(2)
|
||||
return@put DoubleValue(world.isRainingAt(BlockPos(x, y, z)))
|
||||
}
|
||||
map["is_snowing_at"] = put@{ params ->
|
||||
val x = params.getInt(0)
|
||||
val y = params.getInt(1)
|
||||
val z = params.getInt(2)
|
||||
val blockPos = BlockPos(x, y, z)
|
||||
when {
|
||||
!world.isRaining -> return@put DoubleValue.ZERO
|
||||
!world.canSeeSky(blockPos) -> return@put DoubleValue.ZERO
|
||||
(world.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, blockPos).y > blockPos.y) -> {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
|
||||
else -> {
|
||||
val biome = world.getBiome(blockPos).value() as Biome
|
||||
return@put DoubleValue(biome.getPrecipitationAt(blockPos) == Biome.Precipitation.SNOW)
|
||||
}
|
||||
}
|
||||
}
|
||||
map["is_chunk_loaded_at"] = put@{ params ->
|
||||
val x = params.getInt(0)
|
||||
val y = params.getInt(1)
|
||||
val z = params.getInt(2)
|
||||
return@put DoubleValue(world.isLoaded(BlockPos(x, y, z)))
|
||||
}
|
||||
map["is_thundering"] = { _ -> DoubleValue(world.isThundering) }
|
||||
map["is_raining"] = { _ -> DoubleValue(world.isRaining) }
|
||||
map["set_block"] = put@{ params ->
|
||||
val x = params.getInt(0)
|
||||
val y = params.getInt(1)
|
||||
val z = params.getInt(2)
|
||||
val block = world.blockRegistry.get(params.getString(3).asIdentifierDefaultingNamespace())
|
||||
?: run {
|
||||
Cobblemon.LOGGER.error("Unknown block: ${params.getString(3)}")
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
world.setBlock(BlockPos(x, y, z), block.defaultBlockState(), Block.UPDATE_ALL)
|
||||
}
|
||||
map["is_air"] = put@{ params ->
|
||||
val x = params.getDouble(0).toInt()
|
||||
val y = params.getDouble(1).toInt()
|
||||
val z = params.getDouble(2).toInt()
|
||||
val blockState = world.getBlockState(BlockPos(x, y, z))
|
||||
return@put DoubleValue(blockState.isAir)
|
||||
}
|
||||
map["get_block"] = put@{ params ->
|
||||
val x = params.getInt(0)
|
||||
val y = params.getInt(1)
|
||||
val z = params.getInt(2)
|
||||
val block = world.getBlockState(BlockPos(x, y, z)).block
|
||||
return@put world.blockRegistry.wrapAsHolder(block).asMoLangValue(Registries.BLOCK)
|
||||
}
|
||||
map["spawn_explosion"] = { params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
val range = params.getDouble(3).toFloat()
|
||||
world.explode(
|
||||
null,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
range,
|
||||
ExplosionInteraction.valueOf(
|
||||
params.getStringOrNull(4)?.uppercase() ?: ExplosionInteraction.TNT.name
|
||||
)
|
||||
)
|
||||
}
|
||||
map["spawn_lightning"] = put@{ params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
val lightning = LightningBolt(EntityType.LIGHTNING_BOLT, world)
|
||||
lightning.setPos(x, y, z)
|
||||
world.addFreshEntity(lightning)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
// q.entity.world.spawn_bedrock_particles(effect, x, y, z, [player]) - sends to everyone nearby or just to the player if they're set.
|
||||
map["spawn_bedrock_particles"] = { params ->
|
||||
val particle = params.getString(0).asResource()
|
||||
val x = params.getDouble(1)
|
||||
val y = params.getDouble(2)
|
||||
val z = params.getDouble(3)
|
||||
val player = params.getOrNull<MoValue>(4)?.let {
|
||||
when (it) {
|
||||
is StringValue -> world.getPlayerByUUID(UUID.fromString(it.value))
|
||||
is ObjectValue<*> -> it.obj
|
||||
else -> null
|
||||
}
|
||||
} as? ServerPlayer
|
||||
val pos = Vec3(x, y, z)
|
||||
|
||||
val packet = SpawnSnowstormParticlePacket(particle, pos)
|
||||
if (player != null) {
|
||||
packet.sendToPlayer(player)
|
||||
} else {
|
||||
packet.sendToPlayersAround(x, y, z, 64.0, world.dimension())
|
||||
}
|
||||
}
|
||||
map["spawn_pokemon"] = put@{ params ->
|
||||
val x = params.getInt(0)
|
||||
val y = params.getInt(1)
|
||||
val z = params.getInt(2)
|
||||
val props = params.getString(3).toProperties()
|
||||
|
||||
val pos = BlockPos(x, y, z)
|
||||
|
||||
if (!Level.isInSpawnableBounds(pos)) {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
|
||||
val pokemon = props.createEntity(world)
|
||||
pokemon.moveTo(pos, pokemon.yRot, pokemon.xRot)
|
||||
|
||||
if (world.addFreshEntity(pokemon)) {
|
||||
return@put pokemon.struct
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map["spawn_npc"] = put@{ 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["play_sound_on_server"] = { params ->
|
||||
val sound = params.getString(0).asResource()
|
||||
val soundSource = params.getString(1).uppercase()
|
||||
val x = params.getDouble(2)
|
||||
val y = params.getDouble(3)
|
||||
val z = params.getDouble(4)
|
||||
val player = params.getOrNull<MoValue>(5)?.let {
|
||||
when (it) {
|
||||
is StringValue -> world.getPlayerByUUID(UUID.fromString(it.value))
|
||||
is ObjectValue<*> -> it.obj
|
||||
else -> null
|
||||
}
|
||||
} as? ServerPlayer
|
||||
val volume = params.getDoubleOrNull(6)?.toFloat() ?: 1.0f
|
||||
val pitch = params.getDoubleOrNull(7)?.toFloat() ?: 1.0f
|
||||
|
||||
val packet =
|
||||
UnvalidatedPlaySoundS2CPacket(sound, SoundSource.valueOf(soundSource), x, y, z, volume, pitch)
|
||||
if (player != null) {
|
||||
packet.sendToPlayer(player)
|
||||
} else {
|
||||
packet.sendToPlayersAround(x, y, z, 16.0, world.dimension())
|
||||
}
|
||||
}
|
||||
map["get_entities_around"] = put@{ params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
val range = params.getDouble(3) * 2
|
||||
val entities = world.getEntities(null, AABB.ofSize(Vec3(x, y, z), range, range, range))
|
||||
return@put entities
|
||||
.filterIsInstance<LivingEntity>()
|
||||
.map { it.asMostSpecificMoLangValue() }
|
||||
.asArrayValue()
|
||||
}
|
||||
map["is_healer_in_use"] = put@{ params ->
|
||||
val pos = params.get<ArrayStruct>(0).asBlockPos()
|
||||
val healer = world.getBlockEntity(pos, CobblemonBlockEntities.HEALING_MACHINE).orElse(null)
|
||||
?: return@put DoubleValue.ONE
|
||||
return@put DoubleValue(healer.isInUse)
|
||||
}
|
||||
|
||||
map["spawn_loot_table_items"] = put@{ params ->
|
||||
val serverLevel = world as? ServerLevel
|
||||
if (serverLevel == null) {
|
||||
Cobblemon.LOGGER.warn("spawn_loot_table_items: world is not a ServerLevel")
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
val lootTableId = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val x = params.getDouble(1)
|
||||
val y = params.getDouble(2)
|
||||
val z = params.getDouble(3)
|
||||
|
||||
val lootTableKey = ResourceKey.create(Registries.LOOT_TABLE, lootTableId)
|
||||
val lootTable = serverLevel.server.reloadableRegistries().getLootTable(lootTableKey)
|
||||
|
||||
val lootParams = LootParams.Builder(serverLevel)
|
||||
.withParameter(LootContextParams.ORIGIN, Vec3(x, y, z))
|
||||
.create(LootContextParamSets.COMMAND)
|
||||
|
||||
val items = lootTable.getRandomItems(lootParams)
|
||||
val spawnedItems = ArrayStruct(hashMapOf())
|
||||
var index = 0
|
||||
|
||||
for (itemStack in items) {
|
||||
if (!itemStack.isEmpty) {
|
||||
val itemEntity = ItemEntity(serverLevel, x, y, z, itemStack)
|
||||
itemEntity.setDefaultPickUpDelay()
|
||||
if (serverLevel.addFreshEntity(itemEntity)) {
|
||||
spawnedItems.setDirectly("$index", itemStack.asMoLangValue(serverLevel.registryAccess()))
|
||||
index++
|
||||
} else {
|
||||
Cobblemon.LOGGER.warn("spawn_loot_table_items: Failed to add entity to world for ${itemStack.item}")
|
||||
}
|
||||
}
|
||||
}
|
||||
return@put spawnedItems
|
||||
}
|
||||
|
||||
return map
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
class BenchedMoves : Iterable<BenchedMove> {
|
||||
var changeFunction: ((BenchedMoves) -> Unit) = {}
|
||||
// var moveAddedFunction: (BenchedMove) -> Unit = {}
|
||||
private var emit = true
|
||||
private val benchedMoves = mutableListOf<BenchedMove>()
|
||||
|
||||
@@ -58,6 +59,9 @@ class BenchedMoves : Iterable<BenchedMove> {
|
||||
return false
|
||||
}
|
||||
|
||||
/*if (emit) {
|
||||
moveAddedFunction(benchedMove)
|
||||
}*/
|
||||
doThenEmit { benchedMoves.add(benchedMove) }
|
||||
return true
|
||||
}
|
||||
@@ -174,4 +178,4 @@ data class BenchedMove(val moveTemplate: MoveTemplate, val ppRaisedStages: Int)
|
||||
Codec.intRange(0, 3).fieldOf(DataKeys.POKEMON_MOVESET_RAISED_PP_STAGES).forGetter(BenchedMove::ppRaisedStages)
|
||||
).apply(it, ::BenchedMove) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.moves
|
||||
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* This is all you need tbh. A moveset builder that picks moves from up to 4 slots of move selectors.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since December 5th, 2025
|
||||
*/
|
||||
class DefaultMovesetBuilder : MovesetBuilder {
|
||||
companion object {
|
||||
val TYPE = cobblemonResource("default")
|
||||
}
|
||||
|
||||
override val type: ResourceLocation = TYPE
|
||||
override var id: ResourceLocation = cobblemonResource("temp") // replaced on load.
|
||||
|
||||
val slot1: List<MoveSelector> = listOf()
|
||||
val slot2: List<MoveSelector> = listOf()
|
||||
val slot3: List<MoveSelector> = listOf()
|
||||
val slot4: List<MoveSelector> = listOf()
|
||||
|
||||
override fun build(form: FormData, level: Int): MoveSet {
|
||||
val chosenMoves = mutableSetOf<MoveTemplate>()
|
||||
tryAddMoveFromSlot(form, level, slot1, chosenMoves)
|
||||
tryAddMoveFromSlot(form, level, slot2, chosenMoves)
|
||||
tryAddMoveFromSlot(form, level, slot3, chosenMoves)
|
||||
tryAddMoveFromSlot(form, level, slot4, chosenMoves)
|
||||
if (chosenMoves.isEmpty()) {
|
||||
chosenMoves.add(Moves.getExceptional())
|
||||
}
|
||||
val moveset = MoveSet()
|
||||
chosenMoves.map(MoveTemplate::create).forEach(moveset::add)
|
||||
return moveset
|
||||
}
|
||||
|
||||
fun tryAddMoveFromSlot(form: FormData, level: Int, selectors: List<MoveSelector>, chosenMoves: MutableSet<MoveTemplate>) {
|
||||
for (selector in selectors) {
|
||||
val move = selector(form, form.moves, level, chosenMoves)
|
||||
if (move != null) {
|
||||
chosenMoves.add(move)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* 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.moves
|
||||
|
||||
import com.cobblemon.mod.common.api.moves.categories.DamageCategories
|
||||
import com.cobblemon.mod.common.api.pokemon.moves.Learnset
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
import com.cobblemon.mod.common.util.weightedSelection
|
||||
|
||||
/**
|
||||
* A function that selects a move from a given [Learnset] for a particular [FormData] at a certain level,
|
||||
* taking into account any moves that have already been chosen for the moveset.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since December 5th, 2025
|
||||
*/
|
||||
fun interface MoveSelector {
|
||||
operator fun invoke(form: FormData, learnset: Learnset, level: Int, chosenMoves: Set<MoveTemplate>): MoveTemplate?
|
||||
|
||||
companion object {
|
||||
val NONE = MoveSelector { _, _, _, _ -> null }
|
||||
val LAST_LEVELUP = MoveSelector { form, learnset, level, chosenMoves ->
|
||||
val levels = learnset.levelUpMoves.keys.filter { it <= level }.sortedDescending()
|
||||
levels.forEach {
|
||||
val moves = (learnset.levelUpMoves[it] ?: return@forEach) - chosenMoves
|
||||
if (moves.isNotEmpty()) {
|
||||
return@MoveSelector moves.weightedSelection { it.getSelectionWeight(form) }
|
||||
}
|
||||
}
|
||||
return@MoveSelector null
|
||||
}
|
||||
val LAST_OFFENSIVE = MoveSelector { form, learnset, level, chosenMoves ->
|
||||
val levels = learnset.levelUpMoves.keys.filter { it <= level }.sortedDescending()
|
||||
levels.forEach {
|
||||
val moves = (learnset.levelUpMoves[it]?.filter { it.damageCategory != DamageCategories.STATUS } ?: return@forEach) - chosenMoves
|
||||
if (moves.isNotEmpty()) {
|
||||
return@MoveSelector moves.weightedSelection { it.getSelectionWeight(form) }
|
||||
}
|
||||
}
|
||||
return@MoveSelector null
|
||||
}
|
||||
val LAST_SUITABLE_OFFENSIVE = MoveSelector { form, learnset, level, chosenMoves ->
|
||||
val levels = learnset.levelUpMoves.keys.filter { it <= level }.sortedDescending()
|
||||
val suitableCategories = if (form.baseStats[Stats.ATTACK]!! > form.baseStats[Stats.SPECIAL_ATTACK]!!) {
|
||||
setOf(DamageCategories.PHYSICAL)
|
||||
} else if (form.baseStats[Stats.SPECIAL_ATTACK]!! > form.baseStats[Stats.ATTACK]!!) {
|
||||
setOf(DamageCategories.SPECIAL)
|
||||
} else {
|
||||
setOf(DamageCategories.PHYSICAL, DamageCategories.SPECIAL)
|
||||
}
|
||||
levels.forEach {
|
||||
val moves = (learnset.levelUpMoves[it]?.filter { it.damageCategory in suitableCategories } ?: return@forEach) - chosenMoves
|
||||
if (moves.isNotEmpty()) {
|
||||
return@MoveSelector moves.weightedSelection { it.getSelectionWeight(form) }
|
||||
}
|
||||
}
|
||||
return@MoveSelector null
|
||||
}
|
||||
val LAST_STATUS = MoveSelector { form, learnset, level, chosenMoves ->
|
||||
val levels = learnset.levelUpMoves.keys.filter { it <= level }.sortedDescending()
|
||||
levels.forEach {
|
||||
val moves = (learnset.levelUpMoves[it]?.filter { it.damageCategory == DamageCategories.STATUS } ?: return@forEach) - chosenMoves
|
||||
if (moves.isNotEmpty()) {
|
||||
return@MoveSelector moves.weightedSelection { it.getSelectionWeight(form) }
|
||||
}
|
||||
}
|
||||
return@MoveSelector null
|
||||
}
|
||||
val STAB = MoveSelector { form, learnset, level, chosenMoves ->
|
||||
val moves = learnset.getLevelUpMovesUpTo(level) - chosenMoves
|
||||
val stabMoves = moves.filter { it.elementalType in form.types && it.damageCategory != DamageCategories.STATUS }
|
||||
return@MoveSelector stabMoves.weightedSelection { it.getSelectionWeight(form) }
|
||||
}
|
||||
val STAB_PHYSICAL = MoveSelector { form, learnset, level, chosenMoves ->
|
||||
val moves = learnset.getLevelUpMovesUpTo(level) - chosenMoves
|
||||
val stabMoves = moves.filter { it.elementalType in form.types && it.damageCategory == DamageCategories.PHYSICAL }
|
||||
return@MoveSelector stabMoves.weightedSelection { it.getSelectionWeight(form) }
|
||||
}
|
||||
val STAB_SPECIAL = MoveSelector { form, learnset, level, chosenMoves ->
|
||||
val moves = learnset.getLevelUpMovesUpTo(level) - chosenMoves
|
||||
val stabMoves = moves.filter { it.elementalType in form.types && it.damageCategory == DamageCategories.SPECIAL }
|
||||
return@MoveSelector stabMoves.weightedSelection { it.getSelectionWeight(form) }
|
||||
}
|
||||
val PHYSICAL = MoveSelector { form, learnset, level, chosenMoves ->
|
||||
val moves = learnset.getLevelUpMovesUpTo(level) - chosenMoves
|
||||
val physicalMoves = moves.filter { it.damageCategory == DamageCategories.PHYSICAL }
|
||||
return@MoveSelector physicalMoves.weightedSelection { it.getSelectionWeight(form) }
|
||||
}
|
||||
val SPECIAL = MoveSelector { form, learnset, level, chosenMoves ->
|
||||
val moves = learnset.getLevelUpMovesUpTo(level) - chosenMoves
|
||||
val specialMoves = moves.filter { it.damageCategory == DamageCategories.SPECIAL }
|
||||
return@MoveSelector specialMoves.weightedSelection { it.getSelectionWeight(form) }
|
||||
}
|
||||
val OFFENSIVE = MoveSelector { form, learnset, level, chosenMoves ->
|
||||
val moves = learnset.getLevelUpMovesUpTo(level) - chosenMoves
|
||||
val offensiveMoves = moves.filter { it.damageCategory != DamageCategories.STATUS }
|
||||
return@MoveSelector offensiveMoves.weightedSelection { it.getSelectionWeight(form) }
|
||||
}
|
||||
val RANDOM_LEVELUP = MoveSelector { form, learnset, level, chosenMoves ->
|
||||
val moves = learnset.getLevelUpMovesUpTo(level) - chosenMoves
|
||||
return@MoveSelector moves.weightedSelection { it.getSelectionWeight(form) }
|
||||
}
|
||||
val STATUS = MoveSelector { form, learnset, level, chosenMoves ->
|
||||
val moves = learnset.getLevelUpMovesUpTo(level) - chosenMoves
|
||||
val statusMoves = moves.filter { it.damageCategory == DamageCategories.STATUS }
|
||||
return@MoveSelector statusMoves.weightedSelection { it.getSelectionWeight(form) }
|
||||
}
|
||||
val TM = MoveSelector { form, learnset, level, chosenMoves ->
|
||||
val moves = learnset.tmMoves - chosenMoves - learnset.getLevelUpMovesUpTo(level)
|
||||
return@MoveSelector moves.weightedSelection { it.getSelectionWeight(form) }
|
||||
}
|
||||
val STAB_TM = MoveSelector { form, learnset, level, chosenMoves ->
|
||||
val moves = learnset.tmMoves - chosenMoves - learnset.getLevelUpMovesUpTo(level)
|
||||
val stabMoves = moves.filter { it.elementalType in form.types }
|
||||
return@MoveSelector stabMoves.weightedSelection { it.getSelectionWeight(form) }
|
||||
}
|
||||
val EGG = MoveSelector { form, learnset, level, chosenMoves ->
|
||||
val moves = learnset.eggMoves - chosenMoves - learnset.getLevelUpMovesUpTo(level)
|
||||
return@MoveSelector moves.weightedSelection { it.getSelectionWeight(form) }
|
||||
}
|
||||
|
||||
val selectors = mutableMapOf(
|
||||
"none" to NONE,
|
||||
"last_levelup" to LAST_LEVELUP,
|
||||
"last_offensive" to LAST_OFFENSIVE,
|
||||
"last_suitable_offensive" to LAST_SUITABLE_OFFENSIVE,
|
||||
"last_status" to LAST_STATUS,
|
||||
"levelup" to RANDOM_LEVELUP,
|
||||
"stab" to STAB,
|
||||
"stab_physical" to STAB_PHYSICAL,
|
||||
"stab_special" to STAB_SPECIAL,
|
||||
"physical" to PHYSICAL,
|
||||
"special" to SPECIAL,
|
||||
"offensive" to OFFENSIVE,
|
||||
"status" to STATUS,
|
||||
"tm" to TM,
|
||||
"stab_tm" to STAB_TM,
|
||||
"egg" to EGG
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,7 @@ class MoveSet : Iterable<Move> {
|
||||
*/
|
||||
fun getMoves() = moves.filterNotNull()
|
||||
fun getMovesWithNulls() = moves.toList()
|
||||
fun getMoveTemplates() = getMoves().map { move -> move.template }
|
||||
fun hasSpace() = moves.any { it == null }
|
||||
|
||||
/**
|
||||
@@ -222,4 +223,4 @@ class MoveSet : Iterable<Move> {
|
||||
{ moveSet -> moveSet.toList() }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,12 +12,12 @@ import com.bedrockk.molang.runtime.struct.MoStruct
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectTimeline
|
||||
import com.cobblemon.mod.common.api.moves.categories.DamageCategories
|
||||
import com.cobblemon.mod.common.api.moves.categories.DamageCategory
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.battles.MoveTarget
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
import com.cobblemon.mod.common.util.codec.CodecUtils
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
@@ -53,7 +53,8 @@ open class MoveTemplate(
|
||||
val pp: Int,
|
||||
val priority: Int,
|
||||
val critRatio: Double,
|
||||
val effectChances: Array<Double>
|
||||
val effectChances: Array<Double>,
|
||||
val weight: Float
|
||||
) {
|
||||
val struct: MoStruct by lazy {
|
||||
QueryStruct(hashMapOf())
|
||||
@@ -69,9 +70,9 @@ open class MoveTemplate(
|
||||
.addFunction("max_pp") { DoubleValue(maxPp) }
|
||||
.addFunction("priority") { DoubleValue(priority) }
|
||||
.addFunction("crit_ratio") { DoubleValue(critRatio) }
|
||||
.addFunction("weight") { DoubleValue(weight.toDouble()) }
|
||||
}
|
||||
|
||||
|
||||
val displayName: MutableComponent
|
||||
get() = lang("move.$name")
|
||||
val description: MutableComponent
|
||||
@@ -89,7 +90,8 @@ open class MoveTemplate(
|
||||
pp = 5,
|
||||
priority = 0,
|
||||
critRatio = 0.0,
|
||||
effectChances = emptyArray()
|
||||
effectChances = emptyArray(),
|
||||
weight = 0F,
|
||||
)
|
||||
|
||||
companion object {
|
||||
@@ -160,4 +162,12 @@ open class MoveTemplate(
|
||||
}
|
||||
return this.elementalType
|
||||
}
|
||||
|
||||
fun getSelectionWeight(form: FormData): Float {
|
||||
return if (this in form.signatureMoves) {
|
||||
weight * MovesetBuilder.signatureMoveWeightMultiplier
|
||||
} else {
|
||||
weight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,15 +10,15 @@ package com.cobblemon.mod.common.api.moves
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.data.DataRegistry
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
|
||||
import com.cobblemon.mod.common.api.moves.categories.DamageCategories
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.battles.MoveTarget
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.net.messages.client.data.MovesRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.fromJson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
@@ -46,6 +46,14 @@ object Moves : DataRegistry {
|
||||
this.idMapping.clear()
|
||||
this.moveScripts.clear()
|
||||
|
||||
val weights = mutableMapOf<String, Number>()
|
||||
manager.getResourceStack(cobblemonResource("move_weights.json")).forEach {
|
||||
it.open().use {
|
||||
val map = GsonBuilder().create().fromJson<Map<String, Number>>(it.reader())
|
||||
weights.putAll(map)
|
||||
}
|
||||
}
|
||||
|
||||
ShowdownService.service.resetRegistryData("move")
|
||||
manager.listResources("moves") { it.path.endsWith(".js") }.forEach { (identifier, resource) ->
|
||||
resource.open().use { stream ->
|
||||
@@ -75,6 +83,7 @@ object Moves : DataRegistry {
|
||||
val priority = jsMove.get("priority").asInt
|
||||
val critRatio = jsMove.get("critRatio")?.asDouble ?: 1.0
|
||||
val effectChances = arrayListOf<Double>()
|
||||
val weight = weights[id]?.toFloat() ?: 50F
|
||||
val secondariesMember = jsMove.get("secondaries")
|
||||
val secondaryMember = jsMove.get("secondary")
|
||||
if (secondariesMember != null && secondariesMember is JsonArray) {
|
||||
@@ -92,7 +101,20 @@ object Moves : DataRegistry {
|
||||
effectChances += secondaryMember.get("chance").asDouble
|
||||
}
|
||||
}
|
||||
val move = MoveTemplate(id, num, elementalType, damageCategory, power, target, accuracy, pp, priority, critRatio, effectChances.toTypedArray())
|
||||
val move = MoveTemplate(
|
||||
name = id,
|
||||
num = num,
|
||||
elementalType = elementalType,
|
||||
damageCategory = damageCategory,
|
||||
power = power,
|
||||
target = target,
|
||||
accuracy = accuracy,
|
||||
pp = pp,
|
||||
priority = priority,
|
||||
critRatio = critRatio,
|
||||
effectChances = effectChances.toTypedArray(),
|
||||
weight = weight
|
||||
)
|
||||
this.register(move)
|
||||
} catch (e: Exception) {
|
||||
Cobblemon.LOGGER.error("Caught exception trying to resolve the move '{}'", id, e)
|
||||
@@ -113,7 +135,7 @@ object Moves : DataRegistry {
|
||||
@JvmStatic
|
||||
fun getByNameOrDummy(name: String) = allMoves[name.lowercase()] ?: MoveTemplate.dummy(name.lowercase())
|
||||
@JvmStatic
|
||||
fun getExceptional() = getByName("tackle") ?: allMoves.values.random()
|
||||
fun getExceptional() = getByName("tackle") ?: allMoves.values.first()
|
||||
@JvmStatic
|
||||
fun count() = allMoves.size
|
||||
@JvmStatic
|
||||
@@ -129,5 +151,4 @@ object Moves : DataRegistry {
|
||||
this.allMoves[move.name] = move
|
||||
this.idMapping[move.num] = move
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.moves
|
||||
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* Builders a moveset for a particular [FormData] and level. Probably only really need [DefaultMovesetBuilder] though.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since December 5th, 2025
|
||||
*/
|
||||
interface MovesetBuilder {
|
||||
/** The ID of this builder. It's autofilled from the registry loader. */
|
||||
var id: ResourceLocation
|
||||
/** The subtype designation for this moveset builder. Generally unused once read from JSON. */
|
||||
val type: ResourceLocation
|
||||
|
||||
/** Builds a brand new moveset for the [form] and [level]. */
|
||||
fun build(form: FormData, level: Int): MoveSet
|
||||
|
||||
companion object {
|
||||
/** Weight multiplier for moves that are in the form's signatureMoves list. Makes them way more likely to be chosen. */
|
||||
var signatureMoveWeightMultiplier = 5F
|
||||
/** Subtypes of MovesetBuilder. I personally doubt you'll need any other subtypes, but hey. */
|
||||
val movesetBuilderTypes = mutableMapOf<ResourceLocation, Class<out MovesetBuilder>>(
|
||||
cobblemonResource("default") to DefaultMovesetBuilder::class.java
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
package com.cobblemon.mod.common.api.net
|
||||
|
||||
/**
|
||||
* This marks a packet as "unsplitable". This is used so neoforge doesnt destroy our buffers when encoding/decoding
|
||||
* This marks a packet as "unsplittable". This is used so neoforge doesnt destroy our buffers when encoding/decoding
|
||||
* PokemonDTOs (We allocate buffers, and it kinda breaks when the same instance of PokemonDTO is encoded more than once
|
||||
*
|
||||
* Technically, even if Neo didnt encode the same packet twice, we wouldnt want to split PokemonDTOs.
|
||||
|
||||
@@ -61,7 +61,7 @@ object NPCClasses : JsonDataRegistry<NPCClass> {
|
||||
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
|
||||
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.registerTypeAdapter(TimeRange::class.java, IntRangesAdapter(TimeRange.timeRanges) { TimeRange(*it) })
|
||||
.registerTypeAdapter(TimeRange::class.java, TimeRange.adapter)
|
||||
.registerTypeAdapter(ItemDropMethod::class.java, ItemDropMethod.adapter)
|
||||
.registerTypeAdapter(SleepDepth::class.java, SleepDepth.adapter)
|
||||
.registerTypeAdapter(DropEntry::class.java, DropEntryAdapter)
|
||||
|
||||
@@ -8,12 +8,14 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.npc
|
||||
|
||||
import com.cobblemon.mod.common.api.npc.partyproviders.ComposedPoolPartyProvider
|
||||
import com.cobblemon.mod.common.api.npc.partyproviders.PoolPartyProvider
|
||||
import com.cobblemon.mod.common.api.npc.partyproviders.ScriptPartyProvider
|
||||
import com.cobblemon.mod.common.api.npc.partyproviders.SimplePartyProvider
|
||||
import com.cobblemon.mod.common.api.storage.party.NPCPartyStore
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.google.gson.JsonElement
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
/**
|
||||
@@ -25,14 +27,15 @@ import net.minecraft.server.level.ServerPlayer
|
||||
*/
|
||||
interface NPCPartyProvider {
|
||||
companion object {
|
||||
val types = mutableMapOf<String, (String) -> NPCPartyProvider>(
|
||||
SimplePartyProvider.TYPE to { SimplePartyProvider() },
|
||||
PoolPartyProvider.TYPE to { PoolPartyProvider() },
|
||||
ScriptPartyProvider.TYPE to { ScriptPartyProvider() }
|
||||
val types = mutableMapOf(
|
||||
SimplePartyProvider.TYPE to ::SimplePartyProvider,
|
||||
PoolPartyProvider.TYPE to ::PoolPartyProvider,
|
||||
ScriptPartyProvider.TYPE to ::ScriptPartyProvider,
|
||||
ComposedPoolPartyProvider.TYPE to ::ComposedPoolPartyProvider
|
||||
)
|
||||
}
|
||||
|
||||
val type: String
|
||||
val type: ResourceLocation
|
||||
val isStatic: Boolean
|
||||
fun provide(npc: NPCEntity, level: Int, players: List<ServerPlayer> = emptyList()): NPCPartyStore
|
||||
// Why did I opt for manual JSON loading??? I must have had a reason but I can't remember. Maybe for S2C? Use a codec doofus
|
||||
|
||||
@@ -57,7 +57,7 @@ object NPCPresets : JsonDataRegistry<NPCPreset> {
|
||||
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
|
||||
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.registerTypeAdapter(TimeRange::class.java, IntRangesAdapter(TimeRange.timeRanges) { TimeRange(*it) })
|
||||
.registerTypeAdapter(TimeRange::class.java, TimeRange.adapter)
|
||||
.registerTypeAdapter(ItemDropMethod::class.java, ItemDropMethod.adapter)
|
||||
.registerTypeAdapter(SleepDepth::class.java, SleepDepth.adapter)
|
||||
.registerTypeAdapter(DropEntry::class.java, DropEntryAdapter)
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.npc
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
* A kind of definition for how to compose a party of Pokémon given a [PartyPool].
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since December 7th, 2025
|
||||
*/
|
||||
class PartyComposition {
|
||||
var id = cobblemonResource("dummy_composition")
|
||||
// val displayName = "cobblemon.party_composition.$id" // dunno if i will use this yet
|
||||
val scrambleOrder: Boolean = false
|
||||
val slot1 = listOf<String>()
|
||||
val slot2 = listOf<String>()
|
||||
val slot3 = listOf<String>()
|
||||
val slot4 = listOf<String>()
|
||||
val slot5 = listOf<String>()
|
||||
val slot6 = listOf<String>()
|
||||
|
||||
fun compose(pool: PartyPool, level: Int, aspects: Set<String>, desiredPokemonCount: Int, runtime: MoLangRuntime, random: Random = Random.Default): List<Pokemon> {
|
||||
val chosenEntries = mutableListOf<PartyPool.PoolEntry>()
|
||||
val availableEntries = pool.entries.filter { level in it.npcLevels.resolve(runtime) && (it.npcAspects.isEmpty() || it.npcAspects.all(aspects::contains)) }
|
||||
|
||||
for (labelSet in listOf(slot1, slot2, slot3, slot4, slot5, slot6)) {
|
||||
pool.tryChoosingEntry(labelSet, runtime, random, availableEntries, chosenEntries)
|
||||
if (chosenEntries.size >= desiredPokemonCount) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
val finalEntries = if (scrambleOrder) {
|
||||
chosenEntries.shuffled(random)
|
||||
} else {
|
||||
chosenEntries
|
||||
}
|
||||
|
||||
val pokemon = finalEntries.map { entry ->
|
||||
val levelVariation = entry.levelVariation.resolve(runtime)
|
||||
val lvlVariation = if (levelVariation.first == levelVariation.last) {
|
||||
levelVariation.first
|
||||
} else {
|
||||
levelVariation.random(random)
|
||||
}
|
||||
|
||||
val finalLevel = level + lvlVariation
|
||||
val properties = entry.pokemon.copy()
|
||||
properties.level = properties.level ?: finalLevel
|
||||
val pokemon = properties.create()
|
||||
entry.movesetBuilders.randomOrNull()?.let(pokemon::initializeMovesetFrom)
|
||||
pokemon
|
||||
}
|
||||
|
||||
return pokemon
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.npc
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.moves.MovesetBuilder
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.util.ScriptableIntRange
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.toProperties
|
||||
import com.cobblemon.mod.common.util.weightedSelection
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
* A pool of Pokémon to choose from, typically for generating a party for an NPC. The entries
|
||||
* of the pool work on a weight and label system which is used as part of a [PartyComposition].
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since December 7th, 2025
|
||||
*/
|
||||
class PartyPool {
|
||||
var id = cobblemonResource("dummy")
|
||||
val displayName = "cobblemon.party_pool.$id"
|
||||
val entries = mutableListOf<PoolEntry>()
|
||||
|
||||
class PoolEntry {
|
||||
val pokemon: PokemonProperties = "pikachu".toProperties()
|
||||
val labels: List<String> = emptyList()
|
||||
val npcAspects: List<String> = emptyList()
|
||||
val required: List<String> = emptyList()
|
||||
val excluded: List<String> = emptyList()
|
||||
val maxSelectableTimes: ExpressionLike = "6".asExpressionLike()
|
||||
val levelVariation: ScriptableIntRange = ScriptableIntRange(0, 0)
|
||||
val npcLevels: ScriptableIntRange = ScriptableIntRange(1, 100)
|
||||
val movesetBuilders: List<MovesetBuilder> = emptyList()
|
||||
val weight: ExpressionLike = "50".asExpressionLike()
|
||||
}
|
||||
|
||||
/**
|
||||
* Goes through each of the labels in order, trying to find a valid choice. Once it finds one, it skips the later
|
||||
* labels and adds the value to [chosenEntries] and returns. If none of the labels could find a valid entry, nothing is added.
|
||||
*/
|
||||
fun tryChoosingEntry(
|
||||
labels: List<String>,
|
||||
runtime: MoLangRuntime,
|
||||
random: Random,
|
||||
availableEntries: List<PoolEntry>,
|
||||
chosenEntries: MutableList<PoolEntry>,
|
||||
) {
|
||||
for (label in labels) {
|
||||
val entries = availableEntries
|
||||
.filter { label in it.labels || label.lowercase() == "any" }
|
||||
.filter { entry ->
|
||||
// Check required labels
|
||||
entry.required.all { it in labels } &&
|
||||
// Check excluded labels
|
||||
entry.excluded.none { it in labels } &&
|
||||
// Check max selectable times
|
||||
chosenEntries.count { it == entry } < entry.maxSelectableTimes.resolveFloat(runtime)
|
||||
}
|
||||
|
||||
if (entries.isNotEmpty()) {
|
||||
val selectedEntry = entries.weightedSelection(random) { it.weight.resolveFloat(runtime) } ?: return
|
||||
chosenEntries.add(selectedEntry)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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.npc.partyproviders
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.CobblemonPartyCompositions
|
||||
import com.cobblemon.mod.common.CobblemonPartyPools
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
import com.cobblemon.mod.common.api.npc.NPCPartyProvider
|
||||
import com.cobblemon.mod.common.api.npc.PartyComposition
|
||||
import com.cobblemon.mod.common.api.npc.PartyPool
|
||||
import com.cobblemon.mod.common.api.storage.party.NPCPartyStore
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.util.asArrayValue
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.resolveInt
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import kotlin.random.Random
|
||||
import kotlin.ranges.random
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
/**
|
||||
* An [NPCPartyProvider] which uses a [PartyComposition] and [PartyPool] to generate a party.
|
||||
*
|
||||
* There are controls for minimum and maximum Pokémon count, as well as whether to use a fixed random seed for the NPC.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since December 8th, 2025
|
||||
*/
|
||||
class ComposedPoolPartyProvider : NPCPartyProvider {
|
||||
companion object {
|
||||
val TYPE = cobblemonResource("composed_pool")
|
||||
}
|
||||
|
||||
override val type = TYPE
|
||||
override var isStatic: Boolean = true
|
||||
var useFixedRandom: Boolean = false
|
||||
var minPokemon: Expression = "1".asExpression()
|
||||
var maxPokemon: Expression = "6".asExpression()
|
||||
lateinit var pool: PartyPool
|
||||
lateinit var composition: PartyComposition
|
||||
|
||||
override fun loadFromJSON(json: JsonElement) {
|
||||
json as JsonObject
|
||||
isStatic = json.getAsJsonPrimitive("isStatic")?.asBoolean ?: true
|
||||
useFixedRandom = json.getAsJsonPrimitive("useFixedRandom")?.asBoolean ?: false
|
||||
pool = CobblemonPartyPools.partyPools[json.get("pool").asString.asIdentifierDefaultingNamespace()]
|
||||
?: throw IllegalArgumentException("Unknown PartyPool id: ${json.get("pool").asString}")
|
||||
composition = CobblemonPartyCompositions.partyCompositions[json.get("composition").asString.asIdentifierDefaultingNamespace()]
|
||||
?: throw IllegalArgumentException("Unknown PartyComposition id: ${json.get("composition").asString}")
|
||||
minPokemon = json.getAsJsonPrimitive("minPokemon").asString?.asExpression() ?: "1".asExpression()
|
||||
maxPokemon = json.getAsJsonPrimitive("maxPokemon").asString?.asExpression() ?: "6".asExpression()
|
||||
}
|
||||
|
||||
override fun provide(npc: NPCEntity, level: Int, players: List<ServerPlayer>): NPCPartyStore {
|
||||
val runtime = MoLangRuntime().setup().withQueryValue("npc", npc.struct)
|
||||
val random = if (useFixedRandom) Random(npc.uuid.hashCode()) else Random.Default
|
||||
runtime.withQueryValue("level", DoubleValue(level))
|
||||
runtime.withQueryValue("players", players.asArrayValue { it.asMoLangValue() })
|
||||
if (players.size == 1) {
|
||||
// This is for the convenience, most cases will be pvn one player
|
||||
runtime.withQueryValue("player", players.first().asMoLangValue())
|
||||
}
|
||||
val minPokemon = runtime.resolveInt(this.minPokemon)
|
||||
val maxPokemon = runtime.resolveInt(this.maxPokemon)
|
||||
val desiredPokemonCount = (minPokemon..maxPokemon).random(random)
|
||||
|
||||
val pokemonList = composition.compose(
|
||||
pool = pool,
|
||||
level = level,
|
||||
aspects = npc.aspects,
|
||||
desiredPokemonCount = desiredPokemonCount,
|
||||
runtime = runtime,
|
||||
random = random
|
||||
)
|
||||
|
||||
val partyStore = NPCPartyStore(npc)
|
||||
pokemonList.forEach(partyStore::add)
|
||||
return partyStore
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user