mirror of
https://gitlab.com/cable-mc/cobblemon.git
synced 2026-10-21 14:46:25 -04:00
Compare commits
623
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e3302bdec | ||
|
|
f77af7c954 | ||
|
|
63ed488b03 | ||
|
|
fcce15e210 | ||
|
|
d816710566 | ||
|
|
c8a8e689cc | ||
|
|
222f6dd955 | ||
|
|
f61efb11b1 | ||
|
|
19b4d841f4 | ||
|
|
704320f1bc | ||
|
|
e5480bd05b | ||
|
|
8d504d23e1 | ||
|
|
2912ffdad9 | ||
|
|
986f22c5a5 | ||
|
|
880f6de77a | ||
|
|
7f87b0f184 | ||
|
|
a811a823e6 | ||
|
|
a45999894b | ||
|
|
eeba80e7e7 | ||
|
|
97b20158e9 | ||
|
|
586eaa9135 | ||
|
|
dc7925ab3e | ||
|
|
8986159a25 | ||
|
|
b864f715ea | ||
|
|
02b7d5eeae | ||
|
|
614be610d4 | ||
|
|
1084c7a289 | ||
|
|
110d6dc828 | ||
|
|
d98d68c7d4 | ||
|
|
6369d876e3 | ||
|
|
8388a53017 | ||
|
|
3a59d7d7c9 | ||
|
|
3d9d3318b1 | ||
|
|
d032e0449b | ||
|
|
bcdbb19a75 | ||
|
|
2181cdb4ef | ||
|
|
24257a4c6e | ||
|
|
2a293def4f | ||
|
|
a3d77dd2a0 | ||
|
|
311f0e3192 | ||
|
|
be2099130b | ||
|
|
6ebe0b9c7e | ||
|
|
dde5c00789 | ||
|
|
d980faed64 | ||
|
|
7a80bc54ea | ||
|
|
f207206b13 | ||
|
|
edf510d8e8 | ||
|
|
721384547f | ||
|
|
ce5189e83a | ||
|
|
d7fc09b967 | ||
|
|
703eb7645c | ||
|
|
604c184029 | ||
|
|
727ada3e00 | ||
|
|
d29e471ab4 | ||
|
|
6c1e6677b3 | ||
|
|
aaa7fcee47 | ||
|
|
37b050f2e5 | ||
|
|
16ccd6bcc3 | ||
|
|
8b0d228339 | ||
|
|
5019026472 | ||
|
|
ace38ad6be | ||
|
|
5c885112ef | ||
|
|
a37532579e | ||
|
|
0f751eb732 | ||
|
|
9840a2758a | ||
|
|
ad52d1ed5f | ||
|
|
74464dc13a | ||
|
|
f280ec8979 | ||
|
|
25ca0fbd13 | ||
|
|
2c8d544088 | ||
|
|
7dbe31d084 | ||
|
|
de05133b31 | ||
|
|
3b70b8351a | ||
|
|
784439475a | ||
|
|
2024ddb4f0 | ||
|
|
ad35be7ddb | ||
|
|
dbf688fade | ||
|
|
17d3d5f829 | ||
|
|
fba00c23b4 | ||
|
|
cfe516e086 | ||
|
|
63d921cb12 | ||
|
|
6acde8c90f | ||
|
|
196ce2ab6d | ||
|
|
6fb6ea2927 | ||
|
|
538027cb1b | ||
|
|
51fcc91d88 | ||
|
|
63c603cc01 | ||
|
|
a7406dd321 | ||
|
|
0e1611fd8e | ||
|
|
51e575c6a6 | ||
|
|
8789d16a53 | ||
|
|
f7986d5ccf | ||
|
|
c7423408a9 | ||
|
|
52b0c63751 | ||
|
|
7ae20ccd60 | ||
|
|
621b867d14 | ||
|
|
b0ea22d220 | ||
|
|
dfb54b3bc3 | ||
|
|
2928667500 | ||
|
|
1c528a909c | ||
|
|
1083e770d0 | ||
|
|
920f3f114b | ||
|
|
9ca5d12f84 | ||
|
|
cc9bb528a8 | ||
|
|
1cbd00691f | ||
|
|
f4c847fdd5 | ||
|
|
ec6e1403e7 | ||
|
|
10a4a03a8b | ||
|
|
e0a86f8794 | ||
|
|
767e024ec4 | ||
|
|
c8b0f36049 | ||
|
|
be2a0be806 | ||
|
|
ba4d42ccf6 | ||
|
|
5545d3498f | ||
|
|
641bd7ebaa | ||
|
|
234c4fa0d4 | ||
|
|
3dfda71ed5 | ||
|
|
65ee6de93f | ||
|
|
be88b727bc | ||
|
|
a69fc462ee | ||
|
|
7d5c30d27c | ||
|
|
f7c9c9d4f4 | ||
|
|
41bc14168a | ||
|
|
9540c38a72 | ||
|
|
7a96ed7cd1 | ||
|
|
677adc8d2f | ||
|
|
a99b1dfc6c | ||
|
|
4a718ad0f6 | ||
|
|
ddd417d458 | ||
|
|
10a12a897e | ||
|
|
9fce99ed73 | ||
|
|
c35f60dd5d | ||
|
|
54e5a5e819 | ||
|
|
79ae35ea9e | ||
|
|
58f85e6d0d | ||
|
|
71bfdf41f4 | ||
|
|
9976965673 | ||
|
|
098df1a5a6 | ||
|
|
f49f34efa3 | ||
|
|
eac19929c4 | ||
|
|
59cced5b2c | ||
|
|
f07707221f | ||
|
|
89b89592ab | ||
|
|
cdeb977769 | ||
|
|
78192c43a7 | ||
|
|
a3987b433c | ||
|
|
99de91bd7e | ||
|
|
4b292b1241 | ||
|
|
1e6e4d8c5f | ||
|
|
9a61c3471b | ||
|
|
77f7439853 | ||
|
|
439161c180 | ||
|
|
e9718b7e8b | ||
|
|
4cadd4e20f | ||
|
|
17a3d86324 | ||
|
|
48653073f5 | ||
|
|
82c7c03e28 | ||
|
|
2d7f3257d1 | ||
|
|
bc7b4f94c2 | ||
|
|
7a9e9d7ced | ||
|
|
0f85ece16f | ||
|
|
c4656e1a4e | ||
|
|
0c48a318c5 | ||
|
|
dab89a9f8c | ||
|
|
53fd2dba2d | ||
|
|
a5b15c6127 | ||
|
|
174fb59bbb | ||
|
|
2f8bd44938 | ||
|
|
9d58c7126e | ||
|
|
2afd722125 | ||
|
|
c9cfe489d6 | ||
|
|
4ee029fa54 | ||
|
|
e2ebb32708 | ||
|
|
5b0a221ade | ||
|
|
af23b82ce5 | ||
|
|
b0466447cc | ||
|
|
727ff96ff8 | ||
|
|
9307f957b7 | ||
|
|
d48978f6a4 | ||
|
|
d1a8171079 | ||
|
|
d4b673ea38 | ||
|
|
36e78a5af2 | ||
|
|
55621b01b7 | ||
|
|
b828fe2b3a | ||
|
|
b7b10151e1 | ||
|
|
de21b7c402 | ||
|
|
10fe3c4362 | ||
|
|
aca1433595 | ||
|
|
082c382179 | ||
|
|
cac6ed843a | ||
|
|
8f5f40c360 | ||
|
|
0e586b4f38 | ||
|
|
62dd487448 | ||
|
|
04ecd9189c | ||
|
|
55be22e942 | ||
|
|
f2e1b3dab4 | ||
|
|
66f98ebf12 | ||
|
|
c1842e42b0 | ||
|
|
e6c2f241d4 | ||
|
|
c2d85a0cdf | ||
|
|
0f9c282e1f | ||
|
|
7bd5d77b60 | ||
|
|
f7b4d4e65a | ||
|
|
e78f403198 | ||
|
|
4ca91cc8d8 | ||
|
|
1518181f6b | ||
|
|
b915237c8d | ||
|
|
bb41a72067 | ||
|
|
fbb8492c75 | ||
|
|
c07aef04b1 | ||
|
|
6ff4befb04 | ||
|
|
a858d4f532 | ||
|
|
28fc273b26 | ||
|
|
fc89757a09 | ||
|
|
243c9abfd1 | ||
|
|
7cea5ad69e | ||
|
|
0b9b5ac306 | ||
|
|
8941f3079b | ||
|
|
e9a0815ca6 | ||
|
|
b871645e9e | ||
|
|
cec0ee04e0 | ||
|
|
069bc91aa1 | ||
|
|
692a1bd6ba | ||
|
|
c79916d8a0 | ||
|
|
d503d4a1eb | ||
|
|
3d877496b3 | ||
|
|
1e4964d80c | ||
|
|
a0f75dd090 | ||
|
|
5eceaa849c | ||
|
|
0e0233993a | ||
|
|
56ed5a6412 | ||
|
|
92ecaa66fa | ||
|
|
4d3e6400ab | ||
|
|
f2e5ecef66 | ||
|
|
ad9e85923a | ||
|
|
60d0c490dd | ||
|
|
bf461297b2 | ||
|
|
3ec89cd84e | ||
|
|
6452521434 | ||
|
|
5cbf961ac2 | ||
|
|
6b473cffa3 | ||
|
|
f353548f9e | ||
|
|
bba995f3d2 | ||
|
|
5965d5a81b | ||
|
|
4e02ca310d | ||
|
|
3260271d3c | ||
|
|
a77863adab | ||
|
|
455cfeb894 | ||
|
|
46266c08ac | ||
|
|
19aa45e0af | ||
|
|
0fe8ebd008 | ||
|
|
3e393546a2 | ||
|
|
e5995a5491 | ||
|
|
7de083bf4f | ||
|
|
5f7451c284 | ||
|
|
fd3c614c2c | ||
|
|
fe48da6277 | ||
|
|
12c9c1c295 | ||
|
|
454c2c60ab | ||
|
|
c581673ef1 | ||
|
|
09e29137fc | ||
|
|
fd1e5ef421 | ||
|
|
8de05006f4 | ||
|
|
7188978a66 | ||
|
|
f60fd26cbf | ||
|
|
3ba59d2165 | ||
|
|
cd80598857 | ||
|
|
0978febaba | ||
|
|
bb67629fa5 | ||
|
|
d21039953f | ||
|
|
682f931f0f | ||
|
|
3796997a12 | ||
|
|
b4cf49e1a8 | ||
|
|
2820989586 | ||
|
|
d37f6bc5a4 | ||
|
|
90d7fc3c2f | ||
|
|
e2779c55f2 | ||
|
|
349b4c28cc | ||
|
|
7f2540a48f | ||
|
|
367db9a368 | ||
|
|
7471b336be | ||
|
|
4f3eb7e4ba | ||
|
|
c95f4cb9ad | ||
|
|
c0ccefd9b4 | ||
|
|
f45a1681fa | ||
|
|
12c820313a | ||
|
|
e6b287434b | ||
|
|
d39ee60158 | ||
|
|
d405ede256 | ||
|
|
bbaf4332c6 | ||
|
|
954e690a20 | ||
|
|
0dda070c4c | ||
|
|
e855e0f1ae | ||
|
|
0515b3204b | ||
|
|
049c0c308a | ||
|
|
e424bd76b5 | ||
|
|
0443aa5109 | ||
|
|
f9a3d2dc0e | ||
|
|
f817c18f4f | ||
|
|
bd801b1517 | ||
|
|
0f17d30ece | ||
|
|
f54aed0953 | ||
|
|
2b5a16d30f | ||
|
|
f1ed9b010e | ||
|
|
e8dcd994be | ||
|
|
da371fd940 | ||
|
|
0cdeab0800 | ||
|
|
eca3c582aa | ||
|
|
8dd09a2e14 | ||
|
|
a96880b8ed | ||
|
|
40de8b3ab9 | ||
|
|
6eb814a7f0 | ||
|
|
2f2fd3b17e | ||
|
|
4234f9be67 | ||
|
|
f5766cdad4 | ||
|
|
8c11ef9c00 | ||
|
|
78ac8542f6 | ||
|
|
8c1d03359a | ||
|
|
bb574e58d8 | ||
|
|
5c3c7b4195 | ||
|
|
ddec062e87 | ||
|
|
3322f3ffb6 | ||
|
|
ab1ec11c26 | ||
|
|
36aaa97029 | ||
|
|
6b3443fbd2 | ||
|
|
268a1ab311 | ||
|
|
40957b6eac | ||
|
|
d67be68b4b | ||
|
|
f6f34a2b10 | ||
|
|
b2f71d305f | ||
|
|
51a8c31997 | ||
|
|
67ae5ff155 | ||
|
|
9e0cf03d8e | ||
|
|
6a076d36f9 | ||
|
|
4314a0b506 | ||
|
|
77490b5084 | ||
|
|
109eecd4fe | ||
|
|
d6353aa2c7 | ||
|
|
fd0e741a25 | ||
|
|
b0e0a06e9a | ||
|
|
1c569ac5cf | ||
|
|
5ff5de8d14 | ||
|
|
881e1dc1a6 | ||
|
|
09b9fade87 | ||
|
|
acd3b7cade | ||
|
|
482a0015bc | ||
|
|
b881c96630 | ||
|
|
79cd1f5224 | ||
|
|
f369877c10 | ||
|
|
1f1ff0e4fd | ||
|
|
0a34e51e07 | ||
|
|
741530eb43 | ||
|
|
6c9c5a93f1 | ||
|
|
ddbf6d7345 | ||
|
|
0da09c767c | ||
|
|
fb39ff8657 | ||
|
|
dd516c153e | ||
|
|
739d31a9ca | ||
|
|
a5675aedcb | ||
|
|
196bf42fb6 | ||
|
|
1d01a2bf1f | ||
|
|
948f299f8f | ||
|
|
ce6087f368 | ||
|
|
f706dcda26 | ||
|
|
8e46a55cbd | ||
|
|
d3ae4cd3e7 | ||
|
|
b41b496b80 | ||
|
|
9cd3ded705 | ||
|
|
ca4b50d727 | ||
|
|
f6bc3f662a | ||
|
|
7ab621e5b9 | ||
|
|
4e63cb0bb3 | ||
|
|
671c7adb05 | ||
|
|
edbe539537 | ||
|
|
e20728faa0 | ||
|
|
5a2f948acb | ||
|
|
8a1faf3270 | ||
|
|
d0b148a91a | ||
|
|
f6f88f5eef | ||
|
|
3f36624ba7 | ||
|
|
7e256e8381 | ||
|
|
ad1aa9ad47 | ||
|
|
b4e403a701 | ||
|
|
08557913d8 | ||
|
|
830c409a06 | ||
|
|
5e2ef6b5fb | ||
|
|
a05f4edc82 | ||
|
|
97a3dc4d61 | ||
|
|
768789d998 | ||
|
|
bfa58e9fb9 | ||
|
|
9a514477c5 | ||
|
|
7623452f4a | ||
|
|
72635d0f2f | ||
|
|
34e581a470 | ||
|
|
5444d67c21 | ||
|
|
4c8c0fd063 | ||
|
|
73209ab6b5 | ||
|
|
539331bdc7 | ||
|
|
0d0598d68c | ||
|
|
08d621ca5e | ||
|
|
a1dcede488 | ||
|
|
aaec9ea397 | ||
|
|
6dfb1323b7 | ||
|
|
973e730b48 | ||
|
|
5011607415 | ||
|
|
580b0f2ccf | ||
|
|
fcd3951815 | ||
|
|
5caa6d5950 | ||
|
|
352ac4e15d | ||
|
|
e6d35e709e | ||
|
|
83cfcdce43 | ||
|
|
96ae28ea91 | ||
|
|
062fd2451a | ||
|
|
235de329ee | ||
|
|
aede012933 | ||
|
|
1613cd281d | ||
|
|
706fcc8ca4 | ||
|
|
5568bb6a95 | ||
|
|
d5f22e7394 | ||
|
|
0dc8456623 | ||
|
|
f5228db99f | ||
|
|
5c075d7d29 | ||
|
|
de8ebc9fcb | ||
|
|
093e0f2811 | ||
|
|
1a782e4185 | ||
|
|
2e054dd5e1 | ||
|
|
5e47102c10 | ||
|
|
e56a7d5325 | ||
|
|
9b8261d23b | ||
|
|
e1705056f2 | ||
|
|
76db87d9a5 | ||
|
|
bbb06677b9 | ||
|
|
8a5279741d | ||
|
|
780d9ece59 | ||
|
|
fc8b6fb54b | ||
|
|
854f02d34f | ||
|
|
a3c3fa12e5 | ||
|
|
3527da02a6 | ||
|
|
96f537fa27 | ||
|
|
9e22b662a4 | ||
|
|
d731bde915 | ||
|
|
6ecdf25821 | ||
|
|
4b55783785 | ||
|
|
77c3f12634 | ||
|
|
d13d3fb137 | ||
|
|
f436b4a738 | ||
|
|
9e1a1c957b | ||
|
|
e04155094b | ||
|
|
188115bf15 | ||
|
|
e3fca67d52 | ||
|
|
932cdaaad3 | ||
|
|
e3f9937e78 | ||
|
|
5b46d04c27 | ||
|
|
c23aeaba78 | ||
|
|
ecff621084 | ||
|
|
1d99037005 | ||
|
|
83ce63325d | ||
|
|
4f33612bbb | ||
|
|
4624faf63e | ||
|
|
187674afb6 | ||
|
|
fe5cf5a7d8 | ||
|
|
3b3219ba69 | ||
|
|
0e98b07b7b | ||
|
|
57d380e18f | ||
|
|
086cdcdbd1 | ||
|
|
2b796f7e4c | ||
|
|
b7d1f9bb2d | ||
|
|
ba0f0657b2 | ||
|
|
8f59ec871f | ||
|
|
b3d4306165 | ||
|
|
20ddab8722 | ||
|
|
5327ff54b5 | ||
|
|
b5f8181118 | ||
|
|
64b164bfca | ||
|
|
93cb0f40f3 | ||
|
|
20a56b1ff0 | ||
|
|
97dd08c35d | ||
|
|
25c64841fe | ||
|
|
dc5f616c90 | ||
|
|
ff9033f6c4 | ||
|
|
9f937f6b3b | ||
|
|
c5c6bc340a | ||
|
|
90c7e5ab61 | ||
|
|
b3028dc45d | ||
|
|
f64b9e2fe7 | ||
|
|
1e7ffedd36 | ||
|
|
e9f58df359 | ||
|
|
6885569f9e | ||
|
|
601927406c | ||
|
|
e4fab76f0b | ||
|
|
5a7b186478 | ||
|
|
79d30f4fa5 | ||
|
|
56dab6c18b | ||
|
|
6546a5ae05 | ||
|
|
57c243bd34 | ||
|
|
f2cd3fc435 | ||
|
|
9b75b1fb93 | ||
|
|
81f4b6e680 | ||
|
|
de783b6948 | ||
|
|
53c6f707e6 | ||
|
|
23142c6ece | ||
|
|
1fa3170355 | ||
|
|
9025b7b825 | ||
|
|
1487ae1036 | ||
|
|
dab1240d19 | ||
|
|
feb6622762 | ||
|
|
347b0bfa37 | ||
|
|
efa63b105f | ||
|
|
18d8c19df7 | ||
|
|
3c4e1c59b1 | ||
|
|
8d7c92f30e | ||
|
|
4e7b7d8d59 | ||
|
|
1213fc9145 | ||
|
|
3cd30681d7 | ||
|
|
308a4e9c06 | ||
|
|
34cc6da0a9 | ||
|
|
6f1b7edca4 | ||
|
|
fdc7a3d61a | ||
|
|
9798ba28ec | ||
|
|
c8522488be | ||
|
|
f951e93875 | ||
|
|
d51c623168 | ||
|
|
fe57312d4f | ||
|
|
ee148954c0 | ||
|
|
6ffc7d45c3 | ||
|
|
a0301c85e8 | ||
|
|
6928b9a017 | ||
|
|
ce0d32a857 | ||
|
|
717e861c1d | ||
|
|
541dd1e284 | ||
|
|
f68384a505 | ||
|
|
f406e6da6b | ||
|
|
c75c5e4901 | ||
|
|
b4aedf9ec9 | ||
|
|
cba00fba13 | ||
|
|
90403d111b | ||
|
|
47460ef884 | ||
|
|
e48d44c6e6 | ||
|
|
8e522e3dd2 | ||
|
|
d9e0429ff4 | ||
|
|
bd35fc3c9a | ||
|
|
80d882a437 | ||
|
|
610f9dccc1 | ||
|
|
bddb5b9f40 | ||
|
|
84fc94cf4b | ||
|
|
e1186ebf2b | ||
|
|
e56a778246 | ||
|
|
a2d89f320f | ||
|
|
071038dc09 | ||
|
|
7e11c4d47b | ||
|
|
cc70fd18d5 | ||
|
|
334b47a778 | ||
|
|
a0c6ec7918 | ||
|
|
665883474d | ||
|
|
e0fce5386e | ||
|
|
d00829e609 | ||
|
|
de5b1e33fe | ||
|
|
f2e6ca1439 | ||
|
|
974bf73516 | ||
|
|
3c8fe3bff7 | ||
|
|
795a14a291 | ||
|
|
0dca4a26e5 | ||
|
|
2688e37f77 | ||
|
|
e8a9ea1185 | ||
|
|
4770c273f9 | ||
|
|
0e2e42b826 | ||
|
|
de819828ce | ||
|
|
fed34e9b98 | ||
|
|
edb98ae078 | ||
|
|
02ba40facb | ||
|
|
6fc87fdafd | ||
|
|
d430498243 | ||
|
|
2a17f76869 | ||
|
|
d5adfb70ea | ||
|
|
23b422bed6 | ||
|
|
f346b8db0c | ||
|
|
a951bd8d25 | ||
|
|
0d7ff74bd2 | ||
|
|
a36b832826 | ||
|
|
055583de1d | ||
|
|
63f2c49606 | ||
|
|
ff879f488e | ||
|
|
0942553bb7 | ||
|
|
ac7d2f6075 | ||
|
|
f1fcf60924 | ||
|
|
1d460555af | ||
|
|
904dcb61b0 | ||
|
|
b038841242 | ||
|
|
7efa155b51 | ||
|
|
07cef9c683 | ||
|
|
85b836ee28 | ||
|
|
74222fe727 | ||
|
|
a30dacb4be | ||
|
|
66473d9b47 | ||
|
|
0764a932e1 | ||
|
|
a227ef1001 | ||
|
|
d568644155 | ||
|
|
2d13b85488 | ||
|
|
a2edfcfdcb | ||
|
|
63ab04d3d8 | ||
|
|
34a8badd6f | ||
|
|
54cb857224 | ||
|
|
21b6c1dc29 | ||
|
|
29a8522a86 | ||
|
|
73af1863ec | ||
|
|
9cd8f0e7c2 | ||
|
|
ec3458b717 | ||
|
|
1117792bf7 | ||
|
|
1649b9790b | ||
|
|
b23c9b2133 | ||
|
|
eaeb918b33 | ||
|
|
f05af3d427 | ||
|
|
a73c5fac86 | ||
|
|
79be2e64ae | ||
|
|
e66951debc | ||
|
|
1e8a8d0896 | ||
|
|
f9def3acf5 | ||
|
|
a7e6e638fe | ||
|
|
9ef3ea2932 | ||
|
|
38dcee3327 | ||
|
|
12b92d3f35 | ||
|
|
dacfb4f1ff | ||
|
|
4086ba73d3 |
+148
-29
@@ -3,23 +3,155 @@ image: amazoncorretto:21
|
||||
before_script:
|
||||
- export GRADLE_USER_HOME=`pwd`/.gradle
|
||||
|
||||
# TODO add client reference test process, search for import com.cobblemon.mod.common.client
|
||||
|
||||
stages:
|
||||
- preprocessing
|
||||
- packsquash
|
||||
- replaceresources
|
||||
- build
|
||||
- deploy
|
||||
|
||||
#TODO add client reference test process, search for import com.cobblemon.mod.common.client
|
||||
check_assets_cache:
|
||||
stage: preprocessing
|
||||
script:
|
||||
- echo "Checking if cached hash matches current hash"
|
||||
|
||||
# Calculate the current hash
|
||||
- find common/src/main/resources/assets common/src/main/resources/data -type f -exec sha256sum {} + | sort -k 2 | sha256sum > current_hash.txt
|
||||
- echo "Current hash:"
|
||||
- cat current_hash.txt
|
||||
|
||||
# Check for the existence of the previous hash
|
||||
- if [[ -f assets_data_hash.txt ]]; then
|
||||
echo "Previous cached hash found:";
|
||||
cat assets_data_hash.txt;
|
||||
if cmp -s assets_data_hash.txt current_hash.txt; then
|
||||
echo "Hashes match, skipping PackSquash";
|
||||
rm -f run_packsquash.flag || true;
|
||||
else
|
||||
echo "Hashes differ, PackSquash needed";
|
||||
touch run_packsquash.flag;
|
||||
fi;
|
||||
else
|
||||
echo "No cached hash found, marking PackSquash as required";
|
||||
touch run_packsquash.flag;
|
||||
fi
|
||||
|
||||
# Save the current hash for future runs
|
||||
- echo "Saving current hash to cache";
|
||||
- cp current_hash.txt assets_data_hash.txt
|
||||
artifacts:
|
||||
paths:
|
||||
- current_hash.txt
|
||||
- assets_data_hash.txt
|
||||
- run_packsquash.flag
|
||||
expire_in: 7 days
|
||||
cache:
|
||||
key: "cache-${CI_COMMIT_REF_NAME}"
|
||||
paths:
|
||||
- assets_data_hash.txt
|
||||
- ./compressed_pack.zip
|
||||
|
||||
setup_resource_pack:
|
||||
stage: preprocessing
|
||||
script:
|
||||
- echo "Setting up fake resource pack"
|
||||
- mkdir ./fake_resource_pack
|
||||
- cp -r common/src/main/resources/assets common/src/main/resources/data ./fake_resource_pack/
|
||||
- echo '{"pack":{"pack_format":34,"description":"Fake Cobblemon Pack"}}' > ./fake_resource_pack/pack.mcmeta
|
||||
artifacts:
|
||||
paths:
|
||||
- ./fake_resource_pack/
|
||||
expire_in: 7 days
|
||||
|
||||
setup_packsquash:
|
||||
stage: preprocessing
|
||||
script:
|
||||
- echo "Installing wget & unzip"
|
||||
- yum install -y wget unzip
|
||||
- echo "Downloading PackSquash"
|
||||
- wget https://github.com/ComunidadAylas/PackSquash/releases/download/v0.4.0/PackSquash.CLI.executable.x86_64-unknown-linux-musl.zip
|
||||
- unzip PackSquash.CLI.executable.x86_64-unknown-linux-musl.zip -d ./packsquash_bin
|
||||
- echo "Creating PackSquash options.toml"
|
||||
- |
|
||||
cat <<EOF > ./options.toml
|
||||
pack_directory = "./fake_resource_pack/"
|
||||
output_file_path = "./compressed_pack.zip"
|
||||
zip_spec_conformance_level = "high"
|
||||
['**/*?.ogg']
|
||||
transcode_ogg = false
|
||||
two_pass_vorbis_optimization_and_validation = false
|
||||
sampling_frequency = 999999
|
||||
empty_audio_optimization=false
|
||||
EOF
|
||||
artifacts:
|
||||
paths:
|
||||
- ./packsquash_bin/
|
||||
- ./options.toml
|
||||
expire_in: 7 days
|
||||
|
||||
default:
|
||||
artifacts:
|
||||
expire_in: 7 days
|
||||
|
||||
run_packsquash:
|
||||
stage: packsquash
|
||||
dependencies:
|
||||
- check_assets_cache
|
||||
- setup_resource_pack
|
||||
- setup_packsquash
|
||||
script:
|
||||
- ls -l
|
||||
- if [[ ! -f ./compressed_pack.zip || -f run_packsquash.flag ]]; then
|
||||
echo "Running PackSquash";
|
||||
chmod +x ./packsquash_bin/packsquash;
|
||||
./packsquash_bin/packsquash ./options.toml;
|
||||
else
|
||||
echo "Skipping PackSquash; no changes detected and compressed pack exists";
|
||||
fi
|
||||
cache:
|
||||
key: "cache-${CI_COMMIT_REF_NAME}"
|
||||
policy: pull-push
|
||||
paths:
|
||||
- assets_data_hash.txt
|
||||
- ./compressed_pack.zip
|
||||
artifacts:
|
||||
paths:
|
||||
- ./compressed_pack.zip
|
||||
|
||||
replace_resources:
|
||||
stage: replaceresources
|
||||
dependencies:
|
||||
- run_packsquash
|
||||
script:
|
||||
- echo "Installing unzip"
|
||||
- yum install -y unzip
|
||||
|
||||
- echo "Unzipping compressed pack"
|
||||
- mkdir temp_unpack
|
||||
- unzip ./compressed_pack.zip -d temp_unpack
|
||||
|
||||
- echo "Removing original assets and data"
|
||||
- rm -rf common/src/main/resources/assets
|
||||
- rm -rf common/src/main/resources/data
|
||||
|
||||
- echo "Replacing assets and data"
|
||||
- mv temp_unpack/assets common/src/main/resources/
|
||||
- mv temp_unpack/data common/src/main/resources/
|
||||
artifacts:
|
||||
paths:
|
||||
- common/src/main/resources/
|
||||
|
||||
build:
|
||||
stage: build
|
||||
only:
|
||||
- branches@cable-mc/cobblemon
|
||||
dependencies:
|
||||
- replace_resources
|
||||
script:
|
||||
- export GRADLE_OPTS="-Xmx4G -Xms4G"
|
||||
- chmod +x ./gradlew
|
||||
- ./gradlew build
|
||||
cache:
|
||||
key: "$CI_COMMIT_REF_NAME"
|
||||
policy: push
|
||||
artifacts:
|
||||
paths:
|
||||
- neoforge/build/libs
|
||||
- fabric/build/libs
|
||||
@@ -28,39 +160,26 @@ publish:
|
||||
stage: deploy
|
||||
dependencies:
|
||||
- build
|
||||
cache:
|
||||
key: "CI_COMMIT_REF_NAME"
|
||||
policy: pull
|
||||
paths:
|
||||
- neoforge/build/libs/*.jar
|
||||
- fabric/build/libs/*.jar
|
||||
rules:
|
||||
- if: '$CI_COMMIT_BRANCH == "main"'
|
||||
- if: '$CI_PROJECT_PATH == "cable-mc/cobblemon" && $CI_COMMIT_BRANCH == "main"'
|
||||
script:
|
||||
- export GRADLE_OPTS="-Xmx4G -Xms4G"
|
||||
- chmod +x ./gradlew
|
||||
- ./gradlew publish
|
||||
|
||||
# Separate step to deploy the artifacts, save Norry the headache of keeping even more shit around
|
||||
deploy:
|
||||
stage: deploy
|
||||
dependencies:
|
||||
- build
|
||||
when: manual
|
||||
cache:
|
||||
key: "$CI_COMMIT_REF_NAME"
|
||||
policy: pull
|
||||
artifacts:
|
||||
paths:
|
||||
- neoforge/build/libs/*.jar
|
||||
- fabric/build/libs/*.jar
|
||||
script:
|
||||
|
||||
deploy_artifacts:
|
||||
stage: deploy
|
||||
dependencies:
|
||||
- build
|
||||
script:
|
||||
- echo "Collecting artifacts..."
|
||||
- mv neoforge/build/libs/*+*.jar ./
|
||||
- mv fabric/build/libs/*+*.jar ./
|
||||
- find neoforge/build/libs/ -type f -name "*.jar" | grep -vE "dev-shadow|sources|javadoc" | xargs -I {} mv {} ./
|
||||
- find fabric/build/libs/ -type f -name "*.jar" | grep -vE "dev-shadow|sources|javadoc" | xargs -I {} mv {} ./
|
||||
- for f in *.jar; do mv "$f" "$(echo "$f" | sed s/+/b$CI_PIPELINE_IID+/)"; done
|
||||
artifacts:
|
||||
expire_in: 28 days
|
||||
paths:
|
||||
- ./*.jar
|
||||
# - neoforge/build/libs/*+*.jar
|
||||
# - fabric/build/libs/*+*.jar
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
### Additions
|
||||
- Added the Poké Dex as a craftable item.
|
||||
- Poké Dexes can be placed on lecterns.
|
||||
@@ -1,6 +0,0 @@
|
||||
### Additions
|
||||
- Added `moves` option to PokemonProperties, allowing you to set the moves of a Pokémon in commands and spawn files.
|
||||
- Added /spawnnpc and /spawnnpcat commands.
|
||||
|
||||
### Fixes
|
||||
- Fixed sync issues with recently learned moves where their PP would not go down until you logout and in.
|
||||
@@ -1,24 +0,0 @@
|
||||
### Additions
|
||||
- Pokémon are now animated when seen in any GUI that isn't the party GUI.
|
||||
- Quirk animations can now occur for Pokémon that are shoulder mounted.
|
||||
|
||||
### Changes
|
||||
- Improved the performance of display cases that contain Pokémon photos.
|
||||
|
||||
### Fixes
|
||||
- Fixed some Pokémon photos in display cases flashing if the Pokémon has gender differences.
|
||||
- Fixed Bidoof and Jigglypuff sleep animations stopping after some time, causing them to be 'asleep' while T-posing menacingly.
|
||||
- Fixed entities not changing poses unless you're looking at them.
|
||||
- Fixed cries not playing on send-out if the Pokémon is off-screen.
|
||||
|
||||
### Developer
|
||||
- Rebuilt large swaths of the model animation code to simplify it.
|
||||
- Renamed a bunch of things from %Poseable% to %Posable% because spelling.
|
||||
- Renamed StatelessAnimation to PoseAnimation.
|
||||
- Renamed StatefulAnimation to ActiveAnimation.
|
||||
- Documented the animation system.
|
||||
|
||||
### Data Pack & Resource Pack Creators
|
||||
- Added MoLang compatibility in the isVisible property for transformed parts.
|
||||
- Added q.has_aspect('some_aspect') function to animations, posers, and entity particle effects.
|
||||
- Added support for conditional pose animations.
|
||||
+149
-103
@@ -1,45 +1,57 @@
|
||||
# Changelog
|
||||
## [1.6.0 - The Bass Pro Fishing Update (Month Xth, 2024)](#1-6-0)
|
||||
## [1.6.0 - The Record Catch Update (Month Xth, 2024)](#1-6-0)
|
||||
#### "Now that there's a fishing mechanic, the mod is actually good!"
|
||||
|
||||
### Additions
|
||||
- Added Pokémon (and item) fishing using modified fishing rods - Poké Rods! You'll need a Poké Rod smithing template, a fishing rod, and some type of Poké Ball. Each Poké Ball makes a differently themed rod. Why not?
|
||||
- Added the Pokédex as a craftable item that can be placed on lecterns.
|
||||
- Added Pokémon (and item) fishing using modified fishing rods - Poké Rods! You'll need a Poké Rod smithing template, a fishing rod, and some type of Poké Ball. Each Poké Ball makes a differently themed rod because it's cool.
|
||||
- Added Lure Ball functionality, increasing the catch rate of Pokémon that were caught on a fishing rod.
|
||||
- Added Repeat Ball functionality, increasing the catch rate of Pokémon that are already registered as caught in a player's Pokédex.
|
||||
- Added property chaining support for duplicate CustomPokemonPropertyType elements.
|
||||
- Added flat level battling with options to set all Pokemon to level 50, 100, or 5 for the duration of a battle. No experience or EVs are granted for a flat battle.
|
||||
- Added `aspect` and `unaspect` PokemonProperty arguments (which also includes commands such as `/pokemonedit`, `/spawnpokemon`, and `/givepokemon`) to allow forcing or un-forcing an aspect on a Pokémon.
|
||||
- Added `type` alternatively `elemental_type` PokemonProperty argument, this is only used for filtering and is not applied to Pokémon. Example `type=fire` would be true for Charmander but false for Squirtle.
|
||||
- Added flat level battling with options to set all Pokémon to level 50, 100, or 5 for the duration of a battle. No experience or EVs are granted for a flat battle.
|
||||
- Added support for Double Battles, Triple Battles, and Multi-battles.
|
||||
- Added raft platforms for non-swimming, non-flying Pokémon to stand on during battles that take place on the water's surface (Flying Pokémon will fly over water in battle, and water breathing Pokémon will swim in water during battle).
|
||||
- Added smarter send-out positions for Pokémon in battle. They should get in your way less!
|
||||
- CriticalCaptures and Pokédex progress capture multiplier now work with the Pokédex.
|
||||
- Added shiny Pokémon particle effects and sounds to help find them.
|
||||
- Added effects for the burn status effect.
|
||||
- Added effects for the moves: Seismic Toss, Withdraw, Bite, Crunch, Super Fang, Hyper Fang, Pursuit, Mist, Haze, Lick, Kinesis, Psychic, Water Sport, and Mud Sport.
|
||||
- Added extra visuals to early berry growth stages.
|
||||
- Moves impacted by the abilities Pixelate, Refrigerate, Aerilate, Galvanize, and Normalize now display as their altered typing.
|
||||
- Added Polished Tumblestone and Tumblestone Brick block sets from Tumblestone, Black Tumblestone, and Sky Tumblestone.
|
||||
- Added Metronome, Protective Pads, Punching Glove, Room Service, Scope Lens, Shed Shell, Terrain Extender, Throat Spray, Utility Umbrella, Wide Lens, and Zoom Lens held items
|
||||
- Added particles for moves: Watersport, Mud Sport, and Psychic.
|
||||
- Moves impacted by the abilities Pixelate, Refrigerate, Aerilate, Galvanize, and Normalize now display as their altered typing.
|
||||
- Added Fire, Water, Thunder, Leaf, Ice, Sun, Moon, Shiny, Dawn, and Dusk Stone storage blocks.
|
||||
- Added Eject Pack, Metronome, Protective Pads, Punching Glove, Room Service, Scope Lens, Shed Shell, Terrain Extender, Throat Spray, Utility Umbrella, Wide Lens, and Zoom Lens held items
|
||||
- Added `stonjourner_henge_ruins`, `luna_henge_ruins`, and `sol_henge_ruins` structures.
|
||||
- Added Eject Pack, Metronome, Protective Pads, Punching Glove, Room Service, Scope Lens, Shed Shell, Terrain Extender, Throat Spray, Utility Umbrella, Wide Lens, and Zoom Lens held items.
|
||||
- Added a full evolution particle effect for Pokémon that are sent out when evolution is started.
|
||||
- Added a Nurse profession that can be unlocked by having villagers claim a healing machine block.
|
||||
- Pokémon are now animated when seen in any GUI that isn't the party GUI.
|
||||
- Added animation and sounds for trading.
|
||||
- Added icons for pending trade, team-up, and battle requests from other players.
|
||||
- Quirk animations can now occur for Pokémon that are shoulder mounted.
|
||||
- Added new sounds for Poké Balls bouncing off of Pokémon and landing on the ground during capture.
|
||||
- Added a unique set of sounds for Ancient Poké Balls.
|
||||
- Added a sound for using Exp Candy and Rare Candy items.
|
||||
- Added revamped Poké Ball animation for Pokémon breaking out.
|
||||
- Added battle log messages for switching out Pokémon.
|
||||
- Added config setting `displayEntityNameLabel` and `displayEntityLabelsWhenCrouchingOnly` to control what and when is displayed for the pokemon label.
|
||||
- Added `no_ai` and `freeze_frame` options to the `/spawnpokemon` command.
|
||||
- Added `moves` option to Pokémon properties, allowing you to set the moves of a Pokémon in commands and spawn files using comma-separated move names.
|
||||
- Added a `natural` block state property for the healing machine block; when property is set to true, the block will have a different texture and drop an iron ingot instead of itself.
|
||||
- Added a `battleInvulnerability` gamerule to make players invulnerable to any damage during a battle.
|
||||
- Added a `mobTargetInBattle` gamerule to exclude players from being targeted by mobs during a battle.
|
||||
- Added battle log messages for switching out Pokémon.
|
||||
- Added Evolution particles and sounds for Pokémon that are sent out when evolution is started.
|
||||
- Added a Nurse profession that can be unlocked by having villagers claim a healing machine block.
|
||||
- Added pokecenters to all 5 village types.
|
||||
- Added a `natural` block state property for the healing machine block; when property is set to true, the block will have a different texture and drop an iron ingot instead of itself.
|
||||
- New config setting `displayEntityNameLabel` and `displayEntityLabelsWhenCrouchingOnly` to control what and when is displayed for the pokemon label
|
||||
- `/freezepokemon` command to pause a Pokémon's animation at a specific point in time.
|
||||
- Added `no_ai` and `freeze_frame` options to the `/spawnpokemon` command.
|
||||
- Added shiny Pokémon particles with sound effects.
|
||||
- Added animation for trading.
|
||||
- Added shiny Pokémon effects.
|
||||
- Added effects for the burn status effect.
|
||||
- Added effects for the moves: Psychic, Water Sport, and Mud Sport.
|
||||
- Added a new universal locator called "top".
|
||||
- Added a `/freezepokemon` command to pause a Pokémon's animation at a specific point in time.
|
||||
- Added `/spawnnpc` and `/spawnnpcat` commands.
|
||||
- Added `hiddenability=false` option to `/spawnpokemon` and `/pokemonedit` commands, allowing the Pokémon's ability to be reverted to a normal ability.
|
||||
- Added `aspect` and `unaspect` PokemonProperty arguments (which also includes commands such as `/pokemonedit`, `/spawnpokemon`, and `/givepokemon`) to allow forcing or un-forcing an aspect on a Pokémon.
|
||||
- Added `type` alternatively `elemental_type` PokemonProperty argument, this is only used for filtering and is not applied to Pokémon. Example `type=fire` would be true for Charmander but false for Squirtle.
|
||||
|
||||
### Structures Added
|
||||
- Fishing boat structure that contain an Explorer Map leading to a shipwreck cove and a Poké Rod Smithing Template.
|
||||
- Stonjourner Henge Ruins, Luna Henge Ruins, and Sol Henge Ruins.
|
||||
- Submerged Shipwreck Cove and Lush Shipwreck Cove.
|
||||
- Pokémon Centers to all 5 village types.
|
||||
|
||||
### Pokémon Added
|
||||
#### Gen 1
|
||||
- Alola Bias Cubone
|
||||
- Alola Bias Cubone (built-in resource pack)
|
||||
- Alolan Marowak
|
||||
|
||||
#### Gen 2
|
||||
@@ -54,10 +66,7 @@
|
||||
- Octillery
|
||||
- Mantine
|
||||
- Smeargle
|
||||
- Remoraid
|
||||
- Octillery
|
||||
- Mantine
|
||||
- Corsola
|
||||
- Delibird
|
||||
|
||||
#### Gen 3
|
||||
- Slakoth
|
||||
@@ -75,18 +84,6 @@
|
||||
- Bagon
|
||||
- Shelgon
|
||||
- Salamence
|
||||
|
||||
#### Gen 4
|
||||
- Shellos [East Sea form]
|
||||
- Gastrodon [East Sea form]
|
||||
- Finneon
|
||||
- Lumineon
|
||||
- Mantyke
|
||||
- Feebas
|
||||
- Milotic
|
||||
- Spheal
|
||||
- Sealeo
|
||||
- Walrein
|
||||
- Kecleon
|
||||
|
||||
#### Gen 4
|
||||
@@ -122,12 +119,6 @@
|
||||
- Hatenna
|
||||
- Hattrem
|
||||
- Hatterene
|
||||
- Bruxish
|
||||
- Mareanie
|
||||
- Toxapex
|
||||
|
||||
#### Gen 8
|
||||
- Cramorant
|
||||
- Pincurchin
|
||||
|
||||
#### Gen 9
|
||||
@@ -144,10 +135,11 @@
|
||||
- Wiglett
|
||||
- Wugtrio
|
||||
- Flamigo
|
||||
- Dondozo
|
||||
|
||||
#### Unique Forms
|
||||
- Magikarp Jump Variants
|
||||
- Gyarados Jump Variants
|
||||
- Magikarp Jump variants for Magikarp
|
||||
- Magikarp Jump variants for Gyarados (built-in resource pack, enabled by default on Fabric)
|
||||
|
||||
### Added cries to the following Pokémon
|
||||
- Sandshrew, Sandslash
|
||||
@@ -189,7 +181,7 @@
|
||||
- Hatenna, Hattrem, Hatterene
|
||||
- Pincurchin
|
||||
- Klawf
|
||||
- Finizen, Palafin [Zero], Palafin [Hero]
|
||||
- Finizen, Palafin \[Zero\], Palafin \[Hero\]
|
||||
- Dondozo
|
||||
|
||||
### Added shoulder mounts for the following Pokémon
|
||||
@@ -208,6 +200,7 @@
|
||||
- Buneary
|
||||
- Combee
|
||||
- Pachirisu
|
||||
- Tepig
|
||||
- Pidove
|
||||
- Petilil, Hisui Bias Petilil
|
||||
- Zorua, Hisui Bias Zorua
|
||||
@@ -215,35 +208,47 @@
|
||||
- Fletchling
|
||||
- Skrelp
|
||||
- Klefki
|
||||
- Litten
|
||||
- Fomantis
|
||||
- Morelull
|
||||
- Dreepy
|
||||
- Sprigatito
|
||||
- Shroodle
|
||||
- Tatsugiri
|
||||
- Glimmet
|
||||
- Gimmighoul [Roaming]
|
||||
- Gimmighoul \[Roaming\]
|
||||
|
||||
### Animation updates for the following Pokémon
|
||||
- Bellsprout, Weepinbel, Victreebel
|
||||
- Bellsprout, Weepinbell, Victreebel
|
||||
- Shellder, Cloyster
|
||||
- Porygon, Porygon2, Porygon-Z
|
||||
- Furret
|
||||
- Swinub, Piloswine, Mamoswine
|
||||
- Skarmory
|
||||
- Tyrogue
|
||||
- Spinda
|
||||
- Torkoal
|
||||
- Nincada, Ninjask
|
||||
- Lunatone
|
||||
- Solrock
|
||||
- Buneary, Lopunny
|
||||
- Magnezone
|
||||
- Alomomola
|
||||
- Tepig, Pignite, Emboar
|
||||
- Flabebe, Floette, Florges
|
||||
- Litten, Torracat, Incineroar
|
||||
- Fomantis, Lurantis
|
||||
- Dreepy, Drakloak
|
||||
- Kleavor
|
||||
- Scorbunny
|
||||
- Gimmighoul \[Roaming\]
|
||||
|
||||
### Model updates for the following Pokémon
|
||||
- Bulbasaur, Ivysaur, Venusaur
|
||||
- Charmander, Charmeleon, Charizard
|
||||
- Caterpie
|
||||
- Kakuna, Beedrill
|
||||
- Lapras
|
||||
- Porygon, Porygon2, Porygon-Z
|
||||
- Igglybuff, Jigglypuff, Wigglytuff
|
||||
- Poliwhirl, Poliwrath
|
||||
@@ -277,9 +282,11 @@
|
||||
- Phantump, Trevenant
|
||||
- Litten, Torracat, Incineroar
|
||||
- Popplio, Brionne, Primarina
|
||||
- Mudbray
|
||||
- Lurantis
|
||||
- Scorbunny, Cinderace
|
||||
- Quaxly
|
||||
- Skeledirge
|
||||
- Tatsugiri
|
||||
|
||||
### Changes
|
||||
@@ -289,126 +296,143 @@
|
||||
- Adjusted volumes of sounds made by Display Cases, Berry Bushes, Energy Root, Medicinal Leek, Vivichoke, Mints, Revival Herbs and Gilded Chests.
|
||||
- Changes to mulch buffs/durations.
|
||||
- Updated potion sprites.
|
||||
- Link Cable recipe has been redesigned.
|
||||
- Changed Link Cable recipe to be much cheaper.
|
||||
- Updated sounds for Medicinal Leeks, Big Roots, Energy Roots and Revival Herbs.
|
||||
- Updated UI sounds for clicking and evolving Pokémon.
|
||||
- Updated cries for Chansey, Crobat, Hoothoot, Noctowl.
|
||||
- Removed the interchangeable evolution results for the first stage Hisuian starters to prevent confusion. The method still exists for stage 2 to 3, but stage 1 to 2 was not distinguishable enough for many users.
|
||||
- Bulbasaur, Ivysaur, Venusaur, Kakuna, Beedrill, Snorlax, Munchlax, Krabby, Kingler, Lurantis, Horsea, Seadra, Kingdra, Litten, Torracat, Incineroar, Mudkip, Marshtomp, Swampert, Scorbunny, Cinderace, Prinplup, Empoleon, Relicanth, Goldeen, Seaking, Shellder, Cloyster, Tatsugiri, Igglybuff, Jigglypuff, Wigglytuff, Krookodile, Gible, Gabite, Garchomp, Rhyhorn, Rhydon, Rhyperior, and Carvanha received model updates.
|
||||
- Vivillon wings will stop being clear when you remove any Vivillon related resourcepacks. The default pattern will be the meadow wings.
|
||||
- Adjusted Pokémon sendout positions for single battles.
|
||||
- Wild Pokémon interaction range increased to 12 blocks (from 10 blocks).
|
||||
- Player trade range increased to 12 blocks (from 10 blocks).
|
||||
- PvP battle range increased to 32 blocks (from 10 blocks).
|
||||
- Spectate range increased to 64 blocks (from 10 blocks).
|
||||
- The inventory key now closes our GUIs if they are open, mirroring how Minecraft screens usually work.
|
||||
- The noise from the pasture and PC blocks being interacted with now activates Sculk sensors.
|
||||
- Updated dialogue GUI assets with proper assets.
|
||||
- Updated dialogue GUI assets with proper assets not made by a programmer. Well, it was still a programmer, but- someone who can also draw!
|
||||
- Updated party switching interface when in battle.
|
||||
- Relic coin pouches can now be waterlogged.
|
||||
- Pokémon name and level labels are now only visible when crouching by default. Set the `displayEntityLabelsWhenCrouchingOnly` config to `false` to always have them displayed.
|
||||
- The "Press R to start battle prompt" will now disappear after a player's first battle has been won.
|
||||
- The default number of digits for the Pokédex number in the summary has been increased, from 3 to 4.
|
||||
- The "Press R to start battle prompt" will now disappear after a player's first battle has been won. You've probably figured that detail out by then.
|
||||
- The default number of digits for the Pokédex number in the summary has been increased, from 3 to 4. There sure are a lot of Pokémon these days.
|
||||
- The pasture block model's screen is now off by default.
|
||||
- The Poké Balls creative tab is now named Utility Items as it also encompasses Poké Rod and Pokédex items.
|
||||
- Moves can now be benched without specifying a replacement move, allowing for empty move slots.
|
||||
- Moves learned via Sketch now persist after battle.
|
||||
- Updated potion sprites
|
||||
- Removed the interchangeable evolution results for the first stage Hisuian starters to prevent confusion. The method still exists for stage 2 to 3, but stage 1 to 2 was not distinguishable enough for many users.
|
||||
- Hidden Power now displays its effective typing.
|
||||
- Updated particles for moves: Confusion, Protect, Sand Attack, and Quick Attack.
|
||||
- Updated particles for status effects: Paralysis, Poison, and Sleep.
|
||||
- Updated particles on Gastly.
|
||||
- Revamped stat buff and de-buff particles.
|
||||
- Improved the performance of display cases that contain Pokémon photos.
|
||||
- Updated sounds for Poké Balls closing, opening and for Pokémon breaking out.
|
||||
- Improved the performance of display cases that contain Pokémon Model items.
|
||||
- Removed species Base Stats from the summary interface as it is now viewable within the Pokédex.
|
||||
- Changed summary tab text labels to icons. You'll get used to them. Or else.
|
||||
- Clicking the summary interface exit button while the swap moves or evolve screen is open will cause the interface to switch back to the party screen. The button will exit the interface otherwise.
|
||||
- Ancient Poke Balls now jump once rather than shaking 3 times when capturing Pokémon.
|
||||
- The jump height indicate the number of shakes that would have occurred.
|
||||
- A high wobbly jump indicates 1 shake.
|
||||
- A high jump indicates 2 shakes.
|
||||
- A medium jump indicates 3 shakes.
|
||||
- A short jump indicates that you caught the Pokémon.
|
||||
- Pokémon sent out during battle will spawn facing their opponent.
|
||||
- Pokémon sent out outside a battle will spawn facing their trainer.
|
||||
- Wailord is now 25% bigger. ... [But I have an idea.](https://tenor.com/view/star-wars-more-gif-21856591)
|
||||
- Region-biased Pokémon forms are now optional to a built-in resource pack. This pack is enabled by default for Fabric. For NeoForge users, all our built-in resource packs default to disabled because that's all it supports right now.
|
||||
- Update item sprites for Cell Battery, Chipped Pot, Covert Cloak, Cracked Pot, Masterpiece Teacup, Red Card, Sachet, and Unremarkable Teacup.
|
||||
|
||||
### Fixes
|
||||
- Fixed Ability Patches not reverting Hidden Abilities back to Normal Abilities.
|
||||
- Fixed awarding Pokémon experience upon forfeiting battles.
|
||||
- Scaled down Amaura's fetus model to avoid clipping through the tank while animating.
|
||||
- Fixed Cubone's cry not having a sound.
|
||||
- Fixed the sendout sound erronously playing when a wild Pokémon breaks out of a Poké ball.
|
||||
- Fixed Cubone's cry not having a sound (as if that Pokémon needed to be more tragic).
|
||||
- Fixed the sendout sound erroneously playing when a wild Pokémon breaks out of a Poké ball.
|
||||
- Flamethrower is no longer missing sounds.
|
||||
- Fixed the Seafloor spawning context not being a usable context.
|
||||
- Fixed Pokemon spawning in non-full blocks like slabs.
|
||||
- Fixed Gilded Chests not dropping the chest itself when broken, only the contents.
|
||||
- Fixed Pokémon losing their Hidden Ability through evolution if the middle stage did not have a Hidden Ability.
|
||||
- Hidden Power no longer plays the water type action effect (It now plays the normal type action effect).
|
||||
- Fixed Chimchar and Monferno T-posing whenever they sleep.
|
||||
- Fixed the Magby line not having any placeholder walk animations.
|
||||
- Fixed Duskull and Dusclops using skylight levels for their nether spawn data. There is no sun in the Nether!
|
||||
- Fixed Hisuian Zoroark using base Zoroark stats.
|
||||
- Fixed Bellossom clipping into the player head when shoulder mounted.
|
||||
- Fixed Shroomish's look range to prevent it from looking higher than it should.
|
||||
- Fixed Cetitan's cry breaking its walk and sleep animations.
|
||||
- Fixed Crumbling Arch not blending in with the world.
|
||||
- Fixed Energy Root applying being usable on a fainted Pokémon.
|
||||
- Fixed Pokémon entity not spawning when starting a battle while recalling said Pokémon.
|
||||
- Fixed Crumbling Arch structures not blending in with the world.
|
||||
- Fixed Energy Root being usable on a fainted Pokémon.
|
||||
- Fixed Pokémon not being sent out when starting a battle while recalling said Pokémon.
|
||||
- Fixed species comparison not using namespace for both sides in `PokemonProperties.isSubsetOf`.
|
||||
- Fixed `PokemonProperties#asString` prefixing nicknames with a '$'.
|
||||
- Fixed PC not saving when Pokémon was changed outside.
|
||||
- Fixed shearable Pokémon not dropping correct wool colors.
|
||||
- Fixed pasture spawning Pokémon inside solid blocks when closest spawning position is blocked off.
|
||||
- Fixed Tumbling Down advancement not being granted by tumblestone variants.
|
||||
- Improve error handling when loading spawn sets to ensure invalid configurations don't crash the server.
|
||||
- Fixed empty `JsonPlayerData` files resulting in players being unable to join server/world.
|
||||
- Evolution notification and UI sound is now correctly playing (again).
|
||||
- Sound for evolving Pokémon through the Summary Menu is now correctly playing again.
|
||||
- Fix Starter prompt not showing up.
|
||||
- Fix evolution now granting Pokédex entries.
|
||||
- Fix Pokédex crash when a form is looked up without ever seeing the "normal" form.
|
||||
- Fixed owned poké balls floating upwards if pausing mid-transition.
|
||||
- Fixed owned Poké balls floating upwards if pausing the game mid-transition (which looked absolutely hilarious).
|
||||
- Fixed all mouse buttons working for Battle UI navigation (now it's only primary/left click).
|
||||
- Fixed trading Pokémon setting their friendship to 0 instead of the base value.
|
||||
- Fixed a scenario where a Pokémon that rolled to spawn with a special Tera type sometimes has a Tera type that is already a part of their natural typing.
|
||||
- Fixed a scenario where a Pokémon that rolled to spawn with a special Tera type sometimes having a Tera type that is already a part of their natural typing.
|
||||
- Fixed PokemonProperty argument suggestions for `tera` and `tera_type` not suggesting `stellar`.
|
||||
- Fixed being able to stack Relic Coin Pouches on top of each other.
|
||||
- Fixed Model loader generating misleading crash-reports when client is crashing.
|
||||
- Fixed model loader generating misleading crash-reports when client is crashing - that resurrection tank error wasn't our fault! We're innocent, your honour!
|
||||
- Big Roots and Energy Roots now share the same sounds as intended.
|
||||
- Fixed sounds made by blocks playing at a lower pitch than intended.
|
||||
- Fixed Chimchar and Monferno T posing whenever they sleep.
|
||||
- Fixed Chimchar and Monferno comically T posing whenever they sleep.
|
||||
- Fixed Cetitan's cry breaking its walk and sleep animations.
|
||||
- Fixed the Magby line not having any placeholder walk animations.
|
||||
- Fixed Duskull and Dusclops using skylight levels for their nether spawn data. There is no sun in the Nether!
|
||||
- Fixed Hisuian Zoroark using base Zoroark stats.
|
||||
- Fixed Bellossom clipping into the player head when shoulder mounted.
|
||||
- Fixed Shroomish's look range to prevent it from looking higher than it should.
|
||||
- Fixed Maushold's faint animations not playing. It's sadder than Tandemaus!
|
||||
- Fixed Maushold's faint animations not playing. It's even sadder than Tandemaus!
|
||||
- Fixed Slowking's battle idle.
|
||||
- Fixed Grafaiai's walk speed to prevent model sliding.
|
||||
- Fixed Maushold's faint animations not playing. It's sadder than Tandemaus!
|
||||
- Fixed Slowking's battle idle.
|
||||
- Fixed Grafaiai's walk speed to prevent model sliding.
|
||||
- Fixed Alolan Exeggutor's tail not showing in the party UI. It will now also stand in front of all Pokémon in the party menu :)
|
||||
- Fixed Timburr duplicating its log while fainting.
|
||||
- Fixed reviving items throwing an exception when used in battle.
|
||||
- Fixed hitbox sizes for Grotle and Torterra being set to default values.
|
||||
- Fixed Hisuian Sneasel using Johtonian Sneasel's cry. Johto-nian. Jotonion. Jotunheim?
|
||||
- Fixed Trevenant T-posing after 8 seconds of sleeping. I also do that.
|
||||
- Fixed Bewear T-posing for a bit if a wild one faints where you can see.
|
||||
- Fixed Noctowl's placeholder fly animation.
|
||||
- Corrected Lotad blinking animation.
|
||||
- Fixed reviving items causing errors when used in battle.
|
||||
- Fixed messages for Focus Sash, Confusion, Mummy, Ice Face, Own Tempo, and Revive.
|
||||
- Improve error handling when loading spawn-sets to ensure invalid configurations don't crash the server.
|
||||
- Fixed `crumbling_arch_ruins` from generating a giant cube of air and removed the chest.
|
||||
- Fixed Crumbling Arch Ruins generating a giant cube of air and removed the chest.
|
||||
- Fixed possible crash on large population servers due to concurrent access of data.
|
||||
- Quickly sending out and recalling Pokémon now looks smoother and can no longer be spammed to cause desync issues.
|
||||
- Pokémon are now invulnerable during sendout animation and intangible during recall animation.
|
||||
- Pokémon are now invincible during sendout animation and intangible during recall animation.
|
||||
- Scrolling with party keybinding now behaves properly when using high scroll speed or scroll sensitivity.
|
||||
- Fixed battle log GUI flashing when Battle GUI is opened.
|
||||
- Bag Items used during a turn will now be refunded if the battle ends before the next turn begins.
|
||||
- Pokémon whose current owner is not its Original Trainer now gains extra experience.
|
||||
- Pokémon whose current owner is not its Original Trainer now gain extra experience.
|
||||
- Fixed Fossil Resurrection advancement not being granted.
|
||||
- Fixed village generation caps not applying for berry farms. You were not meant to see more than two berry farms per village!
|
||||
- Fixed village generation caps not applying for berry farms. You were not meant to see more than two berry farms per village! This has been broken for ages but no one noticed... Hmmm... We could just remove it from here...
|
||||
- Fixed the Ice Face ability activation being displayed incorrectly in a battle.
|
||||
- Fixed edge case of F1 and R locking you in battle with no R functionality (requiring ESC to get out).
|
||||
- Fixed Poké Ball render orientation in battle interface when capturing.
|
||||
- Fixed sync issues with recently learned moves where their PP would not go down until you logout and in.
|
||||
- Fixed some Pokémon photos in display cases flashing if the Pokémon has gender differences.
|
||||
- Fixed Bidoof and Jigglypuff sleep animations stopping after some time, causing them to be 'asleep' while T-posing menacingly.
|
||||
- Fixed entities not changing poses unless you're looking at them.
|
||||
- Fixed cries not playing on send-out if the Pokémon is off-screen.
|
||||
- Fixed text row selection highlight in battle log.
|
||||
- Fixed top black border rendering in scroll interfaces in summary UI.
|
||||
- Fixed aspect tracking for Advancements.
|
||||
- Fixed illusion not copying aspects or caught ball.
|
||||
- Prevent summary stats tab from making sounds when clicking on an already open tab.
|
||||
- Fixed display case not being able to be fed items from underneath.
|
||||
- Fixed Pokémon battling in water continuously sinking to the bottom.
|
||||
- Fixed passive healing and wake from faint not disabling when their config values are set to 0.
|
||||
- Fixed an issue where rebinding the R key to a mouse button prevented players from closing the battle interface using that button.
|
||||
|
||||
### Developer
|
||||
- `SpawnCause` is now an implementation of `SpawningInfluence`.
|
||||
- Many types related to `Pokemon` including itself now have dedicated `Codec`, please migrate to them from the NBT/JSON/PacketBuffer write/read methods.
|
||||
- `TeraTypes` now implements `Iterable`.
|
||||
- `PokemonBattle` now starts on turn 0.
|
||||
- SpawnCause is now an implementation of SpawningInfluence.
|
||||
- Renamed Pokemon.hp to Pokemon.maxHealth to make it clearer. Backwards compatibility is provided but it is an active deprecation, please migrate away from it.
|
||||
- Many types related to ``Pokemon`` including itself now have dedicated ``Codec``, please migrate to them from the NBT/JSON/PacketBuffer write/read methods.
|
||||
- Serialization of PokemonStores and Pokemon themselves now require registry access, which will break some sidemods. This sucked for us more than it will suck for you! If you have a world or player instance, you can get it from there.
|
||||
- ``TeraTypes`` now implements ``Iterable``.
|
||||
- PokemonBattle now starts on turn 0.
|
||||
- Added `forcedAspects` to Pokémon to make it easier to easily add basic aspects to a Pokémon in a way that persists. We already know that everyone is going to overuse this.
|
||||
- Made the `Pokemon.aspects` setter private. This could technically break side-mods but if you are affected by this then you were using it wrong! Use `Pokemon.forcedAspects` to fix it.
|
||||
- Cobblemon's main logger is properly static now.
|
||||
- PokemonEntity.enablePoseTypeRecalculation can be used to disable automatic pose type recalculation.
|
||||
- `PokemonEntity.enablePoseTypeRecalculation` can be used to disable automatic pose type recalculation.
|
||||
- Added CollectEggEvent and HatchEggEvent for compatibility usage.
|
||||
- Added events for:
|
||||
- Showdown Instructions
|
||||
@@ -423,22 +447,43 @@
|
||||
- Pokémon Heal (with context)
|
||||
- Added HealingSource, an interface applied to all sources of healing from player actions, for easier tracking of healing sources.
|
||||
- Move Change
|
||||
- Shiny Chance Calculation Event (with player context)
|
||||
- Dex Information Changed (Pre and Post); this event is fired when the Pokédex is updated with new information.
|
||||
- Rebuilt large swaths of the model animation code to simplify it.
|
||||
- Renamed a bunch of things from %Poseable% to %Posable% because spelling.
|
||||
- Renamed StatelessAnimation to PoseAnimation.
|
||||
- Renamed StatefulAnimation to ActiveAnimation.
|
||||
- Documented the animation system.
|
||||
- Allowed for SpawnSnowstormEntityParticleHandler to handle non-posable entities (due to being non-posable, locators are useless - to set the offset use the settings in the particle itself).
|
||||
- Added property chaining support for duplicate CustomPokemonPropertyType elements.
|
||||
|
||||
### Data Pack & Resource Pack Creators
|
||||
- Added experimental `flow` datapack directory for handling events using MoLang event handlers.
|
||||
- Added support for "shedders" similar to Shedinja's evolution logic.
|
||||
- Fixed the placeholder `WingFlapIdle` animation so the wings are not rotating opposite to each other.
|
||||
- 'player' type dialogue faces can now be explicitly stated so that NPC mods that use fake players can show in dialogue portraits.
|
||||
- Added `isLeftSide` field for dialogue faces. This determines what side of dialogue box the portrait is on.
|
||||
- "sounds/attacks" folder has been renamed to "sounds/move"
|
||||
- `sounds/attacks` directory has been renamed to `sounds/move`.
|
||||
- Moves sharing generic sounds now have unique sound events, allowing them to be changed with resource packs.
|
||||
- All move sound events have been renamed to "move.NAME.SOURCE" for consistency.
|
||||
- Status moves have been moved out of the 'attacks' folder and split into volatile and nonvolatile.
|
||||
- 'status.badlypoison.actor' sound event has been renamed to "status.toxpoison.actor"
|
||||
- Mulch and berry harvesting sound events have been renamed and moved to their respective folders in "sounds/block."
|
||||
- All sounds related to evolving Pokémon have been moved to the "sounds/evolution" folder.
|
||||
- Sound events for all blocks now start with "block."
|
||||
- All move sound events have been renamed to `move.<NAME>.<SOURCE>` for consistency.
|
||||
- Status moves have been moved out of the `attacks` directory and split into volatile and nonvolatile.
|
||||
- `status.badlypoison.actor` sound event has been renamed to `status.toxpoison.actor`
|
||||
- Mulch and berry harvesting sound events have been renamed and moved to their respective directories in `sounds/block`.
|
||||
- All sounds related to evolving Pokémon have been moved to the `sounds/evolution` directory.
|
||||
- Sound events for all blocks now start with `block`.
|
||||
- Gimmighoul chest and item interaction sounds have been moved to where its cry is.
|
||||
- Unused sound files and sound events have been removed.
|
||||
- Poké Ball sounds are now in their animation files, making them more flexible to edit.
|
||||
- Added MoLang compatibility in the isVisible property for transformed parts.
|
||||
- Added `q.has_aspect('some_aspect')` function to animations, posers, and entity particle effects.
|
||||
- Added support for conditional pose animations.
|
||||
- Added a new universal locator called "top".
|
||||
- Added `eggs_collected` and `eggs_hatched` Advancement triggers.
|
||||
- Added missing `minYaw` configuration to the `q.look()` function for JSON posers.
|
||||
- Some pose condition names have been changed:
|
||||
- `isTouchingWaterOrRain` has been changed to `isInWaterOrRain`
|
||||
- `isSubermegedInWater` has been changed to `isUnderWater`
|
||||
|
||||
|
||||
### Localization
|
||||
- Updated translations for:
|
||||
@@ -1673,6 +1718,7 @@ Thank you so much to all of our community translators that bring the mod to the
|
||||
- Lowered spawn rate of Tauros.
|
||||
- Sableye now spawns near gem ores as well as amethyst.
|
||||
- Added evolution stones and items to item tags
|
||||
- Pokémon now play their cry animations when clicked on the starter selection screen.
|
||||
|
||||
### Fixes
|
||||
- Fixed catch rates being entirely too difficult.
|
||||
|
||||
@@ -45,21 +45,4 @@ public class ChunkStorageMixin {
|
||||
int curVersion = vanillaFixed.contains(VERSION_KEY) ? vanillaFixed.getInt(VERSION_KEY) : 0;
|
||||
return DataFixTypes.CHUNK.update(CobblemonSchemas.getDATA_FIXER(), vanillaFixed, curVersion, CobblemonSchemas.DATA_VERSION);
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "upgradeChunkTag(Lnet/minecraft/resources/ResourceKey;Ljava/util/function/Supplier;Lnet/minecraft/nbt/CompoundTag;Ljava/util/Optional;)Lnet/minecraft/nbt/CompoundTag;",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/nbt/NbtUtils;addCurrentDataVersion(Lnet/minecraft/nbt/CompoundTag;)Lnet/minecraft/nbt/CompoundTag;"
|
||||
)
|
||||
)
|
||||
public void cobblemon$addChunkVersion(
|
||||
ResourceKey<Level> resourceKey,
|
||||
Supplier<DimensionDataStorage> supplier,
|
||||
CompoundTag compoundTag,
|
||||
Optional<ResourceKey<MapCodec<? extends ChunkGenerator>>> optional,
|
||||
CallbackInfoReturnable<CompoundTag> cir
|
||||
) {
|
||||
compoundTag.put(VERSION_KEY, IntTag.valueOf(CobblemonSchemas.DATA_VERSION));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.entity.fishing.PokeRodFishingBobberEntity;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.renderer.item.ItemProperties;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
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(ItemProperties.class)
|
||||
public class ItemPropertiesMixin {
|
||||
|
||||
// Mixins into the fishing rod's register() lambda expression
|
||||
// Without this, the fishing rod item model appears cast when using a PokeRod in the other hand
|
||||
@Inject(method = "method_27883", at = @At(value = "RETURN", ordinal = 1), cancellable = true)
|
||||
private static void cobblemon$preventRodMixup(ItemStack itemStack, ClientLevel clientLevel, LivingEntity livingEntity, int i, CallbackInfoReturnable<Float> cir) {
|
||||
if (!(livingEntity instanceof Player) || ((Player) livingEntity).fishing instanceof PokeRodFishingBobberEntity) {
|
||||
cir.setReturnValue(0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon;
|
||||
import com.cobblemon.mod.common.ModAPI;
|
||||
import com.cobblemon.mod.common.client.CobblemonBakingOverrides;
|
||||
import com.cobblemon.mod.common.item.PokeBallItem;
|
||||
import com.cobblemon.mod.common.item.PokedexItem;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
@@ -20,7 +19,9 @@ import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.entity.ItemRenderer;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemDisplayContext;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
@@ -52,25 +53,33 @@ public abstract class ItemRendererMixin {
|
||||
)
|
||||
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;
|
||||
if (!shouldBe2d && stack.getItem() instanceof PokedexItem) {
|
||||
BakedModel replacementModel = CobblemonBakingOverrides.INSTANCE.getPokedexOverride(((PokedexItem) stack.getItem()).getType()).getModel();
|
||||
if (!cobblemon$isSameModel(model, replacementModel)) {
|
||||
ci.cancel();
|
||||
render(stack, renderMode, leftHanded, matrices, multiBufferSource, light, overlay, replacementModel);
|
||||
}
|
||||
} else if (shouldBe2d && stack.getItem() instanceof PokeBallItem pokeBallItem) {
|
||||
BakedModel replacementModel = this.itemModelShaper.getModelManager().getModel(new ModelResourceLocation(pokeBallItem.getPokeBall().getModel2d(), "inventory"));
|
||||
if (!cobblemon$isSameModel(model, replacementModel)) {
|
||||
ci.cancel();
|
||||
render(stack, renderMode, leftHanded, matrices, multiBufferSource, light, overlay, replacementModel);
|
||||
if (shouldBe2d) {
|
||||
ResourceLocation resourceLocation = null;
|
||||
if (stack.getItem() instanceof PokeBallItem pokeBallItem) resourceLocation = pokeBallItem.getPokeBall().getModel2d();
|
||||
else if (stack.getItem() instanceof PokedexItem pokedexItem) resourceLocation = pokedexItem.getType().getItemSpritePath();
|
||||
|
||||
if (resourceLocation != null) {
|
||||
BakedModel replacementModel = this.itemModelShaper.getModelManager().getModel(new ModelResourceLocation(resourceLocation, "inventory"));
|
||||
if (!cobblemon$isSameModel(model, replacementModel)) {
|
||||
ci.cancel();
|
||||
render(stack, renderMode, leftHanded, matrices, multiBufferSource, light, overlay, replacementModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "getModel", at = @At("HEAD"), cancellable = true)
|
||||
private void cobblemon$bakePokeballModel(ItemStack stack, Level world, LivingEntity entity, int seed, CallbackInfoReturnable<BakedModel> cir) {
|
||||
if (stack.getItem() instanceof PokeBallItem pokeBallItem) {
|
||||
BakedModel model = this.itemModelShaper.getModelManager().getModel(new ModelResourceLocation(pokeBallItem.getPokeBall().getModel3d(), MODEL_PATH));
|
||||
private void cobblemon$getItemModel(ItemStack stack, Level world, LivingEntity entity, int seed, CallbackInfoReturnable<BakedModel> cir) {
|
||||
ResourceLocation resourceLocation = null;
|
||||
if (stack.getItem() instanceof PokeBallItem pokeBallItem) resourceLocation = pokeBallItem.getPokeBall().getModel3d();
|
||||
else if (stack.getItem() instanceof PokedexItem pokedexItem) {
|
||||
boolean isScanModel = entity instanceof Player && entity.getUseItem() == stack && entity.isUsingItem();
|
||||
boolean canOpenScreen = entity != null && ((entity.getOffhandItem() == stack && !(entity.getMainHandItem().getItem() instanceof PokedexItem)) || entity.getMainHandItem() == stack);
|
||||
resourceLocation = pokedexItem.getType().getItemModelPath(isScanModel ? "scanning" : (canOpenScreen ? null : "off"));
|
||||
}
|
||||
|
||||
if (resourceLocation != null) {
|
||||
BakedModel model = this.itemModelShaper.getModelManager().getModel(new ModelResourceLocation(resourceLocation, MODEL_PATH));
|
||||
ClientLevel clientWorld = world instanceof ClientLevel ? (ClientLevel) world : null;
|
||||
BakedModel overriddenModel = model.getOverrides().resolve(model, stack, clientWorld, entity, seed);
|
||||
cir.setReturnValue(overriddenModel == null ? this.itemModelShaper.getModelManager().getMissingModel() : overriddenModel);
|
||||
|
||||
@@ -52,10 +52,11 @@ public abstract class LecternBlockMixin {
|
||||
|
||||
if (lecternEntity instanceof LecternBlockEntity) {
|
||||
((LecternBlockEntity) lecternEntity).setItemStack((pokedexItemStack));
|
||||
world.playSound(null, blockPos, CobblemonSounds.POKEDEX_CLOSE, SoundSource.BLOCKS, 0.5F, 1.25F);
|
||||
cir.setReturnValue(ItemInteractionResult.SUCCESS);
|
||||
} else {
|
||||
cir.setReturnValue(itemStack.isEmpty() && interactionHand == InteractionHand.MAIN_HAND ? ItemInteractionResult.SKIP_DEFAULT_BLOCK_INTERACTION : ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION);
|
||||
}
|
||||
|
||||
world.playSound(null, blockPos, CobblemonSounds.POKEDEX_CLOSE, SoundSource.BLOCKS, 0.5F, 1.25F);
|
||||
cir.setReturnValue(itemStack.isEmpty() && interactionHand == InteractionHand.MAIN_HAND ? ItemInteractionResult.SKIP_DEFAULT_BLOCK_INTERACTION : ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ public abstract class MinecraftDedicatedServerMixin implements ServerInterface {
|
||||
|
||||
@Inject(method = "shouldDiscardEntity", at = @At(value = "HEAD"), cancellable = true)
|
||||
public void cobblemon$allowPokemonSpawns(Entity entity, CallbackInfoReturnable<Boolean> callback) {
|
||||
if(entity instanceof PokemonEntity) {
|
||||
if (entity instanceof PokemonEntity) {
|
||||
callback.setReturnValue(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.mixin;
|
||||
|
||||
import static com.cobblemon.mod.common.datafixer.CobblemonSchemas.VERSION_KEY;
|
||||
|
||||
import com.cobblemon.mod.common.datafixer.CobblemonSchemas;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtUtils;
|
||||
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(NbtUtils.class)
|
||||
public class NbtUtilsMixin {
|
||||
@Inject(
|
||||
method = "addCurrentDataVersion(Lnet/minecraft/nbt/CompoundTag;)Lnet/minecraft/nbt/CompoundTag;",
|
||||
at = @At("TAIL")
|
||||
)
|
||||
private static void addCobblemonDataVersion(
|
||||
CompoundTag tag,
|
||||
CallbackInfoReturnable<CompoundTag> cir
|
||||
) {
|
||||
tag.putInt(VERSION_KEY, CobblemonSchemas.DATA_VERSION);
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.client.CobblemonClient;
|
||||
import com.cobblemon.mod.common.client.render.ClientPlayerIcon;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
@@ -20,11 +20,9 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(PlayerRenderer.class)
|
||||
public class PlayerRendererMixin {
|
||||
@Inject(
|
||||
method = "render(Lnet/minecraft/client/player/AbstractClientPlayer;FFLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;I)V",
|
||||
at = @At(value = "TAIL")
|
||||
)
|
||||
|
||||
@Inject(method = "render", at = @At(value = "TAIL"))
|
||||
public void renderPlayer(AbstractClientPlayer player, float f, float g, PoseStack poseStack, MultiBufferSource multiBufferSource, int i, CallbackInfo ci) {
|
||||
CobblemonClient.INSTANCE.getRequests().onRenderPlayer(player, g);
|
||||
ClientPlayerIcon.Companion.onRenderPlayer(player);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,39 +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.client;
|
||||
|
||||
import com.cobblemon.mod.common.client.CobblemonClient;
|
||||
import net.minecraft.client.DeltaTracker;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.Gui;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
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.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
/**
|
||||
* Mixin to give us a hook to render the PartyOverlay below the Chat
|
||||
*
|
||||
* @author Qu
|
||||
* @since 2022-02-22
|
||||
*/
|
||||
@Mixin(Gui.class)
|
||||
public class GuiMixin {
|
||||
//This injects into the actual BooleanSupplier lambda for the "this.layers.add(layeredDraw, () -> !minecraft.options.hideGui)" call
|
||||
// sus af. You have to get the method name by looking at the bytecode of the class. Can do in intellij By clicking View -> Show Bytecode (top left)
|
||||
@Inject(method = "method_55797", at = @At("HEAD"), cancellable = true)
|
||||
private static void cobblemon$dontRenderUiInPokedex(Minecraft minecraft, CallbackInfoReturnable<Boolean> cir) {
|
||||
if (CobblemonClient.INSTANCE.getPokedexUsageContext().getScanningGuiOpen() && minecraft.options.getCameraType().isFirstPerson()) {
|
||||
cir.setReturnValue(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import com.cobblemon.mod.common.api.drop.DropEntry
|
||||
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.interaction.RequestManager
|
||||
import com.cobblemon.mod.common.api.permission.PermissionValidator
|
||||
import com.cobblemon.mod.common.api.pokeball.catching.calculators.CaptureCalculator
|
||||
import com.cobblemon.mod.common.api.pokeball.catching.calculators.CaptureCalculators
|
||||
@@ -43,6 +44,7 @@ import com.cobblemon.mod.common.api.spawning.prospecting.SpawningProspector
|
||||
import com.cobblemon.mod.common.api.starter.StarterHandler
|
||||
import com.cobblemon.mod.common.api.storage.PokemonStoreManager
|
||||
import com.cobblemon.mod.common.api.storage.adapter.conversions.ReforgedConversion
|
||||
import com.cobblemon.mod.common.api.storage.adapter.database.MongoDBStoreAdapter
|
||||
import com.cobblemon.mod.common.api.storage.adapter.flatfile.FileStoreAdapter
|
||||
import com.cobblemon.mod.common.api.storage.adapter.flatfile.JSONStoreAdapter
|
||||
import com.cobblemon.mod.common.api.storage.adapter.flatfile.NBTStoreAdapter
|
||||
@@ -52,8 +54,10 @@ import com.cobblemon.mod.common.api.storage.pc.PCStore
|
||||
import com.cobblemon.mod.common.api.storage.pc.link.PCLinkManager
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreManager
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
|
||||
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.factory.CachedPlayerDataStoreFactory
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonEntityTypeTags
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
@@ -64,22 +68,14 @@ import com.cobblemon.mod.common.battles.BattleSide
|
||||
import com.cobblemon.mod.common.battles.ShowdownThread
|
||||
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.command.argument.DexArgumentType
|
||||
import com.cobblemon.mod.common.command.argument.DialogueArgumentType
|
||||
import com.cobblemon.mod.common.command.argument.FormArgumentType
|
||||
import com.cobblemon.mod.common.command.argument.MoveArgumentType
|
||||
import com.cobblemon.mod.common.command.argument.NPCClassArgumentType
|
||||
import com.cobblemon.mod.common.command.argument.PartySlotArgumentType
|
||||
import com.cobblemon.mod.common.command.argument.PokemonPropertiesArgumentType
|
||||
import com.cobblemon.mod.common.command.argument.PokemonStoreArgumentType
|
||||
import com.cobblemon.mod.common.command.argument.SpawnBucketArgumentType
|
||||
import com.cobblemon.mod.common.command.argument.SpeciesArgumentType
|
||||
import com.cobblemon.mod.common.command.argument.*
|
||||
import com.cobblemon.mod.common.config.CobblemonConfig
|
||||
import com.cobblemon.mod.common.config.LastChangedVersion
|
||||
import com.cobblemon.mod.common.config.constraint.IntConstraint
|
||||
import com.cobblemon.mod.common.config.starter.StarterConfig
|
||||
import com.cobblemon.mod.common.data.CobblemonDataProvider
|
||||
import com.cobblemon.mod.common.events.AdvancementHandler
|
||||
import com.cobblemon.mod.common.events.FlowHandler
|
||||
import com.cobblemon.mod.common.events.PokedexHandler
|
||||
import com.cobblemon.mod.common.events.ServerTickHandler
|
||||
import com.cobblemon.mod.common.net.messages.client.settings.ServerSettingsPacket
|
||||
@@ -92,26 +88,33 @@ import com.cobblemon.mod.common.pokemon.evolution.variants.BlockClickEvolution
|
||||
import com.cobblemon.mod.common.pokemon.feature.TagSeasonResolver
|
||||
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
|
||||
import com.cobblemon.mod.common.pokemon.properties.AspectPropertyType
|
||||
import com.cobblemon.mod.common.pokemon.properties.BattleCloneProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.FreezeFrameProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.HiddenAbilityPropertyType
|
||||
import com.cobblemon.mod.common.pokemon.properties.NoAIProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.UnaspectPropertyType
|
||||
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.BattleCloneProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.tags.PokemonFlagProperty
|
||||
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
|
||||
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
|
||||
import com.cobblemon.mod.common.trade.TradeManager
|
||||
import com.cobblemon.mod.common.util.*
|
||||
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.server
|
||||
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
|
||||
import com.mongodb.ConnectionString
|
||||
import com.mongodb.MongoClientSettings
|
||||
import com.mongodb.client.MongoClient
|
||||
import com.mongodb.client.MongoClients
|
||||
import java.io.File
|
||||
import java.io.FileReader
|
||||
import java.io.FileWriter
|
||||
import java.io.PrintWriter
|
||||
import java.util.*
|
||||
import java.util.UUID
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.properties.Delegates
|
||||
import kotlin.reflect.full.memberProperties
|
||||
@@ -210,7 +213,7 @@ object Cobblemon {
|
||||
BattleRegistry.onPlayerDisconnect(it.player)
|
||||
storage.onPlayerDisconnect(it.player)
|
||||
playerDataManager.onPlayerDisconnect(it.player)
|
||||
TradeManager.onLogoff(it.player)
|
||||
RequestManager.onLogoff(it.player)
|
||||
}
|
||||
PlatformEvents.PLAYER_DEATH.subscribe {
|
||||
PCLinkManager.removeLink(it.player.uuid)
|
||||
@@ -282,6 +285,10 @@ object Cobblemon {
|
||||
CustomPokemonProperty.register(FreezeFrameProperty)
|
||||
CustomPokemonProperty.register(NoAIProperty)
|
||||
|
||||
CobblemonEvents.POKEMON_PROPERTY_INITIALISED.emit(Unit)
|
||||
|
||||
FlowHandler.setup()
|
||||
|
||||
ifDedicatedServer {
|
||||
isDedicatedServer = true
|
||||
}
|
||||
@@ -322,9 +329,7 @@ object Cobblemon {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
"mongodb" -> {
|
||||
/*
|
||||
try {
|
||||
Class.forName("com.mongodb.client.MongoClient")
|
||||
|
||||
@@ -332,18 +337,22 @@ object Cobblemon {
|
||||
.applyConnectionString(ConnectionString(config.mongoDBConnectionString))
|
||||
.build()
|
||||
mongoClient = MongoClients.create(mongoClientSettings)
|
||||
val mongoFactory = MongoPlayerDataStoreFactory(mongoClient, config.mongoDBDatabaseName)
|
||||
playerData.setFactory(mongoFactory, PlayerInstancedDataStoreType.GENERAL)
|
||||
val generalMongoFactory = CachedPlayerDataStoreFactory(PlayerDataMongoBackend(mongoClient, config.mongoDBDatabaseName, "PlayerDataCollection"))
|
||||
generalMongoFactory.setup(server)
|
||||
|
||||
val pokedexMongoFactory = CachedPlayerDataStoreFactory(DexDataMongoBackend(mongoClient, config.mongoDBDatabaseName, "PokeDexCollection"))
|
||||
pokedexMongoFactory.setup(server)
|
||||
|
||||
playerDataManager.setFactory(generalMongoFactory, PlayerInstancedDataStoreTypes.GENERAL)
|
||||
playerDataManager.setFactory(pokedexMongoFactory, PlayerInstancedDataStoreTypes.POKEDEX)
|
||||
MongoDBStoreAdapter(mongoClient, config.mongoDBDatabaseName)
|
||||
} catch (e: ClassNotFoundException) {
|
||||
LOGGER.error("MongoDB driver not found.")
|
||||
throw e
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
else -> throw IllegalArgumentException("Unsupported storageFormat: ${config.storageFormat}")
|
||||
}
|
||||
@@ -375,11 +384,19 @@ object Cobblemon {
|
||||
BattleFormat.GEN_9_SINGLES,
|
||||
BattleSide(PokemonBattleActor(UUID.randomUUID(), BattlePokemon(Pokemon().initialize()), -1F)),
|
||||
BattleSide(PokemonBattleActor(UUID.randomUUID(), BattlePokemon(Pokemon().initialize()), -1F)),
|
||||
true
|
||||
).ifSuccessful { it.mute = true }
|
||||
false
|
||||
).ifSuccessful {
|
||||
it.mute = true
|
||||
}.ifErrored {
|
||||
val errors = it.errors.joinToString("\n") { it.javaClass.name }
|
||||
LOGGER.error("Failed to start dummy Showdown battle: $errors")
|
||||
}
|
||||
}
|
||||
|
||||
registerEventHandlers()
|
||||
|
||||
CobblemonEvents.COBBLEMON_INITIALISED.emit(Unit)
|
||||
|
||||
}
|
||||
|
||||
fun registerEventHandlers() {
|
||||
|
||||
@@ -12,9 +12,8 @@ import net.minecraft.world.entity.schedule.Activity
|
||||
|
||||
object CobblemonActivities {
|
||||
val activities = mutableListOf<Activity>()
|
||||
val BATTLING_ACTIVITY = register(Activity("pokemon_battling"))
|
||||
val NPC_BATTLING = register(Activity("npc_battling"))
|
||||
val NPC_ACTION_EFFECT = register(Activity("npc_action_effect"))
|
||||
val BATTLING = register(Activity("battling"))
|
||||
val ACTION_EFFECT = register(Activity("action_effect"))
|
||||
val NPC_CHATTING = register(Activity("npc_chatting"))
|
||||
|
||||
fun register(activity: Activity): Activity {
|
||||
|
||||
@@ -121,10 +121,17 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
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))
|
||||
|
||||
@JvmField //if you wonder why we don't copy of OAK_WALL_SIGN, then I can tell you its because it uses Properties#dropsLike to point to the SIGN loot table, but we cant use that in our setup due to a deeper rooted issue, so falling back to this and writing dedicated (duplicated) loot tables
|
||||
val APRICORN_WALL_SIGN = this.create(
|
||||
"apricorn_wall_sign",
|
||||
CobblemonWallSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_SIGN), APRICORN_WOOD_TYPE)
|
||||
)
|
||||
@JvmField
|
||||
val APRICORN_WALL_SIGN = this.create("apricorn_wall_sign", CobblemonWallSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_WALL_SIGN), APRICORN_WOOD_TYPE))
|
||||
@JvmField
|
||||
val APRICORN_HANGING_SIGN = this.create("apricorn_hanging_sign", CobblemonHangingSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_WALL_HANGING_SIGN), APRICORN_WOOD_TYPE))
|
||||
val APRICORN_HANGING_SIGN = this.create(
|
||||
"apricorn_hanging_sign",
|
||||
CobblemonHangingSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_HANGING_SIGN), APRICORN_WOOD_TYPE)
|
||||
)
|
||||
@JvmField
|
||||
val APRICORN_WALL_HANGING_SIGN = this.create("apricorn_wall_hanging_sign", CobblemonWallHangingSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_HANGING_SIGN), APRICORN_WOOD_TYPE))
|
||||
@JvmField
|
||||
|
||||
@@ -52,9 +52,11 @@ object CobblemonCommands {
|
||||
BedrockParticleCommand.register(dispatcher)
|
||||
OpenDialogueCommand.register(dispatcher)
|
||||
NPCEditCommand.register(dispatcher)
|
||||
NPCDeleteCommand.register(dispatcher)
|
||||
AbandonMultiTeam.register(dispatcher)
|
||||
FreezePokemonCommand.register(dispatcher)
|
||||
ApplyPlayerTextureCommand.register(dispatcher)
|
||||
RunMolangScriptCommand.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
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.data.DataRegistry
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
|
||||
import com.cobblemon.mod.common.net.messages.client.data.FlowRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.endsWith
|
||||
import java.util.concurrent.ExecutionException
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
import net.minecraft.server.packs.resources.ResourceManager
|
||||
|
||||
/**
|
||||
* Holds all the flows that are loaded from the server's data packs. A flow is foldered under the event identifier,
|
||||
* which can be Cobblemon's namespace but can also be minecraft or any other namespace someone has added hooks for.
|
||||
* The value of the map is a list of MoLang scripts that will execute when that event occurs.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 24th, 2024
|
||||
*/
|
||||
object CobblemonFlows : DataRegistry {
|
||||
override val id = cobblemonResource("flows")
|
||||
override val observable = SimpleObservable<CobblemonFlows>()
|
||||
override val type = PackType.SERVER_DATA
|
||||
override fun sync(player: ServerPlayer) {
|
||||
player.sendPacket(FlowRegistrySyncPacket(clientFlows.entries))
|
||||
}
|
||||
|
||||
val runtime by lazy { MoLangRuntime().setup() } // Lazy for if someone adds to generalFunctions in MoLangFunctions
|
||||
|
||||
val clientFlows = hashMapOf<ResourceLocation, MutableList<ExpressionLike>>()
|
||||
val flows = hashMapOf<ResourceLocation, MutableList<ExpressionLike>>()
|
||||
|
||||
override fun reload(manager: ResourceManager) {
|
||||
clientFlows.clear()
|
||||
flows.clear()
|
||||
|
||||
val unsortedFlows = mutableMapOf<ResourceLocation, MutableList<Pair<String, ExpressionLike>>>()
|
||||
val folderBeforeNameRegex = ".*\\/([^\\/]+)\\/[^\\/]+\$".toRegex()
|
||||
manager.listResources("flows") { path -> path.endsWith(CobblemonScripts.MOLANG_EXTENSION) }.forEach { (identifier, resource) ->
|
||||
resource.openAsReader().use { stream ->
|
||||
stream.buffered().use { reader ->
|
||||
try {
|
||||
val expression = reader.readText().asExpressionLike()
|
||||
|
||||
val event = folderBeforeNameRegex.find(identifier.path)?.groupValues?.get(1)
|
||||
?: throw IllegalArgumentException("Invalid flow path: $identifier. Should have a folder structure that includes the name of the event being flowed.")
|
||||
|
||||
val flowKey = ResourceLocation.fromNamespaceAndPath(identifier.namespace, event)
|
||||
unsortedFlows.putIfAbsent(flowKey, mutableListOf())
|
||||
unsortedFlows[flowKey]!!.add(identifier.path to expression)
|
||||
} catch (exception: Exception) {
|
||||
throw ExecutionException("Error loading MoLang script for flow: $identifier", exception)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsortedFlows.forEach { (key, value) ->
|
||||
value.sortBy { it.first }
|
||||
if (key.path.startsWith("flows/client/")) {
|
||||
clientFlows.getOrPut(key) { mutableListOf() }.addAll(value.map { it.second })
|
||||
} else {
|
||||
flows.getOrPut(key) { mutableListOf() }.addAll(value.map { it.second })
|
||||
}
|
||||
}
|
||||
|
||||
Cobblemon.LOGGER.info("Loaded ${flows.size} flows and ${clientFlows.size} client flows")
|
||||
observable.emit(this)
|
||||
}
|
||||
|
||||
fun run(eventResourceLocation: ResourceLocation, context: Map<String, MoValue>, cancelable: Cancelable? = null) {
|
||||
if (cancelable == null) {
|
||||
runtime.environment.query.functions.remove("cancel")
|
||||
} else {
|
||||
runtime.environment.query.addFunction("cancel") { cancelable }
|
||||
}
|
||||
|
||||
flows[eventResourceLocation]?.forEach {
|
||||
if (cancelable != null && cancelable.isCanceled) {
|
||||
return
|
||||
}
|
||||
it.resolve(runtime, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,25 +8,22 @@
|
||||
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.mojang.brigadier.arguments.ArgumentType
|
||||
import kotlin.reflect.KClass
|
||||
import net.minecraft.commands.synchronization.ArgumentTypeInfo
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceKey
|
||||
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.server.packs.resources.PreparableReloadListener
|
||||
import net.minecraft.server.packs.resources.ResourceManager
|
||||
import net.minecraft.tags.TagKey
|
||||
import net.minecraft.world.level.GameRules
|
||||
import net.minecraft.world.level.ItemLike
|
||||
import net.minecraft.world.level.biome.Biome
|
||||
import net.minecraft.world.level.levelgen.GenerationStep
|
||||
import net.minecraft.world.level.levelgen.placement.PlacedFeature
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
interface CobblemonImplementation {
|
||||
val modAPI: ModAPI
|
||||
@@ -171,14 +168,6 @@ interface CobblemonImplementation {
|
||||
*/
|
||||
fun server(): MinecraftServer?
|
||||
|
||||
/**
|
||||
* Handles platform independent reloading of a [JsonDataRegistry].
|
||||
*
|
||||
* @param registry The [JsonDataRegistry] to reload.
|
||||
* @param manager The [ResourceManager] to reload from.
|
||||
*/
|
||||
fun <T> reloadJsonRegistry(registry: JsonDataRegistry<T>, manager: ResourceManager): HashMap<ResourceLocation, T>
|
||||
|
||||
/**
|
||||
* Registers an item to the [ComposterBlock].
|
||||
*
|
||||
@@ -186,16 +175,6 @@ interface CobblemonImplementation {
|
||||
* @param chance The chance % of increasing the composter level, 0 to 1 expected.
|
||||
*/
|
||||
fun registerCompostable(item: ItemLike, chance: Float)
|
||||
|
||||
/**
|
||||
* Registers a builtin resource pack.
|
||||
*
|
||||
* @param id The unique [ResourceLocation] of this pack.
|
||||
* @param title The title displayed in the resource pack GUI, the description is still a part of the pack metadata.
|
||||
* @param activationBehaviour The [ResourcePackActivationBehaviour] for this pack.
|
||||
*/
|
||||
fun registerBuiltinResourcePack(id: ResourceLocation, title: Component, activationBehaviour: ResourcePackActivationBehaviour)
|
||||
|
||||
}
|
||||
|
||||
enum class ResourcePackActivationBehaviour {
|
||||
|
||||
@@ -19,7 +19,7 @@ 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.PokedexTypes
|
||||
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
|
||||
@@ -54,6 +54,9 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
override val registry: Registry<Item> = BuiltInRegistries.ITEM
|
||||
override val resourceKey: ResourceKey<Registry<Item>> = Registries.ITEM
|
||||
|
||||
@JvmField
|
||||
val NPC_EDITOR = create("npc_editor", CobblemonItem(Item.Properties().stacksTo(1)))
|
||||
|
||||
@JvmField
|
||||
val pokeBalls = mutableListOf<PokeBallItem>()
|
||||
@JvmField
|
||||
@@ -155,19 +158,19 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
|
||||
val pokedexes = mutableListOf<PokedexItem>()
|
||||
@JvmField
|
||||
val POKEDEX_BLACK = pokedexItem(PokedexTypes.BLACK)
|
||||
val POKEDEX_RED = pokedexItem(PokedexType.RED)
|
||||
@JvmField
|
||||
val POKEDEX_BLUE = pokedexItem(PokedexTypes.BLUE)
|
||||
val POKEDEX_YELLOW = pokedexItem(PokedexType.YELLOW)
|
||||
@JvmField
|
||||
val POKEDEX_GREEN = pokedexItem(PokedexTypes.GREEN)
|
||||
val POKEDEX_GREEN = pokedexItem(PokedexType.GREEN)
|
||||
@JvmField
|
||||
val POKEDEX_PINK = pokedexItem(PokedexTypes.PINK)
|
||||
val POKEDEX_BLUE = pokedexItem(PokedexType.BLUE)
|
||||
@JvmField
|
||||
val POKEDEX_RED = pokedexItem(PokedexTypes.RED)
|
||||
val POKEDEX_PINK = pokedexItem(PokedexType.PINK)
|
||||
@JvmField
|
||||
val POKEDEX_WHITE = pokedexItem(PokedexTypes.WHITE)
|
||||
val POKEDEX_BLACK = pokedexItem(PokedexType.BLACK)
|
||||
@JvmField
|
||||
val POKEDEX_YELLOW = pokedexItem(PokedexTypes.YELLOW)
|
||||
val POKEDEX_WHITE = pokedexItem(PokedexType.WHITE)
|
||||
|
||||
@JvmField
|
||||
val VIVICHOKE = compostableItem("vivichoke")
|
||||
@@ -1242,13 +1245,13 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
private fun candyItem(name: String, calculator: CandyItem.Calculator): CandyItem = this.create(name, CandyItem(calculator))
|
||||
|
||||
private fun pokerodItem(pokeRodId: ResourceLocation): PokerodItem {
|
||||
val settings = Item.Properties().stacksTo(1).durability(Items.FISHING_ROD.components().get(DataComponents.MAX_DAMAGE)!!)
|
||||
val settings = Item.Properties().stacksTo(1).durability(256)
|
||||
val item = create(pokeRodId.path, PokerodItem(pokeRodId, settings))
|
||||
pokeRods.add(item)
|
||||
return item
|
||||
}
|
||||
|
||||
private fun pokedexItem(type: PokedexTypes): PokedexItem {
|
||||
private fun pokedexItem(type: PokedexType): PokedexItem {
|
||||
val item = create("pokedex_${type.name.lowercase()}", PokedexItem(type))
|
||||
pokedexes.add(item)
|
||||
return item
|
||||
|
||||
@@ -54,6 +54,7 @@ import com.cobblemon.mod.common.client.net.toast.ToastPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.*
|
||||
import com.cobblemon.mod.common.net.PacketRegisterInfo
|
||||
import com.cobblemon.mod.common.net.messages.client.PlayerInteractOptionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.*
|
||||
import com.cobblemon.mod.common.net.messages.client.callback.OpenMoveCallbackPacket
|
||||
@@ -74,6 +75,7 @@ import com.cobblemon.mod.common.net.messages.client.pasture.ClosePasturePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pasture.OpenPasturePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pasture.PokemonPasturedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pasture.PokemonUnpasturedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pokedex.ServerConfirmedRegisterPacket
|
||||
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
|
||||
@@ -85,7 +87,6 @@ import com.cobblemon.mod.common.net.messages.client.spawn.SpawnNPCPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnPokeballPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.starter.OpenStarterUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.RemoveClientPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.SwapClientPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.party.InitializePartyPacket
|
||||
@@ -94,13 +95,13 @@ import com.cobblemon.mod.common.net.messages.client.storage.party.SetPartyPokemo
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.party.SetPartyReferencePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.*
|
||||
import com.cobblemon.mod.common.net.messages.client.toast.ToastPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.*
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.InteractPokemonUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.PokedexUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.SummaryUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.*
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.BattleSelectActionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.RemoveSpectatorPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.SpectateBattlePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.*
|
||||
import com.cobblemon.mod.common.net.messages.server.block.AdjustBlockEntityViewerCountPacket
|
||||
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
|
||||
@@ -113,6 +114,7 @@ import com.cobblemon.mod.common.net.messages.server.npc.SaveNPCPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pasture.PasturePokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pasture.UnpastureAllPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pasture.UnpasturePokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.FinishScanningPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.StartScanningPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.interact.InteractPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetNicknamePacket
|
||||
@@ -128,10 +130,10 @@ import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePartyPokemonT
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.ReleasePCPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.SwapPCPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.UnlinkPlayerFromPCPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.*
|
||||
import com.cobblemon.mod.common.net.serverhandling.*
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.BattleSelectActionsHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.RemoveSpectatorHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.SpectateBattleHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.*
|
||||
import com.cobblemon.mod.common.net.serverhandling.block.AdjustBlockEntityViewerCountHandler
|
||||
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
|
||||
@@ -145,6 +147,8 @@ import com.cobblemon.mod.common.net.serverhandling.npc.SaveNPCHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pasture.PasturePokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pasture.UnpastureAllPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pasture.UnpasturePokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.FinishScanningHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.StartScanningHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.interact.InteractPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetNicknameHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.starter.RequestStarterScreenHandler
|
||||
@@ -162,12 +166,6 @@ import com.cobblemon.mod.common.net.serverhandling.storage.pc.MovePartyPokemonTo
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.pc.ReleasePartyPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.pc.SwapPCPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.pc.UnlinkPlayerFromPCHandler
|
||||
import com.cobblemon.mod.common.net.messages.client.pokedex.ServerConfirmedRegisterPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.*
|
||||
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.FinishScanningPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.*
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.FinishScanningHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.StartScanningHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.trade.*
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
@@ -277,7 +275,6 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(BattleChallengeExpiredPacket.ID, BattleChallengeExpiredPacket::decode, BattleChallengeExpiredHandler))
|
||||
list.add(PacketRegisterInfo(BattleReplacePokemonPacket.ID, BattleReplacePokemonPacket::decode, BattleReplacePokemonHandler))
|
||||
list.add(PacketRegisterInfo(BattleTransformPokemonPacket.ID, BattleTransformPokemonPacket::decode, BattleTransformPokemonHandler))
|
||||
list.add(PacketRegisterInfo(BattleAppendTargetedEntityPacket.ID, BattleAppendTargetedEntityPacket::decode, BattleSetTargetedEntityHandler))
|
||||
|
||||
// MultiBattleTeam Packets
|
||||
list.add(PacketRegisterInfo(TeamRequestNotificationPacket.ID, TeamRequestNotificationPacket::decode, TeamRequestNotificationHandler))
|
||||
@@ -306,6 +303,7 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(PokedexDexSyncPacket.ID, PokedexDexSyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(DexEntrySyncPacket.ID, DexEntrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(FishingBaitRegistrySyncPacket.ID, FishingBaitRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(FlowRegistrySyncPacket.ID, FlowRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
|
||||
// Effects
|
||||
list.add(PacketRegisterInfo(SpawnSnowstormParticlePacket.ID, SpawnSnowstormParticlePacket::decode, SpawnSnowstormParticleHandler))
|
||||
@@ -356,7 +354,7 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(CloseNPCEditorPacket.ID, CloseNPCEditorPacket::decode, CloseNPCEditorHandler))
|
||||
list.add(PacketRegisterInfo(OpenNPCEditorPacket.ID, OpenNPCEditorPacket::decode, OpenNPCEditorHandler))
|
||||
|
||||
//Pokedex scanning
|
||||
// Pokédex scanning
|
||||
list.add(PacketRegisterInfo(ServerConfirmedRegisterPacket.ID, ServerConfirmedRegisterPacket::decode, ServerConfirmedRegisterHandler))
|
||||
return list
|
||||
}
|
||||
@@ -414,7 +412,7 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(OfferTradePacket.ID, OfferTradePacket::decode, OfferTradeHandler))
|
||||
list.add(PacketRegisterInfo(UpdateTradeOfferPacket.ID, UpdateTradeOfferPacket::decode, UpdateTradeOfferHandler))
|
||||
|
||||
// Pokedex scanning
|
||||
// Pokédex scanning
|
||||
list.add(PacketRegisterInfo(StartScanningPacket.ID, StartScanningPacket::decode, StartScanningHandler))
|
||||
list.add(PacketRegisterInfo(FinishScanningPacket.ID, FinishScanningPacket::decode, FinishScanningHandler))
|
||||
|
||||
@@ -423,6 +421,9 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(UnpasturePokemonPacket.ID, UnpasturePokemonPacket::decode, UnpasturePokemonHandler))
|
||||
list.add(PacketRegisterInfo(UnpastureAllPokemonPacket.ID, UnpastureAllPokemonPacket::decode, UnpastureAllPokemonHandler))
|
||||
|
||||
// Block entity
|
||||
list.add(PacketRegisterInfo(AdjustBlockEntityViewerCountPacket.ID, AdjustBlockEntityViewerCountPacket::decode, AdjustBlockEntityViewerCountHandler))
|
||||
|
||||
// Move select packets
|
||||
list.add(PacketRegisterInfo(MoveSelectedPacket.ID, MoveSelectedPacket::decode, MoveSelectedHandler))
|
||||
list.add(PacketRegisterInfo(MoveSelectCancelledPacket.ID, MoveSelectCancelledPacket::decode, MoveSelectCancelledHandler))
|
||||
|
||||
@@ -46,16 +46,6 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
@JvmField
|
||||
val HEALING_MACHINE_ACTIVE = this.create("block.healing_machine.active")
|
||||
|
||||
@JvmField
|
||||
val POKE_BALL_CAPTURE_STARTED = this.create("poke_ball.capture_started")
|
||||
@JvmField
|
||||
val POKE_BALL_CAPTURE_SUCCEEDED = this.create("poke_ball.capture_succeeded")
|
||||
@JvmField
|
||||
val POKE_BALL_SHAKE = this.create("poke_ball.shake")
|
||||
@JvmField
|
||||
val POKE_BALL_OPEN = this.create("poke_ball.open")
|
||||
@JvmField
|
||||
val POKE_BALL_SHINY_OPEN = this.create("poke_ball.shiny_open")
|
||||
@JvmField
|
||||
val POKE_BALL_HIT = this.create("poke_ball.hit")
|
||||
@JvmField
|
||||
@@ -108,6 +98,8 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
@JvmField
|
||||
val PVW_BATTLE = this.create("battle.pvw.default")
|
||||
|
||||
@JvmField
|
||||
val MEDICINE_CANDY_USE = this.create("item.medicine.candy.use")
|
||||
@JvmField
|
||||
val MEDICINE_HERB_USE = this.create("item.medicine.herb.use")
|
||||
@JvmField
|
||||
@@ -266,6 +258,13 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
RELIC_COIN_SACK_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val MOVE_QUICKATTACK_TARGET = this.create("move.quickattack.target")
|
||||
@JvmField
|
||||
val MOVE_PURSUIT_TARGET = this.create("move.pursuit.target")
|
||||
@JvmField
|
||||
val MOVE_PSYCHIC_TARGET = this.create("move.psychic.target")
|
||||
|
||||
@JvmField
|
||||
val IMPACT_NORMAL = this.create("impact.normal")
|
||||
@JvmField
|
||||
|
||||
@@ -77,7 +77,7 @@ object CobblemonTradeOffers {
|
||||
)
|
||||
VillagerProfession.FISHERMAN -> listOf(
|
||||
VillagerTradeOffer(VillagerProfession.FISHERMAN, 5, listOf(
|
||||
VillagerTrades.ItemsForEmeralds(CobblemonItems.POKEROD_SMITHING_TEMPLATE, 12, 3, 30)
|
||||
VillagerTrades.ItemsForEmeralds(CobblemonItems.POKEROD_SMITHING_TEMPLATE, 12, 1, 3, 30)
|
||||
))
|
||||
)
|
||||
else -> emptyList()
|
||||
|
||||
@@ -29,6 +29,8 @@ object CobblemonCriteria : PlatformRegistry<Registry<CriterionTrigger<*>>, Resou
|
||||
|
||||
val CATCH_SHINY_POKEMON = this.create("catch_shiny_pokemon", SimpleCriterionTrigger(CountableCriterion.CODEC))
|
||||
|
||||
val EGG_COLLECT = this.create("eggs_collected", SimpleCriterionTrigger(CountableCriterion.CODEC))
|
||||
|
||||
val EGG_HATCH = this.create("eggs_hatched", SimpleCriterionTrigger(CountableCriterion.CODEC))
|
||||
|
||||
val EVOLVE_POKEMON = this.create("pokemon_evolved", SimpleCriterionTrigger(EvolvePokemonCriterion.CODEC))
|
||||
@@ -53,9 +55,11 @@ object CobblemonCriteria : PlatformRegistry<Registry<CriterionTrigger<*>>, Resou
|
||||
|
||||
val CAST_POKE_ROD = this.create("cast_poke_rod", SimpleCriterionTrigger(CastPokeRodCriterionCondition.CODEC))
|
||||
|
||||
val REEL_IN_POKEMON = this.create("reel_in_pokemon", SimpleCriterionTrigger(ReelInPokemonCriterionCondition.CODEC))
|
||||
|
||||
// Advancement criteria for [grow_tumblestone.json]
|
||||
val PLANT_TUMBLESTONE = this.create("plant_tumblestone", SimpleCriterionTrigger(PlantTumblestoneCriterion.CODEC))
|
||||
|
||||
override val registry = BuiltInRegistries.TRIGGER_TYPES
|
||||
override val resourceKey = Registries.TRIGGER_TYPE
|
||||
}
|
||||
}
|
||||
|
||||
+14
-10
@@ -8,26 +8,30 @@
|
||||
|
||||
package com.cobblemon.mod.common.advancement.criterion
|
||||
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.PrimitiveCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import java.util.Optional
|
||||
import net.minecraft.advancements.critereon.ContextAwarePredicate
|
||||
import net.minecraft.advancements.critereon.EntityPredicate
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
class CastPokeRodContext(val baitId: ResourceLocation)
|
||||
|
||||
class CastPokeRodCriterionCondition(
|
||||
playerCtx: Optional<ContextAwarePredicate>,
|
||||
val hasBait: Boolean
|
||||
): SimpleCriterionCondition<Boolean>(playerCtx) {
|
||||
playerCtx: Optional<ContextAwarePredicate>,
|
||||
val baitId: String
|
||||
): SimpleCriterionCondition<CastPokeRodContext>(playerCtx) {
|
||||
|
||||
companion object {
|
||||
val CODEC: Codec<CastPokeRodCriterionCondition> = RecordCodecBuilder.create { it.group(
|
||||
EntityPredicate.ADVANCEMENT_CODEC.optionalFieldOf("player").forGetter(CastPokeRodCriterionCondition::playerCtx),
|
||||
PrimitiveCodec.BOOL.fieldOf("hasBait").forGetter(CastPokeRodCriterionCondition::hasBait)
|
||||
).apply(it, ::CastPokeRodCriterionCondition) }
|
||||
EntityPredicate.ADVANCEMENT_CODEC.optionalFieldOf("player").forGetter(CastPokeRodCriterionCondition::playerCtx),
|
||||
Codec.STRING.optionalFieldOf("baitId", "empty_bait").forGetter(CastPokeRodCriterionCondition::baitId)
|
||||
).apply(it, { playerCtx, baitId -> CastPokeRodCriterionCondition(playerCtx, baitId.ifEmpty { "empty_bait" }) }) }
|
||||
}
|
||||
|
||||
override fun matches(player: ServerPlayer, context: Boolean): Boolean {
|
||||
return hasBait == context
|
||||
override fun matches(player: ServerPlayer, context: CastPokeRodContext): Boolean {
|
||||
return (context.baitId == this.baitId.asIdentifierDefaultingNamespace() || this.baitId.equals("empty_bait"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -26,7 +26,7 @@ class PartyCheckCriterion(
|
||||
companion object {
|
||||
val CODEC: Codec<PartyCheckCriterion> = RecordCodecBuilder.create { it.group(
|
||||
EntityPredicate.ADVANCEMENT_CODEC.optionalFieldOf("player").forGetter(PartyCheckCriterion::playerCtx),
|
||||
ResourceLocation.CODEC.listOf().optionalFieldOf("id", listOf()).forGetter(PartyCheckCriterion::party)
|
||||
ResourceLocation.CODEC.listOf().optionalFieldOf("party", listOf()).forGetter(PartyCheckCriterion::party)
|
||||
).apply(it, ::PartyCheckCriterion) }
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ class PartyCheckCriterion(
|
||||
matches.add(it.species.resourceIdentifier)
|
||||
}
|
||||
}
|
||||
return matches.containsAll(party) && matches.size == partyCount
|
||||
return matches.containsAll(party) && matches.size == party.size
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+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.advancement.criterion
|
||||
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import java.util.Optional
|
||||
import net.minecraft.advancements.critereon.ContextAwarePredicate
|
||||
import net.minecraft.advancements.critereon.EntityPredicate
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
class ReelInPokemonContext(val pokemonId : ResourceLocation, val baitId: ResourceLocation)
|
||||
|
||||
class ReelInPokemonCriterionCondition(
|
||||
playerCtx: Optional<ContextAwarePredicate>,
|
||||
val pokemonId: String,
|
||||
val baitId: String
|
||||
) : SimpleCriterionCondition<ReelInPokemonContext>(playerCtx) {
|
||||
|
||||
companion object {
|
||||
val CODEC: Codec<ReelInPokemonCriterionCondition> = RecordCodecBuilder.create { it.group(
|
||||
EntityPredicate.ADVANCEMENT_CODEC.optionalFieldOf("player").forGetter { it.playerCtx },
|
||||
Codec.STRING.optionalFieldOf("pokemonId", "any").forGetter { it.pokemonId },
|
||||
Codec.STRING.optionalFieldOf("baitId", "empty_bait").forGetter { it.baitId }
|
||||
).apply(it, { playerCtx, pokemonId, baitId -> ReelInPokemonCriterionCondition(playerCtx, pokemonId, baitId.ifEmpty { "empty_bait" }) }) }
|
||||
}
|
||||
|
||||
override fun matches(player: ServerPlayer, context: ReelInPokemonContext): Boolean {
|
||||
return (context.pokemonId == this.pokemonId.asIdentifierDefaultingNamespace() || this.pokemonId == "any") && (context.baitId == this.baitId.asIdentifierDefaultingNamespace() || this.baitId == "empty_bait")
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ import net.minecraft.nbt.NbtOps
|
||||
* @author Qu
|
||||
* @since January 9th, 2022
|
||||
*/
|
||||
open class Ability internal constructor(var template: AbilityTemplate, forced: Boolean) {
|
||||
open class Ability internal constructor(var template: AbilityTemplate, forced: Boolean, priority: Priority) {
|
||||
|
||||
val name: String
|
||||
get() = template.name
|
||||
@@ -57,7 +57,7 @@ open class Ability internal constructor(var template: AbilityTemplate, forced: B
|
||||
*
|
||||
* @see [Pokemon.updateAbility].
|
||||
*/
|
||||
var priority = Priority.LOWEST
|
||||
var priority = priority
|
||||
internal set
|
||||
|
||||
@Deprecated("Please use the Codec instead", ReplaceWith("Ability.CODEC"))
|
||||
@@ -111,7 +111,7 @@ open class Ability internal constructor(var template: AbilityTemplate, forced: B
|
||||
Codec.BOOL.optionalFieldOf(DataKeys.POKEMON_ABILITY_FORCED, false).forGetter(Ability::forced),
|
||||
Codec.INT.optionalFieldOf(DataKeys.POKEMON_ABILITY_INDEX, -1).forGetter(Ability::index),
|
||||
Priority.CODEC.optionalFieldOf(DataKeys.POKEMON_ABILITY_PRIORITY, Priority.LOWEST).forGetter(Ability::priority)
|
||||
).apply(it) { template, forced, index, priority -> Ability(template, forced).apply {
|
||||
).apply(it) { template, forced, index, priority -> Ability(template, forced, priority).apply {
|
||||
this.index = index
|
||||
this.priority = priority
|
||||
} }
|
||||
|
||||
@@ -23,7 +23,7 @@ import com.cobblemon.mod.common.pokemon.Species
|
||||
*/
|
||||
open class AbilityPool : PrioritizedList<PotentialAbility>() {
|
||||
fun select(species: Species, aspects: Set<String>): Pair<Ability, Priority> {
|
||||
for (priority in Priority.values()) {
|
||||
for (priority in Priority.entries) {
|
||||
val potentialAbilities = priorityMap[priority]?.filter { it.isSatisfiedBy(aspects) } ?: continue
|
||||
if (potentialAbilities.isNotEmpty()) {
|
||||
return potentialAbilities.random().template.create() to priority
|
||||
@@ -31,7 +31,7 @@ open class AbilityPool : PrioritizedList<PotentialAbility>() {
|
||||
}
|
||||
|
||||
LOGGER.error("Unable to select an ability from the pool for $species and aspects: ${aspects.joinToString()}")
|
||||
LOGGER.error("Usually this happens when a client is doing logic it shouldn't. Please show this to the Cobblemon developers!")
|
||||
LOGGER.error("Please show this to the Cobblemon developers!")
|
||||
Exception().printStackTrace()
|
||||
return Abilities.first().create() to Priority.LOWEST
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.abilities
|
||||
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
import com.cobblemon.mod.common.util.codec.CodecUtils
|
||||
import com.google.gson.JsonObject
|
||||
import com.mojang.serialization.Codec
|
||||
@@ -21,14 +22,14 @@ import net.minecraft.nbt.CompoundTag
|
||||
*/
|
||||
class AbilityTemplate(
|
||||
val name: String = "",
|
||||
var builder: (AbilityTemplate, forced: Boolean) -> Ability = { template, forced -> Ability(template, forced) },
|
||||
var builder: (AbilityTemplate, forced: Boolean, priority: Priority) -> Ability = { template, forced, priority -> Ability(template, forced, priority) },
|
||||
val displayName: String = "cobblemon.ability.$name",
|
||||
val description: String = "cobblemon.ability.$name.desc"
|
||||
) {
|
||||
/**
|
||||
* Returns the Ability or if applicable the extension connected to this template
|
||||
*/
|
||||
fun create(forced: Boolean = false) = builder(this, forced)
|
||||
fun create(forced: Boolean = false, priority: Priority = Priority.LOWEST) = builder(this, forced, priority)
|
||||
|
||||
/**
|
||||
* Returns the Ability and loads the given NBT Tag into it.
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
# Brain Configuration
|
||||
|
||||
A "Brain" is a Java Edition AI concept for more modern entities. It has tasks that are listed inside of 'activities',
|
||||
and in some cases a schedule which determines which activities will occur during different times of the day. There is
|
||||
also a set of core activities, and a default activity. In Minecraft, the most complex usage of the Brain AI system
|
||||
is on villagers which have schedules that control their behaviour during the day and night as well as child villager play behaviours.
|
||||
|
||||
Cobblemon's NPCs use the Brain AI system. As part of that, Cobblemon added the concept of a Brain Configuration
|
||||
that allows Java Edition brains to be configured in a more data-driven way.
|
||||
|
||||
By default, without any brain configuration, the default activity will be `minecraft:idle` and the core activities will be `[minecraft:core]`.
|
||||
The activities would not have any tasks though.
|
||||
|
||||
## Possible Activities
|
||||
The activities that can be used in brains is determined by code and cannot be added to using datapacks. Note that except for
|
||||
when used as defaults and core activities, these are purely names and what you use them for is entirely up to you.
|
||||
|
||||
The activities that exist in Minecraft are:
|
||||
- `minecraft:core`: The core activity intended to be used for fundamental actions like floating in water and looking at look targets.
|
||||
- `minecraft:idle`: The idle activity is used when the entity is not doing anything else. This is typically used for things like wandering and other random actions.
|
||||
- `minecraft:work`: The work activity is used for actions that are related to the entity's job. For villagers, this is things like farming, smithing, and other job-related tasks.
|
||||
- `minecraft:play`
|
||||
- `minecraft:rest`: The rest activity is used in villagers for the activity that moves them to a bed and hops into it.
|
||||
- `minecraft:meet`: The meet activity is used in villagers for going to the meeting place and gossipping with other NPCs.
|
||||
- `minecraft:panic`: The panic activity is used in villagers for when they are panicking and fleeing, usually from an attacker.
|
||||
- `minecraft:raid`
|
||||
- `minecraft:pre_raid`: The raid activity is used in villagers for what they do when a raid is about to start, like running to a safe location during a raid.
|
||||
- `minecraft:hide`
|
||||
- `minecraft:fight`: Intended to be used when engaging in combat.
|
||||
- `minecraft:celebrate`
|
||||
- `minecraft:admire_item`
|
||||
- `minecraft:avoid`
|
||||
- `minecraft:ride`
|
||||
- `minecraft:play_dead`
|
||||
- `minecraft:long_jump`
|
||||
- `minecraft:ram`
|
||||
- `minecraft:tongue`
|
||||
- `minecraft:swim`
|
||||
- `minecraft:lay_spawn`
|
||||
- `minecraft:sniff`
|
||||
- `minecraft:investigate`
|
||||
- `minecraft:roar`
|
||||
- `minecraft:emerge`
|
||||
- `minecraft:dig`
|
||||
|
||||
The activities that were added by Cobblemon are:
|
||||
- `cobblemon:battling`: For when the entity is in a Pokémon battle.
|
||||
- `cobblemon:action_effect`: For when the entity is performing an action effect. Usually registered as an almost empty activity that simply prevents the entity from doing other things.
|
||||
- `cobblemon:npc_chatting`: Intended for NPC entities using dialogue so that they look at their chat partner and do nothing else.
|
||||
|
||||
## Brain Configuration Format
|
||||
A brain is configured using a series of "Brain Configurations". Each configuration is a JSON object that contains
|
||||
a "type" field which determines the action to perform when configuring a brain. Many different types are available.
|
||||
|
||||
### script
|
||||
The `script` type executes a MoLang script which will apply some changes to the brain.
|
||||
- `script`: The MoLang script to execute. This will be a resource location for the `molang` datapack folder. Something like `cobblemon:some_script`.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if the script should be executed. Defaults to `"true"`.
|
||||
|
||||
### add_tasks_to_activity
|
||||
The 'add_tasks_to_activity' type adds a list of tasks to an activity.
|
||||
- `activity`: The activity to add the tasks to. This must be a pre-existing activity - a brain configuration cannot
|
||||
create new activities.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if the tasks should be added. If this
|
||||
is not present, the tasks will always be added.
|
||||
- `tasksByPriority`: A map of task priorities to lists of tasks to add to the activity. Each task is a JSON object with a "type" field that determines the
|
||||
task to add or in simple cases can just be the type as a simple JSON primitive to use default properties. Many different types of task config are available. See [Task Configuration](./task/README.md).
|
||||
The priority of the task is used to determine the execution order each tick. This usually does not matter, but in some cases
|
||||
where the execution of one task will move to a different activity or otherwise prevent another task, the priority matters a lot.
|
||||
As an example, a task that moves to nearby healing machines will set a walk target which prevents the use of the healing machine,
|
||||
so the use task would need to have a lower priority or be earlier in the list so that it runs sooner.
|
||||
|
||||
Example, making sure `heal_using_healing_machine` comes before `go_to_healing_machine`:
|
||||
```json
|
||||
{
|
||||
"type": "add_tasks_to_activity",
|
||||
"activity": "minecraft:idle",
|
||||
"tasksByPriority": {
|
||||
"1": [
|
||||
{
|
||||
"type": "cobblemon:heal_using_healing_machine",
|
||||
"horizontalUseRange": "2"
|
||||
},
|
||||
"cobblemon:go_to_healing_machine"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note that when specifying tasks, you can either use an object format with the "type" property, or if you want to use only
|
||||
default parameters for the task you can simply state the type as a string. The above example uses both formats.
|
||||
|
||||
### set_core_activities
|
||||
The `set_core_activities` type sets the core activities of the brain. It has only one property, `activities`, which is a list of activities.
|
||||
|
||||
### set_default_activity
|
||||
The `set_default_activity` type sets the default activity of the brain. It has only one property, `activity`, which is the activity to set as the default.
|
||||
|
||||
### apply_preset
|
||||
The `apply_preset` type applies a preset to the brain. Brain presets are JSON arrays under the `brain_presets` datapack folder.
|
||||
This is a way to apply a set of configurations to the brain.
|
||||
- `preset`: The name of the preset to apply. This is the name of the file in the `brain_presets` folder without the `.json` extension.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if the preset should be applied. Defaults to `"true"`.
|
||||
|
||||
Example brain preset (`uses_healing_machine.json`):
|
||||
```json
|
||||
[
|
||||
{
|
||||
"type": "add_tasks_to_activity",
|
||||
"activity": "idle",
|
||||
"tasksByPriority": {
|
||||
"1": [
|
||||
"heal_using_healing_machine",
|
||||
"go_to_healing_machine"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
|
||||
import com.cobblemon.mod.common.entity.PosableEntity
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.VillagerCalmDown
|
||||
|
||||
class CalmDownTaskConfig : SingleTaskConfig {
|
||||
var condition = "true".asExpression()
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
|
||||
if (!runtime.resolveBoolean(condition)) return null
|
||||
return VillagerCalmDown.create()
|
||||
}
|
||||
}
|
||||
+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.ai.config.task
|
||||
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
import com.cobblemon.mod.common.entity.PosableEntity
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
|
||||
class ExitBattleWhenHurtTaskConfig : SingleTaskConfig {
|
||||
var condition = "true".asExpression()
|
||||
var includePassiveDamage = true
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
|
||||
if (!runtime.resolveBoolean(condition)) return null
|
||||
|
||||
if (entity is NPCEntity) {
|
||||
fun cancelNPCBattles(npcEntity: NPCEntity): Boolean {
|
||||
val battles = npcEntity.battleIds.mapNotNull(BattleRegistry::getBattle)
|
||||
battles.forEach { it.end() }
|
||||
return battles.isNotEmpty()
|
||||
}
|
||||
|
||||
return if (includePassiveDamage) {
|
||||
BehaviorBuilder.create {
|
||||
it.group(it.present(MemoryModuleType.HURT_BY), it.present(CobblemonMemories.NPC_BATTLING))
|
||||
.apply(it) { _, _ -> Trigger { world, entity, _ -> return@Trigger cancelNPCBattles(entity as NPCEntity) } }
|
||||
}
|
||||
} else {
|
||||
BehaviorBuilder.create {
|
||||
it.group(it.present(MemoryModuleType.HURT_BY_ENTITY), it.present(CobblemonMemories.NPC_BATTLING))
|
||||
.apply(it) { _, _ -> Trigger { world, entity, _ -> return@Trigger cancelNPCBattles(entity as NPCEntity) } }
|
||||
}
|
||||
}
|
||||
} else { return null }/* else if (entity is PokemonEntity) { TODO uncomment when pokemon have brain :)
|
||||
fun cancelPokemonBattle(pokemonEntity: PokemonEntity): Boolean {
|
||||
val battle = BattleRegistry.getBattle(pokemonEntity.battleId ?: return false)
|
||||
battle?.end()
|
||||
return battle != null
|
||||
}
|
||||
|
||||
return if (includePassiveDamage) {
|
||||
BehaviorBuilder.create {
|
||||
it.group(it.present(MemoryModuleType.HURT_BY), it.present(CobblemonMemories.POKEMON_BATTLING))
|
||||
.apply(it) { _, _ -> Trigger { world, entity, _ -> return@Trigger cancelPokemonBattle(entity as PokemonEntity) } }
|
||||
}
|
||||
} else {
|
||||
BehaviorBuilder.create {
|
||||
it.group(it.present(MemoryModuleType.HURT_BY_ENTITY), it.present(CobblemonMemories.POKEMON_BATTLING))
|
||||
.apply(it) { _, _ -> Trigger { world, entity, _ -> return@Trigger cancelPokemonBattle(entity as PokemonEntity) } }
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
+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.ai.config.task
|
||||
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.entity.PosableEntity
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
import com.cobblemon.mod.common.util.resolveFloat
|
||||
import com.cobblemon.mod.common.util.resolveInt
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.SetWalkTargetAwayFrom
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
|
||||
class FleeAttackerTaskConfig : SingleTaskConfig {
|
||||
var condition = "true".asExpression()
|
||||
var speedMultiplier = "0.5".asExpression()
|
||||
var desiredDistance = "9".asExpression()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
|
||||
if (!runtime.resolveBoolean(condition) || entity !is PathfinderMob) return null
|
||||
val speedMultiplier = runtime.resolveFloat(speedMultiplier)
|
||||
val desiredDistance = runtime.resolveInt(desiredDistance)
|
||||
return WrapperLivingEntityTask(
|
||||
SetWalkTargetAwayFrom.entity(MemoryModuleType.HURT_BY_ENTITY, speedMultiplier, desiredDistance, false),
|
||||
PathfinderMob::class.java
|
||||
)
|
||||
}
|
||||
}
|
||||
+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.ai.config.task
|
||||
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.entity.PosableEntity
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
import com.cobblemon.mod.common.util.resolveFloat
|
||||
import com.cobblemon.mod.common.util.resolveInt
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.SetWalkTargetAwayFrom
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
|
||||
class FleeNearestHostileTaskConfig : SingleTaskConfig {
|
||||
var condition = "true".asExpression()
|
||||
var speedMultiplier = "0.5".asExpression()
|
||||
var desiredDistance = "9".asExpression()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
|
||||
if (!runtime.resolveBoolean(condition) || entity !is PathfinderMob) return null
|
||||
val speedMultiplier = runtime.resolveFloat(speedMultiplier)
|
||||
val desiredDistance = runtime.resolveInt(desiredDistance)
|
||||
return WrapperLivingEntityTask(
|
||||
SetWalkTargetAwayFrom.entity(MemoryModuleType.NEAREST_HOSTILE, speedMultiplier, desiredDistance, false),
|
||||
PathfinderMob::class.java
|
||||
)
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -26,14 +26,14 @@ import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
|
||||
class FollowWalkTargetTaskConfig : SingleTaskConfig {
|
||||
val condition: ExpressionLike = "true".asExpressionLike()
|
||||
val minRunTime: Expression = "150".asExpression()
|
||||
val maxRunTime: Expression = "250".asExpression()
|
||||
val minRunTicks: Expression = "150".asExpression()
|
||||
val maxRunTicks: Expression = "250".asExpression()
|
||||
|
||||
override fun createTask(entity: LivingEntity, brainConfigurationContext: BrainConfigurationContext): BehaviorControl<LivingEntity>? {
|
||||
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
|
||||
if (!runtime.resolveBoolean(condition)) return null
|
||||
return WrapperLivingEntityTask(
|
||||
FollowWalkTargetTask(runtime.resolveInt(minRunTime), runtime.resolveInt(maxRunTime)),
|
||||
FollowWalkTargetTask(runtime.resolveInt(minRunTicks), runtime.resolveInt(maxRunTicks)),
|
||||
PathfinderMob::class.java
|
||||
)
|
||||
}
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ class GoToHealingMachineTaskConfig(
|
||||
val condition: ExpressionLike = "true".asExpressionLike(),
|
||||
val horizontalSearchRange: Expression = "10".asExpression(),
|
||||
val verticalSearchRange: Expression = "5".asExpression(),
|
||||
val speedMultiplier: Expression = "0.33".asExpression(),
|
||||
val speedMultiplier: Expression = "0.35".asExpression(),
|
||||
val completionRange: Expression = "1".asExpression()
|
||||
) : SingleTaskConfig {
|
||||
override fun createTask(
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
|
||||
import com.cobblemon.mod.common.entity.ai.LookInDirectionTask
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
|
||||
class LookInDirectionTaskConfig : SingleTaskConfig {
|
||||
var yaw: Expression = "0".asExpression()
|
||||
var pitch: Expression = "0".asExpression()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
) = LookInDirectionTask(yaw, pitch)
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
# Task Configuration
|
||||
|
||||
A task configuration is responsible for producing one or multiple tasks as part of a
|
||||
[BrainConfiguration](../README.md). Tasks are the smallest unit of behaviour in a brain and are executed
|
||||
as part of an activity.
|
||||
|
||||
A task configuration can be a simple string that represents the type of task to create, or a JSON object
|
||||
which a `type` property that describes what type of task is being created and other properties that configure it.
|
||||
|
||||
### one_of
|
||||
The 'one_of' type creates a task that randomly selects one of the tasks in the list to execute each tick.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
- `options`: A list of objects representing the different tasks that could run for a tick. Each object must have a `task`
|
||||
which is a task configuration. Optionally, you can also specify a `weight` which is a number that determines the
|
||||
probability of the task being selected. If no weight is specified, the task will have a weight of 1.
|
||||
|
||||
### all_of
|
||||
The 'all_of' type creates a task that executes all of the tasks in the list each tick.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
- `tasks`: A list of task configurations to execute each tick.
|
||||
|
||||
### do_nothing
|
||||
The 'do_nothing' type creates a task that does nothing for some period of time.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
- `minDurationTicks`: A MoLang expression that determines the minimum number of ticks to do nothing. Defaults to 40. Has `q.entity` as the entity.
|
||||
- `maxDurationTicks`: A MoLang expression that determines the maximum number of ticks to do nothing. Defaults to 80. Has `q.entity` as the entity.
|
||||
|
||||
### random
|
||||
The 'random' type creates a task that chooses one of the contained tasks when the entity is spawned. Note that this does not save forever,
|
||||
so if the entity is reloaded, it will choose a new task. To make persisting randomization you have to use MoLang conditions on other task
|
||||
configurations that use `q.entity.id_modulo(some_number)` to apply randomization based on the entity UUID.
|
||||
For example, `q.entity.id_modulo(2)` will return 0 or 1 for an entity but will always be the same for the same entity,
|
||||
so using `q.entity.id_modulo(2) == 0` as a task condition will make it occur for 50% of entities and remain after reloads.
|
||||
`q.entity.id_modulo(3)` will return 0 or 1 or 2, and so on.
|
||||
|
||||
### follow_walk_target
|
||||
The 'follow_walk_target' type creates a task that makes the entity follow a walk target. This does not choose the walk target,
|
||||
it is only responsible for ensuring the entity will follow a path to whatever the current walk target is. Note that the target
|
||||
does not necessarily need to be a land target.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
- `minRunTicks`: A MoLang expression that determines the minimum number of ticks to run to the target before stopping. Defaults to 150.
|
||||
- `maxRunTicks`: A MoLang expression that determines the maximum number of ticks to run to the target before giving up. Defaults to 250.
|
||||
|
||||
### look_at_target
|
||||
The 'look_at_target' type creates a task that makes the entity look at a target. This does not choose the target, it is only
|
||||
responsible for ensuring the entity will look at the target position.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
- `minDurationTicks`: A MoLang expression that determines the minimum number of ticks that the entity will look at the target. Defaults to 40.
|
||||
- `maxDurationTicks`: A MoLang expression that determines the maximum number of ticks that the entity will look at the target. Defaults to 80.
|
||||
|
||||
### stay_afloat
|
||||
The 'stay_afloat' type creates a task that makes the entity stay afloat in water instead of quietly sinking and drowning.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
- `chance`: A MoLang expression that determines the chance of the entity trying to stay afloat. The higher it is, the more desperately it stays above the water. Defaults to 0.01. You don't need to change this, honestly.
|
||||
|
||||
### wander
|
||||
The 'wander' type creates a task that makes the entity wander around randomly.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
- `shouldWander`: A MoLang expression that determines if the entity should wander. Defaults to `math.random(0, 20*8) <= 1`, which makes it wander approximately once every 8 seconds.
|
||||
- `horizontalRange`: A MoLang expression that determines the maximum distance the entity will wander in a single path. Defaults to 10.
|
||||
- `verticalRange`: A MoLang expression that determines the maximum vertical distance the entity will wander from its starting position. Defaults to 5.
|
||||
- `speedMultiplier`: A MoLang expression that determines the speed multiplier for wandering. Defaults to 0.35 which is a standard walk speed.
|
||||
- `completionRange`: A MoLang expression that determines the distance from the target location that the entity needs to be for the path to be considered complete. Defaults to 1.
|
||||
- `lookTargetYOffset`: A MoLang expression that determines the vertical offset of the look target from the target walk position. Defaults to 1.5. You probably don't need to change this.
|
||||
|
||||
### look_at_entities
|
||||
The 'look_at_entities' type creates a task that will randomly set the entity's look target to a nearby entity.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
- `maxDistance`: A MoLang expression that determines the maximum distance for an entity to be from this entity and be looked at.
|
||||
|
||||
### go_to_healing_machine
|
||||
The 'go_to_healing_machine' type creates a task that makes the entity go to a nearby healing machine when they have a static NPC party that could be healed.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
- `horizontalSearchRange`: A MoLang expression that determines the maximum distance to search for a healing machine. Defaults to 10.
|
||||
- `verticalSearchRange`: A MoLang expression that determines the maximum vertical distance to search for a healing machine. Defaults to 5.
|
||||
- `speedMultiplier`: A MoLang expression that determines the speed multiplier for walking to the healing machine. Defaults to 0.35 which is a standard walk speed.
|
||||
- `completionRange`: A MoLang expression that determines the distance from the target location that the entity needs to be for the path to be considered complete. Defaults to 1.
|
||||
|
||||
### heal_using_healing_machine
|
||||
The 'heal_using_healing_machine' type creates a task that makes the entity heal its static NPC party using a healing machine if one is right next to the NPC.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
- `horizontalUseRange`: A MoLang expression that determines the maximum distance to a healing machine from which the NPC can use it. Defaults to 3.
|
||||
- `verticalUseRange`: A MoLang expression that determines the maximum vertical distance to a healing machine from which the NPC can use it. Defaults to 2.
|
||||
|
||||
### switch_npc_to_battle
|
||||
The 'switch_npc_to_battle' type creates a task that will switch NPC entities to the battle activity when it is in a battle. You'd generally put this
|
||||
on the idle activity so that it automatically transitions activities when a battle starts.
|
||||
|
||||
### switch_npc_from_battle
|
||||
The 'switch_npc_from_battle' type creates a task that will switch NPC entities from the battle activity when it is no longer in a battle. You should
|
||||
put this in the battle activity so that it automatically transitions activities when a battle ends. If the NPC is in multiple battles, this
|
||||
will only activate when no battles remain. It will rely on the brain schedule to decide what activity to switch back to (usually idle).
|
||||
|
||||
### look_at_battling_pokemon
|
||||
The 'look_at_battling_pokemon' type creates a task that will set the entity's look target to switch between the battling Pokémon.
|
||||
- `minDurationTicks`: A MoLang expression that determines the minimum number of ticks that the entity will look at a specific battling Pokémon. Defaults to 40.
|
||||
- `maxDurationTicks`: A MoLang expression that determines the maximum number of ticks that the entity will look at a specific battling Pokémon. Defaults to 80.
|
||||
|
||||
### exit_battle_when_hurt
|
||||
The 'exit_battle_when_hurt' type creates a task that will make the entity exit the battle when it is hurt.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
- `includePassiveDamage`: A boolean that determines if passive damage (like poison or cacti) should trigger this task. Defaults to true.
|
||||
|
||||
### switch_to_chatting
|
||||
The 'switch_to_chatting' type creates a task that will switch the entity to the chatting activity when a dialogue has been opened with that NPC.
|
||||
|
||||
### switch_from_chatting
|
||||
The 'switch_from_chatting' type creates a task that will switch the entity from the chatting activity when the dialogue has been closed with that NPC.
|
||||
It will rely on the brain schedule to decide what activity to switch back to (usually idle).
|
||||
|
||||
### look_at_speaker
|
||||
The 'look_at_speaker' type creates a task that will set the entity's look target to the speaker of the dialogue.
|
||||
|
||||
### switch_to_action_effect
|
||||
The 'switch_to_action_effect' type creates a task that will switch the entity to the action effect activity when it is performing an action effect.
|
||||
|
||||
### switch_from_action_effect
|
||||
The 'switch_from_action_effect' type creates a task that will switch the entity from the action effect activity when it is no longer performing an action effect.
|
||||
It will rely on the brain schedule to decide what activity to switch back to (usually idle).
|
||||
|
||||
### switch_to_fight
|
||||
The 'switch_to_fight' type creates a task that will switch the entity to the fight activity when it has an attack target.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
- `activity`: The activity to switch to when the NPC is in a fight. Defaults to `minecraft:fight`.
|
||||
|
||||
### switch_from_fight
|
||||
The 'switch_from_fight' type creates a task that will switch the entity from the fight activity when it no longer has an attack target. It will rely on the brain schedule to decide what activity to switch back to (usually idle).
|
||||
|
||||
### get_angry_at_attacker
|
||||
The 'get_angry_at_attacker' type creates a task that will make the entity get angry at the attacker when it is attacked.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
|
||||
### stop_being_angry_if_attacker_dead
|
||||
The 'stop_being_angry_if_attacker_dead' type creates a task that will make the entity stop being angry at the attacker when the attacker is dead.
|
||||
|
||||
### attack_angry_at
|
||||
The 'attack_angry_at' type creates a task that will make the entity choose the entity that it is angry at to be its attack target.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
|
||||
### move_to_attack_target
|
||||
The 'move_to_attack_target' type creates a task that will make the entity move to its attack target.
|
||||
- `speedMultiplier`: A MoLang expression that determines the speed multiplier for moving to the attack target. Defaults to 0.5 which is an elevated speed to usual.
|
||||
- `closeEnoughDistance`: A MoLang expression that determines the distance from the attack target that the entity needs to be for the path to be considered complete. Defaults to 1.
|
||||
|
||||
### melee_attack
|
||||
The 'melee_attack' type creates a task that will make the entity perform a melee attack on its attack target when it is close enough. The damage dealt is determined by entity properties, which cannot be easily configured at time of writing.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
- `range`: A MoLang expression that determines the maximum distance from the attack target that the entity can be to perform the attack. Defaults to 1.5.
|
||||
- `cooldownTicks`: A MoLang expression that determines the number of ticks to wait before performing another attack. Defaults to 30.
|
||||
|
||||
### switch_to_panic_when_hurt
|
||||
The 'switch_to_panic_when_hurt' type creates a task that will switch the entity to the panic activity when it is hurt.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
- `includePassiveDamage`: A boolean that determines if passive damage (like poison or cacti) should trigger this task. Defaults to false.
|
||||
|
||||
### switch_to_panic_when_hostiles_nearby
|
||||
The 'switch_to_panic_when_hostiles_nearby' type creates a task that will switch the entity to the panic activity when hostiles are nearby. The definition of nearby depends
|
||||
on the entity type and this piggy-backs off the Minecraft list of aggressive mobs and the right distances.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
|
||||
### calm_down
|
||||
The 'calm_down' type creates a task that will make the entity calm down when it is no longer being hurt and is far from hostiles and its attacker.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
|
||||
### flee_attacker
|
||||
The 'flee_attacker' type creates a task that will make the entity flee from its attacker when it is hurt.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
- `speedMultiplier`: A MoLang expression that determines the speed multiplier for fleeing from the attacker. Defaults to 0.5 which is an elevated speed to usual.
|
||||
- `desiredDistance`: A MoLang expression that determines the distance from the attacker that the entity should try to maintain. Defaults to 9.
|
||||
|
||||
### flee_nearest_hostile
|
||||
The 'flee_nearest_hostile' type creates a task that will make the entity flee from the nearest hostile entity when it is hurt.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
- `speedMultiplier`: A MoLang expression that determines the speed multiplier for fleeing from the nearest hostile. Defaults to 0.5 which is an elevated speed to usual.
|
||||
- `desiredDistance`: A MoLang expression that determines the distance from the nearest hostile that the entity should try to maintain. Defaults to 9.
|
||||
|
||||
### run_script
|
||||
The 'run_script' type creates a task that will run a MoLang script every tick on this entity. This has the risk of being very laggy
|
||||
if the script is doing expensive things. Remember to use `q.entity.world.game_time` and `math.mod` to do intermittent actions inside
|
||||
the script to avoid lagging the game.
|
||||
- `script`: A resource location identifying the script to run. This should be in the `molang` datapack folder.
|
||||
|
||||
### look_in_direction
|
||||
The 'look_in_direction' type creates a task that will make the entity look in a specific direction at all times where they aren't being told to look elsewhere.
|
||||
- `yaw`: A MoLang expression that determines the yaw angle (degrees) of the entity. Defaults to `0`.
|
||||
- `pitch`: A MoLang expression that determines the pitch angle (degrees) of the entity. Defaults to `0`.
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
|
||||
import com.cobblemon.mod.common.entity.PosableEntity
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
|
||||
/**
|
||||
* Runs a registered MoLang script (from the molang datapack folder) each tick.
|
||||
*
|
||||
* It's up to the user to not make expensive ass scripts. Remember you can access the game time
|
||||
* from q.entity.level.game_time which can be used with a modulo operator to make something intermittent.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since December 2nd, 2024
|
||||
*/
|
||||
class RunScript : SingleTaskConfig {
|
||||
val script = cobblemonResource("dummy")
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? = BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.registered(MemoryModuleType.LOOK_TARGET) // I think I need to have at least something here?
|
||||
).apply(it) { _ ->
|
||||
Trigger { world, entity, _ ->
|
||||
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: entity.asMostSpecificMoLangValue())
|
||||
return@Trigger CobblemonScripts.run(script, runtime) == DoubleValue.ONE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
-4
@@ -8,15 +8,24 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
|
||||
import com.cobblemon.mod.common.entity.npc.ai.SwitchFromActionEffectTask
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.schedule.Activity
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
|
||||
class SwitchFromActionEffectTaskConfig : SingleTaskConfig {
|
||||
val activity = Activity.IDLE
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
) = SwitchFromActionEffectTask.create(activity)
|
||||
) = BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.absent(CobblemonMemories.ACTIVE_ACTION_EFFECT)
|
||||
).apply(it) { _ ->
|
||||
Trigger { level, entity, _ ->
|
||||
entity.brain.updateActivityFromSchedule(level.dayTime, level.gameTime)
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-4
@@ -13,10 +13,8 @@ import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.schedule.Activity
|
||||
|
||||
class SwitchFromChattingTaskConfig : SingleTaskConfig {
|
||||
val activity = Activity.IDLE
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
@@ -24,8 +22,8 @@ class SwitchFromChattingTaskConfig : SingleTaskConfig {
|
||||
it.group(
|
||||
it.absent(CobblemonMemories.DIALOGUES)
|
||||
).apply(it) { _ ->
|
||||
Trigger { world, entity, _ ->
|
||||
entity.brain.setActiveActivityIfPossible(activity)
|
||||
Trigger { level, entity, _ ->
|
||||
entity.brain.updateActivityFromSchedule(level.dayTime, level.gameTime)
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
|
||||
+2
-4
@@ -13,10 +13,8 @@ import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.schedule.Activity
|
||||
|
||||
class SwitchFromFightTaskConfig : SingleTaskConfig {
|
||||
val activity = Activity.IDLE
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
@@ -24,8 +22,8 @@ class SwitchFromFightTaskConfig : SingleTaskConfig {
|
||||
it.group(
|
||||
it.absent(MemoryModuleType.ATTACK_TARGET)
|
||||
).apply(it) { _ ->
|
||||
Trigger { world, entity, _ ->
|
||||
entity.brain.setActiveActivityIfPossible(activity)
|
||||
Trigger { level, entity, _ ->
|
||||
entity.brain.updateActivityFromSchedule(level.dayTime, level.gameTime)
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
|
||||
+11
-4
@@ -8,18 +8,25 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
|
||||
import com.cobblemon.mod.common.entity.npc.ai.SwitchFromBattleTask
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.schedule.Activity
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
|
||||
class SwitchFromNPCBattleTaskConfig : SingleTaskConfig {
|
||||
val activity: Activity = Activity.IDLE
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
return SwitchFromBattleTask.create(activity)
|
||||
return BehaviorBuilder.create {
|
||||
it.group(it.absent(CobblemonMemories.NPC_BATTLING)).apply(it) { _ ->
|
||||
Trigger { level, entity, _ ->
|
||||
entity.brain.updateActivityFromSchedule(level.dayTime, level.gameTime)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -26,7 +26,7 @@ class SwitchToActionEffectTaskConfig : SingleTaskConfig {
|
||||
it.absent(CobblemonMemories.ACTIVE_ACTION_EFFECT)
|
||||
).apply(it) { _ ->
|
||||
Trigger { world, entity, time ->
|
||||
entity.brain.setActiveActivityIfPossible(CobblemonActivities.NPC_ACTION_EFFECT)
|
||||
entity.brain.setActiveActivityIfPossible(CobblemonActivities.ACTION_EFFECT)
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
|
||||
-8
@@ -8,21 +8,15 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.cobblemon.mod.common.CobblemonActivities
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
|
||||
import com.cobblemon.mod.common.entity.PosableEntity
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
|
||||
class SwitchToChattingTaskConfig : SingleTaskConfig {
|
||||
val condition = "true".asExpressionLike()
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
@@ -32,8 +26,6 @@ class SwitchToChattingTaskConfig : SingleTaskConfig {
|
||||
it.registered(MemoryModuleType.WALK_TARGET)
|
||||
).apply(it) { _, walkTarget ->
|
||||
Trigger { world, entity, _ ->
|
||||
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
|
||||
if (!runtime.resolveBoolean(condition)) return@Trigger false
|
||||
walkTarget.erase()
|
||||
entity.brain.setActiveActivityIfPossible(CobblemonActivities.NPC_CHATTING)
|
||||
return@Trigger true
|
||||
|
||||
+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.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.schedule.Activity
|
||||
|
||||
class SwitchToPanicWhenHostilesNearbyTaskConfig : SingleTaskConfig {
|
||||
var condition = "true".asExpression()
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
) = BehaviorBuilder.create {
|
||||
it.group(it.present(MemoryModuleType.NEAREST_HOSTILE))
|
||||
.apply(it) { nearestHostile ->
|
||||
Trigger { world, entity, _ ->
|
||||
(entity as? PathfinderMob)?.navigation?.stop()
|
||||
entity.brain.setActiveActivityIfPossible(Activity.PANIC)
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
|
||||
import com.cobblemon.mod.common.entity.PosableEntity
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.schedule.Activity
|
||||
|
||||
class SwitchToPanicWhenHurtTaskConfig : SingleTaskConfig {
|
||||
var condition = "true".asExpression()
|
||||
var includePassiveDamage = false
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
|
||||
if (!runtime.resolveBoolean(condition)) return null
|
||||
return if (includePassiveDamage) {
|
||||
BehaviorBuilder.create {
|
||||
it.group(it.present(MemoryModuleType.HURT_BY))
|
||||
.apply(it) { _ ->
|
||||
Trigger { world, entity, _ ->
|
||||
(entity as? PathfinderMob)?.navigation?.stop()
|
||||
entity.brain.setActiveActivityIfPossible(Activity.PANIC)
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
BehaviorBuilder.create {
|
||||
it.group(it.present(MemoryModuleType.HURT_BY_ENTITY))
|
||||
.apply(it) { _ ->
|
||||
Trigger { world, entity, _ ->
|
||||
(entity as? PathfinderMob)?.navigation?.stop()
|
||||
entity.brain.setActiveActivityIfPossible(Activity.PANIC)
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,6 +52,14 @@ fun interface TaskConfig {
|
||||
"look_at_speaker" to LookAtSpeakerTaskConfig::class.java,
|
||||
"switch_to_action_effect" to SwitchToActionEffectTaskConfig::class.java,
|
||||
"switch_from_action_effect" to SwitchFromActionEffectTaskConfig::class.java,
|
||||
"exit_battle_when_hurt" to ExitBattleWhenHurtTaskConfig::class.java,
|
||||
"switch_to_panic_when_hurt" to SwitchToPanicWhenHurtTaskConfig::class.java,
|
||||
"switch_to_panic_when_hostiles_nearby" to SwitchToPanicWhenHostilesNearbyTaskConfig::class.java,
|
||||
"calm_down" to CalmDownTaskConfig::class.java,
|
||||
"flee_attacker" to FleeAttackerTaskConfig::class.java,
|
||||
"flee_nearest_hostile" to FleeNearestHostileTaskConfig::class.java,
|
||||
"run_script" to RunScript::class.java,
|
||||
"look_in_direction" to LookInDirectionTaskConfig::class.java,
|
||||
)
|
||||
|
||||
val runtime = MoLangRuntime().setup()
|
||||
|
||||
+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.battles.interpreter
|
||||
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
|
||||
/**
|
||||
* An exception thrown when properties from a [BattleMessage] cannot be interpreted for an [InterpreterInstruction].
|
||||
*
|
||||
* @author Segfault Guy
|
||||
* @since November 10th, 2024
|
||||
*/
|
||||
class InvalidInstructionException(battleMessage: BattleMessage) : Exception("Failed to interpret ${battleMessage.rawMessage}")
|
||||
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.battles.model
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
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.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.CobblemonNetwork
|
||||
@@ -22,7 +21,6 @@ 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.moves.Moves
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore
|
||||
@@ -43,6 +41,7 @@ import com.cobblemon.mod.common.battles.dispatch.WaitDispatch
|
||||
import com.cobblemon.mod.common.battles.interpreter.ContextManager
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.entity.PlatformType
|
||||
import com.cobblemon.mod.common.entity.npc.NPCBattleActor
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
@@ -51,17 +50,16 @@ import com.cobblemon.mod.common.net.messages.client.battle.BattleMessagePacket
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.DefeatEvolutionProgress
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.LastBattleCriticalHitsEvolutionProgress
|
||||
import com.cobblemon.mod.common.pokemon.evolution.requirements.DefeatRequirement
|
||||
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import net.minecraft.network.chat.Component
|
||||
import com.cobblemon.mod.common.util.giveOrDropItemStack
|
||||
import com.cobblemon.mod.common.util.itemRegistry
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import java.io.File
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.concurrent.ConcurrentLinkedDeque
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
@@ -96,7 +94,6 @@ open class PokemonBattle(
|
||||
.filterIsInstance<LastBattleCriticalHitsEvolutionProgress>()
|
||||
.forEach { it.reset() }
|
||||
}
|
||||
actor.setupStruct()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,22 +268,31 @@ open class PokemonBattle(
|
||||
else -> continue
|
||||
}
|
||||
val experience = Cobblemon.experienceCalculator.calculate(opponentPokemon, faintedPokemon, multiplier)
|
||||
if (experience > 0) {
|
||||
if (experience > 0 && actor.pokemonList.all { it.health <= 0 }) {
|
||||
opponent.awardExperience(opponentPokemon, experience)
|
||||
}
|
||||
Cobblemon.evYieldCalculator.calculate(opponentPokemon, faintedPokemon).forEach { (stat, amount) ->
|
||||
pokemon.evs.add(stat, amount)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
if(actor.itemsUsed.isNotEmpty() && actor.getPlayerUUIDs().count() > 0) {
|
||||
if (actor.itemsUsed.isNotEmpty() && actor.getPlayerUUIDs().count() > 0) {
|
||||
val player = actor.getPlayerUUIDs().first().getPlayer()
|
||||
player?.level()?.itemRegistry.let { registry ->
|
||||
actor.itemsUsed.mapNotNull { registry?.get(ResourceLocation.tryBySeparator(it.itemName.substringAfter('.'), '.')) }
|
||||
.forEach { player?.giveOrDropItemStack(ItemStack(it))}
|
||||
}
|
||||
}
|
||||
|
||||
// Heal NPC's team if enabled
|
||||
if (actor is NPCBattleActor) {
|
||||
if (actor.npc.npc.autoHealParty) {
|
||||
actor.npc.party?.heal()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// Heal Mon if wild
|
||||
actors.filter { it.type == ActorType.WILD }
|
||||
@@ -297,6 +303,11 @@ open class PokemonBattle(
|
||||
actors.forEach { actor ->
|
||||
actor.pokemonList.forEach { battlePokemon ->
|
||||
battlePokemon.entity?.let { entity -> battlePokemon.postBattleEntityOperation(entity) }
|
||||
if (battlePokemon.effectedPokemon.entity != null
|
||||
&& battlePokemon.effectedPokemon.entity?.beamMode == 0
|
||||
&& battlePokemon.effectedPokemon.entity?.platform != PlatformType.NONE) {
|
||||
battlePokemon.effectedPokemon.tryRecallWithAnimation()
|
||||
}
|
||||
}
|
||||
}
|
||||
sendUpdate(BattleEndPacket())
|
||||
|
||||
+3
-5
@@ -11,6 +11,7 @@ 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
|
||||
import com.cobblemon.mod.common.battles.ActiveBattlePokemon
|
||||
import com.cobblemon.mod.common.battles.ForcePassActionResponse
|
||||
@@ -36,10 +37,11 @@ abstract class BattleActor(
|
||||
pokemonList.forEach { it.actor = this }
|
||||
}
|
||||
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
val struct = asMoLangValue()
|
||||
|
||||
lateinit var showdownId: String
|
||||
lateinit var battle: PokemonBattle
|
||||
fun isInitialized() = this::battle.isInitialized && this::showdownId.isInitialized
|
||||
|
||||
val activePokemon = mutableListOf<ActiveBattlePokemon>()
|
||||
var canDynamax = false
|
||||
@@ -138,10 +140,6 @@ abstract class BattleActor(
|
||||
battle.writeShowdownAction(">$showdownId ${showdownMessages.joinToString()}")
|
||||
}
|
||||
|
||||
open fun setupStruct() {
|
||||
struct.addFunction("uuid") { StringValue(uuid.toString()) }
|
||||
}
|
||||
|
||||
abstract fun getName(): MutableComponent
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.api.data
|
||||
import com.cobblemon.mod.common.api.net.Decodable
|
||||
import com.cobblemon.mod.common.api.net.Encodable
|
||||
|
||||
// TODO this is implemented in multiple places but unused. Can we scratch this?
|
||||
/**
|
||||
* Represents an object that will possibly be synchronized to the client during datapack synchronization.
|
||||
*
|
||||
|
||||
@@ -9,8 +9,11 @@
|
||||
package com.cobblemon.mod.common.api.data
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.util.endsWith
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import java.io.File
|
||||
import java.util.concurrent.ExecutionException
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.packs.resources.ResourceManager
|
||||
|
||||
@@ -42,7 +45,24 @@ interface JsonDataRegistry<T> : DataRegistry {
|
||||
val resourcePath: String
|
||||
|
||||
override fun reload(manager: ResourceManager) {
|
||||
this.reload(Cobblemon.implementation.reloadJsonRegistry(this, manager))
|
||||
val data = hashMapOf<ResourceLocation, T>()
|
||||
manager.listResources(resourcePath) { path -> path.endsWith(JSON_EXTENSION) }.forEach { (identifier, resource) ->
|
||||
if (identifier.namespace == "pixelmon") {
|
||||
return@forEach
|
||||
}
|
||||
|
||||
resource.open().use { stream ->
|
||||
stream.bufferedReader().use { reader ->
|
||||
val resolvedIdentifier = ResourceLocation.fromNamespaceAndPath(identifier.namespace, File(identifier.path).nameWithoutExtension)
|
||||
try {
|
||||
data[resolvedIdentifier] = gson.fromJson(reader, typeToken.type)
|
||||
} catch (exception: Exception) {
|
||||
throw ExecutionException("Error loading JSON for data: $identifier", exception)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
reload(data)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,13 +21,13 @@ import com.cobblemon.mod.common.util.adapters.DialogueTextAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.ExpressionAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.ExpressionLikeAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.TextAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.TranslatedTextAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
|
||||
/**
|
||||
@@ -56,7 +56,7 @@ object Dialogues : JsonDataRegistry<Dialogue> {
|
||||
.registerTypeAdapter(DialogueText::class.java, DialogueTextAdapter)
|
||||
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
|
||||
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
|
||||
.registerTypeAdapter(MutableComponent::class.java, TextAdapter)
|
||||
.registerTypeAdapter(MutableComponent::class.java, TranslatedTextAdapter)
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.also { gsonObservable.emit(it) }
|
||||
.create()
|
||||
|
||||
@@ -11,11 +11,12 @@ package com.cobblemon.mod.common.api.drop
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.LOOT_DROPPED
|
||||
import com.cobblemon.mod.common.api.events.drops.LootDroppedEvent
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import kotlin.random.Random
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
* A table of drops that can produce a list of [DropEntry]. You can produce a drop list from [getDrops] to
|
||||
@@ -96,4 +97,14 @@ class DropTable {
|
||||
ifSucceeded = { it.drops.forEach { it.drop(entity, world, pos, player) } }
|
||||
)
|
||||
}
|
||||
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeInt(this.entries.size)
|
||||
this.entries.filterIsInstance<ItemDropEntry>().forEach { it.encode(buffer) }
|
||||
}
|
||||
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
val entries = buffer.readInt()
|
||||
repeat(entries) { this.entries.add(ItemDropEntry().decode(buffer)) }
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,10 @@ import com.cobblemon.mod.common.util.toBlockPos
|
||||
import net.minecraft.core.component.DataComponentMap
|
||||
import net.minecraft.core.component.DataComponentPatch
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.item.ItemStack
|
||||
@@ -33,12 +34,12 @@ import net.minecraft.world.phys.Vec3
|
||||
* @since July 24th, 2022
|
||||
*/
|
||||
open class ItemDropEntry : DropEntry {
|
||||
override val percentage = 100F
|
||||
override val quantity = 1
|
||||
open val quantityRange: IntRange? = null
|
||||
override val maxSelectableTimes = 1
|
||||
override var percentage = 100F
|
||||
override var quantity = 1
|
||||
open var quantityRange: IntRange? = null
|
||||
override var maxSelectableTimes = 1
|
||||
open val dropMethod: ItemDropMethod? = null
|
||||
open val item = ResourceLocation.parse("minecraft:fish")
|
||||
open var item = ResourceLocation.parse("minecraft:fish")
|
||||
open val components: DataComponentMap? = null
|
||||
|
||||
override fun drop(entity: LivingEntity?, world: ServerLevel, pos: Vec3, player: ServerPlayer?) {
|
||||
@@ -60,7 +61,7 @@ open class ItemDropEntry : DropEntry {
|
||||
|
||||
if (dropMethod == ItemDropMethod.ON_PLAYER && player != null) {
|
||||
world.addFreshEntity(ItemEntity(player.level(), player.x, player.y, player.z, stack))
|
||||
} else if (dropMethod == ItemDropMethod.TO_INVENTORY && player != null) {
|
||||
} else if (dropMethod == ItemDropMethod.TO_INVENTORY && player != null && !stack.isEmpty) {
|
||||
val name = stack.hoverName
|
||||
val count = stack.count
|
||||
val succeeded = player.addItem(stack)
|
||||
@@ -76,4 +77,19 @@ open class ItemDropEntry : DropEntry {
|
||||
world.addFreshEntity(ItemEntity(world, pos.x, pos.y, pos.z, stack))
|
||||
}
|
||||
}
|
||||
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeFloat(this.percentage)
|
||||
buffer.writeVarInt(this.quantity)
|
||||
buffer.writeResourceLocation(this.item)
|
||||
buffer.writeNullable(this.quantityRange) { _, it -> buffer.writeVarInt(it.first); buffer.writeVarInt(it.last) }
|
||||
}
|
||||
|
||||
fun decode(buffer: RegistryFriendlyByteBuf): ItemDropEntry {
|
||||
this.percentage = buffer.readFloat()
|
||||
this.quantity = buffer.readVarInt()
|
||||
this.item = buffer.readResourceLocation()
|
||||
this.quantityRange = buffer.readNullable { buffer.readVarInt()..buffer.readVarInt() }
|
||||
return this
|
||||
}
|
||||
}
|
||||
@@ -8,11 +8,13 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.entity.pokemon
|
||||
|
||||
import com.cobblemon.mod.common.api.pokeball.PokeBalls
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.effects.IllusionEffect
|
||||
import com.cobblemon.mod.common.entity.pokemon.effects.TransformEffect
|
||||
import com.cobblemon.mod.common.pokeball.PokeBall
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
@@ -95,4 +97,7 @@ interface MocKEffect : PhysicalEffect {
|
||||
get() = this.mock.form?.let {
|
||||
formID -> this.exposedSpecies?.forms?.firstOrNull { it.formOnlyShowdownId().equals(formID, true) } }
|
||||
?: this.exposedSpecies?.standardForm
|
||||
|
||||
val exposedBall: PokeBall?
|
||||
get() = this.mock.pokeball?.let { PokeBalls.getPokeBall(it.asIdentifierDefaultingNamespace()) }
|
||||
}
|
||||
|
||||
@@ -54,6 +54,10 @@ import net.minecraft.server.level.ServerPlayer
|
||||
@Suppress("unused")
|
||||
object CobblemonEvents {
|
||||
|
||||
@JvmField
|
||||
val POKEMON_PROPERTY_INITIALISED = SimpleObservable<Unit>()
|
||||
@JvmField
|
||||
val COBBLEMON_INITIALISED = SimpleObservable<Unit>()
|
||||
@JvmField
|
||||
val DATA_SYNCHRONIZED = SimpleObservable<ServerPlayer>()
|
||||
@JvmField
|
||||
@@ -96,7 +100,7 @@ object CobblemonEvents {
|
||||
|
||||
// instructions
|
||||
@JvmField
|
||||
val MEGA_EVOLUTON = EventObservable<MegaEvolutionEvent>()
|
||||
val MEGA_EVOLUTION = EventObservable<MegaEvolutionEvent>()
|
||||
@JvmField
|
||||
val TERASTALLIZATION = EventObservable<TerastallizationEvent>()
|
||||
@JvmField
|
||||
@@ -129,6 +133,8 @@ object CobblemonEvents {
|
||||
val POKEMON_ENTITY_SAVE_TO_WORLD = CancelableObservable<PokemonEntitySaveToWorldEvent>()
|
||||
@JvmField
|
||||
val ENTITY_SPAWN = CancelableObservable<SpawnEvent<*>>()
|
||||
@JvmField
|
||||
val SHINY_CHANCE_CALCULATION = EventObservable<ShinyChanceCalculationEvent>()
|
||||
|
||||
@JvmField
|
||||
val POKEMON_ENTITY_SPAWN = ENTITY_SPAWN
|
||||
@@ -186,6 +192,10 @@ object CobblemonEvents {
|
||||
val POKEMON_GAINED = EventObservable<PokemonGainedEvent>()
|
||||
@JvmField
|
||||
val POKEMON_SEEN = EventObservable<PokemonSeenEvent>()
|
||||
@JvmField
|
||||
val POKEDEX_DATA_CHANGED_PRE = CancelableObservable<PokedexDataChangedEvent.Pre>()
|
||||
@JvmField
|
||||
val POKEDEX_DATA_CHANGED_POST = EventObservable<PokedexDataChangedEvent.Post>()
|
||||
|
||||
// Fishing
|
||||
@JvmField
|
||||
|
||||
+4
-4
@@ -19,8 +19,8 @@ import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
* @author Segfault Guy
|
||||
* @since April 6th, 2023
|
||||
*/
|
||||
data class BattleFaintedEvent (
|
||||
override val battle: PokemonBattle,
|
||||
val killed: BattlePokemon,
|
||||
val context: BattleContext
|
||||
data class BattleFaintedEvent(
|
||||
override val battle: PokemonBattle,
|
||||
val killed: BattlePokemon,
|
||||
val context: BattleContext
|
||||
) : BattleEvent
|
||||
|
||||
@@ -18,8 +18,6 @@ import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
|
||||
* @since March 25th 2023
|
||||
*/
|
||||
data class BattleFledEvent (
|
||||
|
||||
override val battle: PokemonBattle,
|
||||
val player: PlayerBattleActor
|
||||
|
||||
) : BattleEvent
|
||||
+23
-1
@@ -8,8 +8,14 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.battles
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
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.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
|
||||
import com.cobblemon.mod.common.entity.npc.NPCBattleActor
|
||||
import com.cobblemon.mod.common.util.asArrayValue
|
||||
|
||||
/**
|
||||
* Event fired when a battle is won by some number of [BattleActor]s.
|
||||
@@ -22,4 +28,20 @@ data class BattleVictoryEvent (
|
||||
val winners : List<BattleActor>,
|
||||
val losers : List<BattleActor>,
|
||||
val wasWildCapture : Boolean
|
||||
) : BattleEvent
|
||||
) : BattleEvent {
|
||||
val context = mutableMapOf<String, MoValue>(
|
||||
"battle" to battle.struct,
|
||||
"winners" to winners.asArrayValue { it.struct },
|
||||
"losers" to losers.asArrayValue { it.struct },
|
||||
"was_wild_capture" to DoubleValue(wasWildCapture),
|
||||
"player_winners" to winners.filter { it.type == ActorType.PLAYER }.asArrayValue { it.struct },
|
||||
"player_losers" to losers.filter { it.type == ActorType.PLAYER }.asArrayValue { it.struct },
|
||||
"npc_winners" to winners.filter { it.type == ActorType.NPC }.asArrayValue { it.struct },
|
||||
"npc_losers" to losers.filter { it.type == ActorType.NPC }.asArrayValue { it.struct },
|
||||
"wild_pokemon_winners" to winners.filter { it.type == ActorType.WILD }.asArrayValue { it.struct },
|
||||
"wild_pokemon_losers" to losers.filter { it.type == ActorType.WILD }.asArrayValue { it.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 }
|
||||
)
|
||||
}
|
||||
+60
@@ -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.api.events.pokemon
|
||||
|
||||
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.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.pokedex.AbstractPokedexManager
|
||||
import com.cobblemon.mod.common.api.pokedex.FormDexRecord
|
||||
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
|
||||
import com.cobblemon.mod.common.pokedex.scanner.PokedexEntityData
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import com.mojang.datafixers.util.Either
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Event that fires when a Pokémon's information is gained or updated in the Pokédex.
|
||||
*/
|
||||
sealed interface PokedexDataChangedEvent {
|
||||
val dataSource: Either<PokedexEntityData, Pokemon>
|
||||
val knowledge: PokedexEntryProgress
|
||||
val playerUUID: UUID
|
||||
val record: FormDexRecord
|
||||
|
||||
val pokedexManager: AbstractPokedexManager
|
||||
get() = record.speciesDexRecord.pokedexManager
|
||||
|
||||
fun getContext(): MutableMap<String, MoValue> {
|
||||
return mutableMapOf(
|
||||
"player" to (playerUUID.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
|
||||
"pokemon" to dataSource.map({ DoubleValue.ZERO }, { it.struct }),
|
||||
"data" to dataSource.map({ it.struct }, { DoubleValue.ZERO }),
|
||||
"knowledge" to StringValue(knowledge.name.lowercase()),
|
||||
"pokedex" to pokedexManager.struct
|
||||
)
|
||||
}
|
||||
|
||||
class Pre(
|
||||
override val dataSource: Either<PokedexEntityData, Pokemon>,
|
||||
override val knowledge: PokedexEntryProgress,
|
||||
override val playerUUID: UUID,
|
||||
override val record: FormDexRecord
|
||||
) : PokedexDataChangedEvent, Cancelable()
|
||||
|
||||
class Post(
|
||||
override val dataSource: Either<PokedexEntityData, Pokemon>,
|
||||
override val knowledge: PokedexEntryProgress,
|
||||
override val playerUUID: UUID,
|
||||
override val record: FormDexRecord
|
||||
) : PokedexDataChangedEvent
|
||||
}
|
||||
+12
-1
@@ -8,12 +8,23 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokemon
|
||||
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.itemRegistry
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
data class PokemonCapturedEvent (
|
||||
val pokemon: Pokemon,
|
||||
val player: ServerPlayer,
|
||||
val pokeBallEntity: EmptyPokeBallEntity
|
||||
)
|
||||
) {
|
||||
val context = mutableMapOf<String, MoValue>(
|
||||
"pokemon" to pokemon.struct,
|
||||
"player" to player.asMoLangValue(),
|
||||
"poke_ball" to pokeBallEntity.struct,
|
||||
"item" to player.level().itemRegistry.wrapAsHolder(pokeBallEntity.pokeBall.item).asMoLangValue(Registries.ITEM)
|
||||
)
|
||||
}
|
||||
+1
-2
@@ -14,5 +14,4 @@ import java.util.UUID
|
||||
data class PokemonGainedEvent(
|
||||
val playerId: UUID,
|
||||
val pokemon: Pokemon
|
||||
) {
|
||||
}
|
||||
)
|
||||
+1
-3
@@ -9,11 +9,9 @@
|
||||
package com.cobblemon.mod.common.api.events.pokemon
|
||||
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import java.util.UUID
|
||||
|
||||
data class PokemonSeenEvent(
|
||||
val playerId: UUID,
|
||||
val pokemon: Pokemon
|
||||
) {
|
||||
}
|
||||
)
|
||||
+57
@@ -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.events.pokemon
|
||||
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
/**
|
||||
* An event to modify the shiny chance of a Pokémon.
|
||||
*/
|
||||
class ShinyChanceCalculationEvent(
|
||||
val baseChance: Float,
|
||||
val pokemon: Pokemon
|
||||
) {
|
||||
val chance: Float = baseChance
|
||||
var isShiny: Boolean = false
|
||||
private val modifiers = mutableListOf<Float>()
|
||||
private val modificationFunctions = mutableListOf<(Float, ServerPlayer?, Pokemon) -> Float>()
|
||||
|
||||
/**
|
||||
* Adds a modifier to the shiny chance.
|
||||
*/
|
||||
fun addModifier(modifier: Float) {
|
||||
modifiers.add(modifier)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a function to modify the shiny chance.
|
||||
*/
|
||||
fun addModificationFunction(function: (Float, ServerPlayer?, Pokemon) -> Float) {
|
||||
modificationFunctions.add(function)
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the shiny chance of a Pokémon.
|
||||
*/
|
||||
fun calculate(player: ServerPlayer?): Float {
|
||||
var result = baseChance
|
||||
for (modifier in modifiers) {
|
||||
result += modifier
|
||||
}
|
||||
for (function in modificationFunctions) {
|
||||
result = function(result, player, pokemon)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun isShiny(player: ServerPlayer?): Boolean {
|
||||
return calculate(player) >= chance || isShiny
|
||||
}
|
||||
}
|
||||
@@ -13,11 +13,12 @@ import com.cobblemon.mod.common.api.events.fishing.BaitEffectFunctionRegistryEve
|
||||
import com.cobblemon.mod.common.api.spawning.fishing.FishingSpawnCause
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import java.util.Optional
|
||||
import net.minecraft.core.Registry
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.ListTag
|
||||
import net.minecraft.network.codec.ByteBufCodecs
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
@@ -33,46 +34,30 @@ data class FishingBait(
|
||||
val chance: Double = 0.0,
|
||||
val value: Double = 0.0
|
||||
) {
|
||||
fun toNbt(): CompoundTag {
|
||||
val nbt = CompoundTag()
|
||||
nbt.putString("Type", type.toString())
|
||||
subcategory?.let { nbt.putString("Subcategory", it.toString()) }
|
||||
nbt.putDouble("Chance", chance)
|
||||
nbt.putDouble("Value", value)
|
||||
return nbt
|
||||
}
|
||||
constructor(type: ResourceLocation, subcategory: Optional<ResourceLocation>, chance: Double, value: Double) : this(type, subcategory.orElse(null), chance, value)
|
||||
|
||||
companion object {
|
||||
fun fromNbt(nbt: CompoundTag): Effect {
|
||||
val type = ResourceLocation.parse(nbt.getString("Type"))
|
||||
val subcategory = if (nbt.contains("Subcategory")) ResourceLocation.parse(nbt.getString("Subcategory")) else null
|
||||
val chance = nbt.getDouble("Chance")
|
||||
val value = nbt.getDouble("Value")
|
||||
return Effect(type, subcategory, chance, value)
|
||||
val CODEC = RecordCodecBuilder.create<Effect> { instance ->
|
||||
instance.group(
|
||||
ResourceLocation.CODEC.fieldOf("type").forGetter { it.type },
|
||||
ResourceLocation.CODEC.optionalFieldOf("subcategory").forGetter { Optional.ofNullable(it.subcategory) },
|
||||
Codec.DOUBLE.fieldOf("chance").forGetter { it.chance },
|
||||
Codec.DOUBLE.fieldOf("value").forGetter { it.value }
|
||||
).apply(instance, ::Effect)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun toNbt(): CompoundTag {
|
||||
val nbt = CompoundTag()
|
||||
nbt.putString("Item", item.toString())
|
||||
val effectsList = ListTag()
|
||||
effects.forEach { effectsList.add(it.toNbt()) }
|
||||
nbt.put("Effects", effectsList)
|
||||
return nbt
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromNbt(nbt: CompoundTag): FishingBait {
|
||||
val item = ResourceLocation.parse(nbt.getString("Item"))
|
||||
val effectsList = nbt.getList("Effects", 10) // 10 is the type for NbtCompound
|
||||
val effects = mutableListOf<Effect>()
|
||||
for (i in 0 until effectsList.size) {
|
||||
effects.add(Effect.fromNbt(effectsList.getCompound(i)))
|
||||
}
|
||||
return FishingBait(item, effects)
|
||||
val CODEC = RecordCodecBuilder.create<FishingBait> { instance ->
|
||||
instance.group(
|
||||
ResourceLocation.CODEC.fieldOf("item").forGetter { it.item },
|
||||
Effect.CODEC.listOf().fieldOf("effects").forGetter {it.effects}
|
||||
).apply(instance, ::FishingBait)
|
||||
}
|
||||
|
||||
val STREAM_CODEC = ByteBufCodecs.fromCodec(CODEC)
|
||||
|
||||
val BLANK_BAIT = FishingBait(
|
||||
cobblemonResource("blank"),
|
||||
emptyList()
|
||||
@@ -87,7 +72,8 @@ data class FishingBait(
|
||||
val BITE_TIME = cobblemonResource("bite_time")
|
||||
val GENDER_CHANCE = cobblemonResource("gender_chance")
|
||||
val LEVEL_RAISE = cobblemonResource("level_raise")
|
||||
val TERA = cobblemonResource("tera")
|
||||
val TYPING = cobblemonResource("typing")
|
||||
val EGG_GROUP = cobblemonResource("egg_group")
|
||||
val SHINY_REROLL = cobblemonResource("shiny_reroll")
|
||||
val HIDDEN_ABILITY_CHANCE = cobblemonResource("ha_chance")
|
||||
val POKEMON_CHANCE = cobblemonResource("pokemon_chance")
|
||||
@@ -108,7 +94,6 @@ data class FishingBait(
|
||||
EFFECT_FUNCTIONS[SHINY_REROLL] = { entity, effect -> FishingSpawnCause.shinyReroll(entity, effect) }
|
||||
EFFECT_FUNCTIONS[GENDER_CHANCE] = { entity, effect -> FishingSpawnCause.alterGenderAttempt(entity, effect) }
|
||||
EFFECT_FUNCTIONS[LEVEL_RAISE] = { entity, effect -> FishingSpawnCause.alterLevelAttempt(entity, effect) }
|
||||
EFFECT_FUNCTIONS[TERA] = { entity, effect -> FishingSpawnCause.alterTeraAttempt(entity, effect) }
|
||||
EFFECT_FUNCTIONS[HIDDEN_ABILITY_CHANCE] = { entity, _ -> FishingSpawnCause.alterHAAttempt(entity) }
|
||||
EFFECT_FUNCTIONS[FRIENDSHIP] = { entity, effect -> FishingSpawnCause.alterFriendshipAttempt(entity, effect) }
|
||||
CobblemonEvents.BAIT_EFFECT_REGISTRATION.post(BaitEffectFunctionRegistryEvent()) { event ->
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.gui
|
||||
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import net.minecraft.client.gui.components.AbstractWidget
|
||||
import net.minecraft.client.gui.components.Renderable
|
||||
import net.minecraft.client.gui.components.events.GuiEventListener
|
||||
@@ -22,9 +23,9 @@ abstract class ParentWidget(
|
||||
pX: Int, pY: Int,
|
||||
pWidth: Int, pHeight: Int,
|
||||
component: Component
|
||||
): Renderable, AbstractWidget(pX, pY, pWidth, pHeight, component) {
|
||||
): CobblemonRenderable, AbstractWidget(pX, pY, pWidth, pHeight, component) {
|
||||
|
||||
private val children: MutableList<GuiEventListener> = mutableListOf()
|
||||
val children: MutableList<GuiEventListener> = mutableListOf()
|
||||
|
||||
/**
|
||||
* Adds Widget to the children list
|
||||
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.interaction
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonNetwork
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.api.text.yellow
|
||||
import com.cobblemon.mod.common.battles.TeamManager
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Represents an interaction request between players.
|
||||
*
|
||||
* @author Segfault Guy
|
||||
* @since September 3rd, 2024
|
||||
*/
|
||||
interface PlayerActionRequest {
|
||||
/** The unique ID of this request. */
|
||||
val requestID: UUID
|
||||
|
||||
/** The unique ID of the party that's initiating this request. */
|
||||
val senderID: UUID
|
||||
|
||||
/** The amount of seconds this request is valid for. */
|
||||
val expiryTime: Int
|
||||
}
|
||||
|
||||
/**
|
||||
* An outbound [PlayerActionRequest].
|
||||
*
|
||||
* @author Segfault Guy
|
||||
* @since September 29th, 2024
|
||||
*/
|
||||
interface ServerPlayerActionRequest : PlayerActionRequest {
|
||||
|
||||
/** The subkey identifying lang entries associated with this request. */
|
||||
val key: String
|
||||
|
||||
/** The player initiating this interaction request. */
|
||||
val sender: ServerPlayer
|
||||
|
||||
/** The player that's the target of this interaction request. */
|
||||
val receiver: ServerPlayer
|
||||
|
||||
/** The unique ID of the party that's the target of this request. */
|
||||
val receiverID: UUID get() = receiver.uuid
|
||||
|
||||
override val senderID: UUID get() = sender.uuid
|
||||
|
||||
/** Sends [packet] to sending party of this request. */
|
||||
fun sendToSender(packet: NetworkPacket<*>) = sender.sendPacket(packet)
|
||||
|
||||
/** Sends [packet] to receiving party of this request. */
|
||||
fun sendToReceiver(packet: NetworkPacket<*>) = receiver.sendPacket(packet)
|
||||
|
||||
/** Notifies the sending party of this request about [langKey]. */
|
||||
fun notifySender(error: Boolean, langKey: String, vararg params: Any) = notify(sender, error, "$key.$langKey", *params)
|
||||
|
||||
/** Notifies the receiving party of this request about [langKey]. */
|
||||
fun notifyReceiver(error: Boolean, langKey: String, vararg params: Any) = notify(receiver, error, "$key.$langKey", *params)
|
||||
|
||||
/** System message to inform individual [player] about [langKey]. */
|
||||
fun notify(player: ServerPlayer, error: Boolean, langKey: String, vararg params: Any) {
|
||||
val lang = lang(langKey, *params).apply { if (error) red() else yellow() }
|
||||
player.sendSystemMessage(lang, false)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An outbound [PlayerActionRequest] made on behalf of a team.
|
||||
*
|
||||
* @author Segfault Guy
|
||||
* @since October 29th, 2024
|
||||
*/
|
||||
interface ServerTeamActionRequest : ServerPlayerActionRequest {
|
||||
|
||||
/** The team initiating this request. */
|
||||
val senderTeam: TeamManager.MultiBattleTeam
|
||||
|
||||
/** The team that's the target of this request. */
|
||||
val receiverTeam: TeamManager.MultiBattleTeam
|
||||
|
||||
override val senderID: UUID get() = senderTeam.teamID
|
||||
|
||||
override val receiverID: UUID get() = receiverTeam.teamID
|
||||
|
||||
override fun sendToSender(packet: NetworkPacket<*>) = CobblemonNetwork.sendPacketToPlayers(senderTeam.teamPlayers, packet)
|
||||
|
||||
override fun sendToReceiver(packet: NetworkPacket<*>) = CobblemonNetwork.sendPacketToPlayers(receiverTeam.teamPlayers, packet)
|
||||
|
||||
override fun notifySender(error: Boolean, langKey: String, vararg params: Any) =
|
||||
senderTeam.teamPlayers.forEach { this.notify(it, error, "$key.$langKey", *params) }
|
||||
|
||||
override fun notifyReceiver(error: Boolean, langKey: String, vararg params: Any) =
|
||||
receiverTeam.teamPlayers.forEach { this.notify(it, error, "$key.$langKey", *params) }
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* 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.interaction
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.api.scheduling.afterOnServer
|
||||
import com.cobblemon.mod.common.api.text.aqua
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.battles.TeamManager
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Defines a manager for organizing active interaction requests between parties. Responsible for dispatching requests to clients,
|
||||
* notifying senders and receivers of requests, and handling their acceptance, expiration, or cancellation.
|
||||
*
|
||||
* Implementations can manage either PLAYER-to-PLAYER requests or TEAM-to-TEAM requests.
|
||||
*
|
||||
* @param T The type of interaction request this manager tracks.
|
||||
*
|
||||
* @author Segfault Guy
|
||||
* @since October 26th, 2024
|
||||
*/
|
||||
abstract class RequestManager<T : ServerPlayerActionRequest> {
|
||||
|
||||
/**
|
||||
* Tracking of the active [ServerPlayerActionRequest]s by their sender.
|
||||
*
|
||||
* NOTE: only 1 request can be sent by a sender at a time.
|
||||
*/
|
||||
private val outboundRequests = mutableMapOf<UUID, T>()
|
||||
|
||||
/**
|
||||
* Tracking of the active [ServerPlayerActionRequest]s by their recipient.
|
||||
*
|
||||
* NOTE: a recipient can receive multiple requests from different senders at the same time.
|
||||
*/
|
||||
private val inboundRequests = mutableMapOf<UUID, MutableList<T>>()
|
||||
|
||||
// we use the term 'sender' because BattleChallenges can be sent TEAM-to-TEAM instead of PLAYER-to-PLAYER for multi battles
|
||||
|
||||
/** Queries the latest pending request that was sent by [senderID]. */
|
||||
fun getOutboundRequest(senderID: UUID) = outboundRequests.get(senderID)
|
||||
|
||||
/** Queries the pending request of [requestID] that was sent by [senderID]. */
|
||||
fun getOutboundRequest(senderID: UUID, requestID: UUID) = this.getOutboundRequest(senderID)?.takeIf { it.requestID == requestID }
|
||||
|
||||
/** Queries the pending request that was sent by [senderID] and received by [receiverID]. */
|
||||
fun getOutboundRequestByRecipient(senderID: UUID, receiverID: UUID) = this.getOutboundRequest(senderID)?.takeIf { it.receiverID == receiverID }
|
||||
|
||||
/** Queries the pending request of [requestID] that was sent by [sender] or their team. */
|
||||
fun getOutboundRequest(sender: ServerPlayer, requestID: UUID) = TeamManager.getTeam(sender)?.let { this.getOutboundRequest(it.teamID, requestID) }
|
||||
?: this.getOutboundRequest(sender.uuid, requestID)
|
||||
|
||||
/** Queries all pending requests that were received by [receiverID]. */
|
||||
fun getInboundRequests(receiverID: UUID) = inboundRequests.get(receiverID)
|
||||
|
||||
/** Queries the pending request of [requestID] that was received by [receiverID]. */
|
||||
fun getInboundRequest(receiverID: UUID, requestID: UUID) = this.getInboundRequests(receiverID)?.find { it.requestID == requestID }
|
||||
|
||||
/** Queries the pending request that was sent by [senderID] and received by [receiverID]. */
|
||||
fun getInboundRequestBySender(receiverID: UUID, senderID: UUID) = this.getInboundRequests(receiverID)?.find { it.senderID == senderID }
|
||||
|
||||
/** Queries the pending request of [requestID] that was received by [receiver] or their team. */
|
||||
fun getInboundRequest(receiver: ServerPlayer, requestID: UUID) = TeamManager.getTeam(receiver)?.let { this.getInboundRequest(it.teamID, requestID) }
|
||||
?: this.getInboundRequest(receiver.uuid, requestID)
|
||||
|
||||
/** Determines whether [player] can receive a [T]. */
|
||||
open fun isBusy(player: ServerPlayer): Boolean = player.isInBattle() || player.isTrading() || player.party().find { it.entity?.isBusy == true } != null
|
||||
|
||||
/** Determines if the [target] is a valid interaction request target. */
|
||||
abstract fun isValidInteraction(player: ServerPlayer, target: ServerPlayer): Boolean
|
||||
|
||||
/** Determines whether [request] response is valid and can be accepted. If invalid, notifies the sender and receiver of the request why. */
|
||||
protected abstract fun canAccept(request: T): Boolean
|
||||
|
||||
/** Creates a packet for notifying a recipient about a received request. */
|
||||
protected abstract fun notificationPacket(request: T): NetworkPacket<*>
|
||||
|
||||
/** Creates a packet for canceling received requests. */
|
||||
protected abstract fun expirationPacket(request: T): NetworkPacket<*>
|
||||
|
||||
protected fun addRequest(request: T) {
|
||||
outboundRequests.put(request.senderID, request)
|
||||
inboundRequests.computeIfAbsent(request.receiverID) { mutableListOf() }.add(request)
|
||||
request.sendToReceiver(this.notificationPacket(request))
|
||||
}
|
||||
|
||||
protected fun removeRequest(request: T): Boolean {
|
||||
val inbound = inboundRequests.get(request.receiverID)
|
||||
val removeOutbound = outboundRequests.remove(request.senderID, request)
|
||||
val removeInbound = inbound?.remove(request) == true
|
||||
if (removeInbound && inbound?.size == 0) inboundRequests.remove(request.receiverID, inbound)
|
||||
if (removeInbound != removeOutbound)
|
||||
Cobblemon.LOGGER.error("RequestManager: PlayerActionRequest desync") // USE ADDREQUEST AND REMOVEREQUEST
|
||||
|
||||
val isRemoved = removeInbound && removeOutbound
|
||||
if (isRemoved) request.sendToReceiver(this.expirationPacket(request))
|
||||
return isRemoved
|
||||
}
|
||||
|
||||
/** Notifies and removes an outbound request [T] sent by [ServerPlayerActionRequest.sender] to [ServerPlayerActionRequest.receiver]. */
|
||||
open fun cancelRequest(request: T, expired: Boolean = false) {
|
||||
if (!this.removeRequest(request)) return
|
||||
// canceled by expiration
|
||||
if (expired) {
|
||||
request.notifySender(true, "expired.sender", request.receiver.name.copy().aqua())
|
||||
request.notifyReceiver(true, "expired.receiver", request.sender.name.copy().aqua())
|
||||
}
|
||||
// canceled by sender
|
||||
else {
|
||||
request.notifySender(true, "canceled.sender", request.receiver.name.copy().aqua())
|
||||
request.notifyReceiver(true, "canceled.receiver", request.sender.name.copy().aqua())
|
||||
}
|
||||
// receivers DECLINE, not cancel
|
||||
}
|
||||
|
||||
/** Hook into [sendRequest] immediately after the [request] is sent and added to the manager. Default behavior is to notify parties. */
|
||||
protected open fun onSend(request: T) {
|
||||
request.notifySender(false, "sent", request.receiver.name.copy().aqua())
|
||||
request.notifyReceiver(false, "received", request.sender.name.copy().aqua())
|
||||
}
|
||||
|
||||
/** Sends an outbound request [T] from [ServerPlayerActionRequest.sender] to [ServerPlayerActionRequest.receiver]. */
|
||||
fun sendRequest(request: T): Boolean {
|
||||
val existingRequest = this.getOutboundRequest(request.senderID)
|
||||
val pendingRequest = this.getInboundRequestBySender(request.senderID, request.receiverID)
|
||||
|
||||
// old request
|
||||
if (existingRequest != null && existingRequest.receiverID != request.receiverID)
|
||||
this.cancelRequest(existingRequest)
|
||||
// if sender already has a request awaiting a response from the receiver they're trying to send to.
|
||||
if (existingRequest != null && existingRequest.receiverID == request.receiverID)
|
||||
request.notify(request.sender, true, "${request.key}.error.duplicate", request.receiver.name.copy().aqua())
|
||||
// if sender already has a pending request from the receiver they're trying to send to.
|
||||
else if (pendingRequest != null)
|
||||
request.notify(request.sender, true, "${request.key}.error.pending", request.receiver.name.copy().aqua())
|
||||
// verify sending player can interact with target player
|
||||
else if (!this.isValidInteraction(request.sender, request.receiver))
|
||||
request.notify(request.sender, true, "ui.interact.failed")
|
||||
else if (this.isBusy(request.receiver)) // TODO call to isBusy when setting up the interaction packet
|
||||
request.notify(request.sender, true, "ui.interact.unavailable")
|
||||
// new request
|
||||
else {
|
||||
this.addRequest(request)
|
||||
afterOnServer(seconds = request.expiryTime.toFloat()) { this.cancelRequest(request, true) }
|
||||
this.onSend(request)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/** Hook into [acceptRequest] immediately after the [request] is accepted and removed from the manager. Default behavior is to notify parties. */
|
||||
protected open fun onAccept(request: T) {
|
||||
request.notifySender(false, "accept.sender", request.receiver.name.copy().aqua())
|
||||
request.notifyReceiver(false, "accept.receiver", request.sender.name.copy().aqua())
|
||||
}
|
||||
|
||||
/** Accepts a pending inbound [requestID] for [player]. */
|
||||
fun acceptRequest(player: ServerPlayer, requestID: UUID, target: ServerPlayer? = null): Boolean {
|
||||
var accepted = false
|
||||
val request = this.getInboundRequest(player, requestID)
|
||||
|
||||
// verify request being responded to still valid
|
||||
if (request == null)
|
||||
player.sendSystemMessage(lang("ui.interact.request_already_expired").red(), false)
|
||||
// verify accepting player can respond to sending player
|
||||
else if (!this.isValidInteraction(player, target ?: request.sender)) // with teams anyone can accept
|
||||
request.notify(player, true, "ui.interact.failed")
|
||||
// if sending player is occupied, can't accept response
|
||||
else if (this.isBusy(request.sender) || this.isBusy(request.receiver)) // TODO enhancement: allow retries - modify client so we don't remove request when sending acceptance
|
||||
request.notify(player, true, "ui.interact.unavailable")
|
||||
// if no condition is blocking acceptance, accept
|
||||
else if (this.canAccept(request))
|
||||
accepted = true
|
||||
|
||||
request?.let {
|
||||
this.removeRequest(it)
|
||||
if (accepted) this.onAccept(request) // order matters
|
||||
}
|
||||
return accepted
|
||||
}
|
||||
|
||||
/** Hook into [declineRequest] immediately after the [request] is declined and removed from the manager. Default behavior is to notify parties. */
|
||||
protected open fun onDecline(request: T) {
|
||||
request.notifySender(true, "decline.sender", request.receiver.name.copy().aqua())
|
||||
request.notifyReceiver(false, "decline.receiver", request.sender.name.copy().aqua())
|
||||
}
|
||||
|
||||
/** Declines a pending [request]. */
|
||||
fun declineRequest(request: T) {
|
||||
this.removeRequest(request)
|
||||
this.onDecline(request)
|
||||
}
|
||||
|
||||
/** Declines an inbound request [requestID] for [receiver]. */
|
||||
fun declineRequest(receiver: ServerPlayer, requestID: UUID) {
|
||||
val request = this.getInboundRequest(receiver, requestID) ?: return
|
||||
this.declineRequest(request)
|
||||
}
|
||||
|
||||
/** Cancels pending outbound requests sent from, and pending inbound request sent to, [player]. */
|
||||
protected open fun onLogoff(player: ServerPlayer) {
|
||||
// ONLY regarding the player. see TeamManager for how team requests are canceled on team disband.
|
||||
outboundRequests.get(player.uuid)?.let { request ->
|
||||
this.cancelRequest(request)
|
||||
}
|
||||
inboundRequests.get(player.uuid)?.toMutableList()?.let { requests ->
|
||||
requests.forEach { request ->
|
||||
this.declineRequest(player, request.requestID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val managers = mutableListOf<RequestManager<*>>()
|
||||
|
||||
fun register(manager: RequestManager<*>) = managers.add(manager)
|
||||
|
||||
fun onLogoff(player: ServerPlayer) = managers.forEach { it.onLogoff(player) }
|
||||
}
|
||||
}
|
||||
@@ -38,8 +38,9 @@ import net.minecraft.world.phys.AABB
|
||||
*/
|
||||
interface PokemonSelectingItem {
|
||||
fun use(player: ServerPlayer, stack: ItemStack): InteractionResultHolder<ItemStack> {
|
||||
val range = player.entityInteractionRange()
|
||||
val entity = player.level()
|
||||
.getEntities(player, AABB.ofSize(player.position(), 16.0, 16.0, 16.0))
|
||||
.getEntities(player, AABB.ofSize(player.position(), range, range, range))
|
||||
.filter { player.isLookingAt(it, stepDistance = 0.1F) }
|
||||
.minByOrNull { it.distanceTo(player) } as? PokemonEntity?
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.item.ability
|
||||
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
import com.cobblemon.mod.common.api.abilities.*
|
||||
import com.cobblemon.mod.common.item.interactive.ability.AbilityTypeChanger
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
@@ -33,7 +34,7 @@ interface AbilityChanger<T : PotentialAbility> {
|
||||
* @param pokemon The [Pokemon] being queried.
|
||||
* @return A set containing all their legal abilities associated with [type], can be empty.
|
||||
*/
|
||||
fun queryPossible(pokemon: Pokemon): Set<AbilityTemplate>
|
||||
fun queryPossible(pokemon: Pokemon): Set<Pair<AbilityTemplate, Priority>>
|
||||
|
||||
/**
|
||||
* Attempts to update the ability of the given [pokemon].
|
||||
|
||||
@@ -22,27 +22,56 @@ import com.cobblemon.mod.common.CobblemonActivities
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.api.dialogue.PlayerDialogueFaceProvider
|
||||
import com.cobblemon.mod.common.api.dialogue.ReferenceDialogueFaceProvider
|
||||
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.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.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.storage.PokemonStore
|
||||
import com.cobblemon.mod.common.api.storage.party.PartyStore
|
||||
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore
|
||||
import com.cobblemon.mod.common.api.storage.pc.PCPosition
|
||||
import com.cobblemon.mod.common.api.storage.pc.PCStore
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
|
||||
import com.cobblemon.mod.common.client.particle.BedrockParticleOptionsRepository
|
||||
import com.cobblemon.mod.common.client.particle.ParticleStorm
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.WaveFunctions
|
||||
import com.cobblemon.mod.common.entity.PosableEntity
|
||||
import com.cobblemon.mod.common.entity.npc.NPCBattleActor
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonBehaviourFlag
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.PokemonMoveControl
|
||||
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
|
||||
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.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.mojang.datafixers.util.Either
|
||||
import java.util.UUID
|
||||
import kotlin.math.sqrt
|
||||
import net.minecraft.client.multiplayer.ClientLevel
|
||||
import net.minecraft.commands.arguments.EntityAnchorArgument
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.core.Registry
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.DoubleTag
|
||||
@@ -51,15 +80,23 @@ import net.minecraft.nbt.StringTag
|
||||
import net.minecraft.nbt.Tag
|
||||
import net.minecraft.resources.ResourceKey
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.MinecraftServer
|
||||
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.EntityType
|
||||
import net.minecraft.world.entity.LightningBolt
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
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.player.Player
|
||||
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.dimension.DimensionType
|
||||
@@ -74,6 +111,31 @@ import net.minecraft.world.phys.Vec3
|
||||
*/
|
||||
object MoLangFunctions {
|
||||
val generalFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>(
|
||||
"print" to java.util.function.Function { params ->
|
||||
val message = params.get<MoValue>(0).asString()
|
||||
Cobblemon.LOGGER.info(message)
|
||||
},
|
||||
"set_query" to java.util.function.Function { params ->
|
||||
val variable = params.getString(0)
|
||||
val value = params.get<MoValue>(1)
|
||||
params.environment.query.addFunction(variable) { value }
|
||||
return@Function value
|
||||
},
|
||||
"replace" to java.util.function.Function { params ->
|
||||
val text = params.getString(0)
|
||||
val search = params.getString(1)
|
||||
val replace = params.getString(2)
|
||||
return@Function StringValue(text.replace(search, replace))
|
||||
},
|
||||
"is_blank" to java.util.function.Function { params ->
|
||||
val arg = params.get<MoValue>(0)
|
||||
return@Function DoubleValue((arg is StringValue && (arg.value.isBlank() || arg.value.toDoubleOrNull() == 0.0)) || (arg is DoubleValue && arg.value == 0.0))
|
||||
},
|
||||
"run_command" to java.util.function.Function { params ->
|
||||
val command = params.getString(0)
|
||||
val server = server() ?: return@Function DoubleValue.ZERO
|
||||
server.commands.performPrefixedCommand(server.createCommandSourceStack(), command)
|
||||
},
|
||||
"is_int" to java.util.function.Function { params -> DoubleValue(params.get<MoValue>(0).asString().isInt()) },
|
||||
"is_number" to java.util.function.Function { params -> DoubleValue(params.get<MoValue>(0).asString().toDoubleOrNull() != null) },
|
||||
"to_number" to java.util.function.Function { params -> DoubleValue(params.get<MoValue>(0).asString().toDoubleOrNull() ?: 0.0) },
|
||||
@@ -108,36 +170,156 @@ object MoLangFunctions {
|
||||
runtime.environment.variable = params.environment.variable
|
||||
runtime.environment.context = params.environment.context
|
||||
val script = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
CobblemonScripts.run(script, runtime) ?: DoubleValue(0)
|
||||
CobblemonScripts.run(script, runtime) ?: DoubleValue.ZERO
|
||||
},
|
||||
"run_command" to java.util.function.Function { params ->
|
||||
val command = params.getString(0)
|
||||
server()?.let {
|
||||
return@Function DoubleValue(it.commands.dispatcher.execute(command, it.createCommandSourceStack()))
|
||||
} ?: return@Function DoubleValue.ZERO
|
||||
"run_molang" to java.util.function.Function { 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()
|
||||
runtime.resolve(expression)
|
||||
},
|
||||
"system_time_millis" to java.util.function.Function { _ ->
|
||||
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 java.util.function.Function { _ ->
|
||||
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 java.util.function.Function { 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 java.util.function.Function { params ->
|
||||
val dateA = params.getString(0)
|
||||
val dateB = params.getString(1)
|
||||
val format = java.text.SimpleDateFormat("DD/MM/YYYY")
|
||||
val a = format.parse(dateA)
|
||||
val b = format.parse(dateB)
|
||||
DoubleValue(a.after(b))
|
||||
}
|
||||
)
|
||||
val biomeFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
val worldFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
val dimensionTypeFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
val blockFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
val biomeFunctions = mutableListOf<(Holder<Biome>) -> HashMap<String, java.util.function.Function<MoParams, Any>>>()
|
||||
val worldFunctions = mutableListOf<(Holder<Level>) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ worldHolder ->
|
||||
val world = worldHolder.value()
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("game_time") { _ -> DoubleValue(world.gameTime.toDouble()) }
|
||||
map.put("time_of_day") {
|
||||
val time = world.dayTime % 24000
|
||||
return@put DoubleValue(time.toDouble())
|
||||
}
|
||||
map.put("server") { _ -> server()?.asMoLangValue() ?: DoubleValue.ZERO }
|
||||
map.put("is_raining_at") { 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.put("is_chunk_loaded_at") { 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.put("is_thundering") { _ -> DoubleValue(world.isThundering) }
|
||||
map.put("is_raining") { _ -> DoubleValue(world.isRaining) }
|
||||
map.put("set_block") { 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.put("get_block") { 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.put("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.put("spawn_lightning") { 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.put("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 {
|
||||
if (it is StringValue) world.getPlayerByUUID(UUID.fromString(it.value))
|
||||
else if (it is ObjectValue<*>) it.obj
|
||||
else null
|
||||
} as? ServerPlayer
|
||||
val pos = Vec3(x, y, z)
|
||||
|
||||
if (world !is ClientLevel) {
|
||||
val packet = SpawnSnowstormParticlePacket(particle, pos)
|
||||
if (player != null) {
|
||||
packet.sendToPlayer(player)
|
||||
} else {
|
||||
packet.sendToPlayersAround(x, y, z, 64.0, world.dimension())
|
||||
}
|
||||
} else {
|
||||
val effect = BedrockParticleOptionsRepository.getEffect(particle) ?: return@put DoubleValue.ZERO
|
||||
ParticleStorm.createAtPosition(world, effect, pos).spawn()
|
||||
}
|
||||
}
|
||||
map.put("get_entities_around") { 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()
|
||||
}
|
||||
|
||||
return@mutableListOf map
|
||||
}
|
||||
)
|
||||
val dimensionTypeFunctions = mutableListOf<(Holder<DimensionType>) -> HashMap<String, java.util.function.Function<MoParams, Any>>>()
|
||||
val blockFunctions = mutableListOf<(Holder<Block>) -> HashMap<String, java.util.function.Function<MoParams, Any>>>()
|
||||
val playerFunctions = mutableListOf<(Player) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ player ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("username") { _ -> StringValue(player.gameProfile.name) }
|
||||
map.put("uuid") { _ -> StringValue(player.gameProfile.id.toString()) }
|
||||
map.put("data") { _ -> return@put if (player is ServerPlayer) Cobblemon.molangData.load(player.uuid) else DoubleValue(0) }
|
||||
map.put("save_data") { _ -> if (player is ServerPlayer) Cobblemon.molangData.save(player.uuid) else DoubleValue(0) }
|
||||
map.put("main_held_item") { _ -> player.level().itemRegistry.wrapAsHolder(player.mainHandItem.item).asMoLangValue(Registries.ITEM) }
|
||||
map.put("off_held_item") { _ -> player.level().itemRegistry.wrapAsHolder(player.offhandItem.item).asMoLangValue(Registries.ITEM) }
|
||||
map.put("face") { params -> ObjectValue(PlayerDialogueFaceProvider(player.uuid, params.getBooleanOrNull(0) != false)) }
|
||||
map.put("swing_hand") { _ -> player.swing(player.usedItemHand) }
|
||||
map.put("food_level") { _ -> DoubleValue(player.foodData.foodLevel) }
|
||||
map.put("saturation_level") { _ -> DoubleValue(player.foodData.saturationLevel) }
|
||||
// map.put("has_permission") { params -> DoubleValue(Cobblemon.permissionValidator.hasPermission(player, Permission)) }
|
||||
map.put("tell") { params ->
|
||||
val message = params.getString(0).text()
|
||||
val overlay = params.getBooleanOrNull(1) ?: false
|
||||
val overlay = params.getBooleanOrNull(1) == true
|
||||
player.displayClientMessage(message, overlay)
|
||||
}
|
||||
map.put("teleport") { params ->
|
||||
@@ -156,7 +338,12 @@ object MoLangFunctions {
|
||||
environment.query = player.asMoLangValue()
|
||||
environment
|
||||
}
|
||||
map.put("is_player") { DoubleValue.ONE }
|
||||
if (player is ServerPlayer) {
|
||||
map.put("run_command") { params ->
|
||||
val command = params.getString(0)
|
||||
player.server.commands.performPrefixedCommand(player.createCommandSourceStack(), command)
|
||||
}
|
||||
map.put("is_party_at_full_health") { _ ->
|
||||
DoubleValue(player.party().none(Pokemon::canBeHealed)) }
|
||||
map.put("can_heal_at_healer") { params ->
|
||||
@@ -177,11 +364,53 @@ object MoLangFunctions {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("party") { player.party().struct }
|
||||
map.put("pc") { player.pc().struct }
|
||||
map.put("has_permission") { params -> DoubleValue(Cobblemon.permissionValidator.hasPermission(player, params.getString(0), params.getIntOrNull(1) ?: 4)) }
|
||||
map.put("data") { _ -> Cobblemon.molangData.load(player.uuid) }
|
||||
map.put("save_data") { _ -> Cobblemon.molangData.save(player.uuid) }
|
||||
map.put("in_battle") { DoubleValue(player.isInBattle()) }
|
||||
map.put("battle") { player.getBattleState()?.first?.struct ?: DoubleValue.ZERO }
|
||||
map.put("get_npc_data") { params ->
|
||||
val npcId = (params.get<MoValue>(0) as? ObjectValue<NPCEntity>)?.obj?.stringUUID ?: params.getString(0)
|
||||
val data = Cobblemon.molangData.load(player.uuid)
|
||||
if (data.map.containsKey(npcId)) {
|
||||
return@put data.map[npcId]!!
|
||||
} else {
|
||||
val vars = VariableStruct()
|
||||
data.map[npcId] = vars
|
||||
return@put vars
|
||||
}
|
||||
}
|
||||
map.put("get_npc_variable") { params ->
|
||||
val npcId = (params.get<MoValue>(0) as? ObjectValue<NPCEntity>)?.obj?.stringUUID ?: params.getString(0)
|
||||
val variable = params.getString(1)
|
||||
val data = Cobblemon.molangData.load(player.uuid)
|
||||
if (data.map.containsKey(npcId)) {
|
||||
return@put (data.map[npcId] as VariableStruct).map[variable] ?: DoubleValue.ZERO
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("set_npc_variable") { params ->
|
||||
val npcId = (params.get<MoValue>(0) as? ObjectValue<NPCEntity>)?.obj?.stringUUID ?: params.getString(0)
|
||||
val variable = params.getString(1)
|
||||
val value = params.get<MoValue>(2)
|
||||
val saveAfterwards = params.getBooleanOrNull(3) != false
|
||||
val data = Cobblemon.molangData.load(player.uuid)
|
||||
val npcData = data.map.getOrPut(npcId) { VariableStruct() } as VariableStruct
|
||||
npcData.map[variable] = value
|
||||
if (saveAfterwards) {
|
||||
Cobblemon.molangData.save(player.uuid)
|
||||
}
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("pokedex") { player.pokedex().struct }
|
||||
}
|
||||
map
|
||||
}
|
||||
)
|
||||
val entityFunctions = mutableListOf<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
val entityFunctions: MutableList<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ entity ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("uuid") { _ -> StringValue(entity.uuid.toString()) }
|
||||
@@ -190,6 +419,28 @@ object MoLangFunctions {
|
||||
val source = DamageSource(entity.level().registryAccess().registryOrThrow(Registries.DAMAGE_TYPE).getHolder(DamageTypes.GENERIC).get())
|
||||
entity.hurt(source, amount.toFloat())
|
||||
}
|
||||
map.put("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 is PathfinderMob) {
|
||||
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.put("has_walk_target") { _ ->
|
||||
if (entity is PathfinderMob) {
|
||||
DoubleValue(entity.brain.getMemory(MemoryModuleType.WALK_TARGET).isPresent || entity.isPathFinding)
|
||||
} else {
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("is_sneaking") { _ -> DoubleValue(entity.isShiftKeyDown) }
|
||||
map.put("is_sprinting") { _ -> DoubleValue(entity.isSprinting) }
|
||||
map.put("is_flying") { _ -> DoubleValue(entity.isFallFlying) }
|
||||
@@ -201,10 +452,21 @@ object MoLangFunctions {
|
||||
map.put("is_on_fire") { _ -> DoubleValue(entity.isOnFire) }
|
||||
map.put("is_invisible") { _ -> DoubleValue(entity.isInvisible) }
|
||||
map.put("is_sleeping") { _ -> DoubleValue(entity.isSleeping) }
|
||||
map.put("is_riding") { _ -> DoubleValue(entity.isPassenger()) }
|
||||
map.put("is_riding") { _ -> DoubleValue(entity.isPassenger) }
|
||||
map.put("health") { _ -> DoubleValue(entity.health) }
|
||||
map.put("distance_to_pos") { params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
return@put DoubleValue(sqrt(entity.distanceToSqr(Vec3(x, y, z))))
|
||||
}
|
||||
map.put("max_health") { _ -> DoubleValue(entity.maxHealth) }
|
||||
map.put("name") { _ -> StringValue(entity.effectiveName().string) }
|
||||
map.put("type") { _ ->
|
||||
entity.registryAccess().registry(Registries.ENTITY_TYPE).get().getKey(entity.type)?.toString()?.let {
|
||||
StringValue(it)
|
||||
} ?: DoubleValue.ZERO
|
||||
}
|
||||
map.put("yaw") { _ -> DoubleValue(entity.yRot.toDouble()) }
|
||||
map.put("pitch") { _ -> DoubleValue(entity.xRot.toDouble()) }
|
||||
map.put("x") { _ -> DoubleValue(entity.x) }
|
||||
@@ -218,7 +480,7 @@ object MoLangFunctions {
|
||||
map.put("entity_size") { DoubleValue(entity.boundingBox.run { if (xsize > ysize) xsize else ysize }) }
|
||||
map.put("entity_width") { DoubleValue(entity.boundingBox.xsize) }
|
||||
map.put("entity_height") { DoubleValue(entity.boundingBox.ysize) }
|
||||
|
||||
map.put("id_modulo") { params -> DoubleValue(entity.uuid.hashCode() % params.getDouble(0)) }
|
||||
map.put("horizontal_velocity") { _ -> DoubleValue(entity.deltaMovement.horizontalDistance()) }
|
||||
map.put("vertical_velocity") { DoubleValue(entity.deltaMovement.y) }
|
||||
map.put("is_on_ground") { _ -> DoubleValue(entity.onGround()) }
|
||||
@@ -244,6 +506,56 @@ object MoLangFunctions {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("get_nearby_entities") { params ->
|
||||
val distance = params.getDouble(0)
|
||||
val entities = entity.level().getEntities(entity, AABB.ofSize(entity.position(), distance, distance, distance))
|
||||
return@put entities
|
||||
.filterIsInstance<LivingEntity>()
|
||||
.map { it.asMostSpecificMoLangValue() }
|
||||
.asArrayValue()
|
||||
}
|
||||
if (entity is PosableEntity) {
|
||||
map.put("play_animation") { params ->
|
||||
val animation = params.getString(0)
|
||||
val packet = PlayPosableAnimationPacket(entity.id, setOf(animation), emptyList())
|
||||
val target = params.getStringOrNull(1)
|
||||
if (target != null) {
|
||||
val targetPlayer = if (target.asUUID != null) {
|
||||
entity.level().getPlayerByUUID(target.asUUID!!) as ServerPlayer
|
||||
} else if (entity.level() is ServerLevel) {
|
||||
entity.level().server!!.playerList.getPlayerByName(target)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
if (targetPlayer != null) {
|
||||
packet.sendToPlayer(targetPlayer)
|
||||
return@put DoubleValue.ONE
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
} else {
|
||||
packet.sendToPlayersAround(entity.x, entity.y, entity.z, 64.0, entity.level().dimension())
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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.
|
||||
map.put("spawn_bedrock_particles") { params ->
|
||||
val particle = params.getString(0).asResource()
|
||||
val locator = params.getString(1)
|
||||
val player = params.getOrNull<MoValue>(2)?.let {
|
||||
if (it is StringValue) entity.level().getPlayerByUUID(UUID.fromString(it.value))
|
||||
else if (it is ObjectValue<*>) it.obj
|
||||
else null
|
||||
} as? ServerPlayer
|
||||
|
||||
val packet = SpawnSnowstormEntityParticlePacket(particle, entity.id, listOf(locator))
|
||||
if (player == null) {
|
||||
packet.sendToPlayersAround(entity.x, entity.y, entity.z, 64.0, entity.level().dimension())
|
||||
} else {
|
||||
packet.sendToPlayer(player)
|
||||
}
|
||||
}
|
||||
map
|
||||
}
|
||||
)
|
||||
@@ -252,8 +564,21 @@ object MoLangFunctions {
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("class") { StringValue(npc.npc.id.toString()) }
|
||||
map.put("name") { StringValue(npc.name.string) }
|
||||
map.put("level") { DoubleValue(npc.level) }
|
||||
map.put("has_aspect") { params -> DoubleValue(npc.aspects.contains(params.getString(0))) }
|
||||
map.put("face") { params -> ObjectValue(ReferenceDialogueFaceProvider(npc.id, params.getBooleanOrNull(0) != false)) }
|
||||
map.put("in_battle") { DoubleValue(npc.isInBattle()) }
|
||||
map.put("battles") { ArrayStruct(npc.battleIds.mapNotNull { BattleRegistry.getBattle(it)?.struct }.mapIndexed { index, value -> "$index" to value }.toMap()) }
|
||||
map.put("stop_battles") { _ -> npc.battleIds.forEach { BattleRegistry.getBattle(it)?.stop() } }
|
||||
map.put("is_doing_activity") { params ->
|
||||
val identifiers = params.params.map { it.asString().asIdentifierDefaultingNamespace(namespace = "minecraft") }
|
||||
val activities = identifiers.mapNotNull { identifier -> BuiltInRegistries.ACTIVITY.get(identifier) }
|
||||
if (activities.isNotEmpty()) {
|
||||
return@put DoubleValue(activities.any { activity -> npc.brain.isActive(activity) })
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("run_script_on_client") { params ->
|
||||
val world = npc.level()
|
||||
if (world is ServerLevel) {
|
||||
@@ -282,22 +607,22 @@ object MoLangFunctions {
|
||||
)
|
||||
npc.actionEffect = context
|
||||
npc.brain.setMemory(CobblemonMemories.ACTIVE_ACTION_EFFECT, context)
|
||||
npc.brain.setActiveActivityIfPossible(CobblemonActivities.NPC_ACTION_EFFECT)
|
||||
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.NPC_ACTION_EFFECT)) {
|
||||
if (npcActionEffect == context && npc.brain.isActive(CobblemonActivities.ACTION_EFFECT)) {
|
||||
npc.brain.eraseMemory(CobblemonMemories.ACTIVE_ACTION_EFFECT)
|
||||
npc.actionEffect = null
|
||||
}
|
||||
}
|
||||
|
||||
return@put DoubleValue(1)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
return@put DoubleValue(0)
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
map.put("put_pokemon_in_healer") { params ->
|
||||
val healer = npc.level().getBlockEntity(params.get<ArrayStruct>(0).asBlockPos(), CobblemonBlockEntities.HEALING_MACHINE).orElse(null) ?: return@put DoubleValue.ZERO
|
||||
val party = npc.staticParty ?: 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
|
||||
@@ -310,6 +635,10 @@ object MoLangFunctions {
|
||||
npc.lookAt(EntityAnchorArgument.Anchor.EYES, pos)
|
||||
}
|
||||
map.put("environment") { _ -> npc.runtime.environment }
|
||||
map.put("party") { npc.party?.struct ?: DoubleValue.ZERO }
|
||||
map.put("has_party") { DoubleValue(npc.party != null) }
|
||||
map.put("is_npc") { DoubleValue.ONE }
|
||||
map.put("can_battle") { DoubleValue(npc.party?.any { it.currentHealth > 0 } == true || npc.npc.party?.isStatic == false) }
|
||||
map
|
||||
}
|
||||
)
|
||||
@@ -328,17 +657,55 @@ object MoLangFunctions {
|
||||
val actor = battle.actors.find { it.uuid == uuid } ?: return@put DoubleValue.ZERO
|
||||
return@put actor.struct
|
||||
}
|
||||
map.put("stop") { _ -> battle.stop() }
|
||||
map.put("actors") { battle.actors.toList().asArrayValue { it.struct } }
|
||||
map
|
||||
}
|
||||
)
|
||||
|
||||
val battleActorFunctions = mutableListOf<(BattleActor) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ battleActor ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("is_npc") { DoubleValue(battleActor.type == ActorType.NPC) }
|
||||
map.put("is_player") { DoubleValue(battleActor.type == ActorType.PLAYER) }
|
||||
map.put("is_wild") { DoubleValue(battleActor.type == ActorType.WILD) }
|
||||
if (battleActor is NPCBattleActor) {
|
||||
map.put("npc") { battleActor.entity.struct }
|
||||
} else if (battleActor is PlayerBattleActor) {
|
||||
map.put("player") { battleActor.entity?.asMoLangValue() ?: DoubleValue.ZERO }
|
||||
}
|
||||
return@mutableListOf map
|
||||
}
|
||||
)
|
||||
|
||||
val pokemonFunctions = mutableListOf<(Pokemon) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ pokemon ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("is_wild") { DoubleValue(pokemon.isWild()) }
|
||||
map.put("id") { StringValue(pokemon.uuid.toString()) }
|
||||
map.put("level") { DoubleValue(pokemon.level.toDouble()) }
|
||||
map.put("max_hp") { DoubleValue(pokemon.maxHealth.toDouble()) }
|
||||
map.put("current_hp") { DoubleValue(pokemon.currentHealth.toDouble()) }
|
||||
map.put("friendship") { DoubleValue(pokemon.friendship.toDouble()) }
|
||||
map.put("evs") {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for (stat in Stats.PERMANENT) {
|
||||
struct.addFunction(stat.showdownId) { DoubleValue(pokemon.evs.getOrDefault(stat).toDouble()) }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map.put("ivs") {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for (stat in Stats.PERMANENT) {
|
||||
struct.addFunction(stat.showdownId) { DoubleValue(pokemon.ivs.getOrDefault(stat).toDouble()) }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map.put("is_wild") { DoubleValue(pokemon.entity?.let { it.ownerUUID == null } == true) }
|
||||
map.put("is_shiny") { DoubleValue(pokemon.shiny) }
|
||||
map.put("species") { pokemon.species.struct }
|
||||
map.put("form") { StringValue(pokemon.form.name) }
|
||||
map.put("weight") { DoubleValue(pokemon.species.weight.toDouble()) }
|
||||
map.put("matches") { params -> DoubleValue(params.getString(0).toProperties().matches(pokemon)) }
|
||||
map
|
||||
}
|
||||
)
|
||||
@@ -350,6 +717,83 @@ object MoLangFunctions {
|
||||
map.put("is_moving") { DoubleValue((pokemonEntity.moveControl as? PokemonMoveControl)?.hasWanted() == true) }
|
||||
map.put("is_flying") { DoubleValue(pokemonEntity.getBehaviourFlag(PokemonBehaviourFlag.FLYING)) }
|
||||
map.put("has_aspect") { DoubleValue(it.getString(0) in pokemonEntity.aspects) }
|
||||
map.put("is_pokemon") { DoubleValue.ONE }
|
||||
map
|
||||
}
|
||||
)
|
||||
|
||||
val pokemonStoreFunctions = mutableListOf<(PokemonStore<*>) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ store ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("add") { params ->
|
||||
val pokemon = params.get<ObjectValue<Pokemon>>(0)
|
||||
return@put DoubleValue(store.add(pokemon.obj))
|
||||
}
|
||||
map.put("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)
|
||||
return@put DoubleValue(store.add(pokemon))
|
||||
}
|
||||
map.put("find_by_properties") { params ->
|
||||
val props = params.getString(0).toProperties()
|
||||
val pokemon = store.find { props.matches(it) }
|
||||
return@put pokemon?.asStruct() ?: DoubleValue.ZERO
|
||||
}
|
||||
map.put("find_all_by_properties") { params ->
|
||||
val props = params.getString(0).toProperties()
|
||||
val pokemon = store.filter { props.matches(it) }
|
||||
return@put ArrayStruct(pokemon.mapIndexed { index, value -> "$index" to value.asStruct() }.toMap())
|
||||
}
|
||||
map.put("find_by_id") { params ->
|
||||
val id = params.getString(0).asUUID
|
||||
val pokemon = store.find { it.uuid == id }
|
||||
return@put pokemon?.asStruct() ?: DoubleValue.ZERO
|
||||
}
|
||||
map.put("remove_by_id") { params ->
|
||||
val id = params.getString(0).asUUID
|
||||
val pokemon = store.find { it.uuid == id } ?: return@put DoubleValue.ZERO
|
||||
return@put DoubleValue(store.remove(pokemon))
|
||||
}
|
||||
map.put("average_level") { _ ->
|
||||
var numberOfPokemon = 0
|
||||
var totalLevel = 0
|
||||
for (pokemon in store) {
|
||||
totalLevel += pokemon.level
|
||||
numberOfPokemon++
|
||||
}
|
||||
if (numberOfPokemon == 0) {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
return@put DoubleValue(totalLevel.toDouble() / numberOfPokemon)
|
||||
}
|
||||
map.put("count") { _ -> DoubleValue(store.count()) }
|
||||
map.put("count_by_properties") { params ->
|
||||
val props = params.getString(0).toProperties()
|
||||
return@put DoubleValue(store.count { props.matches(it) })
|
||||
}
|
||||
map.put("highest_level") {
|
||||
val highest = store.maxOfOrNull { it.level } ?: 0
|
||||
return@put DoubleValue(highest)
|
||||
}
|
||||
map.put("lowest_level") {
|
||||
val lowest = store.minOfOrNull { it.level } ?: 0
|
||||
return@put DoubleValue(lowest)
|
||||
}
|
||||
map.put("heal") {
|
||||
for (pokemon in store) {
|
||||
pokemon.heal()
|
||||
}
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("healing_remainder_percent") { _ ->
|
||||
var totalPercent = 0.0f
|
||||
for (pokemon in store) {
|
||||
totalPercent += (1.0f - (pokemon.currentHealth.toFloat() / pokemon.maxHealth))
|
||||
}
|
||||
DoubleValue(totalPercent)
|
||||
}
|
||||
map.put("has_usable_pokemon") { _ -> DoubleValue(store.any { !it.isFainted() }) }
|
||||
map
|
||||
}
|
||||
)
|
||||
@@ -360,19 +804,192 @@ object MoLangFunctions {
|
||||
map.put("get_pokemon") { params ->
|
||||
val index = params.getInt(0)
|
||||
val pokemon = party.get(index) ?: return@put DoubleValue.ZERO
|
||||
val struct = VariableStruct()
|
||||
pokemon.writeVariables(struct)
|
||||
return@put struct
|
||||
return@put pokemon.struct
|
||||
}
|
||||
map.put("healing_remainder_percent") { _ -> DoubleValue(party.getHealingRemainderPercent()) }
|
||||
|
||||
return@mutableListOf map
|
||||
})
|
||||
|
||||
fun Holder<Biome>.asBiomeMoLangValue() = asMoLangValue(Registries.BIOME).addFunctions(biomeFunctions)
|
||||
fun Holder<Level>.asWorldMoLangValue() = asMoLangValue(Registries.DIMENSION).addFunctions(worldFunctions)
|
||||
fun Holder<Block>.asBlockMoLangValue() = asMoLangValue(Registries.BLOCK).addFunctions(blockFunctions)
|
||||
fun Holder<DimensionType>.asDimensionTypeMoLangValue() = asMoLangValue(Registries.DIMENSION_TYPE).addFunctions(dimensionTypeFunctions)
|
||||
val pcFunctions = mutableListOf<(PCStore) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ pc ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("get_pokemon") { params ->
|
||||
val box = params.getInt(0)
|
||||
val slot = params.getInt(1)
|
||||
val pokemon = pc[PCPosition(box, slot)] ?: return@put DoubleValue.ZERO
|
||||
return@put pokemon.struct
|
||||
}
|
||||
map.put("resize") { params ->
|
||||
val newSize = params.getInt(0)
|
||||
val lockNewSize = params.getBooleanOrNull(1) == true
|
||||
pc.resize(newSize, lockNewSize)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("get_box_count") { _ -> DoubleValue(pc.boxes.size.toDouble()) }
|
||||
return@mutableListOf map
|
||||
}
|
||||
)
|
||||
|
||||
val spawningContextFunctions = mutableListOf<(SpawningContext) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ spawningContext ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
val worldValue = spawningContext.world.registryAccess().registryOrThrow(Registries.DIMENSION).wrapAsHolder(spawningContext.world).asWorldMoLangValue()
|
||||
val biomeValue = spawningContext.biomeRegistry.wrapAsHolder(spawningContext.biome).asBiomeMoLangValue()
|
||||
map.put("biome") { _ -> biomeValue }
|
||||
map.put("world") { _ -> worldValue }
|
||||
map.put("light") { _ -> DoubleValue(spawningContext.light.toDouble()) }
|
||||
map.put("x") { _ -> DoubleValue(spawningContext.position.x.toDouble()) }
|
||||
map.put("y") { _ -> DoubleValue(spawningContext.position.y.toDouble()) }
|
||||
map.put("z") { _ -> DoubleValue(spawningContext.position.z.toDouble()) }
|
||||
map.put("moon_phase") { _ -> DoubleValue(spawningContext.moonPhase.toDouble()) }
|
||||
map.put("can_see_sky") { _ -> DoubleValue(spawningContext.canSeeSky) }
|
||||
map.put("sky_light") { _ -> DoubleValue(spawningContext.skyLight.toDouble()) }
|
||||
map.put("bucket") { _ -> StringValue(spawningContext.cause.bucket.name) }
|
||||
map.put("player") { _ ->
|
||||
val causeEntity = spawningContext.cause.entity ?: return@put DoubleValue.ZERO
|
||||
if (causeEntity is ServerPlayer) {
|
||||
return@put causeEntity.asMoLangValue()
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map
|
||||
}
|
||||
)
|
||||
|
||||
val serverFunctions: MutableList<(MinecraftServer) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
|
||||
{ server ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
|
||||
map.put("get_world") { params ->
|
||||
val world = server.getLevel(
|
||||
ResourceKey.create(
|
||||
Registries.DIMENSION,
|
||||
params.getString(0).asIdentifierDefaultingNamespace(namespace = "minecraft")
|
||||
)
|
||||
) ?: return@put DoubleValue.ZERO
|
||||
|
||||
return@put world
|
||||
.registryAccess()
|
||||
.registryOrThrow(Registries.DIMENSION)
|
||||
.wrapAsHolder(world)
|
||||
.asWorldMoLangValue()
|
||||
}
|
||||
|
||||
map.put("broadcast") { params ->
|
||||
val message = params.getString(0)
|
||||
server.playerList.broadcastSystemMessage(message.text(), params.getBooleanOrNull(1) == true)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
|
||||
map.put("get_player_by_uuid") { params ->
|
||||
val uuid = UUID.fromString(params.getString(0))
|
||||
val player = server.playerList.getPlayer(uuid) ?: return@put DoubleValue.ZERO
|
||||
return@put player.asMoLangValue()
|
||||
}
|
||||
|
||||
map.put("get_player_by_username") { params ->
|
||||
val username = params.getString(0)
|
||||
val player = server.playerList.getPlayerByName(username) ?: return@put DoubleValue.ZERO
|
||||
return@put player.asMoLangValue()
|
||||
}
|
||||
|
||||
map.put("data") { params ->
|
||||
val data = Cobblemon.molangData.load(UUID(0L, 0L))
|
||||
return@put data
|
||||
}
|
||||
|
||||
map.put("save_data") {
|
||||
Cobblemon.molangData.save(UUID(0L, 0L))
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
|
||||
// Maybe later...
|
||||
// map.put("stop") { params ->
|
||||
// Cobblemon.LOGGER.warn("Server is being stopped from a MoLang script.")
|
||||
// server.stopServer()
|
||||
// }
|
||||
|
||||
return@mutableListOf map
|
||||
}
|
||||
)
|
||||
|
||||
val pokedexFunctions: MutableList<(AbstractPokedexManager) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
|
||||
{ pokedex ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("get_species_record") { params ->
|
||||
val speciesId = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
pokedex.speciesRecords[speciesId]?.struct ?: QueryStruct(hashMapOf())
|
||||
}
|
||||
|
||||
map.put("has_seen") { params ->
|
||||
val speciesId = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val formName = params.getStringOrNull(1)
|
||||
|
||||
if (formName == null) {
|
||||
return@put DoubleValue(pokedex.getHighestKnowledgeForSpecies(speciesId).ordinal >= PokedexEntryProgress.ENCOUNTERED.ordinal)
|
||||
} else {
|
||||
return@put DoubleValue((pokedex.getSpeciesRecord(speciesId)?.getFormRecord(formName)?.knowledge?.ordinal ?: 0) >= PokedexEntryProgress.ENCOUNTERED.ordinal)
|
||||
}
|
||||
}
|
||||
|
||||
map.put("has_caught") { params ->
|
||||
val speciesId = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val formName = params.getStringOrNull(1)
|
||||
if (formName == null) {
|
||||
return@put DoubleValue(pokedex.getHighestKnowledgeForSpecies(speciesId) == PokedexEntryProgress.CAUGHT)
|
||||
} else {
|
||||
return@put DoubleValue(pokedex.getSpeciesRecord(speciesId)?.getFormRecord(formName)?.knowledge == PokedexEntryProgress.CAUGHT)
|
||||
}
|
||||
}
|
||||
map.put("caught_count") { DoubleValue(pokedex.getGlobalCalculatedValue(CaughtCount)) }
|
||||
map.put("seen_count") { DoubleValue(pokedex.getGlobalCalculatedValue(SeenCount)) }
|
||||
map.put("caught_percent") { DoubleValue(pokedex.getGlobalCalculatedValue(CaughtPercent)) }
|
||||
map.put("seen_percent") { DoubleValue(pokedex.getGlobalCalculatedValue(SeenPercent)) }
|
||||
|
||||
if (pokedex is PokedexManager) {
|
||||
map.put("player_id") { StringValue(pokedex.uuid.toString()) }
|
||||
map.put("see") { params ->
|
||||
val pokemon = params.get<ObjectValue<Pokemon>>(0).obj
|
||||
pokedex.encounter(pokemon)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("catch") { params ->
|
||||
val pokemon = params.get<ObjectValue<Pokemon>>(0).obj
|
||||
pokedex.catch(pokemon)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
}
|
||||
|
||||
map
|
||||
}
|
||||
)
|
||||
|
||||
val speciesFunctions: MutableList<(Species) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
|
||||
{ species ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
|
||||
map.put("identifier") { StringValue(species.resourceIdentifier.toString()) }
|
||||
map.put("name") { StringValue(species.name) }
|
||||
map.put("primary_type") { StringValue(species.primaryType.name) }
|
||||
map.put("secondary_type") { StringValue(species.secondaryType?.name ?: "null") }
|
||||
map.put("experience_group") { StringValue(species.experienceGroup.name) }
|
||||
map.put("height") { DoubleValue(species.height) }
|
||||
map.put("weight") { DoubleValue(species.weight) }
|
||||
map.put("base_scale") { DoubleValue(species.baseScale) }
|
||||
map.put("hitbox_width") { DoubleValue(species.hitbox.width) }
|
||||
map.put("hitbox_height") { DoubleValue(species.hitbox.height) }
|
||||
map.put("hitbox_fixed") { DoubleValue(species.hitbox.fixed) }
|
||||
map.put("catch_rate") { DoubleValue(species.catchRate) }
|
||||
|
||||
map
|
||||
}
|
||||
)
|
||||
|
||||
fun Holder<Biome>.asBiomeMoLangValue() = asMoLangValue(Registries.BIOME).addFunctions(biomeFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
fun Holder<Level>.asWorldMoLangValue() = asMoLangValue(Registries.DIMENSION).addFunctions(worldFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
fun Holder<Block>.asBlockMoLangValue() = asMoLangValue(Registries.BLOCK).addFunctions(blockFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
fun MinecraftServer.asMoLangValue() = ObjectValue(this).addStandardFunctions().addFunctions(serverFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
fun Holder<DimensionType>.asDimensionTypeMoLangValue() = asMoLangValue(Registries.DIMENSION_TYPE).addFunctions(dimensionTypeFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
fun Player.asMoLangValue(): ObjectValue<Player> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
@@ -383,6 +1000,33 @@ object MoLangFunctions {
|
||||
return value
|
||||
}
|
||||
|
||||
// We need to migrate the writeVariables thing to be all about query structs, variable doesn't make sense and I don't want to break fringe 1.6 compatibility issues close to release
|
||||
fun Pokemon.asStruct(): ObjectValue<Pokemon> {
|
||||
val queryStruct = ObjectValue(this)
|
||||
queryStruct.addFunctions(pokemonFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
return queryStruct
|
||||
}
|
||||
|
||||
fun PartyStore.asMoLangValue(): ObjectValue<PartyStore> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.toString() }
|
||||
)
|
||||
value.addFunctions(pokemonStoreFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
value.addFunctions(partyFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
return value
|
||||
}
|
||||
|
||||
fun PCStore.asMoLangValue(): ObjectValue<PCStore> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.toString() }
|
||||
)
|
||||
value.addFunctions(pokemonStoreFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
value.addFunctions(pcFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
return value
|
||||
}
|
||||
|
||||
fun NPCEntity.asMoLangValue(): ObjectValue<NPCEntity> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
@@ -393,6 +1037,17 @@ object MoLangFunctions {
|
||||
return value
|
||||
}
|
||||
|
||||
fun PokemonEntity.asMoLangValue(): ObjectValue<PokemonEntity> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.pokemon.uuid.toString() }
|
||||
)
|
||||
value.addFunctions(entityFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
value.addFunctions(pokemonFunctions.flatMap { it(this.pokemon).entries.map { it.key to it.value } }.toMap()) // Convenience
|
||||
value.addFunctions(pokemonEntityFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
return value
|
||||
}
|
||||
|
||||
fun PokemonBattle.asMoLangValue(): ObjectValue<PokemonBattle> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
@@ -402,6 +1057,36 @@ object MoLangFunctions {
|
||||
return value
|
||||
}
|
||||
|
||||
fun BattleActor.asMoLangValue(): ObjectValue<BattleActor> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.toString() }
|
||||
)
|
||||
value.addFunctions(battleActorFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
return value
|
||||
}
|
||||
|
||||
fun SpawningContext.asMoLangValue(): ObjectValue<SpawningContext> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.toString() }
|
||||
)
|
||||
value.addFunctions(spawningContextFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
* Different functions exist depending on
|
||||
*/
|
||||
fun LivingEntity.asMostSpecificMoLangValue(): ObjectValue<out LivingEntity> {
|
||||
return when (this) {
|
||||
is Player -> asMoLangValue()
|
||||
is PokemonEntity -> struct
|
||||
is NPCEntity -> struct
|
||||
else -> ObjectValue(this).also { it.addStandardFunctions().addFunctions(entityFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap()) }
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> Holder<T>.asMoLangValue(key: ResourceKey<Registry<T>>): ObjectValue<Holder<T>> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
@@ -431,6 +1116,22 @@ object MoLangFunctions {
|
||||
return this
|
||||
}
|
||||
|
||||
fun QueryStruct.addPokedexFunctions(pokedexManager: AbstractPokedexManager): QueryStruct {
|
||||
val addedFunctions = pokedexFunctions
|
||||
.flatMap { it.invoke(pokedexManager).entries }
|
||||
.associate { it.key to it.value }
|
||||
functions.putAll(addedFunctions)
|
||||
return this
|
||||
}
|
||||
|
||||
fun QueryStruct.addSpeciesFunctions(species: Species): QueryStruct {
|
||||
val addedFunctions = speciesFunctions
|
||||
.flatMap { it.invoke(species).entries }
|
||||
.associate { it.key to it.value }
|
||||
functions.putAll(addedFunctions)
|
||||
return this
|
||||
}
|
||||
|
||||
fun QueryStruct.addPokemonFunctions(pokemon: Pokemon): QueryStruct {
|
||||
val addedFunctions = pokemonFunctions
|
||||
.flatMap { it.invoke(pokemon).entries }
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ class AnimationActionEffectKeyframe : ConditionalActionEffectKeyframe(), EntityC
|
||||
// Treat them as expressions if possible but otherwise yeah just send them as strings
|
||||
val animation = animation.map {
|
||||
try {
|
||||
it.asExpressionLike().resolveString(context.runtime).takeIf { it != "0.0" } ?: it
|
||||
it.asExpressionLike().resolveString(context.runtime).takeIf { it != "0" } ?: it
|
||||
} catch (e: Exception) {
|
||||
it
|
||||
}
|
||||
|
||||
+6
-3
@@ -11,12 +11,14 @@ package com.cobblemon.mod.common.api.moves.animations.keyframes
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.EntityProvider
|
||||
import com.cobblemon.mod.common.api.moves.animations.TargetsProvider
|
||||
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
|
||||
import com.cobblemon.mod.common.api.scheduling.delayedFuture
|
||||
import com.cobblemon.mod.common.entity.PosableEntity
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import net.minecraft.commands.arguments.EntityArgument.getPlayers
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
|
||||
@@ -29,7 +31,8 @@ import net.minecraft.server.level.ServerLevel
|
||||
class EntityParticlesActionEffectKeyframe : ConditionalActionEffectKeyframe(), EntityConditionalActionEffectKeyframe {
|
||||
override val entityCondition = "q.entity.is_user".asExpressionLike()
|
||||
var effect: String? = null
|
||||
var locator: String = "root"
|
||||
var locators: List<String> = listOf("target")
|
||||
|
||||
val delay: ExpressionLike = "0".asExpressionLike()
|
||||
val visibilityRange = 200
|
||||
|
||||
@@ -39,13 +42,13 @@ class EntityParticlesActionEffectKeyframe : ConditionalActionEffectKeyframe(), E
|
||||
.flatMap { prov -> prov.entities.filter { test(context, it, isUser = prov is UsersProvider) } }
|
||||
|
||||
val effectIdentifier = try {
|
||||
effect?.asExpressionLike()?.resolveString(context.runtime)?.takeIf { it != "0.0" } ?: effect
|
||||
effect?.asExpressionLike()?.resolveString(context.runtime)?.takeIf { it != "0" } ?: effect
|
||||
} catch (e: Exception) {
|
||||
effect
|
||||
}?.asIdentifierDefaultingNamespace() ?: return skip()
|
||||
|
||||
entities.filter { it is PosableEntity }.forEach { entity ->
|
||||
val packet = SpawnSnowstormEntityParticlePacket(effectIdentifier, entity.id, listOf(locator))
|
||||
val packet = SpawnSnowstormEntityParticlePacket(effectIdentifier, entity.id, (locators).toList())
|
||||
val players = (entity.level() as ServerLevel).getPlayers { it.distanceTo(entity) <= visibilityRange }
|
||||
packet.sendToPlayers(players)
|
||||
}
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ class EntitySoundActionEffectKeyframe : ConditionalActionEffectKeyframe(), Entit
|
||||
.flatMap { prov -> prov.entities.filter { test(context, it, isUser = prov is UsersProvider) } }
|
||||
|
||||
val soundIdentifier = try {
|
||||
sound?.asExpressionLike()?.resolveString(context.runtime)?.takeIf { it != "0.0" } ?: sound
|
||||
sound?.asExpressionLike()?.resolveString(context.runtime)?.takeIf { it != "0" } ?: sound
|
||||
} catch (e: Exception) {
|
||||
sound
|
||||
}?.asIdentifierDefaultingNamespace() ?: return skip()
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.net.serializers
|
||||
|
||||
import com.cobblemon.mod.common.entity.PlatformType
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.network.codec.StreamCodec
|
||||
import net.minecraft.network.syncher.EntityDataSerializer
|
||||
|
||||
object PlatformTypeDataSerializer : EntityDataSerializer<PlatformType> {
|
||||
// TODO: This ID only looks it's used by NeoForge and that hasn't been tested yet
|
||||
val ID = cobblemonResource("platform_type")
|
||||
fun read(buf: RegistryFriendlyByteBuf) = PlatformType.entries[buf.readInt()]
|
||||
override fun copy(value: PlatformType) = value
|
||||
fun write(buf: RegistryFriendlyByteBuf, value: PlatformType) {
|
||||
buf.writeInt(value.ordinal)
|
||||
}
|
||||
|
||||
override fun codec() = StreamCodec.of(::write, ::read)
|
||||
}
|
||||
@@ -38,7 +38,8 @@ class NPCClass {
|
||||
var resourceIdentifier: ResourceLocation = cobblemonResource("dummy")
|
||||
var names: MutableList<Component> = mutableListOf()
|
||||
var aspects: MutableSet<String> = mutableSetOf() // These only make sense when applied via presets
|
||||
var hitbox = EntityDimensions.scalable(0.6F, 1.8F)
|
||||
var hitbox = EntityDimensions.scalable(0.6F, 1.8F).withEyeHeight(1.62F)
|
||||
var modelScale: Float = 0.94F
|
||||
var battleConfiguration = NPCBattleConfiguration()
|
||||
var interaction: NPCInteractConfiguration? = null
|
||||
var canDespawn = true
|
||||
@@ -47,17 +48,23 @@ class NPCClass {
|
||||
var variables = mutableMapOf<String, MoValue>() // Questionable whether this should be here.
|
||||
var party: NPCPartyProvider? = null
|
||||
var skill: Int = 0
|
||||
var autoHealParty: Boolean = true
|
||||
var randomizePartyOrder: Boolean = false
|
||||
var battleTheme: ResourceLocation? = null
|
||||
var ai: MutableList<BrainConfig> = mutableListOf()
|
||||
var isMovable: Boolean = true
|
||||
var isInvulnerable = false
|
||||
var isLeashable = true
|
||||
var allowProjectileHits = true
|
||||
|
||||
// If you're adding stuff here, add it to NPCPreset and NPCClassAdapter too
|
||||
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeString(resourceIdentifier.toString())
|
||||
buffer.writeCollection(names) { _, v -> buffer.writeText(v) }
|
||||
buffer.writeFloat(this.hitbox.width)
|
||||
buffer.writeFloat(this.hitbox.height)
|
||||
buffer.writeBoolean(this.hitbox.fixed)
|
||||
buffer.writeFloat(hitbox.width)
|
||||
buffer.writeFloat(hitbox.height)
|
||||
buffer.writeBoolean(hitbox.fixed)
|
||||
battleConfiguration.encode(buffer)
|
||||
buffer.writeNullable(interaction) { _, value ->
|
||||
buffer.writeString(value.type)
|
||||
@@ -77,11 +84,17 @@ class NPCClass {
|
||||
buffer.writeString(v.defaultValue)
|
||||
}
|
||||
buffer.writeInt(skill)
|
||||
buffer.writeBoolean(autoHealParty)
|
||||
buffer.writeBoolean(randomizePartyOrder)
|
||||
buffer.writeMapK(size = IntSize.U_BYTE, map = variables) { (key, value) ->
|
||||
buffer.writeString(key)
|
||||
buffer.writeString(value.asString())
|
||||
}
|
||||
buffer.writeNullable(battleTheme) { _, v -> buffer.writeIdentifier(v) }
|
||||
buffer.writeBoolean(isMovable)
|
||||
buffer.writeBoolean(isInvulnerable)
|
||||
buffer.writeBoolean(isLeashable)
|
||||
buffer.writeBoolean(allowProjectileHits)
|
||||
}
|
||||
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
@@ -116,6 +129,8 @@ class NPCClass {
|
||||
NPCConfigVariable(variableName, displayName, description, type, defaultValue)
|
||||
}.toMutableList()
|
||||
skill = buffer.readInt()
|
||||
autoHealParty = buffer.readBoolean()
|
||||
randomizePartyOrder = buffer.readBoolean()
|
||||
buffer.readMapK(size = IntSize.U_BYTE, map = variables) {
|
||||
val key = buffer.readString()
|
||||
val value = buffer.readString()
|
||||
@@ -126,5 +141,9 @@ class NPCClass {
|
||||
}
|
||||
}
|
||||
battleTheme = buffer.readNullable { buffer.readIdentifier() }
|
||||
isMovable = buffer.readBoolean()
|
||||
isInvulnerable = buffer.readBoolean()
|
||||
isLeashable = buffer.readBoolean()
|
||||
allowProjectileHits = buffer.readBoolean()
|
||||
}
|
||||
}
|
||||
@@ -70,7 +70,7 @@ object NPCClasses : JsonDataRegistry<NPCClass> {
|
||||
.registerTypeAdapter(MoValue::class.java, MoValueAdapter)
|
||||
.registerTypeAdapter(NPCClass::class.java, NPCClassAdapter)
|
||||
.registerTypeAdapter(Activity::class.java, ActivityAdapter)
|
||||
.registerTypeAdapter(Component::class.java, TextAdapter)
|
||||
.registerTypeAdapter(Component::class.java, TranslatedTextAdapter)
|
||||
.registerTypeAdapter(WeightedAspect::class.java, WeightedAspectAdapter)
|
||||
.registerTypeAdapter(BrainConfig::class.java, BrainConfigAdapter)
|
||||
.registerTypeAdapter(TaskConfig::class.java, TaskConfigAdapter)
|
||||
|
||||
@@ -8,14 +8,12 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.npc
|
||||
|
||||
import com.cobblemon.mod.common.api.npc.partyproviders.DynamicPartyProvider
|
||||
import com.cobblemon.mod.common.api.npc.partyproviders.NPCParty
|
||||
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.PartyStore
|
||||
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.nbt.CompoundTag
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
/**
|
||||
@@ -29,11 +27,14 @@ interface NPCPartyProvider {
|
||||
companion object {
|
||||
val types = mutableMapOf<String, (String) -> NPCPartyProvider>(
|
||||
SimplePartyProvider.TYPE to { SimplePartyProvider() },
|
||||
DynamicPartyProvider.TYPE to { DynamicPartyProvider() }
|
||||
PoolPartyProvider.TYPE to { PoolPartyProvider() },
|
||||
ScriptPartyProvider.TYPE to { ScriptPartyProvider() }
|
||||
)
|
||||
}
|
||||
|
||||
val type: String
|
||||
fun provide(npc: NPCEntity, level: Int): NPCParty
|
||||
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
|
||||
fun loadFromJSON(json: JsonElement)
|
||||
}
|
||||
@@ -29,7 +29,13 @@ class NPCPreset {
|
||||
var hitbox: EntityDimensions? = null
|
||||
var ai: List<BrainConfig>? = null
|
||||
var skill: Int? = null
|
||||
var autoHealParty: Boolean? = null
|
||||
var randomizePartyOrder: Boolean? = null
|
||||
var battleTheme: ResourceLocation? = null
|
||||
var isMovable: Boolean? = null
|
||||
var isInvulnerable: Boolean? = null
|
||||
var isLeashable: Boolean? = null
|
||||
var allowProjectileHits: Boolean? = null
|
||||
|
||||
fun applyTo(npcClass: NPCClass) {
|
||||
resourceIdentifier?.let { npcClass.resourceIdentifier = it }
|
||||
@@ -47,7 +53,13 @@ class NPCPreset {
|
||||
names?.let { npcClass.names.addAll(it) }
|
||||
hitbox?.let { npcClass.hitbox = it }
|
||||
skill?.let { npcClass.skill = it }
|
||||
autoHealParty?.let { npcClass.autoHealParty = it }
|
||||
randomizePartyOrder?.let { npcClass.randomizePartyOrder = it }
|
||||
battleTheme?.let { npcClass.battleTheme = it }
|
||||
ai?.let { npcClass.ai.addAll(it) }
|
||||
isMovable?.let { npcClass.isMovable = it }
|
||||
isInvulnerable?.let { npcClass.isInvulnerable = it }
|
||||
isLeashable?.let { npcClass.isLeashable = it }
|
||||
allowProjectileHits?.let { npcClass.allowProjectileHits = it }
|
||||
}
|
||||
}
|
||||
@@ -67,7 +67,7 @@ object NPCPresets : JsonDataRegistry<NPCPreset> {
|
||||
.registerTypeAdapter(NPCVariationProvider::class.java, NPCVariationProviderAdapter)
|
||||
.registerTypeAdapter(Activity::class.java, ActivityAdapter)
|
||||
.registerTypeAdapter(MoValue::class.java, MoValueAdapter)
|
||||
.registerTypeAdapter(Component::class.java, TextAdapter)
|
||||
.registerTypeAdapter(Component::class.java, TranslatedTextAdapter)
|
||||
.registerTypeAdapter(BrainConfig::class.java, BrainConfigAdapter)
|
||||
.registerTypeAdapter(TaskConfig::class.java, TaskConfigAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, BiomeLikeConditionAdapter)
|
||||
|
||||
@@ -57,14 +57,15 @@ any number of presets and the properties of those presets will be merged into th
|
||||
"variableName": "challenge_distance",
|
||||
"displayName": "cobblemon.npc.challenge_distance",
|
||||
"description": "cobblemon.npc.challenge_distance.description",
|
||||
"type": "number",
|
||||
"default": "5"
|
||||
"type": "NUMBER",
|
||||
"defaultValue": "5"
|
||||
}
|
||||
],
|
||||
"names": [
|
||||
"Vera",
|
||||
"Hiro",
|
||||
"John Cobblemon"
|
||||
"Red",
|
||||
"Green",
|
||||
"Blue",
|
||||
"Yellow"
|
||||
],
|
||||
"interaction": {
|
||||
"type": "dialogue",
|
||||
@@ -75,6 +76,8 @@ any number of presets and the properties of those presets will be merged into th
|
||||
},
|
||||
"canDespawn": false,
|
||||
"skill": 5,
|
||||
"autoHealParty": true,
|
||||
"randomizePartyOrder": true,
|
||||
"party": {
|
||||
"type": "simple",
|
||||
"pokemon": [
|
||||
@@ -176,19 +179,19 @@ An example situation of using this is if there is an NPC class preset that defin
|
||||
challenging a player when they come within a certain range. Inside that class preset, the AI script may
|
||||
need to know at what distance the player will be engaged. This can be defined in the NPC class preset as
|
||||
a config variable, and therefore any NPC class that relies on this preset will have that config variable
|
||||
and present it to users when they setup an NPC of that class.
|
||||
and present it to users when they set up an NPC of that class.
|
||||
|
||||
"config": [
|
||||
{
|
||||
"variableName": "challenge_distance",
|
||||
"displayName": "cobblemon.npc.challenge_distance",
|
||||
"description": "cobblemon.npc.challenge_distance.description",
|
||||
"type": "number",
|
||||
"default": "5"
|
||||
"displayName": "cobblemon.npc.challenge_distance.name",
|
||||
"description": "cobblemon.npc.challenge_distance.desc",
|
||||
"type": "NUMBER",
|
||||
"defaultValue": "5"
|
||||
}
|
||||
]
|
||||
|
||||
The "type" of variable is one of: `string`, `number`, or `boolean`. The type is used to preset a more
|
||||
The "type" of variable is one of: `STRING`, `NUMBER`, or `BOOLEAN`. The type is used to preset a more
|
||||
appropriate input field for the user when they are setting up the NPC.
|
||||
|
||||
### names
|
||||
@@ -248,22 +251,24 @@ spawning system, it will use a seed level that is based on the player's party's
|
||||
spawned using the NPC command, the seed level can be defined in the command and is otherwise set to 1.
|
||||
|
||||
A party provider produces either 'static' or 'dynamic' parties. A static party is one that is generated
|
||||
when the NPC is initialized and will not change. A dynamic party is one that may be configured using the
|
||||
when the NPC is initialized then does not change. A dynamic party is one that may be configured using the
|
||||
seed level but can be different any time the NPC is challenged.
|
||||
|
||||
A dynamic party prevents the NPC from doing several actions, such as using healing machines, having
|
||||
persistent health of their party that changes between battles, and keeping their Pokémon out of their
|
||||
balls. This is because the NPC's party is not stored in the world.
|
||||
balls. This is because a NPC's dynamic party is not stored in the world and is generated every time
|
||||
they are challenged.
|
||||
|
||||
#### simple
|
||||
The "simple" party type is a way to define a party of Pokémon in a simple list format. Each string in the
|
||||
list should be a set of Pokémon properties, as used in commands and spawn files.
|
||||
|
||||
This party provider type is considered 'static'. Any level that is not mentioned in the Pokémon properties
|
||||
will be set to the seed level.
|
||||
This party provider type can be static or dynamic depending on the value of `isStatic`. Any level that
|
||||
is not mentioned in the Pokémon properties will be set to the seed level.
|
||||
|
||||
"party": {
|
||||
"type": "simple",
|
||||
"isStatic": true,
|
||||
"pokemon": [
|
||||
"spiritomb moves=splash,firepunch,flamethrower,swift",
|
||||
"porygonz speed_ev=252",
|
||||
@@ -274,5 +279,136 @@ will be set to the seed level.
|
||||
]
|
||||
}
|
||||
|
||||
#### dynamic
|
||||
To be implemented
|
||||
#### pool
|
||||
The "pool" party type is a way to define a party of Pokémon using a more complicated format that involves some
|
||||
randomization and weighting to different Pokémon options.
|
||||
|
||||
A pool party provider can be static or dynamic depending on the value of `isStatic`. If `useFixedRandom` is true, the Pokémon it selects
|
||||
from the pool will be constant for a single spawned NPC entity. This is slightly different to a static party because
|
||||
the level of the Pokémon can still be modified dynamically using MoLang values inside each entry's `level` field.
|
||||
|
||||
The pool specifies the minimum and maximum number of Pokémon that can be in the party using `minPokemon` and `maxPokemon`.
|
||||
|
||||
The `pool` property dictates all the possible Pokémon with each being an object with several properties.
|
||||
|
||||
- The `pokemon` property is the Pokémon properties as used in commands and spawn files that will generate the Pokémon.
|
||||
- The `weight` property is a decimal number that represents how likely that Pokémon is to be chosen. If the weight is -1, it will be guaranteed to be selected. Defaults to 1. The larger the value, the more likely it will be chosen.
|
||||
- The `selectableTimes` property is the maximum number of times that the entry can be selected in the party. Defaults to 1.
|
||||
- The `npcLevels` property is a range of seed levels that the Pokémon can be generated at. For example, if the NPC is spawned with seed level 10, and the range is 5-9, the Pokémon will not be selected. The actual level of the Pokémon will be the seed level. Defaults to "1-100".
|
||||
- The `levelVariation` property is the amount of variation the Pokémon's level can have from the NPC seed level. For example, a value of 2 will mean that for an NPC generated with seed level 10, the Pokémon will be generated somewhere between level 8 and 12 inclusive. Defaults to zero.
|
||||
- The `level` property can be used instead of `levelVariation` to force a specific level. This is a MoLang expression.
|
||||
|
||||
For `minPokemon`, `maxPokemon`, `weight`, `selectableTimes`, and `levelVariation`, the values can be a simple number or be full MoLang expressions. At the time
|
||||
of seeding an NPC, the cosmetic variations have already been applied. Therefore, it's possible to make these properties
|
||||
depend on visual aspects of the NPC. For example, if the NPC has a variation for "dirt", you could set a
|
||||
certain Pokémon to be `"weight": "q.npc.has_aspect('dirty') ? -1 : 0"` which would make it a guaranteed party Pokémon for "dirty" NPCs
|
||||
and impossible for all other variations of the NPC.
|
||||
|
||||
The MoLang environment used for the pool party provider contains q.level for the seed level, q.npc for the NPC, as well as q.players for
|
||||
an array of all players that are challenging the NPC. The players list is only present for dynamic parties. If it is specifically
|
||||
a single battle challenge and this is a dynamic party, there will also be a `q.player`, for convenience.
|
||||
|
||||
"party": {
|
||||
"type": "pool",
|
||||
"minPokemon": "3",
|
||||
"maxPokemon": "6",
|
||||
"isStatic": false,
|
||||
"useFixedRandom": true,
|
||||
"pool": [
|
||||
{
|
||||
"pokemon": "weedle",
|
||||
"weight": "10",
|
||||
"selectableTimes": "2",
|
||||
"levelVariation": "2"
|
||||
"npcLevels": "1-8"
|
||||
},
|
||||
{
|
||||
"pokemon": "caterpie",
|
||||
"weight": "5",
|
||||
"npcLevels": "1-8",
|
||||
"selectableTimes": "2"
|
||||
},
|
||||
{
|
||||
"pokemon": "kakuna",
|
||||
"weight": "7",
|
||||
"selectableTimes": "2",
|
||||
"npcLevels": "8-10"
|
||||
},
|
||||
{
|
||||
"pokemon": "metapod",
|
||||
"weight": "7",
|
||||
"npcLevels": "8-10",
|
||||
"selectableTimes": "2"
|
||||
},
|
||||
{
|
||||
"pokemon": "beedrill",
|
||||
"weight": "5",
|
||||
"npcLevels": "10-15",
|
||||
"selectableTimes": "1"
|
||||
},
|
||||
{
|
||||
"pokemon": "butterfree",
|
||||
"weight": "5",
|
||||
"npcLevels": "10-15",
|
||||
"selectableTimes": "1",
|
||||
"level": "q.player.party.highest_level"
|
||||
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
#### script
|
||||
The "script" party type is a way to define a party of Pokémon using a MoLang script. The script can modify a q.party struct,
|
||||
primarily using function calls such as `q.party.add_by_properties('pikachu shiny)`.
|
||||
|
||||
There is an `isStatic` property to control whether the party is static or dynamic. There is a `script` property which is a
|
||||
resource location pointing to the script that should be located in the `molang` datapack folder.
|
||||
|
||||
The MoLang environment used for the script party provider contains q.level for the seed level, q.npc for the NPC, as well as q.players for
|
||||
an array of all players that are challenging the NPC. The players list is only present for dynamic parties. If it is specifically
|
||||
a single battle challenge and this is a dynamic party, there will also be a `q.player`, for convenience.
|
||||
|
||||
"party": {
|
||||
"type": "script",
|
||||
"isStatic": true,
|
||||
"script": "cobblemon:npc_party"
|
||||
}
|
||||
|
||||
An example script might be:
|
||||
|
||||
t.use_butterfree = math.random_integer(1, 2) == 1;
|
||||
t.use_butterfree ? {
|
||||
q.party.add_by_properties('butterfree level=' + (q.level + 3));
|
||||
} : {
|
||||
q.party.add_by_properties('beedrill level=' + (q.level + 3));
|
||||
};
|
||||
q.party.add_by_properties('pidgey');
|
||||
|
||||
### autoHealParty
|
||||
Whether or not the NPC's party will be healed between battles. If the NPC has a dynamic party, this option will
|
||||
do nothing. If the NPC has a static party and this option is false, their Pokémon will not be healed and the
|
||||
NPC will have to use healing machines to heal them if they have that enabled in their AI.
|
||||
|
||||
### isMovable
|
||||
Controls whether the NPC can be moved by players or other entities. If disabled, the NPC will remain
|
||||
stationary.
|
||||
|
||||
### isInvulnerable
|
||||
Controls whether the NPC is immune to all damage sources.
|
||||
|
||||
### isLeashable
|
||||
Controls whether players can attach a lead to a NPC. If disabled, players will be unable to leash
|
||||
the NPC and move them using leads.
|
||||
|
||||
### allowProjectileHits
|
||||
Controls whether the NPC can be hit by projectiles. Disabling this option prevents unwanted
|
||||
interactions, such as players moving the NPC with fishing rods or other projectiles.
|
||||
|
||||
### randomizePartyOrder
|
||||
Whether the NPC's party order will be randomized between battles. When true, this NPC's party (regardless of
|
||||
whether it was dynamic or static) will choose its initial send-out Pokémon randomly instead of always choosing the first
|
||||
Pokémon in the party.
|
||||
|
||||
### ai
|
||||
The AI property is an array of brain configurations. This is used to configure the behaviours of the NPC. See
|
||||
the documentation for brain configurations at [AI Configuration](../ai/config/README.md).
|
||||
+2
-2
@@ -8,15 +8,15 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.npc.configuration
|
||||
|
||||
import com.cobblemon.mod.common.api.npc.NPCPartyProvider
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
class NPCBattleConfiguration {
|
||||
var canChallenge = false
|
||||
var party: NPCPartyProvider? = null
|
||||
@Deprecated("This is not likely to get used")
|
||||
var simultaneousBattles = false
|
||||
@Deprecated("This is not likely to get used")
|
||||
var healAfterwards = true
|
||||
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
|
||||
+36
-8
@@ -13,6 +13,11 @@ import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
import com.cobblemon.mod.common.util.readString
|
||||
import com.cobblemon.mod.common.util.readText
|
||||
import com.cobblemon.mod.common.util.writeString
|
||||
import com.cobblemon.mod.common.util.writeText
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.network.chat.Component
|
||||
|
||||
/**
|
||||
@@ -30,10 +35,38 @@ class NPCConfigVariable(
|
||||
val type: NPCVariableType = NPCVariableType.NUMBER,
|
||||
val defaultValue: String = "0",
|
||||
) {
|
||||
companion object {
|
||||
fun decode(buffer: RegistryFriendlyByteBuf): NPCConfigVariable {
|
||||
return NPCConfigVariable(
|
||||
buffer.readString(),
|
||||
buffer.readText(),
|
||||
buffer.readText(),
|
||||
buffer.readEnum(NPCVariableType::class.java),
|
||||
buffer.readString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
enum class NPCVariableType {
|
||||
NUMBER,
|
||||
STRING,
|
||||
BOOLEAN
|
||||
TEXT,
|
||||
BOOLEAN;
|
||||
|
||||
fun toMoValue(value: String): MoValue {
|
||||
return when (this) {
|
||||
TEXT -> StringValue(value)
|
||||
BOOLEAN -> DoubleValue(value.let { it == "1" || it.toBooleanStrictOrNull() == true })
|
||||
else -> DoubleValue(value.toDouble())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeString(variableName)
|
||||
buffer.writeText(displayName)
|
||||
buffer.writeText(description)
|
||||
buffer.writeEnum(type)
|
||||
buffer.writeString(defaultValue)
|
||||
}
|
||||
|
||||
fun apply(npc: NPCEntity, value: MoValue) {
|
||||
@@ -41,11 +74,6 @@ class NPCConfigVariable(
|
||||
}
|
||||
|
||||
fun applyDefault(npc: NPCEntity) {
|
||||
val value = when (type) {
|
||||
NPCVariableType.STRING -> StringValue(defaultValue)
|
||||
NPCVariableType.BOOLEAN -> DoubleValue(defaultValue.toBoolean())
|
||||
else -> DoubleValue(defaultValue.toDouble())
|
||||
}
|
||||
npc.config.set(variableName.split(".").iterator(), value)
|
||||
npc.config.setDirectly(variableName, type.toMoValue(defaultValue))
|
||||
}
|
||||
}
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.npc.partyproviders
|
||||
|
||||
import com.cobblemon.mod.common.api.npc.NPCPartyProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.google.gson.JsonElement
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* A provider of a party for battling the NPC. It generates a [StaticNPCParty] but using a more complex process
|
||||
* that takes an input level and makes a randomized selection of Pokémon and movesets.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since July 12th, 2024
|
||||
*/
|
||||
class DynamicPartyProvider : NPCPartyProvider {
|
||||
companion object {
|
||||
val TYPE = "dynamic"
|
||||
}
|
||||
|
||||
class DynamicPool {
|
||||
val id = ResourceLocation.parse("cobblemon:pool")
|
||||
val pokemon: MutableList<DynamicPokemon> = mutableListOf()
|
||||
}
|
||||
|
||||
|
||||
class DynamicPokemon(
|
||||
val pokemon: PokemonProperties,
|
||||
val possibleLevels: IntRange,
|
||||
val selectableTimes: IntRange,
|
||||
val guaranteed: Boolean = false
|
||||
)
|
||||
|
||||
override val type = "dynamic"
|
||||
|
||||
override fun loadFromJSON(json: JsonElement) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun provide(npc: NPCEntity, level: Int): NPCParty {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.npc.partyproviders
|
||||
|
||||
import com.cobblemon.mod.common.api.storage.party.NPCPartyStore
|
||||
import com.cobblemon.mod.common.api.storage.party.PartyStore
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.net.IntSize
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.readSizedInt
|
||||
import com.cobblemon.mod.common.util.writeSizedInt
|
||||
import net.minecraft.core.RegistryAccess
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
/**
|
||||
* Represents a party of Pokemon that an NPC can have. This isn't directly a party of Pokémon so that
|
||||
* a dynamically generated party can exist where a party is generated at the time of a challenge.
|
||||
*
|
||||
* This interface is sealed. Implementations should be either [StaticNPCParty] or [DynamicNPCParty].
|
||||
* If you want a custom type then you should implement [DynamicNPCParty] and register it in the
|
||||
* [DynamicNPCParty.types] map.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since July 12th, 2024
|
||||
*/
|
||||
sealed interface NPCParty {
|
||||
fun getParty(player: ServerPlayer, npc: NPCEntity): PartyStore?
|
||||
fun encode(buffer: RegistryFriendlyByteBuf)
|
||||
fun decode(buffer: RegistryFriendlyByteBuf)
|
||||
fun saveToNBT(nbt: CompoundTag, registryAccess: RegistryAccess)
|
||||
fun loadFromNBT(nbt: CompoundTag, registryAccess: RegistryAccess)
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple wrapping around a [NPCPartyStore]. This is a straightforward party that is always the same.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since July 12th, 2024
|
||||
*/
|
||||
class StaticNPCParty(val party: NPCPartyStore) : NPCParty {
|
||||
override fun getParty(player: ServerPlayer, npc: NPCEntity) = party
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeSizedInt(IntSize.U_BYTE, party.size())
|
||||
for (pokemon in party) {
|
||||
Pokemon.S2C_CODEC.encode(buffer, pokemon)
|
||||
}
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
repeat(times = buffer.readSizedInt(IntSize.U_BYTE)) {
|
||||
party.add(Pokemon.S2C_CODEC.decode(buffer))
|
||||
}
|
||||
}
|
||||
|
||||
override fun saveToNBT(nbt: CompoundTag, registryAccess: RegistryAccess) {
|
||||
for ((index, pokemon) in party.withIndex()) {
|
||||
nbt.put(DataKeys.NPC_PARTY_POKEMON + index, pokemon.saveToNBT(registryAccess))
|
||||
}
|
||||
}
|
||||
|
||||
override fun loadFromNBT(nbt: CompoundTag, registryAccess: RegistryAccess) {
|
||||
var index = 0
|
||||
while (nbt.contains(DataKeys.NPC_PARTY_POKEMON + index)) {
|
||||
this.party.add(Pokemon().loadFromNBT(registryAccess, nbt.getCompound(DataKeys.NPC_PARTY_POKEMON + index)))
|
||||
index++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A dynamic party that is generated at the time of a challenge.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since July 12th, 2024
|
||||
*/
|
||||
interface DynamicNPCParty : NPCParty {
|
||||
companion object {
|
||||
val types = mutableMapOf<String, Class<out NPCParty>>()
|
||||
init {}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user