mirror of
https://gitlab.com/cable-mc/cobblemon.git
synced 2026-11-05 15:58:53 -05:00
Compare commits
1094
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b85132e0b | ||
|
|
9b06f6a31f | ||
|
|
b3696dd45c | ||
|
|
aeda9d94a6 | ||
|
|
e817675abc | ||
|
|
49ebd740b6 | ||
|
|
a96b550618 | ||
|
|
b1431b1be1 | ||
|
|
19400a71f2 | ||
|
|
87661224da | ||
|
|
92dc6fefe0 | ||
|
|
c99b1503cb | ||
|
|
8ede54739b | ||
|
|
921f3796c1 | ||
|
|
225d170ee3 | ||
|
|
5e3479eb8f | ||
|
|
13379eb8d0 | ||
|
|
47f05d0b37 | ||
|
|
5f6bfa03aa | ||
|
|
83ac0388d6 | ||
|
|
454c2b368a | ||
|
|
4395514376 | ||
|
|
a8f7141c14 | ||
|
|
eba32fbf60 | ||
|
|
57a3ad9c9b | ||
|
|
40c2c843a2 | ||
|
|
b20db43afa | ||
|
|
e81c025152 | ||
|
|
39a9c60a84 | ||
|
|
dd3787896b | ||
|
|
80ddbaf50c | ||
|
|
62c9654f97 | ||
|
|
98b5d69d01 | ||
|
|
87f0811c2b | ||
|
|
ec58079b4f | ||
|
|
7c3cf3f498 | ||
|
|
a7c0a911e3 | ||
|
|
fee8634661 | ||
|
|
68a517332f | ||
|
|
b78bdd727f | ||
|
|
610e197236 | ||
|
|
27ee34573d | ||
|
|
e4bd024567 | ||
|
|
e41d94b5dc | ||
|
|
00ae3841db | ||
|
|
b0a54a408f | ||
|
|
a34735891a | ||
|
|
17552017f9 | ||
|
|
d90cd5b9a8 | ||
|
|
33fe1a7c32 | ||
|
|
17096f3bb7 | ||
|
|
81f733e89f | ||
|
|
fac7419225 | ||
|
|
69d174b6a6 | ||
|
|
d66f534b95 | ||
|
|
6f342045f4 | ||
|
|
726aeac836 | ||
|
|
f01c5a5ceb | ||
|
|
5876d83147 | ||
|
|
ae1a55fa98 | ||
|
|
417fefa911 | ||
|
|
e30cc3dce8 | ||
|
|
63f721bc9b | ||
|
|
27248fd17d | ||
|
|
c198aee827 | ||
|
|
a8c6d58499 | ||
|
|
9e748cc9cf | ||
|
|
5890918e04 | ||
|
|
c0567ac11f | ||
|
|
5036e334dc | ||
|
|
9f798cddf5 | ||
|
|
eb2828e0a4 | ||
|
|
53f127fc23 | ||
|
|
c463bb6e06 | ||
|
|
4e9d45d38c | ||
|
|
4e2f7d3bc5 | ||
|
|
8975b099ad | ||
|
|
308380706a | ||
|
|
ef80924526 | ||
|
|
fadf57046d | ||
|
|
13cedaaf73 | ||
|
|
0a0806716b | ||
|
|
af59cb5e58 | ||
|
|
c0f8e37206 | ||
|
|
e64aaf66ab | ||
|
|
a946dfea1f | ||
|
|
f2c459cc06 | ||
|
|
6f12f53e85 | ||
|
|
073ee8e283 | ||
|
|
d3a5ad8efa | ||
|
|
62a2dcc606 | ||
|
|
a7f71eb437 | ||
|
|
b65dab7f30 | ||
|
|
ac76878002 | ||
|
|
fe219e52e6 | ||
|
|
bfd28ec9b0 | ||
|
|
1063441b0e | ||
|
|
4e89f45253 | ||
|
|
56aa5ae8d8 | ||
|
|
156a7a70eb | ||
|
|
3b13b9f246 | ||
|
|
0a8d920285 | ||
|
|
f5418a8088 | ||
|
|
cae097bf2c | ||
|
|
f760003c3a | ||
|
|
38f70c5960 | ||
|
|
81b00d7f85 | ||
|
|
948ceeaa8d | ||
|
|
1eda446897 | ||
|
|
49d03c0336 | ||
|
|
c5d254cca7 | ||
|
|
4f7fda1e96 | ||
|
|
d3e10cb570 | ||
|
|
51d7d2cb04 | ||
|
|
ac53d2da95 | ||
|
|
17e41f5f0b | ||
|
|
4bd70698ce | ||
|
|
23b750d5e9 | ||
|
|
ab2b67c426 | ||
|
|
d68c912ca6 | ||
|
|
c391d1a5e0 | ||
|
|
0af39118eb | ||
|
|
392b69937f | ||
|
|
e32af733f1 | ||
|
|
e1509c9dab | ||
|
|
1cb6baee59 | ||
|
|
9403cb6296 | ||
|
|
fd157fae4f | ||
|
|
9ca472ccaf | ||
|
|
13b7e0d418 | ||
|
|
a6ddc1e4ae | ||
|
|
a5b1b87ec5 | ||
|
|
b3255045fa | ||
|
|
90f3acebac | ||
|
|
0da91fb255 | ||
|
|
ba3b26ac74 | ||
|
|
966761776d | ||
|
|
24f07c58a0 | ||
|
|
a8b5f649a8 | ||
|
|
295c73ae69 | ||
|
|
899bdb38ac | ||
|
|
f30e5bda1e | ||
|
|
76c870b472 | ||
|
|
963448e173 | ||
|
|
48d2fc6e2e | ||
|
|
5868d39431 | ||
|
|
c47b3a21db | ||
|
|
0d12fb1f1c | ||
|
|
78c7188a11 | ||
|
|
ff5f63a998 | ||
|
|
b9a8a9be11 | ||
|
|
2046482252 | ||
|
|
6a737999a9 | ||
|
|
89d37e583b | ||
|
|
e077a5d453 | ||
|
|
e7208ed396 | ||
|
|
27f8bbf029 | ||
|
|
2f97a69b8c | ||
|
|
79d67389ba | ||
|
|
61f2150f2e | ||
|
|
5d63c8fe06 | ||
|
|
0d109a64fd | ||
|
|
5daef700e7 | ||
|
|
21bd1550f0 | ||
|
|
0d4f63ba01 | ||
|
|
946cff21fb | ||
|
|
8a2291cd6e | ||
|
|
304ac0e07a | ||
|
|
3cac1138fb | ||
|
|
b19a11f8ab | ||
|
|
cad64ae24b | ||
|
|
9036909968 | ||
|
|
53b98e8008 | ||
|
|
574fd92827 | ||
|
|
e6d7513c20 | ||
|
|
e36252318a | ||
|
|
5d5dbb9e79 | ||
|
|
4036592c9a | ||
|
|
c0ddbad4f5 | ||
|
|
fa1f485cd2 | ||
|
|
b5aeb0e5f7 | ||
|
|
9c2680783a | ||
|
|
b7ed72241b | ||
|
|
689f9eabc0 | ||
|
|
4fd24be1d3 | ||
|
|
7579203527 | ||
|
|
9e3dd9373c | ||
|
|
c9b9ac7d07 | ||
|
|
dddae893cd | ||
|
|
86508801d3 | ||
|
|
de44411ad2 | ||
|
|
e59aa01345 | ||
|
|
3f3e8b5747 | ||
|
|
bb769fa165 | ||
|
|
163218c0a8 | ||
|
|
e61eadb834 | ||
|
|
29c8bbe9e9 | ||
|
|
7c42390515 | ||
|
|
ff84ca69cd | ||
|
|
dda3c4e981 | ||
|
|
072ad5ff12 | ||
|
|
1570bbf42b | ||
|
|
41cfe79b0e | ||
|
|
dc4e3dc39c | ||
|
|
d100537d41 | ||
|
|
fe218399e2 | ||
|
|
8f4a4a7e45 | ||
|
|
b3757113ac | ||
|
|
3bb17ffc6e | ||
|
|
f37924fb0c | ||
|
|
6e2765902e | ||
|
|
d7c32e4ad8 | ||
|
|
8732094c28 | ||
|
|
38fecf337e | ||
|
|
3c7c640217 | ||
|
|
b7e8cad3bd | ||
|
|
d553332017 | ||
|
|
de04379297 | ||
|
|
10bf9d1abd | ||
|
|
03b0bb9937 | ||
|
|
51a3d686ff | ||
|
|
267c14cdd5 | ||
|
|
c9367f63b4 | ||
|
|
48f8561662 | ||
|
|
951a36f82e | ||
|
|
e9edd7a1e9 | ||
|
|
cb407bb649 | ||
|
|
5e28612d99 | ||
|
|
ea007d21db | ||
|
|
dc1a035dae | ||
|
|
71db46d86b | ||
|
|
a61b71205b | ||
|
|
63b14aa4ba | ||
|
|
fb23e3961c | ||
|
|
bdf9606d61 | ||
|
|
bf5b03398c | ||
|
|
3a824b1971 | ||
|
|
0bbecc5951 | ||
|
|
c60fb253cd | ||
|
|
34240a101a | ||
|
|
c6a4db47f4 | ||
|
|
ac2dbde41e | ||
|
|
b8d19b3c4c | ||
|
|
747aedca27 | ||
|
|
9540bc9394 | ||
|
|
a7fed0d042 | ||
|
|
aedb0755f9 | ||
|
|
23eb10f5ea | ||
|
|
2c134c5e1b | ||
|
|
f7f70f14bc | ||
|
|
d3834b0bb7 | ||
|
|
2033240b83 | ||
|
|
1400505757 | ||
|
|
9da0459793 | ||
|
|
7ac41e2f68 | ||
|
|
e0cdc08cd3 | ||
|
|
438e502a5a | ||
|
|
d7b344cff8 | ||
|
|
249c305474 | ||
|
|
5b6ed359a3 | ||
|
|
d14e16fe5b | ||
|
|
65dd132081 | ||
|
|
54cd0ba700 | ||
|
|
7f8b241695 | ||
|
|
0b824fe4c1 | ||
|
|
46b4bfa7ea | ||
|
|
c9fc2ce95d | ||
|
|
91eff015dc | ||
|
|
a9c8825b3c | ||
|
|
9d64b0f06a | ||
|
|
f5a308e579 | ||
|
|
2b3c2358a5 | ||
|
|
a84a2aac58 | ||
|
|
fe86d644ad | ||
|
|
9eb8d5e832 | ||
|
|
5d2e877176 | ||
|
|
b2b08ac098 | ||
|
|
ae8e4a4f3a | ||
|
|
e5f0911b83 | ||
|
|
e1ae600419 | ||
|
|
34315d9f1d | ||
|
|
773cef8e46 | ||
|
|
282a15b1b2 | ||
|
|
3b17ad4f54 | ||
|
|
04c5b698c2 | ||
|
|
123029199c | ||
|
|
8f70bd5eaf | ||
|
|
24310d5144 | ||
|
|
b4b5d62632 | ||
|
|
d550d775a6 | ||
|
|
e572584dbf | ||
|
|
6ac58cf7c2 | ||
|
|
e8a1e66658 | ||
|
|
9c67d0ad6f | ||
|
|
0747d08a90 | ||
|
|
299ab3d5f7 | ||
|
|
7e0f48ea54 | ||
|
|
49a5090903 | ||
|
|
7e0e55d126 | ||
|
|
f3fa765890 | ||
|
|
a9a91a813a | ||
|
|
579e48f93c | ||
|
|
e8520953f6 | ||
|
|
47deb6504b | ||
|
|
cd0f95169a | ||
|
|
a23b37ee47 | ||
|
|
370d65284d | ||
|
|
e2b2ee9031 | ||
|
|
141b38b81c | ||
|
|
f41c2aa0c1 | ||
|
|
87fffe67f5 | ||
|
|
7a13a5a4d1 | ||
|
|
9efc3f1eef | ||
|
|
8188cabb4f | ||
|
|
b545e01aef | ||
|
|
c34a09430a | ||
|
|
403acf5822 | ||
|
|
d53c17bc8e | ||
|
|
7bc9778ce3 | ||
|
|
57687ffcdb | ||
|
|
d40e2706c0 | ||
|
|
abecc6c74d | ||
|
|
709b933661 | ||
|
|
d426d6e843 | ||
|
|
234acf0787 | ||
|
|
b4b0bed36c | ||
|
|
65f0bfdcd9 | ||
|
|
f598af314a | ||
|
|
cef14e938f | ||
|
|
f53cd87081 | ||
|
|
bfc820808f | ||
|
|
57a34e490d | ||
|
|
e817496f7e | ||
|
|
8aea2d5b74 | ||
|
|
47213c3ae4 | ||
|
|
d70fbe70d2 | ||
|
|
3768d4278e | ||
|
|
6283a0bee0 | ||
|
|
43a24a9c98 | ||
|
|
d8b401aee0 | ||
|
|
f64e919bf2 | ||
|
|
a727c099ae | ||
|
|
e4ae8fe6dd | ||
|
|
c46ab35af0 | ||
|
|
25bfdb3aa2 | ||
|
|
7d5b0c9648 | ||
|
|
2ccf49e65c | ||
|
|
bc12466e09 | ||
|
|
7d03f5f780 | ||
|
|
2eb23f0707 | ||
|
|
cbe7626bdc | ||
|
|
f83b10643c | ||
|
|
40be5a2060 | ||
|
|
2b26576a1c | ||
|
|
48d6f7f707 | ||
|
|
ea6bf1fcd1 | ||
|
|
e0ab5e60d0 | ||
|
|
3d4e941af2 | ||
|
|
6aa359d7cc | ||
|
|
646fc606d6 | ||
|
|
37e9823fe0 | ||
|
|
9cd24e76d2 | ||
|
|
fb52c1badd | ||
|
|
24126e582b | ||
|
|
a08ca9c8eb | ||
|
|
896198eca2 | ||
|
|
38a20c76a1 | ||
|
|
7ac80cb716 | ||
|
|
2a83af5626 | ||
|
|
7cc8fd8201 | ||
|
|
901de0031c | ||
|
|
f762ba7425 | ||
|
|
f5b92c11a0 | ||
|
|
ecc5143ae6 | ||
|
|
c6a7dcde33 | ||
|
|
7821801c83 | ||
|
|
85f8f971da | ||
|
|
2808e52873 | ||
|
|
69f8d4a306 | ||
|
|
e7351af2a8 | ||
|
|
7ad5ee633d | ||
|
|
742c4c8087 | ||
|
|
ae41d4ac58 | ||
|
|
3f4086fc90 | ||
|
|
9e993df788 | ||
|
|
a389c21b25 | ||
|
|
b20be79ce6 | ||
|
|
9b05db6c5f | ||
|
|
f7de61ce71 | ||
|
|
9811390573 | ||
|
|
11e7d193fb | ||
|
|
3e2788b789 | ||
|
|
bda999e1fd | ||
|
|
e249a96749 | ||
|
|
2078108ea7 | ||
|
|
ddb24e9621 | ||
|
|
b07795e547 | ||
|
|
7e63cd9c1d | ||
|
|
8680f2ade0 | ||
|
|
5679d4dc05 | ||
|
|
980c0d4c37 | ||
|
|
50f1abd47e | ||
|
|
a555ac8e6d | ||
|
|
96d2b322a3 | ||
|
|
7bae99468b | ||
|
|
8b99cfe98d | ||
|
|
ac6eb38152 | ||
|
|
1621910428 | ||
|
|
b74037afb6 | ||
|
|
753bfa6936 | ||
|
|
53d575b1c4 | ||
|
|
97e90a87c1 | ||
|
|
a72525cf65 | ||
|
|
07e487e0cd | ||
|
|
bcf3fe1dc6 | ||
|
|
8d5d567153 | ||
|
|
0643bc8d58 | ||
|
|
1c6633bfdc | ||
|
|
f876c5f6c8 | ||
|
|
99744af491 | ||
|
|
9f2a4261f0 | ||
|
|
1b6b0524e8 | ||
|
|
5f740b1c58 | ||
|
|
0cd79c6c8f | ||
|
|
f5e492a6ce | ||
|
|
c8c1677182 | ||
|
|
8431e25c31 | ||
|
|
121f7c6a9a | ||
|
|
37f508793d | ||
|
|
5807e4526e | ||
|
|
63a42edb7f | ||
|
|
c523408fe0 | ||
|
|
7baf8e079d | ||
|
|
88105e070b | ||
|
|
cef0ad7efa | ||
|
|
592ead6674 | ||
|
|
98556c1b24 | ||
|
|
137f080ba9 | ||
|
|
4f3601ab3b | ||
|
|
e523c46594 | ||
|
|
251ebe2a37 | ||
|
|
12191b5d96 | ||
|
|
4da74f0bfb | ||
|
|
b893681af5 | ||
|
|
eb159d0702 | ||
|
|
d47197ab3b | ||
|
|
64a2b9c60f | ||
|
|
b4585cabd4 | ||
|
|
dd15de6e94 | ||
|
|
c5dab7208f | ||
|
|
4a5d53a020 | ||
|
|
cc163b73e1 | ||
|
|
d1682b78c6 | ||
|
|
52827c3be6 | ||
|
|
23e2d56db4 | ||
|
|
3451a70cfd | ||
|
|
3869fc590d | ||
|
|
ba4f4c8137 | ||
|
|
7f372c1f16 | ||
|
|
4624e7cab2 | ||
|
|
967053824b | ||
|
|
95bc331dc9 | ||
|
|
3c3ef6a387 | ||
|
|
309de12a0e | ||
|
|
90b22316cb | ||
|
|
dc1aec75ba | ||
|
|
f60b3f1f4f | ||
|
|
3e118dcca6 | ||
|
|
12aac4f52d | ||
|
|
9b1699b507 | ||
|
|
f0cf94b830 | ||
|
|
ddab45148c | ||
|
|
2a23c77da7 | ||
|
|
97625d2750 | ||
|
|
ef600fda79 | ||
|
|
bd6364deac | ||
|
|
1fbdf1fce1 | ||
|
|
6060013110 | ||
|
|
755cf731f0 | ||
|
|
6246af2979 | ||
|
|
6b27f13396 | ||
|
|
9a4fcb15b2 | ||
|
|
07bb57c009 | ||
|
|
2934e38077 | ||
|
|
d60592b5e4 | ||
|
|
f563f69afd | ||
|
|
354914105e | ||
|
|
b154b6de64 | ||
|
|
66e1f621cf | ||
|
|
7d55d52608 | ||
|
|
63d84df283 | ||
|
|
94400e61f5 | ||
|
|
ce337e8fff | ||
|
|
72b6f46c4b | ||
|
|
547af6dd77 | ||
|
|
70119478a5 | ||
|
|
029e120b2c | ||
|
|
7a305792af | ||
|
|
06ed32e4cb | ||
|
|
ee63bf0eab | ||
|
|
48a4add7aa | ||
|
|
dc8d7e36e0 | ||
|
|
1c1a683f2d | ||
|
|
0ee4f60391 | ||
|
|
4a7893f647 | ||
|
|
76890135ef | ||
|
|
930f95d3b6 | ||
|
|
0c15d14f1a | ||
|
|
a2bb0fb3cd | ||
|
|
a97753c339 | ||
|
|
557dda4c1d | ||
|
|
944fdf7c0c | ||
|
|
10b2aecc12 | ||
|
|
565d38f085 | ||
|
|
ada8bc9403 | ||
|
|
2d3d245b03 | ||
|
|
9adeb60abe | ||
|
|
6c91342954 | ||
|
|
98b84d853c | ||
|
|
83ee5f3c3d | ||
|
|
137088296e | ||
|
|
e748059052 | ||
|
|
d748241727 | ||
|
|
f515a2ef72 | ||
|
|
0c9017d5e2 | ||
|
|
fd12789791 | ||
|
|
638c92ec4c | ||
|
|
bee865931a | ||
|
|
77ee8b1743 | ||
|
|
5eef45b49a | ||
|
|
79f7dd1519 | ||
|
|
919dec6809 | ||
|
|
f00f73491d | ||
|
|
c7cf73812c | ||
|
|
80fbab35dc | ||
|
|
ad039cb949 | ||
|
|
0436b36872 | ||
|
|
9f60ce51c7 | ||
|
|
343e13d1ac | ||
|
|
ca175ab164 | ||
|
|
8a62088fb4 | ||
|
|
7e2bb0ca6a | ||
|
|
ebad166b12 | ||
|
|
92f5fd7cd6 | ||
|
|
507319fa0f | ||
|
|
228347e46f | ||
|
|
3a2c12c652 | ||
|
|
a6945adf5a | ||
|
|
295e68b0d5 | ||
|
|
be983f6115 | ||
|
|
6eeda04b78 | ||
|
|
c03097e627 | ||
|
|
163e17fb3f | ||
|
|
a330f95559 | ||
|
|
25d7c8376e | ||
|
|
bc93bae9ab | ||
|
|
4f9ca14301 | ||
|
|
b9515a2a81 | ||
|
|
6200e3a693 | ||
|
|
290f3e2845 | ||
|
|
516e3c4cd9 | ||
|
|
e419030339 | ||
|
|
7675148d37 | ||
|
|
fe739cd644 | ||
|
|
dd5e9c08aa | ||
|
|
03b29ce468 | ||
|
|
d2668a15b0 | ||
|
|
d9a99ad65b | ||
|
|
0fc9d29884 | ||
|
|
f500ab4a87 | ||
|
|
19a85276af | ||
|
|
70c2404b20 | ||
|
|
27a4b3b6f4 | ||
|
|
5df79f083a | ||
|
|
1501c3689a | ||
|
|
277ec8ffcd | ||
|
|
c7b82adfb0 | ||
|
|
fb56430213 | ||
|
|
083b439049 | ||
|
|
98e495f7e4 | ||
|
|
508e607440 | ||
|
|
ab69588331 | ||
|
|
a8ac109a50 | ||
|
|
1813baaa6f | ||
|
|
5b5171b9bc | ||
|
|
b912e8cbca | ||
|
|
cc2eaf6c1a | ||
|
|
fbe31f3232 | ||
|
|
b530af1ff6 | ||
|
|
a608cbe0fe | ||
|
|
a4ed9393f0 | ||
|
|
f294d4fcef | ||
|
|
6046ca10d0 | ||
|
|
d24371b9e4 | ||
|
|
645d84858e | ||
|
|
04ecd3d976 | ||
|
|
c02fe8a00c | ||
|
|
008e42a267 | ||
|
|
316ab772bf | ||
|
|
bca4b06750 | ||
|
|
dea1d74e40 | ||
|
|
12f1259495 | ||
|
|
cc30f4f207 | ||
|
|
775a2038d0 | ||
|
|
1dc8ae036f | ||
|
|
0011993afb | ||
|
|
1ef22ad52a | ||
|
|
e8030c0180 | ||
|
|
e929f6f02d | ||
|
|
8ec91f7a40 | ||
|
|
41b9411d06 | ||
|
|
9518422f62 | ||
|
|
4c03891f19 | ||
|
|
52a3bf50d0 | ||
|
|
c1657b5772 | ||
|
|
e57c9487ca | ||
|
|
7e583d6319 | ||
|
|
11256c0e3a | ||
|
|
ffb53626eb | ||
|
|
a74671f710 | ||
|
|
04d83251b4 | ||
|
|
bece6dfbb0 | ||
|
|
5e5cb4e388 | ||
|
|
8f9dc451f1 | ||
|
|
ffb1c94c3f | ||
|
|
d9b2f0cef8 | ||
|
|
458fa1732c | ||
|
|
3040eee59c | ||
|
|
23e532a214 | ||
|
|
70f1e5e621 | ||
|
|
10033be4b5 | ||
|
|
914067038a | ||
|
|
bf9f08826b | ||
|
|
da27b80e82 | ||
|
|
e6c9c7798e | ||
|
|
d0b7b956e8 | ||
|
|
0301064ed5 | ||
|
|
b8595f8d59 | ||
|
|
d120c8e031 | ||
|
|
aa91b5b20b | ||
|
|
84dfaa0a77 | ||
|
|
1e8ae04ad5 | ||
|
|
0cfa7bf61d | ||
|
|
9987c0c13f | ||
|
|
99a88e66e7 | ||
|
|
b7a3e276f1 | ||
|
|
0240f925f6 | ||
|
|
d9774272ce | ||
|
|
aa2532df6f | ||
|
|
0c14e5dedc | ||
|
|
62248ff97c | ||
|
|
17343abce2 | ||
|
|
0ff73d0c21 | ||
|
|
77da1f8252 | ||
|
|
18301300aa | ||
|
|
ca044e8390 | ||
|
|
a6b68b4076 | ||
|
|
dbce3252ab | ||
|
|
cc3b09f590 | ||
|
|
8ed2b820f2 | ||
|
|
3b967933ca | ||
|
|
665aad40a8 | ||
|
|
527f429c20 | ||
|
|
85803b11b4 | ||
|
|
6d3f3d1353 | ||
|
|
b70748e1b8 | ||
|
|
c6406ab0ea | ||
|
|
28c750caf9 | ||
|
|
ab22780598 | ||
|
|
1b2e0fb58e | ||
|
|
6616234393 | ||
|
|
e33420bab7 | ||
|
|
17dcbb25c3 | ||
|
|
1b36c40959 | ||
|
|
db2cd8501e | ||
|
|
fd6348adbe | ||
|
|
e3c9c16add | ||
|
|
d17f8f90b8 | ||
|
|
bc9182e37f | ||
|
|
68923dd95f | ||
|
|
1d353bdc5b | ||
|
|
df7109ba45 | ||
|
|
ab3449243f | ||
|
|
457e7b65c9 | ||
|
|
755d4ea612 | ||
|
|
ad6bcb1572 | ||
|
|
42235a35ee | ||
|
|
a8f6e66ab4 | ||
|
|
d6e51b9c55 | ||
|
|
d2970c10b3 | ||
|
|
f3cb2aab87 | ||
|
|
d9c6522685 | ||
|
|
ca54a0abf0 | ||
|
|
1ad743a126 | ||
|
|
cde5a5b408 | ||
|
|
e6a45fd356 | ||
|
|
c3eb05e5c6 | ||
|
|
a82dee2beb | ||
|
|
8099bfe330 | ||
|
|
7074a63f95 | ||
|
|
95d6a1c183 | ||
|
|
4fd433b072 | ||
|
|
dc6804bba6 | ||
|
|
3e005e5032 | ||
|
|
f93e8e221a | ||
|
|
cc7433025f | ||
|
|
aa17482a6e | ||
|
|
7f0f6e7c4d | ||
|
|
16a0cd1fe7 | ||
|
|
2fc9d587d9 | ||
|
|
1d950d9640 | ||
|
|
3b63f88669 | ||
|
|
4f35e8348d | ||
|
|
36c76b96b3 | ||
|
|
2fe98577b6 | ||
|
|
dadeb2670b | ||
|
|
aedca9e969 | ||
|
|
e1ca6bbc94 | ||
|
|
22d66103b6 | ||
|
|
0e2a0af914 | ||
|
|
7591345181 | ||
|
|
8d157a5ad5 | ||
|
|
d41ef023ff | ||
|
|
8b857881a8 | ||
|
|
995a8c2dc0 | ||
|
|
30b769db13 | ||
|
|
ac7bd33f08 | ||
|
|
4dc7accef9 | ||
|
|
90b8923b98 | ||
|
|
9a7e4d05f0 | ||
|
|
8d981d1b05 | ||
|
|
f5bf60dd37 | ||
|
|
585eb2a946 | ||
|
|
ee38a90aad | ||
|
|
d9fc5f5c90 | ||
|
|
5d9be82e8f | ||
|
|
808a238d2a | ||
|
|
69908c1ab5 | ||
|
|
f8cdcde27c | ||
|
|
0a505bec75 | ||
|
|
948d599390 | ||
|
|
f561141315 | ||
|
|
8419b140a7 | ||
|
|
4be1ab3cac | ||
|
|
12824f6f4f | ||
|
|
60df079794 | ||
|
|
1924862fcb | ||
|
|
dba3de83fa | ||
|
|
df30a209a5 | ||
|
|
0fea0966c0 | ||
|
|
727fc969fa | ||
|
|
c17ae13578 | ||
|
|
8e09051bf7 | ||
|
|
4f03643840 | ||
|
|
6cdab8f296 | ||
|
|
fc1bade1d1 | ||
|
|
fd5132b317 | ||
|
|
544efc80d5 | ||
|
|
43fe97a6fe | ||
|
|
f021faf0d3 | ||
|
|
965196da2e | ||
|
|
72c4cc0992 | ||
|
|
b0838e88fd | ||
|
|
925d7ce2dc | ||
|
|
2b562ace12 | ||
|
|
a4000a6c0f | ||
|
|
a56d24b9dc | ||
|
|
607b59a978 | ||
|
|
b8eb2c708e | ||
|
|
7bffc44db0 | ||
|
|
1099ce055a | ||
|
|
de5e958bf3 | ||
|
|
ab6494a2f6 | ||
|
|
967fbd2608 | ||
|
|
e6ffd2e16d | ||
|
|
8ff0835e4e | ||
|
|
301df2e1d1 | ||
|
|
4c4f79494b | ||
|
|
bcd998d690 | ||
|
|
7ab3c18762 | ||
|
|
ddb426674f | ||
|
|
6c93a6c1a8 | ||
|
|
28d0ae9b75 | ||
|
|
742a6b244f | ||
|
|
4c5fcf5310 | ||
|
|
aaf5f4d354 | ||
|
|
f6cbadff61 | ||
|
|
5414cbcc14 | ||
|
|
3104239a9f | ||
|
|
e10e048e2c | ||
|
|
32a360ba0c | ||
|
|
99d190abff | ||
|
|
33aa3043c6 | ||
|
|
4577c36afb | ||
|
|
6d9d5d1533 | ||
|
|
a6cc7b77bb | ||
|
|
3ef2eca934 | ||
|
|
e81eaa5b57 | ||
|
|
edc5ce5fb4 | ||
|
|
b1b7d3fea2 | ||
|
|
7ee88cefe5 | ||
|
|
2cfa9f8985 | ||
|
|
5bab095036 | ||
|
|
c394f56548 | ||
|
|
dc270bb64e | ||
|
|
8c6c3a6ca0 | ||
|
|
ac05f24646 | ||
|
|
2b3045594b | ||
|
|
86d8fb01ef | ||
|
|
fe9a1c7e99 | ||
|
|
a9735c69cb | ||
|
|
bf401d1ff0 | ||
|
|
fb6e65d868 | ||
|
|
aa04b27500 | ||
|
|
954d6fa156 | ||
|
|
dd2d22b793 | ||
|
|
03bfce766d | ||
|
|
fab9eed4e1 | ||
|
|
123ed403ae | ||
|
|
0c763e3715 | ||
|
|
b52a0a0ebd | ||
|
|
9c1edea469 | ||
|
|
87d8c17f4f | ||
|
|
506aafbdf0 | ||
|
|
814bba5273 | ||
|
|
b5475b0e3e | ||
|
|
3ab5a4daf3 | ||
|
|
f7672e66ab | ||
|
|
61d926a23d | ||
|
|
5ab364f0ab | ||
|
|
734d2b2414 | ||
|
|
0763a96f34 | ||
|
|
2ae4ba68e4 | ||
|
|
750adabba1 | ||
|
|
a914a69051 | ||
|
|
beacc319b9 | ||
|
|
c48b7b980b | ||
|
|
c60a3939a4 | ||
|
|
d553483dcb | ||
|
|
c9f1a61bf5 | ||
|
|
84d58bf2d9 | ||
|
|
c0c49cc951 | ||
|
|
fe08a94540 | ||
|
|
df35528a53 | ||
|
|
405f6e91de | ||
|
|
ac072e5a88 | ||
|
|
ca0b098866 | ||
|
|
3fd13dd47c | ||
|
|
38bc7e1bde | ||
|
|
6497d770dd | ||
|
|
2410fa6578 | ||
|
|
32e1623998 | ||
|
|
31799ad144 | ||
|
|
b60f7ba22e | ||
|
|
9a03c17a0c | ||
|
|
1bc2295b70 | ||
|
|
37cd4ff11b | ||
|
|
5f9108400b | ||
|
|
c80ef7a8c0 | ||
|
|
7bdc302583 | ||
|
|
8de2a12323 | ||
|
|
6004c153d3 | ||
|
|
ac96ad3720 | ||
|
|
a06793616d | ||
|
|
97f545bd4e | ||
|
|
a267b63df2 | ||
|
|
ea9d961fd8 | ||
|
|
51396f94b1 | ||
|
|
412dc73d9d | ||
|
|
c605e2d030 | ||
|
|
d5c93b60a9 | ||
|
|
7d8c6aef8e | ||
|
|
3c593ea3ed | ||
|
|
5a7732588d | ||
|
|
25488ae972 | ||
|
|
4bf9733fe6 | ||
|
|
0672aebd61 | ||
|
|
5db7d6380f | ||
|
|
7f90a6ffec | ||
|
|
d45f0e382b | ||
|
|
fab6d6e82d | ||
|
|
a79c61c5a4 | ||
|
|
3dfe05dccd | ||
|
|
e485484051 | ||
|
|
865ddcd59b | ||
|
|
999070b270 | ||
|
|
00d1590790 | ||
|
|
6cc17a08dc | ||
|
|
60911f3ec7 | ||
|
|
7f1a35fffe | ||
|
|
10f5fd673e | ||
|
|
765921d219 | ||
|
|
2422e60aa1 | ||
|
|
e9265eb0a6 | ||
|
|
07ad7db139 | ||
|
|
159ecf5ffa | ||
|
|
12be2c7b7b | ||
|
|
95590a2673 | ||
|
|
869f6b1120 | ||
|
|
2cdfac02e6 | ||
|
|
a9127c7021 | ||
|
|
3023de995a | ||
|
|
59c3df6a47 | ||
|
|
7391e3bc69 | ||
|
|
4ab72a6334 | ||
|
|
af357aca51 | ||
|
|
c7d060bb8d | ||
|
|
c3db5c0525 | ||
|
|
04328d2b8a | ||
|
|
fb03f426c6 | ||
|
|
00ed7bb0e8 | ||
|
|
38d81ced56 | ||
|
|
95ecf75a3a | ||
|
|
0afde170f4 | ||
|
|
5a312ee6c6 | ||
|
|
6533da0ed6 | ||
|
|
339ad676ca | ||
|
|
0edc762ff8 | ||
|
|
320a3a11b8 | ||
|
|
f4d177ce8c | ||
|
|
f3598e915e | ||
|
|
3314075f6e | ||
|
|
141296a2d5 | ||
|
|
48147f460b | ||
|
|
2d72fbb82a | ||
|
|
98b72f07b6 | ||
|
|
465fb2bef4 | ||
|
|
d1f96af65f | ||
|
|
990dad0c51 | ||
|
|
67a8230b91 | ||
|
|
0d66e7bc1b | ||
|
|
56f0e0a1e2 | ||
|
|
1e5c48129c | ||
|
|
aed830b1ea | ||
|
|
c30596c89b | ||
|
|
851e0e56dd | ||
|
|
0e92743da5 | ||
|
|
0245d82250 | ||
|
|
9dc3b910f4 | ||
|
|
089d75cf57 | ||
|
|
9ef45d6324 | ||
|
|
881a957f59 | ||
|
|
0e0562c1a3 | ||
|
|
40cc7d30b2 | ||
|
|
c2b1d336db | ||
|
|
eca69b4d96 | ||
|
|
c607198de9 | ||
|
|
3ce9bd05a8 | ||
|
|
d209918dde | ||
|
|
c5de65044d | ||
|
|
96fbe3a685 | ||
|
|
d33defea6a | ||
|
|
d16d3eac34 | ||
|
|
99b175f72a | ||
|
|
ae4d16bfcb | ||
|
|
ff039c0d24 | ||
|
|
f4ef41c130 | ||
|
|
e572d22819 | ||
|
|
9c448eca97 | ||
|
|
742caa227d | ||
|
|
b095e87ec9 | ||
|
|
b2c66efa83 | ||
|
|
7274713e50 | ||
|
|
094f943ab5 | ||
|
|
f55198249f | ||
|
|
2fddfe12e6 | ||
|
|
88619f0512 | ||
|
|
695368932d | ||
|
|
84926f12eb | ||
|
|
9048c00021 | ||
|
|
ed5cc49b6d | ||
|
|
f348b4e8de | ||
|
|
6acd8167fb | ||
|
|
b131c2fe1f | ||
|
|
d848a2e78c | ||
|
|
2447f61f8c | ||
|
|
9839e48dee | ||
|
|
95f4f96cda | ||
|
|
9ce697bf00 | ||
|
|
5424513e7e | ||
|
|
4de268fcd8 | ||
|
|
86c415cc55 | ||
|
|
e08066a26c | ||
|
|
eea985d56d | ||
|
|
000d210b33 | ||
|
|
124d68a8fe | ||
|
|
0c019b1c71 | ||
|
|
fd5468ff6e | ||
|
|
1d2c1413da | ||
|
|
005945447b | ||
|
|
d69ddef3e3 | ||
|
|
4ac7060e4d | ||
|
|
4bd30be29f | ||
|
|
0b52918d8d | ||
|
|
9b73565e28 | ||
|
|
a1e04e8df2 | ||
|
|
51a542b6a9 | ||
|
|
f0e8d80da4 | ||
|
|
532bf008d3 | ||
|
|
701c727475 | ||
|
|
3df605c50f | ||
|
|
ad97188902 | ||
|
|
237edcc2fa | ||
|
|
51ab2c4fd8 | ||
|
|
c0a482a92a | ||
|
|
829a063fe7 | ||
|
|
f3f2fa11f7 | ||
|
|
298ba53ec2 | ||
|
|
28401e3c95 | ||
|
|
3a0ed1a244 | ||
|
|
b27895a3c2 | ||
|
|
e8647313b7 | ||
|
|
7fdff93a25 | ||
|
|
06335ca854 | ||
|
|
ba22800783 | ||
|
|
832f260472 | ||
|
|
df594f18d4 | ||
|
|
5663d50a1c | ||
|
|
373be37287 | ||
|
|
7071d74196 | ||
|
|
f5f3ad0d58 | ||
|
|
7e1b9b65f1 | ||
|
|
57839da643 | ||
|
|
4a4dd66a65 | ||
|
|
cfcf97bc88 | ||
|
|
1adf625044 | ||
|
|
a136ca106b | ||
|
|
cbe1a79c2a | ||
|
|
093f953c7f | ||
|
|
dfd4fd252f | ||
|
|
e22befbc33 | ||
|
|
7bbb0eac56 | ||
|
|
c42facec8a | ||
|
|
7d2663b082 | ||
|
|
43c6751039 | ||
|
|
f0fc94d25a | ||
|
|
10ba9c5f87 | ||
|
|
dcca1ec71f | ||
|
|
7847227a57 | ||
|
|
3f1660295e | ||
|
|
87f1063d6f | ||
|
|
0951a7d73e | ||
|
|
256fcf3a8b | ||
|
|
7e082f27e0 | ||
|
|
e81b8ca4f2 | ||
|
|
351910a3ef | ||
|
|
e700dcb142 | ||
|
|
2527b0e5d1 | ||
|
|
0fe36a98d7 | ||
|
|
c9ef897a2e | ||
|
|
2cc46406de | ||
|
|
1ef39bf1a3 | ||
|
|
e0bc16a232 | ||
|
|
f46109e080 | ||
|
|
21983061a6 | ||
|
|
87543095f1 | ||
|
|
688b0836a5 | ||
|
|
42d2a5ce89 | ||
|
|
307f5e2c8d | ||
|
|
b1a6b43414 | ||
|
|
8d90e2d190 | ||
|
|
767446c804 | ||
|
|
b6d5762ddd | ||
|
|
dc0ab2e810 | ||
|
|
f50ec2ae5a | ||
|
|
a4d0ddf229 | ||
|
|
b1d84fef55 | ||
|
|
e26f4c33ba | ||
|
|
7b023ab34f | ||
|
|
ded186890d | ||
|
|
4c5b16e502 | ||
|
|
6b95f8b7bf | ||
|
|
4a50293e04 | ||
|
|
9fa829b30a | ||
|
|
0dc6b8321e | ||
|
|
4616e61825 | ||
|
|
63c0b7da37 | ||
|
|
aad041998c | ||
|
|
b0e58975f2 | ||
|
|
cf41dd6fb4 | ||
|
|
a66eb04097 | ||
|
|
960d20599e | ||
|
|
8ea6f13d34 | ||
|
|
34d28c798d | ||
|
|
cb0046f4cc | ||
|
|
b475ead059 | ||
|
|
9557c48cab | ||
|
|
1062507ffb | ||
|
|
b5aef1cccc | ||
|
|
658e6a9da5 | ||
|
|
ff6f18d575 | ||
|
|
f9df1085ec | ||
|
|
997bacefe2 | ||
|
|
5f94707a9e |
No files matched your search
+302
-79
@@ -1,41 +1,91 @@
|
||||
# Changelog
|
||||
## [1.4.0](#1-4-0)
|
||||
## [1.4.1](#1-4-1)
|
||||
|
||||
### Changes
|
||||
- Slightly lowered the volume of all cries
|
||||
- Pasture blocks will now also connect their bottom left and right sides to walls, iron bars, glass panes and any other modded block that follows the same connection rules.
|
||||
|
||||
### Additions
|
||||
- Added nicknaming.
|
||||
- Added mints, found on mountains.
|
||||
- Added revival herbs, with pep-up flowers if fully grown, growing in lush caves.
|
||||
- Added medicinal leeks, growing on the surface of rivers and ponds. It is a potion ingredient!
|
||||
- Added big roots, generating from cave ceiling dirt and sometimes spreading into energy roots.
|
||||
- Added trading between players. Press R while looking at another player, you'll figure it out.
|
||||
- Added pasture blocks, used to let your PC Pokémon roam around your base.
|
||||
- Added 69 berry trees. Some are found in village farms, some from planting different berries close to each other.
|
||||
- Cobblemon now has compatibility with [Adorn](https://modrinth.com/mod/adorn) allowing you to craft Apricorn wood furniture.
|
||||
- Berries can now be used in recipes from [Farmer's Delight](https://modrinth.com/mod/farmers-delight) and [Farmer's Delight (Fabric)](https://modrinth.com/mod/farmers-delight-fabric), as well as any other mods using the same berry tags.
|
||||
- The nature of cobblemon will now be displayed italicized when a mint has been applied. Hovering over the nature will display the mint that was applied. This is the intended behaviour, because the original nature and taste of the Cobblemon does not change when a mint is applied.
|
||||
- Giving Pokémon items now plays a sound
|
||||
|
||||
### Fixes
|
||||
- Fixed various stone related blocks not being valid for Big Roots to spread on the Fabric version.
|
||||
- Updated the registration of compostable items to improve compatibility with Fabric forks such as Quilt. Please note this does not mean we officially support Quilt, this change was only done since it was possible by correcting the registration to use the new intended way in the Fabric API.
|
||||
- Fixed Dispensers being unable to shear grown Apricorns.
|
||||
- Fixed Bowl not being given back to player after using Berry Juice
|
||||
- Fixed Moonphases for Clefairy line
|
||||
- Fixed issue where Potions, Super Potions, and Hyper Potions did not work during battle
|
||||
- Fixed the compatibility patch with the Forge version of [Carry On](https://modrinth.com/mod/carry-on) due to a bug on the mod, the Fabric version was unchanged and is still compatible.
|
||||
- Added the ability to place Berries on modded Farmland blocks.
|
||||
- Shouldered Pokémon now hop off when selected in team and r is pressed. This also is in effect in battles leading to shouldered Pokémon jumping of the shoulder of the trainer when it is their turn.
|
||||
- Made more items compostable and changed the process for making items compostable.
|
||||
- Added the ability for Hoppers to fill Brewing Stands with Medicinal Brews and Potions.
|
||||
- Apricorn blocks are now flammable.
|
||||
- The default pose for Pokémon being passengers is now "standing".
|
||||
- Fixed issue where some IVs were changing every player relog.
|
||||
- Fixed cursed battle message
|
||||
- Fixed mistakes in pokemon spawning JSONs
|
||||
- Fixed advancement crash from bad datapack evolution data.
|
||||
|
||||
### Developer
|
||||
- Fixed the `SpawnEvent` not respecting usage of `Cancelable#cancel`.
|
||||
- Added the `EvolutionTestedEvent`, this allows listening and overriding the final result of evolution requirement tests.
|
||||
|
||||
## [1.4.0 - The Friends and Farms Update (October 13th, 2023)](#1-4-0)
|
||||
### Additions
|
||||
- Added pasture blocks, used to let your PC Pokémon roam around an area.
|
||||
- Added nicknaming from the summary menu of a Pokémon (click their name).
|
||||
- Added trading between players. Press R while looking at another player and you'll figure the rest out.
|
||||
- Added mints for changing Pokémon stats. These are most commonly found at high altitudes.
|
||||
- Added [Revival Herbs](https://wiki.cobblemon.com/index.php/Revival_Herb), with pep-up flowers when fully grown, growing in lush caves.
|
||||
- Added [Medicinal Leeks](https://wiki.cobblemon.com/index.php/Medicinal_Leek), growing on the surface of rivers and ponds. It is a potion ingredient and can be cooked as food!.
|
||||
- Added [Big Roots](https://wiki.cobblemon.com/index.php/Big_Root), generating from cave ceiling dirt which sometimes spread as energy roots.
|
||||
- Added [69 Berry Trees](https://wiki.cobblemon.com/index.php/Berry_Tree) and [Berries](https://wiki.cobblemon.com/index.php/Berry). Some are found in village farms, some from planting different berries close to each other.
|
||||
- Added mulches: Mulch Base, Growth Mulch, Surprise Mulch, Coarse Mulch, Humid Mulch, Rich Mulch, Loamy Mulch, Peat Mulch, and Sandy Mulch
|
||||
- Added [Vivichokes](https://wiki.cobblemon.com/index.php/Vivichoke), obtainable from wandering villager trades and some loot chests.
|
||||
- Added medicine brewing using medicinal leeks and berries in brewing stands.
|
||||
- Added Pokémon cries when in battles and being sent out.
|
||||
- Added support for scale in animations.
|
||||
- Added support for jump keyframes (i.e. pre and post keyframes)
|
||||
- Added AI for Nosepass to point towards world spawn when idle.
|
||||
- Added structure spawn conditions
|
||||
- Added cries for Gen 1-9 starters and their evolutions.
|
||||
- Added cries for Chatot, Darmanitan, Darumaka, Lucario, Mimikyu, Quagsire, Riolu, Wooper, Caterpie, Metapod, Butterfree, Weedle, Kakuna, Beedrill, Pidgey, Pidgeotto, Pidgeot, Rattata, Raticate, Cleffa, Clefairy, Clefable, Tauros, Miltank, Buneary, Lopunny, Wooloo, Dubwool.
|
||||
- Added recipes for Berry Juice, Heal Powder, Remedy, Fine Remedy, Superb Remedy, Revive, Max Revive, HP Up, Protein, Iron, Calcium, Zinc, Carbos, PP Up, PP Max, Pasture, Medicinal Leek to Magenta Dye, Roasted Leek, Leek and Potato Stew, Braised Vivichoke, Vivichoke Dip, Mulch Base, Growth Mulch, Surprise Mulch, Coarse Mulch, Humid Mulch, Rich Mulch, Loamy Mulch, Peat Mulch, Sandy Mulch, Health Feather, Muscle Feather, Resist Feather, Genius Feather, Clever Feather, Genius Feather
|
||||
- Added a shoulder mount for Mimikyu
|
||||
- Added Advancement trigger for defeating Pokémon and collecting varieties of Pokémon.
|
||||
- Added sleep animations to Arcanine, Jigglypuff, Wigglytuff, Vulpix and Ninetales.
|
||||
- Added medicinal items: Berry Juice, Heal Powder, Remedy, Fine Remedy, Superb Remedy, Revive, Max Revive, Potion, Super Potion, Hyper Potion, Max Potion, Full Restore, Full Heal, Antidote, Awakening, Burn Heal, Ice Heal, and Paralyze Heal.
|
||||
- Added battle items: X Attack, X Defence, X Sp.Atk, X Sp.Def, X Speed, Dire Hit, and Guard Spec.
|
||||
- Added EV items: Power Anklet, Power Band, Power Belt, Power Bracelet, Power Lens, Power Weight.
|
||||
- Added food items: Roasted Leek, Leek and Potato Stew, Braised Vivichoke, and Vivichoke Dip
|
||||
- Added evolution items: Auspicious Armor and Malicious Armor, which can be used to evolve Charcadet into Armarouge or Ceruledge respectively.
|
||||
- Added (mostly brewing) recipes for HP Up, Protein, Iron, Calcium, Zinc, Carbos, PP Up, PP Max, and Medicinal Leek to Magenta Dye.
|
||||
- Added held items: Bright Powder, Destiny Knot
|
||||
- Added AI for Nosepass to point towards world spawn when idle. We just think it's neat.
|
||||
- Added shoulder mounting for Mimikyu.
|
||||
- Added flying placeholder animations to Pidgey, Pidgeotto, Pidgeot, Golbat, Crobat, Scyther, Scizor, Zapdos, Moltres, Articuno, Dragonite, Rowlet, Dartrix, and Decidueye.
|
||||
- Added loot to various vanilla chest loot tables (Link Cable in Ancient Cities, Woodland Mansions, End Cities, and Igloos, Vivichoke Seeds in Jungle Temples, Dungeons, and Plains, Savanna, Snowy, and Taiga Villages, and all 7 Apricorn Sprouts in Desert, Plains, Savanna, Snowy, and Taiga Villages, as well as the Bonus Chest)
|
||||
- Added loot to various vanilla chest loot tables (Link Cable in Ancient Cities, Woodland Mansions, End Cities, and Igloos, Vivichoke Seeds in Jungle Temples, Dungeons, and Plains, Savanna, Snowy, and Taiga Villages, and all 7 Apricorn Sprouts in Desert, Plains, Savanna, Snowy, and Taiga Villages, as well as the Bonus Chest, which can also have 5 of the basic Poké Ball types)
|
||||
- Added a `doShinyStarters` gamerule to make it quick and easy to be offered shiny starters.
|
||||
- Added a `doPokemonLoot` gamerule to toggle Pokémon dropping items/exp on death.
|
||||
- Added ability activation announcement when in battle.
|
||||
- Added animations for Wailord and made it BIGGER.
|
||||
- Added Cherry Torterra variant.
|
||||
- Added 2 new face spots for Spinda. The number of unique Spindas increases...
|
||||
- Added Forretress Shulker variant.
|
||||
- Added the `/teststore <player> <store> <properties>` command allowing command block/mcfunction users to query a party, PC or both for Pokémon matching specific properties and returning the match count, this will be a cheat command in the Minecraft permission system or use the permission `cobblemon.command.teststore` if a permission mod is present.
|
||||
- Added the `/querylearnset <player> <slot> <move>` command allowing command block/mcfunction users to query a party slot and check if the Pokémon can learn a specific move returning a 1 if yes otherwise 0, this will be a cheat command in the Minecraft permission system or use the permission `cobblemon.command.querylearnset` if a permission mod is present.
|
||||
- Added the `/testpcslot <player> <slot> <properties>` command allowing command block/mcfunction users to query a pc slot and check if the Pokémon matches specific properties returning a 1 if yes otherwise 0, this will be a cheat command in the Minecraft permission system or use the permission `cobblemon.command.testpcslot` if a permission mod is present.
|
||||
- Added the `/testpartyslot <player> <slot> <properties>` command allowing command block/mcfunction users to query a party slot and check if the Pokémon matches a specific properties returning a 1 if yes otherwise 0, this will be a cheat command in the Minecraft permission system or use the permission `cobblemon.command.testpartyslot` if a permission mod is present.
|
||||
- Added the `/clearparty <player>` command for emptying a player's party.
|
||||
- Added the `/clearpc <player>` command for emptying a player's PC.
|
||||
- Added the `/pokemonrestart <reset_starters>` and the `/pokemonrestartother <player> <reset_starters>` command allowing command block/mcfunction users to reset a players Pokémon data.
|
||||
- Added support for "isBattle" and "isTouchingWater" properties on resource pack Pokémon poses. This allows your custom Pokémon to be posed differently when in battle.
|
||||
- Added support for "isVisible" on a transformed part on resource pack Pokémon poses. This allows your custom Pokémon to have bones disappear in specific poses, such as hiding Greninja's throwing star when not in a battle pose.
|
||||
- Added the `doPokemonLoot` gamerule to toggle Pokémon dropping items/exp on death.
|
||||
|
||||
### Pokémon Added
|
||||
#### Gen 2
|
||||
|
||||
- Chikorita
|
||||
- Bayleef
|
||||
- Meganium
|
||||
- Totodile
|
||||
- Croconaw
|
||||
- Feraligatr
|
||||
- Cyndaquil
|
||||
- Quilava
|
||||
- Typhlosion
|
||||
- Spinarak
|
||||
- Ariados
|
||||
- Shuckle
|
||||
@@ -49,6 +99,12 @@
|
||||
- Flaaffy
|
||||
- Ampharos
|
||||
- Sudowoodo
|
||||
- Snubbull
|
||||
- Granbull
|
||||
- Phanpy
|
||||
- Donphan
|
||||
- Teddiursa
|
||||
- Ursaring
|
||||
|
||||
#### Gen 3
|
||||
|
||||
@@ -62,7 +118,7 @@
|
||||
- Cacnea
|
||||
- Cacturne
|
||||
- Poochyena
|
||||
- Mightyenna
|
||||
- Mightyena
|
||||
- Wingull
|
||||
- Pelipper
|
||||
- Numel
|
||||
@@ -75,11 +131,22 @@
|
||||
- Chimecho
|
||||
- Barboach
|
||||
- Whiscash
|
||||
- Volbeat
|
||||
- Illumise
|
||||
- Zigzagoon
|
||||
- Linoone
|
||||
- Ralts
|
||||
- Kirlia
|
||||
- Gardevoir
|
||||
- Nincada
|
||||
- Ninjask
|
||||
- Shedinja
|
||||
- Beldum
|
||||
- Metang
|
||||
- Metagross
|
||||
|
||||
#### Gen 4
|
||||
|
||||
- Cherubi (Planned)
|
||||
- Cherrim (Planned)
|
||||
- Carnivine
|
||||
- Shinx
|
||||
- Luxio
|
||||
@@ -92,48 +159,77 @@
|
||||
- Chatot
|
||||
- Combee
|
||||
- Vespiquen
|
||||
- Buizel
|
||||
- Floatzel
|
||||
- Starly
|
||||
- Staravia
|
||||
- Staraptor
|
||||
- Gallade
|
||||
|
||||
#### Gen 5
|
||||
|
||||
- Bouffolant
|
||||
- Bouffalant
|
||||
- Roggenrola
|
||||
- Boldore
|
||||
- Gigalith
|
||||
- Venepede
|
||||
- Whirlpede
|
||||
- Venipede
|
||||
- Whirlipede
|
||||
- Scolipede
|
||||
- Yamask (Planned)
|
||||
- Cofagrigus (Planned)
|
||||
- Patrat (Planned)
|
||||
- Watchog (Planned)
|
||||
- Yamask
|
||||
- Cofagrigus
|
||||
- Patrat
|
||||
- Watchog
|
||||
- Lillipup
|
||||
- Herdier
|
||||
- Stoutland
|
||||
- Cottonee
|
||||
- Whimsicott
|
||||
- Pidove
|
||||
- Tranquill
|
||||
- Unfezant
|
||||
- Timburr
|
||||
- Gurdurr
|
||||
- Conkeldurr
|
||||
|
||||
#### Gen 6
|
||||
|
||||
- Scatterbug
|
||||
- Spewpa
|
||||
- Vivillion
|
||||
- Skrelp (Planned)
|
||||
- Dragalge (Planned)
|
||||
- Bunnelby (Planned)
|
||||
- Diggersby (Planned)
|
||||
- Vivillon
|
||||
- Skrelp
|
||||
- Dragalge
|
||||
- Bunnelby
|
||||
- Diggersby
|
||||
- Phantump
|
||||
- Trevenant
|
||||
- Fletchling
|
||||
- Fletchinder
|
||||
- Talonflame
|
||||
|
||||
#### Gen 7
|
||||
|
||||
- Wishiwashi (Planned)
|
||||
- Wishiwashi
|
||||
- Cutiefly
|
||||
- Ribombee
|
||||
- Stufful
|
||||
- Bewear
|
||||
- Comfey
|
||||
- Alolan Exeggutor
|
||||
- Alolan Raichu
|
||||
- Alolan Meowth
|
||||
- Alolan Persian
|
||||
- Komala
|
||||
- Wimpod
|
||||
- Golisopod
|
||||
- Crabrawler
|
||||
- Crabominable
|
||||
- Mudbray
|
||||
- Mudsdale
|
||||
|
||||
#### Gen 8
|
||||
|
||||
- Arrokuda (Planned)
|
||||
- Barraskewda (Planned)
|
||||
- Arrokuda
|
||||
- Barraskewda
|
||||
- Nickit
|
||||
- Thievul
|
||||
- Falinks
|
||||
@@ -142,12 +238,28 @@
|
||||
- Rookidee
|
||||
- Corvisquire
|
||||
- Corviknight
|
||||
- Galarian Ponyta
|
||||
- Galarian Rapidash
|
||||
- Yamper
|
||||
- Boltund
|
||||
- Galarian Zigzagoon
|
||||
- Galarian Linoone
|
||||
- Obstagoon
|
||||
- Galarian Meowth
|
||||
- Perrserker
|
||||
- Ursaluna
|
||||
|
||||
#### Gen 9
|
||||
|
||||
- Sprigatitto
|
||||
- Sprigatito
|
||||
- Floragato
|
||||
- Meowscarada
|
||||
- Fuecoco
|
||||
- Crocalor
|
||||
- Skeledirge
|
||||
- Quaxly
|
||||
- Quaxwell
|
||||
- Quaquaval
|
||||
- Flittle
|
||||
- Espathra
|
||||
- Garganacl
|
||||
@@ -157,91 +269,186 @@
|
||||
- Ceruledge
|
||||
- Cetoddle
|
||||
- Cetitan
|
||||
- Shroodle (Planned)
|
||||
- Grafaiai (Planned)
|
||||
- Shroodle
|
||||
- Grafaiai
|
||||
- Tandemaus
|
||||
- Maushold
|
||||
- Varoom
|
||||
- Revavroom
|
||||
- Squawkabilly (Planned)
|
||||
- Squawkabilly
|
||||
- Glimmet
|
||||
- Glimmora
|
||||
|
||||
- Annihilape
|
||||
- Tinkatink
|
||||
- Tinkatuff
|
||||
- Tinkaton
|
||||
- Maschiff
|
||||
- Mabosstiff
|
||||
- Lechonk
|
||||
- Oinkologne
|
||||
- Paldean Wooper
|
||||
- Clodsire
|
||||
|
||||
### Changes
|
||||
- Updated model and textures of Arcanine, Magnemite, Magneton, Magnezone, Exeggcute, Exeggutor, Farfetchd, Elekid, Electabuzz, Electivire, Pichu, Pikachu, Raichu, Gengar, Wooper Drowzee, Hypno, Aerodactyl, Spearow, Fearow, Lickitung, Pidgeotto, Pidgeot, Scyther, Scizor, Kleavor, Popplio, Brionne, Primarina, Torchic, Combusken, Blaziken, Aerodacyl, Scyther, Scizor, Kleavor, Lickitung, Lickilicky, Happiny, Chansey, Pidgey, Pidgeotto, Pidgeot, Spearow, Fearow, Drowzee, Hypno, Arcanine, Magnemite, Magneton, Magnezone, Exeggcute, Exeggutor, Farfetchd, Elekid, Electabuzz, Electivire, Pichu, Pikachu, Raichu, and Gengar.
|
||||
- Updated models of Sceptile and Shuckle.
|
||||
- Removed the existing shoulder effects from Pokémon until we have more balanced versions of them (they're too powerful!)
|
||||
- Updated models and textures of Weedle, Dwebble and Crustle, Spiritomb, Koffing and Weezing, Kadabra and Alakazam, Emolga, Oshawott, Doduo and Dodrio, Dratini and Dragonair and Dragonite, Sneasel and Weavile, Gyarados, Hitmonlee and Hitmonchan, Chesnaught, Spinda, Mamoswine, Steelix, Misdreavus and Mismagius, Buneary and Lopunny, Golduck, Meowth and Persian, Fennekin and Braixen and Delphox, Snivy and Servine and Serperior, Ratatta and Raticate, Nidorina and Nidoqueen, Nidoran Male and Nidoking, Riolu and Lucario, Haunter and Gengar, Mankey and Primeape, Mew and Mewtwo, Arcanine, Magnemite and Magneton and Magnezone, Exeggcute and Exeggutor, Elekid and Electabuzz and Electivire, Pichu and Pikachu and Raichu, Wooper, Drowzee and Hypno, Aerodactyl, Spearow and Fearow, Lickitung and Lickilicky, Pidgey and Pidgeotto and Pidgeot, Scyther and Scizor and Kleavor, Popplio and Brionne and Primarina, Torchic and Combusken and Blaziken, Happiny and Chansey and Blissey.
|
||||
- Updated animations for Steelix, Turtwig and Grotle and Torterra, Ponyta and Rapidash, Piplup and Prinplup and Empoleon, Drowzee and Hypno, Farfetch'd, Exeggcute and Exeggutor, Bidoof, Chimecho, Lickitung and Lickilicky, Popplio and Brionne, Luvdisc, Chimchar and Monferno and Infernape, Sobble and Drizzile and Inteleon, Greninja, Heatmor, Aerodactyl, Ditto, Lotad and Lombre and Ludicolo, Pumpkaboo and Gourgeist.
|
||||
- Updated sprites for EV medicines, the rare candy, and the apricorn door item.
|
||||
- Updated textures for apricorn doors and all the evolution stone ores.
|
||||
- Ponyta and Rapidash now have animated textures; they look insane.
|
||||
- Updated Apricorn Leaves color.
|
||||
- Wild Pokémon now heal if you are defeated by them or flee from them.
|
||||
- Doubled the default time between ambient Pokémon cries (they have cries if you're using a resource pack to add them)
|
||||
- Moved spawn attempts per tick to a config option (ticksBetweenSpawnAttempts)
|
||||
- PCs can now be waterlogged
|
||||
- Starter selection prompt now appears as a tutorial-esque toast instead of plain text
|
||||
- Cobblemon items can now all have their own tooltips via resourcepacks, to add a tooltip simply add a lang entry like "item.cobblemon.{item_id}.tooltip", if you want to add multiple tooltip lines you can do so with "item.cobblemon.{item_id}.tooltip_1" and upwards.
|
||||
- Updated texture of Weedle.
|
||||
- Updated shiny texture of Dwebble and Crustle.
|
||||
- Updated Animations for Piplup, Prinplup, Empoleon, Drowzee, Hypno, Farfetch'd, Exeggcute, Exeggutor, Bidoof, Chimecho, Lickytung, Lickilicky, Popplio, Brionne, Luvdisc, Chimchar, Monferno, Infernape, Sobble, Drizzile, Inteleon, Greninja, Heatmor, Aerodactyl, Ditto, Lotad, Lombre, Ludicolo, Pumpkaboo, Gourgeist.
|
||||
- Reorganised the advancements recipes folder
|
||||
- Pokeedit command now supports IVs and EVs.
|
||||
- Pokéedit command now supports IVs and EVs.
|
||||
- Reorganised creative categories
|
||||
- Pokemon can now wander into non-solid blocks such as foliage
|
||||
- Pokeballs now despawn after 30 seconds
|
||||
- Pokémon can now wander into non-solid blocks such as foliage
|
||||
- Thrown Poké Balls now despawn after 30 seconds so that they don't fly forever.
|
||||
- Dive Balls will now have the same motion speed underwater as if they were thrown in the air.
|
||||
- Hardcoded potion shoulder effects have been removed, you can now use any potion vanilla or otherwise with the parameters you'd like, for more information see the [Datapack & Resourcepack Creators](#datapack-&-resourcepack-creators) section.
|
||||
- Clicking categories of the Stat sub-section or the party reorder button in the Summary screen will now produce a click sound.
|
||||
- Hardcoded potion shoulder effects have been removed. You can now use any potion vanilla or otherwise with the parameters you'd like, for more information see the [Datapack & Resourcepack Creators](#datapack-&-resourcepack-creators) section.
|
||||
- Clicking categories of the Stat subsection or the party reorder button in the Summary screen will now produce a click sound.
|
||||
- Updated PC Recipe.
|
||||
- Improved Pokémon AI and movement.
|
||||
- Friendship will slowly increase when Pokémon are shoulder-mounted.
|
||||
- Master balls are now unable to be burned when dropped into fire/lava.
|
||||
- Master Balls are now unable to be burned when dropped into fire/lava. They're made from stronger stuff.
|
||||
- Pokémon will appear red when hurt, like regular entities, except when they're fainting.
|
||||
- Pokémon's air meter no longer depletes while battling underwater.
|
||||
- Sleeping partially restores PP of Pokémon
|
||||
- Shoulder mounts now match the shoulder position a bit more accurately when sneaking.
|
||||
- Poison Heal will now cause poisoned Pokémon to heal outside of battle.
|
||||
- Updated Poké Ball, PC, UI, evolution and Healing Machine sounds.
|
||||
|
||||
### Added cries to the following Pokémon:
|
||||
- All starters and their evolutions
|
||||
- Caterpie, Metapod, Butterfree
|
||||
- Weedle, Kakuna, Beedrill
|
||||
- Pidgey, Pidgeotto, Pidgeot
|
||||
- Rattata, Raticate
|
||||
- Spearow, Fearow
|
||||
- Ekans, Arbok
|
||||
- Pichu, Pikachu, Raichu, Alolan Raichu
|
||||
- Cleffa, Clefairy, Clefable
|
||||
- Mankey, Primeape, Annihilape
|
||||
- Ponyta, Rapidash, Galarian Ponyta, Galarian Rapidash
|
||||
- Farfetch'd, Galarian Farfetch'd, Sirfetch'd
|
||||
- Onix, Steelix
|
||||
- Tauros
|
||||
- Ditto
|
||||
- Eevee, Vaporeon, Jolteon, Flareon, Espeon, Umbreon, Leafeon, Glaceon, Sylveon
|
||||
- Hoothoot, Noctowl
|
||||
- Mareep, Flaaffy, Ampharos
|
||||
- Aipom, Ambipom
|
||||
- Wooper, Quagsire, Clodsire
|
||||
- Snubbull, Granbull
|
||||
- Miltank
|
||||
- Poochyena, Mightyena
|
||||
- Taillow, Swellow
|
||||
- Ralts, Kirlia, Gardevoir, Gallade
|
||||
- Shroomish, Breloom
|
||||
- Nincada, Ninjask, Shedinja
|
||||
- Buneary, Lopunny
|
||||
- Chingling
|
||||
- Chatot
|
||||
- Riolu, Lucario
|
||||
- Pidove, Tranquill, Unfezant
|
||||
- Roggenrola, Boldore, Gigalith
|
||||
- Venipede, Whirlipede, Scolipede
|
||||
- Maractus
|
||||
- Dwebble, Crustle
|
||||
- Yamask, Cofagrigus
|
||||
- Bunnelby, Diggersby
|
||||
- Fletchling, Fletchinder, Talonflame
|
||||
- Scatterbug, Spewpa, Vivillon
|
||||
- Honedge, Doublade, Aegislash
|
||||
- Skrelp, Dragalge
|
||||
- Phantump, Trevenant
|
||||
- Pumpkaboo, Gourgeist
|
||||
- Bergmite, Avalugg
|
||||
- Mudbray, Mudsdale
|
||||
- Stufful, Bewear
|
||||
- Mimikyu
|
||||
- Rookidee, Corvisquire, Corviknight
|
||||
- Nickit, Thievul
|
||||
- Wooloo, Dubwool
|
||||
- Yamper, Boltund
|
||||
- Tandemaus
|
||||
- Fidough, Dachsbun
|
||||
- Squawkabilly
|
||||
- Nacli, Naclstack, Garganacl
|
||||
- Charcadet, Armarouge, Ceruledge
|
||||
- Maschiff, Mabosstiff
|
||||
- Shroodle, Grafaiai
|
||||
- Flittle, Espathra
|
||||
- Tinkatink, Tinkatuff, Tinkaton
|
||||
- Varoom, Revavroom
|
||||
- Glimmet, Glimmora
|
||||
- Cetoddle, Cetitan
|
||||
- Tatsugiri
|
||||
|
||||
### Fixes
|
||||
- Fixed spawning moon phase dependent Pokémon only when the moon phase is wrong
|
||||
- Fixed spawning moon phase dependent Pokémon only when the moon phase is wrong (that's a funny woopsy)
|
||||
- Fixed large Pokémon spawning partially inside walls where they suffocate.
|
||||
- Fixed messages for entry hazards, screens, Tailwind, Perish Song, Destiny Bond, Shed Skin, Uproar, Forewarn, Disguise, Arena Trap, Yawn, Curse, Clamp, Whirlpool, Liquid Ooze, Miracle Eye and Safeguard.
|
||||
- Fixed custom Pokémon in your party or PC not being removed when the addon is removed, causing major issues.
|
||||
- Fixed messages for entry hazards, screens, weather, damage, healing, Tailwind, Perish Song, Destiny Bond, Shed Skin, Uproar, Forewarn, Disguise, Arena Trap, Yawn, Curse, Clamp, Whirlpool, Liquid Ooze, Miracle Eye, Safeguard, Magic Bounce, Lock On, Focus Energy, Confusion, and more.
|
||||
- Fixed Porygon not evolving with an Upgrade.
|
||||
- Fixed super sized Pumpkaboo not having any moves.
|
||||
- Fixed Infernape look animation.
|
||||
- Fixed Garchomp t-posing while swimming.
|
||||
- Fixed a bug that caused sleeping Pokémon to stay asleep.
|
||||
- Fixed a bug that would freeze a battle when a Pokémon gets trapped due to an ability.
|
||||
- Fixed Garchomp T-posing while swimming which was very funny.
|
||||
- Fixed a bug that caused sleeping Pokémon to stay asleep. Forever. The years passing them by as they dream of a world without hate...
|
||||
- Fixed a bug that would freeze a battle when a Pokémon gets trapped by an ability, making the trap abilities even scarier and trap-like than they were before.
|
||||
- Fixed the Poké Ball close animation canceling whenever colliding with a block.
|
||||
- Fixed faint animations not working properly in add-ons.
|
||||
- Fixed lighting and Pokémon label issues when a Pokémon item frame is nearby.
|
||||
- Fixed Pokémon being able to spawn outside the world border as a tease.
|
||||
- Fixed deepslate water stone ore items looking like deepslate fire stone ores.
|
||||
- Fixed a bunch of client-side logging errors when Pokémon are shoulder mounted.
|
||||
- Fixed deepslate water stone ore items looking like deepslate fire stone ores. Huh?
|
||||
- Fixed a bunch of client-side logging errors when Pokémon are shoulder mounted. You didn't notice? Good.
|
||||
- Fixed a crash when wild Pokémon have to struggle under specific circumstances.
|
||||
- Fixed uncolored pixels on Yanma's shiny texture.
|
||||
- Fixed apricorn tree leaves looking gross on Fast graphics mode.
|
||||
- Fixed apricorn tree leaves looking gross on the Fast graphics mode.
|
||||
- Fixed hoes not breaking apricorn tree leaves any faster.
|
||||
- Fixed Shiftry's PC model position.
|
||||
- Fixed the /pc command not playing the opening sound.
|
||||
- Fixed Shedinja not being able to recover naturally.
|
||||
- Fixed different forms of Pokémon not being able to appear as different sizes.
|
||||
- Fixed the Healing Machine soft locking you from using others when removed by non-players.
|
||||
- Fixed animations being sped up when using the Replay Mod.
|
||||
- Fixed particle animations not running when a Pokémon is off-screen.
|
||||
- Fixed Pokémon variants and layers not rendering correctly when shouldered and playing on a dedicated server, existing shoulders affected will need to be retrieved and shouldered again.
|
||||
- Shoulder effects will now stay applied through situations that remove potion effects such as drinking milk.
|
||||
- Fixed Shedinja evolving to use the consumed Pokeball and removed the held item.
|
||||
- Fixed non-existent species in spawn pool file causing random species to spawn.
|
||||
- Fixed shoulder effects not staying applied through situations that remove potion effects such as drinking milk.
|
||||
- Fixed Shedinja not being able to recover naturally.
|
||||
- Fixed Shedinja evolving to use the consumed Poké Ball and removed the held item to prevent dupes.
|
||||
- Fixed Shedinja healing above 1 HP.
|
||||
- Fixed Shedinja, basically.
|
||||
- Fixed shearing Pokémon dropping 0-2 wool instead of 1-3.
|
||||
- Fixed some alignment issues in the stat hexagon of the summary menu.
|
||||
- Fixed some alignment issues in the stat hexagon of the summary menu. OCD people rejoice.
|
||||
- Fixed capture calculations not applying ball bonuses entirely correctly.
|
||||
- Fixed battles soft-locking when consecutive Pokémon faint on switch-in.
|
||||
- Fixed timing and color of battle window messages.
|
||||
- Fixed players being able to trade, battle and let out their Pokémon while in spectator mode.
|
||||
- Fixed Galarian Yamask not being able to evolve and by proxy the ``damage_taken`` evolution requirement.
|
||||
- Fixed Bisharp not being able to evolve and by proxy the ``defeat`` evolution requirement.
|
||||
- Fixed White-Striped Basculin not being able to evolve because of a broken ``recoil`` evolution requirement.
|
||||
- Fixed Primeape, Qwilfish and Stantler not being able to evolve because of a broken ``use_move`` evolution requirement.
|
||||
- Fixed Bramblin, Pawmo, and Rellor not being able to evolve because of a broken ``blocks_traveled`` evolution requirement.
|
||||
- Fixed displayName property in spawn files not doing what it's meant to do.
|
||||
- Fixed Pokémon not sleeping in the wild like we wanted them to.
|
||||
|
||||
### Developer
|
||||
- Added SpawnEvent
|
||||
- Added persistent NBT property inside Pokemon to store quick and simple data.
|
||||
- Added SpawnEvent, ThrownPokeballHitEvent, PokemonSentEvent, PokemonRecalledEvent.
|
||||
- Added BattleFledEvent, BattleStartedEvent, BattleFaintedEvent.
|
||||
- Added persistent NBT property inside Pokémon to store quick and simple data.
|
||||
- Species and FormData have had their evolutions, pre-evolution and labels properties exposed. It is still recommended to work using a Pokémon instance when possible.
|
||||
- Added capture check to BattleVictoryEvent
|
||||
- Added ThownPokeballHitEvent
|
||||
- Added capture check to BattleVictoryEvent.
|
||||
- The various hardcoded potion shoulder effects have been removed, make use of PotionBaseEffect.
|
||||
- Added ContextManager for tracking causes and contexts of conditions created during a battle. See BattleContext for types of conditions that are tracked.
|
||||
- Added MongoDB support for storing Pokémon and Player data. Must be enabled in config, requires MongoDB core and sync drivers (4.10.0+).
|
||||
- CobblemonShowdown updated to version 10.
|
||||
- Generation of a battle can be set in BattleFormat.
|
||||
- Pokémon now have ``teraType``, ``dmaxLevel``, and ``gmaxFactor`` properties. Gimmicks can be used during battle by adding the respective identifiers to ``keyItems`` in PlayerData: ``key_stone``, ``z_ring``, ``dynamax_band``, and ``tera_orb``. Dynamax is only supported in Gen 8 battles. Mega Evolution and Z-Power require custom held items to be added (e.g. an item with the path ``gengarite`` will allow Gengar to Mega Evolve). Currently custom Z-Crystals and Mega Stones are not supported.
|
||||
|
||||
### Datapack & Resourcepack Creators
|
||||
- All potion related shoulder effects have had their IDs changed, they now all share the same type being `potion_effect` and use the vanilla Potion data [parameters](https://minecraft.fandom.com/wiki/Potion#Item_data), take for example the default Pidgey asset:
|
||||
- All potion related shoulder effects have had their IDs changed. They now all share the same type being `potion_effect` and use the vanilla Potion data [parameters](https://minecraft.fandom.com/wiki/Potion#Item_data). For example, here is the converted Pidgey asset:
|
||||
- ```json
|
||||
{
|
||||
"type": "potion_effect",
|
||||
@@ -252,6 +459,23 @@
|
||||
"showIcon": false
|
||||
}
|
||||
```
|
||||
- Renamed the ``walked_steps`` evolution requirement to ``blocks_traveled``.
|
||||
- Added support for scale in animations.
|
||||
- Added support for jump keyframes (i.e. pre and post keyframes)
|
||||
- Added structure spawning conditions
|
||||
- Added Advancement trigger for defeating Pokémon and collecting varieties of Pokémon.
|
||||
- Added support for "isBattle" and "isTouchingWater" properties on resource pack Pokémon poses. This allows your custom Pokémon to be posed differently when in battle.
|
||||
- Added support for "isVisible" on a transformed part on resource pack Pokémon poses. This allows your custom Pokémon to have bones disappear in specific poses, such as hiding Greninja's throwing star when not in a battle pose.
|
||||
- Added support for battle music. Sounds can be added to the ``battle.pvp.default`` and ``battle.pvw.default`` sound events.
|
||||
- Added 'enabled' optional property on model layers, allowing later variations to disable previously-defined layers. See [this issue](https://gitlab.com/cable-mc/cobblemon/-/issues/335) for how this looks.
|
||||
- Cobblemon items can now all have their own tooltips via resourcepacks. To add a tooltip, add a lang entry like "item.cobblemon.{item_id}.tooltip". If you want to add multiple tooltip lines you can do so with "item.cobblemon.{item_id}.tooltip_1" and upwards.
|
||||
- Item interaction evolutions and held item requirements now support NBT by creating an object JSON containing the key ``item`` for what used to be the existing condition support and a ``nbt`` key for the NBT format, this is the string [format](https://minecraft.fandom.com/wiki/NBT_format) expected in commands. Existing data does not need to be updated.
|
||||
- Fixed faint animations not working properly in add-ons.
|
||||
- Fixed non-existent species in spawn pool files causing random species to spawn.
|
||||
|
||||
### Localization
|
||||
- Added partial translations for Dutch, Polish, Swedish, Hungarian, Czech, Cyprus Greek, and even Esperanto.
|
||||
- Updated translation for French and Canadian French, Simplified Chinese, Japanese, Korean, Spanish and Mexican Spanish, Pirate English, German, Thai, Turkish, Portuguese and Brazilian Portuguese, Ukrainian, and Russian.
|
||||
|
||||
## [1.3.1 (March 31st, 2023)](#1-3-1)
|
||||
|
||||
@@ -265,7 +489,7 @@
|
||||
- Add lava surface spawn preset.
|
||||
- Added an ``any`` evolution requirement allowing you to define ``possibilities`` of other evolution requirements, for example, this allows you to create an evolution that requires the Pokémon to be shiny or a female.
|
||||
- Added the `/spawnpokemonfrompool [amount]` or `/forcespawn [amount]` command to spawn Pokémon(s) in the surrounding area using the natural spawn rates/pool of that area, this will be a cheat command in the Minecraft permission system or use the permission `cobblemon.command.spawnpokemon` if a permission mod is present. On a successful execution of the command, the amount of Pokémon spawned will be the output.
|
||||
- Added the `/pokebox` and `/pokeboxall` commands to move Pokemon(s) to the PC from a Player's party, this will be a cheat command in the Minecraft permission system or use the permission `cobblemon.command.pokebox` if a permission mod is present. On a successful execution of the command the output will be the number of pokemon moved to the Player's PC.
|
||||
- Added the `/pokebox` and `/pokeboxall` commands to move Pokémon(s) to the PC from a Player's party, this will be a cheat command in the Minecraft permission system or use the permission `cobblemon.command.pokebox` if a permission mod is present. On a successful execution of the command the output will be the number of pokemon moved to the Player's PC.
|
||||
- Added the `/pc` command which opens up the PC UI the same way interacting with the block would, this will be a cheat command in the Minecraft permission system or use the permission `cobblemon.command.pc` if a permission mod is present.
|
||||
|
||||
### Changes
|
||||
@@ -288,7 +512,6 @@
|
||||
- Changed the recipes for Mystic Water, Miracle Seed, and Charcoal Stick to utilise the evolution stones, as well as Never-Melt Ice having an alternate recipe using the Ice Stone.
|
||||
- Replaced the `Failed to handle` battle messages to `Missing interpretation` to make it more clear that mechanics do work just still pending dedicated messages.
|
||||
- Healing Machine and PC are now mine-able with pickaxes and Apricorn leaves are mine-able using hoes.
|
||||
- Updated Pokéball, UI and evolution sounds.
|
||||
|
||||
### Fixes
|
||||
- Fixed killing a Dodrio killing your game. Dodrio will never look the same to us.
|
||||
|
||||
@@ -25,6 +25,7 @@ repositories {
|
||||
mavenCentral()
|
||||
//JEI
|
||||
maven("https://maven.blamejared.com/")
|
||||
maven("https://maven.tterrag.com/")
|
||||
}
|
||||
|
||||
license {
|
||||
|
||||
@@ -19,6 +19,7 @@ architectury {
|
||||
|
||||
repositories {
|
||||
maven(url = "${rootProject.projectDir}/deps")
|
||||
maven(url = "https://api.modrinth.com/maven")
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
@@ -27,14 +28,20 @@ dependencies {
|
||||
implementation(libs.reflect)
|
||||
|
||||
modImplementation(libs.fabricLoader)
|
||||
//Flywheel has no common dep so just pick one and don't use any platform specific code in common
|
||||
// modCompileOnly(libs.flywheelFabric)
|
||||
modApi(libs.molang)
|
||||
compileOnlyApi(libs.jeiApi)
|
||||
modCompileOnly(libs.adornFabric)
|
||||
|
||||
// For Showdown
|
||||
modCompileOnly(libs.graal)
|
||||
|
||||
//shadowCommon group: 'commons-io', name: 'commons-io', version: '2.6'
|
||||
|
||||
// For datastore
|
||||
modCompileOnly(libs.mongoDriverCore)
|
||||
modCompileOnly(libs.mongoDriverSync)
|
||||
|
||||
testRuntimeOnly(libs.junitEngine)
|
||||
testImplementation(libs.junitApi)
|
||||
|
||||
+5
-3
@@ -8,6 +8,8 @@
|
||||
|
||||
package com.cobblemon.mod.common.client.render.models.blockbench.pose;
|
||||
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityModel;
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext;
|
||||
import net.minecraft.client.render.VertexConsumer;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.entity.Entity;
|
||||
@@ -38,7 +40,7 @@ public interface Bone {
|
||||
/**
|
||||
* Renders a particular bone in relation to a particular entity.
|
||||
*
|
||||
* @param entity The entity this bone is being drawn for. This parameter need not be available and can be null
|
||||
* @param context The render context this bone is being drawn for.
|
||||
* @param stack The current [MatrixStack] for the renderer
|
||||
* @param buffer The vertex consumer deployed by the renderer
|
||||
* @param packedLight The light level to be applied to the bone
|
||||
@@ -50,8 +52,8 @@ public interface Bone {
|
||||
*
|
||||
* @since 1.4.0
|
||||
*/
|
||||
<T extends Entity> void render(
|
||||
@Nullable T entity,
|
||||
void render(
|
||||
RenderContext context,
|
||||
MatrixStack stack,
|
||||
VertexConsumer buffer,
|
||||
int packedLight,
|
||||
|
||||
-25
@@ -1,25 +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.cobblemonstructures;
|
||||
|
||||
public class CobblemonStructureIDs {
|
||||
//Location of the nbt
|
||||
//Not sure if this is a good way to store them but doing this meant I wouldnt have to keep typing this out during development
|
||||
public static final String PLAINS_POKECENTER = "cobblemon:village_plains/village_plains_pokecenter";
|
||||
public static final String DESERT_POKECENTER = "cobblemon:village_desert/village_desert_pokecenter";
|
||||
public static final String SAVANNA_POKECENTER = "cobblemon:village_savanna/village_savanna_pokecenter";
|
||||
public static final String SNOWY_POKECENTER = "cobblemon:village_snowy/village_snowy_pokecenter";
|
||||
public static final String TAIGA_POKECENTER = "cobblemon:village_taiga/village_taiga_pokecenter";
|
||||
|
||||
public static final String PLAINS_LONG_PATH = "cobblemon:village_plains/village_plains_long_path";
|
||||
public static final String DESERT_LONG_PATH = "cobblemon:village_desert/village_desert_long_path";
|
||||
public static final String SAVANNA_LONG_PATH = "cobblemon:village_savanna/village_savanna_long_path";
|
||||
public static final String SNOWY_LONG_PATH = "cobblemon:village_snowy/village_snowy_long_path";
|
||||
public static final String TAIGA_LONG_PATH = "cobblemon:village_taiga/village_taiga_long_path";
|
||||
}
|
||||
-101
@@ -1,101 +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.cobblemonstructures;
|
||||
|
||||
import com.cobblemon.mod.common.mixin.StructurePoolAccessor;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.registry.Registry;
|
||||
import net.minecraft.registry.RegistryKey;
|
||||
import net.minecraft.registry.RegistryKeys;
|
||||
import net.minecraft.registry.entry.RegistryEntry;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.structure.pool.LegacySinglePoolElement;
|
||||
import net.minecraft.structure.pool.SinglePoolElement;
|
||||
import net.minecraft.structure.pool.StructurePool;
|
||||
import net.minecraft.structure.pool.StructurePoolElement;
|
||||
import net.minecraft.structure.processor.StructureProcessorList;
|
||||
import net.minecraft.structure.processor.StructureProcessorLists;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CobblemonStructures {
|
||||
|
||||
private static final RegistryKey<StructureProcessorList> EMPTY_PROCESSOR_LIST_KEY = RegistryKey.of(RegistryKeys.PROCESSOR_LIST, new Identifier("minecraft", "empty"));
|
||||
private static final Integer pokecenterWeight = 35;
|
||||
private static final Integer longPathWeight = 10;
|
||||
|
||||
public static void registerJigsaws(MinecraftServer server) {
|
||||
Registry<StructurePool> templatePoolRegistry = server.getRegistryManager().get(RegistryKeys.TEMPLATE_POOL);
|
||||
Registry<StructureProcessorList> processorListRegistry = server.getRegistryManager().get(RegistryKeys.PROCESSOR_LIST);
|
||||
|
||||
//No pokecenters yet
|
||||
//addPokecenters(templatePoolRegistry, processorListRegistry);
|
||||
//addLongPaths(templatePoolRegistry, processorListRegistry);
|
||||
}
|
||||
|
||||
private static void addPokecenters(Registry<StructurePool> templatePoolRegistry, Registry<StructureProcessorList> processorListRegistry) {
|
||||
Identifier plainsHousesPoolLocation = new Identifier("minecraft:village/plains/houses");
|
||||
Identifier desertHousesPoolLocation = new Identifier("minecraft:village/desert/houses");
|
||||
Identifier savannaHousesPoolLocation = new Identifier("minecraft:village/savanna/houses");
|
||||
Identifier snowyHousesPoolLocation = new Identifier("minecraft:village/snowy/houses");
|
||||
Identifier taigaHousesPoolLocation = new Identifier("minecraft:village/taiga/houses");
|
||||
|
||||
addBuildingToPool(templatePoolRegistry, processorListRegistry, plainsHousesPoolLocation, CobblemonStructureIDs.PLAINS_POKECENTER, pokecenterWeight, StructurePool.Projection.RIGID, EMPTY_PROCESSOR_LIST_KEY);
|
||||
addBuildingToPool(templatePoolRegistry, processorListRegistry, desertHousesPoolLocation, CobblemonStructureIDs.DESERT_POKECENTER, pokecenterWeight, StructurePool.Projection.RIGID, EMPTY_PROCESSOR_LIST_KEY);
|
||||
addBuildingToPool(templatePoolRegistry, processorListRegistry, savannaHousesPoolLocation, CobblemonStructureIDs.SAVANNA_POKECENTER, pokecenterWeight, StructurePool.Projection.RIGID, EMPTY_PROCESSOR_LIST_KEY);
|
||||
addBuildingToPool(templatePoolRegistry, processorListRegistry, snowyHousesPoolLocation, CobblemonStructureIDs.SNOWY_POKECENTER, pokecenterWeight, StructurePool.Projection.RIGID, EMPTY_PROCESSOR_LIST_KEY);
|
||||
addBuildingToPool(templatePoolRegistry, processorListRegistry, taigaHousesPoolLocation, CobblemonStructureIDs.TAIGA_POKECENTER, pokecenterWeight, StructurePool.Projection.RIGID, StructureProcessorLists.MOSSIFY_10_PERCENT);
|
||||
}
|
||||
private static void addLongPaths(Registry<StructurePool> templatePoolRegistry, Registry<StructureProcessorList> processorListRegistry) {
|
||||
Identifier plainsStreetsPoolLocation = new Identifier("minecraft:village/plains/streets");
|
||||
Identifier desertStreetsPoolLocation = new Identifier("minecraft:village/desert/streets");
|
||||
Identifier savannaStreetsPoolLocation = new Identifier("minecraft:village/savanna/streets");
|
||||
Identifier snowyStreetsPoolLocation = new Identifier("minecraft:village/snowy/streets");
|
||||
Identifier taigaStreetsPoolLocation = new Identifier("minecraft:village/taiga/streets");
|
||||
|
||||
addLegacyBuildingToPool(templatePoolRegistry, processorListRegistry, plainsStreetsPoolLocation, CobblemonStructureIDs.PLAINS_LONG_PATH, longPathWeight, StructurePool.Projection.TERRAIN_MATCHING, StructureProcessorLists.STREET_PLAINS);
|
||||
addLegacyBuildingToPool(templatePoolRegistry, processorListRegistry, desertStreetsPoolLocation, CobblemonStructureIDs.DESERT_LONG_PATH, longPathWeight, StructurePool.Projection.TERRAIN_MATCHING, EMPTY_PROCESSOR_LIST_KEY);
|
||||
addLegacyBuildingToPool(templatePoolRegistry, processorListRegistry, savannaStreetsPoolLocation, CobblemonStructureIDs.SAVANNA_LONG_PATH, longPathWeight, StructurePool.Projection.TERRAIN_MATCHING, StructureProcessorLists.STREET_SAVANNA);
|
||||
addLegacyBuildingToPool(templatePoolRegistry, processorListRegistry, snowyStreetsPoolLocation, CobblemonStructureIDs.SNOWY_LONG_PATH, longPathWeight, StructurePool.Projection.TERRAIN_MATCHING, StructureProcessorLists.STREET_SNOWY_OR_TAIGA);
|
||||
addLegacyBuildingToPool(templatePoolRegistry, processorListRegistry, taigaStreetsPoolLocation, CobblemonStructureIDs.TAIGA_LONG_PATH, longPathWeight, StructurePool.Projection.TERRAIN_MATCHING, StructureProcessorLists.STREET_SNOWY_OR_TAIGA);
|
||||
}
|
||||
|
||||
public static void addLegacyBuildingToPool(Registry<StructurePool> templatePoolRegistry, Registry<StructureProcessorList> processorListRegistry, Identifier poolRL, String nbtPieceRL, int weight, StructurePool.Projection projection, RegistryKey<StructureProcessorList> processorListKey) {
|
||||
addBuildingToPool(templatePoolRegistry, processorListRegistry, poolRL, nbtPieceRL, weight, projection, processorListKey, true);
|
||||
}
|
||||
|
||||
public static void addBuildingToPool(Registry<StructurePool> templatePoolRegistry, Registry<StructureProcessorList> processorListRegistry, Identifier poolRL, String nbtPieceRL, int weight, StructurePool.Projection projection, RegistryKey<StructureProcessorList> processorListKey) {
|
||||
addBuildingToPool(templatePoolRegistry, processorListRegistry, poolRL, nbtPieceRL, weight, projection, processorListKey, false);
|
||||
}
|
||||
|
||||
public static void addBuildingToPool(Registry<StructurePool> templatePoolRegistry, Registry<StructureProcessorList> processorListRegistry, Identifier poolRL, String nbtPieceRL, int weight, StructurePool.Projection projection, RegistryKey<StructureProcessorList> processorListKey, boolean shouldUseLegacySingePoolElement) {
|
||||
if (processorListRegistry.getEntry(processorListKey).isEmpty()) return;
|
||||
RegistryEntry.Reference<StructureProcessorList> processorList = processorListRegistry.getEntry(processorListKey).get();
|
||||
|
||||
StructurePool pool = templatePoolRegistry.get(poolRL);
|
||||
if (pool == null) return;
|
||||
|
||||
SinglePoolElement piece;
|
||||
if (shouldUseLegacySingePoolElement) {
|
||||
piece = LegacySinglePoolElement.ofProcessedLegacySingle(nbtPieceRL, processorList).apply(projection);
|
||||
} else {
|
||||
piece = SinglePoolElement.ofProcessedSingle(nbtPieceRL, processorList).apply(projection);
|
||||
}
|
||||
|
||||
for (int i = 0; i < weight; i++) {
|
||||
((StructurePoolAccessor) pool).getElements().add(piece);
|
||||
}
|
||||
|
||||
List<Pair<StructurePoolElement, Integer>> listOfPieceEntries = new ArrayList<>(((StructurePoolAccessor) pool).getElementCounts());
|
||||
listOfPieceEntries.add(new Pair<>(piece, weight));
|
||||
((StructurePoolAccessor) pool).setElementCounts(listOfPieceEntries);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.block.PreEmptsExplosion;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.explosion.Explosion;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(value = Explosion.class)
|
||||
public class ExplosionMixin {
|
||||
|
||||
@Redirect(
|
||||
method = "affectWorld",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/World;getBlockState(Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/block/BlockState;",
|
||||
ordinal = 0
|
||||
)
|
||||
)
|
||||
public BlockState cobblemon$whenExploded(World world, BlockPos pos) {
|
||||
BlockState blockState = world.getBlockState(pos);
|
||||
if(blockState.getBlock() instanceof PreEmptsExplosion preExplosionBlock) {
|
||||
preExplosionBlock.whenExploded(world, blockState, pos);
|
||||
}
|
||||
return blockState;
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlocks;
|
||||
import com.cobblemon.mod.common.block.PastureBlock;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.FenceBlock;
|
||||
import net.minecraft.block.HorizontalFacingBlock;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(value = FenceBlock.class)
|
||||
public abstract class FenceBlockMixin {
|
||||
|
||||
// This could be done with a modify variable but it just doesn't seem to want to work, very crude unfinished explanation I had on Fabric discord was
|
||||
// "boolean is a primitive, it isnt passed by reference but by value and you have your own object references anyway so assigning a new object to non-primitive references (variables) wont modify it after your mixin returns
|
||||
//(you cant modify a random argument that mixin passes you that way, target the boolean not the FenceBlock)"
|
||||
// However the issue is the only way to get all the params which we need is by passing in the entire method reference which enforces the return type.
|
||||
|
||||
@Redirect(method = "getStateForNeighborUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/FenceBlock;canConnect(Lnet/minecraft/block/BlockState;ZLnet/minecraft/util/math/Direction;)Z"))
|
||||
private boolean cobblemon$getStateForNeighborUpdate(FenceBlock instance, BlockState state, boolean neighborIsFullSquare, Direction dir) {
|
||||
return instance.canConnect(state, neighborIsFullSquare, dir) || this.cobblemon$canConnect(state, dir);
|
||||
}
|
||||
|
||||
@Redirect(method = "getPlacementState", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/FenceBlock;canConnect(Lnet/minecraft/block/BlockState;ZLnet/minecraft/util/math/Direction;)Z"))
|
||||
private boolean cobblemon$getPlacementState(FenceBlock instance, BlockState state, boolean neighborIsFullSquare, Direction dir) {
|
||||
return instance.canConnect(state, neighborIsFullSquare, dir) || this.cobblemon$canConnect(state, dir);
|
||||
}
|
||||
|
||||
private boolean cobblemon$canConnect(BlockState state, Direction dir) {
|
||||
if (state.isOf(CobblemonBlocks.PASTURE) && state.get(PastureBlock.Companion.getPART()) == PastureBlock.PasturePart.BOTTOM) {
|
||||
final Direction pastureFacing = state.get(HorizontalFacingBlock.FACING);
|
||||
return dir == pastureFacing.rotateYClockwise() || dir == pastureFacing.rotateYCounterclockwise();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,7 +8,9 @@
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityModel;
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Bone;
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext;
|
||||
import net.minecraft.client.model.ModelPart;
|
||||
import net.minecraft.client.render.VertexConsumer;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
@@ -36,7 +38,7 @@ public abstract class ModelPartMixin implements Bone {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Entity> void render(T entity, MatrixStack stack, VertexConsumer buffer, int packedLight, int packedOverlay, float r, float g, float b, float a) {
|
||||
public void render(RenderContext context, MatrixStack stack, VertexConsumer buffer, int packedLight, int packedOverlay, float r, float g, float b, float a) {
|
||||
this.render(stack, buffer, packedLight, packedOverlay, r, g, b, a);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.mixins.CachedOnlyChunkAccessor;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import net.minecraft.server.world.ChunkHolder;
|
||||
import net.minecraft.server.world.ServerChunkManager;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraft.world.chunk.ChunkManager;
|
||||
import net.minecraft.world.chunk.ChunkStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Mixin(ServerChunkManager.class)
|
||||
public abstract class ServerChunkManagerMixin extends ChunkManager implements CachedOnlyChunkAccessor {
|
||||
@Shadow @Nullable protected abstract ChunkHolder getChunkHolder(long pos);
|
||||
|
||||
@Override
|
||||
public Chunk cobblemon$request(int x, int z, @NotNull ChunkStatus status) {
|
||||
long l = ChunkPos.toLong(x, z);
|
||||
|
||||
ChunkHolder holder = getChunkHolder(l);
|
||||
if (holder != null) {
|
||||
CompletableFuture<Either<Chunk, ChunkHolder.Unloaded>> future = holder.getFutureFor(status);
|
||||
if (future.isDone()) {
|
||||
return future.join().left().orElse(null);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+116
-98
@@ -8,11 +8,12 @@
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.cobblemonstructures.CobblemonStructureIDs;
|
||||
import com.cobblemon.mod.common.world.CobblemonStructureIDs;
|
||||
import net.minecraft.registry.Registry;
|
||||
import net.minecraft.structure.PoolStructurePiece;
|
||||
import net.minecraft.structure.StructureTemplateManager;
|
||||
import net.minecraft.structure.pool.*;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.world.HeightLimitView;
|
||||
@@ -29,7 +30,6 @@ import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Mixin(StructurePoolBasedGenerator.StructurePoolGenerator.class)
|
||||
public abstract class StructurePoolGeneratorMixin {
|
||||
@@ -37,17 +37,35 @@ public abstract class StructurePoolGeneratorMixin {
|
||||
@Mutable
|
||||
@Shadow Deque<StructurePoolBasedGenerator.ShapedPoolStructurePiece> structurePieces;
|
||||
|
||||
Map<String, Integer> generatedStructureCounts;
|
||||
Map<String, Integer> generatedStructureGroupCounts;
|
||||
|
||||
private static final Map<String, Integer> structureMaxes;
|
||||
private static final Map<Identifier, Set<String>> structureGroups = new HashMap<>();
|
||||
static {
|
||||
String pokecenter = "pokecenter";
|
||||
String berry = "berry_farm";
|
||||
|
||||
structureGroups.put(CobblemonStructureIDs.PLAINS_POKECENTER, Set.of(pokecenter));
|
||||
structureGroups.put(CobblemonStructureIDs.DESERT_POKECENTER, Set.of(pokecenter));
|
||||
structureGroups.put(CobblemonStructureIDs.SAVANNA_POKECENTER, Set.of(pokecenter));
|
||||
structureGroups.put(CobblemonStructureIDs.SNOWY_POKECENTER, Set.of(pokecenter));
|
||||
structureGroups.put(CobblemonStructureIDs.TAIGA_POKECENTER, Set.of(pokecenter));
|
||||
|
||||
structureGroups.put(CobblemonStructureIDs.SAVANNA_BERRY_SMALL, Set.of(berry));
|
||||
structureGroups.put(CobblemonStructureIDs.SAVANNA_BERRY_LARGE, Set.of(berry));
|
||||
structureGroups.put(CobblemonStructureIDs.PLAINS_BERRY_SMALL, Set.of(berry));
|
||||
structureGroups.put(CobblemonStructureIDs.PLAINS_BERRY_LARGE, Set.of(berry));
|
||||
structureGroups.put(CobblemonStructureIDs.SNOWY_BERRY_SMALL, Set.of(berry));
|
||||
structureGroups.put(CobblemonStructureIDs.SNOWY_BERRY_LARGE, Set.of(berry));
|
||||
structureGroups.put(CobblemonStructureIDs.DESERT_BERRY_SMALL, Set.of(berry));
|
||||
structureGroups.put(CobblemonStructureIDs.DESERT_BERRY_LARGE, Set.of(berry));
|
||||
structureGroups.put(CobblemonStructureIDs.TAIGA_BERRY_SMALL, Set.of(berry));
|
||||
structureGroups.put(CobblemonStructureIDs.TAIGA_BERRY_LARGE, Set.of(berry));
|
||||
|
||||
//Mapped using location string as key
|
||||
Map<String, Integer> aMap = new HashMap<>();
|
||||
aMap.put(CobblemonStructureIDs.PLAINS_POKECENTER, 1);
|
||||
aMap.put(CobblemonStructureIDs.DESERT_POKECENTER, 1);
|
||||
aMap.put(CobblemonStructureIDs.SAVANNA_POKECENTER, 1);
|
||||
aMap.put(CobblemonStructureIDs.SNOWY_POKECENTER, 1);
|
||||
aMap.put(CobblemonStructureIDs.TAIGA_POKECENTER, 1);
|
||||
aMap.put(pokecenter, 1);
|
||||
aMap.put(berry, 2);
|
||||
|
||||
// aMap.put(CobblemonStructureIDs.PLAINS_LONG_PATH, 3);
|
||||
// aMap.put(CobblemonStructureIDs.DESERT_LONG_PATH, 3);
|
||||
@@ -57,143 +75,143 @@ public abstract class StructurePoolGeneratorMixin {
|
||||
structureMaxes = Collections.unmodifiableMap(aMap);
|
||||
}
|
||||
|
||||
public Set<String> getGroups(Identifier structureIdentifier) {
|
||||
return structureGroups.getOrDefault(structureIdentifier, Set.of());
|
||||
}
|
||||
|
||||
public boolean hasReachedMaximum(Identifier structureIdentifier) {
|
||||
Set<String> groups = getGroups(structureIdentifier);
|
||||
for (String group : groups) {
|
||||
int count = generatedStructureGroupCounts.getOrDefault(group, 0);
|
||||
if (count >= structureMaxes.getOrDefault(group, Integer.MAX_VALUE)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void incrementStructureCount(Identifier structureIdentifier) {
|
||||
Set<String> groups = getGroups(structureIdentifier);
|
||||
for (String group : groups) {
|
||||
generatedStructureGroupCounts.put(group, generatedStructureGroupCounts.getOrDefault(group, 0) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void onStructurePoolGeneratorCreation(Registry<StructurePool> registry, int maxSize, ChunkGenerator chunkGenerator, StructureTemplateManager structureTemplateManager, List<? super PoolStructurePiece> children, Random random, CallbackInfo ci) {
|
||||
generatedStructureCounts = new HashMap<>();
|
||||
generatedStructureGroupCounts = new HashMap<>();
|
||||
}
|
||||
|
||||
@ModifyVariable(method = "generatePiece", at = @At("STORE"), ordinal = 1)
|
||||
private Iterator<StructurePoolElement> reduceStructurePoolELementIterator(Iterator<StructurePoolElement> iterator) {
|
||||
private Iterator<StructurePoolElement> reduceStructurePoolElementIterator(Iterator<StructurePoolElement> iterator) {
|
||||
List<StructurePoolElement> reducedList = new ArrayList<>();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
StructurePoolElement structure = iterator.next();
|
||||
String structurePieceLocationKey = getCobblemonOnlyLocation(structure);
|
||||
Identifier structurePieceLocationKey = getCobblemonOnlyLocation(structure);
|
||||
if (structurePieceLocationKey == null) {
|
||||
reducedList.add(structure);
|
||||
continue;
|
||||
}
|
||||
|
||||
Integer currentlyPlacedCount = generatedStructureCounts.get(structurePieceLocationKey);
|
||||
if (currentlyPlacedCount == null) currentlyPlacedCount = 0;
|
||||
|
||||
Integer maxAllowed = structureMaxes.get(structurePieceLocationKey);
|
||||
if (maxAllowed == null) {
|
||||
reducedList.add(structure);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (currentlyPlacedCount < maxAllowed) {
|
||||
if (!hasReachedMaximum(structurePieceLocationKey)) {
|
||||
reducedList.add(structure);
|
||||
}
|
||||
}
|
||||
|
||||
for (String maxStructureLocationKey : structureMaxes.keySet()) {
|
||||
Integer maxAllowed = structureMaxes.get(maxStructureLocationKey);
|
||||
|
||||
Integer currentlyGenerated = generatedStructureCounts.get(maxStructureLocationKey);
|
||||
if (currentlyGenerated == null) currentlyGenerated = 0;
|
||||
|
||||
Integer allowedInstances = maxAllowed - currentlyGenerated;
|
||||
reducedList = removeInstanceOfLocationKeyFrom(reducedList, allowedInstances, maxStructureLocationKey);
|
||||
}
|
||||
|
||||
return reducedList.iterator();
|
||||
}
|
||||
|
||||
@ModifyVariable(method = "generatePiece", at = @At("STORE"), ordinal = 1)
|
||||
private PoolStructurePiece injected(PoolStructurePiece poolStructurePiece) {
|
||||
String structureLocationKey = getCobblemonOnlyLocation(poolStructurePiece.getPoolElement());
|
||||
Identifier structureLocationKey = getCobblemonOnlyLocation(poolStructurePiece.getPoolElement());
|
||||
if (structureLocationKey != null) {
|
||||
Integer currentlyGenerated = generatedStructureCounts.get(structureLocationKey);
|
||||
if (currentlyGenerated == null) currentlyGenerated = 0;
|
||||
generatedStructureCounts.put(structureLocationKey, currentlyGenerated + 1);
|
||||
incrementStructureCount(structureLocationKey);
|
||||
}
|
||||
|
||||
return poolStructurePiece;
|
||||
}
|
||||
|
||||
// This doesn't SEEM necessary? It appears to work without.
|
||||
@Inject(method = "generatePiece", at = @At("HEAD"))
|
||||
private void beforeGeneratePiece(PoolStructurePiece piece, MutableObject<VoxelShape> pieceShape, int minY, boolean modifyBoundingBox, HeightLimitView world, NoiseConfig noiseConfig, CallbackInfo ci) {
|
||||
String structureLocationKey = getCobblemonOnlyLocation(piece.getPoolElement());
|
||||
|
||||
if (structureLocationKey != null && structureLocationKey.equals("cobblemon:village_snowy/village_snowy_pokecenter")) System.out.println("Before Counts" + generatedStructureCounts);
|
||||
|
||||
if (structureLocationKey != null) {
|
||||
Integer currentlyGenerated = generatedStructureCounts.get(structureLocationKey);
|
||||
if (currentlyGenerated == null) currentlyGenerated = 0;
|
||||
generatedStructureCounts.put(structureLocationKey, currentlyGenerated + 1);
|
||||
}
|
||||
|
||||
List<StructurePoolBasedGenerator.ShapedPoolStructurePiece> reducedStructurePiecesList = structurePieces.stream().toList();
|
||||
|
||||
for (String maxStructureLocationKey : structureMaxes.keySet()) {
|
||||
Integer maxAllowed = structureMaxes.get(maxStructureLocationKey);
|
||||
|
||||
Integer currentlyGenerated = generatedStructureCounts.get(maxStructureLocationKey);
|
||||
if (currentlyGenerated == null) currentlyGenerated = 0;
|
||||
if (currentlyGenerated < maxAllowed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//Already have max so need to remove
|
||||
reducedStructurePiecesList = reducedStructurePiecesList.stream()
|
||||
.filter(shapedStructurePiece -> {
|
||||
String locationKey = getCobblemonOnlyLocation(shapedStructurePiece.piece.getPoolElement());
|
||||
if (locationKey == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !locationKey.equals(maxStructureLocationKey);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
structurePieces = new ArrayDeque<>(reducedStructurePiecesList);
|
||||
// Identifier structureLocationKey = getCobblemonOnlyLocation(piece.getPoolElement());
|
||||
//
|
||||
// if (structureLocationKey != null) {
|
||||
// Integer currentlyGenerated = generatedStructureCounts.get(structureLocationKey);
|
||||
// if (currentlyGenerated == null) currentlyGenerated = 0;
|
||||
// generatedStructureCounts.put(structureLocationKey, currentlyGenerated + 1);
|
||||
// }
|
||||
//
|
||||
// List<StructurePoolBasedGenerator.ShapedPoolStructurePiece> reducedStructurePiecesList = structurePieces.stream().toList();
|
||||
//
|
||||
// for (Identifier maxStructureLocationKey : structureMaxes.keySet()) {
|
||||
// Integer maxAllowed = structureMaxes.get(maxStructureLocationKey);
|
||||
//
|
||||
// Integer currentlyGenerated = generatedStructureCounts.get(maxStructureLocationKey);
|
||||
// if (currentlyGenerated == null) currentlyGenerated = 0;
|
||||
// if (currentlyGenerated < maxAllowed) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// //Already have max so need to remove
|
||||
// reducedStructurePiecesList = reducedStructurePiecesList.stream()
|
||||
// .filter(shapedStructurePiece -> {
|
||||
// Identifier locationKey = getCobblemonOnlyLocation(shapedStructurePiece.piece.getPoolElement());
|
||||
// if (locationKey == null) {
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// return !locationKey.equals(maxStructureLocationKey);
|
||||
// })
|
||||
// .collect(Collectors.toList());
|
||||
// }
|
||||
//
|
||||
// structurePieces = new ArrayDeque<>(reducedStructurePiecesList);
|
||||
}
|
||||
|
||||
private static String getCobblemonOnlyLocation(StructurePoolElement structurePoolElement) {
|
||||
String location = getLocationIfAvailable(structurePoolElement);
|
||||
private static Identifier getCobblemonOnlyLocation(StructurePoolElement structurePoolElement) {
|
||||
Identifier location = getLocationIfAvailable(structurePoolElement);
|
||||
if (location == null) return null;
|
||||
|
||||
if (!location.contains("cobblemon")) return null;
|
||||
if (!location.getNamespace().equals("cobblemon")) return null;
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
private static String getLocationIfAvailable(StructurePoolElement structurePoolElement) {
|
||||
private static Identifier getLocationIfAvailable(StructurePoolElement structurePoolElement) {
|
||||
if (structurePoolElement instanceof LegacySinglePoolElement legacySinglePoolElement) {
|
||||
if (legacySinglePoolElement.location.left().isEmpty()) return null;
|
||||
|
||||
return legacySinglePoolElement.location.left().get().toString();
|
||||
return legacySinglePoolElement.location.left().get();
|
||||
} else if (structurePoolElement instanceof SinglePoolElement singlePoolElement) {
|
||||
if (singlePoolElement.location.left().isEmpty()) return null;
|
||||
|
||||
return singlePoolElement.location.left().get().toString();
|
||||
return singlePoolElement.location.left().get();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private List<StructurePoolElement> removeInstanceOfLocationKeyFrom(List<StructurePoolElement> structureList, Integer allowedNumberOfInstances, String locationKey) {
|
||||
List<StructurePoolElement> reducedList = new ArrayList<>();
|
||||
int instancesFound = 0;
|
||||
|
||||
for (StructurePoolElement structurePoolElement: structureList) {
|
||||
String structureKey = getCobblemonOnlyLocation(structurePoolElement);
|
||||
if (structureKey == null || !structureKey.equals(locationKey)) {
|
||||
reducedList.add(structurePoolElement);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (instancesFound >= allowedNumberOfInstances) {
|
||||
continue;
|
||||
}
|
||||
|
||||
reducedList.add(structurePoolElement);
|
||||
instancesFound++;
|
||||
}
|
||||
|
||||
return reducedList;
|
||||
}
|
||||
// private List<StructurePoolElement> removeInstanceOfLocationKeyFrom(List<StructurePoolElement> structureList, Integer allowedNumberOfInstances, Identifier locationKey) {
|
||||
// List<StructurePoolElement> reducedList = new ArrayList<>();
|
||||
// int instancesFound = 0;
|
||||
//
|
||||
// for (StructurePoolElement structurePoolElement: structureList) {
|
||||
// Identifier structureKey = getCobblemonOnlyLocation(structurePoolElement);
|
||||
// if (structureKey == null || !structureKey.equals(locationKey)) {
|
||||
// reducedList.add(structurePoolElement);
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// if (instancesFound >= allowedNumberOfInstances) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// reducedList.add(structurePoolElement);
|
||||
// instancesFound++;
|
||||
// }
|
||||
//
|
||||
// return reducedList;
|
||||
// }
|
||||
}
|
||||
+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.mixin.entity;
|
||||
|
||||
import com.cobblemon.mod.common.brewing.BrewingRecipes;
|
||||
import net.minecraft.block.entity.BrewingStandBlockEntity;
|
||||
import net.minecraft.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(BrewingStandBlockEntity.class)
|
||||
public abstract class BrewingStandBlockEntityMixin
|
||||
{
|
||||
@Inject(
|
||||
method = "isValid",
|
||||
at = @At(value = "HEAD"),
|
||||
cancellable = true
|
||||
)
|
||||
private void cobblemon$isValid(int slot, ItemStack stack, CallbackInfoReturnable<Boolean> cir) {
|
||||
final BrewingStandBlockEntity entity = (BrewingStandBlockEntity) (Object) this;
|
||||
if (slot < 3)
|
||||
{
|
||||
if (entity.getStack(slot).isEmpty() && BrewingRecipes.brewableItems.contains(stack.getItem()))
|
||||
cir.setReturnValue(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin.invoker;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import net.minecraft.block.FireBlock;
|
||||
import net.minecraft.block.Blocks;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
@Mixin(FireBlock.class)
|
||||
public interface FireBlockInvoker {
|
||||
@Invoker("registerFlammableBlock") void registerNewFlammableBlock(Block block, int burnChance, int spreadChance);
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.advancement.CobblemonCriteria
|
||||
import com.cobblemon.mod.common.advancement.criterion.EvolvePokemonContext
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
import com.cobblemon.mod.common.api.SeasonResolver
|
||||
import com.cobblemon.mod.common.api.data.DataProvider
|
||||
@@ -18,7 +19,9 @@ import com.cobblemon.mod.common.api.drop.ItemDropEntry
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.BATTLE_VICTORY
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.DATA_SYNCHRONIZED
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.EVOLUTION_COMPLETE
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.LEVEL_UP_EVENT
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.POKEMON_CAPTURED
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.TRADE_COMPLETED
|
||||
import com.cobblemon.mod.common.api.net.serializers.PoseTypeDataSerializer
|
||||
import com.cobblemon.mod.common.api.net.serializers.StringSetDataSerializer
|
||||
import com.cobblemon.mod.common.api.net.serializers.Vec3DataSerializer
|
||||
@@ -46,13 +49,16 @@ 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.flatifle.FileStoreAdapter
|
||||
import com.cobblemon.mod.common.api.storage.adapter.flatifle.JSONStoreAdapter
|
||||
import com.cobblemon.mod.common.api.storage.adapter.flatifle.NBTStoreAdapter
|
||||
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
|
||||
import com.cobblemon.mod.common.api.storage.factory.FileBackedPokemonStoreFactory
|
||||
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.PlayerDataStoreManager
|
||||
import com.cobblemon.mod.common.api.storage.player.factory.JsonPlayerDataStoreFactory
|
||||
import com.cobblemon.mod.common.api.storage.player.factory.MongoPlayerDataStoreFactory
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonEntityTypeTags
|
||||
import com.cobblemon.mod.common.battles.BagItems
|
||||
import com.cobblemon.mod.common.battles.BattleFormat
|
||||
@@ -86,7 +92,6 @@ import com.cobblemon.mod.common.pokemon.feature.TagSeasonResolver
|
||||
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
|
||||
import com.cobblemon.mod.common.pokemon.properties.HiddenAbilityPropertyType
|
||||
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.UntradeableProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.tags.PokemonFlagProperty
|
||||
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
|
||||
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
|
||||
@@ -101,6 +106,10 @@ 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
|
||||
@@ -245,7 +254,6 @@ object Cobblemon {
|
||||
DataKeys.HAS_BEEN_SHEARED,
|
||||
FlagSpeciesFeatureProvider(keys = listOf(DataKeys.HAS_BEEN_SHEARED), default = false))
|
||||
|
||||
CustomPokemonProperty.register(UntradeableProperty)
|
||||
CustomPokemonProperty.register(UncatchableProperty)
|
||||
CustomPokemonProperty.register(PokemonFlagProperty)
|
||||
CustomPokemonProperty.register(HiddenAbilityPropertyType)
|
||||
@@ -258,15 +266,52 @@ object Cobblemon {
|
||||
PlatformEvents.SERVER_STARTING.subscribe { event ->
|
||||
val server = event.server
|
||||
playerData = PlayerDataStoreManager().also { it.setup(server) }
|
||||
|
||||
val mongoClient: MongoClient?
|
||||
|
||||
val pokemonStoreRoot = server.getSavePath(WorldSavePath.ROOT).resolve("pokemon").toFile()
|
||||
val storeAdapter = when (config.storageFormat) {
|
||||
"nbt", "json" -> {
|
||||
val jsonFactory = JsonPlayerDataStoreFactory()
|
||||
jsonFactory.setup(server)
|
||||
playerData.setFactory(jsonFactory)
|
||||
|
||||
if (config.storageFormat == "nbt") {
|
||||
NBTStoreAdapter(pokemonStoreRoot.absolutePath, useNestedFolders = true, folderPerClass = true)
|
||||
} else {
|
||||
JSONStoreAdapter(
|
||||
pokemonStoreRoot.absolutePath,
|
||||
useNestedFolders = true,
|
||||
folderPerClass = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
"mongodb" -> {
|
||||
try {
|
||||
Class.forName("com.mongodb.client.MongoClient")
|
||||
|
||||
val mongoClientSettings = MongoClientSettings.builder()
|
||||
.applyConnectionString(ConnectionString(config.mongoDBConnectionString))
|
||||
.build()
|
||||
mongoClient = MongoClients.create(mongoClientSettings)
|
||||
val mongoFactory = MongoPlayerDataStoreFactory(mongoClient, config.mongoDBDatabaseName)
|
||||
playerData.setFactory(mongoFactory)
|
||||
MongoDBStoreAdapter(mongoClient, config.mongoDBDatabaseName)
|
||||
} catch (e: ClassNotFoundException) {
|
||||
LOGGER.error("MongoDB driver not found.")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
else -> throw IllegalArgumentException("Unsupported storageFormat: ${config.storageFormat}")
|
||||
}
|
||||
.with(ReforgedConversion(server.getSavePath(WorldSavePath.ROOT))) as FileStoreAdapter<*>
|
||||
|
||||
storage.registerFactory(
|
||||
priority = Priority.LOWEST,
|
||||
factory = FileBackedPokemonStoreFactory(
|
||||
adapter = if (config.storageFormat == "nbt") {
|
||||
NBTStoreAdapter(pokemonStoreRoot.absolutePath, useNestedFolders = true, folderPerClass = true)
|
||||
} else {
|
||||
JSONStoreAdapter(pokemonStoreRoot.absolutePath, useNestedFolders = true, folderPerClass = true)
|
||||
}.with(ReforgedConversion(server.getSavePath(WorldSavePath.ROOT))) as FileStoreAdapter<*>,
|
||||
adapter = storeAdapter,
|
||||
createIfMissing = true,
|
||||
pcConstructor = { uuid -> PCStore(uuid).also { it.resize(config.defaultBoxCount) } }
|
||||
)
|
||||
@@ -292,7 +337,7 @@ object Cobblemon {
|
||||
// Ensure the config option is enabled and that the result was a ninjask and that shedinja exists
|
||||
if (this.config.ninjaskCreatesShedinja && pokemon.species.resourceIdentifier == ninjaskIdentifier && PokemonSpecies.getByIdentifier(Pokemon.SHEDINJA) != null) {
|
||||
val player = pokemon.getOwnerPlayer() ?: return@subscribe
|
||||
if (player.inventory.containsAny { it.item is PokeBallItem }) {
|
||||
if (player.isCreative || player.inventory.containsAny { it.item is PokeBallItem }) {
|
||||
var pokeball = Items.AIR
|
||||
player.inventory.combinedInventory.forEach {
|
||||
it.forEach {
|
||||
@@ -301,7 +346,12 @@ object Cobblemon {
|
||||
}
|
||||
}
|
||||
}
|
||||
player.inventory.removeAmountIf(1) { it.item is PokeBallItem }
|
||||
if (!player.isCreative) {
|
||||
player.inventory.removeAmountIf(1) { it.item is PokeBallItem }
|
||||
}
|
||||
if (pokeball == Items.AIR) {
|
||||
pokeball = CobblemonItems.POKE_BALL
|
||||
}
|
||||
val properties = event.evolution.result.copy()
|
||||
properties.species = Pokemon.SHEDINJA.toString()
|
||||
val product = pokemon.clone()
|
||||
@@ -309,9 +359,12 @@ object Cobblemon {
|
||||
properties.apply(product)
|
||||
product.caughtBall = (pokeball as PokeBallItem).pokeBall
|
||||
pokemon.storeCoordinates.get()?.store?.add(product)
|
||||
CobblemonCriteria.EVOLVE_POKEMON.trigger(player, EvolvePokemonContext(event.pokemon.preEvolution!!.species.resourceIdentifier, product.species.resourceIdentifier, playerData.get(player).advancementData.totalEvolvedCount))
|
||||
}
|
||||
}
|
||||
}
|
||||
LEVEL_UP_EVENT.subscribe { AdvancementHandler.onLevelUp(it) }
|
||||
TRADE_COMPLETED.subscribe { AdvancementHandler.onTradeCompleted(it) }
|
||||
|
||||
BagItems.observable.subscribe {
|
||||
LOGGER.info("Starting dummy Showdown battle to force it to pre-load data.")
|
||||
|
||||
@@ -33,6 +33,8 @@ import net.minecraft.util.math.intprovider.UniformIntProvider
|
||||
@Suppress("SameParameterValue", "HasPlatformType", "MemberVisibilityCanBePrivate", "unused")
|
||||
object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<Block>>, Block>() {
|
||||
|
||||
|
||||
|
||||
override val registry: Registry<Block> = Registries.BLOCK
|
||||
override val registryKey: RegistryKey<Registry<Block>> = RegistryKeys.BLOCK
|
||||
|
||||
@@ -228,6 +230,8 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
APRICORN_LOG to STRIPPED_APRICORN_LOG
|
||||
)
|
||||
|
||||
|
||||
|
||||
private fun apricornBlock(name: String, apricorn: Apricorn): ApricornBlock = this.create(name, ApricornBlock(AbstractBlock.Settings.create().mapColor(apricorn.mapColor()).ticksRandomly().strength(Blocks.OAK_LOG.hardness, Blocks.OAK_LOG.blastResistance).sounds(BlockSoundGroup.WOOD).nonOpaque(), apricorn))
|
||||
|
||||
private val berries = mutableMapOf<Identifier, BerryBlock>()
|
||||
@@ -302,24 +306,57 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
val WIKI_BERRY = this.berryBlock("wiki")
|
||||
val YACHE_BERRY = this.berryBlock("yache")
|
||||
|
||||
init {
|
||||
/**
|
||||
* Makes all blocks in array flammable by adding them to FireBlock's flammableRegistry.
|
||||
* second value is burn chance and third value is spread chance
|
||||
*/
|
||||
arrayOf(
|
||||
Triple(APRICORN_LOG, 5, 5),
|
||||
Triple(STRIPPED_APRICORN_LOG, 5, 5),
|
||||
Triple(APRICORN_WOOD, 5, 5),
|
||||
Triple(STRIPPED_APRICORN_WOOD, 5, 5),
|
||||
Triple(APRICORN_PLANKS, 5, 20),
|
||||
Triple(APRICORN_LEAVES, 30, 60),
|
||||
Triple(APRICORN_FENCE, 5, 20),
|
||||
Triple(APRICORN_FENCE_GATE, 5, 20),
|
||||
Triple(APRICORN_SLAB, 5, 20),
|
||||
Triple(APRICORN_STAIRS, 5, 20)
|
||||
).onEach{ data -> setFlammable(data.first, data.second, data.third) }
|
||||
}
|
||||
|
||||
fun berries() = this.berries.toMap()
|
||||
|
||||
private fun berryBlock(name: String): BerryBlock {
|
||||
val identifier = cobblemonResource("${name}_berry")
|
||||
val block = this.create(identifier.path, BerryBlock(identifier, AbstractBlock.Settings.copy(Blocks.WHEAT).dynamicBounds().sounds(BlockSoundGroup.CROP)))
|
||||
val block = this.create(identifier.path, BerryBlock(identifier, AbstractBlock.Settings.copy(Blocks.WHEAT).dynamicBounds().sounds(BlockSoundGroup.CROP).strength(0.2F)))
|
||||
this.berries[identifier] = block
|
||||
return block
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for creating logs
|
||||
* copied over from Vanilla
|
||||
* Calls helper method from Vanilla
|
||||
*/
|
||||
private fun log(name: String, arg: MapColor = MapColor.DIRT_BROWN, arg2: MapColor = MapColor.DIRT_BROWN): PillarBlock {
|
||||
val block = BlocksInvoker.createLogBlock(arg, arg2)
|
||||
return this.create(name, block)
|
||||
}
|
||||
|
||||
/**
|
||||
* Method uses generic E in order to keep the block as the same return type.
|
||||
* If E is not a block then it will not be set as flammable.
|
||||
* Calls Vanilla implementation of registering a flammable block.
|
||||
* Mixins looks cursed but it is java's fault.
|
||||
*/
|
||||
private fun <E> setFlammable(block: E, burnChance: Int, spreadChance: Int): E {
|
||||
if(block !is Block) return block
|
||||
|
||||
var fireBlock: FireBlock = Blocks.FIRE as FireBlock
|
||||
//Cursed Mixin stuff
|
||||
(fireBlock as FireBlockInvoker).registerNewFlammableBlock(block as Block, burnChance, spreadChance)
|
||||
return block
|
||||
}
|
||||
|
||||
private fun evolutionStoneOre(name: String) = this.create(name, ExperienceDroppingBlock(AbstractBlock.Settings.copy(Blocks.IRON_ORE), UniformIntProvider.create(1, 2)))
|
||||
|
||||
private fun deepslateEvolutionStoneOre(name: String) = this.create(name, ExperienceDroppingBlock(AbstractBlock.Settings.copy(Blocks.DEEPSLATE_IRON_ORE), UniformIntProvider.create(1, 2)))
|
||||
|
||||
@@ -44,6 +44,7 @@ object CobblemonCommands {
|
||||
TestPcSlotCommand.register(dispatcher)
|
||||
TestPartySlotCommand.register(dispatcher)
|
||||
ClearPartyCommand.register(dispatcher)
|
||||
ClearPCCommand.register(dispatcher)
|
||||
PokemonRestartCommand.register(dispatcher)
|
||||
}
|
||||
}
|
||||
@@ -8,22 +8,27 @@
|
||||
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
|
||||
import com.mojang.brigadier.arguments.ArgumentType
|
||||
import kotlin.reflect.KClass
|
||||
import net.minecraft.advancement.criterion.Criterion
|
||||
import net.minecraft.block.ComposterBlock
|
||||
import net.minecraft.command.argument.serialize.ArgumentSerializer
|
||||
import net.minecraft.item.ItemConvertible
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
import net.minecraft.network.listener.ClientPlayPacketListener
|
||||
import net.minecraft.network.packet.Packet
|
||||
import net.minecraft.registry.RegistryKey
|
||||
import net.minecraft.registry.tag.TagKey
|
||||
import net.minecraft.resource.ResourceManager
|
||||
import net.minecraft.resource.ResourceReloader
|
||||
import net.minecraft.resource.ResourceType
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.world.GameRules
|
||||
import net.minecraft.world.biome.Biome
|
||||
@@ -160,6 +165,51 @@ 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<Identifier, T>
|
||||
|
||||
/**
|
||||
* Registers an item to the [ComposterBlock].
|
||||
*
|
||||
* @param item The [ItemConvertible] being registered.
|
||||
* @param chance The chance % of increasing the composter level, 0 to 1 expected.
|
||||
*/
|
||||
fun registerCompostable(item: ItemConvertible, chance: Float)
|
||||
|
||||
/**
|
||||
* Registers a builtin resource pack.
|
||||
*
|
||||
* @param id The unique [Identifier] 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: Identifier, title: Text, activationBehaviour: ResourcePackActivationBehaviour)
|
||||
|
||||
}
|
||||
|
||||
enum class ResourcePackActivationBehaviour {
|
||||
|
||||
/**
|
||||
* The resource pack will start disabled.
|
||||
*/
|
||||
NORMAL,
|
||||
|
||||
/**
|
||||
* The resource pack will start enabled.
|
||||
*/
|
||||
DEFAULT_ENABLED,
|
||||
|
||||
/**
|
||||
* The resource pack will always be enabled.
|
||||
* The user can reorder it but cannot remove it.
|
||||
*/
|
||||
ALWAYS_ENABLED;
|
||||
|
||||
}
|
||||
|
||||
enum class ModAPI {
|
||||
|
||||
@@ -14,6 +14,7 @@ import com.cobblemon.mod.common.api.pokemon.Natures
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
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.item.*
|
||||
import com.cobblemon.mod.common.item.battle.DireHitItem
|
||||
@@ -32,7 +33,6 @@ import com.cobblemon.mod.common.pokeball.PokeBall
|
||||
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.block.ComposterBlock
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.entity.effect.StatusEffectInstance
|
||||
import net.minecraft.entity.effect.StatusEffects
|
||||
@@ -117,40 +117,40 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
val CHERISH_BALL = pokeBallItem(PokeBalls.CHERISH_BALL)
|
||||
|
||||
@JvmField
|
||||
val VIVICHOKE = create("vivichoke", Item(Item.Settings()))
|
||||
val VIVICHOKE = compostableItem("vivichoke")
|
||||
|
||||
@JvmField
|
||||
val VIVICHOKE_SEEDS = create("vivichoke_seeds", VivichokeItem(CobblemonBlocks.VIVICHOKE_SEEDS))
|
||||
val VIVICHOKE_SEEDS = compostableItem("vivichoke_seeds", VivichokeItem(CobblemonBlocks.VIVICHOKE_SEEDS))
|
||||
|
||||
@JvmField
|
||||
val RED_APRICORN = create("red_apricorn", ApricornItem(CobblemonBlocks.RED_APRICORN))
|
||||
val RED_APRICORN = apricronItem("red", ApricornItem(CobblemonBlocks.RED_APRICORN))
|
||||
@JvmField
|
||||
val YELLOW_APRICORN = create("yellow_apricorn", ApricornItem(CobblemonBlocks.YELLOW_APRICORN))
|
||||
val YELLOW_APRICORN = apricronItem("yellow", ApricornItem(CobblemonBlocks.YELLOW_APRICORN))
|
||||
@JvmField
|
||||
val GREEN_APRICORN = create("green_apricorn", ApricornItem(CobblemonBlocks.GREEN_APRICORN))
|
||||
val GREEN_APRICORN = apricronItem("green", ApricornItem(CobblemonBlocks.GREEN_APRICORN))
|
||||
@JvmField
|
||||
val BLUE_APRICORN = create("blue_apricorn", ApricornItem(CobblemonBlocks.BLUE_APRICORN))
|
||||
val BLUE_APRICORN = apricronItem("blue", ApricornItem(CobblemonBlocks.BLUE_APRICORN))
|
||||
@JvmField
|
||||
val PINK_APRICORN = create("pink_apricorn", ApricornItem(CobblemonBlocks.PINK_APRICORN))
|
||||
val PINK_APRICORN = apricronItem("pink", ApricornItem(CobblemonBlocks.PINK_APRICORN))
|
||||
@JvmField
|
||||
val BLACK_APRICORN = create("black_apricorn", ApricornItem(CobblemonBlocks.BLACK_APRICORN))
|
||||
val BLACK_APRICORN = apricronItem("black", ApricornItem(CobblemonBlocks.BLACK_APRICORN))
|
||||
@JvmField
|
||||
val WHITE_APRICORN = create("white_apricorn", ApricornItem(CobblemonBlocks.WHITE_APRICORN))
|
||||
val WHITE_APRICORN = apricronItem("white", ApricornItem(CobblemonBlocks.WHITE_APRICORN))
|
||||
|
||||
@JvmField
|
||||
val RED_APRICORN_SEED = create("red_apricorn_seed", ApricornSeedItem(CobblemonBlocks.RED_APRICORN_SAPLING))
|
||||
val RED_APRICORN_SEED = apricronSeedItem("red", ApricornSeedItem(CobblemonBlocks.RED_APRICORN_SAPLING))
|
||||
@JvmField
|
||||
val YELLOW_APRICORN_SEED = create("yellow_apricorn_seed", ApricornSeedItem(CobblemonBlocks.YELLOW_APRICORN_SAPLING))
|
||||
val YELLOW_APRICORN_SEED = apricronSeedItem("yellow", ApricornSeedItem(CobblemonBlocks.YELLOW_APRICORN_SAPLING))
|
||||
@JvmField
|
||||
val GREEN_APRICORN_SEED = create("green_apricorn_seed", ApricornSeedItem(CobblemonBlocks.GREEN_APRICORN_SAPLING))
|
||||
val GREEN_APRICORN_SEED = apricronSeedItem("green", ApricornSeedItem(CobblemonBlocks.GREEN_APRICORN_SAPLING))
|
||||
@JvmField
|
||||
val BLUE_APRICORN_SEED = create("blue_apricorn_seed", ApricornSeedItem(CobblemonBlocks.BLUE_APRICORN_SAPLING))
|
||||
val BLUE_APRICORN_SEED = apricronSeedItem("blue", ApricornSeedItem(CobblemonBlocks.BLUE_APRICORN_SAPLING))
|
||||
@JvmField
|
||||
val PINK_APRICORN_SEED = create("pink_apricorn_seed", ApricornSeedItem(CobblemonBlocks.PINK_APRICORN_SAPLING))
|
||||
val PINK_APRICORN_SEED = apricronSeedItem("pink", ApricornSeedItem(CobblemonBlocks.PINK_APRICORN_SAPLING))
|
||||
@JvmField
|
||||
val BLACK_APRICORN_SEED = create("black_apricorn_seed", ApricornSeedItem(CobblemonBlocks.BLACK_APRICORN_SAPLING))
|
||||
val BLACK_APRICORN_SEED = apricronSeedItem("black", ApricornSeedItem(CobblemonBlocks.BLACK_APRICORN_SAPLING))
|
||||
@JvmField
|
||||
val WHITE_APRICORN_SEED = create("white_apricorn_seed", ApricornSeedItem(CobblemonBlocks.WHITE_APRICORN_SAPLING))
|
||||
val WHITE_APRICORN_SEED = apricronSeedItem("white", ApricornSeedItem(CobblemonBlocks.WHITE_APRICORN_SAPLING))
|
||||
|
||||
@JvmField
|
||||
val APRICORN_LOG = blockItem("apricorn_log", CobblemonBlocks.APRICORN_LOG)
|
||||
@@ -163,13 +163,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
@JvmField
|
||||
val APRICORN_PLANKS = blockItem("apricorn_planks", CobblemonBlocks.APRICORN_PLANKS)
|
||||
@JvmField
|
||||
val APRICORN_LEAVES = blockItem("apricorn_leaves", CobblemonBlocks.APRICORN_LEAVES).also {
|
||||
val compostChance = ComposterBlock.ITEM_TO_LEVEL_INCREASE_CHANCE.getFloat(Items.OAK_LEAVES)
|
||||
// Should always pass unless Mojang reworks leaves to no longer work in the Composter, in that case we already updated w.o doing anything
|
||||
if (compostChance != ComposterBlock.ITEM_TO_LEVEL_INCREASE_CHANCE.defaultReturnValue()) {
|
||||
ComposterBlock.ITEM_TO_LEVEL_INCREASE_CHANCE[it] = compostChance
|
||||
}
|
||||
}
|
||||
val APRICORN_LEAVES = compostableBlockItem("apricorn_leaves", CobblemonBlocks.APRICORN_LEAVES)
|
||||
|
||||
@JvmField
|
||||
val APRICORN_DOOR = blockItem("apricorn_door", CobblemonBlocks.APRICORN_DOOR)
|
||||
@@ -231,8 +225,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
@JvmField val PEAT_BLOCK = noSettingsItem("peat_block")
|
||||
@JvmField val RAZOR_CLAW = noSettingsItem("razor_claw")
|
||||
@JvmField val RAZOR_FANG = noSettingsItem("razor_fang")
|
||||
// ToDo enable me when malicious armor goes in the game
|
||||
//@JvmField val AUSPICIOUS_ARMOR = heldItem("auspicious_armor")
|
||||
@JvmField val AUSPICIOUS_ARMOR = heldItem("auspicious_armor")
|
||||
@JvmField val MALICIOUS_ARMOR = heldItem("malicious_armor")
|
||||
|
||||
private val berries = mutableMapOf<Identifier, BerryItem>()
|
||||
// Plants
|
||||
@@ -338,7 +332,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
@JvmField
|
||||
val ROASTED_LEEK = create("roasted_leek", Item(Item.Settings().food(FoodComponent.Builder().snack().hunger(3).saturationModifier(0.3f).build())))
|
||||
@JvmField
|
||||
val BRAISED_VIVICHOKE = create("braised_vivichoke", Item(Item.Settings().maxCount(1).food(FoodComponent.Builder().hunger(6).saturationModifier(0.6f).build())))
|
||||
val BRAISED_VIVICHOKE = create("braised_vivichoke", Item(Item.Settings().food(FoodComponent.Builder().hunger(6).saturationModifier(0.6f).build())))
|
||||
@JvmField
|
||||
val VIVICHOKE_DIP = create("vivichoke_dip", object : StewItem(Settings().maxCount(1)
|
||||
.food(FoodComponent.Builder()
|
||||
@@ -353,11 +347,11 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
}
|
||||
})
|
||||
@JvmField
|
||||
val ENERGY_ROOT = create("energy_root", EnergyRootItem(CobblemonBlocks.ENERGY_ROOT, Item.Settings().food(FoodComponent.Builder().hunger(1).snack().saturationModifier(0.2f).build())))
|
||||
val ENERGY_ROOT = compostableItem("energy_root", EnergyRootItem(CobblemonBlocks.ENERGY_ROOT, Item.Settings().food(FoodComponent.Builder().hunger(1).snack().saturationModifier(0.2f).build())))
|
||||
@JvmField
|
||||
val REVIVAL_HERB = create("revival_herb", RevivalHerbItem(CobblemonBlocks.REVIVAL_HERB))
|
||||
val REVIVAL_HERB = compostableItem("revival_herb", RevivalHerbItem(CobblemonBlocks.REVIVAL_HERB))
|
||||
@JvmField
|
||||
val PEP_UP_FLOWER = blockItem("pep_up_flower", CobblemonBlocks.PEP_UP_FLOWER)
|
||||
val PEP_UP_FLOWER = compostableBlockItem("pep_up_flower", CobblemonBlocks.PEP_UP_FLOWER)
|
||||
@JvmField
|
||||
val MEDICINAL_BREW = create("medicinal_brew", Item(Item.Settings()))
|
||||
@JvmField
|
||||
@@ -392,72 +386,74 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
val PP_MAX = create("pp_max", PPUpItem(3))
|
||||
|
||||
@JvmField
|
||||
val RED_MINT_SEEDS = blockItem("red_mint_seeds", MintType.RED.getCropBlock())
|
||||
val RED_MINT_SEEDS = mintSeed("red", MintType.RED.getCropBlock())
|
||||
@JvmField
|
||||
val RED_MINT_LEAF = create("red_mint_leaf", MintLeafItem(MintType.RED))
|
||||
val RED_MINT_LEAF = mintLeaf("red", MintLeafItem(MintType.RED))
|
||||
@JvmField
|
||||
val BLUE_MINT_SEEDS = blockItem("blue_mint_seeds", MintType.BLUE.getCropBlock())
|
||||
val BLUE_MINT_SEEDS = mintSeed("blue", MintType.BLUE.getCropBlock())
|
||||
@JvmField
|
||||
val BLUE_MINT_LEAF = create("blue_mint_leaf", MintLeafItem(MintType.BLUE))
|
||||
val BLUE_MINT_LEAF = mintLeaf("blue", MintLeafItem(MintType.BLUE))
|
||||
@JvmField
|
||||
val CYAN_MINT_SEEDS = blockItem("cyan_mint_seeds", MintType.CYAN.getCropBlock())
|
||||
val CYAN_MINT_SEEDS = mintSeed("cyan", MintType.CYAN.getCropBlock())
|
||||
@JvmField
|
||||
val CYAN_MINT_LEAF = create("cyan_mint_leaf", MintLeafItem(MintType.CYAN))
|
||||
val CYAN_MINT_LEAF = mintLeaf("cyan", MintLeafItem(MintType.CYAN))
|
||||
@JvmField
|
||||
val PINK_MINT_SEEDS = blockItem("pink_mint_seeds", MintType.PINK.getCropBlock())
|
||||
val PINK_MINT_SEEDS = mintSeed("pink", MintType.PINK.getCropBlock())
|
||||
@JvmField
|
||||
val PINK_MINT_LEAF = create("pink_mint_leaf", MintLeafItem(MintType.PINK))
|
||||
val PINK_MINT_LEAF = mintLeaf("pink", MintLeafItem(MintType.PINK))
|
||||
@JvmField
|
||||
val GREEN_MINT_SEEDS = blockItem("green_mint_seeds", MintType.GREEN.getCropBlock())
|
||||
val GREEN_MINT_SEEDS = mintSeed("green", MintType.GREEN.getCropBlock())
|
||||
@JvmField
|
||||
val GREEN_MINT_LEAF = create("green_mint_leaf", MintLeafItem(MintType.GREEN))
|
||||
val GREEN_MINT_LEAF = mintLeaf("green", MintLeafItem(MintType.GREEN))
|
||||
@JvmField
|
||||
val WHITE_MINT_SEEDS = blockItem("white_mint_seeds", MintType.WHITE.getCropBlock())
|
||||
val WHITE_MINT_SEEDS = mintSeed("white", MintType.WHITE.getCropBlock())
|
||||
@JvmField
|
||||
val WHITE_MINT_LEAF = create("white_mint_leaf", MintLeafItem(MintType.WHITE))
|
||||
val WHITE_MINT_LEAF = mintLeaf("white", MintLeafItem(MintType.WHITE))
|
||||
|
||||
val mints = mutableMapOf<String, MintItem>()
|
||||
|
||||
@JvmField
|
||||
val LONELY_MINT = create("lonely_mint", MintItem(Natures.LONELY))
|
||||
val LONELY_MINT = mintItem("lonely_mint", MintItem(Natures.LONELY))
|
||||
@JvmField
|
||||
val ADAMANT_MINT = create("adamant_mint", MintItem(Natures.ADAMANT))
|
||||
val ADAMANT_MINT = mintItem("adamant_mint", MintItem(Natures.ADAMANT))
|
||||
@JvmField
|
||||
val NAUGHTY_MINT = create("naughty_mint", MintItem(Natures.NAUGHTY))
|
||||
val NAUGHTY_MINT = mintItem("naughty_mint", MintItem(Natures.NAUGHTY))
|
||||
@JvmField
|
||||
val BRAVE_MINT = create("brave_mint", MintItem(Natures.BRAVE))
|
||||
val BRAVE_MINT = mintItem("brave_mint", MintItem(Natures.BRAVE))
|
||||
@JvmField
|
||||
val BOLD_MINT = create("bold_mint", MintItem(Natures.BOLD))
|
||||
val BOLD_MINT = mintItem("bold_mint", MintItem(Natures.BOLD))
|
||||
@JvmField
|
||||
val IMPISH_MINT = create("impish_mint", MintItem(Natures.IMPISH))
|
||||
val IMPISH_MINT = mintItem("impish_mint", MintItem(Natures.IMPISH))
|
||||
@JvmField
|
||||
val LAX_MINT = create("lax_mint", MintItem(Natures.LAX))
|
||||
val LAX_MINT = mintItem("lax_mint", MintItem(Natures.LAX))
|
||||
@JvmField
|
||||
val RELAXED_MINT = create("relaxed_mint", MintItem(Natures.RELAXED))
|
||||
val RELAXED_MINT = mintItem("relaxed_mint", MintItem(Natures.RELAXED))
|
||||
@JvmField
|
||||
val MODEST_MINT = create("modest_mint", MintItem(Natures.MODEST))
|
||||
val MODEST_MINT = mintItem("modest_mint", MintItem(Natures.MODEST))
|
||||
@JvmField
|
||||
val MILD_MINT = create("mild_mint", MintItem(Natures.MILD))
|
||||
val MILD_MINT = mintItem("mild_mint", MintItem(Natures.MILD))
|
||||
@JvmField
|
||||
val RASH_MINT = create("rash_mint", MintItem(Natures.RASH))
|
||||
val RASH_MINT = mintItem("rash_mint", MintItem(Natures.RASH))
|
||||
@JvmField
|
||||
val QUIET_MINT = create("quiet_mint", MintItem(Natures.QUIET))
|
||||
val QUIET_MINT = mintItem("quiet_mint", MintItem(Natures.QUIET))
|
||||
@JvmField
|
||||
val CALM_MINT = create("calm_mint", MintItem(Natures.CALM))
|
||||
val CALM_MINT = mintItem("calm_mint", MintItem(Natures.CALM))
|
||||
@JvmField
|
||||
val GENTLE_MINT = create("gentle_mint", MintItem(Natures.GENTLE))
|
||||
val GENTLE_MINT = mintItem("gentle_mint", MintItem(Natures.GENTLE))
|
||||
@JvmField
|
||||
val CAREFUL_MINT = create("careful_mint", MintItem(Natures.CAREFUL))
|
||||
val CAREFUL_MINT = mintItem("careful_mint", MintItem(Natures.CAREFUL))
|
||||
@JvmField
|
||||
val SASSY_MINT = create("sassy_mint", MintItem(Natures.SASSY))
|
||||
val SASSY_MINT = mintItem("sassy_mint", MintItem(Natures.SASSY))
|
||||
@JvmField
|
||||
val TIMID_MINT = create("timid_mint", MintItem(Natures.TIMID))
|
||||
val TIMID_MINT = mintItem("timid_mint", MintItem(Natures.TIMID))
|
||||
@JvmField
|
||||
val HASTY_MINT = create("hasty_mint", MintItem(Natures.HASTY))
|
||||
val HASTY_MINT = mintItem("hasty_mint", MintItem(Natures.HASTY))
|
||||
@JvmField
|
||||
val JOLLY_MINT = create("jolly_mint", MintItem(Natures.JOLLY))
|
||||
val JOLLY_MINT = mintItem("jolly_mint", MintItem(Natures.JOLLY))
|
||||
@JvmField
|
||||
val NAIVE_MINT = create("naive_mint", MintItem(Natures.NAIVE))
|
||||
val NAIVE_MINT = mintItem("naive_mint", MintItem(Natures.NAIVE))
|
||||
@JvmField
|
||||
val SERIOUS_MINT = create("serious_mint", MintItem(Natures.SERIOUS))
|
||||
val SERIOUS_MINT = mintItem("serious_mint", MintItem(Natures.SERIOUS))
|
||||
|
||||
@JvmField val X_ACCURACY = create("x_${Stats.ACCURACY.identifier.path}", XStatItem(Stats.ACCURACY))
|
||||
@JvmField val X_ATTACK = create("x_${Stats.ATTACK.identifier.path}", XStatItem(Stats.ATTACK))
|
||||
@@ -616,11 +612,11 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
@JvmField
|
||||
val WISE_GLASSES = heldItem("wise_glasses")
|
||||
@JvmField
|
||||
val MENTAL_HERB = heldItem("mental_herb")
|
||||
val MENTAL_HERB = compostableHeldItem("mental_herb")
|
||||
@JvmField
|
||||
val MIRROR_HERB = heldItem("mirror_herb")
|
||||
val MIRROR_HERB = compostableHeldItem("mirror_herb")
|
||||
@JvmField
|
||||
val POWER_HERB = heldItem("power_herb")
|
||||
val POWER_HERB = compostableHeldItem("power_herb")
|
||||
@JvmField
|
||||
val WHITE_HERB = heldItem("white_herb")
|
||||
@JvmField
|
||||
@@ -670,10 +666,17 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
@JvmField
|
||||
val POKEMON_MODEL = this.create("pokemon_model", PokemonItem())
|
||||
|
||||
//Keyitems
|
||||
@JvmField
|
||||
val PHOTODEX = photodexItem("photodex")
|
||||
|
||||
|
||||
private fun blockItem(name: String, block: Block): BlockItem = this.create(name, BlockItem(block, Item.Settings()))
|
||||
|
||||
private fun noSettingsItem(name: String): CobblemonItem = this.create(name, CobblemonItem(Item.Settings()))
|
||||
|
||||
private fun photodexItem(name: String): PhotodexItem = this.create(name, PhotodexItem())
|
||||
|
||||
fun berries() = this.berries.toMap()
|
||||
|
||||
private fun mulchItem(name: String, mulchVariant: MulchVariant): MulchItem = this.create(name, MulchItem(mulchVariant))
|
||||
@@ -696,9 +699,12 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
}
|
||||
)
|
||||
|
||||
private fun compostable(item: Item, increaseLevelChance: Float) = Cobblemon.implementation.registerCompostable(item, increaseLevelChance)
|
||||
|
||||
private fun berryItem(name: String, berryBlock: BerryBlock): BerryItem {
|
||||
val finalName = "${name}_berry"
|
||||
val item = this.create(finalName, BerryItem(berryBlock))
|
||||
compostable(item, .65f)
|
||||
this.berries[cobblemonResource(finalName)] = item
|
||||
return item
|
||||
}
|
||||
@@ -706,7 +712,62 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
private fun berryItem(name: String, berryItem: BerryItem): BerryItem {
|
||||
val finalName = "${name}_berry"
|
||||
val item = this.create(finalName, berryItem)
|
||||
compostable(item, .65f)
|
||||
this.berries[cobblemonResource(finalName)] = item
|
||||
return item
|
||||
}
|
||||
|
||||
private fun mintItem(name: String, mintItem: MintItem): MintItem {
|
||||
val item = this.create(name, mintItem)
|
||||
mints[item.nature.displayName] = item
|
||||
compostable(item, .65f)
|
||||
return item
|
||||
}
|
||||
|
||||
private fun apricronItem(name: String, apricornItem: ApricornItem): ApricornItem {
|
||||
val finalName = "${name}_apricorn"
|
||||
val item = this.create(finalName, apricornItem)
|
||||
compostable(item, .65f)
|
||||
return item
|
||||
}
|
||||
|
||||
private fun apricronSeedItem(name: String, apricronSeedItem: ApricornSeedItem): ApricornSeedItem {
|
||||
val finalName = "${name}_apricorn_seed"
|
||||
val item = this.create(finalName, apricronSeedItem)
|
||||
compostable(item, .65f)
|
||||
return item
|
||||
}
|
||||
|
||||
private fun mintSeed(name: String, mintBlock: MintBlock): Item {
|
||||
val finalName = "${name}_mint_seeds"
|
||||
val item = this.blockItem(finalName, mintBlock)
|
||||
compostable(item, .65f)
|
||||
return item
|
||||
}
|
||||
|
||||
private fun mintLeaf(name: String, mintLeafItem: MintLeafItem): MintLeafItem {
|
||||
val finalName = "${name}_mint_leaf"
|
||||
val item = this.create(finalName, mintLeafItem)
|
||||
compostable(item, .65f)
|
||||
return item
|
||||
}
|
||||
|
||||
private fun compostableItem(name: String, item: Item? = null, increaseLevelChance: Float = .65f): Item {
|
||||
val createdItem = this.create(name, item ?: Item(Item.Settings()))
|
||||
compostable(createdItem, increaseLevelChance)
|
||||
return createdItem
|
||||
}
|
||||
|
||||
private fun compostableHeldItem(name: String, remappedName: String? = null, increaseLevelChance: Float = .65f): CobblemonItem {
|
||||
val createdItem = heldItem(name, remappedName)
|
||||
compostable(createdItem, increaseLevelChance)
|
||||
return createdItem
|
||||
}
|
||||
|
||||
private fun compostableBlockItem(name: String, block: Block, increaseLevelChance: Float = .65f): Item {
|
||||
val createdItem = this.blockItem(name, block)
|
||||
compostable(createdItem, increaseLevelChance)
|
||||
return createdItem
|
||||
}
|
||||
|
||||
}
|
||||
@@ -211,6 +211,7 @@ object CobblemonNetwork : NetworkManager {
|
||||
this.createClientBound(HeldItemUpdatePacket.ID, HeldItemUpdatePacket::decode, PokemonUpdatePacketHandler())
|
||||
this.createClientBound(PokemonStateUpdatePacket.ID, PokemonStateUpdatePacket::decode, PokemonUpdatePacketHandler())
|
||||
this.createClientBound(TetheringUpdatePacket.ID, TetheringUpdatePacket::decode, PokemonUpdatePacketHandler())
|
||||
this.createClientBound(TradeableUpdatePacket.ID, TradeableUpdatePacket::decode, PokemonUpdatePacketHandler())
|
||||
|
||||
// Evolution start
|
||||
this.createClientBound(AddEvolutionPacket.ID, AddEvolutionPacket::decode, PokemonUpdatePacketHandler())
|
||||
|
||||
@@ -78,6 +78,8 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
|
||||
@JvmField
|
||||
val MEDICINE_SPRAY_USE = this.create("medicine_spray.use")
|
||||
|
||||
@JvmField
|
||||
val BERRY_HARVEST = this.create("berry.harvest")
|
||||
@JvmField
|
||||
val BERRY_EAT = this.create("berry.eat")
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ object CobblemonTradeOffers {
|
||||
* @return The resulting list.
|
||||
*/
|
||||
fun tradeOffersFor(profession: VillagerProfession): List<VillagerTradeOffer> = when (profession) {
|
||||
VillagerProfession.FARMER -> listOf(VillagerTradeOffer(VillagerProfession.FARMER, 3, listOf(TradeOffers.SellItemFactory(CobblemonItems.VIVICHOKE_SEEDS, 32, 1, 1, 6))))
|
||||
VillagerProfession.FARMER -> listOf(VillagerTradeOffer(VillagerProfession.FARMER, 3, listOf(TradeOffers.SellItemFactory(CobblemonItems.VIVICHOKE_SEEDS, 24, 1, 1, 6))))
|
||||
else -> emptyList()
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ object CobblemonTradeOffers {
|
||||
* @return The resulting list.
|
||||
*/
|
||||
fun resolveWanderingTradeOffers(): List<WandererTradeOffer> = listOf(
|
||||
WandererTradeOffer(false, listOf(TradeOffers.SellItemFactory(CobblemonItems.VIVICHOKE_SEEDS, 32, 1, 1, 6)))
|
||||
WandererTradeOffer(false, listOf(TradeOffers.SellItemFactory(CobblemonItems.VIVICHOKE_SEEDS, 24, 1, 1, 6)))
|
||||
)
|
||||
|
||||
/**
|
||||
|
||||
@@ -47,13 +47,13 @@ object CobblemonCriteria {
|
||||
val EVOLVE_POKEMON = this.create(
|
||||
SimpleCriterionTrigger(
|
||||
cobblemonResource("pokemon_evolved"),
|
||||
SimpleCountableCriterionCondition::class.java
|
||||
EvolvePokemonCriterionCondition::class.java
|
||||
)
|
||||
)
|
||||
val WIN_BATTLE = this.create(
|
||||
SimpleCriterionTrigger(
|
||||
BattleCountableCriterionTrigger(
|
||||
cobblemonResource("battles_won"),
|
||||
SimpleCountableCriterionCondition::class.java
|
||||
BattleCountableCriterionCondition::class.java
|
||||
)
|
||||
)
|
||||
|
||||
@@ -71,6 +71,41 @@ object CobblemonCriteria {
|
||||
)
|
||||
)
|
||||
|
||||
val POKEMON_INTERACT = this.create(
|
||||
SimpleCriterionTrigger(
|
||||
cobblemonResource("pokemon_interact"),
|
||||
PokemonInteractCriterion::class.java
|
||||
)
|
||||
)
|
||||
|
||||
val PARTY_CHECK = this.create(
|
||||
SimpleCriterionTrigger(
|
||||
cobblemonResource("party"),
|
||||
PartyCheckCriterion::class.java
|
||||
)
|
||||
)
|
||||
|
||||
val LEVEL_UP = this.create(
|
||||
SimpleCriterionTrigger(
|
||||
cobblemonResource("level_up"),
|
||||
LevelUpCriterionCondition::class.java
|
||||
)
|
||||
)
|
||||
|
||||
val PASTURE_USE = this.create(
|
||||
SimpleCriterionTrigger(
|
||||
cobblemonResource("pasture_use"),
|
||||
PickStarterCriterionCondition::class.java
|
||||
)
|
||||
)
|
||||
|
||||
val TRADE_POKEMON = this.create(
|
||||
SimpleCriterionTrigger(
|
||||
cobblemonResource("trade_pokemon"),
|
||||
TradePokemonCriterionCondition::class.java
|
||||
)
|
||||
)
|
||||
|
||||
private fun <T : Criterion<*>> create(criteria: T): T = Cobblemon.implementation.registerCriteria(criteria)
|
||||
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.Cobblemon
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.util.toJsonArray
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.predicate.entity.LootContextPredicate
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
open class BattleCountableCriterionTrigger(identifier: Identifier, criterionClass: Class<BattleCountableCriterionCondition>) : SimpleCriterionTrigger<BattleCountableContext, BattleCountableCriterionCondition>(identifier, criterionClass) {
|
||||
|
||||
}
|
||||
|
||||
class BattleCountableCriterionCondition(id: Identifier, predicate: LootContextPredicate) : CountableCriterionCondition<BattleCountableContext>(id, predicate) {
|
||||
|
||||
private var battleTypes = mutableListOf("any")
|
||||
|
||||
override fun fromJson(json: JsonObject) {
|
||||
super.fromJson(json)
|
||||
if(!json.get("battle_types").isJsonNull) {
|
||||
battleTypes.clear()
|
||||
json.get("battle_types").asJsonArray.asList().forEach() {
|
||||
battleTypes.add(it.asString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun toJson(json: JsonObject) {
|
||||
super.toJson(json)
|
||||
json.add("battle_types", battleTypes.toJsonArray())
|
||||
}
|
||||
|
||||
override fun matches(player: ServerPlayerEntity, context: BattleCountableContext): Boolean {
|
||||
var typeCheck = false
|
||||
val advancementData = Cobblemon.playerData.get(player).advancementData
|
||||
if (battleTypes.isEmpty() || battleTypes.contains("any")) {
|
||||
typeCheck = true
|
||||
}
|
||||
if (battleTypes.contains("pvp")) {
|
||||
typeCheck = context.battle.isPvP
|
||||
context.times = advancementData.totalPvPBattleVictoryCount
|
||||
}
|
||||
if (battleTypes.contains("pvw")) {
|
||||
typeCheck = context.battle.isPvW
|
||||
context.times = advancementData.totalPvWBattleVictoryCount
|
||||
}
|
||||
if (battleTypes.contains("pvn")) {
|
||||
typeCheck = context.battle.isPvN
|
||||
context.times = advancementData.totalPvWBattleVictoryCount
|
||||
}
|
||||
if (battleTypes.size > 1) {
|
||||
context.times = advancementData.totalBattleVictoryCount
|
||||
}
|
||||
return context.times >= count && typeCheck
|
||||
}
|
||||
}
|
||||
|
||||
open class BattleCountableContext(times : Int, var battle : PokemonBattle) : CountableContext(times)
|
||||
+1
-1
@@ -34,6 +34,6 @@ class CaughtPokemonCriterionCondition(id: Identifier, predicate: LootContextPred
|
||||
}
|
||||
|
||||
override fun matches(player: ServerPlayerEntity, context: CountablePokemonTypeContext): Boolean {
|
||||
return super.matches(player, context) && context.type == type
|
||||
return super.matches(player, context) && (context.type == type || type == "any")
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.advancement.criterion
|
||||
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.predicate.entity.LootContextPredicate
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
class EvolvePokemonCriterionCondition(id: Identifier, entity: LootContextPredicate) : CountableCriterionCondition<EvolvePokemonContext>(id, entity) {
|
||||
var species = "any"
|
||||
var evolution = "any"
|
||||
override fun toJson(json: JsonObject) {
|
||||
super.toJson(json)
|
||||
json.addProperty("species", species)
|
||||
json.addProperty("evolution", evolution)
|
||||
}
|
||||
|
||||
override fun fromJson(json: JsonObject) {
|
||||
super.fromJson(json)
|
||||
species = json.get("species")?.asString ?: "any"
|
||||
evolution = json.get("evolution")?.asString ?: "any"
|
||||
}
|
||||
|
||||
override fun matches(player: ServerPlayerEntity, context: EvolvePokemonContext): Boolean {
|
||||
return context.times >= count && (context.species == species.asIdentifierDefaultingNamespace() || species == "any") &&
|
||||
(context.evolution == evolution.asIdentifierDefaultingNamespace() || evolution == "any")
|
||||
}
|
||||
}
|
||||
|
||||
open class EvolvePokemonContext(val species : Identifier, val evolution : Identifier, times: Int) : CountableContext(times)
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.pokemon.Pokemon
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.predicate.entity.LootContextPredicate
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
class LevelUpCriterionCondition(id: Identifier, entity: LootContextPredicate) : SimpleCriterionCondition<LevelUpContext>(id, entity) {
|
||||
var level = 0
|
||||
var evolved = true
|
||||
override fun toJson(json: JsonObject) {
|
||||
json.addProperty("level", level)
|
||||
json.addProperty("has_evolved", evolved)
|
||||
}
|
||||
|
||||
override fun fromJson(json: JsonObject) {
|
||||
level = json.get("level")?.asInt ?: 0
|
||||
evolved = json.get("has_evolved")?.asBoolean ?: true
|
||||
}
|
||||
|
||||
override fun matches(player: ServerPlayerEntity, context: LevelUpContext): Boolean {
|
||||
val preEvo = context.pokemon.preEvolution == null
|
||||
val hasEvolution = !context.pokemon.evolutions.none()
|
||||
var evolutionCheck = true
|
||||
if (preEvo || hasEvolution) {
|
||||
evolutionCheck = !(preEvo == hasEvolution)
|
||||
}
|
||||
return level == context.level && evolutionCheck == evolved
|
||||
}
|
||||
}
|
||||
|
||||
open class LevelUpContext(var level : Int, var pokemon : Pokemon)
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.api.storage.party.PlayerPartyStore
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.predicate.entity.LootContextPredicate
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
class PartyCheckCriterion(id: Identifier, entity: LootContextPredicate) : SimpleCriterionCondition<PartyCheckContext>(id, entity){
|
||||
val party = mutableListOf<Identifier>()
|
||||
override fun toJson(json: JsonObject) {
|
||||
json.add("party", JsonArray(party.size).also {
|
||||
party.forEach { pokemon -> it.add(pokemon.toString()) }
|
||||
})
|
||||
}
|
||||
|
||||
override fun fromJson(json: JsonObject) {
|
||||
party.clear()
|
||||
json.getAsJsonArray("party").forEach { element ->
|
||||
party.add(element.asString.asIdentifierDefaultingNamespace())
|
||||
}
|
||||
}
|
||||
|
||||
override fun matches(player: ServerPlayerEntity, context: PartyCheckContext): Boolean {
|
||||
val playerParty = player.party()
|
||||
val matches = mutableListOf<Identifier>()
|
||||
party.forEach {
|
||||
if (it == "any".asIdentifierDefaultingNamespace()) {
|
||||
matches.add(it)
|
||||
}
|
||||
}
|
||||
val partyCount = playerParty.count()
|
||||
if (matches.containsAll(party) && party.size == partyCount && matches.size == partyCount) return true
|
||||
playerParty.iterator().forEach {
|
||||
if (party.contains(it.species.resourceIdentifier)) {
|
||||
matches.add(it.species.resourceIdentifier)
|
||||
}
|
||||
}
|
||||
return matches.containsAll(party) && matches.size == partyCount
|
||||
}
|
||||
}
|
||||
|
||||
open class PartyCheckContext(val party : PlayerPartyStore)
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.advancement.criterion
|
||||
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.predicate.entity.LootContextPredicate
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
open class PokemonInteractContext(var type: Identifier, var item : Identifier)
|
||||
class PokemonInteractCriterion(id: Identifier, entity: LootContextPredicate) : SimpleCriterionCondition<PokemonInteractContext>(id, entity) {
|
||||
var type = "any"
|
||||
var item = "any"
|
||||
override fun toJson(json: JsonObject) {
|
||||
json.addProperty("type", type)
|
||||
json.addProperty("item", item)
|
||||
}
|
||||
|
||||
override fun fromJson(json: JsonObject) {
|
||||
type = json.get("type")?.asString ?: "any"
|
||||
item = json.get("item")?.asString ?: "any"
|
||||
}
|
||||
|
||||
override fun matches(player: ServerPlayerEntity, context: PokemonInteractContext): Boolean {
|
||||
return (context.type == type.asIdentifierDefaultingNamespace() || type == "any") && (context.item == item.asIdentifierDefaultingNamespace() || item == "any")
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.advancement.criterion
|
||||
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.predicate.entity.LootContextPredicate
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
class TradePokemonCriterionCondition(id: Identifier, entity: LootContextPredicate) : SimpleCriterionCondition<TradePokemonContext>(id, entity) {
|
||||
var traded = "any"
|
||||
var received = "any"
|
||||
var tradedHeldItem = "any"
|
||||
var receivedHeldItem = "any"
|
||||
override fun toJson(json: JsonObject) {
|
||||
json.addProperty("traded", traded)
|
||||
json.addProperty("received", received)
|
||||
json.addProperty("traded_held_item", tradedHeldItem)
|
||||
json.addProperty("received_held_item", receivedHeldItem)
|
||||
}
|
||||
|
||||
override fun fromJson(json: JsonObject) {
|
||||
traded = json.get("traded")?.asString ?: "any"
|
||||
received = json.get("received")?.asString ?: "any"
|
||||
tradedHeldItem = json.get("traded_held_item")?.asString ?: "minecraft:air"
|
||||
receivedHeldItem = json.get("received_held_item")?.asString ?: "minecraft:air"
|
||||
}
|
||||
|
||||
override fun matches(player: ServerPlayerEntity, context: TradePokemonContext): Boolean {
|
||||
val heldItem1 = context.traded.heldItem().item.registryEntry.registryKey().value
|
||||
val heldItem2 = context.received.heldItem().item.registryEntry.registryKey().value
|
||||
return (context.traded.species.resourceIdentifier == traded.asIdentifierDefaultingNamespace() || traded == "any") &&
|
||||
(context.received.species.resourceIdentifier == received.asIdentifierDefaultingNamespace() || received == "any") &&
|
||||
(heldItem1 == tradedHeldItem.asIdentifierDefaultingNamespace() || heldItem1 == "minecraft:air".asIdentifierDefaultingNamespace()) &&
|
||||
(heldItem2 == receivedHeldItem.asIdentifierDefaultingNamespace() || heldItem2 == "minecraft:air".asIdentifierDefaultingNamespace())
|
||||
}
|
||||
}
|
||||
|
||||
open class TradePokemonContext(val traded: Pokemon, val received: Pokemon)
|
||||
@@ -35,7 +35,7 @@ interface SleepDepth {
|
||||
|
||||
override fun shouldWake(pokemonEntity: PokemonEntity): Boolean {
|
||||
val nearbyPlayers = pokemonEntity.world.getPlayers(TargetPredicate.createNonAttackable(), pokemonEntity, Box.of(pokemonEntity.pos, 16.0, 16.0, 16.0))
|
||||
return nearbyPlayers.none { !it.isSneaking }
|
||||
return nearbyPlayers.any { !it.isSneaking }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
@@ -10,6 +10,8 @@ package com.cobblemon.mod.common.api.battles.interpreter
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.battles.ActiveBattlePokemon
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import java.util.UUID
|
||||
@@ -194,6 +196,11 @@ class BattleMessage(rawMessage: String) {
|
||||
return Effect.parse(data)
|
||||
}
|
||||
|
||||
fun moveAt(index: Int): MoveTemplate? {
|
||||
val argument = argumentAt(index)?.lowercase()?.replace("[^a-z0-9]".toRegex(), "") ?: return null
|
||||
return Moves.getByName(argument)
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries an optional argument with given [argumentName] for a 'pnx' that will be parsed into a [BattleActor] and [ActiveBattlePokemon].
|
||||
*
|
||||
|
||||
@@ -40,6 +40,7 @@ import com.cobblemon.mod.common.net.messages.client.battle.BattleEndPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleMessagePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleMusicPacket
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.DefeatEvolutionProgress
|
||||
import com.cobblemon.mod.common.pokemon.evolution.requirements.DefeatRequirement
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import java.io.File
|
||||
@@ -208,11 +209,16 @@ open class PokemonBattle(
|
||||
faintedPokemons.forEach { faintedPokemon ->
|
||||
for (opponentPokemon in opponentNonFaintedPokemons) {
|
||||
val facedFainted = opponentPokemon.facedOpponents.contains(faintedPokemon)
|
||||
val defeatProgress = DefeatEvolutionProgress()
|
||||
val pokemon = opponentPokemon.effectedPokemon
|
||||
if (facedFainted && defeatProgress.shouldKeep(pokemon)) {
|
||||
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is DefeatEvolutionProgress && it.currentProgress().target.matches(faintedPokemon.effectedPokemon) }) { defeatProgress }
|
||||
progress.updateProgress(DefeatEvolutionProgress.Progress(progress.currentProgress().target, progress.currentProgress().amount + 1))
|
||||
if (facedFainted) {
|
||||
pokemon.evolutions.forEach { evolution ->
|
||||
evolution.requirements.filterIsInstance<DefeatRequirement>().forEach { defeatRequirement ->
|
||||
if (defeatRequirement.target.matches(faintedPokemon.effectedPokemon)) {
|
||||
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is DefeatEvolutionProgress && it.currentProgress().target == defeatRequirement.target }) { DefeatEvolutionProgress() }
|
||||
progress.updateProgress(DefeatEvolutionProgress.Progress(defeatRequirement.target, progress.currentProgress().amount + 1))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val multiplier = when {
|
||||
// ToDo when Exp. All is implement if enabled && !facedFainted return 2.0, probably should be a configurable value too, this will have priority over the Exp. Share
|
||||
|
||||
+14
-7
@@ -26,13 +26,20 @@ abstract class AIBattleActor(
|
||||
super.sendUpdate(packet)
|
||||
|
||||
if (packet is BattleMakeChoicePacket) {
|
||||
try {
|
||||
setActionResponses(request!!.iterate(this.activePokemon, battleAI::choose))
|
||||
} catch (exception: IllegalActionChoiceException) {
|
||||
LOGGER.error("AI was unable to choose a move, we're going to need to pass!")
|
||||
exception.printStackTrace()
|
||||
setActionResponses(request!!.iterate(this.activePokemon) { _, _, _ -> PassActionResponse })
|
||||
}
|
||||
this.onChoiceRequested()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the AI is requested to make a choice.
|
||||
*/
|
||||
open fun onChoiceRequested() {
|
||||
try {
|
||||
setActionResponses(request!!.iterate(this.activePokemon, battleAI::choose))
|
||||
} catch (exception: IllegalActionChoiceException) {
|
||||
LOGGER.error("AI was unable to choose a move, we're going to need to pass!")
|
||||
exception.printStackTrace()
|
||||
setActionResponses(request!!.iterate(this.activePokemon) { _, _, _ -> PassActionResponse })
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,7 @@ object Berries : JsonDataRegistry<Berry> {
|
||||
.registerTypeAdapter(MulchVariant::class.java, MulchVariantAdapter)
|
||||
.registerTypeAdapter(NumberRange.FloatRange::class.java, FloatNumberRangeAdapter)
|
||||
.registerTypeAdapter(Status::class.java, StatusAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(Collection::class.java, Box::class.java).type, BoxCollectionAdapter)
|
||||
.registerTypeAdapter(Box::class.java, BoxAdapter)
|
||||
.registerTypeAdapter(Vec3d::class.java, VerboseVec3dAdapter)
|
||||
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
|
||||
@@ -65,9 +66,6 @@ object Berries : JsonDataRegistry<Berry> {
|
||||
|
||||
private val berries = hashMapOf<Identifier, Berry>()
|
||||
|
||||
val PECHA
|
||||
get() = this.getByName("")
|
||||
|
||||
override fun reload(data: Map<Identifier, Berry>) {
|
||||
this.berries.clear()
|
||||
data.forEach { (identifier, berry) ->
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.cobblemon.mod.common.util.writeBox
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import java.awt.Color
|
||||
import java.util.EnumSet
|
||||
import kotlin.math.min
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.model.ModelPart
|
||||
@@ -159,16 +160,14 @@ class Berry(
|
||||
val base = this.baseYield.random()
|
||||
val bonus = this.bonusYield(world, state, pos)
|
||||
var yield = base + bonus.first
|
||||
val event = BerryYieldCalculationEvent(this, world, state, pos, placer, yield, bonus.second)
|
||||
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
|
||||
if (treeEntity.mulchVariant == MulchVariant.RICH) {
|
||||
yield = yield.times(1.2).coerceAtMost(maxYield().toDouble()).toInt()
|
||||
treeEntity.mulchDuration--
|
||||
if (BerryBlock.getMulch(state) == MulchVariant.RICH) {
|
||||
yield = min(yield + 1, maxYield())
|
||||
treeEntity.decrementMulchDuration(world, pos, state)
|
||||
}
|
||||
val event = BerryYieldCalculationEvent(this, world, state, pos, placer, yield, bonus.second)
|
||||
CobblemonEvents.BERRY_YIELD.post(event) { yield = it.yield }
|
||||
return yield
|
||||
//Just for testing the renderer
|
||||
//return growthPoints.size
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -293,14 +292,13 @@ class Berry(
|
||||
private fun bonusYield(world: World, state: BlockState, pos: BlockPos): Pair<Int, Collection<GrowthFactor>> {
|
||||
var bonus = 0
|
||||
val passed = arrayListOf<GrowthFactor>()
|
||||
val entity = world.getBlockEntity(pos) as BerryBlockEntity
|
||||
val hasBiomeMulch = favoriteMulches.contains(entity.mulchVariant)
|
||||
val mulchVariant = BerryBlock.getMulch(state)
|
||||
val hasBiomeMulch = mulchVariant in favoriteMulches
|
||||
this.growthFactors.forEach { factor ->
|
||||
if (factor.isValid(world, state, pos)) {
|
||||
bonus += factor.yield()
|
||||
passed += factor
|
||||
}
|
||||
else if (hasBiomeMulch) {
|
||||
} else if (hasBiomeMulch) {
|
||||
bonus += factor.yield()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,6 +121,7 @@ class PartySelectCallback(
|
||||
|
||||
open class PartySelectPokemonDTO(
|
||||
val pokemonProperties: PokemonProperties,
|
||||
val aspects: Set<String>,
|
||||
val heldItem: ItemStack = ItemStack.EMPTY,
|
||||
var currentHealth: Int,
|
||||
var maxHealth: Int,
|
||||
@@ -129,13 +130,13 @@ open class PartySelectPokemonDTO(
|
||||
@JvmOverloads
|
||||
constructor(pokemon: Pokemon, enabled: Boolean = true): this(
|
||||
pokemonProperties = pokemon.createPokemonProperties(
|
||||
PokemonPropertyExtractor.ASPECTS,
|
||||
PokemonPropertyExtractor.SPECIES,
|
||||
PokemonPropertyExtractor.LEVEL,
|
||||
PokemonPropertyExtractor.NICKNAME,
|
||||
PokemonPropertyExtractor.POKEBALL,
|
||||
PokemonPropertyExtractor.STATUS
|
||||
PokemonPropertyExtractor.STATUS,
|
||||
),
|
||||
aspects = pokemon.aspects,
|
||||
heldItem = pokemon.heldItemNoCopy(),
|
||||
currentHealth = pokemon.currentHealth,
|
||||
maxHealth = pokemon.hp,
|
||||
@@ -144,6 +145,7 @@ open class PartySelectPokemonDTO(
|
||||
|
||||
constructor(buffer: PacketByteBuf): this(
|
||||
pokemonProperties = PokemonProperties().loadFromNBT(buffer.readNbt() as NbtCompound),
|
||||
aspects = buffer.readList { it.readString() }.toSet(),
|
||||
heldItem = buffer.readItemStack(),
|
||||
currentHealth = buffer.readInt(),
|
||||
maxHealth = buffer.readInt(),
|
||||
@@ -152,6 +154,7 @@ open class PartySelectPokemonDTO(
|
||||
|
||||
fun writeToBuffer(buffer: PacketByteBuf) {
|
||||
buffer.writeNbt(pokemonProperties.saveToNBT())
|
||||
buffer.writeCollection(aspects) { _, aspect -> buffer.writeString(aspect) }
|
||||
buffer.writeItemStack(heldItem)
|
||||
buffer.writeInt(currentHealth)
|
||||
buffer.writeInt(maxHealth)
|
||||
|
||||
@@ -8,11 +8,9 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.data
|
||||
|
||||
import com.cobblemon.mod.common.util.endsWith
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import java.io.File
|
||||
import java.util.concurrent.ExecutionException
|
||||
import net.minecraft.resource.ResourceManager
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
@@ -44,20 +42,7 @@ interface JsonDataRegistry<T> : DataRegistry {
|
||||
val resourcePath: String
|
||||
|
||||
override fun reload(manager: ResourceManager) {
|
||||
val data = hashMapOf<Identifier, T>()
|
||||
manager.findResources(this.resourcePath) { path -> path.endsWith(JSON_EXTENSION) }.forEach { (identifier, resource) ->
|
||||
resource.inputStream.use { stream ->
|
||||
stream.bufferedReader().use { reader ->
|
||||
val resolvedIdentifier = Identifier(identifier.namespace, File(identifier.path).nameWithoutExtension)
|
||||
try {
|
||||
data[resolvedIdentifier] = this.gson.fromJson(reader, this.typeToken.type)
|
||||
} catch (exception: Exception) {
|
||||
throw ExecutionException("Error loading JSON for data: $identifier", exception)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.reload(data)
|
||||
this.reload(Cobblemon.implementation.reloadJsonRegistry(this, manager))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,6 +53,6 @@ interface JsonDataRegistry<T> : DataRegistry {
|
||||
fun reload(data: Map<Identifier, T>)
|
||||
|
||||
companion object {
|
||||
private const val JSON_EXTENSION = ".json"
|
||||
const val JSON_EXTENSION = ".json"
|
||||
}
|
||||
}
|
||||
@@ -10,11 +10,11 @@ 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 kotlin.random.Random
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
* A table of drops that can produce a list of [DropEntry]. You can produce a drop list from [getDrops] to
|
||||
|
||||
@@ -13,8 +13,10 @@ import com.cobblemon.mod.common.api.events.battles.BattleFledEvent
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleStartedPostEvent
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleStartedPreEvent
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleVictoryEvent
|
||||
import com.cobblemon.mod.common.api.events.berry.*
|
||||
import com.cobblemon.mod.common.api.events.battles.*
|
||||
import com.cobblemon.mod.common.api.events.berry.BerryHarvestEvent
|
||||
import com.cobblemon.mod.common.api.events.berry.BerryMutationOfferEvent
|
||||
import com.cobblemon.mod.common.api.events.berry.BerryMutationResultEvent
|
||||
import com.cobblemon.mod.common.api.events.berry.BerryYieldCalculationEvent
|
||||
import com.cobblemon.mod.common.api.events.drops.LootDroppedEvent
|
||||
import com.cobblemon.mod.common.api.events.entity.PokemonEntityLoadEvent
|
||||
import com.cobblemon.mod.common.api.events.entity.PokemonEntitySaveEvent
|
||||
@@ -29,6 +31,7 @@ import com.cobblemon.mod.common.api.events.pokemon.*
|
||||
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionAcceptedEvent
|
||||
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionCompleteEvent
|
||||
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionDisplayEvent
|
||||
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionTestedEvent
|
||||
import com.cobblemon.mod.common.api.events.pokemon.interaction.ExperienceCandyUseEvent
|
||||
import com.cobblemon.mod.common.api.events.starter.StarterChosenEvent
|
||||
import com.cobblemon.mod.common.api.events.storage.ReleasePokemonEvent
|
||||
@@ -41,6 +44,7 @@ import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
|
||||
@Suppress("unused")
|
||||
object CobblemonEvents {
|
||||
|
||||
@JvmField
|
||||
@@ -56,6 +60,8 @@ object CobblemonEvents {
|
||||
@JvmField
|
||||
val EVOLUTION_DISPLAY = EventObservable<EvolutionDisplayEvent>()
|
||||
@JvmField
|
||||
val EVOLUTION_TESTED = EventObservable<EvolutionTestedEvent>()
|
||||
@JvmField
|
||||
val EVOLUTION_COMPLETE = EventObservable<EvolutionCompleteEvent>()
|
||||
@JvmField
|
||||
val POKEMON_NICKNAMED = CancelableObservable<PokemonNicknamedEvent>()
|
||||
@@ -88,6 +94,9 @@ object CobblemonEvents {
|
||||
@JvmField
|
||||
val POKEMON_RECALLED = EventObservable<PokemonRecalledEvent>()
|
||||
|
||||
@JvmField
|
||||
val TRADE_COMPLETED = EventObservable<TradeCompletedEvent>()
|
||||
|
||||
@JvmField
|
||||
val LEVEL_UP_EVENT = EventObservable<LevelUpEvent>()
|
||||
|
||||
@@ -104,7 +113,10 @@ object CobblemonEvents {
|
||||
val POKEMON_ENTITY_SPAWN = ENTITY_SPAWN
|
||||
.pipe(
|
||||
filter { it.entity is PokemonEntity },
|
||||
map { it as SpawnEvent<PokemonEntity> }
|
||||
map {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
it as SpawnEvent<PokemonEntity>
|
||||
}
|
||||
)
|
||||
|
||||
@JvmField
|
||||
|
||||
+2
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.farming
|
||||
|
||||
import com.cobblemon.mod.common.api.apricorn.Apricorn
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
@@ -18,6 +19,7 @@ import net.minecraft.util.math.BlockPos
|
||||
*/
|
||||
class ApricornHarvestEvent(
|
||||
val player: ServerPlayerEntity,
|
||||
val apricorn: Apricorn,
|
||||
val world: ServerWorld,
|
||||
val pos: BlockPos
|
||||
) {
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* 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 com.cobblemon.mod.common.trade.TradeParticipant
|
||||
|
||||
class TradeCompletedEvent(val tradeParticipant1 : TradeParticipant, val tradeParticipant1Pokemon : Pokemon, val tradeParticipant2 : TradeParticipant, val tradeParticipant2Pokemon : Pokemon)
|
||||
+3
-3
@@ -23,6 +23,6 @@ import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
* @since April 28th, 2022
|
||||
*/
|
||||
data class EvolutionAcceptedEvent(
|
||||
val pokemon: Pokemon,
|
||||
val evolution: Evolution
|
||||
) : Cancelable()
|
||||
override val pokemon: Pokemon,
|
||||
override val evolution: Evolution
|
||||
) : Cancelable(), EvolutionEvent
|
||||
+3
-3
@@ -21,6 +21,6 @@ import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
* @since October 2nd, 2022
|
||||
*/
|
||||
data class EvolutionCompleteEvent(
|
||||
val pokemon: Pokemon,
|
||||
val evolution: Evolution
|
||||
)
|
||||
override val pokemon: Pokemon,
|
||||
override val evolution: Evolution
|
||||
) : EvolutionEvent
|
||||
+3
-3
@@ -25,7 +25,7 @@ import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
* @since May 3rd, 2022
|
||||
*/
|
||||
data class EvolutionDisplayEvent(
|
||||
val pokemon: Pokemon,
|
||||
override val pokemon: Pokemon,
|
||||
var display: EvolutionDisplay,
|
||||
val evolution: Evolution
|
||||
)
|
||||
override val evolution: Evolution
|
||||
) : EvolutionEvent
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokemon.evolution
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
|
||||
/**
|
||||
* The root of the various evolution related events.
|
||||
*/
|
||||
interface EvolutionEvent {
|
||||
|
||||
/**
|
||||
* The Pokémon impacted in this event.
|
||||
*/
|
||||
val pokemon: Pokemon
|
||||
|
||||
/**
|
||||
* The evolution triggering this event.
|
||||
*/
|
||||
val evolution: Evolution
|
||||
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokemon.evolution
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
|
||||
/**
|
||||
* An event fired when [Evolution.test] has been performed.
|
||||
* The final state [result] will be the return value of the test function.
|
||||
*
|
||||
* @property pokemon The [Pokemon] being tested against.
|
||||
* @property evolution The [Evolution] instance performing the test.
|
||||
* @property originalResult The base result from the Cobblemon implementation.
|
||||
* @property result The final value returned by [Evolution.test].
|
||||
*/
|
||||
data class EvolutionTestedEvent(
|
||||
override val pokemon: Pokemon,
|
||||
override val evolution: Evolution,
|
||||
val originalResult: Boolean,
|
||||
var result: Boolean
|
||||
) : EvolutionEvent
|
||||
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.api.text.font
|
||||
import com.cobblemon.mod.common.client.gui.battle.BattleOverlay.Companion.PORTRAIT_DIAMETER
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
@@ -121,16 +122,27 @@ fun drawText(
|
||||
y: Number,
|
||||
centered: Boolean = false,
|
||||
colour: Int,
|
||||
shadow: Boolean = true
|
||||
) {
|
||||
shadow: Boolean = true,
|
||||
pMouseX: Number? = null,
|
||||
pMouseY: Number? = null
|
||||
): Boolean {
|
||||
val comp = if (font == null) text else text.setStyle(text.style.withFont(font))
|
||||
val textRenderer = MinecraftClient.getInstance().textRenderer
|
||||
var x = x
|
||||
val width = textRenderer.getWidth(comp)
|
||||
if (centered) {
|
||||
val width = textRenderer.getWidth(comp)
|
||||
x = x.toDouble() - width / 2
|
||||
}
|
||||
context.drawText(textRenderer, comp, x.toInt(), y.toInt(), colour, shadow)
|
||||
var isHovered = false
|
||||
if (pMouseY != null && pMouseX != null) {
|
||||
if (pMouseX.toInt() >= x.toInt() && pMouseX.toInt() <= x.toInt() + width &&
|
||||
pMouseY.toInt() >= y.toInt() && pMouseY.toInt() <= y.toInt() + textRenderer.fontHeight
|
||||
) {
|
||||
isHovered = true
|
||||
}
|
||||
}
|
||||
return isHovered
|
||||
}
|
||||
|
||||
fun drawText(
|
||||
@@ -179,7 +191,11 @@ fun drawPortraitPokemon(
|
||||
partialTicks: Float
|
||||
) {
|
||||
val model = PokemonModelRepository.getPoser(species.resourceIdentifier, aspects)
|
||||
val texture = PokemonModelRepository.getTexture(species.resourceIdentifier, aspects, state)
|
||||
val texture = PokemonModelRepository.getTexture(species.resourceIdentifier, aspects, state?.animationSeconds ?: 0F)
|
||||
|
||||
val context = RenderContext()
|
||||
PokemonModelRepository.getTextureNoSubstitute(species.resourceIdentifier, aspects, 0f).let { it -> context.put(RenderContext.TEXTURE, it) }
|
||||
context.put(RenderContext.SCALE, species.getForm(aspects).baseScale)
|
||||
|
||||
val renderType = model.getLayer(texture)
|
||||
|
||||
@@ -215,11 +231,12 @@ fun drawPortraitPokemon(
|
||||
val packedLight = LightmapTextureManager.pack(11, 7)
|
||||
|
||||
model.withLayerContext(immediate, state, PokemonModelRepository.getLayers(species.resourceIdentifier, aspects)) {
|
||||
model.render(matrixStack, buffer, packedLight, OverlayTexture.DEFAULT_UV, 1F, 1F, 1F, 1F)
|
||||
model.render(context, matrixStack, buffer, packedLight, OverlayTexture.DEFAULT_UV, 1F, 1F, 1F, 1F)
|
||||
immediate.draw()
|
||||
}
|
||||
|
||||
matrixStack.pop()
|
||||
model.setDefault()
|
||||
|
||||
DiffuseLighting.enableGuiDepthLighting()
|
||||
}
|
||||
+6
-1
@@ -98,6 +98,11 @@ interface PokemonAndMoveSelectingItem {
|
||||
fun canUseOnMove(move: Move): Boolean
|
||||
|
||||
fun interactWithSpecific(player: ServerPlayerEntity, stack: ItemStack, pokemon: Pokemon): TypedActionResult<ItemStack>? {
|
||||
|
||||
if (player.isSneaking) {
|
||||
return TypedActionResult.pass(stack)
|
||||
}
|
||||
|
||||
MoveSelectCallbacks.create(
|
||||
player = player,
|
||||
moves = pokemon.moveSet.toList(),
|
||||
@@ -146,4 +151,4 @@ interface PokemonAndMoveSelectingItem {
|
||||
|
||||
return TypedActionResult.success(stack)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.item
|
||||
|
||||
import com.cobblemon.mod.common.advancement.CobblemonCriteria
|
||||
import com.cobblemon.mod.common.advancement.criterion.PokemonInteractContext
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.api.callback.PartySelectCallbacks
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
@@ -22,6 +24,7 @@ import com.cobblemon.mod.common.util.isHeld
|
||||
import com.cobblemon.mod.common.util.isLookingAt
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.registry.Registries
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.TypedActionResult
|
||||
import net.minecraft.util.math.Box
|
||||
@@ -59,15 +62,17 @@ interface PokemonSelectingItem {
|
||||
return interactWithSpecificBattle(player, stack, battlePokemon)
|
||||
}
|
||||
} ?: run {
|
||||
if (entity != null) {
|
||||
val pokemon = entity.pokemon
|
||||
if (entity.ownerUuid == player.uuid) {
|
||||
return applyToPokemon(player, stack, pokemon) ?: TypedActionResult.pass(stack)
|
||||
if (!player.isSneaking) {
|
||||
return if (entity != null) {
|
||||
val pokemon = entity.pokemon
|
||||
if (entity.ownerUuid == player.uuid) {
|
||||
applyToPokemon(player, stack, pokemon) ?: TypedActionResult.pass(stack)
|
||||
} else {
|
||||
TypedActionResult.fail(stack)
|
||||
}
|
||||
} else {
|
||||
return TypedActionResult.fail(stack)
|
||||
interactGeneral(player, stack)
|
||||
}
|
||||
} else {
|
||||
return interactGeneral(player, stack)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +94,7 @@ interface PokemonSelectingItem {
|
||||
if (!player.isCreative) {
|
||||
stack.decrement(1)
|
||||
}
|
||||
CobblemonCriteria.POKEMON_INTERACT.trigger(player, PokemonInteractContext(battlePokemon.entity!!.pokemon.species.resourceIdentifier, Registries.ITEM.getId(stack.item)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,9 +124,7 @@ interface PokemonSelectingItem {
|
||||
handler = { pk ->
|
||||
if (stack.isHeld(player)) {
|
||||
applyToPokemon(player, stack, pk)
|
||||
if (!player.isCreative) {
|
||||
stack.decrement(1)
|
||||
}
|
||||
CobblemonCriteria.POKEMON_INTERACT.trigger(player, PokemonInteractContext(pk.species.resourceIdentifier, Registries.ITEM.getId(stack.item)))
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -18,6 +18,8 @@ import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.nbt.NbtElement
|
||||
import net.minecraft.nbt.NbtList
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
import kotlin.math.min
|
||||
|
||||
class MoveSet : Iterable<Move> {
|
||||
val observable = SimpleObservable<MoveSet>()
|
||||
private var emit = true
|
||||
@@ -63,6 +65,11 @@ class MoveSet : Iterable<Move> {
|
||||
update()
|
||||
}
|
||||
|
||||
fun partialHeal() {
|
||||
getMoves().forEach { it.currentPp = min((it.currentPp + (it.maxPp / 2)), it.maxPp)}
|
||||
update()
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
doWithoutEmitting {
|
||||
for (i in 0 until MOVE_COUNT){
|
||||
|
||||
@@ -8,40 +8,22 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.mulch
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.item.MulchItem
|
||||
import net.minecraft.util.StringIdentifiable
|
||||
import java.time.Duration
|
||||
|
||||
/**
|
||||
* Represents the different types of Mulch implemented in the mod.
|
||||
*
|
||||
*/
|
||||
enum class MulchVariant(val isBiomeMulch: Boolean = true, val duration: Int = 0) : StringIdentifiable {
|
||||
enum class MulchVariant(val duration: Int = -1) : StringIdentifiable {
|
||||
COARSE,
|
||||
GROWTH(false, 3),
|
||||
GROWTH(3),
|
||||
HUMID,
|
||||
LOAMY,
|
||||
PEAT,
|
||||
RICH(false, 3),
|
||||
RICH(3),
|
||||
SANDY,
|
||||
SURPRISE(false, 3);
|
||||
|
||||
/**
|
||||
* Resolves the item version of the Mulch.
|
||||
*
|
||||
* @return The [MulchItem] equivalent.
|
||||
*/
|
||||
fun item(): MulchItem = when (this) {
|
||||
COARSE -> CobblemonItems.COARSE_MULCH
|
||||
GROWTH -> CobblemonItems.GROWTH_MULCH
|
||||
HUMID -> CobblemonItems.HUMID_MULCH
|
||||
LOAMY -> CobblemonItems.LOAMY_MULCH
|
||||
PEAT -> CobblemonItems.PEAT_MULCH
|
||||
RICH -> CobblemonItems.RICH_MULCH
|
||||
SANDY -> CobblemonItems.SANDY_MULCH
|
||||
SURPRISE -> CobblemonItems.SURPRISE_MULCH
|
||||
}
|
||||
SURPRISE(3),
|
||||
NONE;
|
||||
|
||||
override fun asString(): String {
|
||||
return name.lowercase()
|
||||
|
||||
@@ -65,6 +65,7 @@ object CobblemonPermissions {
|
||||
val TEST_PARTY_SLOT = this.create("${COMMAND_PREFIX}testpartyslot", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
val CLEAR_PARTY = this.create("${COMMAND_PREFIX}clearparty", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
val CLEAR_PC = this.create("${COMMAND_PREFIX}clearpc", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
fun all(): Iterable<Permission> = this.permissions
|
||||
|
||||
|
||||
@@ -166,6 +166,16 @@ object Natures {
|
||||
return allNatures.find { nature -> nature.name == name }
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to get a nature by string
|
||||
* @return a nature type or null
|
||||
*/
|
||||
fun getNature(identifier: String): Nature? {
|
||||
val nature = getNature(cobblemonResource(identifier))
|
||||
if(nature != null) return nature
|
||||
return getNature(Identifier(identifier))
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for a random Nature
|
||||
* @return a random nature type
|
||||
|
||||
@@ -15,16 +15,21 @@ import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.*
|
||||
import com.cobblemon.mod.common.pokemon.EVs
|
||||
import com.cobblemon.mod.common.pokemon.Gender
|
||||
import com.cobblemon.mod.common.pokemon.IVs
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.RenderablePokemon
|
||||
import com.cobblemon.mod.common.pokemon.status.PersistentStatus
|
||||
import com.cobblemon.mod.common.pokemon.status.PersistentStatusContainer
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.isInt
|
||||
import com.cobblemon.mod.common.util.splitMap
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonObject
|
||||
import kotlin.random.Random
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.nbt.NbtElement
|
||||
import net.minecraft.nbt.NbtList
|
||||
@@ -86,6 +91,10 @@ open class PokemonProperties {
|
||||
props.ability = parseStringOfRegistry(keyPairs, listOf("ability")) { Abilities.get(it)?.name }
|
||||
props.status = parseStringOfRegistry(keyPairs, listOf("status")) { (Statuses.getStatus(it) ?: Statuses.getStatus(it.asIdentifierDefaultingNamespace()))?.showdownName }
|
||||
props.nickname = parseText(keyPairs, listOf("nickname", "nick"))
|
||||
props.teraType = parseStringOfRegistry(keyPairs, listOf("tera_type", "tera")) { ElementalTypes.get(it)?.name }
|
||||
props.dmaxLevel = parseIntProperty(keyPairs, listOf("dmax_level", "dmax"))?.coerceIn(0, Cobblemon.config.maxDynamaxLevel)
|
||||
props.gmaxFactor = parseBooleanProperty(keyPairs, listOf("gmax_factor", "gmax"))
|
||||
props.tradeable = parseBooleanProperty(keyPairs, listOf("tradeable", "tradable"))
|
||||
|
||||
val maybeIVs = IVs()
|
||||
val maybeEVs = EVs()
|
||||
@@ -147,9 +156,10 @@ open class PokemonProperties {
|
||||
}
|
||||
|
||||
private fun parseSpeciesIdentifier(keyPairs: MutableList<Pair<String, String?>>): String? {
|
||||
fun cleanSpeciesName(string: String) = string.lowercase().replace("[^a-z0-9_:]".toRegex(), "")
|
||||
val matched = getMatchedKeyPair(keyPairs, listOf("species"))
|
||||
if (matched != null) {
|
||||
val value = matched.second?.lowercase() ?: return null
|
||||
val value = matched.second?.let(::cleanSpeciesName) ?: return null
|
||||
return if (value.lowercase() == "random") {
|
||||
"random"
|
||||
} else {
|
||||
@@ -168,7 +178,7 @@ open class PokemonProperties {
|
||||
"random"
|
||||
} else {
|
||||
try {
|
||||
val identifier = pair.first.asIdentifierDefaultingNamespace()
|
||||
val identifier = cleanSpeciesName(pair.first).asIdentifierDefaultingNamespace()
|
||||
val found = PokemonSpecies.getByIdentifier(identifier) ?: return@find false
|
||||
if (found.resourceIdentifier.namespace == Cobblemon.MODID) found.resourceIdentifier.path else found.resourceIdentifier.toString()
|
||||
} catch (e: InvalidIdentifierException) {
|
||||
@@ -246,6 +256,10 @@ open class PokemonProperties {
|
||||
var ability: String? = null
|
||||
var aspects: Set<String> = emptySet()
|
||||
var status: String? = null
|
||||
var teraType: String? = null
|
||||
var dmaxLevel: Int? = null
|
||||
var gmaxFactor: Boolean? = null
|
||||
var tradeable: Boolean? = null
|
||||
|
||||
var ivs: IVs? = null
|
||||
var evs: EVs? = null
|
||||
@@ -295,6 +309,11 @@ open class PokemonProperties {
|
||||
pokemon.setEV(stat.key, stat.value)
|
||||
}
|
||||
}
|
||||
teraType?.let { ElementalTypes.get(it)?.let { type -> pokemon.teraType = type } }
|
||||
dmaxLevel?.let { pokemon.dmaxLevel = it }
|
||||
gmaxFactor?.let { pokemon.gmaxFactor = it }
|
||||
tradeable?.let { pokemon.tradeable = it }
|
||||
pokemon.updateAspects()
|
||||
}
|
||||
|
||||
fun apply(pokemonEntity: PokemonEntity) {
|
||||
@@ -330,6 +349,11 @@ open class PokemonProperties {
|
||||
pokemonEntity.pokemon.setEV(stat.key, stat.value)
|
||||
}
|
||||
}
|
||||
teraType?.let { ElementalTypes.get(it)?.let { type -> pokemonEntity.pokemon.teraType = type } }
|
||||
dmaxLevel?.let { pokemonEntity.pokemon.dmaxLevel = it }
|
||||
gmaxFactor?.let { pokemonEntity.pokemon.gmaxFactor = it }
|
||||
tradeable?.let { pokemonEntity.pokemon.tradeable = it }
|
||||
pokemonEntity.pokemon.updateAspects()
|
||||
}
|
||||
|
||||
fun matches(pokemon: Pokemon): Boolean {
|
||||
@@ -363,6 +387,10 @@ open class PokemonProperties {
|
||||
evs?.forEach{ stat ->
|
||||
if (stat.value != pokemon.evs[stat.key]) { return false }
|
||||
}
|
||||
teraType?.takeIf { it != pokemon.teraType.name }?.let { return false }
|
||||
dmaxLevel?.takeIf { it != pokemon.dmaxLevel }?.let { return false }
|
||||
gmaxFactor?.takeIf { it != pokemon.gmaxFactor }?.let { return false }
|
||||
tradeable?.takeIf { it != pokemon.tradeable }?.let { return false }
|
||||
return customProperties.none { !it.matches(pokemon) }
|
||||
}
|
||||
|
||||
@@ -395,6 +423,10 @@ open class PokemonProperties {
|
||||
evs?.forEach{ stat ->
|
||||
if (stat.value != pokemonEntity.pokemon.evs[stat.key]) { return false }
|
||||
}
|
||||
teraType?.takeIf { it != pokemonEntity.pokemon.teraType.name }?.let { return false }
|
||||
dmaxLevel?.takeIf { it != pokemonEntity.pokemon.dmaxLevel }?.let { return false }
|
||||
gmaxFactor?.takeIf { it != pokemonEntity.pokemon.gmaxFactor }?.let { return false }
|
||||
tradeable?.takeIf { it != pokemonEntity.pokemon.tradeable }?.let { return false }
|
||||
return customProperties.none { !it.matches(pokemonEntity) }
|
||||
}
|
||||
|
||||
@@ -435,24 +467,33 @@ open class PokemonProperties {
|
||||
if (stat.value != propertiesEVs[stat.key]) { return false }
|
||||
}
|
||||
}
|
||||
teraType?.takeIf { it != properties.teraType }?.let { return false }
|
||||
dmaxLevel?.takeIf { it != properties.dmaxLevel }?.let { return false }
|
||||
gmaxFactor?.takeIf { it != properties.gmaxFactor }?.let { return false }
|
||||
tradeable?.takeIf { it != properties.tradeable }?.let { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
fun create(): Pokemon {
|
||||
val pokemon = Pokemon()
|
||||
apply(pokemon)
|
||||
// Force the setter to initialize it
|
||||
pokemon.species = pokemon.species
|
||||
// This should only be a thing once we have moveset control in properties until then a creation should require a moveset init.
|
||||
/*
|
||||
if (pokemon.moveSet.none { it != null }) {
|
||||
pokemon.initializeMoveset()
|
||||
}
|
||||
*/
|
||||
pokemon.initializeMoveset()
|
||||
pokemon.initialize()
|
||||
roll(pokemon)
|
||||
return pokemon
|
||||
}
|
||||
|
||||
// TEST YOUR LUCK!
|
||||
fun roll(pokemon: Pokemon) {
|
||||
val baseTypes = pokemon.species.types.toList()
|
||||
if (this.shiny == null) pokemon.shiny = Cobblemon.config.shinyRate.checkRate()
|
||||
if (this.teraType == null) pokemon.teraType =
|
||||
if (Cobblemon.config.teraTypeRate.checkRate()) ElementalTypes.all().filter { !baseTypes.contains(it) }.random()
|
||||
else baseTypes.random()
|
||||
}
|
||||
|
||||
// If the config value is at least 1, then do 1/x and use that as the property chance
|
||||
private fun Float.checkRate(): Boolean = this >= 1 && (Random.Default.nextFloat() < 1 / this)
|
||||
|
||||
fun createEntity(world: World): PokemonEntity {
|
||||
return PokemonEntity(world, create())
|
||||
}
|
||||
@@ -473,6 +514,10 @@ open class PokemonProperties {
|
||||
status?.let { nbt.putString(DataKeys.POKEMON_STATUS_NAME, it) }
|
||||
ivs?.let { nbt.put(DataKeys.POKEMON_IVS, it.saveToNBT(NbtCompound())) }
|
||||
evs?.let { nbt.put(DataKeys.POKEMON_EVS, it.saveToNBT(NbtCompound())) }
|
||||
teraType?.let { nbt.putString(DataKeys.POKEMON_TERA_TYPE, it) }
|
||||
dmaxLevel?.let { nbt.putInt(DataKeys.POKEMON_DMAX_LEVEL, it) }
|
||||
gmaxFactor?.let { nbt.putBoolean(DataKeys.POKEMON_GMAX_FACTOR, it) }
|
||||
tradeable?.let { nbt.putBoolean(DataKeys.POKEMON_TRADEABLE, it) }
|
||||
val custom = NbtList()
|
||||
customProperties.map { NbtString.of(it.asString()) }.forEach { custom.add(it) }
|
||||
nbt.put(DataKeys.POKEMON_PROPERTIES_CUSTOM, custom)
|
||||
@@ -494,6 +539,10 @@ open class PokemonProperties {
|
||||
status = if (tag.contains(DataKeys.POKEMON_STATUS_NAME)) tag.getString(DataKeys.POKEMON_STATUS_NAME) else null
|
||||
ivs = if (tag.contains(DataKeys.POKEMON_IVS)) ivs?.loadFromNBT(tag.getCompound(DataKeys.POKEMON_IVS)) as IVs? else null
|
||||
evs = if (tag.contains(DataKeys.POKEMON_EVS)) evs?.loadFromNBT(tag.getCompound(DataKeys.POKEMON_EVS)) as EVs? else null
|
||||
teraType = if (tag.contains(DataKeys.POKEMON_TERA_TYPE)) tag.getString(DataKeys.POKEMON_TERA_TYPE) else null
|
||||
dmaxLevel = if (tag.contains(DataKeys.POKEMON_DMAX_LEVEL)) tag.getInt(DataKeys.POKEMON_DMAX_LEVEL) else null
|
||||
gmaxFactor = if (tag.contains(DataKeys.POKEMON_GMAX_FACTOR)) tag.getBoolean(DataKeys.POKEMON_GMAX_FACTOR) else null
|
||||
tradeable = if (tag.contains(DataKeys.POKEMON_TRADEABLE)) tag.getBoolean(DataKeys.POKEMON_TRADEABLE) else null
|
||||
val custom = tag.getList(DataKeys.POKEMON_PROPERTIES_CUSTOM, NbtElement.STRING_TYPE.toInt())
|
||||
// This is kinda gross
|
||||
custom.forEach { customProperties.addAll(parse(it.asString()).customProperties) }
|
||||
@@ -517,6 +566,10 @@ open class PokemonProperties {
|
||||
status?.let { json.addProperty(DataKeys.POKEMON_STATUS_NAME, it) }
|
||||
ivs?.let { json.add(DataKeys.POKEMON_IVS, it.saveToJSON(JsonObject())) }
|
||||
evs?.let { json.add(DataKeys.POKEMON_EVS, it.saveToJSON(JsonObject())) }
|
||||
teraType?.let { json.addProperty(DataKeys.POKEMON_TERA_TYPE, it) }
|
||||
dmaxLevel?.let { json.addProperty(DataKeys.POKEMON_DMAX_LEVEL, it) }
|
||||
gmaxFactor?.let { json.addProperty(DataKeys.POKEMON_GMAX_FACTOR, it) }
|
||||
tradeable?.let { json.addProperty(DataKeys.POKEMON_TRADEABLE, it) }
|
||||
val custom = JsonArray()
|
||||
customProperties.map { it.asString() }.forEach { custom.add(it) }
|
||||
json.add(DataKeys.POKEMON_PROPERTIES_CUSTOM, custom)
|
||||
@@ -539,6 +592,10 @@ open class PokemonProperties {
|
||||
status = json.get(DataKeys.POKEMON_STATUS_NAME)?.asString
|
||||
ivs?.loadFromJSON(json.getAsJsonObject(DataKeys.POKEMON_IVS))
|
||||
evs?.loadFromJSON(json.getAsJsonObject(DataKeys.POKEMON_EVS))
|
||||
teraType = json.get(DataKeys.POKEMON_TERA_TYPE)?.asString
|
||||
dmaxLevel = json.get(DataKeys.POKEMON_DMAX_LEVEL)?.asInt
|
||||
gmaxFactor = json.get(DataKeys.POKEMON_GMAX_FACTOR)?.asBoolean
|
||||
tradeable = json.get(DataKeys.POKEMON_TRADEABLE)?.asBoolean
|
||||
val custom = json.get(DataKeys.POKEMON_PROPERTIES_CUSTOM)?.asJsonArray
|
||||
// This is still kinda gross
|
||||
custom?.forEach { customProperties.addAll(parse(it.asString).customProperties) }
|
||||
@@ -565,6 +622,10 @@ open class PokemonProperties {
|
||||
evs?.forEach{ stat ->
|
||||
pieces.add("${stat.key}_ev=${stat.value}")
|
||||
}
|
||||
teraType?.let { pieces.add("tera_type=$it") }
|
||||
dmaxLevel?.let { pieces.add("dmax_level=$it") }
|
||||
gmaxFactor?.let { pieces.add("gmax_factor=$it") }
|
||||
tradeable?.let { pieces.add("tradeable=$it") }
|
||||
customProperties.forEach { pieces.add(it.asString()) }
|
||||
return pieces.joinToString(separator)
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@ import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.spawning.TimeRange
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.api.types.adapters.ElementalTypeAdapter
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
@@ -42,7 +41,8 @@ import com.cobblemon.mod.common.pokemon.SpeciesAdditions
|
||||
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonEvolutionAdapter
|
||||
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonPreEvolutionAdapter
|
||||
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonRequirementAdapter
|
||||
import com.cobblemon.mod.common.pokemon.helditem.CobblemonEmptyHeldItemManager
|
||||
import com.cobblemon.mod.common.pokemon.evolution.adapters.NbtItemPredicateAdapter
|
||||
import com.cobblemon.mod.common.pokemon.evolution.predicate.NbtItemPredicate
|
||||
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
|
||||
import com.cobblemon.mod.common.util.adapters.*
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
@@ -95,6 +95,7 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, ItemLikeConditionAdapter)
|
||||
.registerTypeAdapter(EggGroup::class.java, EggGroupAdapter)
|
||||
.registerTypeAdapter(StatusEffect::class.java, RegistryElementAdapter<StatusEffect> { Registries.STATUS_EFFECT })
|
||||
.registerTypeAdapter(NbtItemPredicate::class.java, NbtItemPredicateAdapter)
|
||||
.disableHtmlEscaping()
|
||||
.enableComplexMapKeySerialization()
|
||||
.create()
|
||||
@@ -243,6 +244,9 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
val cannotDynamax = form?.dynamaxBlocked ?: species.dynamaxBlocked
|
||||
// ToDo battleOnly
|
||||
// ToDo changesFrom
|
||||
val requiredMove = form?.requiredMove
|
||||
val requiredItem = form?.requiredItem
|
||||
val requiredItems = form?.requiredItems
|
||||
}
|
||||
|
||||
private fun createShowdownName(species: Species): String {
|
||||
|
||||
+15
-1
@@ -10,11 +10,14 @@ package com.cobblemon.mod.common.api.pokemon.effect.adapter
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.effect.ShoulderEffect
|
||||
import com.cobblemon.mod.common.api.pokemon.effect.ShoulderEffectRegistry
|
||||
import com.cobblemon.mod.common.pokemon.effects.PotionBaseEffect
|
||||
import com.google.gson.JsonDeserializationContext
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import java.lang.reflect.Type
|
||||
import net.minecraft.registry.Registries
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
object ShoulderEffectAdapter: JsonDeserializer<ShoulderEffect> {
|
||||
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): ShoulderEffect {
|
||||
@@ -23,7 +26,18 @@ object ShoulderEffectAdapter: JsonDeserializer<ShoulderEffect> {
|
||||
} else {
|
||||
json.asJsonObject.get("type").asString to json.asJsonObject
|
||||
}
|
||||
val effect = ShoulderEffectRegistry.get(typeId) ?: throw IllegalArgumentException("Cannot find shoulder effect with type '$typeId'")
|
||||
val effect = ShoulderEffectRegistry.get(typeId) ?: run {
|
||||
try {
|
||||
val effectId = Identifier(typeId.replace("-", "_").replace("slow_fall", "slow_falling"))
|
||||
val registry = Registries.STATUS_EFFECT
|
||||
val effect = registry.get(effectId)
|
||||
if (effect != null) {
|
||||
return PotionBaseEffect(effect, 0, true, false, false)
|
||||
}
|
||||
} catch (_: Exception) {}
|
||||
|
||||
throw IllegalArgumentException("Cannot find shoulder effect with type '$typeId'")
|
||||
}
|
||||
return context.deserialize(obj, effect)
|
||||
}
|
||||
}
|
||||
@@ -9,9 +9,9 @@
|
||||
package com.cobblemon.mod.common.api.pokemon.evolution
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.abilities.AbilityPool
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionCompleteEvent
|
||||
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionTestedEvent
|
||||
import com.cobblemon.mod.common.api.moves.BenchedMove
|
||||
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
@@ -67,7 +67,12 @@ interface Evolution : EvolutionLike {
|
||||
* @param pokemon The [Pokemon] being queried.
|
||||
* @return If the [Evolution] can start.
|
||||
*/
|
||||
fun test(pokemon: Pokemon) = this.requirements.all { requirement -> requirement.check(pokemon) }
|
||||
fun test(pokemon: Pokemon): Boolean {
|
||||
val result = this.requirements.all { requirement -> requirement.check(pokemon) }
|
||||
val event = EvolutionTestedEvent(pokemon, this, result, result)
|
||||
CobblemonEvents.EVOLUTION_TESTED.post(event)
|
||||
return event.result
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts this evolution or queues it if [optional] is true.
|
||||
|
||||
+2
-4
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.api.spawning
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.Cobblemon.config
|
||||
import com.cobblemon.mod.common.api.spawning.mixins.CachedOnlyChunkAccessor
|
||||
import com.cobblemon.mod.common.api.spawning.prospecting.SpawningProspector
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.SpawningArea
|
||||
@@ -81,8 +80,7 @@ object CobblemonSpawningProspector : SpawningProspector {
|
||||
for (x in area.baseX until area.baseX + area.length) {
|
||||
for (z in area.baseZ until area.baseZ + area.width) {
|
||||
val query = chunks.computeIfAbsent(Pair(getSectionCoord(x), getSectionCoord(z))) {
|
||||
val manager = world.chunkManager as CachedOnlyChunkAccessor
|
||||
manager.`cobblemon$request`(it.first, it.second, ChunkStatus.FULL)
|
||||
world.getChunk(it.first, it.second, ChunkStatus.FULL, false)
|
||||
} ?: continue
|
||||
|
||||
var canSeeSky = world.isSkyVisibleAllowingSea(pos.set(x, yRange.first, z))
|
||||
@@ -90,7 +88,7 @@ object CobblemonSpawningProspector : SpawningProspector {
|
||||
val state = query.getBlockState(pos.set(x, y, z))
|
||||
blocks[x - area.baseX][y - baseY][z - area.baseZ] = WorldSlice.BlockData(
|
||||
state = state,
|
||||
light = state.getOpacity(world, pos)
|
||||
light = world.getLightLevel(pos)
|
||||
)
|
||||
|
||||
if (canSeeSky) {
|
||||
|
||||
-6
@@ -14,9 +14,7 @@ import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.feature.SeasonFeatureHandler
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import com.cobblemon.mod.common.util.weightedSelection
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
* A [SpawnAction] that will spawn a single [PokemonEntity].
|
||||
@@ -35,10 +33,6 @@ class PokemonSpawnAction(
|
||||
if (props.level == null) {
|
||||
props.level = detail.getDerivedLevelRange().random()
|
||||
}
|
||||
if (props.shiny == null) {
|
||||
// If the config value is at least 1, then do 1/x and use that as the shiny chance
|
||||
props.shiny = Cobblemon.config.shinyRate.takeIf { it >= 1 }?.let { Random.Default.nextFloat() < 1 / it }
|
||||
}
|
||||
val heldItems = detail.heldItems?.takeIf { it.isNotEmpty() }?.toMutableList() ?: mutableListOf()
|
||||
val heldItem = if (heldItems.isNotEmpty()) {
|
||||
val until100 = 1 - heldItems.sumOf { it.percentage / 100 }
|
||||
|
||||
+3
@@ -17,6 +17,7 @@ import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlin.math.ceil
|
||||
@@ -48,6 +49,8 @@ class PokemonSpawnDetail : SpawnDetail() {
|
||||
|
||||
|
||||
override fun getName(): MutableText {
|
||||
displayName?.let { return it.asTranslated() }
|
||||
|
||||
val speciesString = pokemon.species
|
||||
if (speciesString != null) {
|
||||
if (speciesString.lowercase() == "random") {
|
||||
|
||||
@@ -28,13 +28,12 @@ abstract class SpawnAction<T : Entity>(
|
||||
) {
|
||||
abstract fun createEntity(): T?
|
||||
|
||||
fun run() {
|
||||
open fun run() {
|
||||
ctx.influences.forEach { it.affectAction(this) }
|
||||
val e = createEntity() ?: return
|
||||
e.setPosition(ctx.position.toVec3d().add(0.5, 1.0, 0.5))
|
||||
entity.emit(e)
|
||||
CobblemonEvents.ENTITY_SPAWN.post(SpawnEvent(e, ctx))
|
||||
ctx.world.spawnEntity(e)
|
||||
CobblemonEvents.ENTITY_SPAWN.postThen(SpawnEvent(e, ctx), ifSucceeded = { ctx.world.spawnEntity(e) })
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,4 +46,4 @@ abstract class SpawnAction<T : Entity>(
|
||||
val entity: SimpleObservable<T> = SimpleObservable<T>().apply {
|
||||
subscribe { entity -> ctx.afterSpawn(entity) }
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
-3
@@ -17,9 +17,9 @@ import com.cobblemon.mod.common.api.spawning.context.calculators.AreaSpawningCon
|
||||
import com.cobblemon.mod.common.api.spawning.context.calculators.SpawningContextCalculator.Companion.prioritizedAreaCalculators
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
|
||||
import com.cobblemon.mod.common.api.spawning.mixins.CachedOnlyChunkAccessor
|
||||
import com.cobblemon.mod.common.api.spawning.prospecting.SpawningProspector
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.isBoxLoaded
|
||||
import com.cobblemon.mod.common.util.squeezeWithinBounds
|
||||
import com.cobblemon.mod.common.util.toVec3f
|
||||
import net.minecraft.entity.ai.pathing.NavigationType
|
||||
@@ -31,6 +31,7 @@ import net.minecraft.world.World
|
||||
import net.minecraft.world.chunk.Chunk
|
||||
import net.minecraft.world.chunk.ChunkStatus
|
||||
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules.DO_POKEMON_SPAWNING
|
||||
import net.minecraft.util.math.ChunkPos
|
||||
|
||||
/**
|
||||
* A type of [TickingSpawner] that operates within some area. When this spawner type
|
||||
@@ -59,6 +60,7 @@ abstract class AreaSpawner(
|
||||
var resolver: AreaContextResolver = Cobblemon.areaContextResolver
|
||||
var contextCalculators: List<AreaSpawningContextCalculator<*>> = prioritizedAreaCalculators
|
||||
|
||||
|
||||
override fun run(cause: SpawnCause): Pair<SpawningContext, SpawnDetail>? {
|
||||
val area = getArea(cause)
|
||||
if (area?.world?.gameRules?.getBoolean(DO_POKEMON_SPAWNING) == false) {
|
||||
@@ -66,9 +68,15 @@ abstract class AreaSpawner(
|
||||
}
|
||||
val constrainedArea = if (area != null) constrainArea(area) else null
|
||||
if (constrainedArea != null) {
|
||||
|
||||
val areaBox = Box.of(Vec3d(constrainedArea.getCenter().toVec3f()), CHUNK_REACH * 16.0 * 2, 1000.0, CHUNK_REACH * 16.0 * 2)
|
||||
if (!constrainedArea.world.isBoxLoaded(areaBox)) {
|
||||
return null
|
||||
}
|
||||
|
||||
val numberNearby = constrainedArea.world.getEntitiesByClass(
|
||||
PokemonEntity::class.java,
|
||||
Box.of(Vec3d(constrainedArea.getCenter().toVec3f()), CHUNK_REACH * 16.0 * 2, 1000.0, CHUNK_REACH * 16.0 * 2)
|
||||
areaBox
|
||||
) { true }.size
|
||||
|
||||
val chunksCovered = CHUNK_REACH * CHUNK_REACH
|
||||
@@ -117,7 +125,11 @@ abstract class AreaSpawner(
|
||||
val originalY = area.baseY
|
||||
|
||||
val (chunkX, chunkZ) = Pair(ChunkSectionPos.getSectionCoord(area.baseX), ChunkSectionPos.getSectionCoord(area.baseZ))
|
||||
val chunk = (area.world.chunkManager as CachedOnlyChunkAccessor).`cobblemon$request`(chunkX, chunkZ, ChunkStatus.FULL) ?: return null
|
||||
|
||||
// if the chunk isn't loaded, we don't want to go further & we don't want the getChunk function below to load/create the chunk.
|
||||
if (!area.world.isChunkLoaded(ChunkPos.toLong(chunkX, chunkZ))) return null
|
||||
|
||||
val chunk = area.world.getChunk(chunkX, chunkZ, ChunkStatus.FULL) ?: return null
|
||||
|
||||
var valid = isValidStartPoint(area.world, chunk, basePos)
|
||||
|
||||
|
||||
@@ -32,12 +32,12 @@ open class BottomlessStore(override val uuid: UUID) : PokemonStore<BottomlessPos
|
||||
override fun iterator() = pokemon.iterator()
|
||||
|
||||
override fun get(position: BottomlessPosition) = position.currentIndex
|
||||
.takeIf { it < pokemon.size && it >= 0 }
|
||||
.takeIf { it in pokemon.indices }
|
||||
?.let { pokemon[it] }
|
||||
|
||||
override fun getFirstAvailablePosition() = BottomlessPosition(pokemon.size)
|
||||
override fun isValidPosition(position: BottomlessPosition) = position.currentIndex >= 0
|
||||
operator fun get(index: Int) = index.takeIf { it >= 0 && it < pokemon.size }?.let { pokemon[it] }
|
||||
operator fun get(index: Int) = index.takeIf { it in pokemon.indices }?.let { pokemon[it] }
|
||||
override fun getObservingPlayers() = emptySet<ServerPlayerEntity>()
|
||||
override fun sendTo(player: ServerPlayerEntity) {}
|
||||
|
||||
@@ -58,7 +58,12 @@ open class BottomlessStore(override val uuid: UUID) : PokemonStore<BottomlessPos
|
||||
override fun loadFromNBT(nbt: NbtCompound): BottomlessStore {
|
||||
var i = -1
|
||||
while (nbt.contains(DataKeys.STORE_SLOT + ++i)) {
|
||||
pokemon.add(Pokemon().loadFromNBT(nbt.getCompound(DataKeys.STORE_SLOT + i)))
|
||||
val pokemonNBT = nbt.getCompound(DataKeys.STORE_SLOT + i)
|
||||
try {
|
||||
pokemon.add(Pokemon().loadFromNBT(pokemonNBT))
|
||||
} catch(_: InvalidSpeciesException) {
|
||||
handleInvalidSpeciesNBT(pokemonNBT)
|
||||
}
|
||||
}
|
||||
return this
|
||||
}
|
||||
@@ -71,7 +76,12 @@ open class BottomlessStore(override val uuid: UUID) : PokemonStore<BottomlessPos
|
||||
override fun loadFromJSON(json: JsonObject): BottomlessStore {
|
||||
var i = -1
|
||||
while (json.has(DataKeys.STORE_SLOT + ++i)) {
|
||||
pokemon.add(Pokemon().loadFromJSON(json.getAsJsonObject(DataKeys.STORE_SLOT + i)))
|
||||
val pokemonJSON = json.getAsJsonObject(DataKeys.STORE_SLOT + i)
|
||||
try {
|
||||
pokemon.add(Pokemon().loadFromJSON(pokemonJSON))
|
||||
} catch (_: InvalidSpeciesException) {
|
||||
handleInvalidSpeciesJSON(pokemonJSON)
|
||||
}
|
||||
}
|
||||
return this
|
||||
}
|
||||
@@ -92,11 +102,16 @@ open class BottomlessStore(override val uuid: UUID) : PokemonStore<BottomlessPos
|
||||
this.pokemon.add(pokemon)
|
||||
storeChangeObservable.emit(Unit)
|
||||
} else if (position.currentIndex in 0 until this.pokemon.size) {
|
||||
var startIndex = position.currentIndex;
|
||||
if (pokemon != null) {
|
||||
this.pokemon.add(position.currentIndex, pokemon)
|
||||
startIndex += 1;
|
||||
} else {
|
||||
this.pokemon.removeAt(position.currentIndex)
|
||||
}
|
||||
for(i in startIndex until this.pokemon.size) {
|
||||
this.pokemon[i].storeCoordinates.set(StoreCoordinates(this, BottomlessPosition(i)))
|
||||
}
|
||||
storeChangeObservable.emit(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
+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.storage
|
||||
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
/**
|
||||
* An exception when trying to deserialize a Pokemon instance with an identifier that doesn't seem to be in the registry.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since October 9th, 2023
|
||||
*/
|
||||
class InvalidSpeciesException(val identifier: Identifier) : IllegalStateException("Invalid species: $identifier")
|
||||
@@ -146,8 +146,10 @@ abstract class PokemonStore<T : StorePosition> : Iterable<Pokemon> {
|
||||
|
||||
operator fun get(uuid: UUID) = find { it.uuid == uuid }
|
||||
|
||||
open fun handleInvalidSpeciesNBT(nbt: NbtCompound) {}
|
||||
abstract fun saveToNBT(nbt: NbtCompound): NbtCompound
|
||||
abstract fun loadFromNBT(nbt: NbtCompound): PokemonStore<T>
|
||||
open fun handleInvalidSpeciesJSON(json: JsonObject) {}
|
||||
abstract fun saveToJSON(json: JsonObject): JsonObject
|
||||
abstract fun loadFromJSON(json: JsonObject): PokemonStore<T>
|
||||
abstract fun savePositionToNBT(position: T, nbt: NbtCompound)
|
||||
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.storage.adapter.database
|
||||
|
||||
import com.cobblemon.mod.common.api.storage.PokemonStore
|
||||
import com.cobblemon.mod.common.api.storage.StorePosition
|
||||
import com.cobblemon.mod.common.api.storage.adapter.CobblemonAdapterParent
|
||||
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
|
||||
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore
|
||||
import com.cobblemon.mod.common.api.storage.pc.PCStore
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonObject
|
||||
import com.mongodb.client.MongoClient
|
||||
import com.mongodb.client.MongoCollection
|
||||
import com.mongodb.client.model.ReplaceOptions
|
||||
import net.minecraft.util.WorldSavePath
|
||||
import org.bson.Document
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* A [FileStoreAdapter] for MongoDB. This allows a [PokemonStore] to be serialized to a MongoDB database.
|
||||
*
|
||||
* @author Pebbles
|
||||
* @since August 23rd, 2023
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
open class MongoDBStoreAdapter(
|
||||
protected val mongoClient: MongoClient,
|
||||
protected val databaseName: String,
|
||||
) : CobblemonAdapterParent<JsonObject>(), FileStoreAdapter<JsonObject> {
|
||||
|
||||
protected val gson: Gson = this.createGson()
|
||||
|
||||
override fun <E : StorePosition, T : PokemonStore<E>> serialize(store: T): JsonObject = store.saveToJSON(JsonObject())
|
||||
|
||||
override fun save(storeClass: Class<out PokemonStore<*>>, uuid: UUID, serialized: JsonObject) {
|
||||
val document = Document.parse(this.gson.toJson(serialized))
|
||||
document["uuid"] = uuid.toString()
|
||||
val collection = getCollection(storeClass)
|
||||
val filter = Document("uuid", uuid.toString())
|
||||
collection.replaceOne(filter, document, ReplaceOptions().upsert(true))
|
||||
}
|
||||
|
||||
override fun <E : StorePosition, T : PokemonStore<E>> provide(storeClass: Class<T>, uuid: UUID): T? {
|
||||
val server = server()!!
|
||||
val pokemonStoreRoot = server.getSavePath(WorldSavePath.ROOT).resolve("pokemon").toFile()
|
||||
val jsonAdapter = JSONStoreAdapter(
|
||||
pokemonStoreRoot.absolutePath,
|
||||
useNestedFolders = true,
|
||||
folderPerClass = true
|
||||
)
|
||||
val nbtAdapter = NBTStoreAdapter(pokemonStoreRoot.absolutePath, useNestedFolders = true, folderPerClass = true)
|
||||
|
||||
// 1. Check if data exists in MongoDB
|
||||
val collection = getCollection(storeClass)
|
||||
val filter = Document("uuid", uuid.toString())
|
||||
val document = collection.find(filter).first()
|
||||
|
||||
if (document != null) {
|
||||
val json = this.gson.fromJson(document.toJson(), JsonObject::class.java)
|
||||
val store = storeClass.getConstructor(UUID::class.java).newInstance(uuid)
|
||||
store.loadFromJSON(json)
|
||||
return store
|
||||
}
|
||||
|
||||
// 2. Fallback to checking JSON and NBT
|
||||
val nbtStore = nbtAdapter.provide(storeClass, uuid)
|
||||
if (nbtStore != null) {
|
||||
save(storeClass, uuid, serialize(nbtStore))
|
||||
return nbtStore
|
||||
}
|
||||
|
||||
val jsonStore = jsonAdapter.provide(storeClass, uuid)
|
||||
if (jsonStore != null) {
|
||||
save(storeClass, uuid, serialize(jsonStore))
|
||||
return jsonStore
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
protected open fun createGson(): Gson = Gson()
|
||||
|
||||
protected open fun getCollection(storeClass: Class<out PokemonStore<*>>): MongoCollection<Document> {
|
||||
val collectionName = when (storeClass) {
|
||||
PlayerPartyStore::class.java -> "PlayerPartyCollection"
|
||||
PCStore::class.java -> "PCCollection"
|
||||
else -> "OtherCollection"
|
||||
}
|
||||
return this.mongoClient.getDatabase(this.databaseName).getCollection(collectionName)
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.storage.adapter.flatifle
|
||||
package com.cobblemon.mod.common.api.storage.adapter.flatfile
|
||||
|
||||
import com.cobblemon.mod.common.api.storage.PokemonStore
|
||||
import com.cobblemon.mod.common.api.storage.StorePosition
|
||||
+6
-2
@@ -6,7 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.storage.adapter.flatifle
|
||||
package com.cobblemon.mod.common.api.storage.adapter.flatfile
|
||||
|
||||
import com.cobblemon.mod.common.api.storage.PokemonStore
|
||||
import com.cobblemon.mod.common.api.storage.StorePosition
|
||||
@@ -48,7 +48,11 @@ open class JSONStoreAdapter(
|
||||
val br = BufferedReader(FileReader(file))
|
||||
val json = gson.fromJson<JsonObject>(br)
|
||||
br.close()
|
||||
val store = storeClass.getConstructor(UUID::class.java).newInstance(uuid)
|
||||
val store = try {
|
||||
storeClass.getConstructor(UUID::class.java, UUID::class.java).newInstance(uuid, uuid)
|
||||
} catch (exception: NoSuchMethodException) {
|
||||
storeClass.getConstructor(UUID::class.java).newInstance(uuid)
|
||||
}
|
||||
store.loadFromJSON(json)
|
||||
store
|
||||
} catch (e: Exception) {
|
||||
+7
-2
@@ -6,7 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.storage.adapter.flatifle
|
||||
package com.cobblemon.mod.common.api.storage.adapter.flatfile
|
||||
|
||||
import com.cobblemon.mod.common.api.storage.PokemonStore
|
||||
import com.cobblemon.mod.common.api.storage.StorePosition
|
||||
@@ -31,7 +31,12 @@ open class NBTStoreAdapter(
|
||||
override fun <E : StorePosition, T : PokemonStore<E>> serialize(store: T) = store.saveToNBT(NbtCompound())
|
||||
override fun save(file: File, serialized: NbtCompound) = NbtIo.writeCompressed(serialized, file)
|
||||
override fun <E, T : PokemonStore<E>> load(file: File, storeClass: Class<out T>, uuid: UUID): T? {
|
||||
val store = storeClass.getConstructor(UUID::class.java).newInstance(uuid)
|
||||
val store = try {
|
||||
storeClass.getConstructor(UUID::class.java, UUID::class.java).newInstance(uuid, uuid)
|
||||
} catch (exception: NoSuchMethodException) {
|
||||
storeClass.getConstructor(UUID::class.java).newInstance(uuid)
|
||||
}
|
||||
|
||||
return try {
|
||||
val nbt = NbtIo.readCompressed(file)
|
||||
store.loadFromNBT(nbt)
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.storage.adapter.flatifle
|
||||
package com.cobblemon.mod.common.api.storage.adapter.flatfile
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.api.storage.PokemonStore
|
||||
+2
-2
@@ -14,7 +14,7 @@ import com.cobblemon.mod.common.api.reactive.Observable.Companion.emitWhile
|
||||
import com.cobblemon.mod.common.api.storage.PokemonStore
|
||||
import com.cobblemon.mod.common.api.storage.StorePosition
|
||||
import com.cobblemon.mod.common.api.storage.adapter.SerializedStore
|
||||
import com.cobblemon.mod.common.api.storage.adapter.flatifle.FileStoreAdapter
|
||||
import com.cobblemon.mod.common.api.storage.adapter.flatfile.FileStoreAdapter
|
||||
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore
|
||||
import com.cobblemon.mod.common.api.storage.pc.PCStore
|
||||
import com.cobblemon.mod.common.platform.events.PlatformEvents
|
||||
@@ -123,7 +123,7 @@ open class FileBackedPokemonStoreFactory<S>(
|
||||
}
|
||||
|
||||
override fun onPlayerDisconnect(playerID: UUID) {
|
||||
dirtyStores.find { it.uuid == playerID }?.let { save(it) }
|
||||
dirtyStores.filter { it.uuid == playerID }.forEach(::save)
|
||||
storeCaches.forEach { (_, cache) -> cache.cacheMap.remove(playerID) }
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.reactive.Observable
|
||||
import com.cobblemon.mod.common.api.reactive.Observable.Companion.stopAfter
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.storage.InvalidSpeciesException
|
||||
import com.cobblemon.mod.common.api.storage.PokemonStore
|
||||
import com.cobblemon.mod.common.api.storage.StoreCoordinates
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
@@ -168,8 +169,12 @@ open class PartyStore(override val uuid: UUID) : PokemonStore<PartyPosition>() {
|
||||
while (slotCount < slots.size) { slots.add(null) }
|
||||
for (slot in slots.indices) {
|
||||
val pokemonNBT = nbt.getCompound(DataKeys.STORE_SLOT + slot)
|
||||
if (!pokemonNBT.isEmpty) {
|
||||
slots[slot] = Pokemon().loadFromNBT(pokemonNBT)
|
||||
try {
|
||||
if (!pokemonNBT.isEmpty) {
|
||||
slots[slot] = Pokemon().loadFromNBT(pokemonNBT)
|
||||
}
|
||||
} catch (_: InvalidSpeciesException) {
|
||||
handleInvalidSpeciesNBT(pokemonNBT)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,7 +201,12 @@ open class PartyStore(override val uuid: UUID) : PokemonStore<PartyPosition>() {
|
||||
for (slot in slots.indices) {
|
||||
val key = DataKeys.STORE_SLOT + slot
|
||||
if (json.has(key)) {
|
||||
slots[slot] = Pokemon().loadFromJSON(json.get(key).asJsonObject)
|
||||
val pokemonJSON = json.get(key).asJsonObject
|
||||
try {
|
||||
slots[slot] = Pokemon().loadFromJSON(pokemonJSON)
|
||||
} catch (_: InvalidSpeciesException) {
|
||||
handleInvalidSpeciesJSON(pokemonJSON)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+37
-5
@@ -9,6 +9,8 @@
|
||||
package com.cobblemon.mod.common.api.storage.party
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.advancement.CobblemonCriteria
|
||||
import com.cobblemon.mod.common.advancement.criterion.PartyCheckContext
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.PassiveEvolution
|
||||
import com.cobblemon.mod.common.api.storage.pc.PCStore
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
@@ -18,13 +20,13 @@ import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import com.cobblemon.mod.common.util.isPokemonEntity
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import java.util.UUID
|
||||
import kotlin.math.round
|
||||
import kotlin.random.Random
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.text.Text
|
||||
import java.util.*
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.round
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
* A [PartyStore] used for a single player. This uses the player's UUID as the store's UUID, and is declared as its own
|
||||
@@ -36,8 +38,10 @@ import kotlin.math.ceil
|
||||
*/
|
||||
open class PlayerPartyStore(
|
||||
/** The UUID of the player this store is for. */
|
||||
val playerUUID: UUID
|
||||
) : PartyStore(playerUUID) {
|
||||
val playerUUID: UUID,
|
||||
/** The UUID of the store. This is the same as [playerUUID] by default, but can be changed to allow for multiple parties. */
|
||||
storageUUID: UUID = playerUUID
|
||||
) : PartyStore(storageUUID) {
|
||||
|
||||
private var secondsSinceFriendshipUpdate = 0
|
||||
|
||||
@@ -52,6 +56,7 @@ open class PlayerPartyStore(
|
||||
|
||||
override fun add(pokemon: Pokemon): Boolean {
|
||||
return if (super.add(pokemon)) {
|
||||
pokemon.getOwnerPlayer()?.let { CobblemonCriteria.PARTY_CHECK.trigger(it, PartyCheckContext(this)) }
|
||||
true
|
||||
} else {
|
||||
val player = playerUUID.getPlayer()
|
||||
@@ -151,4 +156,31 @@ open class PlayerPartyStore(
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun swap(position1: PartyPosition, position2: PartyPosition) {
|
||||
super.swap(position1, position2)
|
||||
|
||||
//Make it so we can check what's in the Player's party
|
||||
val pokemon1 = get(position1)
|
||||
val pokemon2 = get(position2)
|
||||
if (pokemon1 != null && pokemon2 != null) {
|
||||
val player = pokemon1.getOwnerPlayer()
|
||||
if (player != null) {
|
||||
CobblemonCriteria.PARTY_CHECK.trigger(player, PartyCheckContext(this))
|
||||
}
|
||||
} else if (pokemon1 != null || pokemon2 != null) {
|
||||
var player = pokemon1?.getOwnerPlayer()
|
||||
if (player != null) {
|
||||
CobblemonCriteria.PARTY_CHECK.trigger(player, PartyCheckContext(this))
|
||||
} else {
|
||||
player = pokemon2!!.getOwnerPlayer()
|
||||
CobblemonCriteria.PARTY_CHECK.trigger(player!!, PartyCheckContext(this))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun set(position: PartyPosition, pokemon: Pokemon) {
|
||||
super.set(position, pokemon)
|
||||
pokemon.getOwnerPlayer()?.let { CobblemonCriteria.PARTY_CHECK.trigger(it, PartyCheckContext(this)) }
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.api.storage.pc
|
||||
|
||||
import com.cobblemon.mod.common.api.reactive.Observable.Companion.stopAfter
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.storage.InvalidSpeciesException
|
||||
import com.cobblemon.mod.common.api.storage.StoreCoordinates
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.SetPCBoxPokemonPacket
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
@@ -120,7 +121,12 @@ open class PCBox(val pc: PCStore) : Iterable<Pokemon> {
|
||||
open fun loadFromJSON(json: JsonObject): PCBox {
|
||||
for (slot in 0 until POKEMON_PER_BOX) {
|
||||
if (json.has(DataKeys.STORE_SLOT + slot)) {
|
||||
pokemon[slot] = Pokemon().loadFromJSON(json.getAsJsonObject(DataKeys.STORE_SLOT + slot))
|
||||
val pokemonJson = json.getAsJsonObject(DataKeys.STORE_SLOT + slot)
|
||||
try {
|
||||
pokemon[slot] = Pokemon().loadFromJSON(pokemonJson)
|
||||
} catch (_: InvalidSpeciesException) {
|
||||
pc.handleInvalidSpeciesJSON(pokemonJson)
|
||||
}
|
||||
}
|
||||
}
|
||||
return this
|
||||
@@ -129,7 +135,12 @@ open class PCBox(val pc: PCStore) : Iterable<Pokemon> {
|
||||
open fun loadFromNBT(nbt: NbtCompound): PCBox {
|
||||
for (slot in 0 until POKEMON_PER_BOX) {
|
||||
if (nbt.contains(DataKeys.STORE_SLOT + slot)) {
|
||||
pokemon[slot] = Pokemon().loadFromNBT(nbt.getCompound(DataKeys.STORE_SLOT + slot))
|
||||
val pokemonNBT = nbt.getCompound(DataKeys.STORE_SLOT + slot)
|
||||
try {
|
||||
pokemon[slot] = Pokemon().loadFromNBT(pokemonNBT)
|
||||
} catch (_: InvalidSpeciesException) {
|
||||
pc.handleInvalidSpeciesNBT(pokemonNBT)
|
||||
}
|
||||
}
|
||||
}
|
||||
return this
|
||||
|
||||
+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.storage.player
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
|
||||
/**
|
||||
* A dummy implementation of the [PlayerDataExtension] interface.
|
||||
* Used for when a previous [PlayerDataExtension] is no longer registered.
|
||||
*
|
||||
* @author Deltric
|
||||
* @since 2023-10-01
|
||||
*/
|
||||
class DummyPlayerDataExtension(
|
||||
val json: JsonObject
|
||||
): PlayerDataExtension {
|
||||
|
||||
override fun name(): String {
|
||||
return this.json.get(PlayerDataExtension.NAME_KEY).asString
|
||||
}
|
||||
|
||||
override fun serialize(): JsonObject {
|
||||
return this.json
|
||||
}
|
||||
|
||||
override fun deserialize(json: JsonObject): PlayerDataExtension {
|
||||
return DummyPlayerDataExtension(json)
|
||||
}
|
||||
}
|
||||
+25
-1
@@ -24,8 +24,16 @@ class PlayerAdvancementData {
|
||||
private set
|
||||
var totalBattleVictoryCount: Int = 0
|
||||
private set
|
||||
var totalPvPBattleVictoryCount: Int = 0
|
||||
private set
|
||||
var totalPvWBattleVictoryCount: Int = 0
|
||||
private set
|
||||
var totalPvNBattleVictoryCount: Int = 0
|
||||
private set
|
||||
var totalShinyCaptureCount: Int = 0
|
||||
private set
|
||||
var totalTradedCount: Int = 0
|
||||
private set
|
||||
|
||||
private var totalTypeCaptureCounts = mutableMapOf<String, Int>()
|
||||
private var totalDefeatedCounts = mutableMapOf<Identifier, Int>()
|
||||
@@ -45,13 +53,29 @@ class PlayerAdvancementData {
|
||||
}
|
||||
|
||||
fun updateTotalBattleVictoryCount() {
|
||||
totalEvolvedCount++
|
||||
totalBattleVictoryCount++
|
||||
}
|
||||
|
||||
fun updateTotalPvPBattleVictoryCount() {
|
||||
totalPvPBattleVictoryCount++
|
||||
}
|
||||
|
||||
fun updateTotalPvWBattleVictoryCount() {
|
||||
totalPvWBattleVictoryCount++
|
||||
}
|
||||
|
||||
fun updateTotalPvNBattleVictoryCount() {
|
||||
totalPvNBattleVictoryCount++
|
||||
}
|
||||
|
||||
fun updateTotalShinyCaptureCount() {
|
||||
totalShinyCaptureCount++
|
||||
}
|
||||
|
||||
fun updateTotalTradedCount() {
|
||||
totalTradedCount++
|
||||
}
|
||||
|
||||
fun getTotalTypeCaptureCount(type: ElementalType): Int {
|
||||
if (!totalTypeCaptureCounts.containsKey(key = type.name)) {
|
||||
totalTypeCaptureCounts[type.name] = 0
|
||||
|
||||
@@ -11,8 +11,10 @@ package com.cobblemon.mod.common.api.storage.player
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.starter.SetClientPlayerDataPacket
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import java.util.UUID
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
data class PlayerData(
|
||||
val uuid: UUID,
|
||||
@@ -20,7 +22,8 @@ data class PlayerData(
|
||||
var starterLocked: Boolean,
|
||||
var starterSelected: Boolean,
|
||||
var starterUUID: UUID?,
|
||||
val extraData: MutableMap<String, PlayerDataExtension>
|
||||
var keyItems: MutableSet<Identifier>,
|
||||
val extraData: MutableMap<String, PlayerDataExtension>,
|
||||
) {
|
||||
var advancementData: PlayerAdvancementData = PlayerAdvancementData()
|
||||
|
||||
@@ -36,6 +39,7 @@ data class PlayerData(
|
||||
starterLocked = !Cobblemon.starterConfig.allowStarterOnJoin,
|
||||
starterSelected = false,
|
||||
starterUUID = null,
|
||||
keyItems = mutableSetOf(),
|
||||
extraData = mutableMapOf()
|
||||
)
|
||||
}
|
||||
|
||||
+7
-6
@@ -8,13 +8,10 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.storage.player.adapter
|
||||
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerAdvancementData
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerData
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerDataExtension
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.fromJson
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import com.cobblemon.mod.common.util.removeIf
|
||||
import com.google.gson.GsonBuilder
|
||||
import java.io.BufferedReader
|
||||
import java.io.FileReader
|
||||
@@ -24,6 +21,8 @@ import java.util.UUID
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.WorldSavePath
|
||||
import kotlin.reflect.KMutableProperty
|
||||
import kotlin.reflect.full.memberProperties
|
||||
|
||||
class JsonPlayerData: PlayerDataStoreAdapter {
|
||||
|
||||
@@ -57,9 +56,11 @@ class JsonPlayerData: PlayerDataStoreAdapter {
|
||||
playerFile.parentFile.mkdirs()
|
||||
return if (playerFile.exists()) {
|
||||
gson.fromJson<PlayerData>(BufferedReader(FileReader(playerFile))).also {
|
||||
// Resolves old data from pre 1.0, don't even ask man. - Hiroku
|
||||
if (it.advancementData == null) {
|
||||
it.advancementData = PlayerAdvancementData()
|
||||
// Resolves old data that's missing new properties
|
||||
val newProps = it::class.memberProperties.filterIsInstance<KMutableProperty<*>>().filter { member -> member.getter.call(it) == null }
|
||||
if (newProps.isNotEmpty()) {
|
||||
val defaultData = PlayerData.defaultData(uuid)
|
||||
newProps.forEach { member -> member.setter.call(it, member.getter.call(defaultData)) }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.storage.player.adapter
|
||||
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerAdvancementData
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerData
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerDataExtension
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.mongodb.client.MongoClient
|
||||
import com.mongodb.client.model.ReplaceOptions
|
||||
import net.minecraft.util.Identifier
|
||||
import org.bson.Document
|
||||
import java.util.UUID
|
||||
import kotlin.reflect.KMutableProperty
|
||||
import kotlin.reflect.full.memberProperties
|
||||
|
||||
class MongoPlayerDataAdapter(
|
||||
mongoClient: MongoClient,
|
||||
databaseName: String
|
||||
) : PlayerDataStoreAdapter {
|
||||
|
||||
companion object {
|
||||
val gson = GsonBuilder()
|
||||
.disableHtmlEscaping()
|
||||
.registerTypeAdapter(PlayerDataExtension::class.java, PlayerDataExtensionAdapter)
|
||||
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
|
||||
.create()
|
||||
}
|
||||
|
||||
private val collection = mongoClient.getDatabase(databaseName).getCollection("PlayerDataCollection")
|
||||
|
||||
override fun load(uuid: UUID): PlayerData {
|
||||
val filter = Document("uuid", uuid.toString())
|
||||
val document = collection.find(filter).first()
|
||||
|
||||
return if (document != null) {
|
||||
val jsonStr = document.toJson()
|
||||
gson.fromJson(jsonStr, PlayerData::class.java).also {
|
||||
val newProps = it::class.memberProperties.filterIsInstance<KMutableProperty<*>>().filter { member -> member.getter.call(it) == null }
|
||||
if (newProps.isNotEmpty()) {
|
||||
val defaultData = PlayerData.defaultData(uuid)
|
||||
newProps.forEach { member -> member.setter.call(it, member.getter.call(defaultData)) }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
PlayerData.defaultData(uuid).also(::save)
|
||||
}
|
||||
}
|
||||
|
||||
override fun save(playerData: PlayerData) {
|
||||
val jsonStr = gson.toJson(playerData)
|
||||
val document = Document.parse(jsonStr)
|
||||
document.put("uuid", playerData.uuid.toString())
|
||||
val filter = Document("uuid", playerData.uuid.toString())
|
||||
|
||||
collection.replaceOne(filter, document, ReplaceOptions().upsert(true))
|
||||
}
|
||||
}
|
||||
+10
-2
@@ -8,6 +8,8 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.storage.player.adapter
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.storage.player.DummyPlayerDataExtension
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerDataExtension
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerDataExtensionRegistry
|
||||
import com.google.gson.JsonDeserializationContext
|
||||
@@ -34,7 +36,13 @@ object PlayerDataExtensionAdapter: JsonSerializer<PlayerDataExtension>, JsonDese
|
||||
val jObject = json.asJsonObject
|
||||
val name = jObject.get(PlayerDataExtension.NAME_KEY)
|
||||
?: throw IllegalStateException("PlayerDataExtension without name")
|
||||
val extension = PlayerDataExtensionRegistry.getOrException(name.asString)
|
||||
return extension.getDeclaredConstructor().newInstance().deserialize(jObject)
|
||||
val extension = PlayerDataExtensionRegistry.get(name.asString)
|
||||
|
||||
return if (extension != null) {
|
||||
extension.getDeclaredConstructor().newInstance().deserialize(jObject)
|
||||
} else {
|
||||
Cobblemon.LOGGER.info("No PlayerDataExtension registered with name ${name.asString}, loading data with a dummy extension.")
|
||||
DummyPlayerDataExtension(jObject)
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -26,7 +26,7 @@ class JsonPlayerDataStoreFactory : PlayerDataStoreFactory {
|
||||
}
|
||||
|
||||
override fun load(uuid: UUID): PlayerData {
|
||||
return if(cache.contains(uuid))
|
||||
return if (cache.contains(uuid))
|
||||
cache[uuid]!!;
|
||||
else {
|
||||
val data = adapter.load(uuid);
|
||||
@@ -40,7 +40,7 @@ class JsonPlayerDataStoreFactory : PlayerDataStoreFactory {
|
||||
}
|
||||
|
||||
override fun saveAll() {
|
||||
cache.forEach { (_, pd) -> adapter.save(pd)}
|
||||
cache.forEach { (_, pd) -> adapter.save(pd) }
|
||||
cache.removeIf { (uuid, _) -> uuid.getPlayer() == null }
|
||||
}
|
||||
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.storage.player.factory
|
||||
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerData
|
||||
import com.cobblemon.mod.common.api.storage.player.adapter.MongoPlayerDataAdapter
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import com.cobblemon.mod.common.util.removeIf
|
||||
import com.mongodb.client.MongoClient
|
||||
import java.util.UUID
|
||||
|
||||
class MongoPlayerDataStoreFactory(mongoClient: MongoClient, databaseName: String) : PlayerDataStoreFactory {
|
||||
|
||||
private val cache = mutableMapOf<UUID, PlayerData>()
|
||||
private val adapter = MongoPlayerDataAdapter(mongoClient, databaseName)
|
||||
|
||||
override fun load(uuid: UUID): PlayerData {
|
||||
return if (cache.contains(uuid))
|
||||
cache[uuid]!!;
|
||||
else {
|
||||
val data = adapter.load(uuid);
|
||||
cache[uuid] = data
|
||||
data
|
||||
}
|
||||
}
|
||||
|
||||
override fun save(data: PlayerData) {
|
||||
adapter.save(data)
|
||||
}
|
||||
|
||||
override fun saveAll() {
|
||||
cache.forEach { (_, pd) -> adapter.save(pd) }
|
||||
cache.removeIf { (uuid, _) -> uuid.getPlayer() == null }
|
||||
}
|
||||
|
||||
override fun onPlayerDisconnect(uuid: UUID) {
|
||||
cache.remove(uuid)
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ import net.minecraft.registry.tag.TagKey
|
||||
object CobblemonBiomeTags {
|
||||
|
||||
@JvmField
|
||||
val IS_ABYSS = create("is_abyss")
|
||||
val IS_DEEP_DARK = create("is_deep_dark")
|
||||
@JvmField
|
||||
val IS_ARID = create("is_arid")
|
||||
@JvmField
|
||||
@@ -38,7 +38,7 @@ object CobblemonBiomeTags {
|
||||
@JvmField
|
||||
val IS_COLD = create("is_cold")
|
||||
@JvmField
|
||||
val IS_DEEP = create("is_deep")
|
||||
val IS_DEEP_OCEAN = create("is_deep_ocean")
|
||||
@JvmField
|
||||
val IS_DESERT = create("is_desert")
|
||||
@JvmField
|
||||
@@ -52,9 +52,9 @@ object CobblemonBiomeTags {
|
||||
@JvmField
|
||||
val IS_FRESHWATER = create("is_freshwater")
|
||||
@JvmField
|
||||
val IS_FRIGID = create("is_frigid")
|
||||
val IS_COLD_OCEAN = create("is_cold_ocean")
|
||||
@JvmField
|
||||
val IS_FROZEN = create("is_frozen")
|
||||
val IS_FROZEN_OCEAN = create("is_frozen_ocean")
|
||||
@JvmField
|
||||
val IS_GLACIAL = create("is_glacial")
|
||||
@JvmField
|
||||
@@ -68,7 +68,7 @@ object CobblemonBiomeTags {
|
||||
@JvmField
|
||||
val IS_JUNGLE = create("is_jungle")
|
||||
@JvmField
|
||||
val IS_LUKEWARM = create("is_lukewarm")
|
||||
val IS_LUKEWARM_OCEAN = create("is_lukewarm_ocean")
|
||||
@JvmField
|
||||
val IS_LUSH = create("is_lush")
|
||||
@JvmField
|
||||
@@ -86,7 +86,7 @@ object CobblemonBiomeTags {
|
||||
@JvmField
|
||||
val IS_PLATEAU = create("is_plateau")
|
||||
@JvmField
|
||||
val IS_REEF = create("is_reef")
|
||||
val IS_WARM_OCEAN = create("is_warm_ocean")
|
||||
@JvmField
|
||||
val IS_RIVER = create("is_river")
|
||||
@JvmField
|
||||
@@ -96,7 +96,7 @@ object CobblemonBiomeTags {
|
||||
@JvmField
|
||||
val IS_SKY = create("is_sky")
|
||||
@JvmField
|
||||
val IS_SNOWY = create("is_snowy")
|
||||
val IS_SNOWY_FOREST = create("is_snowy_forest")
|
||||
@JvmField
|
||||
val IS_SPOOKY = create("is_spooky")
|
||||
@JvmField
|
||||
@@ -119,6 +119,10 @@ object CobblemonBiomeTags {
|
||||
val IS_VOlCANIC = create("is_volcanic")
|
||||
@JvmField
|
||||
val IS_WINTER = create("is_winter")
|
||||
@JvmField
|
||||
val IS_SPARSE = create("is_sparse")
|
||||
@JvmField
|
||||
val IS_DENSE = create("is_dense")
|
||||
|
||||
// Has Feature tags
|
||||
@JvmField
|
||||
|
||||
@@ -26,7 +26,9 @@ object CobblemonBlockTags {
|
||||
@JvmField val APRICORN_LOGS = createTag("apricorn_logs")
|
||||
@JvmField val APRICORN_SAPLINGS = createTag("apricorn_saplings")
|
||||
@JvmField val APRICORNS = createTag("apricorns")
|
||||
@JvmField val BERRY_WILD_SOIL = createTag("berry_wild_soil")
|
||||
@JvmField val BERRY_SOIL = createTag("berry_soil")
|
||||
@JvmField val BERRY_REPLACEABLE = createTag("berry_replaceable")
|
||||
@JvmField val CROPS = createTag("crops")
|
||||
@JvmField val DRIPSTONE_GROWABLE = createTag("dripstone_growable")
|
||||
@JvmField val DRIPSTONE_REPLACEABLES = createTag("dripstone_replaceables")
|
||||
@@ -36,7 +38,8 @@ object CobblemonBlockTags {
|
||||
@JvmField val ROOTS_SPREADABLE = createTag("roots_spreadable")
|
||||
@JvmField val SMALL_FLOWERS = createTag("small_flowers")
|
||||
@JvmField val SEES_SKY = createTag("sees_sky")
|
||||
|
||||
@JvmField val SNOW_BLOCK = createTag("snow_block")
|
||||
@JvmField val ROOTS = createTag("roots")
|
||||
private fun createTag(name: String) = TagKey.of(RegistryKeys.BLOCK, cobblemonResource(name))
|
||||
|
||||
}
|
||||
@@ -158,6 +158,7 @@ fun String.stripCodes(): String = this.replace("[&§][A-Ea-e0-9K-Ok-oRr]".toRege
|
||||
fun MutableText.onClick(consumed: AtomicBoolean, action: (p: ServerPlayerEntity) -> Unit) = also { it.style = it.style.withClickEvent(click(consumed, action)) }
|
||||
fun MutableText.onClick(onlyOnce: Boolean = false, action: (p: ServerPlayerEntity) -> Unit) = also { it.style = it.style.withClickEvent(click(onlyOnce, action)) }
|
||||
fun MutableText.onHover(string: String) = also { it.style = it.style.withHoverEvent(hover(string)) }
|
||||
fun MutableText.onHover(text: Text) = also { it.style = it.style.withHoverEvent(hover(text)) }
|
||||
fun MutableText.onHover(text: MutableText) = also { it.style = it.style.withHoverEvent(hover(text)) }
|
||||
fun MutableText.underline() = also { it.style = it.style.withUnderline(true) }
|
||||
fun MutableText.bold() = also { it.style = it.style.withBold(true) }
|
||||
|
||||
@@ -169,4 +169,6 @@ object ElementalTypes {
|
||||
}
|
||||
|
||||
fun count() = allTypes.size
|
||||
|
||||
fun all() = this.allTypes.toList()
|
||||
}
|
||||
@@ -66,7 +66,7 @@ object BattleBuilder {
|
||||
CobblemonEvents.BATTLE_STARTED_PRE.postThen(
|
||||
BattleStartedPreEvent(listOf(player1Actor, player2Actor), battleFormat, true, false, false))
|
||||
{
|
||||
SuccessfulBattleStart(
|
||||
return SuccessfulBattleStart(
|
||||
BattleRegistry.startBattle(
|
||||
battleFormat = battleFormat,
|
||||
side1 = BattleSide(player1Actor),
|
||||
@@ -136,7 +136,7 @@ object BattleBuilder {
|
||||
if (!cloneParties) {
|
||||
pokemonEntity.battleId.set(Optional.of(battle.battleId))
|
||||
}
|
||||
SuccessfulBattleStart(battle)
|
||||
return SuccessfulBattleStart(battle)
|
||||
}
|
||||
errors
|
||||
} else {
|
||||
|
||||
@@ -28,7 +28,7 @@ data class BattleFormat(
|
||||
companion object {
|
||||
val GEN_9_SINGLES = BattleFormat(
|
||||
battleType = BattleTypes.SINGLES,
|
||||
ruleSet = setOf(BattleRules.OBTAINABLE)
|
||||
ruleSet = setOf(BattleRules.OBTAINABLE, BattleRules.PAST, BattleRules.UNOBTAINABLE)
|
||||
)
|
||||
|
||||
val GEN_9_DOUBLES = BattleFormat(
|
||||
|
||||
@@ -16,9 +16,9 @@ import com.cobblemon.mod.common.api.pokemon.helditem.HeldItemProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleChallengeExpiredPacket
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.google.gson.GsonBuilder
|
||||
import java.time.Instant
|
||||
import java.util.Optional
|
||||
@@ -38,7 +38,10 @@ object BattleRegistry {
|
||||
fun isExpired() = Instant.now().isAfter(challengedTime.plusSeconds(expiryTimeSeconds.toLong()))
|
||||
}
|
||||
|
||||
val gson = GsonBuilder().disableHtmlEscaping().create()
|
||||
val gson = GsonBuilder()
|
||||
.disableHtmlEscaping()
|
||||
.registerTypeAdapter(ShowdownMoveset::class.java, ShowdownMovesetAdapter)
|
||||
.create()
|
||||
private val battleMap = ConcurrentHashMap<UUID, PokemonBattle>()
|
||||
// Challenger to challenge
|
||||
val pvpChallenges = mutableMapOf<UUID, BattleChallenge>()
|
||||
@@ -105,7 +108,7 @@ object BattleRegistry {
|
||||
}|"
|
||||
)
|
||||
// Nature
|
||||
val battleNature = pk.mintedNature ?: pk.nature
|
||||
val battleNature = pk.effectiveNature
|
||||
packedTeamBuilder.append("${battleNature.name.path}|")
|
||||
// EVs
|
||||
val evsInOrder = Stats.PERMANENT.map { pk.evs.getOrDefault(it) }.joinToString(separator = ",")
|
||||
@@ -130,11 +133,12 @@ object BattleRegistry {
|
||||
// Hidden Power Type
|
||||
packedTeamBuilder.append(",")
|
||||
// Gigantamax
|
||||
packedTeamBuilder.append(",")
|
||||
packedTeamBuilder.append("${if (pk.gmaxFactor) "G" else ""},")
|
||||
// DynamaxLevel
|
||||
packedTeamBuilder.append(",")
|
||||
// 0 - 9, empty == 10
|
||||
packedTeamBuilder.append("${if (pk.dmaxLevel < 10) pk.dmaxLevel else ""},")
|
||||
// Teratype
|
||||
packedTeamBuilder.append(",")
|
||||
packedTeamBuilder.append("${pokemon.effectedPokemon.teraType.name},")
|
||||
|
||||
team.add(packedTeamBuilder.toString())
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ package com.cobblemon.mod.common.battles
|
||||
|
||||
object BattleRules {
|
||||
const val OBTAINABLE = "Obtainable"
|
||||
const val PAST = "+Past"
|
||||
const val UNOBTAINABLE = "+Unobtainable"
|
||||
const val TEAM_PREVIEW = "Team Preview"
|
||||
const val ENDLESS_BATTLE_CLAUSE = "Endless Battle Clause"
|
||||
const val CANCEL_MOD = "Cancel Mod"
|
||||
|
||||
@@ -19,6 +19,7 @@ class InBattleMove {
|
||||
var maxpp: Int = 100
|
||||
var target: MoveTarget = MoveTarget.self
|
||||
var disabled: Boolean = false
|
||||
var gimmickMove: InBattleGimmickMove? = null
|
||||
|
||||
companion object {
|
||||
fun loadFromBuffer(buffer: PacketByteBuf): InBattleMove {
|
||||
@@ -44,4 +45,27 @@ class InBattleMove {
|
||||
buffer.writeEnumConstant(target)
|
||||
buffer.writeBoolean(disabled)
|
||||
}
|
||||
}
|
||||
|
||||
// Defined in sim/battle-actions.ts canZMove and getMaxMove
|
||||
class InBattleGimmickMove {
|
||||
lateinit var move: String
|
||||
var target: MoveTarget = MoveTarget.self
|
||||
var disabled: Boolean = false
|
||||
|
||||
companion object {
|
||||
fun loadFromBuffer(buffer: PacketByteBuf): InBattleGimmickMove {
|
||||
return InBattleGimmickMove().apply {
|
||||
move = buffer.readString()
|
||||
target = buffer.readEnumConstant(MoveTarget::class.java)
|
||||
disabled = buffer.readBoolean()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun saveToBuffer(buffer: PacketByteBuf) {
|
||||
buffer.writeString(move)
|
||||
buffer.writeEnumConstant(target)
|
||||
buffer.writeBoolean(disabled)
|
||||
}
|
||||
}
|
||||
@@ -8,14 +8,22 @@
|
||||
|
||||
package com.cobblemon.mod.common.battles
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.item.battle.BagItem
|
||||
import com.cobblemon.mod.common.net.IntSize
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.hasKeyItem
|
||||
import com.cobblemon.mod.common.util.readSizedInt
|
||||
import com.cobblemon.mod.common.util.writeSizedInt
|
||||
import com.google.gson.*
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import java.lang.Integer.max
|
||||
import java.util.UUID
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
import java.lang.reflect.Type
|
||||
|
||||
class ShowdownActionRequest(
|
||||
var wait: Boolean = false,
|
||||
var active: MutableList<ShowdownMoveset>? = null,
|
||||
@@ -63,6 +71,22 @@ class ShowdownActionRequest(
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
fun sanitize(battle: PokemonBattle, battleActor: BattleActor) {
|
||||
val player = battle.players.find { it.uuid == battleActor.uuid } ?: return
|
||||
this.active?.forEach { moveset ->
|
||||
moveset.getGimmicks().forEach { gimmick ->
|
||||
// TODO: use the Identifiers of actual items
|
||||
val triggerItem = when(gimmick) {
|
||||
ShowdownMoveset.Gimmick.MEGA_EVOLUTION -> cobblemonResource("key_stone")
|
||||
ShowdownMoveset.Gimmick.DYNAMAX -> cobblemonResource("dynamax_band")
|
||||
ShowdownMoveset.Gimmick.TERASTALLIZATION -> cobblemonResource("tera_orb")
|
||||
else -> cobblemonResource("z_ring")
|
||||
}
|
||||
if (!player.hasKeyItem(triggerItem)) moveset.blockGimmick(gimmick)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class ShowdownActionResponseType(val loader: (PacketByteBuf) -> ShowdownActionResponse) {
|
||||
@@ -91,18 +115,20 @@ abstract class ShowdownActionResponse(val type: ShowdownActionResponseType) {
|
||||
abstract fun toShowdownString(activeBattlePokemon: ActiveBattlePokemon, showdownMoveSet: ShowdownMoveset?): String
|
||||
}
|
||||
|
||||
data class MoveActionResponse(var moveName: String, var targetPnx: String? = null): ShowdownActionResponse(ShowdownActionResponseType.MOVE) {
|
||||
data class MoveActionResponse(var moveName: String, var targetPnx: String? = null, var gimmickID: String? = null): ShowdownActionResponse(ShowdownActionResponseType.MOVE) {
|
||||
override fun isValid(activeBattlePokemon: ActiveBattlePokemon, showdownMoveSet: ShowdownMoveset?, forceSwitch: Boolean): Boolean {
|
||||
if (forceSwitch || showdownMoveSet == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
val move = showdownMoveSet.moves.find { it.id == moveName } ?: return false
|
||||
if (!move.canBeUsed()) {
|
||||
val gimmickMove = move.gimmickMove
|
||||
val validGimmickMove = gimmickMove != null && !gimmickMove.disabled
|
||||
if (!validGimmickMove && !move.canBeUsed()) {
|
||||
// No PP or disabled or something
|
||||
return false
|
||||
}
|
||||
val availableTargets = move.target.targetList(activeBattlePokemon)?.takeIf { it.isNotEmpty() } ?: return true
|
||||
val availableTargets = (gimmickMove?.target ?: move.target).targetList(activeBattlePokemon)?.takeIf { it.isNotEmpty() } ?: return true
|
||||
|
||||
val pnx = targetPnx ?: return false // If the targets list is non-null then they need to have specified a target
|
||||
val (_, targetPokemon) = activeBattlePokemon.actor.battle.getActorAndActiveSlotFromPNX(pnx)
|
||||
@@ -124,22 +150,21 @@ data class MoveActionResponse(var moveName: String, var targetPnx: String? = nul
|
||||
"move $moveIndex $digit"
|
||||
} else {
|
||||
"move $moveIndex"
|
||||
}
|
||||
}.plus(gimmickID?.let { " $gimmickID" } ?: "")
|
||||
}
|
||||
|
||||
override fun saveToBuffer(buffer: PacketByteBuf) {
|
||||
super.saveToBuffer(buffer)
|
||||
buffer.writeString(moveName)
|
||||
buffer.writeBoolean(targetPnx != null)
|
||||
targetPnx?.let(buffer::writeString)
|
||||
buffer.writeNullable(targetPnx) { _, targetPnx -> buffer.writeString(targetPnx) }
|
||||
buffer.writeNullable(gimmickID) { _, gimmickID -> buffer.writeString(gimmickID) }
|
||||
}
|
||||
|
||||
override fun loadFromBuffer(buffer: PacketByteBuf): ShowdownActionResponse {
|
||||
super.loadFromBuffer(buffer)
|
||||
moveName = buffer.readString()
|
||||
if (buffer.readBoolean()) {
|
||||
targetPnx = buffer.readString()
|
||||
}
|
||||
targetPnx = buffer.readNullable { buffer.readString() }
|
||||
gimmickID = buffer.readNullable { buffer.readString() }
|
||||
return this
|
||||
}
|
||||
}
|
||||
@@ -243,11 +268,31 @@ class BagItemActionResponse(val bagItem: BagItem, val target: BattlePokemon, val
|
||||
class ShowdownMoveset {
|
||||
lateinit var moves: List<InBattleMove>
|
||||
var trapped = false
|
||||
var canMegaEvo = false
|
||||
var canUltraBurst = false
|
||||
var canZMove: List<InBattleGimmickMove?>? = null
|
||||
var canDynamax = false
|
||||
var maxMoves: List<InBattleGimmickMove?>? = null
|
||||
var canTerastallize: String? = null
|
||||
|
||||
fun saveToBuffer(buffer: PacketByteBuf) {
|
||||
buffer.writeSizedInt(IntSize.U_BYTE, moves.size)
|
||||
moves.forEach { it.saveToBuffer(buffer) }
|
||||
buffer.writeBoolean(trapped)
|
||||
buffer.writeBoolean(canMegaEvo)
|
||||
buffer.writeBoolean(canUltraBurst)
|
||||
buffer.writeNullable(canZMove) { _, canZMove ->
|
||||
canZMove.forEach {
|
||||
buffer.writeNullable(it) { _, zmove -> zmove.saveToBuffer(buffer)}
|
||||
}
|
||||
}
|
||||
buffer.writeBoolean(canDynamax)
|
||||
buffer.writeNullable(maxMoves) { _, maxMoves ->
|
||||
maxMoves.forEach {
|
||||
buffer.writeNullable(it) { _, maxMove -> maxMove.saveToBuffer(buffer)}
|
||||
}
|
||||
}
|
||||
buffer.writeNullable(canTerastallize) { _, teraType -> buffer.writeString(teraType) }
|
||||
}
|
||||
|
||||
fun loadFromBuffer(buffer: PacketByteBuf): ShowdownMoveset {
|
||||
@@ -257,8 +302,60 @@ class ShowdownMoveset {
|
||||
}
|
||||
this.moves = moves
|
||||
this.trapped = buffer.readBoolean()
|
||||
this.canMegaEvo = buffer.readBoolean()
|
||||
this.canUltraBurst = buffer.readBoolean()
|
||||
this.canZMove = buffer.readNullable {
|
||||
val zMoves = mutableListOf<InBattleGimmickMove?>()
|
||||
repeat(moves.size) { zMoves.add(buffer.readNullable { InBattleGimmickMove.loadFromBuffer(buffer) }) }
|
||||
return@readNullable zMoves
|
||||
}
|
||||
this.canDynamax = buffer.readBoolean()
|
||||
this.maxMoves = buffer.readNullable {
|
||||
val maxMoves = mutableListOf<InBattleGimmickMove?>()
|
||||
repeat(moves.size) { maxMoves.add(buffer.readNullable { InBattleGimmickMove.loadFromBuffer(buffer) }) }
|
||||
return@readNullable maxMoves
|
||||
}
|
||||
this.canTerastallize = buffer.readNullable{ buffer.readString() }
|
||||
this.setGimmickMapping()
|
||||
return this
|
||||
}
|
||||
|
||||
/** Showdown IDs of battle gimmicks. */
|
||||
enum class Gimmick(val id: String) {
|
||||
MEGA_EVOLUTION("mega"),
|
||||
ULTRA_BURST("ultra"),
|
||||
Z_POWER("zmove"),
|
||||
DYNAMAX("max"),
|
||||
TERASTALLIZATION("terastal")
|
||||
}
|
||||
|
||||
/** Check whether Dynamax/Gigantamax is already active. */
|
||||
fun hasActiveGimmick() = !this.canDynamax && this.maxMoves != null
|
||||
|
||||
/** [Gimmick]s that can be activated with a [MoveActionResponse]. */
|
||||
fun getGimmicks() = if (!hasActiveGimmick()) buildList{
|
||||
if (canMegaEvo) add(Gimmick.MEGA_EVOLUTION)
|
||||
if (canUltraBurst) add(Gimmick.ULTRA_BURST)
|
||||
if (canZMove != null) add(Gimmick.Z_POWER)
|
||||
if (canDynamax) add(Gimmick.DYNAMAX)
|
||||
if (canTerastallize != null) add(Gimmick.TERASTALLIZATION)
|
||||
}.toList() else listOf() // keep this immutable
|
||||
|
||||
/** Map each [InBattleMove] to their respective [InBattleGimmickMove] variant. */
|
||||
fun setGimmickMapping() = (canZMove ?: maxMoves)?.let { gimmickMoves ->
|
||||
moves.forEachIndexed { index, move -> move.gimmickMove = gimmickMoves[index] }
|
||||
}
|
||||
|
||||
/** Sanitize Moveset by disabling gimmick. */
|
||||
fun blockGimmick(gimmick: Gimmick) {
|
||||
when (gimmick) {
|
||||
Gimmick.MEGA_EVOLUTION -> this.canMegaEvo = false
|
||||
Gimmick.DYNAMAX -> this.canDynamax = false
|
||||
Gimmick.ULTRA_BURST -> this.canUltraBurst = false
|
||||
Gimmick.Z_POWER -> this.canZMove = null
|
||||
else -> this.canTerastallize = null
|
||||
}
|
||||
}
|
||||
}
|
||||
class ShowdownSide {
|
||||
lateinit var name: UUID
|
||||
@@ -320,4 +417,30 @@ class ShowdownPokemon {
|
||||
ability = buffer.readString()
|
||||
return this
|
||||
}
|
||||
}
|
||||
/** Unwraps useless maxMoves object and initializes [ShowdownMoveset.gimmickMapping] */
|
||||
object ShowdownMovesetAdapter : JsonDeserializer<ShowdownMoveset> {
|
||||
|
||||
val gson = GsonBuilder().addDeserializationExclusionStrategy(MovesetExclusionStrategy).create()
|
||||
|
||||
object MovesetExclusionStrategy : ExclusionStrategy {
|
||||
override fun shouldSkipField(field: FieldAttributes?): Boolean {
|
||||
return field?.name == "maxMoves"
|
||||
}
|
||||
override fun shouldSkipClass(p0: Class<*>?): Boolean {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
override fun deserialize(jsonElement: JsonElement, type: Type, context: JsonDeserializationContext): ShowdownMoveset {
|
||||
val json = jsonElement.asJsonObject
|
||||
val moveset = gson.fromJson(json, ShowdownMoveset::class.java)
|
||||
|
||||
json.get("maxMoves")?.asJsonObject?.let { dynamaxOptions ->
|
||||
val maxMovesToken = object: TypeToken<List<InBattleGimmickMove?>?>() {}.type
|
||||
moveset.maxMoves = Gson().fromJson(dynamaxOptions.get("maxMoves"), maxMovesToken)
|
||||
}
|
||||
moveset.setGimmickMapping()
|
||||
return moveset
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.*
|
||||
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.battles.model.actor.EntityBackedBattleActor
|
||||
import com.cobblemon.mod.common.api.data.ShowdownIdentifiable
|
||||
@@ -23,6 +24,8 @@ import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.api.scheduling.after
|
||||
import com.cobblemon.mod.common.api.text.*
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
|
||||
import com.cobblemon.mod.common.battles.dispatch.BattleDispatch
|
||||
import com.cobblemon.mod.common.battles.dispatch.DispatchResult
|
||||
import com.cobblemon.mod.common.battles.dispatch.GO
|
||||
@@ -108,6 +111,12 @@ object ShowdownInterpreter {
|
||||
updateInstructions["|-sidestart|"] = this::handleSideStartInstructions
|
||||
updateInstructions["|-sideend|"] = this::handleSideEndInstructions
|
||||
updateInstructions["|-fieldactivate|"] = this::handleFieldActivateInstructions
|
||||
updateInstructions["|-clearnegativeboost|"] = this::handleClearNegativeBoostInstructions
|
||||
updateInstructions["|-zpower|"] = this::handleZPowerInstructions
|
||||
updateInstructions["|-zbroken|"] = this::handleZBrokenInstructions
|
||||
updateInstructions["|-terastallize|"] = this::handleTerastallizeInstructions
|
||||
updateInstructions["|detailschange|"] = this::handleDetailsChangeInstructions
|
||||
updateInstructions["|-mega|"] = this::handleMegaInstructions
|
||||
|
||||
sideUpdateInstructions["|request|"] = this::handleRequestInstruction
|
||||
splitUpdateInstructions["|switch|"] = this::handleSwitchInstruction
|
||||
@@ -148,7 +157,12 @@ object ShowdownInterpreter {
|
||||
}
|
||||
|
||||
battle.dispatchWaiting(1.5F) {
|
||||
battle.broadcastChatMessage(battleLang("$rootKey.$severity", pokemon.getName(), stat))
|
||||
val lang = when {
|
||||
message.hasOptionalArgument("zeffect") -> battleLang("$rootKey.$severity.zeffect", pokemon.getName(), stat)
|
||||
else -> battleLang("$rootKey.$severity", pokemon.getName(), stat)
|
||||
}
|
||||
battle.broadcastChatMessage(lang)
|
||||
|
||||
val boostBucket = if (isBoost) BattleContext.Type.BOOST else BattleContext.Type.UNBOOST
|
||||
val context = getContextFromAction(message, boostBucket, battle)
|
||||
// TODO: replace with context that tracks detailed information such as # of stages
|
||||
@@ -567,6 +581,17 @@ object ShowdownInterpreter {
|
||||
battle.end()
|
||||
|
||||
val wasCaught = battle.showdownMessages.any { "capture" in it }
|
||||
|
||||
// If the battle was a PvW battle, we need to set the killer of the wild Pokémon to the player
|
||||
if (battle.isPvW) {
|
||||
val nonPlayerActor = battle.actors.first { it.type == ActorType.WILD }
|
||||
val wildPokemon: BattlePokemon = nonPlayerActor.pokemonList.first()
|
||||
|
||||
if (!wasCaught && losers.any { it.uuid == wildPokemon.uuid }) {
|
||||
wildPokemon.effectedPokemon.entity?.killer = (battle.actors.firstOrNull { it.type == ActorType.PLAYER } as? PlayerBattleActor)?.entity
|
||||
}
|
||||
}
|
||||
|
||||
CobblemonEvents.BATTLE_VICTORY.post(BattleVictoryEvent(battle, winners, losers, wasCaught))
|
||||
|
||||
this.lastCauser.remove(battle.battleId)
|
||||
@@ -646,10 +671,9 @@ object ShowdownInterpreter {
|
||||
this.lastCauser[battle.battleId] = message
|
||||
|
||||
userPokemon.effectedPokemon.let { pokemon ->
|
||||
val progress = UseMoveEvolutionProgress()
|
||||
if (progress.shouldKeep(pokemon)) {
|
||||
val created = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is UseMoveEvolutionProgress && it.currentProgress().move == move }) { progress }
|
||||
created.updateProgress(UseMoveEvolutionProgress.Progress(created.currentProgress().move, created.currentProgress().amount + 1))
|
||||
if (UseMoveEvolutionProgress.supports(pokemon, move)) {
|
||||
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is UseMoveEvolutionProgress && it.currentProgress().move == move }) { UseMoveEvolutionProgress() }
|
||||
progress.updateProgress(UseMoveEvolutionProgress.Progress(move, progress.currentProgress().amount + 1))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -679,7 +703,7 @@ object ShowdownInterpreter {
|
||||
val effectID = message.effectAt(1)?.id ?: return@dispatchWaiting
|
||||
val name = pokemon.getName()
|
||||
// Move may be null as it's not always given
|
||||
val moveName = message.argumentAt(2)?.let { Moves.getByName(it)?.displayName } ?: Text.EMPTY
|
||||
val moveName = message.moveAt(2)?.displayName ?: run { println(message.argumentAt(2)); "(Unrecognized: ${message.argumentAt(2)})".text() }
|
||||
|
||||
val lang = when (effectID) {
|
||||
// TODO: in the games they use a generic image because there is a popup of the ability and the sprite of the mon, it may be good to have a similar system here
|
||||
@@ -876,6 +900,9 @@ object ShowdownInterpreter {
|
||||
val lang = when (effectID) {
|
||||
"confusion", "perish3" -> return@dispatch GO // Skip
|
||||
"perish2", "perish1", "perish0" -> battleLang("start.perish", pokemon.getName(), effectID.last().digitToInt())
|
||||
"dynamax" -> battleLang("start.${message.effectAt(2)?.id ?: effectID}", pokemon.getName()).yellow()
|
||||
"disable" -> battleLang("start.disable", pokemon.getName(), message.effectAt(2)!!.rawData)
|
||||
"curse" -> battleLang("start.curse", message.getSourceBattlePokemon(battle)!!.getName(), pokemon.getName())
|
||||
else -> battleLang("start.$effectID", pokemon.getName())
|
||||
}
|
||||
battle.broadcastChatMessage(lang)
|
||||
@@ -950,10 +977,11 @@ object ShowdownInterpreter {
|
||||
}
|
||||
// Includes revealed move
|
||||
"forewarn" -> {
|
||||
val moveName = message.argumentAt(2)?.let { Moves.getByName(it)?.displayName } ?: Text.EMPTY
|
||||
val moveName = message.moveAt(2)?.displayName ?: run { println(message.argumentAt(2)); "(Unrecognized: ${message.argumentAt(2)})".text() }
|
||||
battleLang("activate.forewarn", sourceName, moveName)
|
||||
}
|
||||
"focussash", "focusband" -> battleLang("activate.focusband", pokemonName, message.effectAt(1)!!.typelessData)
|
||||
"maxguard", "protect" -> battleLang("activate.protect", pokemonName)
|
||||
else -> battleLang("activate.${effect.id}", pokemonName, sourceName)
|
||||
}
|
||||
battle.broadcastChatMessage(lang)
|
||||
@@ -1223,6 +1251,7 @@ object ShowdownInterpreter {
|
||||
|
||||
// Parse Json message and update state info for actor
|
||||
val request = BattleRegistry.gson.fromJson(message.rawMessage.split("|request|")[1], ShowdownActionRequest::class.java)
|
||||
request.sanitize(battle, battleActor)
|
||||
if (battle.started) {
|
||||
battle.dispatchGo {
|
||||
// This request won't be acted on until the start of next turn
|
||||
@@ -1355,14 +1384,12 @@ object ShowdownInterpreter {
|
||||
val battlePokemon = publicMessage.getBattlePokemon(0, battle) ?: return
|
||||
if (privateMessage.optionalArgument("from")?.equals("recoil", true) == true) {
|
||||
battlePokemon.effectedPokemon.let { pokemon ->
|
||||
val recoilProgress = RecoilEvolutionProgress()
|
||||
// Lazy cheat to see if it's necessary to use this
|
||||
if (recoilProgress.shouldKeep(pokemon)) {
|
||||
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is RecoilEvolutionProgress }) { recoilProgress }
|
||||
if (RecoilEvolutionProgress.supports(pokemon)) {
|
||||
val newPercentage = privateMessage.argumentAt(1)?.split("/")?.getOrNull(0)?.toIntOrNull() ?: 0
|
||||
val newHealth = (pokemon.hp * (newPercentage / 100.0)).roundToInt()
|
||||
val difference = pokemon.currentHealth - newHealth
|
||||
if (difference > 0) {
|
||||
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is RecoilEvolutionProgress }) { RecoilEvolutionProgress() }
|
||||
progress.updateProgress(RecoilEvolutionProgress.Progress(progress.currentProgress().recoil + difference))
|
||||
}
|
||||
}
|
||||
@@ -1406,10 +1433,8 @@ object ShowdownInterpreter {
|
||||
battlePokemon.effectedPokemon.currentHealth = remainingHealth
|
||||
if (difference > 0) {
|
||||
battlePokemon.effectedPokemon.let { pokemon ->
|
||||
val damageProgress = DamageTakenEvolutionProgress()
|
||||
// Lazy cheat to see if it's necessary to use this
|
||||
if (damageProgress.shouldKeep(pokemon)) {
|
||||
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is DamageTakenEvolutionProgress }) { damageProgress }
|
||||
if (DamageTakenEvolutionProgress.supports(pokemon)) {
|
||||
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is DamageTakenEvolutionProgress }) { DamageTakenEvolutionProgress() }
|
||||
progress.updateProgress(DamageTakenEvolutionProgress.Progress(progress.currentProgress().amount + difference))
|
||||
}
|
||||
}
|
||||
@@ -1434,8 +1459,8 @@ object ShowdownInterpreter {
|
||||
* The switched Pokémon has HP HP, and status STATUS.
|
||||
*/
|
||||
fun handleDragInstruction(battle: PokemonBattle, actor: BattleActor, publicMessage: BattleMessage, privateMessage: BattleMessage) {
|
||||
battle.dispatchGo {
|
||||
val (pnx, pokemonID) = publicMessage.pnxAndUuid(0) ?: return@dispatchGo
|
||||
battle.dispatchInsert {
|
||||
val (pnx, pokemonID) = publicMessage.pnxAndUuid(0)!!
|
||||
val (_, activePokemon) = battle.getActorAndActiveSlotFromPNX(pnx)
|
||||
val pokemon = battle.getBattlePokemon(pnx, pokemonID)
|
||||
|
||||
@@ -1447,13 +1472,15 @@ object ShowdownInterpreter {
|
||||
battle.majorBattleActions[pokemon.uuid] = publicMessage
|
||||
|
||||
val entity = if (actor is EntityBackedBattleActor<*>) actor.entity else null
|
||||
battle.dispatch {
|
||||
if (entity != null) {
|
||||
this.createEntitySwitch(battle, actor, entity, pnx, activePokemon, pokemon)
|
||||
} else {
|
||||
this.createNonEntitySwitch(battle, actor, pnx, activePokemon, pokemon)
|
||||
setOf(
|
||||
BattleDispatch {
|
||||
if (entity != null) {
|
||||
this.createEntitySwitch(battle, actor, entity, pnx, activePokemon, pokemon)
|
||||
} else {
|
||||
this.createNonEntitySwitch(battle, actor, pnx, activePokemon, pokemon)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1532,15 +1559,16 @@ object ShowdownInterpreter {
|
||||
val battlePokemon = privateMessage.getBattlePokemon(0, battle) ?: return
|
||||
val rawHpAndStatus = privateMessage.argumentAt(1)?.split(" ") ?: return
|
||||
val rawHpRatio = rawHpAndStatus.getOrNull(0) ?: return
|
||||
val newHealth = rawHpRatio.split("/").getOrNull(0)?.toIntOrNull() ?: return
|
||||
val newHealthRatio = publicMessage.argumentAt(1)?.split("/")?.getOrNull(0)?.toFloatOrNull()?.times(0.01F) ?: return
|
||||
val newHealth = rawHpRatio.split("/").map { it.toFloatOrNull() ?: return }
|
||||
val newHealthRatio = rawHpRatio.split("/").map { it.toFloatOrNull()?.div(100) ?: return }
|
||||
val effect = privateMessage.effect()
|
||||
val pokemonName = battlePokemon.getName()
|
||||
broadcastOptionalAbility(battle, effect, pokemonName)
|
||||
|
||||
battle.dispatchWaiting {
|
||||
if (pnx != null) {
|
||||
battle.sendSidedUpdate(actor, BattleHealthChangePacket(pnx, newHealth.toFloat()), BattleHealthChangePacket(pnx, newHealthRatio))
|
||||
// dynamax changes max health
|
||||
battle.sendSidedUpdate(actor, BattleHealthChangePacket(pnx, newHealth[0], newHealth[1]), BattleHealthChangePacket(pnx, newHealthRatio[0]))
|
||||
}
|
||||
val silent = privateMessage.hasOptionalArgument("silent")
|
||||
if (!silent) {
|
||||
@@ -1575,7 +1603,7 @@ object ShowdownInterpreter {
|
||||
battle.broadcastChatMessage(lang)
|
||||
}
|
||||
battle.minorBattleActions[battlePokemon.uuid] = privateMessage
|
||||
battlePokemon.effectedPokemon.currentHealth = newHealth
|
||||
battlePokemon.effectedPokemon.currentHealth = newHealth[0].toInt()
|
||||
|
||||
// This part is not always present
|
||||
val rawStatus = rawHpAndStatus.getOrNull(1) ?: return@dispatchWaiting
|
||||
@@ -1631,6 +1659,105 @@ object ShowdownInterpreter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format:
|
||||
* |-clearnegativeboost|POKEMON
|
||||
*
|
||||
* Clear the negative boosts from the target Pokémon POKEMON (usually as the result of a zeffect).
|
||||
*/
|
||||
private fun handleClearNegativeBoostInstructions(battle: PokemonBattle, message: BattleMessage, remainingLines: MutableList<String>) {
|
||||
val battlePokemon = message.getBattlePokemon(0, battle) ?: return
|
||||
val pokemonName = battlePokemon.getName()
|
||||
battle.dispatchWaiting(1.5F) {
|
||||
val lang = when {
|
||||
message.hasOptionalArgument("zeffect") -> battleLang("clearallnegativeboost.zeffect", pokemonName)
|
||||
else -> battleLang("clearallnegativeboost", pokemonName)
|
||||
}
|
||||
battle.broadcastChatMessage(lang)
|
||||
|
||||
battlePokemon.contextManager.clear(BattleContext.Type.UNBOOST)
|
||||
battle.minorBattleActions[battlePokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format:
|
||||
* |-zpower|POKEMON
|
||||
*
|
||||
* The Pokémon POKEMON has used the z-move version of its move.
|
||||
*/
|
||||
private fun handleZPowerInstructions(battle: PokemonBattle, message: BattleMessage, remainingLines: MutableList<String>) {
|
||||
val battlePokemon = message.getBattlePokemon(0, battle) ?: return
|
||||
val pokemonName = battlePokemon.getName()
|
||||
battle.dispatchWaiting {
|
||||
battle.broadcastChatMessage(battleLang("zpower", pokemonName).yellow())
|
||||
battle.minorBattleActions[battlePokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format:
|
||||
* |-zbroken|POKEMON
|
||||
*
|
||||
* A z-move has broken through protect and hit the Pokémon POKEMON.
|
||||
*/
|
||||
private fun handleZBrokenInstructions(battle: PokemonBattle, message: BattleMessage, remainingLines: MutableList<String>) {
|
||||
val battlePokemon = message.getBattlePokemon(0, battle) ?: return
|
||||
val pokemonName = battlePokemon.getName()
|
||||
battle.dispatchWaiting {
|
||||
battle.broadcastChatMessage(battleLang("zbroken", pokemonName).red())
|
||||
battle.minorBattleActions[battlePokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format:
|
||||
* |-terastallize|POKEMON|TYPE
|
||||
*
|
||||
* The Pokémon POKEMON terastallized into type TYPE.
|
||||
*/
|
||||
private fun handleTerastallizeInstructions(battle: PokemonBattle, message: BattleMessage, remainingLines: MutableList<String>) {
|
||||
val battlePokemon = message.getBattlePokemon(0, battle) ?: return
|
||||
val pokemonName = battlePokemon.getName()
|
||||
val type = message.effectAt(1)?.let { ElementalTypes.get(it.id) } ?: return
|
||||
battle.dispatchWaiting {
|
||||
battle.broadcastChatMessage(battleLang("terastallize", pokemonName, type.displayName).yellow())
|
||||
battle.minorBattleActions[battlePokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format:
|
||||
* |detailschange|POKEMON|DETAILS|HP STATUS
|
||||
*
|
||||
* The specified Pokémon has changed formes (via Mega Evolution, ability, etc.). If the forme change is permanent,
|
||||
* then detailschange will appear; otherwise, the client will send -formechange.
|
||||
*/
|
||||
private fun handleDetailsChangeInstructions(battle: PokemonBattle, message: BattleMessage, remainingLines: MutableList<String>) {
|
||||
val battlePokemon = message.getBattlePokemon(0, battle) ?: return
|
||||
val pokemonName = battlePokemon.getName()
|
||||
val formName = message.argumentAt(1)?.split(',')?.get(0)?.substringAfter('-')?.lowercase() ?: return
|
||||
battle.dispatchWaiting {
|
||||
battle.broadcastChatMessage(battleLang("detailschange.$formName", pokemonName))
|
||||
battle.majorBattleActions[battlePokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format:
|
||||
* |-mega|POKEMON|MEGASTONE
|
||||
*
|
||||
* The Pokémon POKEMON used MEGASTONE to Mega Evolve.
|
||||
*/
|
||||
private fun handleMegaInstructions(battle: PokemonBattle, message: BattleMessage, remainingLines: MutableList<String>) {
|
||||
val battlePokemon = message.getBattlePokemon(0, battle) ?: return
|
||||
val pokemonName = battlePokemon.getName()
|
||||
battle.dispatchWaiting {
|
||||
battle.broadcastChatMessage(battleLang("mega", pokemonName).yellow())
|
||||
battle.minorBattleActions[battlePokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
|
||||
// Used for things that are only meant for visual information we don't have
|
||||
private fun handleSilently(battle: PokemonBattle, message: BattleMessage, remainingLines: MutableList<String>) {
|
||||
battle.dispatchGo { }
|
||||
|
||||
@@ -137,7 +137,7 @@ class ApricornBlock(settings: Settings, val apricorn: Apricorn) : HorizontalFaci
|
||||
world.playSoundServer(position = pos.toVec3d(), sound = SoundEvents.ENTITY_ITEM_PICKUP, volume = 0.7F, pitch = 1.4F)
|
||||
|
||||
if (world is ServerWorld && player is ServerPlayerEntity) {
|
||||
CobblemonEvents.APRICORN_HARVESTED.post(ApricornHarvestEvent(player, world, pos))
|
||||
CobblemonEvents.APRICORN_HARVESTED.post(ApricornHarvestEvent(player, apricorn, world, pos))
|
||||
}
|
||||
}
|
||||
return ActionResult.success(world.isClient)
|
||||
@@ -166,7 +166,7 @@ class ApricornBlock(settings: Settings, val apricorn: Apricorn) : HorizontalFaci
|
||||
}
|
||||
|
||||
override fun attemptShear(world: World, state: BlockState, pos: BlockPos, successCallback: () -> Unit): Boolean {
|
||||
if (state.get(AGE) == MAX_AGE) {
|
||||
if (state.get(AGE) != MAX_AGE) {
|
||||
return false
|
||||
}
|
||||
world.playSound(null, pos, SoundEvents.ENTITY_SHEEP_SHEAR, SoundCategory.BLOCKS, 1F, 1F)
|
||||
|
||||
@@ -8,9 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.berry.Berries
|
||||
import com.cobblemon.mod.common.api.berry.Berry
|
||||
@@ -21,8 +19,14 @@ import com.cobblemon.mod.common.api.mulch.MulchVariant
|
||||
import com.cobblemon.mod.common.api.mulch.Mulchable
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
|
||||
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.HealingMachineBlockEntity
|
||||
import net.minecraft.block.*
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.block.BlockRenderType
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.BlockWithEntity
|
||||
import net.minecraft.block.Blocks
|
||||
import net.minecraft.block.FarmlandBlock
|
||||
import net.minecraft.block.Fertilizable
|
||||
import net.minecraft.block.ShapeContext
|
||||
import net.minecraft.block.entity.BlockEntity
|
||||
import net.minecraft.block.entity.BlockEntityTicker
|
||||
import net.minecraft.block.entity.BlockEntityType
|
||||
@@ -42,6 +46,7 @@ import net.minecraft.util.Hand
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.hit.BlockHitResult
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Box
|
||||
import net.minecraft.util.math.Direction
|
||||
import net.minecraft.util.math.random.Random
|
||||
import net.minecraft.util.shape.VoxelShape
|
||||
@@ -63,6 +68,14 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
|
||||
*/
|
||||
fun berry(): Berry? = Berries.getByIdentifier(this.berryIdentifier)
|
||||
|
||||
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity) {
|
||||
if (!player.isCreative && state.get(AGE) == FRUIT_AGE) {
|
||||
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
|
||||
treeEntity.harvest(world, state, pos, player).forEach { drop -> Block.dropStack(world, pos, drop) }
|
||||
}
|
||||
super.onBreak(world, pos, state, player)
|
||||
}
|
||||
|
||||
override fun createBlockEntity(pos: BlockPos, state: BlockState) = BerryBlockEntity(pos, state, berryIdentifier)
|
||||
|
||||
override fun isFertilizable(world: WorldView, pos: BlockPos, state: BlockState, isClient: Boolean) = !this.isMaxAge(state)
|
||||
@@ -75,17 +88,20 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
|
||||
defaultState = this.stateManager.defaultState
|
||||
.with(WAS_GENERATED, false)
|
||||
.with(AGE, 0)
|
||||
.with(MULCH, MulchVariant.NONE)
|
||||
}
|
||||
|
||||
override fun grow(world: ServerWorld, random: Random, pos: BlockPos, state: BlockState) {
|
||||
val curAge = state.get(AGE)
|
||||
val newAge = curAge + 1
|
||||
if (newAge > FRUIT_AGE) return
|
||||
var newState = state.with(AGE, newAge)
|
||||
val newState = state.with(AGE, newAge)
|
||||
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
|
||||
if (curAge == MATURE_AGE) {
|
||||
treeEntity.generateGrowthPoints(world, state, pos, null)
|
||||
determineMutation(world, random, pos, state)
|
||||
}
|
||||
|
||||
world.setBlockState(pos, newState, Block.NOTIFY_LISTENERS)
|
||||
treeEntity.goToNextStageTimer(FRUIT_AGE - curAge)
|
||||
treeEntity.markDirty()
|
||||
@@ -106,9 +122,9 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
|
||||
CobblemonEvents.BERRY_MUTATION_OFFER.post(BerryMutationOfferEvent(berry, world, state, pos, mutations)) { berryMutationOffer ->
|
||||
if (berryMutationOffer.mutations.isNotEmpty()) {
|
||||
var mutateChance = 125
|
||||
if (treeEntity.mulchVariant == MulchVariant.SURPRISE) {
|
||||
if (getMulch(state) == MulchVariant.SURPRISE) {
|
||||
mutateChance *= 4
|
||||
treeEntity.mulchDuration--
|
||||
treeEntity.decrementMulchDuration(world, pos, state)
|
||||
}
|
||||
val mutation = if (random.nextInt(1000) < mutateChance) mutations.random() else null
|
||||
(world.getBlockEntity(pos) as? BerryBlockEntity)?.let { blockEntity ->
|
||||
@@ -127,8 +143,7 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
|
||||
state: BlockState,
|
||||
variant: MulchVariant
|
||||
): Boolean {
|
||||
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
|
||||
return treeEntity.mulchVariant == null
|
||||
return getMulch(state) == MulchVariant.NONE && state.get(AGE) < FLOWER_AGE && world.getBlockState(pos.down()).isOf(Blocks.FARMLAND)
|
||||
}
|
||||
|
||||
override fun applyMulch(
|
||||
@@ -139,29 +154,31 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
|
||||
variant: MulchVariant
|
||||
) {
|
||||
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
|
||||
treeEntity.mulchVariant = variant
|
||||
setMulch(world, pos, state, variant)
|
||||
treeEntity.mulchDuration = variant.duration
|
||||
world.playSound(null, pos, CobblemonSounds.MULCH_PLACE, SoundCategory.BLOCKS)
|
||||
world.playSound(null, pos, CobblemonSounds.MULCH_PLACE, SoundCategory.BLOCKS, 0.6F, 1F)
|
||||
treeEntity.refreshTimers(pos)
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun onUse(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hit: BlockHitResult): ActionResult {
|
||||
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
|
||||
if(player.getStackInHand(hand).item is ShovelItem && treeEntity.mulchVariant != null) {
|
||||
treeEntity.mulchVariant = null
|
||||
if (player.getStackInHand(hand).item is ShovelItem && getMulch(state) != MulchVariant.NONE) {
|
||||
setMulch(world, pos, state, MulchVariant.NONE)
|
||||
treeEntity.markDirty()
|
||||
world.playSound(null, pos, CobblemonSounds.MULCH_REMOVE, SoundCategory.BLOCKS)
|
||||
world.playSound(null, pos, CobblemonSounds.MULCH_REMOVE, SoundCategory.BLOCKS, 0.6F, 1F)
|
||||
this.spawnBreakParticles(world, player, pos, state.with(AGE, 0))
|
||||
return ActionResult.SUCCESS
|
||||
}
|
||||
|
||||
if (player.getStackInHand(hand).isOf(Items.BONE_MEAL)) {
|
||||
if (player.getStackInHand(hand).isOf(Items.BONE_MEAL) && !this.isMaxAge(state)) {
|
||||
return ActionResult.PASS
|
||||
}
|
||||
else if (this.isMaxAge(state)) {
|
||||
} else if (this.isMaxAge(state)) {
|
||||
val blockEntity = world.getBlockEntity(pos) as? BerryBlockEntity ?: return ActionResult.PASS
|
||||
blockEntity.harvest(world, state, pos, player).forEach { drop ->
|
||||
Block.dropStack(world, pos, drop)
|
||||
}
|
||||
world.playSound(null, pos, CobblemonSounds.BERRY_HARVEST, SoundCategory.BLOCKS, 0.4F, 1F)
|
||||
return ActionResult.success(world.isClient)
|
||||
}
|
||||
return super.onUse(state, world, pos, player, hand, hit)
|
||||
@@ -169,7 +186,10 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun canPlaceAt(state: BlockState, world: WorldView, pos: BlockPos): Boolean {
|
||||
return state.get(WAS_GENERATED) || world.getBlockState(pos.down()).isIn(CobblemonBlockTags.BERRY_SOIL)
|
||||
val below = world.getBlockState(pos.down())
|
||||
return (state.get(WAS_GENERATED) && below.isIn(CobblemonBlockTags.BERRY_WILD_SOIL))
|
||||
|| below.isIn(CobblemonBlockTags.BERRY_SOIL)
|
||||
|| below.block is FarmlandBlock
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
@@ -178,15 +198,16 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
|
||||
}
|
||||
|
||||
override fun onPlaced(world: World, pos: BlockPos, state: BlockState, placer: LivingEntity?, itemStack: ItemStack) {
|
||||
if (!world.isClient) {
|
||||
val blockEntity = world.getBlockEntity(pos) as? BerryBlockEntity ?: return
|
||||
blockEntity.generateGrowthPoints(world, state, pos, placer)
|
||||
}
|
||||
// if (!world.isClient) {
|
||||
// val blockEntity = world.getBlockEntity(pos) as? BerryBlockEntity ?: return
|
||||
// blockEntity.generateGrowthPoints(world, state, pos, placer)
|
||||
// }
|
||||
}
|
||||
|
||||
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
|
||||
builder.add(AGE)
|
||||
builder.add(WAS_GENERATED)
|
||||
builder.add(MULCH)
|
||||
}
|
||||
|
||||
override fun getPickStack(world: BlockView?, pos: BlockPos?, state: BlockState?): ItemStack {
|
||||
@@ -197,7 +218,12 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun getOutlineShape(state: BlockState, world: BlockView, pos: BlockPos, context: ShapeContext): VoxelShape {
|
||||
val berry = this.berry() ?: return VoxelShapes.fullCube()
|
||||
return if (state.get(AGE) >= MATURE_AGE) berry.matureShape else berry.sproutShape
|
||||
return when (state.get(AGE)) {
|
||||
0 -> PLANTED_SHAPE
|
||||
1 -> PLANTED_SHAPE
|
||||
2 -> berry.sproutShape
|
||||
else -> berry.matureShape
|
||||
}
|
||||
}
|
||||
|
||||
private fun isMaxAge(state: BlockState) = state.get(AGE) == FRUIT_AGE
|
||||
@@ -209,7 +235,81 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
|
||||
const val MATURE_AGE = 3
|
||||
const val FLOWER_AGE = 4
|
||||
const val FRUIT_AGE = 5
|
||||
|
||||
val AGE: IntProperty = IntProperty.of("age", 0, FRUIT_AGE)
|
||||
val MULCH: EnumProperty<MulchVariant> = EnumProperty.of("mulch", MulchVariant::class.java)
|
||||
val WAS_GENERATED: BooleanProperty = BooleanProperty.of("generated")
|
||||
// val PLANTED_SHAPE = VoxelShapes.union(
|
||||
// VoxelShapes.cuboid(0.3125, -0.0625, 0.3125, 0.6875, 0.0, 0.6875),
|
||||
// VoxelShapes.cuboid(0.375, 0.0, 0.375, 0.625, 0.0625, 0.625)
|
||||
// )
|
||||
val PLANTED_SHAPE = VoxelShapes.cuboid(0.0, -0.1, 0.0, 1.0, 0.25, 1.0)
|
||||
|
||||
|
||||
val STANDARD_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 16.0, 16.0))
|
||||
val STANDARD_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 24.0, 16.0))
|
||||
|
||||
val SHORT_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 12.0, 16.0))
|
||||
val SHORT_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 16.0, 16.0))
|
||||
|
||||
val VOLCANO_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 6.0, 16.0))
|
||||
val VOLCANO_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 16.0, 16.0))
|
||||
|
||||
val NEST_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 6.0, 16.0))
|
||||
val NEST_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 20.0, 16.0))
|
||||
|
||||
val FRILL_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 8.0, 16.0))
|
||||
val FRILL_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 14.0, 16.0))
|
||||
|
||||
val BLOCK_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 17.0, 16.0))
|
||||
val BLOCK_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 24.0, 16.0))
|
||||
|
||||
val PYRAMID_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 17.0, 16.0))
|
||||
val PYRAMID_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 24.0, 16.0))
|
||||
|
||||
val TAIL_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 16.0, 16.0))
|
||||
val TAIL_MATURE = listOf(Box(0.0, 1.0, 0.0, 16.0, 24.0, 16.0))
|
||||
|
||||
val SWORD_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 5.0, 16.0))
|
||||
val SWORD_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 24.0, 16.0))
|
||||
|
||||
val PLATFORM_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 7.0, 16.0))
|
||||
val PLATFORM_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 22.0, 16.0))
|
||||
|
||||
val STAND_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 13.0, 16.0))
|
||||
val STAND_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 24.0, 16.0))
|
||||
|
||||
val CONE_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 16.0, 16.0))
|
||||
val CONE_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 23.0, 16.0))
|
||||
|
||||
val SQUAT_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 12.0, 16.0))
|
||||
val SQUAT_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 22.0, 16.0))
|
||||
|
||||
val LANTERN_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 20.0, 16.0))
|
||||
val LANTERN_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 24.0, 16.0))
|
||||
|
||||
val BOX_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 16.0, 16.0))
|
||||
val BOX_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 24.0, 16.0))
|
||||
|
||||
val BLOSSOM_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 4.0, 16.0))
|
||||
val BLOSSOM_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 6.0, 16.0))
|
||||
|
||||
val LILYPAD_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 11.0, 16.0))
|
||||
val LILYPAD_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 16.0, 16.0))
|
||||
|
||||
val TALL_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 16.0, 16.0))
|
||||
val TALL_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 24.0, 16.0))
|
||||
|
||||
|
||||
fun getMulch(state: BlockState): MulchVariant {
|
||||
if (!state.contains(MULCH)) {
|
||||
return MulchVariant.NONE
|
||||
}
|
||||
return state.get(MULCH)
|
||||
}
|
||||
|
||||
fun setMulch(world: World, pos: BlockPos, state: BlockState, mulch: MulchVariant) {
|
||||
world.setBlockState(pos, state.with(MULCH, mulch))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,6 @@ import net.minecraft.world.World
|
||||
|
||||
@Suppress("OVERRIDE_DEPRECATION", "DEPRECATION")
|
||||
class BigRootBlock(settings: Settings) : RootBlock(settings) {
|
||||
|
||||
override fun getOutlineShape(state: BlockState, world: BlockView, pos: BlockPos, context: ShapeContext): VoxelShape = AABB
|
||||
|
||||
override fun onUse(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hit: BlockHitResult): ActionResult {
|
||||
|
||||
@@ -50,7 +50,7 @@ class MedicinalLeekBlock(settings: Settings) : CropBlock(settings) {
|
||||
|
||||
// These 3 are still around for the sake of compatibility, vanilla won't trigger it but some mods might
|
||||
// We implement applyGrowth & getGrowthAmount for them
|
||||
override fun isFertilizable(world: WorldView?, pos: BlockPos?, state: BlockState?, isClient: Boolean): Boolean = false
|
||||
override fun isFertilizable(world: WorldView?, pos: BlockPos?, state: BlockState?, isClient: Boolean): Boolean = !this.isMature(state)
|
||||
|
||||
override fun applyGrowth(world: World, pos: BlockPos, state: BlockState) {
|
||||
world.setBlockState(pos, state.with(this.ageProperty, (this.getAge(state) + 1).coerceAtMost(this.maxAge)), NOTIFY_LISTENERS)
|
||||
|
||||
@@ -39,7 +39,7 @@ class MintBlock(private val mintType: MintType, settings: Settings) : CropBlock(
|
||||
// DO NOT use withAge
|
||||
// Explanation for these 2 beautiful copy pasta are basically that we need to keep the blockstate and that's not possible with the default impl :(
|
||||
override fun randomTick(state: BlockState, world: ServerWorld, pos: BlockPos, random: Random) {
|
||||
if (world.getBaseLightLevel(pos, 0) < 9 || this.isMature(state) || random.nextInt(50) != 0) {
|
||||
if (world.getBaseLightLevel(pos, 0) < 9 || this.isMature(state) || random.nextInt(8) != 0) {
|
||||
return
|
||||
}
|
||||
this.applyGrowth(world, pos, state, false)
|
||||
@@ -139,4 +139,4 @@ class MintBlock(private val mintType: MintType, settings: Settings) : CropBlock(
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,14 @@ import com.cobblemon.mod.common.util.isInBattle
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.cobblemon.mod.common.util.playSoundServer
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import net.minecraft.block.*
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.block.BlockRenderType
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.BlockWithEntity
|
||||
import net.minecraft.block.Blocks
|
||||
import net.minecraft.block.HorizontalFacingBlock
|
||||
import net.minecraft.block.ShapeContext
|
||||
import net.minecraft.block.Waterloggable
|
||||
import net.minecraft.block.entity.BlockEntity
|
||||
import net.minecraft.block.entity.BlockEntityType
|
||||
import net.minecraft.entity.LivingEntity
|
||||
@@ -34,7 +41,11 @@ import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.state.StateManager
|
||||
import net.minecraft.state.property.BooleanProperty
|
||||
import net.minecraft.state.property.EnumProperty
|
||||
import net.minecraft.util.*
|
||||
import net.minecraft.util.ActionResult
|
||||
import net.minecraft.util.BlockMirror
|
||||
import net.minecraft.util.BlockRotation
|
||||
import net.minecraft.util.Hand
|
||||
import net.minecraft.util.StringIdentifiable
|
||||
import net.minecraft.util.hit.BlockHitResult
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction
|
||||
@@ -43,6 +54,7 @@ import net.minecraft.util.shape.VoxelShapes
|
||||
import net.minecraft.world.BlockView
|
||||
import net.minecraft.world.World
|
||||
import net.minecraft.world.WorldAccess
|
||||
import net.minecraft.world.WorldEvents
|
||||
import net.minecraft.world.WorldView
|
||||
|
||||
class PCBlock(properties: Settings): BlockWithEntity(properties), Waterloggable {
|
||||
@@ -168,15 +180,6 @@ class PCBlock(properties: Settings): BlockWithEntity(properties), Waterloggable
|
||||
|
||||
private fun isBase(state: BlockState): Boolean = state.get(PART) == PCPart.BOTTOM
|
||||
|
||||
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity?) {
|
||||
super.onBreak(world, pos, state, player)
|
||||
val otherPart = world.getBlockState(getPositionOfOtherPart(state, pos))
|
||||
if (otherPart.block is PCBlock) {
|
||||
world.setBlockState(getPositionOfOtherPart(state, pos), Blocks.AIR.defaultState, 35)
|
||||
world.syncWorldEvent(player, 2001, getPositionOfOtherPart(state, pos), Block.getRawIdFromState(otherPart))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPlaced(world: World, pos: BlockPos, state: BlockState, placer: LivingEntity?, itemStack: ItemStack?) {
|
||||
world.setBlockState(pos.up(), state
|
||||
.with(PART, PCPart.TOP)
|
||||
@@ -205,6 +208,20 @@ class PCBlock(properties: Settings): BlockWithEntity(properties), Waterloggable
|
||||
return if (state.get(PART) == PCPart.BOTTOM) blockState.isSideSolidFullSquare(world, blockPos, Direction.UP) else blockState.isOf(this)
|
||||
}
|
||||
|
||||
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity?) {
|
||||
if (!world.isClient && player?.isCreative == true) {
|
||||
var blockPos: BlockPos = BlockPos.ORIGIN
|
||||
var blockState: BlockState = state
|
||||
val part = state.get(PART)
|
||||
if (part == PCPart.TOP && world.getBlockState(pos.down().also { blockPos = it }).also { blockState = it }.isOf(state.block) && blockState.get(PART) == PCPart.BOTTOM) {
|
||||
val blockState2 = if (blockState.fluidState.isOf(Fluids.WATER)) Blocks.WATER.defaultState else Blocks.AIR.defaultState
|
||||
world.setBlockState(blockPos, blockState2, NOTIFY_ALL or SKIP_DROPS)
|
||||
world.syncWorldEvent(player, WorldEvents.BLOCK_BROKEN, blockPos, getRawIdFromState(blockState))
|
||||
}
|
||||
}
|
||||
super.onBreak(world, pos, state, player)
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun canPathfindThrough(blockState: BlockState, blockGetter: BlockView, blockPos: BlockPos, pathComputationType: NavigationType) = false
|
||||
|
||||
@@ -281,15 +298,24 @@ class PCBlock(properties: Settings): BlockWithEntity(properties), Waterloggable
|
||||
|
||||
override fun getStateForNeighborUpdate(
|
||||
state: BlockState,
|
||||
direction: Direction?,
|
||||
neighborState: BlockState?,
|
||||
direction: Direction,
|
||||
neighborState: BlockState,
|
||||
world: WorldAccess,
|
||||
pos: BlockPos?,
|
||||
neighborPos: BlockPos?
|
||||
): BlockState? {
|
||||
pos: BlockPos,
|
||||
neighborPos: BlockPos
|
||||
): BlockState {
|
||||
if (state.get(WATERLOGGED)) {
|
||||
world.scheduleFluidTick(pos, Fluids.WATER, Fluids.WATER.getTickRate(world))
|
||||
}
|
||||
return super.getStateForNeighborUpdate(state, direction, neighborState, world, pos, neighborPos)
|
||||
|
||||
val isPC = neighborState.isOf(this)
|
||||
val part = state.get(PART)
|
||||
if (!isPC && part == PCPart.TOP && neighborPos == pos.down()) {
|
||||
return Blocks.AIR.defaultState
|
||||
} else if (!isPC && part == PCPart.BOTTOM && neighborPos == pos.up()) {
|
||||
return Blocks.AIR.defaultState
|
||||
}
|
||||
|
||||
return state
|
||||
}
|
||||
}
|
||||
@@ -21,39 +21,48 @@ import com.cobblemon.mod.common.util.isInBattle
|
||||
import com.cobblemon.mod.common.util.playSoundServer
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import com.cobblemon.mod.common.util.voxelShape
|
||||
import net.minecraft.block.*
|
||||
import java.util.UUID
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.block.BlockRenderType
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.BlockWithEntity
|
||||
import net.minecraft.block.Blocks
|
||||
import net.minecraft.block.HorizontalFacingBlock
|
||||
import net.minecraft.block.ShapeContext
|
||||
import net.minecraft.block.Waterloggable
|
||||
import net.minecraft.block.entity.BlockEntity
|
||||
import net.minecraft.block.entity.BlockEntityTicker
|
||||
import net.minecraft.block.entity.BlockEntityType
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.entity.ai.pathing.NavigationType
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.fluid.FluidState
|
||||
import net.minecraft.fluid.Fluids
|
||||
import net.minecraft.item.ItemPlacementContext
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
import net.minecraft.state.StateManager
|
||||
import net.minecraft.util.*
|
||||
import net.minecraft.state.property.BooleanProperty
|
||||
import net.minecraft.state.property.EnumProperty
|
||||
import net.minecraft.util.ActionResult
|
||||
import net.minecraft.util.BlockMirror
|
||||
import net.minecraft.util.BlockRotation
|
||||
import net.minecraft.util.Hand
|
||||
import net.minecraft.util.StringIdentifiable
|
||||
import net.minecraft.util.hit.BlockHitResult
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction
|
||||
import net.minecraft.util.shape.VoxelShape
|
||||
import net.minecraft.util.shape.VoxelShapes
|
||||
import net.minecraft.world.BlockView
|
||||
import net.minecraft.world.World
|
||||
import net.minecraft.world.WorldAccess
|
||||
import net.minecraft.world.WorldEvents
|
||||
import net.minecraft.world.WorldView
|
||||
import java.util.*
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.fluid.FluidState
|
||||
import net.minecraft.fluid.Fluids
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
import net.minecraft.state.property.BooleanProperty
|
||||
import net.minecraft.state.property.EnumProperty
|
||||
import net.minecraft.util.BlockMirror
|
||||
import net.minecraft.util.BlockRotation
|
||||
import net.minecraft.util.StringIdentifiable
|
||||
import net.minecraft.util.shape.VoxelShape
|
||||
import net.minecraft.util.shape.VoxelShapes
|
||||
|
||||
@Suppress("OVERRIDE_DEPRECATION", "DEPRECATION")
|
||||
class PastureBlock(properties: Settings): BlockWithEntity(properties), Waterloggable {
|
||||
class PastureBlock(properties: Settings): BlockWithEntity(properties), Waterloggable, PreEmptsExplosion {
|
||||
companion object {
|
||||
val PART: EnumProperty<PasturePart> = EnumProperty.of("part", PasturePart::class.java)
|
||||
val ON: BooleanProperty = BooleanProperty.of("on")
|
||||
@@ -105,7 +114,10 @@ class PastureBlock(properties: Settings): BlockWithEntity(properties), Waterlogg
|
||||
voxelShape(0.1875, 0.25, 0.25, 0.1875, 0.4375, 0.875, direction),
|
||||
voxelShape(0.8125, 0.25, 0.25, 0.8125, 0.4375, 0.875, direction),
|
||||
voxelShape(0.1875, 0.3125, 0.3125, 0.8125, 0.5, 0.3125, direction),
|
||||
voxelShape(0.25, 0.75, 0.3125, 0.75, 1.0, 0.8125, direction)
|
||||
voxelShape(0.25, 0.75, 0.3125, 0.75, 1.0, 0.8125, direction),
|
||||
// I'm lazy and this gets optimized in union anyway
|
||||
voxelShape(0.0, 0.0, 0.0, 0.0625, 1.0, 1.0, direction),
|
||||
voxelShape(0.9375, 0.0, 0.0, 1.0, 1.0, 1.0, direction),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -145,7 +157,7 @@ class PastureBlock(properties: Settings): BlockWithEntity(properties), Waterlogg
|
||||
}
|
||||
|
||||
fun getPositionOfOtherPart(state: BlockState, pos: BlockPos): BlockPos {
|
||||
return if (state.get(PART) == PasturePart.BOTTOM) {
|
||||
return if (state.contains(PART) && state.get(PART) == PasturePart.BOTTOM) {
|
||||
pos.up()
|
||||
} else {
|
||||
pos.down()
|
||||
@@ -160,7 +172,7 @@ class PastureBlock(properties: Settings): BlockWithEntity(properties), Waterlogg
|
||||
}
|
||||
}
|
||||
|
||||
private fun isBase(state: BlockState): Boolean = state.get(PART) == PasturePart.BOTTOM
|
||||
private fun isBase(state: BlockState): Boolean = state.contains(PART) && state.get(PART) == PasturePart.BOTTOM
|
||||
|
||||
override fun canPathfindThrough(blockState: BlockState, blockGetter: BlockView, blockPos: BlockPos, pathComputationType: NavigationType) = false
|
||||
|
||||
@@ -171,18 +183,34 @@ class PastureBlock(properties: Settings): BlockWithEntity(properties), Waterlogg
|
||||
builder.add(WATERLOGGED)
|
||||
}
|
||||
|
||||
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity?) {
|
||||
super.onBreak(world, pos, state, player)
|
||||
val otherPart = world.getBlockState(getPositionOfOtherPart(state, pos))
|
||||
if (otherPart.block is PastureBlock) {
|
||||
world.setBlockState(getPositionOfOtherPart(state, pos), Blocks.AIR.defaultState, 35)
|
||||
world.syncWorldEvent(player, 2001, getPositionOfOtherPart(state, pos), Block.getRawIdFromState(otherPart))
|
||||
fun checkBreakEntity(world: WorldAccess, state: BlockState, pos: BlockPos) {
|
||||
if (state.get(PART) == PasturePart.TOP) {
|
||||
return
|
||||
}
|
||||
val blockEntity = world.getBlockEntity(pos)
|
||||
if (blockEntity is PokemonPastureBlockEntity) {
|
||||
blockEntity.onBroken()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBroken(world: WorldAccess, pos: BlockPos, state: BlockState) {
|
||||
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity?) {
|
||||
checkBreakEntity(world, state, pos)
|
||||
if (!world.isClient && player?.isCreative == true) {
|
||||
var blockPos: BlockPos = BlockPos.ORIGIN
|
||||
var blockState: BlockState = state
|
||||
val part = state.get(PART)
|
||||
if (part == PasturePart.TOP && world.getBlockState(pos.down().also { blockPos = it }).also { blockState = it }.isOf(state.block) && blockState.get(PART) == PasturePart.BOTTOM) {
|
||||
checkBreakEntity(world, blockState, blockPos)
|
||||
val blockState2 = if (blockState.fluidState.isOf(Fluids.WATER)) Blocks.WATER.defaultState else Blocks.AIR.defaultState
|
||||
world.setBlockState(blockPos, blockState2, NOTIFY_ALL or SKIP_DROPS)
|
||||
world.syncWorldEvent(player, WorldEvents.BLOCK_BROKEN, blockPos, getRawIdFromState(blockState))
|
||||
}
|
||||
}
|
||||
super.onBreak(world, pos, state, player)
|
||||
}
|
||||
|
||||
override fun whenExploded(world: World, state: BlockState, pos: BlockPos) {
|
||||
val blockEntity = world.getBlockEntity(pos) as? PokemonPastureBlockEntity ?: return
|
||||
super.onBroken(world, pos, state)
|
||||
blockEntity.onBroken()
|
||||
}
|
||||
|
||||
@@ -293,16 +321,25 @@ class PastureBlock(properties: Settings): BlockWithEntity(properties), Waterlogg
|
||||
|
||||
override fun getStateForNeighborUpdate(
|
||||
state: BlockState,
|
||||
direction: Direction?,
|
||||
neighborState: BlockState?,
|
||||
direction: Direction,
|
||||
neighborState: BlockState,
|
||||
world: WorldAccess,
|
||||
pos: BlockPos?,
|
||||
neighborPos: BlockPos?
|
||||
): BlockState? {
|
||||
pos: BlockPos,
|
||||
neighborPos: BlockPos
|
||||
): BlockState {
|
||||
if (state.get(WATERLOGGED)) {
|
||||
world.scheduleFluidTick(pos, Fluids.WATER, Fluids.WATER.getTickRate(world))
|
||||
}
|
||||
return super.getStateForNeighborUpdate(state, direction, neighborState, world, pos, neighborPos)
|
||||
}
|
||||
|
||||
val isPasture = neighborState.isOf(this)
|
||||
val part = state.get(PART)
|
||||
if (!isPasture && part == PasturePart.TOP && neighborPos == pos.down()) {
|
||||
return Blocks.AIR.defaultState
|
||||
} else if (!isPasture && part == PasturePart.BOTTOM && neighborPos == pos.up()) {
|
||||
checkBreakEntity(world, state, pos)
|
||||
return Blocks.AIR.defaultState
|
||||
}
|
||||
|
||||
return state
|
||||
}
|
||||
}
|
||||
Loaded 100 of 5720 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user