mirror of
https://gitlab.com/cable-mc/cobblemon.git
synced 2026-10-30 13:58:53 -04:00
Compare commits
564
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6dcee2b8f3 | ||
|
|
224c3faa4c | ||
|
|
c42a2a6933 | ||
|
|
072ad5ff12 | ||
|
|
b3757113ac | ||
|
|
3bb17ffc6e | ||
|
|
f37924fb0c | ||
|
|
6e2765902e | ||
|
|
d7c32e4ad8 | ||
|
|
8732094c28 | ||
|
|
38fecf337e | ||
|
|
3c7c640217 | ||
|
|
b7e8cad3bd | ||
|
|
d553332017 | ||
|
|
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 | ||
|
|
40af1f0bdf | ||
|
|
9da0459793 | ||
|
|
7ac41e2f68 | ||
|
|
e0cdc08cd3 | ||
|
|
438e502a5a | ||
|
|
d7b344cff8 | ||
|
|
249c305474 | ||
|
|
5b6ed359a3 | ||
|
|
d14e16fe5b | ||
|
|
65dd132081 | ||
|
|
7f8b241695 | ||
|
|
0b824fe4c1 | ||
|
|
c9fc2ce95d | ||
|
|
91eff015dc | ||
|
|
a9c8825b3c | ||
|
|
9d64b0f06a | ||
|
|
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 | ||
|
|
bb8dffdf50 | ||
|
|
a555ac8e6d | ||
|
|
952fdd8541 | ||
|
|
c30789cea4 | ||
|
|
0ada2265ff | ||
|
|
9aacff4051 | ||
|
|
3b72b558f6 | ||
|
|
ba0d7299ef | ||
|
|
9f2a4261f0 | ||
|
|
1b6b0524e8 | ||
|
|
5f740b1c58 | ||
|
|
f5e492a6ce | ||
|
|
c8c1677182 | ||
|
|
8431e25c31 | ||
|
|
121f7c6a9a | ||
|
|
c523408fe0 | ||
|
|
f0ef34f435 | ||
|
|
646ed14146 | ||
|
|
e8d8499462 | ||
|
|
7656d1e0d8 | ||
|
|
cef0ad7efa | ||
|
|
592ead6674 | ||
|
|
f6d298f228 | ||
|
|
44a2f9e859 | ||
|
|
eb159d0702 | ||
|
|
d47197ab3b | ||
|
|
b4585cabd4 | ||
|
|
dd15de6e94 | ||
|
|
c5dab7208f | ||
|
|
4a5d53a020 | ||
|
|
cc163b73e1 | ||
|
|
d1682b78c6 | ||
|
|
52827c3be6 | ||
|
|
3869fc590d | ||
|
|
ba4f4c8137 | ||
|
|
7f372c1f16 | ||
|
|
4624e7cab2 | ||
|
|
95bc331dc9 | ||
|
|
90b22316cb | ||
|
|
dc1aec75ba | ||
|
|
f60b3f1f4f | ||
|
|
3e118dcca6 | ||
|
|
12aac4f52d | ||
|
|
97625d2750 | ||
|
|
ef600fda79 | ||
|
|
bd6364deac | ||
|
|
1fbdf1fce1 | ||
|
|
6060013110 | ||
|
|
755cf731f0 | ||
|
|
6246af2979 | ||
|
|
6b27f13396 | ||
|
|
f563f69afd | ||
|
|
354914105e | ||
|
|
b154b6de64 | ||
|
|
7d55d52608 | ||
|
|
ce337e8fff | ||
|
|
72b6f46c4b | ||
|
|
547af6dd77 | ||
|
|
70119478a5 | ||
|
|
10b2aecc12 | ||
|
|
565d38f085 | ||
|
|
9adeb60abe | ||
|
|
d748241727 | ||
|
|
77ee8b1743 | ||
|
|
5eef45b49a | ||
|
|
79f7dd1519 | ||
|
|
919dec6809 | ||
|
|
f00f73491d | ||
|
|
c7cf73812c | ||
|
|
80fbab35dc | ||
|
|
9f60ce51c7 | ||
|
|
343e13d1ac | ||
|
|
ca175ab164 | ||
|
|
ebad166b12 | ||
|
|
92f5fd7cd6 | ||
|
|
507319fa0f | ||
|
|
163e17fb3f | ||
|
|
290f3e2845 | ||
|
|
516e3c4cd9 | ||
|
|
e419030339 | ||
|
|
fe739cd644 | ||
|
|
dd5e9c08aa | ||
|
|
70c2404b20 | ||
|
|
fb56430213 | ||
|
|
083b439049 | ||
|
|
a8ac109a50 | ||
|
|
1813baaa6f | ||
|
|
5b5171b9bc | ||
|
|
fbe31f3232 | ||
|
|
b530af1ff6 | ||
|
|
8ec91f7a40 | ||
|
|
41b9411d06 | ||
|
|
3040eee59c | ||
|
|
10033be4b5 | ||
|
|
914067038a | ||
|
|
bf9f08826b | ||
|
|
da27b80e82 | ||
|
|
e6c9c7798e | ||
|
|
d0b7b956e8 | ||
|
|
0301064ed5 | ||
|
|
b8595f8d59 | ||
|
|
d120c8e031 | ||
|
|
aa91b5b20b | ||
|
|
84dfaa0a77 | ||
|
|
9987c0c13f | ||
|
|
b7a3e276f1 | ||
|
|
d9774272ce | ||
|
|
3b967933ca | ||
|
|
665aad40a8 | ||
|
|
527f429c20 | ||
|
|
85803b11b4 | ||
|
|
6d3f3d1353 | ||
|
|
b70748e1b8 | ||
|
|
c6406ab0ea | ||
|
|
28c750caf9 | ||
|
|
ab22780598 | ||
|
|
1b2e0fb58e | ||
|
|
6616234393 | ||
|
|
e33420bab7 | ||
|
|
17dcbb25c3 | ||
|
|
1b36c40959 | ||
|
|
db2cd8501e | ||
|
|
fd6348adbe | ||
|
|
e3c9c16add | ||
|
|
d17f8f90b8 | ||
|
|
bc9182e37f | ||
|
|
68923dd95f | ||
|
|
ab3449243f | ||
|
|
a8f6e66ab4 | ||
|
|
f3cb2aab87 | ||
|
|
d9c6522685 | ||
|
|
7074a63f95 | ||
|
|
3e005e5032 | ||
|
|
f93e8e221a | ||
|
|
cc7433025f | ||
|
|
aa17482a6e | ||
|
|
7f0f6e7c4d | ||
|
|
2fc9d587d9 | ||
|
|
1d950d9640 | ||
|
|
3b63f88669 | ||
|
|
4f35e8348d | ||
|
|
36c76b96b3 | ||
|
|
2fe98577b6 | ||
|
|
dadeb2670b | ||
|
|
aedca9e969 | ||
|
|
e1ca6bbc94 | ||
|
|
d41ef023ff | ||
|
|
30b769db13 | ||
|
|
ac7bd33f08 | ||
|
|
69908c1ab5 | ||
|
|
f8cdcde27c | ||
|
|
948d599390 | ||
|
|
4be1ab3cac | ||
|
|
fd5132b317 | ||
|
|
f021faf0d3 | ||
|
|
2b562ace12 | ||
|
|
a56d24b9dc | ||
|
|
607b59a978 | ||
|
|
b8eb2c708e | ||
|
|
7bffc44db0 | ||
|
|
1099ce055a | ||
|
|
de5e958bf3 | ||
|
|
ab6494a2f6 | ||
|
|
967fbd2608 | ||
|
|
e6ffd2e16d | ||
|
|
8ff0835e4e | ||
|
|
301df2e1d1 | ||
|
|
4c4f79494b | ||
|
|
bcd998d690 | ||
|
|
7ab3c18762 | ||
|
|
ddb426674f | ||
|
|
6c93a6c1a8 | ||
|
|
28d0ae9b75 | ||
|
|
742a6b244f | ||
|
|
4c5fcf5310 | ||
|
|
aaf5f4d354 | ||
|
|
5414cbcc14 | ||
|
|
3104239a9f | ||
|
|
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 | ||
|
|
3ab5a4daf3 | ||
|
|
f7672e66ab | ||
|
|
61d926a23d | ||
|
|
5ab364f0ab | ||
|
|
734d2b2414 | ||
|
|
0763a96f34 | ||
|
|
2ae4ba68e4 | ||
|
|
750adabba1 | ||
|
|
a914a69051 | ||
|
|
beacc319b9 | ||
|
|
c48b7b980b | ||
|
|
d553483dcb | ||
|
|
c9f1a61bf5 | ||
|
|
84d58bf2d9 | ||
|
|
df35528a53 | ||
|
|
405f6e91de | ||
|
|
ac072e5a88 | ||
|
|
ca0b098866 | ||
|
|
3fd13dd47c | ||
|
|
38bc7e1bde | ||
|
|
6497d770dd | ||
|
|
2410fa6578 | ||
|
|
32e1623998 | ||
|
|
31799ad144 | ||
|
|
b60f7ba22e | ||
|
|
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 | ||
|
|
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 |
No files matched your search
+90
-10
@@ -1,4 +1,15 @@
|
||||
# Changelog
|
||||
## [1.?.?]()
|
||||
### Developer
|
||||
- Various breaking changes have been made to ElementalTypes, Moves and other registries, Cobblemon has migrated to make use of proper Minecraft registries.
|
||||
- DamageCategory is now an Enum.
|
||||
- Gender has been moved to an API package.
|
||||
|
||||
### Datapack & Resourcepack Creators
|
||||
- Pokémon types can now be created via datapacks. Data path is ``your_mod_namespace/cobblemon/elemental_types/path_to_your_file``.
|
||||
- Custom moves are now possible via datapacks, these require JavaScript/TypeScript programming knowledge. Data path is ``your_mod_namespace/cobblemon/moves/path_to_your_file``.
|
||||
- Pokémon species data path has been changed to ``your_mod_namespace/cobblemon/species/path_to_your_file``.
|
||||
|
||||
## [1.4.0](#1-4-0)
|
||||
### Additions
|
||||
- Added nicknaming.
|
||||
@@ -15,9 +26,7 @@
|
||||
- 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 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, X Attack, X Defense, X Special Attack, X Special Defense, X Speed, X Accuracy, Dire Hit, Guard Spec, Power Anklet, Power Band, Power Belt, Power Bracelet, Power Lens, Power Weight, Bright Powder, Destiny Knot
|
||||
- Added cries for Farfetchd, Galarian Farfetched, Rookidee, Corvisquire, Corviknight, Caterpie, Metapod, Butterfree, Weedle, Kakuna, Beedrill, Pidgey, Pidgeotto, Pidgeot, Rattata, Raticate, Clefairy, Clefable, Tauros, Eevee, Vaporeon, Flareon, Jolteon, Chatot, Darmanitan, Darumaka, Lucario, Mimikyu, Quagsire, Riolu, Wooper, Cleffa, Miltank, Buneary, Lopunny, Wooloo, Dubwool.
|
||||
- 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.
|
||||
@@ -87,7 +96,7 @@
|
||||
- Cacnea
|
||||
- Cacturne
|
||||
- Poochyena
|
||||
- Mightyenna
|
||||
- Mightyena
|
||||
- Wingull
|
||||
- Pelipper
|
||||
- Numel
|
||||
@@ -137,12 +146,12 @@
|
||||
|
||||
#### Gen 5
|
||||
|
||||
- Bouffolant
|
||||
- Bouffalant
|
||||
- Roggenrola
|
||||
- Boldore
|
||||
- Gigalith
|
||||
- Venepede
|
||||
- Whirlpede
|
||||
- Venipede
|
||||
- Whirlipede
|
||||
- Scolipede
|
||||
- Yamask
|
||||
- Cofagrigus
|
||||
@@ -164,7 +173,7 @@
|
||||
|
||||
- Scatterbug
|
||||
- Spewpa
|
||||
- Vivillion
|
||||
- Vivillon
|
||||
- Skrelp
|
||||
- Dragalge
|
||||
- Bunnelby
|
||||
@@ -192,6 +201,8 @@
|
||||
- Golisopod
|
||||
- Crabrawler
|
||||
- Crabominable
|
||||
- Mudbray
|
||||
- Mudsdale
|
||||
|
||||
#### Gen 8
|
||||
|
||||
@@ -218,7 +229,7 @@
|
||||
|
||||
#### Gen 9
|
||||
|
||||
- Sprigatitto
|
||||
- Sprigatito
|
||||
- Floragato
|
||||
- Meowscarada
|
||||
- Fuecoco
|
||||
@@ -289,6 +300,74 @@
|
||||
- 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.
|
||||
- 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
|
||||
@@ -319,7 +398,7 @@
|
||||
- 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 Shedinja evolving to use the consumed PokéBall and removed the held item.
|
||||
- Fixed non-existent species in spawn pool file causing random species to spawn.
|
||||
- Fixed shearing Pokémon dropping 0-2 wool instead of 1-3.
|
||||
- Fixed some alignment issues in the stat hexagon of the summary menu.
|
||||
@@ -334,6 +413,7 @@
|
||||
- Fixed Primeape, Qwilfish and Stantler not being able to evolve and by proxy the ``use_move`` evolution requirement.
|
||||
- Fixed Bramblin, Pawmo, and Rellor not being able to evolve and by proxy the ``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, ThrownPokeballHitEvent, PokemonSentEvent, PokemonRecalledEvent.
|
||||
@@ -343,6 +423,7 @@
|
||||
- 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+).
|
||||
|
||||
### 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:
|
||||
@@ -393,7 +474,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 {
|
||||
|
||||
@@ -27,6 +27,8 @@ 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)
|
||||
|
||||
@@ -35,6 +37,9 @@ dependencies {
|
||||
|
||||
//shadowCommon group: 'commons-io', name: 'commons-io', version: '2.6'
|
||||
|
||||
// For datastore
|
||||
modCompileOnly(libs.mongoDriverCore)
|
||||
modCompileOnly(libs.mongoDriverSync)
|
||||
|
||||
testRuntimeOnly(libs.junitEngine)
|
||||
testImplementation(libs.junitApi)
|
||||
|
||||
@@ -9,36 +9,30 @@
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.block.PreEmptsExplosion;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectListIterator;
|
||||
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.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(value = Explosion.class)
|
||||
public class ExplosionMixin {
|
||||
|
||||
@Shadow @Final private World world;
|
||||
|
||||
@Inject(
|
||||
@Redirect(
|
||||
method = "affectWorld",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/block/BlockState;getBlock()Lnet/minecraft/block/Block;"
|
||||
),
|
||||
locals = LocalCapture.CAPTURE_FAILSOFT
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/World;getBlockState(Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/block/BlockState;",
|
||||
ordinal = 0
|
||||
)
|
||||
)
|
||||
public void cobblemon$whenExploded(boolean particles, CallbackInfo ci, boolean bl, ObjectArrayList<?> objectArrayList, boolean bl2, ObjectListIterator<?> var5, BlockPos blockPos, BlockState blockState) {
|
||||
if (blockState.getBlock() instanceof PreEmptsExplosion preExplosionBlock) {
|
||||
preExplosionBlock.whenExploded(world, blockState, blockPos);
|
||||
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,52 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonBiomeTags;
|
||||
import com.cobblemon.mod.common.music.CustomMusic;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
import net.minecraft.registry.entry.RegistryEntry;
|
||||
import net.minecraft.sound.MusicSound;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
@Mixin(MinecraftClient.class)
|
||||
public class MinecraftClientMixin {
|
||||
|
||||
@Shadow
|
||||
public ClientPlayerEntity player;
|
||||
|
||||
@Inject(method = "getMusicType", at = @At("RETURN"), cancellable = true)
|
||||
private void cobblemon$getMusicType(CallbackInfoReturnable<MusicSound> cir) {
|
||||
|
||||
if (this.player != null) {
|
||||
RegistryEntry<Biome> playerBiome = this.player.getWorld().getBiome(this.player.getBlockPos());
|
||||
|
||||
List<MusicSound> possibleTracks = new ArrayList<>();
|
||||
|
||||
possibleTracks.add(cir.getReturnValue());
|
||||
|
||||
// Custom BiomeTag check to add music to certain biomes
|
||||
if (playerBiome.isIn(CobblemonBiomeTags.IS_ABYSS)) {
|
||||
possibleTracks.add(CustomMusic.DEEP_DARK);
|
||||
}
|
||||
|
||||
cir.setReturnValue(possibleTracks.get(new Random().nextInt(possibleTracks.size())));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.advancement.CobblemonCriteria
|
||||
@@ -29,6 +31,7 @@ import com.cobblemon.mod.common.api.pokeball.catching.calculators.CaptureCalcula
|
||||
import com.cobblemon.mod.common.api.pokeball.catching.calculators.CaptureCalculators
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.pokemon.effect.ShoulderEffectRegistry
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.adapters.EvolutionRegistry
|
||||
import com.cobblemon.mod.common.api.pokemon.experience.ExperienceCalculator
|
||||
import com.cobblemon.mod.common.api.pokemon.experience.ExperienceGroups
|
||||
import com.cobblemon.mod.common.api.pokemon.experience.StandardExperienceCalculator
|
||||
@@ -40,6 +43,7 @@ import com.cobblemon.mod.common.api.pokemon.stats.EvCalculator
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Generation8EvCalculator
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.StatProvider
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
|
||||
import com.cobblemon.mod.common.api.registry.CobblemonRegistryKeys
|
||||
import com.cobblemon.mod.common.api.scheduling.ScheduledTaskTracker
|
||||
import com.cobblemon.mod.common.api.spawning.BestSpawner
|
||||
import com.cobblemon.mod.common.api.spawning.CobblemonSpawningProspector
|
||||
@@ -48,14 +52,18 @@ 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.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.battles.BagItems
|
||||
import com.cobblemon.mod.common.battles.BattleFormat
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
@@ -63,7 +71,12 @@ import com.cobblemon.mod.common.battles.BattleSide
|
||||
import com.cobblemon.mod.common.battles.ShowdownThread
|
||||
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.command.argument.*
|
||||
import com.cobblemon.mod.common.command.argument.MoveArgumentType
|
||||
import com.cobblemon.mod.common.command.argument.PartySlotArgumentType
|
||||
import com.cobblemon.mod.common.command.argument.PokemonArgumentType
|
||||
import com.cobblemon.mod.common.command.argument.PokemonPropertiesArgumentType
|
||||
import com.cobblemon.mod.common.command.argument.PokemonStoreArgumentType
|
||||
import com.cobblemon.mod.common.command.argument.SpawnBucketArgumentType
|
||||
import com.cobblemon.mod.common.config.CobblemonConfig
|
||||
import com.cobblemon.mod.common.config.LastChangedVersion
|
||||
import com.cobblemon.mod.common.config.constraint.IntConstraint
|
||||
@@ -83,15 +96,33 @@ 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
|
||||
import com.cobblemon.mod.common.trade.TradeManager
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.ifDedicatedServer
|
||||
import com.cobblemon.mod.common.util.isLaterVersion
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import com.cobblemon.mod.common.util.removeAmountIf
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import com.cobblemon.mod.common.world.feature.CobblemonPlacedFeatures
|
||||
import com.cobblemon.mod.common.world.feature.ore.CobblemonOrePlacedFeatures
|
||||
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
|
||||
import com.mongodb.ConnectionString
|
||||
import com.mongodb.MongoClientSettings
|
||||
import com.mongodb.client.MongoClient
|
||||
import com.mongodb.client.MongoClients
|
||||
import java.io.File
|
||||
import java.io.FileReader
|
||||
import java.io.FileWriter
|
||||
import java.io.PrintWriter
|
||||
import java.util.UUID
|
||||
import kotlin.properties.Delegates
|
||||
import kotlin.reflect.full.memberProperties
|
||||
import kotlin.reflect.jvm.isAccessible
|
||||
import kotlin.reflect.jvm.javaField
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.command.argument.serialize.ConstantArgumentSerializer
|
||||
import net.minecraft.entity.data.TrackedDataHandlerRegistry
|
||||
@@ -101,15 +132,6 @@ import net.minecraft.registry.RegistryKey
|
||||
import net.minecraft.util.WorldSavePath
|
||||
import net.minecraft.world.World
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import java.io.File
|
||||
import java.io.FileReader
|
||||
import java.io.FileWriter
|
||||
import java.io.PrintWriter
|
||||
import java.util.*
|
||||
import kotlin.properties.Delegates
|
||||
import kotlin.reflect.full.memberProperties
|
||||
import kotlin.reflect.jvm.isAccessible
|
||||
import kotlin.reflect.jvm.javaField
|
||||
|
||||
object Cobblemon {
|
||||
const val MODID = "cobblemon"
|
||||
@@ -144,6 +166,7 @@ object Cobblemon {
|
||||
|
||||
fun preInitialize(implementation: CobblemonImplementation) {
|
||||
this.implementation = implementation
|
||||
this.queueRegistryCreation()
|
||||
implementation.registerPermissionValidator()
|
||||
implementation.registerSoundEvents()
|
||||
implementation.registerBlocks()
|
||||
@@ -171,6 +194,8 @@ object Cobblemon {
|
||||
|
||||
ShoulderEffectRegistry.register()
|
||||
|
||||
EvolutionRegistry.init()
|
||||
|
||||
DATA_SYNCHRONIZED.subscribe {
|
||||
storage.onPlayerDataSync(it)
|
||||
playerData.get(it).sendToPlayer(it)
|
||||
@@ -236,7 +261,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)
|
||||
@@ -249,15 +273,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) } }
|
||||
)
|
||||
@@ -419,4 +480,8 @@ object Cobblemon {
|
||||
this.implementation.registerCommandArgument(cobblemonResource("pokemon_store"), PokemonStoreArgumentType::class, ConstantArgumentSerializer.of(PokemonStoreArgumentType::pokemonStore))
|
||||
}
|
||||
|
||||
private fun queueRegistryCreation() {
|
||||
this.implementation.createRegistry(CobblemonRegistryKeys.ELEMENTAL_TYPE, ElementalType.CODEC, ElementalType.NETWORK_CODEC)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -306,7 +306,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -8,18 +8,22 @@
|
||||
|
||||
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 com.mojang.serialization.Codec
|
||||
import kotlin.reflect.KClass
|
||||
import net.minecraft.advancement.criterion.Criterion
|
||||
import net.minecraft.command.argument.serialize.ArgumentSerializer
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
import net.minecraft.network.listener.ClientPlayPacketListener
|
||||
import net.minecraft.network.packet.Packet
|
||||
import net.minecraft.registry.Registry
|
||||
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
|
||||
@@ -29,6 +33,7 @@ import net.minecraft.world.GameRules
|
||||
import net.minecraft.world.biome.Biome
|
||||
import net.minecraft.world.gen.GenerationStep
|
||||
import net.minecraft.world.gen.feature.PlacedFeature
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
interface CobblemonImplementation {
|
||||
val modAPI: ModAPI
|
||||
@@ -160,6 +165,41 @@ 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>
|
||||
/**
|
||||
* Queue the creation of a registry in the implementation.
|
||||
* This registry will not be immediately available for access.
|
||||
*
|
||||
* @param T The type of the registry entries.
|
||||
* @param registryKey The [RegistryKey] of this registry.
|
||||
* @param codec The [Codec] used for this registry entries to be parsed on the server.
|
||||
* @param networkCodec The [Codec] used when the client is synchronizing this registry from the server. If this is null the client will not require the registry aka not load any data.
|
||||
*
|
||||
* @see getRegistry
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
fun <T> createRegistry(registryKey: RegistryKey<Registry<T>>, codec: Codec<T>, networkCodec: Codec<T>? = null)
|
||||
|
||||
/**
|
||||
* Fetches a registry.
|
||||
* This is intended for Cobblemon registries but can be used for any.
|
||||
*
|
||||
* @param T The type of the registry.
|
||||
* @param registryKey The [RegistryKey] of the registry being fetched.
|
||||
* @return The registry if present.
|
||||
*
|
||||
* @throws Exception If the registry doesn't exist or isn't loaded yet.
|
||||
* @see createRegistry
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
fun <T> getRegistry(registryKey: RegistryKey<Registry<T>>): Registry<T>
|
||||
|
||||
}
|
||||
|
||||
enum class ModAPI {
|
||||
|
||||
@@ -338,7 +338,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()
|
||||
|
||||
@@ -55,11 +55,8 @@ import com.cobblemon.mod.common.net.messages.client.battle.*
|
||||
import com.cobblemon.mod.common.net.messages.client.callback.OpenMoveCallbackPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyCallbackPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyMoveCallbackPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.AbilityRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.BerryRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.MovesRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.*
|
||||
import com.cobblemon.mod.common.net.messages.client.data.PropertiesCompletionRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.UnlockReloadPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pasture.ClosePasturePacket
|
||||
@@ -211,6 +208,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())
|
||||
|
||||
@@ -88,9 +88,6 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
|
||||
@JvmField
|
||||
val MULCH_REMOVE = this.create("mulch.remove")
|
||||
|
||||
@JvmField
|
||||
val MUSIC_DEEP_DARK = this.create("music.deep_dark")
|
||||
|
||||
|
||||
private fun create(name: String): SoundEvent = this.create(name, SoundEvent.of(cobblemonResource(name)))
|
||||
}
|
||||
@@ -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)))
|
||||
)
|
||||
|
||||
/**
|
||||
|
||||
+6
-1
@@ -8,6 +8,7 @@
|
||||
|
||||
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
|
||||
@@ -30,7 +31,11 @@ class CaughtPokemonCriterionCondition(id: Identifier, predicate: LootContextPred
|
||||
|
||||
override fun fromJson(json: JsonObject) {
|
||||
super.fromJson(json)
|
||||
type = json.get("type")?.asString ?: "any"
|
||||
var stored = json.get("type")?.asString
|
||||
if (stored != null && stored != "any") {
|
||||
stored = stored.asIdentifierDefaultingNamespace().toString()
|
||||
}
|
||||
type = stored ?: "any"
|
||||
}
|
||||
|
||||
override fun matches(player: ServerPlayerEntity, context: CountablePokemonTypeContext): Boolean {
|
||||
|
||||
+4
-1
@@ -30,7 +30,10 @@ class LevelUpCriterionCondition(id: Identifier, entity: LootContextPredicate) :
|
||||
override fun matches(player: ServerPlayerEntity, context: LevelUpContext): Boolean {
|
||||
val preEvo = context.pokemon.preEvolution == null
|
||||
val hasEvolution = !context.pokemon.evolutions.none()
|
||||
val evolutionCheck = !(preEvo == hasEvolution)
|
||||
var evolutionCheck = true
|
||||
if (preEvo || hasEvolution) {
|
||||
evolutionCheck = !(preEvo == hasEvolution)
|
||||
}
|
||||
return level == context.level && evolutionCheck == evolved
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.api.PrioritizedList
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.DataResult
|
||||
|
||||
/**
|
||||
* A pool of potential abilities, as a [PrioritizedList]. The added logic of this subclass
|
||||
@@ -35,4 +37,22 @@ open class AbilityPool : PrioritizedList<PotentialAbility>() {
|
||||
Exception().printStackTrace()
|
||||
return Abilities.first().create() to Priority.LOWEST
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmField
|
||||
val CODEC: Codec<AbilityPool> = Codec.list(Codec.STRING)
|
||||
.comapFlatMap(
|
||||
{ raw ->
|
||||
val pool = AbilityPool()
|
||||
if (raw.isEmpty()) {
|
||||
return@comapFlatMap DataResult.error { "Cannot create ability pool from an empty list" }
|
||||
}
|
||||
raw.forEach { }
|
||||
DataResult.success(pool)
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,9 +10,12 @@ package com.cobblemon.mod.common.api.abilities
|
||||
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
import com.google.gson.JsonElement
|
||||
import com.mojang.serialization.Codec
|
||||
|
||||
interface PotentialAbilityType<T : PotentialAbility> {
|
||||
fun parseFromJSON(element: JsonElement): T?
|
||||
|
||||
fun codec(): Codec<out PotentialAbilityType<*>>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,6 +48,10 @@ object CommonAbilityType : PotentialAbilityType<CommonAbility> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun codec(): Codec<out PotentialAbilityType<*>> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
|
||||
open class CommonAbility(override val template: AbilityTemplate) : PotentialAbility {
|
||||
|
||||
@@ -10,6 +10,8 @@ package com.cobblemon.mod.common.api.ai
|
||||
|
||||
import com.cobblemon.mod.common.api.serialization.StringIdentifiedObjectAdapter
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.DataResult
|
||||
import net.minecraft.entity.ai.TargetPredicate
|
||||
import net.minecraft.util.math.Box
|
||||
|
||||
@@ -35,7 +37,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 }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +46,11 @@ interface SleepDepth {
|
||||
"normal" to normal
|
||||
)
|
||||
val adapter = StringIdentifiedObjectAdapter { depths[it] ?: throw IllegalArgumentException("Unknown sleep depth: $it") }
|
||||
|
||||
val CODEC: Codec<SleepDepth> = Codec.STRING.flatXmap(
|
||||
{ string -> depths[string]?.let { DataResult.success(it) } ?: DataResult.error { "Cannot find a SleepDepth with the ID $string" } },
|
||||
{ depth -> depths.entries.firstOrNull { it.value == depth }?.let { DataResult.success(it.key) } ?: DataResult.error { "Cannot resolve a ID for the SleepDepth $depth" } }
|
||||
)
|
||||
}
|
||||
|
||||
fun canSleep(pokemonEntity: PokemonEntity): Boolean
|
||||
|
||||
+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].
|
||||
*
|
||||
|
||||
@@ -66,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 (BerryBlock.getMulch(state) == MulchVariant.RICH) {
|
||||
yield = yield.times(1.2).coerceAtMost(maxYield().toDouble()).toInt()
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -299,8 +298,7 @@ class Berry(
|
||||
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)
|
||||
|
||||
+20
@@ -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.api.conditional
|
||||
|
||||
import net.minecraft.registry.Registry
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
class RegistryElementCondition<T>(val element: T, private val source: Identifier) : RegistryLikeCondition<T> {
|
||||
|
||||
override fun fits(value: T, registry: Registry<T>): Boolean = value == this.element
|
||||
|
||||
override fun pretty(): String = this.source.toString()
|
||||
|
||||
}
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.conditional
|
||||
|
||||
import com.google.gson.JsonDeserializationContext
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.JsonElement
|
||||
import java.lang.reflect.Type
|
||||
|
||||
/**
|
||||
* Adapter for [RegistryLikeCondition]s. Its logic is that the implementation of this adapter must
|
||||
* provide a list of resolvers, and the deserializer will find the first resolver that returns a non-null
|
||||
* value for the [JsonElement].
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since July 17th, 2022
|
||||
*/
|
||||
interface RegistryLikeAdapter<B> : JsonDeserializer<RegistryLikeCondition<B>> {
|
||||
val registryLikeConditions: MutableList<(JsonElement) -> RegistryLikeCondition<B>?>
|
||||
|
||||
override fun deserialize(json: JsonElement, type: Type, ctx: JsonDeserializationContext): RegistryLikeCondition<B> {
|
||||
return registryLikeConditions.firstNotNullOfOrNull { it(json) }
|
||||
?: throw IllegalArgumentException("Unable to deserialize $json")
|
||||
}
|
||||
}
|
||||
+43
-2
@@ -8,7 +8,11 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.conditional
|
||||
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.DataResult
|
||||
import net.minecraft.registry.Registry
|
||||
import net.minecraft.registry.tag.TagKey
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
/**
|
||||
* A condition which applies to an entry in some [Registry].
|
||||
@@ -16,6 +20,43 @@ import net.minecraft.registry.Registry
|
||||
* @author Hiroku
|
||||
* @since July 16th, 2022
|
||||
*/
|
||||
interface RegistryLikeCondition<T> {
|
||||
fun fits(t: T, registry: Registry<T>): Boolean
|
||||
sealed interface RegistryLikeCondition<T> {
|
||||
|
||||
/**
|
||||
* Checks if a given [value] matches this condition.
|
||||
*
|
||||
* @param value The value being matched against this condition.
|
||||
* @param registry The [Registry] of type [T] being queried.
|
||||
* @return If the condition is fulfilled.
|
||||
*/
|
||||
fun fits(value: T, registry: Registry<T>): Boolean
|
||||
|
||||
/**
|
||||
* Generates a string like human-friendly representation used by the Codec when serializing.
|
||||
*
|
||||
* @return The string like human-friendly representation.
|
||||
*/
|
||||
fun pretty(): String
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
fun <T> createCodec(registryAccessor: () -> Registry<T>): Codec<RegistryLikeCondition<T>> = Codec.STRING.comapFlatMap(
|
||||
{ string ->
|
||||
val registry = registryAccessor.invoke()
|
||||
val isTag = string.startsWith("#")
|
||||
val rawIdentifier = if (isTag) string.substringAfter("#") else string
|
||||
val identifier = Identifier.tryParse(rawIdentifier) ?: return@comapFlatMap DataResult.error { "Cannot resolve identifier/tag from the string $string" }
|
||||
if (isTag) {
|
||||
val tag = TagKey.of(registry.key, identifier)
|
||||
return@comapFlatMap DataResult.success(RegistryTagCondition(tag))
|
||||
}
|
||||
val element = registry.get(identifier) ?: return@comapFlatMap DataResult.error { "Cannot find an element in registry ${registry.key.value} with the ID $identifier" }
|
||||
return@comapFlatMap DataResult.success(RegistryElementCondition(element, identifier))
|
||||
},
|
||||
{ it.pretty() }
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.conditional
|
||||
|
||||
import com.google.gson.JsonElement
|
||||
import net.minecraft.registry.Registry
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
/**
|
||||
* A condition for some registry type which asserts that the entry must have the given [Identifier].
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since July 16th, 2022
|
||||
*/
|
||||
open class RegistryLikeIdentifierCondition<T>(val identifier: Identifier) : RegistryLikeCondition<T> {
|
||||
companion object {
|
||||
fun <T> resolver(
|
||||
constructor: (Identifier) -> RegistryLikeIdentifierCondition<T>
|
||||
): (JsonElement) -> RegistryLikeIdentifierCondition<T>? = { constructor(Identifier(it.asString)) }
|
||||
}
|
||||
override fun fits(t: T, registry: Registry<T>) = registry.getId(t) == identifier
|
||||
}
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.conditional
|
||||
|
||||
import com.google.gson.JsonElement
|
||||
import net.minecraft.registry.Registry
|
||||
import net.minecraft.registry.RegistryKey
|
||||
import net.minecraft.registry.tag.TagKey
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
/**
|
||||
* A condition for some registry type that asserts that the entry must be inside the given [TagKey]. This is presented
|
||||
* in JSONs as an identifier prefixed with a hash.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since July 16th, 2022
|
||||
*/
|
||||
open class RegistryLikeTagCondition<T>(val tag: TagKey<T>) : RegistryLikeCondition<T> {
|
||||
|
||||
companion object {
|
||||
const val PREFIX = "#"
|
||||
fun <T> resolver(
|
||||
registryKey: RegistryKey<Registry<T>>,
|
||||
constructor: (TagKey<T>) -> RegistryLikeTagCondition<T>
|
||||
): (JsonElement) -> RegistryLikeTagCondition<T>? = {
|
||||
val firstSymbol = it.asString.substring(0, 1)
|
||||
if (firstSymbol == PREFIX) {
|
||||
val identifier = Identifier(it.asString.substring(1))
|
||||
constructor(TagKey.of(registryKey, identifier))
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun fits(t: T, registry: Registry<T>): Boolean {
|
||||
// val registryHasTag = registry.containsTag(tag)
|
||||
// if (!registryHasTag) {
|
||||
//// LOGGER.warn("No tag in registry: ${tag.id}")
|
||||
// return false
|
||||
// }
|
||||
|
||||
return registry.getKey(t)
|
||||
.flatMap(registry::getEntry)
|
||||
.map { entry -> entry.isIn(tag) }
|
||||
.orElse(false)
|
||||
}
|
||||
}
|
||||
+20
@@ -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.api.conditional
|
||||
|
||||
import net.minecraft.registry.Registry
|
||||
import net.minecraft.registry.tag.TagKey
|
||||
|
||||
class RegistryTagCondition<T>(val tag: TagKey<T>) : RegistryLikeCondition<T> {
|
||||
|
||||
override fun fits(value: T, registry: Registry<T>): Boolean = registry.getEntry(value).isIn(this.tag)
|
||||
|
||||
override fun pretty(): String = "#${this.tag.id}"
|
||||
|
||||
}
|
||||
@@ -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,23 +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) ->
|
||||
if (identifier.namespace == "pixelmon") {
|
||||
return@forEach
|
||||
}
|
||||
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))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,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"
|
||||
}
|
||||
}
|
||||
@@ -8,11 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events
|
||||
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleFaintedEvent
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleFledEvent
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleStartedPostEvent
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleStartedPreEvent
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleVictoryEvent
|
||||
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
|
||||
@@ -43,6 +39,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
|
||||
@@ -105,6 +102,7 @@ object CobblemonEvents {
|
||||
@JvmField
|
||||
val ENTITY_SPAWN = CancelableObservable<SpawnEvent<*>>()
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@JvmField
|
||||
val POKEMON_ENTITY_SPAWN = ENTITY_SPAWN
|
||||
.pipe(
|
||||
|
||||
@@ -180,10 +180,10 @@ 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.variations[species.resourceIdentifier]?.getTexture(aspects, 0f).let { it -> context.put(RenderContext.TEXTURE, it) }
|
||||
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)
|
||||
|
||||
@@ -124,9 +124,6 @@ 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)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.moves
|
||||
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.mojang.serialization.Codec
|
||||
import net.minecraft.text.MutableText
|
||||
import net.minecraft.util.StringIdentifiable
|
||||
|
||||
/**
|
||||
* Represents the damage category of a [MoveTemplate]/[Move].
|
||||
*
|
||||
* @see MoveTemplate
|
||||
* @see Move
|
||||
*/
|
||||
enum class DamageCategory : StringIdentifiable {
|
||||
|
||||
PHYSICAL,
|
||||
SPECIAL,
|
||||
STATUS;
|
||||
|
||||
override fun asString(): String = this.name
|
||||
|
||||
/**
|
||||
* Resolves the display name of this damage category.
|
||||
*
|
||||
* @return The generated text.
|
||||
*/
|
||||
fun displayName(): MutableText = lang("move.category.${this.name.lowercase()}")
|
||||
|
||||
companion object {
|
||||
|
||||
val CODEC: Codec<DamageCategory> = StringIdentifiable.createCodec(DamageCategory::values)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.moves
|
||||
|
||||
import com.cobblemon.mod.common.api.moves.categories.DamageCategory
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.net.IntSize
|
||||
|
||||
@@ -8,14 +8,15 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.moves
|
||||
|
||||
import com.cobblemon.mod.common.api.moves.categories.DamageCategories
|
||||
import com.cobblemon.mod.common.api.moves.categories.DamageCategory
|
||||
import com.cobblemon.mod.common.api.registry.CobblemonRegistries
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.battles.MoveTarget
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.text.MutableText
|
||||
import net.minecraft.util.math.random.Random
|
||||
|
||||
/**
|
||||
* This class represents the base of a Move.
|
||||
@@ -48,6 +49,27 @@ open class MoveTemplate(
|
||||
val effectChances: Array<Double>
|
||||
) {
|
||||
|
||||
companion object {
|
||||
fun dummy(name: String) = Dummy(name)
|
||||
|
||||
val CODEC: Codec<MoveTemplate> = RecordCodecBuilder.create { builder ->
|
||||
builder.group(
|
||||
Codec.STRING.fieldOf("name").forGetter(MoveTemplate::name),
|
||||
Codec.INT.fieldOf("num").forGetter(MoveTemplate::num),
|
||||
CobblemonRegistries.ELEMENTAL_TYPE.codec.fieldOf("type").forGetter(MoveTemplate::elementalType),
|
||||
DamageCategory.CODEC.fieldOf("damageCategory").forGetter(MoveTemplate::damageCategory),
|
||||
Codec.DOUBLE.fieldOf("power").forGetter(MoveTemplate::power),
|
||||
MoveTarget.CODEC.fieldOf("target").forGetter(MoveTemplate::target),
|
||||
Codec.DOUBLE.fieldOf("accuracy").forGetter(MoveTemplate::accuracy),
|
||||
Codec.INT.fieldOf("pp").forGetter(MoveTemplate::pp),
|
||||
Codec.INT.fieldOf("priority").forGetter(MoveTemplate::priority),
|
||||
Codec.DOUBLE.fieldOf("critRatio").forGetter(MoveTemplate::critRatio),
|
||||
Codec.list(Codec.DOUBLE).fieldOf("effectChances").xmap({ e -> e.toTypedArray() }, { e -> e.toList() }).forGetter(MoveTemplate::effectChances)
|
||||
).apply(builder, ::MoveTemplate)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val displayName: MutableText
|
||||
get() = lang("move.$name")
|
||||
val description: MutableText
|
||||
@@ -57,8 +79,8 @@ open class MoveTemplate(
|
||||
class Dummy(name: String) : MoveTemplate(
|
||||
name = name,
|
||||
num = -1,
|
||||
elementalType = ElementalTypes.NORMAL,
|
||||
damageCategory = DamageCategories.STATUS,
|
||||
elementalType = CobblemonRegistries.ELEMENTAL_TYPE.getRandom(Random.create()).orElseThrow().value(),
|
||||
damageCategory = DamageCategory.STATUS,
|
||||
power = 0.0,
|
||||
target = MoveTarget.all,
|
||||
accuracy = 100.0,
|
||||
@@ -68,10 +90,6 @@ open class MoveTemplate(
|
||||
effectChances = emptyArray()
|
||||
)
|
||||
|
||||
companion object {
|
||||
fun dummy(name: String) = Dummy(name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the Move with full PP
|
||||
*/
|
||||
@@ -92,4 +110,5 @@ open class MoveTemplate(
|
||||
template = this
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,9 +10,8 @@ package com.cobblemon.mod.common.api.moves
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.data.DataRegistry
|
||||
import com.cobblemon.mod.common.api.moves.categories.DamageCategories
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.api.registry.CobblemonRegistries
|
||||
import com.cobblemon.mod.common.battles.MoveTarget
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.net.messages.client.data.MovesRegistrySyncPacket
|
||||
@@ -44,8 +43,8 @@ object Moves : DataRegistry {
|
||||
val id = jsMove.get("id").asString
|
||||
try {
|
||||
val num = jsMove.get("num").asInt
|
||||
val elementalType = ElementalTypes.getOrException(jsMove.get("type").asString)
|
||||
val damageCategory = DamageCategories.getOrException(jsMove.get("category").asString)
|
||||
val elementalType = CobblemonRegistries.ELEMENTAL_TYPE.first { it.showdownId() == jsMove.get("type").asString }
|
||||
val damageCategory = DamageCategory.valueOf(jsMove.get("category").asString.uppercase())
|
||||
val power = jsMove.get("basePower").asDouble
|
||||
val target = MoveTarget.fromShowdownId(jsMove.get("target").asString)
|
||||
// If not a double it's always true
|
||||
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.moves.adapters
|
||||
|
||||
import com.cobblemon.mod.common.api.moves.categories.DamageCategories
|
||||
import com.cobblemon.mod.common.api.moves.categories.DamageCategory
|
||||
import com.google.gson.JsonDeserializationContext
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonPrimitive
|
||||
import com.google.gson.JsonSerializationContext
|
||||
import com.google.gson.JsonSerializer
|
||||
import java.lang.reflect.Type
|
||||
|
||||
object DamageCategoryAdapter: JsonSerializer<DamageCategory>, JsonDeserializer<DamageCategory> {
|
||||
|
||||
override fun serialize(src: DamageCategory, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
|
||||
return JsonPrimitive(src.name)
|
||||
}
|
||||
|
||||
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): DamageCategory {
|
||||
return DamageCategories.getOrException(json.asString)
|
||||
}
|
||||
}
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.moves.categories
|
||||
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
object DamageCategories {
|
||||
private val allCategories = mutableListOf<DamageCategory>()
|
||||
|
||||
val PHYSICAL = register(
|
||||
name = "physical",
|
||||
displayName = Text.translatable("cobblemon.move.category.physical"),
|
||||
textureXMultiplier = 0
|
||||
)
|
||||
val SPECIAL = register(
|
||||
name = "special",
|
||||
displayName = Text.translatable("cobblemon.move.category.special"),
|
||||
textureXMultiplier = 1
|
||||
)
|
||||
val STATUS = register(
|
||||
name = "status",
|
||||
displayName = Text.translatable("cobblemon.move.category.status"),
|
||||
textureXMultiplier = 2
|
||||
)
|
||||
|
||||
fun register(
|
||||
name: String,
|
||||
displayName: Text,
|
||||
resourceLocation: Identifier = cobblemonResource("textures/gui/categories.png"),
|
||||
textureXMultiplier: Int
|
||||
): DamageCategory {
|
||||
return DamageCategory(
|
||||
name = name,
|
||||
displayName = displayName,
|
||||
textureXMultiplier = textureXMultiplier,
|
||||
resourceLocation = resourceLocation
|
||||
).also {
|
||||
allCategories.add(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun register(damageCategory: DamageCategory): DamageCategory {
|
||||
allCategories.add(damageCategory)
|
||||
return damageCategory
|
||||
}
|
||||
|
||||
fun get(name: String): DamageCategory? {
|
||||
return allCategories.firstOrNull { cat -> cat.name.equals(name, ignoreCase = true) }
|
||||
}
|
||||
|
||||
fun getOrException(name: String): DamageCategory {
|
||||
return allCategories.first { cat -> cat.name.equals(name, ignoreCase = true) }
|
||||
}
|
||||
|
||||
fun count() = allCategories.size
|
||||
}
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.moves.categories
|
||||
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
/**
|
||||
* Representing a DamageCategory from the Pokemon Game
|
||||
*
|
||||
* @param name: The English name used to load / find it (spaces -> _)
|
||||
* @param displayName: A Component used to display the name, normally a TranslatableText
|
||||
* @param resourceLocation: The location of the resource used in the GUI
|
||||
*/
|
||||
class DamageCategory(
|
||||
val name: String,
|
||||
val displayName: Text,
|
||||
val textureXMultiplier: Int,
|
||||
val resourceLocation: Identifier = cobblemonResource("textures/gui/categories.png")
|
||||
) {
|
||||
}
|
||||
@@ -24,7 +24,7 @@ import com.cobblemon.mod.common.api.pokemon.labels.CobblemonPokemonLabels
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.api.types.CobblemonElementalTypeTags
|
||||
import com.cobblemon.mod.common.pokeball.PokeBall
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.Gson
|
||||
@@ -136,13 +136,13 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
|
||||
createDefault("fast_ball", BaseStatModifier(Stats.SPEED, { it >= 100 }, 4F))
|
||||
createDefault("level_ball", CatchRateModifiers.LEVEL)
|
||||
// ToDo we will need fishing context here once fishing is implemented for a multiplier
|
||||
createDefault("lure_ball", CatchRateModifiers.typeBoosting(2F, ElementalTypes.WATER))
|
||||
createDefault("lure_ball", CatchRateModifiers.typeBoosting(2F, CobblemonElementalTypeTags.LURE_BALL_BOOSTING))
|
||||
createDefault("heavy_ball", CatchRateModifiers.WEIGHT_BASED)
|
||||
createDefault("love_ball", CatchRateModifiers.LOVE)
|
||||
createDefault("friend_ball", effects = listOf(CaptureEffects.friendshipSetter(150)))
|
||||
createDefault("moon_ball", CatchRateModifiers.MOON_PHASES)
|
||||
createDefault("sport_ball", MultiplierModifier(1.5F))
|
||||
createDefault("net_ball", CatchRateModifiers.typeBoosting(3F, ElementalTypes.BUG, ElementalTypes.WATER))
|
||||
createDefault("net_ball", CatchRateModifiers.typeBoosting(3F, CobblemonElementalTypeTags.NET_BALL_BOOSTING))
|
||||
createDefault("dive_ball", CatchRateModifiers.SUBMERGED_IN_WATER, waterDragValue = 0.99F)
|
||||
createDefault("nest_ball", CatchRateModifiers.NEST)
|
||||
// ToDo implement effect once pokedex is implemented, we have a custom multiplier of 2.5 instead of the official pokeball
|
||||
|
||||
+5
-4
@@ -14,7 +14,8 @@ import com.cobblemon.mod.common.api.pokemon.status.Status
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonBiomeTags
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
import com.cobblemon.mod.common.pokemon.Gender
|
||||
import com.cobblemon.mod.common.api.pokemon.gender.Gender
|
||||
import net.minecraft.registry.tag.TagKey
|
||||
|
||||
/**
|
||||
* A collection of some default usages of [CatchRateModifier]s.
|
||||
@@ -139,11 +140,11 @@ object CatchRateModifiers {
|
||||
* Used by [PokeBalls.NET_BALL].
|
||||
* Boosts the catch rate if the target is of the given types.
|
||||
*
|
||||
* @param multiplier The multiplier to be applied if the target has any of the accepted [types].
|
||||
* @param types The [ElementalType]s that will trigger the multiplier.
|
||||
* @param multiplier The multiplier to be applied if the target has any of the accepted [typeTag].
|
||||
* @param typeTag The [ElementalType] [TagKey] that will trigger the multiplier.
|
||||
* @return The multiplier modifier.
|
||||
*/
|
||||
fun typeBoosting(multiplier: Float, vararg types: ElementalType): CatchRateModifier = MultiplierModifier(multiplier) { _, pokemon -> pokemon.types.any { type -> types.contains(type) } }
|
||||
fun typeBoosting(multiplier: Float, typeTag: TagKey<ElementalType>): CatchRateModifier = MultiplierModifier(multiplier) { _, pokemon -> pokemon.types.any { type -> type.isIn(typeTag) } }
|
||||
|
||||
/**
|
||||
* Used by [PokeBalls.DREAM_BALL].
|
||||
|
||||
@@ -12,13 +12,14 @@ import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.abilities.Abilities
|
||||
import com.cobblemon.mod.common.api.pokeball.PokeBalls
|
||||
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.gender.Gender
|
||||
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.api.registry.CobblemonRegistries
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
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
|
||||
@@ -30,10 +31,12 @@ import com.cobblemon.mod.common.util.splitMap
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonObject
|
||||
import kotlin.random.Random
|
||||
import com.mojang.serialization.Codec
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.nbt.NbtElement
|
||||
import net.minecraft.nbt.NbtList
|
||||
import net.minecraft.nbt.NbtString
|
||||
import net.minecraft.registry.Registry
|
||||
import net.minecraft.text.MutableText
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
@@ -91,9 +94,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.teraType = parseRegistryValue(CobblemonRegistries.ELEMENTAL_TYPE, keyPairs, "tera_type", "tera")
|
||||
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()
|
||||
@@ -195,6 +199,17 @@ open class PokemonProperties {
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T> parseRegistryValue(registry: Registry<T>, keyPairs: MutableList<Pair<String, String?>>, vararg labels: String): T? {
|
||||
val matchingKeyPair = getMatchedKeyPair(keyPairs, labels.asIterable()) ?: return null
|
||||
val rawId = matchingKeyPair.second ?: return null
|
||||
return try {
|
||||
val id = Identifier(rawId)
|
||||
registry.get(id)
|
||||
} catch (_: InvalidIdentifierException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseForm(keyPairs: MutableList<Pair<String, String?>>): String? {
|
||||
val matchingKeyPair = getMatchedKeyPair(keyPairs, listOf("form")) ?: return null
|
||||
keyPairs.remove(matchingKeyPair)
|
||||
@@ -239,6 +254,9 @@ open class PokemonProperties {
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
val CODEC: Codec<PokemonProperties> = Codec.STRING.xmap(PokemonProperties::parse, PokemonProperties::asString)
|
||||
|
||||
}
|
||||
|
||||
var originalString: String = ""
|
||||
@@ -255,9 +273,10 @@ open class PokemonProperties {
|
||||
var ability: String? = null
|
||||
var aspects: Set<String> = emptySet()
|
||||
var status: String? = null
|
||||
var teraType: String? = null
|
||||
var teraType: ElementalType? = null
|
||||
var dmaxLevel: Int? = null
|
||||
var gmaxFactor: Boolean? = null
|
||||
var tradeable: Boolean? = null
|
||||
|
||||
var ivs: IVs? = null
|
||||
var evs: EVs? = null
|
||||
@@ -307,9 +326,11 @@ open class PokemonProperties {
|
||||
pokemon.setEV(stat.key, stat.value)
|
||||
}
|
||||
}
|
||||
teraType?.let { ElementalTypes.get(it)?.let { type -> pokemon.teraType = type } }
|
||||
teraType?.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) {
|
||||
@@ -345,9 +366,11 @@ open class PokemonProperties {
|
||||
pokemonEntity.pokemon.setEV(stat.key, stat.value)
|
||||
}
|
||||
}
|
||||
teraType?.let { ElementalTypes.get(it)?.let { type -> pokemonEntity.pokemon.teraType = type } }
|
||||
teraType?.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 {
|
||||
@@ -381,9 +404,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 }
|
||||
teraType?.takeIf { it != pokemon.teraType }?.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) }
|
||||
}
|
||||
|
||||
@@ -401,7 +425,7 @@ open class PokemonProperties {
|
||||
if (pokemonEntity.pokemon.species != species) {
|
||||
return false
|
||||
}
|
||||
} catch (e: InvalidIdentifierException) {}
|
||||
} catch (_: InvalidIdentifierException) {}
|
||||
}
|
||||
nickname?.takeIf { it != pokemonEntity.pokemon.nickname }?.let { return false }
|
||||
form?.takeIf { !it.equals(pokemonEntity.pokemon.form.name, true) }?.let { return false }
|
||||
@@ -416,9 +440,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 }
|
||||
teraType?.takeIf { it != pokemonEntity.pokemon.teraType }?.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) }
|
||||
}
|
||||
|
||||
@@ -436,7 +461,7 @@ open class PokemonProperties {
|
||||
if (properties.species != species.toString()) {
|
||||
return false
|
||||
}
|
||||
} catch (e: InvalidIdentifierException) {}
|
||||
} catch (_: InvalidIdentifierException) {}
|
||||
}
|
||||
nickname?.takeIf { it != properties.nickname }?.let { return false }
|
||||
form?.takeIf { !it.equals(properties.form, true) }?.let { return false }
|
||||
@@ -462,6 +487,7 @@ open class PokemonProperties {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -479,7 +505,7 @@ open class PokemonProperties {
|
||||
if (this.shiny == null) pokemon.shiny = Cobblemon.config.shinyRate.checkRate()
|
||||
if (this.gmaxFactor == null) pokemon.gmaxFactor = Cobblemon.config.gigantamaxFactorRate.checkRate()
|
||||
if (this.teraType == null) pokemon.teraType =
|
||||
if (Cobblemon.config.teraTypeRate.checkRate()) ElementalTypes.all().filter { !baseTypes.contains(it) }.random()
|
||||
if (Cobblemon.config.teraTypeRate.checkRate()) CobblemonRegistries.ELEMENTAL_TYPE.filter { !baseTypes.contains(it) }.random()
|
||||
else baseTypes.random()
|
||||
}
|
||||
|
||||
@@ -506,9 +532,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) }
|
||||
teraType?.let { nbt.putString(DataKeys.POKEMON_TERA_TYPE, it.id().toString()) }
|
||||
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)
|
||||
@@ -530,9 +557,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
|
||||
teraType = if (tag.contains(DataKeys.POKEMON_TERA_TYPE)) CobblemonRegistries.ELEMENTAL_TYPE.get(Identifier(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) }
|
||||
@@ -556,9 +584,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) }
|
||||
teraType?.let { json.addProperty(DataKeys.POKEMON_TERA_TYPE, it.id().toString()) }
|
||||
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)
|
||||
@@ -581,9 +610,12 @@ 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
|
||||
teraType = json.get(DataKeys.POKEMON_TERA_TYPE)?.asString?.let { CobblemonRegistries.ELEMENTAL_TYPE.get(
|
||||
Identifier(it)
|
||||
) }
|
||||
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) }
|
||||
@@ -610,9 +642,10 @@ open class PokemonProperties {
|
||||
evs?.forEach{ stat ->
|
||||
pieces.add("${stat.key}_ev=${stat.value}")
|
||||
}
|
||||
teraType?.let { pieces.add("tera_type=$it") }
|
||||
teraType?.let { pieces.add("tera_type=${it.id()}") }
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import com.cobblemon.mod.common.api.pokemon.effect.ShoulderEffect
|
||||
import com.cobblemon.mod.common.api.pokemon.effect.adapter.ShoulderEffectAdapter
|
||||
import com.cobblemon.mod.common.api.pokemon.egg.EggGroup
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.PreEvolution
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.requirement.EvolutionRequirement
|
||||
import com.cobblemon.mod.common.api.pokemon.experience.ExperienceGroup
|
||||
import com.cobblemon.mod.common.api.pokemon.experience.ExperienceGroupAdapter
|
||||
@@ -38,14 +37,14 @@ import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPack
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
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.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
|
||||
import com.cobblemon.mod.common.util.codec.toTypeAdapter
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import com.google.common.collect.HashBasedTable
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
@@ -56,6 +55,7 @@ import net.minecraft.entity.effect.StatusEffect
|
||||
import net.minecraft.item.Item
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.registry.Registries
|
||||
import net.minecraft.registry.RegistryKeys
|
||||
import net.minecraft.resource.ResourceType
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
@@ -76,11 +76,8 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
.registerTypeAdapter(ExperienceGroup::class.java, ExperienceGroupAdapter)
|
||||
.registerTypeAdapter(EntityDimensions::class.java, EntityDimensionsAdapter)
|
||||
.registerTypeAdapter(Learnset::class.java, LearnsetAdapter)
|
||||
.registerTypeAdapter(Evolution::class.java, CobblemonEvolutionAdapter)
|
||||
.registerTypeAdapter(Box::class.java, BoxAdapter)
|
||||
.registerTypeAdapter(AbilityPool::class.java, AbilityPoolAdapter)
|
||||
.registerTypeAdapter(EvolutionRequirement::class.java, CobblemonRequirementAdapter)
|
||||
.registerTypeAdapter(PreEvolution::class.java, CobblemonPreEvolutionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(Set::class.java, Evolution::class.java).type, LazySetAdapter(Evolution::class))
|
||||
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
|
||||
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
|
||||
@@ -90,9 +87,9 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
.registerTypeAdapter(SleepDepth::class.java, SleepDepth.adapter)
|
||||
.registerTypeAdapter(DropEntry::class.java, DropEntryAdapter)
|
||||
.registerTypeAdapter(NbtCompound::class.java, NbtCompoundAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, BiomeLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Block::class.java).type, BlockLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, ItemLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, RegistryLikeCondition.createCodec { server()!!.registryManager.get(RegistryKeys.BIOME) }.toTypeAdapter())
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Block::class.java).type, RegistryLikeCondition.createCodec { Registries.BLOCK }.toTypeAdapter())
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, RegistryLikeCondition.createCodec { Registries.ITEM }.toTypeAdapter())
|
||||
.registerTypeAdapter(EggGroup::class.java, EggGroupAdapter)
|
||||
.registerTypeAdapter(StatusEffect::class.java, RegistryElementAdapter<StatusEffect> { Registries.STATUS_EFFECT })
|
||||
.registerTypeAdapter(NbtItemPredicate::class.java, NbtItemPredicateAdapter)
|
||||
@@ -211,7 +208,7 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
"H" to "No Ability",
|
||||
"S" to "No Ability"
|
||||
)
|
||||
val types = (form?.types ?: species.types).map { it.name.replaceFirstChar(Char::uppercase) }
|
||||
val types = (form?.types ?: species.types).map { it.showdownId() }
|
||||
val preevo: String? = (form?.preEvolution ?: species.preEvolution)?.let { if (it.form == it.species.standardForm) createShowdownName(it.species) else "${createShowdownName(it.species)}-${it.form.name}" }
|
||||
// For the context of battles the content here doesn't matter whatsoever and due to how PokemonProperties work we can't guarantee an actual specific species is defined.
|
||||
val evos = if ((form?.evolutions ?: species.evolutions).isEmpty()) emptyList() else arrayListOf("")
|
||||
|
||||
+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)
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,9 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.egg
|
||||
|
||||
import com.mojang.serialization.Codec
|
||||
import net.minecraft.util.StringIdentifiable
|
||||
|
||||
/**
|
||||
* These represent categories which determine which Pokémon are able to interbreed.
|
||||
*
|
||||
@@ -15,7 +18,7 @@ package com.cobblemon.mod.common.api.pokemon.egg
|
||||
*
|
||||
* @property showdownID used for data synchronization with Showdown data format.
|
||||
*/
|
||||
enum class EggGroup(internal val showdownID: String) {
|
||||
enum class EggGroup(internal val showdownID: String) : StringIdentifiable {
|
||||
|
||||
MONSTER("Monster"),
|
||||
WATER_1("Water 1"),
|
||||
@@ -31,6 +34,15 @@ enum class EggGroup(internal val showdownID: String) {
|
||||
WATER_2("Water 2"),
|
||||
DITTO("Ditto"),
|
||||
DRAGON("Dragon"),
|
||||
UNDISCOVERED("Undiscovered")
|
||||
UNDISCOVERED("Undiscovered");
|
||||
|
||||
override fun asString(): String = this.name
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmField
|
||||
val CODEC: Codec<EggGroup> = StringIdentifiable.createCodec(EggGroup::values)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,12 +9,12 @@
|
||||
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.moves.BenchedMove
|
||||
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.adapters.Variant
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.requirement.EvolutionRequirement
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.activestate.ShoulderedState
|
||||
@@ -22,6 +22,7 @@ import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolut
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.LevelUpEvolution
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.TradeEvolution
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.mojang.serialization.Codec
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.sound.SoundCategory
|
||||
|
||||
@@ -61,6 +62,11 @@ interface Evolution : EvolutionLike {
|
||||
*/
|
||||
val learnableMoves: MutableSet<MoveTemplate>
|
||||
|
||||
/**
|
||||
* The [Variant] of this [Evolution].
|
||||
*/
|
||||
val variant: Variant<Evolution>
|
||||
|
||||
/**
|
||||
* Checks if the given [Pokemon] passes all the conditions and is ready to evolve.
|
||||
*
|
||||
@@ -120,4 +126,12 @@ interface Evolution : EvolutionLike {
|
||||
fun applyTo(pokemon: Pokemon) {
|
||||
result.apply(pokemon)
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
fun codec(): Codec<out Evolution> = this.variant.codec
|
||||
|
||||
}
|
||||
+8
-8
@@ -8,9 +8,10 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.evolution
|
||||
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
import com.cobblemon.mod.common.pokemon.evolution.StandardPreEvolution
|
||||
import com.cobblemon.mod.common.api.pokemon.species.Species
|
||||
import com.cobblemon.mod.common.api.registry.CobblemonRegistries
|
||||
import com.mojang.serialization.Codec
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
/**
|
||||
* Represents the previous stage in the evolutionary line of a given Pokémon.
|
||||
@@ -19,15 +20,14 @@ import com.cobblemon.mod.common.pokemon.evolution.StandardPreEvolution
|
||||
* @author Licious
|
||||
* @since March 22nd, 2022
|
||||
*/
|
||||
interface PreEvolution {
|
||||
class PreEvolution internal constructor(private val speciesKey: Identifier){
|
||||
|
||||
val species: Species
|
||||
|
||||
val form: FormData
|
||||
val species: Species by lazy { CobblemonRegistries.SPECIES.get(this.speciesKey)!! }
|
||||
|
||||
companion object {
|
||||
|
||||
fun of(species: Species, form: FormData = species.standardForm): PreEvolution = StandardPreEvolution(species, form)
|
||||
@JvmField
|
||||
val CODEC: Codec<PreEvolution> = Identifier.CODEC.xmap(::PreEvolution, PreEvolution::speciesKey)
|
||||
|
||||
}
|
||||
|
||||
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.evolution.adapters
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonEvolutionAdapter
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.JsonSerializer
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* Saves and loads [Evolution]s with JSON.
|
||||
* For the default implementation see [CobblemonEvolutionAdapter].
|
||||
*
|
||||
* @author Licious
|
||||
* @since March 20th, 2022
|
||||
*/
|
||||
interface EvolutionAdapter : JsonDeserializer<Evolution>, JsonSerializer<Evolution> {
|
||||
|
||||
/**
|
||||
* Registers the given type of [Evolution] to it's associated ID for deserialization.
|
||||
*
|
||||
* @param T The type of [Evolution].
|
||||
* @param id The id of the evolution event.
|
||||
* @param type The [KClass] of the type.
|
||||
*/
|
||||
fun <T : Evolution> registerType(id: String, type: KClass<T>)
|
||||
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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.pokemon.evolution.adapters
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.requirement.EvolutionRequirement
|
||||
import com.cobblemon.mod.common.pokemon.evolution.requirements.*
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.BlockClickEvolution
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolution
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.LevelUpEvolution
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.TradeEvolution
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.Lifecycle
|
||||
import net.minecraft.registry.RegistryKey
|
||||
import net.minecraft.registry.SimpleRegistry
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
object EvolutionRegistry {
|
||||
|
||||
private val EVOLUTION_REGISTRY = SimpleRegistry<Variant<Evolution>>(RegistryKey.ofRegistry(cobblemonResource("evolution_variants")), Lifecycle.stable())
|
||||
private val REQUIREMENT_REGISTRY = SimpleRegistry<Variant<EvolutionRequirement>>(RegistryKey.ofRegistry(cobblemonResource("evolution_requirements")), Lifecycle.stable())
|
||||
|
||||
/**
|
||||
* A [Codec] for [Evolution].
|
||||
* If you wish to register more variants see [registerEvolutionVariant].
|
||||
*/
|
||||
@JvmField
|
||||
val EVOLUTION_CODEC: Codec<Evolution> = EVOLUTION_REGISTRY.codec.dispatch("variant", Evolution::variant) { variant -> variant.codec }
|
||||
|
||||
/**
|
||||
* A [Codec] for [EvolutionRequirement].
|
||||
* If you wish to register more variants see [registerRequirementVariant].
|
||||
*/
|
||||
@JvmField
|
||||
val REQUIREMENT_CODEC: Codec<EvolutionRequirement> = REQUIREMENT_REGISTRY.codec.dispatch("variant", EvolutionRequirement::variant) { variant -> variant.codec }
|
||||
|
||||
/**
|
||||
* Registers an [Evolution] type.
|
||||
*
|
||||
* @param variant The [Variant] of an [Evolution] being registered.
|
||||
*
|
||||
* @throws IllegalStateException If the backing registry already contains an entry with the [Variant.identifier].
|
||||
* @throws AssertionError If the registry is missing an intrusive holder.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun registerEvolutionVariant(variant: Variant<Evolution>) {
|
||||
EVOLUTION_REGISTRY.add(RegistryKey.of(EVOLUTION_REGISTRY.key, variant.identifier), variant, Lifecycle.stable())
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an [EvolutionRequirement] type.
|
||||
*
|
||||
* @param variant The [Variant] of an [EvolutionRequirement] being registered.
|
||||
*
|
||||
* @throws IllegalStateException If the backing registry already contains an entry with the [Variant.identifier].
|
||||
* @throws AssertionError If the registry is missing an intrusive holder.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun registerRequirementVariant(variant: Variant<EvolutionRequirement>) {
|
||||
REQUIREMENT_REGISTRY.add(RegistryKey.of(REQUIREMENT_REGISTRY.key, variant.identifier), variant, Lifecycle.stable())
|
||||
}
|
||||
|
||||
internal fun init() {
|
||||
this.registerDefaultEvolutions()
|
||||
this.registerDefaultRequirements()
|
||||
}
|
||||
|
||||
private fun registerDefaultEvolutions() {
|
||||
this.registerEvolutionVariant(BlockClickEvolution.VARIANT)
|
||||
this.registerEvolutionVariant(ItemInteractionEvolution.VARIANT)
|
||||
this.registerEvolutionVariant(LevelUpEvolution.MAIN_VARIANT)
|
||||
this.registerEvolutionVariant(LevelUpEvolution.ALTERNATIVE_VARIANT)
|
||||
this.registerEvolutionVariant(TradeEvolution.VARIANT)
|
||||
}
|
||||
|
||||
private fun registerDefaultRequirements() {
|
||||
this.registerRequirementVariant(AllOfRequirement.VARIANT)
|
||||
this.registerRequirementVariant(AnyOfRequirement.VARIANT)
|
||||
this.registerRequirementVariant(AreaRequirement.VARIANT)
|
||||
this.registerRequirementVariant(AttackDefenceRatioRequirement.VARIANT)
|
||||
this.registerRequirementVariant(BattleCriticalHitsRequirement.VARIANT)
|
||||
this.registerRequirementVariant(BiomeRequirement.VARIANT)
|
||||
this.registerRequirementVariant(BlocksTraveledRequirement.VARIANT)
|
||||
this.registerRequirementVariant(DamageTakenRequirement.VARIANT)
|
||||
this.registerRequirementVariant(DefeatRequirement.VARIANT)
|
||||
this.registerRequirementVariant(FriendshipRequirement.VARIANT)
|
||||
this.registerRequirementVariant(HeldItemRequirement.VARIANT)
|
||||
this.registerRequirementVariant(LevelRequirement.VARIANT)
|
||||
this.registerRequirementVariant(MoonPhaseRequirement.VARIANT)
|
||||
this.registerRequirementVariant(MoveSetRequirement.VARIANT)
|
||||
this.registerRequirementVariant(MoveTypeRequirement.VARIANT)
|
||||
this.registerRequirementVariant(NoneOfRequirement.VARIANT)
|
||||
this.registerRequirementVariant(NotRequirement.VARIANT)
|
||||
this.registerRequirementVariant(PartyMemberRequirement.VARIANT)
|
||||
this.registerRequirementVariant(PokemonPropertiesRequirement.VARIANT)
|
||||
this.registerRequirementVariant(RecoilRequirement.VARIANT)
|
||||
this.registerRequirementVariant(TimeRangeRequirement.VARIANT)
|
||||
this.registerRequirementVariant(UseMoveRequirement.VARIANT)
|
||||
this.registerRequirementVariant(WeatherRequirement.VARIANT)
|
||||
this.registerRequirementVariant(WorldRequirement.VARIANT)
|
||||
}
|
||||
|
||||
}
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.evolution.adapters
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.requirement.EvolutionRequirement
|
||||
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonRequirementAdapter
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.JsonSerializer
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* Saves and loads [EvolutionRequirement]s with JSON.
|
||||
* For the default implementation see [CobblemonRequirementAdapter].
|
||||
*
|
||||
* @author Licious
|
||||
* @since March 21st, 2022
|
||||
*/
|
||||
interface RequirementAdapter : JsonDeserializer<EvolutionRequirement>, JsonSerializer<EvolutionRequirement> {
|
||||
|
||||
/**
|
||||
* Registers the given type of [EvolutionRequirement] to it's associated ID for deserialization.
|
||||
*
|
||||
* @param T The type of [EvolutionRequirement].
|
||||
* @param id The id of the evolution event.
|
||||
* @param type The [KClass] of the type.
|
||||
*/
|
||||
fun <T : EvolutionRequirement> registerType(id: String, type: KClass<T>)
|
||||
|
||||
}
|
||||
+8
-6
@@ -6,10 +6,12 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.pokemon
|
||||
package com.cobblemon.mod.common.api.pokemon.evolution.adapters
|
||||
|
||||
enum class Gender(val showdownName: String) {
|
||||
MALE("M"),
|
||||
FEMALE("F"),
|
||||
GENDERLESS("N")
|
||||
}
|
||||
import com.mojang.serialization.Codec
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
data class Variant<T>(
|
||||
val identifier: Identifier,
|
||||
val codec: Codec<out T>
|
||||
)
|
||||
+22
-1
@@ -10,7 +10,10 @@ package com.cobblemon.mod.common.api.pokemon.evolution.requirement
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.ContextEvolution
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.adapters.EvolutionRegistry
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.adapters.Variant
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.mojang.serialization.Codec
|
||||
|
||||
/**
|
||||
* Represents a requirement for an evolution.
|
||||
@@ -22,7 +25,12 @@ import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
* @author Licious
|
||||
* @since March 19th, 2022
|
||||
*/
|
||||
fun interface EvolutionRequirement {
|
||||
interface EvolutionRequirement {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
val variant: Variant<EvolutionRequirement>
|
||||
|
||||
/**
|
||||
* Checks if the given [Pokemon] satisfies the requirement.
|
||||
@@ -32,4 +40,17 @@ fun interface EvolutionRequirement {
|
||||
*/
|
||||
fun check(pokemon: Pokemon): Boolean
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
fun codec(): Codec<out EvolutionRequirement> = this.variant.codec
|
||||
|
||||
companion object {
|
||||
|
||||
val CODEC: Codec<EvolutionRequirement> = EvolutionRegistry.REQUIREMENT_CODEC
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+5
@@ -18,6 +18,7 @@ import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonPrimitive
|
||||
import com.google.gson.JsonSerializationContext
|
||||
import com.google.gson.JsonSerializer
|
||||
import com.mojang.serialization.Codec
|
||||
import java.lang.reflect.Type
|
||||
import kotlin.math.max
|
||||
import net.minecraft.text.MutableText
|
||||
@@ -74,6 +75,10 @@ interface ExperienceGroup : LevelCurve {
|
||||
override fun getExperience(level: Int) = 0
|
||||
override fun getLevel(experience: Int) = 1
|
||||
}
|
||||
|
||||
@JvmField
|
||||
val CODEC: Codec<ExperienceGroup> = Codec.STRING.xmap(ExperienceGroups::findByName, ExperienceGroup::name)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.api.pokemon.gender
|
||||
|
||||
import com.cobblemon.mod.common.api.data.ShowdownIdentifiable
|
||||
import com.cobblemon.mod.common.api.pokemon.species.Species
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Represents the [Gender] of a [Pokemon].
|
||||
* This is taken into account by [Species] when generating the [Pokemon].
|
||||
*
|
||||
* @property showdownId The ID of the [Gender] in Showdown.
|
||||
*
|
||||
* @see Species.genderSelector
|
||||
* @see Pokemon.gender
|
||||
* @see GenderSelector
|
||||
*/
|
||||
enum class Gender(private val showdownId: String) : ShowdownIdentifiable {
|
||||
|
||||
MALE("M"),
|
||||
FEMALE("F"),
|
||||
GENDERLESS("N");
|
||||
|
||||
override fun showdownId(): String = this.showdownId
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* The [Gender]s when a ratio is attached to a [GenderSelector].
|
||||
*
|
||||
* @see GenderSelector.maleRatio
|
||||
*/
|
||||
@JvmField
|
||||
val RATIO_BASED_GENDERS: Set<Gender> = EnumSet.of(MALE, FEMALE)
|
||||
|
||||
/**
|
||||
* The [Gender]s when no ratio is attached to a [GenderSelector].
|
||||
*
|
||||
* @see GenderSelector.maleRatio
|
||||
*/
|
||||
@JvmField
|
||||
val NO_RATIO_GENDERS: Set<Gender> = EnumSet.of(GENDERLESS)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.gender
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.species.Species
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.mojang.serialization.Codec
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
* Class responsible for querying possible [Gender]s of [Species] and assigning them randomly to generated [Pokemon].
|
||||
*
|
||||
* @property maleRatio The raw ratio for the [Gender.MALE] despite the naming this is able to cover all possible genders.
|
||||
*
|
||||
* @see Species.genderSelector
|
||||
*
|
||||
* @throws IllegalArgumentException If male ratio is greater than 1.
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
class GenderSelector internal constructor(private val maleRatio: Float) {
|
||||
|
||||
init {
|
||||
if (this.maleRatio < 1F) {
|
||||
throw IllegalArgumentException("Male ratio cannot be greater than 1.0")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects all the possible [Gender]s from [generate].
|
||||
*
|
||||
* @return The possible [Gender]s.
|
||||
*/
|
||||
fun possibleGenders(): Set<Gender> = if (this.maleRatio < 0F) Gender.NO_RATIO_GENDERS else Gender.RATIO_BASED_GENDERS
|
||||
|
||||
/**
|
||||
* Randomly picks a [Gender] using the [maleRatio].
|
||||
*
|
||||
* @return The generated [Gender].
|
||||
*/
|
||||
fun generate(): Gender = when {
|
||||
this.maleRatio < 0F -> Gender.GENDERLESS
|
||||
this.maleRatio == 1F || Random.nextFloat() < this.maleRatio -> Gender.MALE
|
||||
else -> Gender.FEMALE
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given [gender] is a possible selection of [generate].
|
||||
*
|
||||
* @param gender The [Gender] being checked.
|
||||
* @return If the [gender] is possible with the context in this [GenderSelector].
|
||||
*/
|
||||
fun isValid(gender: Gender): Boolean = this.possibleGenders().contains(gender)
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* A [Codec] for [GenderSelector].
|
||||
* This will (de)serialize from a primitive float.
|
||||
* It will error if the value is not between [Float.MIN_VALUE] and 1F.
|
||||
*/
|
||||
@JvmField
|
||||
val CODEC: Codec<GenderSelector> = Codec.floatRange(Float.MIN_VALUE, 1F).xmap(::GenderSelector, GenderSelector::maleRatio)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.species
|
||||
|
||||
import com.cobblemon.mod.common.api.data.ShowdownIdentifiable
|
||||
import com.cobblemon.mod.common.api.registry.CobblemonRegistries
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
/**
|
||||
* Additional data only present in species that are a variation of an existing species.
|
||||
* This is commonly referred to as a Pokémon form.
|
||||
* In the context of Cobblemon aspects are used when a form is purely cosmetic.
|
||||
* This is only present if a [Species] has actual stat related differences from the "base".
|
||||
*
|
||||
* @see <a href="https://bulbapedia.bulbagarden.net/wiki/Variant_Pok%C3%A9mon">Bulbapedias entry on variant Pokémon</a>
|
||||
*/
|
||||
class FormData internal constructor(
|
||||
private var baseSpeciesIdentifier: Identifier,
|
||||
private var battleOnly: Boolean
|
||||
) {
|
||||
|
||||
/**
|
||||
* Resolves the base species of this form.
|
||||
*
|
||||
* @return The [Species] that is the base of this form.
|
||||
*/
|
||||
fun baseSpecies(): Species = CobblemonRegistries.SPECIES.get(this.baseSpeciesIdentifier)!!
|
||||
|
||||
/**
|
||||
* Checks if this [FormData] can only exist in battles.
|
||||
*
|
||||
* @return True if this [FormData] can only exist in battles.
|
||||
*/
|
||||
fun isBattleOnly(): Boolean = this.battleOnly
|
||||
|
||||
companion object {
|
||||
|
||||
val CODEC: Codec<FormData> = RecordCodecBuilder.create { builder ->
|
||||
builder.group(
|
||||
Identifier.CODEC.fieldOf("baseSpecies").forGetter(FormData::baseSpeciesIdentifier),
|
||||
Codec.BOOL.fieldOf("battleOnly").forGetter(FormData::battleOnly)
|
||||
).apply(builder, ::FormData)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* 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.pokemon.species
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.abilities.AbilityPool
|
||||
import com.cobblemon.mod.common.api.data.ShowdownIdentifiable
|
||||
import com.cobblemon.mod.common.api.pokemon.egg.EggGroup
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.PreEvolution
|
||||
import com.cobblemon.mod.common.api.pokemon.experience.ExperienceGroup
|
||||
import com.cobblemon.mod.common.api.pokemon.gender.Gender
|
||||
import com.cobblemon.mod.common.api.pokemon.gender.GenderSelector
|
||||
import com.cobblemon.mod.common.api.pokemon.moves.Learnset
|
||||
import com.cobblemon.mod.common.api.pokemon.species.internal.*
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.StatMap
|
||||
import com.cobblemon.mod.common.api.registry.CobblemonRegistryElement
|
||||
import com.cobblemon.mod.common.api.registry.CobblemonRegistryKeys
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.ai.PokemonBehaviour
|
||||
import com.cobblemon.mod.common.util.codec.ExtraCodecs
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.entity.EntityDimensions
|
||||
import net.minecraft.registry.Registry
|
||||
import net.minecraft.registry.entry.RegistryEntry
|
||||
import net.minecraft.text.MutableText
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
import java.util.*
|
||||
|
||||
@Suppress("unused")
|
||||
class Species internal constructor(
|
||||
nationalPokedexNumber: Int,
|
||||
types: Pair<ElementalType, Optional<ElementalType>>,
|
||||
abilities: AbilityPool,
|
||||
baseStats: StatMap,
|
||||
catchRate: Int,
|
||||
genderSelector: GenderSelector,
|
||||
private val experienceData: ExperienceData,
|
||||
private val eggData: EggData,
|
||||
learnset: Learnset,
|
||||
private val behaviourData: BehaviourData,
|
||||
private val evolutionData: EvolutionData,
|
||||
baseFriendship: Int,
|
||||
evYield: StatMap,
|
||||
private val cosmeticData: CosmeticData,
|
||||
formData: Optional<FormData>
|
||||
): CobblemonRegistryElement<Species>, ShowdownIdentifiable {
|
||||
|
||||
companion object {
|
||||
|
||||
private val TYPES_CODEC = ExtraCodecs.ENTITY_DIMENSIONS
|
||||
|
||||
val codec = RecordCodecBuilder.create { builder ->
|
||||
builder.group(
|
||||
Codec.INT.fieldOf("nationalPokedexNumber").forGetter(Species::nationalPokedexNumber),
|
||||
ExtraCodecs.DUAL_TYPE_CODEC.fieldOf("types").forGetter(Species::types),
|
||||
// ToDo: abilities
|
||||
StatMap.BASE_STATS_CODEC.fieldOf("baseStats").forGetter(Species::baseStats),
|
||||
Codec.INT.fieldOf("catchRate").forGetter(Species::catchRate),
|
||||
GenderSelector.CODEC.fieldOf("genderSelector").forGetter(Species::genderSelector),
|
||||
ExperienceData.MAP_CODEC.forGetter(Species::experienceData),
|
||||
EggData.MAP_CODEC.fieldOf("eggData").forGetter(Species::eggData),
|
||||
// ToDo: learnset
|
||||
BehaviourData.MAP_CODEC.forGetter(Species::behaviourData),
|
||||
EvolutionData.MAP_CODEC.forGetter(Species::evolutionData),
|
||||
Codec.INT.fieldOf("baseFriendship").forGetter(Species::baseFriendship),
|
||||
StatMap.BASE_STATS_CODEC.fieldOf("evYield").forGetter(Species::evYield),
|
||||
CosmeticData.MAP_CODEC.forGetter(Species::cosmeticData),
|
||||
FormData.CODEC.optionalFieldOf("formData").forGetter(Species::formData)
|
||||
).apply(builder, ::Species)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val displayName: MutableText by lazy { Text.translatable("${this.id().namespace}.species.${this.id().path.replace("/", ".")}.name") }
|
||||
|
||||
var nationalPokedexNumber: Int = nationalPokedexNumber
|
||||
private set
|
||||
|
||||
var types: Pair<ElementalType, Optional<ElementalType>> = types
|
||||
private set
|
||||
|
||||
val primaryType: ElementalType get() = this.types.first
|
||||
|
||||
val secondaryType: Optional<ElementalType> get() = this.types.second
|
||||
|
||||
var abilities: AbilityPool = abilities
|
||||
private set
|
||||
|
||||
var baseStats: StatMap = baseStats
|
||||
private set
|
||||
|
||||
var catchRate: Int = catchRate
|
||||
private set
|
||||
|
||||
/**
|
||||
* Responsible for picking a [Gender] for this [Species] on [create].
|
||||
*/
|
||||
var genderSelector: GenderSelector = genderSelector
|
||||
private set
|
||||
|
||||
val shoulderMountable: Boolean get() = this.behaviourData.shoulderMountable
|
||||
|
||||
val baseExperienceYield: Int get() = this.experienceData.baseExperienceYield
|
||||
|
||||
val experienceGroup: ExperienceGroup get() = this.experienceData.experienceGroup
|
||||
|
||||
val eggCycles: Int get() = this.eggData.eggCycles
|
||||
|
||||
val eggGroups: Set<EggGroup> get() = this.eggData.eggGroups
|
||||
|
||||
var learnset: Learnset = learnset
|
||||
private set
|
||||
|
||||
val behaviour: PokemonBehaviour get() = this.behaviourData.pokemonBehaviour
|
||||
|
||||
val evolutions: Set<Evolution> get() = this.evolutionData.evolutions
|
||||
|
||||
val preEvolution: Optional<PreEvolution> get() = this.evolutionData.preEvolution
|
||||
|
||||
val baseScale: Float get() = this.cosmeticData.baseScale
|
||||
|
||||
val hitbox: EntityDimensions get() = this.cosmeticData.hitbox
|
||||
|
||||
var baseFriendship: Int = baseFriendship
|
||||
private set
|
||||
|
||||
var evYield: StatMap = evYield
|
||||
private set
|
||||
|
||||
val weight: Float get() = this.cosmeticData.weight
|
||||
|
||||
val height: Float get() = this.cosmeticData.height
|
||||
|
||||
val aspects: Set<String> get() = this.cosmeticData.aspects
|
||||
|
||||
val features: Set<String> get() = this.behaviourData.features
|
||||
|
||||
val standingEyeHeight: Float get() = this.cosmeticData.standingEyeHeight
|
||||
|
||||
val swimmingEyeHeight: Float get() = this.cosmeticData.swimmingEyeHeight
|
||||
|
||||
val flyingEyeHeight: Float get() = this.cosmeticData.flyingEyeHeight
|
||||
|
||||
var formData: Optional<FormData> = formData
|
||||
private set
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @param level
|
||||
* @return
|
||||
*/
|
||||
fun create(level: Int): Pokemon {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
/**
|
||||
* Picks a randomly generated [Gender] using the [genderSelector].
|
||||
*
|
||||
* @return The picked [Gender].
|
||||
*/
|
||||
fun pickGender(): Gender = this.genderSelector.generate()
|
||||
|
||||
/**
|
||||
* Checks if this [Species] can be of the given [gender].
|
||||
* This is checked against the [genderSelector].
|
||||
*
|
||||
* @param gender The [Gender] being checked.
|
||||
* @return If the given [gender] is possible for this [Species].
|
||||
*/
|
||||
fun canBeOfGender(gender: Gender): Boolean = this.genderSelector.isValid(gender)
|
||||
|
||||
override fun showdownId(): String {
|
||||
val id = this.id()
|
||||
val showdownId = ShowdownIdentifiable.REGEX.replace(id.path, "")
|
||||
if (id.namespace == Cobblemon.MODID) {
|
||||
return showdownId
|
||||
}
|
||||
return id.namespace + showdownId
|
||||
}
|
||||
|
||||
override fun id(): Identifier = this.registry().getId(this)!!
|
||||
|
||||
override fun registryEntry(): RegistryEntry<Species> = this.registry().getEntry(this)
|
||||
|
||||
override fun registry(): Registry<Species> = Cobblemon.implementation.getRegistry(CobblemonRegistryKeys.SPECIES)
|
||||
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.pokemon.species.internal
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.species.Species
|
||||
import com.cobblemon.mod.common.pokemon.ai.PokemonBehaviour
|
||||
import com.cobblemon.mod.common.util.codec.setCodec
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
|
||||
/**
|
||||
* Intermediate for [Species] data.
|
||||
*/
|
||||
internal data class BehaviourData(
|
||||
var pokemonBehaviour: PokemonBehaviour,
|
||||
var shoulderMountable: Boolean,
|
||||
var features: Set<String>,
|
||||
var dynamaxBlocked: Boolean,
|
||||
) {
|
||||
|
||||
companion object {
|
||||
|
||||
val MAP_CODEC: MapCodec<BehaviourData> = RecordCodecBuilder.mapCodec { builder ->
|
||||
builder.group(
|
||||
PokemonBehaviour.CODEC.fieldOf("pokemonBehaviour").forGetter(BehaviourData::pokemonBehaviour),
|
||||
Codec.BOOL.fieldOf("shoulderMountable").forGetter(BehaviourData::shoulderMountable),
|
||||
setCodec(Codec.STRING).fieldOf("features").forGetter(BehaviourData::features),
|
||||
Codec.BOOL.optionalFieldOf("dynamaxBlocked", false).forGetter(BehaviourData::dynamaxBlocked)
|
||||
).apply(builder, ::BehaviourData)
|
||||
}
|
||||
|
||||
val CODEC: Codec<BehaviourData> = MAP_CODEC.codec()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+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.api.pokemon.species.internal
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.species.Species
|
||||
import com.cobblemon.mod.common.util.codec.ExtraCodecs
|
||||
import com.cobblemon.mod.common.util.codec.setCodec
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.entity.EntityDimensions
|
||||
|
||||
/**
|
||||
* Intermediate for [Species] data.
|
||||
*/
|
||||
internal data class CosmeticData(
|
||||
var baseScale: Float,
|
||||
var hitbox: EntityDimensions,
|
||||
var weight: Float,
|
||||
var height: Float,
|
||||
var aspects: Set<String>,
|
||||
var standingEyeHeight: Float,
|
||||
var swimmingEyeHeight: Float,
|
||||
var flyingEyeHeight: Float,
|
||||
) {
|
||||
|
||||
companion object {
|
||||
|
||||
val MAP_CODEC: MapCodec<CosmeticData> = RecordCodecBuilder.mapCodec { builder ->
|
||||
builder.group(
|
||||
Codec.FLOAT.fieldOf("baseScale").forGetter(CosmeticData::baseScale),
|
||||
ExtraCodecs.ENTITY_DIMENSIONS.fieldOf("hitbox").forGetter(CosmeticData::hitbox),
|
||||
Codec.FLOAT.fieldOf("weight").forGetter(CosmeticData::weight),
|
||||
Codec.FLOAT.fieldOf("height").forGetter(CosmeticData::height),
|
||||
setCodec(Codec.STRING).fieldOf("aspects").forGetter(CosmeticData::aspects),
|
||||
Codec.FLOAT.optionalFieldOf("standingEyeHeight", VANILLA_DEFAULT_EYE_HEIGHT).forGetter(CosmeticData::standingEyeHeight),
|
||||
Codec.FLOAT.optionalFieldOf("swimmingEyeHeight", VANILLA_DEFAULT_EYE_HEIGHT).forGetter(CosmeticData::swimmingEyeHeight),
|
||||
Codec.FLOAT.optionalFieldOf("flyingEyeHeight", VANILLA_DEFAULT_EYE_HEIGHT).forGetter(CosmeticData::flyingEyeHeight),
|
||||
).apply(builder, ::CosmeticData)
|
||||
}
|
||||
|
||||
val CODEC: Codec<CosmeticData> = MAP_CODEC.codec()
|
||||
|
||||
private const val VANILLA_DEFAULT_EYE_HEIGHT = 0.85F
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.species.internal
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.egg.EggGroup
|
||||
import com.cobblemon.mod.common.api.pokemon.species.Species
|
||||
import com.cobblemon.mod.common.util.codec.setCodec
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import java.util.EnumSet
|
||||
|
||||
/**
|
||||
* Intermediate for [Species] data.
|
||||
*/
|
||||
internal data class EggData(
|
||||
var eggCycles: Int,
|
||||
var eggGroups: EnumSet<EggGroup>,
|
||||
) {
|
||||
|
||||
companion object {
|
||||
|
||||
val MAP_CODEC: MapCodec<EggData> = RecordCodecBuilder.mapCodec { builder ->
|
||||
builder.group(
|
||||
Codec.INT.fieldOf("eggCycles").forGetter(EggData::eggCycles),
|
||||
setCodec(EggGroup.CODEC).fieldOf("eggGroups").xmap({ set -> EnumSet.copyOf(set) }, { enumSet -> enumSet }).forGetter { eggData -> eggData.eggGroups }
|
||||
).apply(builder, ::EggData)
|
||||
}
|
||||
|
||||
val CODEC: Codec<EggData> = MAP_CODEC.codec()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.species.internal
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.PreEvolution
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.adapters.EvolutionRegistry
|
||||
import com.cobblemon.mod.common.api.pokemon.species.Species
|
||||
import com.cobblemon.mod.common.util.codec.setCodec
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Intermediate for [Species] data.
|
||||
*/
|
||||
internal data class EvolutionData(
|
||||
var evolutions: Set<Evolution>,
|
||||
var preEvolution: Optional<PreEvolution>,
|
||||
) {
|
||||
|
||||
companion object {
|
||||
|
||||
val MAP_CODEC: MapCodec<EvolutionData> = RecordCodecBuilder.mapCodec { builder ->
|
||||
builder.group(
|
||||
setCodec(EvolutionRegistry.EVOLUTION_CODEC).optionalFieldOf("evolutions", emptySet()).forGetter(EvolutionData::evolutions),
|
||||
PreEvolution.CODEC.optionalFieldOf("preEvolution").forGetter(EvolutionData::preEvolution)
|
||||
).apply(builder, ::EvolutionData)
|
||||
}
|
||||
|
||||
val CODEC: Codec<EvolutionData> = MAP_CODEC.codec()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.pokemon.species.internal
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.experience.ExperienceGroup
|
||||
import com.cobblemon.mod.common.api.pokemon.species.Species
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
|
||||
/**
|
||||
* Intermediate for [Species] data.
|
||||
*/
|
||||
internal data class ExperienceData(
|
||||
var baseExperienceYield: Int,
|
||||
var experienceGroup: ExperienceGroup
|
||||
) {
|
||||
|
||||
companion object {
|
||||
|
||||
val MAP_CODEC: MapCodec<ExperienceData> = RecordCodecBuilder.mapCodec { builder ->
|
||||
builder.group(
|
||||
Codec.INT.fieldOf("baseExperienceYield").forGetter(ExperienceData::baseExperienceYield),
|
||||
ExperienceGroup.CODEC.fieldOf("experienceGroup").forGetter(ExperienceData::experienceGroup),
|
||||
).apply(builder, ::ExperienceData)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.stats
|
||||
|
||||
import com.mojang.serialization.Codec
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* An implementation of [StatMap] aimed solely at the EV data structure.
|
||||
* This exists due to a few extra constraints present in EVs regarding the total value of the structure.
|
||||
*
|
||||
* @see <a href="https://bulbapedia.bulbagarden.net/wiki/Effort_values">Effort values Bulbapedia entry</a>
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
class EvMap internal constructor(stats: HashMap<Stat, Int>) : StatMap(Variant.EV, stats) {
|
||||
|
||||
init {
|
||||
if (this.values.sum() > MAX_TOTAL) {
|
||||
throw IllegalArgumentException("Cannot instance ${this::class.simpleName} with values total exceeding $MAX_TOTAL")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherits the logic of [StatMap.validateValue].
|
||||
* This has the added constraint of the maximum possible total value being [MAX_TOTAL].
|
||||
*
|
||||
* @see StatMap.validateValue
|
||||
*/
|
||||
override fun validateValue(key: Stat, value: Int) {
|
||||
super.validateValue(key, value)
|
||||
val existing = this[key]
|
||||
val current = this.values.sum() - existing
|
||||
if (current + value > MAX_TOTAL) {
|
||||
throw IllegalArgumentException("The total of the values of an ${this::class.simpleName} cannot exceed $MAX_TOTAL")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds to the given [stat].
|
||||
*
|
||||
* @param stat The [Stat] being added to.
|
||||
* @param amount The amount being added. This value will be sanitized with [Int.absoluteValue].
|
||||
* @param coerceSafe Should the value be coerced to be safe to add?
|
||||
* @return The new resulting value of [stat].
|
||||
*
|
||||
* @throws IllegalArgumentException if [validateValue] fails.
|
||||
*
|
||||
* @see subtract
|
||||
* @see mutate
|
||||
*/
|
||||
fun add(stat: Stat, amount: Int, coerceSafe: Boolean): Int = this.mutate(stat, amount.absoluteValue, coerceSafe)
|
||||
|
||||
/**
|
||||
* Subtracts from the given [stat].
|
||||
*
|
||||
* @param stat The [Stat] being subtracted from.
|
||||
* @param amount The amount being subtracted. This value will be sanitized with [Int.absoluteValue].
|
||||
* @param coerceSafe Should the value be coerced to be safe to subtract?
|
||||
* @return The new resulting value of [stat].
|
||||
*
|
||||
* @throws IllegalArgumentException if [validateValue] fails.
|
||||
*
|
||||
* @see add
|
||||
* @see mutate
|
||||
*/
|
||||
fun subtract(stat: Stat, amount: Int, coerceSafe: Boolean): Int = this.mutate(stat, amount.absoluteValue, coerceSafe)
|
||||
|
||||
/**
|
||||
* Mutates the given [stat].
|
||||
*
|
||||
* @param stat The [Stat] being mutated.
|
||||
* @param amount The amount being used in the mutation operation.
|
||||
* @param coerceSafe Should the value be coerced to be safe to operate with?
|
||||
* @return The new resulting value of [stat].
|
||||
*
|
||||
* @throws IllegalArgumentException if [validateValue] fails.
|
||||
*
|
||||
* @see add
|
||||
* @see subtract
|
||||
*/
|
||||
fun mutate(stat: Stat, amount: Int, coerceSafe: Boolean): Int {
|
||||
var result = this[stat] + amount
|
||||
if (coerceSafe) {
|
||||
result = result.coerceIn(this.variant.minValue, min(this.variant.maxValue, this.remaining()))
|
||||
}
|
||||
this.put(stat, result)
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves how many EVs can still be allocated to this structure.
|
||||
*
|
||||
* @return How many EVs can still be allocated.
|
||||
*/
|
||||
fun remaining(): Int = MAX_TOTAL - this.values.sum()
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* A [Codec] for a [EvMap].
|
||||
*/
|
||||
@JvmField
|
||||
val EV_STATS_CODEC: Codec<EvMap> = Codec.unboundedMap(Stat.CODEC, Codec.intRange(Variant.EV.minValue, Variant.EV.maxValue))
|
||||
.xmap({ map -> EvMap(HashMap(map)) }, { evMap -> evMap })
|
||||
|
||||
/**
|
||||
* The maximum total EV values.
|
||||
*/
|
||||
const val MAX_TOTAL = 510
|
||||
|
||||
/**
|
||||
* Creates a [EvMap] with the given params.
|
||||
*
|
||||
* @param map The base map to take values from.
|
||||
* @return The generated [EvMap].
|
||||
*
|
||||
* @throws IllegalArgumentException If the constraints seen in [StatMap.create] are not fulfilled or if the total of the values exceeds [MAX_TOTAL].
|
||||
*/
|
||||
fun create(map: Map<Stat, Int>): EvMap = EvMap(HashMap(map))
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.stats
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.mojang.serialization.Codec
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
@@ -61,4 +63,10 @@ interface Stat {
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
val CODEC: Codec<Stat> = Identifier.CODEC.xmap(Cobblemon.statProvider::fromIdentifierOrThrow, Stat::identifier)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
/*
|
||||
* 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.pokemon.stats
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.serialization.BufferSerializer
|
||||
import com.cobblemon.mod.common.api.serialization.DataSerializer
|
||||
import com.cobblemon.mod.common.pokemon.EVs
|
||||
import com.cobblemon.mod.common.pokemon.IVs
|
||||
import com.google.gson.JsonObject
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.JsonOps
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.nbt.NbtOps
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
import kotlin.jvm.optionals.getOrDefault
|
||||
|
||||
/**
|
||||
* Represents a map-like structure that is used solely for the purpose of various stat-like Pokémon data structures.
|
||||
*
|
||||
* @property variant The [Variant] of this map.
|
||||
* @property stats The base map used for this map.
|
||||
*
|
||||
* @see <a href="https://bulbapedia.bulbagarden.net/wiki/Base_stats">Base stats Bulbapedia entry</a>
|
||||
* @see <a href="https://bulbapedia.bulbagarden.net/wiki/Effort_values">Effort values Bulbapedia entry</a>
|
||||
* @see <a href="https://bulbapedia.bulbagarden.net/wiki/Individual_values">Individual values Bulbapedia entry</a>
|
||||
*
|
||||
*/
|
||||
@Suppress("unused", "MemberVisibilityCanBePrivate")
|
||||
open class StatMap internal constructor(
|
||||
val variant: Variant,
|
||||
protected val stats: HashMap<Stat, Int>
|
||||
) : Map<Stat, Int>, DataSerializer<NbtCompound, JsonObject>, BufferSerializer {
|
||||
|
||||
init {
|
||||
if (!Cobblemon.statProvider.ofType(Stat.Type.PERMANENT).all(this.stats::containsKey)) {
|
||||
throw IllegalArgumentException("Cannot instance ${this::class.simpleName} without all ${Stat.Type.PERMANENT.name} ${Stat::class.simpleName}")
|
||||
}
|
||||
if (!this.stats.values.any { value -> this.variant.fits(value) }) {
|
||||
throw IllegalArgumentException("Cannot instance ${this::class.simpleName} with values not in range [${this.variant.minValue}-${this.variant.maxValue}]")
|
||||
}
|
||||
}
|
||||
|
||||
override val entries: Set<Map.Entry<Stat, Int>>
|
||||
get() = this.stats.entries
|
||||
override val keys: Set<Stat>
|
||||
get() = this.stats.keys
|
||||
override val size: Int
|
||||
get() = this.stats.size
|
||||
override val values: Collection<Int>
|
||||
get() = this.stats.values
|
||||
|
||||
/**
|
||||
* A constraint of implementing [Map].
|
||||
* This is impossible to be false.
|
||||
*
|
||||
* @return Always true.
|
||||
*/
|
||||
override fun isEmpty(): Boolean = false
|
||||
|
||||
/**
|
||||
* Returns the value corresponding to the given [key].
|
||||
* Due to implementation details unlike a typical [Map.get] this will never return null.
|
||||
*
|
||||
* @param key The [Stat] being checked.
|
||||
* @return The value attached to the [key].
|
||||
*/
|
||||
override fun get(key: Stat): Int = this.stats[key]!!
|
||||
|
||||
override fun containsValue(value: Int): Boolean = this.stats.containsValue(value)
|
||||
|
||||
override fun containsKey(key: Stat): Boolean = this.stats.containsKey(key)
|
||||
|
||||
override fun loadFromNBT(nbt: NbtCompound) {
|
||||
this.codec().decode(NbtOps.INSTANCE, nbt)
|
||||
.result()
|
||||
.ifPresent { pair -> this.putAll(pair.first) }
|
||||
}
|
||||
|
||||
override fun saveToNBT(): NbtCompound {
|
||||
val result = this.codec().encodeStart(NbtOps.INSTANCE, this)
|
||||
.result()
|
||||
.getOrDefault(NbtCompound())
|
||||
// It's safe to cast it will always result in a NbtCompound
|
||||
return result as NbtCompound
|
||||
}
|
||||
|
||||
override fun loadFromJson(json: JsonObject) {
|
||||
this.codec().decode(JsonOps.INSTANCE, json)
|
||||
.result()
|
||||
.ifPresent { pair -> this.putAll(pair.first) }
|
||||
}
|
||||
|
||||
override fun saveToJson(): JsonObject {
|
||||
val result = this.codec().encodeStart(JsonOps.INSTANCE, this)
|
||||
.result()
|
||||
.getOrDefault(JsonObject())
|
||||
// It's safe to cast it will always result in a JsonObject
|
||||
return result as JsonObject
|
||||
}
|
||||
|
||||
override fun saveToBuffer(buffer: PacketByteBuf, toClient: Boolean) {
|
||||
buffer.writeMap(this.stats, { pb, stat -> pb.writeIdentifier(stat.identifier) }, PacketByteBuf::writeInt)
|
||||
}
|
||||
|
||||
override fun loadFromBuffer(buffer: PacketByteBuf) {
|
||||
// This is safe to do as the server will have sanitized the values.
|
||||
this.stats.clear()
|
||||
this.stats.putAll(buffer.readMap({ pb -> Cobblemon.statProvider.decode(pb) }, PacketByteBuf::readInt))
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the value for the given [key] to the [Variant.minValue] of [variant].
|
||||
*
|
||||
* @param key The [Stat] being reset.
|
||||
* @return The previous attached value.
|
||||
*
|
||||
* @throws IllegalArgumentException If the [key] fails [validateKey].
|
||||
*/
|
||||
fun reset(key: Stat): Int {
|
||||
this.validateKey(key)
|
||||
val existing = this.stats[key]!!
|
||||
this.stats[key] = this.variant.minValue
|
||||
return existing
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets all values of this map to the [Variant.minValue] of [variant].
|
||||
*/
|
||||
fun reset() {
|
||||
Cobblemon.statProvider.ofType(Stat.Type.PERMANENT).forEach(this::reset)
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges key-value pairs from a map into this [StatMap].
|
||||
*
|
||||
* @param from The map being merged.
|
||||
*
|
||||
* @throws IllegalArgumentException If any value of [from] fails [validateValue].
|
||||
*/
|
||||
fun putAll(from: Map<out Stat, Int>) {
|
||||
from.forEach(this::put)
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the given [value] on the given [key] [Stat] of this [StatMap].
|
||||
*
|
||||
* @param key The [Stat] being set.
|
||||
* @param value The value being set to the [key].
|
||||
* @return The previously attached value.
|
||||
*
|
||||
* @throws IllegalArgumentException If the [value] fails [validateValue].
|
||||
*/
|
||||
fun put(key: Stat, value: Int): Int {
|
||||
this.validateValue(key, value)
|
||||
val existing = this.stats[key]!!
|
||||
this.stats[key] = value
|
||||
return existing
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the [Codec] for this instance.
|
||||
* This is done based on the [variant].
|
||||
*
|
||||
* @return The [Codec] for this specific [StatMap] setup.
|
||||
*
|
||||
* @see StatMap.BASE_STATS_CODEC
|
||||
* @see StatMap.EV_STATS_CODEC
|
||||
* @see StatMap.IV_STATS_CODEC
|
||||
*/
|
||||
fun codec(): Codec<StatMap> = when (this.variant) {
|
||||
Variant.BASE_STATS -> BASE_STATS_CODEC
|
||||
Variant.IV -> IV_STATS_CODEC
|
||||
Variant.EV -> EV_STATS_CODEC
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a [Stat.Type] is [Stat.Type.PERMANENT].
|
||||
*
|
||||
* @param stat The [Stat] being checked.
|
||||
*
|
||||
* @throws IllegalArgumentException If the [Stat.Type] is not [Stat.Type.PERMANENT].
|
||||
*/
|
||||
protected open fun validateKey(stat: Stat) {
|
||||
if (stat.type != Stat.Type.PERMANENT) {
|
||||
throw IllegalArgumentException("Unsupported stat type of ${stat.type.name} for ${stat.identifier}")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a value is possible for this map.
|
||||
*
|
||||
* @param key The key having the [value] assigned.
|
||||
* @param value The value being assigned.
|
||||
*
|
||||
* @throws IllegalArgumentException If the [value] doesn't fulfill [Variant.fits] of the [variant].
|
||||
*/
|
||||
protected open fun validateValue(key: Stat, value: Int) {
|
||||
if (!this.variant.fits(value)) {
|
||||
throw IllegalArgumentException("Cannot accept $value, a value must be in range [${this.variant.minValue}-${this.variant.maxValue}]")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a type of data collected inside [StatMap].
|
||||
*
|
||||
* @property minValue The minimum possible value for a value of a [StatMap].
|
||||
* @property maxValue The maximum possible value for a value of a [StatMap].
|
||||
*/
|
||||
enum class Variant(val minValue: Int, val maxValue: Int) {
|
||||
|
||||
/**
|
||||
* Used to represent [base stats](https://bulbapedia.bulbagarden.net/wiki/Base_stats).
|
||||
*/
|
||||
BASE_STATS(1, Int.MAX_VALUE),
|
||||
|
||||
/**
|
||||
* Used to represent [effort values](https://bulbapedia.bulbagarden.net/wiki/Effort_values).
|
||||
*/
|
||||
EV(0, EVs.MAX_STAT_VALUE),
|
||||
|
||||
/**
|
||||
* Used to represent [individual values](https://bulbapedia.bulbagarden.net/wiki/Individual_values).
|
||||
*/
|
||||
IV(0, IVs.MAX_VALUE);
|
||||
|
||||
/**
|
||||
* Checks if the given [value] fits within the range created by [minValue] & [maxValue].
|
||||
*
|
||||
* @param value The value being checked.
|
||||
* @return If the value can fit in this variant.
|
||||
*/
|
||||
fun fits(value: Int): Boolean = value >= this.minValue && value <= this.maxValue
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* A [Codec] for a [StatMap] of the [Variant.BASE_STATS] variant.
|
||||
*/
|
||||
@JvmField
|
||||
val BASE_STATS_CODEC: Codec<StatMap> = this.createCodec(Variant.BASE_STATS)
|
||||
|
||||
/**
|
||||
* A [Codec] for a [StatMap] of the [Variant.EV] variant.
|
||||
* For the [EvMap] typed Codec see [EvMap.EV_STATS_CODEC].
|
||||
* The result as far as instance types go is the same this is just a constraint of [Codec].
|
||||
*
|
||||
* @see EvMap.EV_STATS_CODEC
|
||||
*/
|
||||
@JvmField
|
||||
val EV_STATS_CODEC: Codec<StatMap> = EvMap.EV_STATS_CODEC.xmap({ evMap -> this.create(Variant.EV, evMap) }, EvMap::create)
|
||||
|
||||
/**
|
||||
* A [Codec] for a [StatMap] of the [Variant.IV] variant.
|
||||
*/
|
||||
@JvmField
|
||||
val IV_STATS_CODEC: Codec<StatMap> = this.createCodec(Variant.IV)
|
||||
|
||||
/**
|
||||
* Creates a [StatMap] with the given params.
|
||||
*
|
||||
* @param variant The [StatMap] [Variant].
|
||||
* @param map The base map to take values from.
|
||||
* @return The generated [StatMap].
|
||||
*
|
||||
* @throws IllegalArgumentException If map doesn't contain every possible key with type [Stat.Type.PERMANENT] or if any value doesn't respect [Variant.fits] for the given [variant].
|
||||
*/
|
||||
fun create(variant: Variant, map: Map<Stat, Int>): StatMap = when (variant) {
|
||||
Variant.BASE_STATS -> StatMap(variant, HashMap(map))
|
||||
Variant.EV -> EvMap(HashMap(map))
|
||||
Variant.IV -> StatMap(variant, HashMap(map))
|
||||
}
|
||||
|
||||
private fun createCodec(variant: Variant): Codec<StatMap> = Codec.unboundedMap(Stat.CODEC, Codec.intRange(variant.minValue, variant.maxValue))
|
||||
.xmap({ map -> StatMap(variant, HashMap(map)) }, { statMap -> statMap })
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,11 +9,7 @@
|
||||
package com.cobblemon.mod.common.api.pokemon.stats
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.pokemon.EVs
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
import com.cobblemon.mod.common.pokemon.IVs
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
import com.cobblemon.mod.common.pokemon.*
|
||||
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.registry
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.pokemon.species.Species
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import net.minecraft.registry.Registry
|
||||
import net.minecraft.registry.RegistryKey
|
||||
|
||||
object CobblemonRegistries {
|
||||
|
||||
val ELEMENTAL_TYPE: Registry<ElementalType> get() = this.get(CobblemonRegistryKeys.ELEMENTAL_TYPE)
|
||||
|
||||
val SPECIES: Registry<Species> get() = this.get(CobblemonRegistryKeys.SPECIES)
|
||||
|
||||
private fun <T> get(key: RegistryKey<Registry<T>>): Registry<T> = Cobblemon.implementation.getRegistry(key)
|
||||
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.registry
|
||||
|
||||
import net.minecraft.registry.Registry
|
||||
import net.minecraft.registry.entry.RegistryEntry
|
||||
import net.minecraft.registry.tag.TagKey
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
interface CobblemonRegistryElement<T : CobblemonRegistryElement<T>> {
|
||||
|
||||
fun isIn(tag: TagKey<T>): Boolean = this.registryEntry().isIn(tag)
|
||||
|
||||
fun id(): Identifier
|
||||
|
||||
fun registryEntry(): RegistryEntry<T>
|
||||
|
||||
fun registry(): Registry<T>
|
||||
|
||||
}
|
||||
@@ -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.registry
|
||||
|
||||
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.pokemon.species.Species
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.registry.Registry
|
||||
import net.minecraft.registry.RegistryKey
|
||||
|
||||
/**
|
||||
* Contains the various [RegistryKey]s for Cobblemon.
|
||||
*/
|
||||
object CobblemonRegistryKeys {
|
||||
|
||||
val ELEMENTAL_TYPE: RegistryKey<Registry<ElementalType>> = this.create("elemental_types")
|
||||
|
||||
val SPECIES: RegistryKey<Registry<Species>> = this.create("species")
|
||||
|
||||
private fun <T> create(key: String): RegistryKey<Registry<T>> = RegistryKey.ofRegistry(cobblemonResource(key))
|
||||
|
||||
}
|
||||
+1
-1
@@ -90,7 +90,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) {
|
||||
|
||||
@@ -19,12 +19,15 @@ import com.cobblemon.mod.common.api.spawning.detail.PossibleHeldItem
|
||||
import com.cobblemon.mod.common.api.spawning.preset.SpawnDetailPreset
|
||||
import com.cobblemon.mod.common.util.adapters.*
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.codec.toTypeAdapter
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.mojang.datafixers.util.Either
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.fluid.Fluid
|
||||
import net.minecraft.registry.Registries
|
||||
import net.minecraft.registry.RegistryKeys
|
||||
import net.minecraft.registry.tag.TagKey
|
||||
import net.minecraft.resource.ResourceType
|
||||
@@ -48,9 +51,9 @@ object SpawnDetailPresets : JsonDataRegistry<SpawnDetailPreset> {
|
||||
.disableHtmlEscaping()
|
||||
.registerTypeAdapter(SpawnBucket::class.java, SpawnBucketAdapter)
|
||||
.registerTypeAdapter(RegisteredSpawningContext::class.java, RegisteredSpawningContextAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, BiomeLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Block::class.java).type, BlockLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Fluid::class.java).type, FluidLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, RegistryLikeCondition.createCodec { server()!!.registryManager.get(RegistryKeys.BIOME) }.toTypeAdapter())
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Block::class.java).type, RegistryLikeCondition.createCodec { Registries.BLOCK }.toTypeAdapter())
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Fluid::class.java).type, RegistryLikeCondition.createCodec { Registries.FLUID }.toTypeAdapter())
|
||||
.registerTypeAdapter(
|
||||
TypeToken.getParameterized(
|
||||
Either::class.java,
|
||||
|
||||
@@ -17,12 +17,15 @@ import com.cobblemon.mod.common.api.spawning.context.RegisteredSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.detail.PossibleHeldItem
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.util.adapters.*
|
||||
import com.cobblemon.mod.common.util.codec.toTypeAdapter
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.mojang.datafixers.util.Either
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.fluid.Fluid
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.registry.Registries
|
||||
import net.minecraft.registry.RegistryKeys
|
||||
import net.minecraft.registry.tag.TagKey
|
||||
import net.minecraft.util.Identifier
|
||||
@@ -41,9 +44,9 @@ object SpawnLoader {
|
||||
.setPrettyPrinting()
|
||||
.disableHtmlEscaping()
|
||||
.setLenient()
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, BiomeLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Block::class.java).type, BlockLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Fluid::class.java).type, FluidLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, RegistryLikeCondition.createCodec { server()!!.registryManager.get(RegistryKeys.BIOME) }.toTypeAdapter())
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Block::class.java).type, RegistryLikeCondition.createCodec { Registries.BLOCK }.toTypeAdapter())
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Fluid::class.java).type, RegistryLikeCondition.createCodec { Registries.FLUID }.toTypeAdapter())
|
||||
.registerTypeAdapter(
|
||||
TypeToken.getParameterized(
|
||||
Either::class.java,
|
||||
|
||||
@@ -10,6 +10,8 @@ package com.cobblemon.mod.common.api.spawning
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.TimeRange.Companion.timeRanges
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.DataResult
|
||||
|
||||
/**
|
||||
* A range of time ticks for a world mainly to be used in [SpawningCondition]s. A time range
|
||||
@@ -41,6 +43,12 @@ class TimeRange : IntRanges {
|
||||
"morning" to TimeRange(0..4999),
|
||||
"afternoon" to TimeRange(7000..12039)
|
||||
)
|
||||
|
||||
val CODEC: Codec<TimeRange> = Codec.STRING.flatXmap(
|
||||
{ string -> timeRanges[string]?.let { DataResult.success(it) } ?: DataResult.error { "Can't find a time range with ID $string" } },
|
||||
{ timeRange -> timeRanges.entries.firstOrNull { (_, range) -> range == timeRange }?.let { DataResult.success(it.key) } ?: DataResult.error { "Couldn't resolve a time range ID for $timeRange" } }
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
constructor() : super()
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.condition
|
||||
|
||||
import net.minecraft.util.StringIdentifiable
|
||||
import net.minecraft.world.World
|
||||
|
||||
/**
|
||||
@@ -17,7 +18,7 @@ import net.minecraft.world.World
|
||||
* @author Licious
|
||||
* @since January 25th, 2023
|
||||
*/
|
||||
enum class MoonPhase {
|
||||
enum class MoonPhase : StringIdentifiable {
|
||||
|
||||
FULL_MOON,
|
||||
WANING_GIBBOUS,
|
||||
@@ -28,6 +29,8 @@ enum class MoonPhase {
|
||||
FIRST_QUARTER,
|
||||
WAXING_GIBBOUS;
|
||||
|
||||
override fun asString(): String = this.name
|
||||
|
||||
companion object {
|
||||
private val VALUES = values()
|
||||
|
||||
|
||||
+2
-2
@@ -74,8 +74,8 @@ class PokemonSpawnDetail : SpawnDetail() {
|
||||
val species = PokemonSpecies.getByIdentifier(pokemon.species!!.asIdentifierDefaultingNamespace())
|
||||
if (species != null) {
|
||||
labels.addAll(
|
||||
species.secondaryType?.let { listOf(species.primaryType.name.lowercase(), it.name.lowercase()) }
|
||||
?: listOf(species.primaryType.name.lowercase())
|
||||
species.secondaryType?.let { listOf(species.primaryType.id().toString(), it.id().toString()) }
|
||||
?: listOf(species.primaryType.id().toString())
|
||||
)
|
||||
|
||||
if (height == -1) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ import com.cobblemon.mod.common.api.storage.StorePosition
|
||||
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore
|
||||
import com.cobblemon.mod.common.api.storage.pc.PCStore
|
||||
import com.cobblemon.mod.common.pokemon.EVs
|
||||
import com.cobblemon.mod.common.pokemon.Gender
|
||||
import com.cobblemon.mod.common.api.pokemon.gender.Gender
|
||||
import com.cobblemon.mod.common.pokemon.IVs
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import java.nio.file.Path
|
||||
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.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
|
||||
*/
|
||||
open class MongoDBStoreAdapter(
|
||||
private val mongoClient: MongoClient,
|
||||
private val databaseName: String,
|
||||
) : CobblemonAdapterParent<JsonObject>(), FileStoreAdapter<JsonObject> {
|
||||
private fun getCollection(storeClass: Class<out PokemonStore<*>>): MongoCollection<Document> {
|
||||
val collectionName = when (storeClass) {
|
||||
PlayerPartyStore::class.java -> "PlayerPartyCollection"
|
||||
PCStore::class.java -> "PCCollection"
|
||||
else -> "OtherCollection"
|
||||
}
|
||||
return mongoClient.getDatabase(databaseName).getCollection(collectionName)
|
||||
}
|
||||
|
||||
private val gson: Gson = Gson()
|
||||
|
||||
override fun <E : StorePosition, T : PokemonStore<E>> serialize(store: T): JsonObject {
|
||||
return store.saveToJSON(JsonObject())
|
||||
}
|
||||
|
||||
override fun save(storeClass: Class<out PokemonStore<*>>, uuid: UUID, serialized: JsonObject) {
|
||||
val document = Document.parse(gson.toJson(serialized))
|
||||
document.put("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 = 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
|
||||
}
|
||||
|
||||
}
|
||||
+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
|
||||
+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
|
||||
+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
|
||||
+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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
+8
-7
@@ -14,6 +14,7 @@ import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
@Suppress("unused")
|
||||
class PlayerAdvancementData {
|
||||
|
||||
var totalCaptureCount: Int = 0
|
||||
@@ -35,7 +36,7 @@ class PlayerAdvancementData {
|
||||
var totalTradedCount: Int = 0
|
||||
private set
|
||||
|
||||
private var totalTypeCaptureCounts = mutableMapOf<String, Int>()
|
||||
private var totalTypeCaptureCounts = mutableMapOf<Identifier, Int>()
|
||||
private var totalDefeatedCounts = mutableMapOf<Identifier, Int>()
|
||||
var aspectsCollected = mutableMapOf<Identifier, MutableSet<String>>()
|
||||
private set
|
||||
@@ -77,18 +78,18 @@ class PlayerAdvancementData {
|
||||
}
|
||||
|
||||
fun getTotalTypeCaptureCount(type: ElementalType): Int {
|
||||
if (!totalTypeCaptureCounts.containsKey(key = type.name)) {
|
||||
totalTypeCaptureCounts[type.name] = 0
|
||||
if (!totalTypeCaptureCounts.containsKey(type.id())) {
|
||||
totalTypeCaptureCounts[type.id()] = 0
|
||||
}
|
||||
return totalTypeCaptureCounts.get(key = type.name) ?: 0
|
||||
return totalTypeCaptureCounts[type.id()] ?: 0
|
||||
}
|
||||
|
||||
fun updateTotalTypeCaptureCount(type: ElementalType) {
|
||||
val count = totalTypeCaptureCounts[type.name] ?: 0
|
||||
val count = totalTypeCaptureCounts[type.id()] ?: 0
|
||||
if (count == 0) {
|
||||
totalTypeCaptureCounts[type.name] = 1
|
||||
totalTypeCaptureCounts[type.id()] = 1
|
||||
} else {
|
||||
totalTypeCaptureCounts.replace(type.name, count + 1)
|
||||
totalTypeCaptureCounts.replace(type.id(), count + 1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+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
|
||||
|
||||
+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.types
|
||||
|
||||
import com.cobblemon.mod.common.api.registry.CobblemonRegistryKeys
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.registry.tag.TagKey
|
||||
|
||||
/**
|
||||
* Contains all the Cobblemon default elemental type related tags.
|
||||
*/
|
||||
object CobblemonElementalTypeTags {
|
||||
|
||||
val FIRE_IMMUNE: TagKey<ElementalType> = TagKey.of(CobblemonRegistryKeys.ELEMENTAL_TYPE, cobblemonResource("fire_immune"))
|
||||
|
||||
val FALL_DAMAGE_IMMUNE: TagKey<ElementalType> = TagKey.of(CobblemonRegistryKeys.ELEMENTAL_TYPE, cobblemonResource("fall_damage_immune"))
|
||||
|
||||
val LURE_BALL_BOOSTING: TagKey<ElementalType> = TagKey.of(CobblemonRegistryKeys.ELEMENTAL_TYPE, cobblemonResource("lure_ball_boosting"))
|
||||
|
||||
val NET_BALL_BOOSTING: TagKey<ElementalType> = TagKey.of(CobblemonRegistryKeys.ELEMENTAL_TYPE, cobblemonResource("net_ball_boosting"))
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.types
|
||||
|
||||
import com.cobblemon.mod.common.api.registry.CobblemonRegistries
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.util.Identifier
|
||||
import java.util.Optional
|
||||
|
||||
/**
|
||||
* The official [ElementalType]s and the ones the Cobblemon mod ships with.
|
||||
* These are not guaranteed to be present in a game instance as we can't control what datapacks may remove them.
|
||||
* It's also not safe to assume they function the exact same as they would in the official games, please check the various properties of [ElementalType].
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate", "unused")
|
||||
object CobblemonElementalTypes {
|
||||
|
||||
val BUG_KEY: Identifier = cobblemonResource("bug")
|
||||
val BUG: Optional<ElementalType> get() = Optional.ofNullable(CobblemonRegistries.ELEMENTAL_TYPE.get(BUG_KEY))
|
||||
|
||||
val DARK_KEY: Identifier = cobblemonResource("dark")
|
||||
val DARK: Optional<ElementalType> get() = Optional.ofNullable(CobblemonRegistries.ELEMENTAL_TYPE.get(DARK_KEY))
|
||||
|
||||
val DRAGON_KEY: Identifier = cobblemonResource("dragon")
|
||||
val DRAGON: Optional<ElementalType> get() = Optional.ofNullable(CobblemonRegistries.ELEMENTAL_TYPE.get(DRAGON_KEY))
|
||||
|
||||
val ELECTRIC_KEY: Identifier = cobblemonResource("electric")
|
||||
val ELECTRIC: Optional<ElementalType> get() = Optional.ofNullable(CobblemonRegistries.ELEMENTAL_TYPE.get(ELECTRIC_KEY))
|
||||
|
||||
val FAIRY_KEY: Identifier = cobblemonResource("fairy")
|
||||
val FAIRY: Optional<ElementalType> get() = Optional.ofNullable(CobblemonRegistries.ELEMENTAL_TYPE.get(FAIRY_KEY))
|
||||
|
||||
val FIGHTING_KEY: Identifier = cobblemonResource("fighting")
|
||||
val FIGHTING: Optional<ElementalType> get() = Optional.ofNullable(CobblemonRegistries.ELEMENTAL_TYPE.get(FIGHTING_KEY))
|
||||
|
||||
val FIRE_KEY: Identifier = cobblemonResource("fire")
|
||||
val FIRE: Optional<ElementalType> get() = Optional.ofNullable(CobblemonRegistries.ELEMENTAL_TYPE.get(FIRE_KEY))
|
||||
|
||||
val FLYING_KEY: Identifier = cobblemonResource("flying")
|
||||
val FLYING: Optional<ElementalType> get() = Optional.ofNullable(CobblemonRegistries.ELEMENTAL_TYPE.get(FLYING_KEY))
|
||||
|
||||
val GHOST_KEY: Identifier = cobblemonResource("ghost")
|
||||
val GHOST: Optional<ElementalType> get() = Optional.ofNullable(CobblemonRegistries.ELEMENTAL_TYPE.get(GHOST_KEY))
|
||||
|
||||
val GRASS_KEY: Identifier = cobblemonResource("grass")
|
||||
val GRASS: Optional<ElementalType> get() = Optional.ofNullable(CobblemonRegistries.ELEMENTAL_TYPE.get(GRASS_KEY))
|
||||
|
||||
val GROUND_KEY: Identifier = cobblemonResource("ground")
|
||||
val GROUND: Optional<ElementalType> get() = Optional.ofNullable(CobblemonRegistries.ELEMENTAL_TYPE.get(GROUND_KEY))
|
||||
|
||||
val ICE_KEY: Identifier = cobblemonResource("ice")
|
||||
val ICE: Optional<ElementalType> get() = Optional.ofNullable(CobblemonRegistries.ELEMENTAL_TYPE.get(ICE_KEY))
|
||||
|
||||
val NORMAL_KEY: Identifier = cobblemonResource("normal")
|
||||
val NORMAL: Optional<ElementalType> get() = Optional.ofNullable(CobblemonRegistries.ELEMENTAL_TYPE.get(NORMAL_KEY))
|
||||
|
||||
val POISON_KEY: Identifier = cobblemonResource("poison")
|
||||
val POISON: Optional<ElementalType> get() = Optional.ofNullable(CobblemonRegistries.ELEMENTAL_TYPE.get(POISON_KEY))
|
||||
|
||||
val PSYCHIC_KEY: Identifier = cobblemonResource("psychic")
|
||||
val PSYCHIC: Optional<ElementalType> get() = Optional.ofNullable(CobblemonRegistries.ELEMENTAL_TYPE.get(PSYCHIC_KEY))
|
||||
|
||||
val ROCK_KEY: Identifier = cobblemonResource("rock")
|
||||
val ROCK: Optional<ElementalType> get() = Optional.ofNullable(CobblemonRegistries.ELEMENTAL_TYPE.get(ROCK_KEY))
|
||||
|
||||
val STEEL_KEY: Identifier = cobblemonResource("steel")
|
||||
val STEEL: Optional<ElementalType> get() = Optional.ofNullable(CobblemonRegistries.ELEMENTAL_TYPE.get(STEEL_KEY))
|
||||
|
||||
val WATER_KEY: Identifier = cobblemonResource("water")
|
||||
val WATER: Optional<ElementalType> get() = Optional.ofNullable(CobblemonRegistries.ELEMENTAL_TYPE.get(WATER_KEY))
|
||||
|
||||
}
|
||||
@@ -9,21 +9,119 @@
|
||||
package com.cobblemon.mod.common.api.types
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.data.ShowdownIdentifiable
|
||||
import com.cobblemon.mod.common.api.registry.CobblemonRegistries
|
||||
import com.cobblemon.mod.common.api.registry.CobblemonRegistryElement
|
||||
import com.cobblemon.mod.common.api.types.client.ClientData
|
||||
import com.cobblemon.mod.common.api.types.hiddenpower.HiddenPowerRequirement
|
||||
import com.cobblemon.mod.common.api.types.resistance.*
|
||||
import com.cobblemon.mod.common.util.codec.setCodec
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.registry.Registry
|
||||
import net.minecraft.registry.entry.RegistryEntry
|
||||
import net.minecraft.text.MutableText
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
import java.util.Optional
|
||||
|
||||
/**
|
||||
* Class representing a type of a Pokemon or Move
|
||||
* Class representing the elemental type of Pokémon or Move.
|
||||
*
|
||||
* @param name: The English name used to load / find it (spaces -> _)
|
||||
* @param displayName: A Component used to display the name, normally a TranslatableText
|
||||
* @param textureXMultiplier: The multiplier by which the TypeWidget shall move the display
|
||||
* @param resourceLocation: The location of the resource used in the TypeWidget
|
||||
* @param damageTaken The [RawResistanceInformation] that will be converted into [ResistanceInformation] for this type.
|
||||
* @param hiddenPowerRequirement The [HiddenPowerRequirement] for this elemental type. If not present that means the type doesn't support hidden power.
|
||||
* @param clientData The [ClientData] of this type.
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate", "unused")
|
||||
class ElementalType(
|
||||
val name: String,
|
||||
val displayName: MutableText,
|
||||
val hue: Int,
|
||||
val textureXMultiplier: Int,
|
||||
val resourceLocation: Identifier = Identifier(Cobblemon.MODID, "ui/types.png")
|
||||
)
|
||||
private val damageTaken: Set<RawResistanceInformation>,
|
||||
val hiddenPowerRequirement: Optional<HiddenPowerRequirement>,
|
||||
val clientData: ClientData
|
||||
): CobblemonRegistryElement<ElementalType>, ShowdownIdentifiable {
|
||||
|
||||
/**
|
||||
* The translatable component responsible for showing this
|
||||
*/
|
||||
val displayName: MutableText by lazy { Text.translatable("${this.id().namespace}.type.${this.id().path.replace("/", ".")}") }
|
||||
|
||||
/**
|
||||
* The resolved [ResistanceInformation]s.
|
||||
* This is done by lazily invoking [RawResistanceInformation.toImplementation].
|
||||
*/
|
||||
private val resistanceInformation: HashSet<ResistanceInformation> by lazy {
|
||||
val resulting = this.damageTaken.map(RawResistanceInformation::toImplementation).toHashSet()
|
||||
this.populateMissingTypes(resulting)
|
||||
return@lazy resulting
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
fun resistanceInformation(): Set<ResistanceInformation> = this.resistanceInformation
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @param other
|
||||
* @return
|
||||
*/
|
||||
fun resistanceAgainst(other: ElementalType): Resistance {
|
||||
return this.resistanceInformation().firstOrNull { it is ElementalTypeResistanceInformation && it.elementalType == other }?.resistance
|
||||
?: throw IllegalArgumentException("Cannot find type resistance for ${other.id()} this should be impossible.")
|
||||
}
|
||||
|
||||
override fun registryEntry(): RegistryEntry<ElementalType> = this.registry().getEntry(this)
|
||||
|
||||
override fun id(): Identifier = this.registry().getId(this)!!
|
||||
|
||||
override fun registry(): Registry<ElementalType> = CobblemonRegistries.ELEMENTAL_TYPE
|
||||
|
||||
override fun showdownId(): String {
|
||||
val showdownId = ShowdownIdentifiable.REGEX.replace(this.id().path.lowercase(), "")
|
||||
if (this.id().namespace == Cobblemon.MODID) {
|
||||
return showdownId
|
||||
}
|
||||
return this.id().namespace + showdownId
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean = other is ElementalType && other.id() == this.id()
|
||||
|
||||
override fun hashCode(): Int = this.id().hashCode()
|
||||
|
||||
/**
|
||||
* Adds in any missing [ResistanceInformation] for other loaded [ElementalType]s.
|
||||
*
|
||||
* @param current The currently loaded [ResistanceInformation].
|
||||
*/
|
||||
private fun populateMissingTypes(current: HashSet<ResistanceInformation>) {
|
||||
CobblemonRegistries.ELEMENTAL_TYPE.forEach { type ->
|
||||
if (current.none { it is ElementalTypeResistanceInformation && it.elementalType == type }) {
|
||||
val information = ElementalTypeResistanceInformation(type, Resistance.NEUTRAL)
|
||||
current.add(information)
|
||||
Cobblemon.LOGGER.info("{} elemental type was missing resistance information against {}, defaulting to neutral, it is recommended to validate if this is intended or if you should add additional data for the missing type", this.id(), type.id())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
val CODEC: Codec<ElementalType> = RecordCodecBuilder.create { builder ->
|
||||
builder.group(
|
||||
setCodec(RawResistanceInformation.CODEC).fieldOf("damageTaken").forGetter(ElementalType::damageTaken),
|
||||
HiddenPowerRequirement.CODEC.optionalFieldOf("hiddenPowerRequirement").forGetter(ElementalType::hiddenPowerRequirement),
|
||||
ClientData.CODEC.fieldOf("clientData").forGetter(ElementalType::clientData)
|
||||
).apply(builder, ::ElementalType)
|
||||
}
|
||||
|
||||
val NETWORK_CODEC: Codec<ElementalType> = RecordCodecBuilder.create { builder ->
|
||||
builder.group(
|
||||
setCodec(RawResistanceInformation.CODEC).fieldOf("damageTaken").forGetter(ElementalType::damageTaken),
|
||||
ClientData.CODEC.fieldOf("clientData").forGetter(ElementalType::clientData)
|
||||
).apply(builder) { damageTaken, clientData -> ElementalType(damageTaken, Optional.empty(), clientData) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,169 +6,3 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.types
|
||||
|
||||
import net.minecraft.text.MutableText
|
||||
import net.minecraft.text.Text
|
||||
|
||||
/**
|
||||
* Registry for all known ElementalTypes
|
||||
*/
|
||||
object ElementalTypes {
|
||||
|
||||
private val allTypes = mutableListOf<ElementalType>()
|
||||
|
||||
val NORMAL = register(
|
||||
name = "normal",
|
||||
displayName = Text.translatable("cobblemon.type.normal"),
|
||||
hue = 0xDDDDCF,
|
||||
textureXMultiplier = 0
|
||||
)
|
||||
|
||||
val FIRE = register(
|
||||
name = "fire",
|
||||
displayName = Text.translatable("cobblemon.type.fire"),
|
||||
hue = 0xE55C32,
|
||||
textureXMultiplier = 1
|
||||
)
|
||||
|
||||
val WATER = register(
|
||||
name = "water",
|
||||
displayName = Text.translatable("cobblemon.type.water"),
|
||||
hue = 0x4A9BE8,
|
||||
textureXMultiplier = 2
|
||||
)
|
||||
|
||||
val GRASS = register(
|
||||
name = "grass",
|
||||
displayName = Text.translatable("cobblemon.type.grass"),
|
||||
hue = 0x4DBC3C,
|
||||
textureXMultiplier = 3
|
||||
)
|
||||
|
||||
val ELECTRIC = register(
|
||||
name = "electric",
|
||||
displayName = Text.translatable("cobblemon.type.electric"),
|
||||
hue = 0xEFD128,
|
||||
textureXMultiplier = 4
|
||||
)
|
||||
|
||||
val ICE = register(
|
||||
name = "ice",
|
||||
displayName = Text.translatable("cobblemon.type.ice"),
|
||||
hue = 0x6BC3EF,
|
||||
textureXMultiplier = 5
|
||||
)
|
||||
|
||||
val FIGHTING = register(
|
||||
name = "fighting",
|
||||
displayName = Text.translatable("cobblemon.type.fighting"),
|
||||
hue = 0xC44C5C,
|
||||
textureXMultiplier = 6
|
||||
)
|
||||
|
||||
val POISON = register(
|
||||
name = "poison",
|
||||
displayName = Text.translatable("cobblemon.type.poison"),
|
||||
hue = 0xA24BD8,
|
||||
textureXMultiplier = 7
|
||||
)
|
||||
|
||||
val GROUND = register(
|
||||
name = "ground",
|
||||
displayName = Text.translatable("cobblemon.type.ground"),
|
||||
hue = 0xD89950,
|
||||
textureXMultiplier = 8
|
||||
)
|
||||
|
||||
val FLYING = register(
|
||||
name = "flying",
|
||||
displayName = Text.translatable("cobblemon.type.flying"),
|
||||
hue = 0xBCC1FF,
|
||||
textureXMultiplier = 9
|
||||
)
|
||||
|
||||
val PSYCHIC = register(
|
||||
name = "psychic",
|
||||
displayName = Text.translatable("cobblemon.type.psychic"),
|
||||
hue = 0xD86AD6,
|
||||
textureXMultiplier = 10
|
||||
)
|
||||
|
||||
val BUG = register(
|
||||
name = "bug",
|
||||
displayName = Text.translatable("cobblemon.type.bug"),
|
||||
hue = 0xA2C831,
|
||||
textureXMultiplier = 11
|
||||
)
|
||||
|
||||
val ROCK = register(
|
||||
name = "rock",
|
||||
displayName = Text.translatable("cobblemon.type.rock"),
|
||||
hue = 0xAA9666,
|
||||
textureXMultiplier = 12
|
||||
)
|
||||
|
||||
val GHOST = register(
|
||||
name = "ghost",
|
||||
displayName = Text.translatable("cobblemon.type.ghost"),
|
||||
hue = 0x9572E5,
|
||||
textureXMultiplier = 13
|
||||
)
|
||||
|
||||
val DRAGON = register(
|
||||
name = "dragon",
|
||||
displayName = Text.translatable("cobblemon.type.dragon"),
|
||||
hue = 0x535DE8,
|
||||
textureXMultiplier = 14
|
||||
)
|
||||
|
||||
val DARK = register(
|
||||
name = "dark",
|
||||
displayName = Text.translatable("cobblemon.type.dark"),
|
||||
hue = 0x5C6CB2,
|
||||
textureXMultiplier = 15
|
||||
)
|
||||
|
||||
val STEEL = register(
|
||||
name = "steel",
|
||||
displayName = Text.translatable("cobblemon.type.steel"),
|
||||
hue = 0xC3CCE0,
|
||||
textureXMultiplier = 16
|
||||
)
|
||||
|
||||
val FAIRY = register(
|
||||
name = "fairy",
|
||||
displayName = Text.translatable("cobblemon.type.fairy"),
|
||||
hue = 0xEA727E,
|
||||
textureXMultiplier = 17
|
||||
)
|
||||
|
||||
fun register(name: String, displayName: MutableText, hue: Int, textureXMultiplier: Int): ElementalType {
|
||||
return ElementalType(
|
||||
name = name,
|
||||
displayName = displayName,
|
||||
hue = hue,
|
||||
textureXMultiplier = textureXMultiplier
|
||||
).also {
|
||||
allTypes.add(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun register(elementalType: ElementalType): ElementalType {
|
||||
allTypes.add(elementalType)
|
||||
return elementalType
|
||||
}
|
||||
|
||||
fun get(name: String): ElementalType? {
|
||||
return allTypes.firstOrNull { type -> type.name.equals(name, ignoreCase = true) }
|
||||
}
|
||||
|
||||
fun getOrException(name: String): ElementalType {
|
||||
return allTypes.first { type -> type.name.equals(name, ignoreCase = true) }
|
||||
}
|
||||
|
||||
fun count() = allTypes.size
|
||||
|
||||
fun all() = this.allTypes.toList()
|
||||
}
|
||||
+8
-9
@@ -8,22 +8,21 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.types.adapters
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.registry.CobblemonRegistries
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.google.gson.JsonDeserializationContext
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonPrimitive
|
||||
import com.google.gson.JsonSerializationContext
|
||||
import com.google.gson.JsonSerializer
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.google.gson.*
|
||||
import java.lang.reflect.Type
|
||||
|
||||
object ElementalTypeAdapter: JsonSerializer<ElementalType>, JsonDeserializer<ElementalType> {
|
||||
|
||||
override fun serialize(src: ElementalType, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
|
||||
return JsonPrimitive(src.name)
|
||||
return JsonPrimitive(if (src.id().namespace == Cobblemon.MODID) src.id().path else src.id().toString())
|
||||
}
|
||||
|
||||
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): ElementalType {
|
||||
return ElementalTypes.getOrException(json.asString)
|
||||
return CobblemonRegistries.ELEMENTAL_TYPE.get(json.asString.asIdentifierDefaultingNamespace())!!
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.types.client
|
||||
|
||||
import com.cobblemon.mod.common.util.math.toRGB
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
/**
|
||||
* Represents various properties used in UIs.
|
||||
* This data is present on the server and synchronized to clients.
|
||||
*
|
||||
* @property hue The RGB tint applied in a few UIs.
|
||||
* @property primaryTexture The primary texture for the type icon.
|
||||
* @property secondaryTexture The secondary texture used when a miniature icon is wanted.
|
||||
* @property uOffset The uOffset on the blitk.
|
||||
* @property vOffset The vOffset on the blitk.
|
||||
*/
|
||||
data class ClientData(
|
||||
val hue: Int,
|
||||
val primaryTexture: Identifier,
|
||||
val secondaryTexture: Identifier,
|
||||
val uOffset: Float,
|
||||
val vOffset: Float
|
||||
) {
|
||||
|
||||
val something = 0xA2C831
|
||||
|
||||
companion object {
|
||||
|
||||
val CODEC: Codec<ClientData> = RecordCodecBuilder.create { builder ->
|
||||
builder.group(
|
||||
Codec.STRING.xmap(Integer::decode, Integer::toHexString).fieldOf("hue").forGetter(ClientData::hue),
|
||||
Identifier.CODEC.fieldOf("primaryTexture").forGetter(ClientData::primaryTexture),
|
||||
Identifier.CODEC.fieldOf("secondaryTexture").forGetter(ClientData::secondaryTexture),
|
||||
Codec.FLOAT.optionalFieldOf("uOffset", 0F).forGetter(ClientData::uOffset),
|
||||
Codec.FLOAT.optionalFieldOf("vOffset", 0F).forGetter(ClientData::vOffset)
|
||||
).apply(builder, ::ClientData)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.types.hiddenpower
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.Environment
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stat
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.mojang.serialization.Codec
|
||||
|
||||
/**
|
||||
* Responsible for determining if a [Pokemon] can use the hidden power of a given type.
|
||||
* This will be attached to a [ElementalType.hiddenPowerRequirement].
|
||||
*
|
||||
* @property statMap The stat map that checks [IvCondition]s for each IV value of a [Pokemon].
|
||||
*
|
||||
* @throws IllegalArgumentException if not every stat of type [Stat.Type.PERMANENT] is present.
|
||||
*/
|
||||
class HiddenPowerRequirement(internal val statMap: Map<Stat, IvCondition>) {
|
||||
|
||||
init {
|
||||
if (Cobblemon.implementation.environment() == Environment.SERVER && !this.statMap.keys.containsAll(Cobblemon.statProvider.ofType(Stat.Type.PERMANENT))) {
|
||||
throw IllegalArgumentException("Cannot create a ${this::class.simpleName} when missing permanent stats")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given [Pokemon] passes this requirement.
|
||||
*
|
||||
* @param pokemon The [Pokemon] being checked.
|
||||
* @return If this requirement is passed against the [pokemon].
|
||||
*/
|
||||
fun isValid(pokemon: Pokemon): Boolean = pokemon.ivs.all { (stat, value) -> this.statMap[stat]!!.fits(value) }
|
||||
|
||||
companion object {
|
||||
|
||||
val CODEC: Codec<HiddenPowerRequirement> = Codec.unboundedMap(Stat.CODEC, IvCondition.CODEC)
|
||||
.xmap(::HiddenPowerRequirement, HiddenPowerRequirement::statMap)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Loaded 100 of 2393 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user