forked from mirrors/cobblemon
Compare commits
470
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ccd0bbe11 | ||
|
|
d289880cc8 | ||
|
|
6609c7d038 | ||
|
|
88afe05c16 | ||
|
|
99aa25e1bf | ||
|
|
76faa50035 | ||
|
|
8ce989cb6b | ||
|
|
02030950aa | ||
|
|
6148b58fdc | ||
|
|
1e4936aeac | ||
|
|
15ea72e777 | ||
|
|
3c3d4e4552 | ||
|
|
796e416578 | ||
|
|
7608dfde94 | ||
|
|
b2b871617a | ||
|
|
bc4c8d13b7 | ||
|
|
0f9c51f7dd | ||
|
|
2c2623e0f9 | ||
|
|
8ec85e7d2f | ||
|
|
d5a08314eb | ||
|
|
062c32be2a | ||
|
|
09590b49cd | ||
|
|
420cbd2c99 | ||
|
|
0380013f37 | ||
|
|
6c1fcc103a | ||
|
|
59b01c3988 | ||
|
|
678fa2d0ce | ||
|
|
94abd79923 | ||
|
|
0a1931aaae | ||
|
|
85d76dcc29 | ||
|
|
bd0a9ef654 | ||
|
|
7a702e276c | ||
|
|
3f9a2825cb | ||
|
|
d1462bc27b | ||
|
|
211b14dbd0 | ||
|
|
47e83003c3 | ||
|
|
59134c4212 | ||
|
|
44b434562c | ||
|
|
7af9b0854a | ||
|
|
344b0750f9 | ||
|
|
10d9d97c11 | ||
|
|
94bcfba076 | ||
|
|
69a75ffcd4 | ||
|
|
cc1317dd06 | ||
|
|
e15ac73b1b | ||
|
|
6c6576c117 | ||
|
|
ebdfa7edc0 | ||
|
|
297e8eae77 | ||
|
|
ea8179018d | ||
|
|
14fccfae1d | ||
|
|
26386638ad | ||
|
|
5d1d99b64a | ||
|
|
a3c5a6c377 | ||
|
|
eb2a5837eb | ||
|
|
93298d9a87 | ||
|
|
abb5346305 | ||
|
|
9466f66721 | ||
|
|
716ea55bc7 | ||
|
|
4d1f46c37c | ||
|
|
15f0ad1821 | ||
|
|
a2101b5759 | ||
|
|
187a190ec6 | ||
|
|
0d433ff394 | ||
|
|
a16975c750 | ||
|
|
c0e6d87c40 | ||
|
|
91078b917e | ||
|
|
b7e27fa52a | ||
|
|
dc87ffd2a8 | ||
|
|
d873399d05 | ||
|
|
a7aab51e0d | ||
|
|
e8e2057558 | ||
|
|
56b5dba911 | ||
|
|
f0a66458e5 | ||
|
|
dba6a38f9e | ||
|
|
342c0041e9 | ||
|
|
cbeda361d4 | ||
|
|
a811b3347c | ||
|
|
80e593f5c6 | ||
|
|
ff617348fe | ||
|
|
1adecbf5f6 | ||
|
|
29dbb13a0e | ||
|
|
67e03ecebd | ||
|
|
215f48bdc7 | ||
|
|
d4beab1bfb | ||
|
|
f2c21ec902 | ||
|
|
c9f3912677 | ||
|
|
c21b7d1a7d | ||
|
|
26068694ad | ||
|
|
c86d8f0696 | ||
|
|
16468059e4 | ||
|
|
54dbe4769e | ||
|
|
82019c3722 | ||
|
|
079a2fb50d | ||
|
|
78f0951345 | ||
|
|
96aaab29e2 | ||
|
|
b1def44953 | ||
|
|
e2e38add1d | ||
|
|
1db0b0709a | ||
|
|
c3ce3db387 | ||
|
|
b5e6fd3816 | ||
|
|
5b5dcddb25 | ||
|
|
0158524bff | ||
|
|
2611e311a7 | ||
|
|
17061691a1 | ||
|
|
9c6d7ddfef | ||
|
|
e8119198c1 | ||
|
|
55c0e3e8a0 | ||
|
|
70443bc7b0 | ||
|
|
acb166b5b9 | ||
|
|
462c361998 | ||
|
|
76c90fb1ae | ||
|
|
27e11dee87 | ||
|
|
d7c8b8dd62 | ||
|
|
eb12ccf6d0 | ||
|
|
e7b58edcd1 | ||
|
|
ca5c2ba381 | ||
|
|
8aa538d8e7 | ||
|
|
1bd33a0635 | ||
|
|
ff9ca4ee09 | ||
|
|
1def8fcd1a | ||
|
|
d53603ec49 | ||
|
|
5362995fbb | ||
|
|
e2d0b12f94 | ||
|
|
b32c2cb81c | ||
|
|
98a7240191 | ||
|
|
84adc0e1cb | ||
|
|
3ab2dfa0b3 | ||
|
|
d67669a278 | ||
|
|
2a85dee373 | ||
|
|
86e8238230 | ||
|
|
bdc7b5a504 | ||
|
|
f3c165d282 | ||
|
|
9caf8376bc | ||
|
|
8661f7a01e | ||
|
|
e933a03e92 | ||
|
|
982fc054a9 | ||
|
|
cb73bfb31b | ||
|
|
5b84c2c8a0 | ||
|
|
9f998d92e6 | ||
|
|
b835bfe596 | ||
|
|
757e3d48cb | ||
|
|
2fbf77dd65 | ||
|
|
bd451bf951 | ||
|
|
d88a9dda92 | ||
|
|
86c2282cf8 | ||
|
|
ac85cddeb6 | ||
|
|
a9c2373979 | ||
|
|
6ac8523cf5 | ||
|
|
acb0aa854e | ||
|
|
d5cff3ba06 | ||
|
|
8e22538707 | ||
|
|
f161699b6a | ||
|
|
fdafb1e64a | ||
|
|
b0119e32c5 | ||
|
|
58bab4b94b | ||
|
|
a91bf60ff8 | ||
|
|
2d4c419216 | ||
|
|
981f7307e6 | ||
|
|
c04345a789 | ||
|
|
93e84e6ffd | ||
|
|
cc6305bfec | ||
|
|
7d38efaa50 | ||
|
|
9a3f5a605a | ||
|
|
bd525c8740 | ||
|
|
b9922057ce | ||
|
|
d9c765ca53 | ||
|
|
96c3f7764e | ||
|
|
255930751a | ||
|
|
d3f32ee645 | ||
|
|
e444206a55 | ||
|
|
209dee1d25 | ||
|
|
e58acd646f | ||
|
|
bdcc05aa78 | ||
|
|
bb899118ef | ||
|
|
71585174c0 | ||
|
|
1eedc4862a | ||
|
|
b1fbf9ca02 | ||
|
|
3449c98b56 | ||
|
|
5fda445274 | ||
|
|
60aa991d55 | ||
|
|
8da0fc34c6 | ||
|
|
41c55d80a8 | ||
|
|
09dfbccb8e | ||
|
|
ce0265e954 | ||
|
|
a66d263dd8 | ||
|
|
5a929e3901 | ||
|
|
da1654d9aa | ||
|
|
27132031cd | ||
|
|
cdc4a571b6 | ||
|
|
e8bc138e5d | ||
|
|
038d239f95 | ||
|
|
d95db24426 | ||
|
|
a64cbb7e8a | ||
|
|
9b2a1dc065 | ||
|
|
3c222a3349 | ||
|
|
e2ba1dc575 | ||
|
|
f7e381924e | ||
|
|
a1035608c9 | ||
|
|
2345565e38 | ||
|
|
f4c2a5197c | ||
|
|
620002c165 | ||
|
|
361a8fcbe1 | ||
|
|
e179e044ef | ||
|
|
d89224a684 | ||
|
|
f55d8737ba | ||
|
|
336180656e | ||
|
|
856eb2a1bd | ||
|
|
620589681d | ||
|
|
8359b1ade1 | ||
|
|
136d7c08b8 | ||
|
|
ddc03766bb | ||
|
|
81dccf9c07 | ||
|
|
ea93b8c1d8 | ||
|
|
8018c1142a | ||
|
|
cfca238573 | ||
|
|
e3cf367e45 | ||
|
|
b9b01af451 | ||
|
|
1abe39db3c | ||
|
|
4e7cdb6266 | ||
|
|
79b13acdc6 | ||
|
|
c68f637b70 | ||
|
|
d44126179a | ||
|
|
432b350d98 | ||
|
|
26677420dd | ||
|
|
a1487e4ee5 | ||
|
|
78b2a7d721 | ||
|
|
d4a3dfdb07 | ||
|
|
0f3dcb760e | ||
|
|
0ffd6fad90 | ||
|
|
3c9661dfd3 | ||
|
|
520f522f3c | ||
|
|
113ae1f749 | ||
|
|
61bf5ffab1 | ||
|
|
0e1b59fb25 | ||
|
|
3565015039 | ||
|
|
218b90860b | ||
|
|
a59573073f | ||
|
|
6475d7d732 | ||
|
|
c467e98345 | ||
|
|
6b206cfaaf | ||
|
|
8aac4c294f | ||
|
|
2e330076b8 | ||
|
|
e1cd217290 | ||
|
|
87da3e83a0 | ||
|
|
c325d0215a | ||
|
|
8afb4e6d16 | ||
|
|
60f00ca34d | ||
|
|
416a2ac390 | ||
|
|
0ffff6f4af | ||
|
|
690ada8271 | ||
|
|
b1b06f19b2 | ||
|
|
5bc41245ad | ||
|
|
490fa91ce7 | ||
|
|
8606d6928d | ||
|
|
5897b43564 | ||
|
|
9fd8d32215 | ||
|
|
03c278229b | ||
|
|
8ce3322105 | ||
|
|
364d084584 | ||
|
|
916e6de1d2 | ||
|
|
0b9e706a36 | ||
|
|
6d4f529fc0 | ||
|
|
015de548d3 | ||
|
|
04ca03b3a5 | ||
|
|
c7c145ab78 | ||
|
|
a376863953 | ||
|
|
2e68c3fc44 | ||
|
|
36f2eee7f9 | ||
|
|
71ac29ba8b | ||
|
|
781c82230e | ||
|
|
c58d49ebab | ||
|
|
fd8d5630f4 | ||
|
|
a86b11fdd5 | ||
|
|
829dca9d99 | ||
|
|
0f0368f3bd | ||
|
|
6533030d36 | ||
|
|
0efb0d9767 | ||
|
|
8151f9b3e6 | ||
|
|
4e8ffd3558 | ||
|
|
b88873fbc3 | ||
|
|
dba1ab26a5 | ||
|
|
cc2295d629 | ||
|
|
4d3d8d1607 | ||
|
|
1d09360844 | ||
|
|
079e75d828 | ||
|
|
a3567ead04 | ||
|
|
f4fef2855b | ||
|
|
6f614eb02e | ||
|
|
72b82858b3 | ||
|
|
361047c309 | ||
|
|
18f43f72ad | ||
|
|
cbb409b057 | ||
|
|
f8cec09f70 | ||
|
|
5579975b46 | ||
|
|
5a57f41fbb | ||
|
|
1845faf9ad | ||
|
|
0e93682457 | ||
|
|
cef69f478d | ||
|
|
5ea7bc9c48 | ||
|
|
bfc8ace83a | ||
|
|
e0f93fd797 | ||
|
|
ba13414ada | ||
|
|
66c63d54db | ||
|
|
9e836bcc3b | ||
|
|
27fb1f6c66 | ||
|
|
7f60885f92 | ||
|
|
cb7f6d7bba | ||
|
|
a3498fe03b | ||
|
|
af4af3395c | ||
|
|
3ab07ecbca | ||
|
|
b96eccbf2f | ||
|
|
afa7426116 | ||
|
|
a5ace52cb5 | ||
|
|
9f3940bb31 | ||
|
|
8a8c131a44 | ||
|
|
8b0fba4201 | ||
|
|
e1e04b19bf | ||
|
|
a04beb1ce8 | ||
|
|
bd9aea7c59 | ||
|
|
757e3a7ff7 | ||
|
|
66d03651f9 | ||
|
|
3c1577d58f | ||
|
|
dd7d562228 | ||
|
|
a01ac32e54 | ||
|
|
5c0ae854bf | ||
|
|
55c189b749 | ||
|
|
b5b81fa1aa | ||
|
|
eda58859ce | ||
|
|
3888a48cb0 | ||
|
|
c9b609a3f8 | ||
|
|
70fb7c87ad | ||
|
|
a24189b816 | ||
|
|
c0d80634a4 | ||
|
|
b52e4de85c | ||
|
|
7d37a98a58 | ||
|
|
a424796aa8 | ||
|
|
8518be0d44 | ||
|
|
24760270bd | ||
|
|
19f9490274 | ||
|
|
93a94f04a1 | ||
|
|
97eac48cd7 | ||
|
|
2c31f002b2 | ||
|
|
4f0eb16ab0 | ||
|
|
5004b61967 | ||
|
|
cf137efa02 | ||
|
|
862c0bda89 | ||
|
|
868874b06d | ||
|
|
068e4a1c92 | ||
|
|
267f2aa5a8 | ||
|
|
333d8fa732 | ||
|
|
e8d4e406d5 | ||
|
|
9e5c296309 | ||
|
|
69dc89b04f | ||
|
|
dd31fb1a51 | ||
|
|
9b3a729b3c | ||
|
|
c72ab20cfa | ||
|
|
3cfc915b1a | ||
|
|
b8541486d4 | ||
|
|
c69b38454e | ||
|
|
0e5787b8ab | ||
|
|
082be6f2d0 | ||
|
|
398f14efd5 | ||
|
|
e8ace8ba05 | ||
|
|
1a78625111 | ||
|
|
596c2b95b4 | ||
|
|
38648111a6 | ||
|
|
149b8f27e6 | ||
|
|
91433d7e13 | ||
|
|
85aadbabe9 | ||
|
|
e64c1487cf | ||
|
|
4d866f6207 | ||
|
|
e81418836e | ||
|
|
3d779e637b | ||
|
|
bd0ad89971 | ||
|
|
586ced7bfb | ||
|
|
218f2c67d3 | ||
|
|
59b0397ea4 | ||
|
|
aadb78149c | ||
|
|
3305874dd4 | ||
|
|
2f688291dc | ||
|
|
1f099b231c | ||
|
|
c1ccc718cc | ||
|
|
74df81006b | ||
|
|
e7a87e299c | ||
|
|
c688e9f80c | ||
|
|
ac1897b41a | ||
|
|
40e8f4efa5 | ||
|
|
e00165aafb | ||
|
|
b2015c9cd8 | ||
|
|
d0b796f777 | ||
|
|
83488ec728 | ||
|
|
b3d387d8da | ||
|
|
902c6751c5 | ||
|
|
4088f144e4 | ||
|
|
3e592b21f1 | ||
|
|
b1186c8033 | ||
|
|
d038f63024 | ||
|
|
7a3088eb59 | ||
|
|
de51d2b1b2 | ||
|
|
3c3aa3d4a3 | ||
|
|
50e4829b79 | ||
|
|
ebdd06a4a9 | ||
|
|
82fd4bae08 | ||
|
|
277a7dc32b | ||
|
|
f00d578f64 | ||
|
|
ea9fba72ec | ||
|
|
4654c3c2b8 | ||
|
|
b4496b30a9 | ||
|
|
431662b237 | ||
|
|
1299214d61 | ||
|
|
c5c2c3a059 | ||
|
|
64b7970cb5 | ||
|
|
ce6c777c4e | ||
|
|
284b032dd8 | ||
|
|
23d70ab1fe | ||
|
|
3340239440 | ||
|
|
e513d52c3d | ||
|
|
edd4d18150 | ||
|
|
a1b6bd79d2 | ||
|
|
83eb483f0b | ||
|
|
d8498d7a37 | ||
|
|
3693e7eec0 | ||
|
|
60c9f5d054 | ||
|
|
e1541a3512 | ||
|
|
1c1c3f51bb | ||
|
|
500f1f53a4 | ||
|
|
9ed10bb217 | ||
|
|
2f6d4313e7 | ||
|
|
d640a04fa6 | ||
|
|
7749f346b5 | ||
|
|
a29bc68663 | ||
|
|
1358380905 | ||
|
|
2679885fb4 | ||
|
|
278c5d8e81 | ||
|
|
a55916998b | ||
|
|
cca2d54682 | ||
|
|
f5bd6a4054 | ||
|
|
13466815e5 | ||
|
|
e871047cf1 | ||
|
|
a1de8c95f6 | ||
|
|
eb94bdb9c2 | ||
|
|
509c2365bb | ||
|
|
6271476ed3 | ||
|
|
f5cec10e3e | ||
|
|
b6f20c1d75 | ||
|
|
750d583770 | ||
|
|
05f0b439a8 | ||
|
|
1cb4ffd02f | ||
|
|
8600d026c3 | ||
|
|
4e24c764de | ||
|
|
192d40a475 | ||
|
|
8c86bb636d | ||
|
|
5dc43a8d58 | ||
|
|
68a3d7e3b1 | ||
|
|
8f4b2c3c0a | ||
|
|
6645cb68f7 | ||
|
|
4c5f330f2a | ||
|
|
00d445b793 | ||
|
|
cd236c2dea | ||
|
|
aebf40f78c | ||
|
|
52b28fb73f | ||
|
|
9b75f20fff | ||
|
|
56b1fc1149 | ||
|
|
662ca83a60 | ||
|
|
d80ff055f5 | ||
|
|
898f48cf8f | ||
|
|
1688dd42b3 | ||
|
|
c4e8fe9c74 | ||
|
|
3ecaf2dd13 | ||
|
|
281afd74e6 |
@@ -24,6 +24,7 @@ eclipse
|
||||
run
|
||||
runClient
|
||||
runServer
|
||||
utilityscripts/implementation/*.txt
|
||||
|
||||
# Files from Forge MDK
|
||||
forge*changelog.txt
|
||||
|
||||
+2
-2
@@ -71,8 +71,8 @@ setup_packsquash:
|
||||
- echo "Installing wget & unzip"
|
||||
- yum install -y wget unzip
|
||||
- echo "Downloading PackSquash"
|
||||
- wget https://github.com/ComunidadAylas/PackSquash/releases/download/v0.4.0/PackSquash.CLI.executable.x86_64-unknown-linux-musl.zip
|
||||
- unzip PackSquash.CLI.executable.x86_64-unknown-linux-musl.zip -d ./packsquash_bin
|
||||
- wget https://github.com/ComunidadAylas/PackSquash/releases/download/v0.4.1/packsquash-x86_64-unknown-linux-musl.zip
|
||||
- unzip packsquash-x86_64-unknown-linux-musl.zip -d ./packsquash_bin
|
||||
- echo "Creating PackSquash options.toml"
|
||||
- |
|
||||
cat <<EOF > ./options.toml
|
||||
|
||||
+264
-35
@@ -42,9 +42,8 @@
|
||||
- All ruin structures gilded chest loot tables have been updated to include a guaranteed TM not learned naturally by any Pokémon through their level-up moveset. Each ruin gets its own specific TM, so you can now go hunting ruins to complete your move dex!
|
||||
- All fossil structures have been transferred over to biome features, allowing for better placement in generation.
|
||||
- The prehistoric_underwater_fissure & prehistoric_submerged_impact fossil structures now use the biome tag `#cobblemon:is_temperate_ocean`.
|
||||
- A new Biome tag called `#cobblemon:is_stony_beach` has been added.
|
||||
- Gimmighoul towers now generate at ground level rather than sinking one block into the ground.
|
||||
- 1 New shipwreck cove structure called the "magma_shipwreck_cove" has been added.
|
||||
- A new shipwreck cove structure called the "magma_shipwreck_cove" has been added.
|
||||
- Shipwreck coves now generate slightly more frequently.
|
||||
- Added equipment loot tables for shipwreck cove enemies.
|
||||
- Maps from abandoned fishing boats leading to shipwreck coves now will skip explored chunks, allowing the player to find more shipwreck coves easier.
|
||||
@@ -53,80 +52,162 @@
|
||||
- All shipwreck coves' spawners have been adjusted to give compatible mobs enchanted iron or diamond armour and weapons while in its ominous state. The armour given to the mobs will also use the automaton trim. We upped the difficulty because it was too easy now that your Pokémon can fight for you.
|
||||
- One TM of the elemental hyper beams has been added as guaranteed loot to the main treasure chest in every shipwreck cove. (Magma cove gets blast burn, lush cove gets frenzy plant, and submerged cove gets hydro cannon)
|
||||
- Added 7 new advancements
|
||||
- Pressing the inventory key now closes the Pokémon interact wheel GUI
|
||||
- Pokémon cries now play when clicking Pokémon in the summary screen.
|
||||
- Pressing the Summary key while either the starter selection or summary screen are open now closes those screens
|
||||
- Ice type Pokémon are now immune to Packed Snow
|
||||
- Held item visibility can now be controlled by the held_item_visible command argument.
|
||||
|
||||
### New rideable Pokémon
|
||||
- Drampa
|
||||
- Archeops
|
||||
- Skarmory
|
||||
- Crobat
|
||||
- Goodra
|
||||
- Hisuian Goodra
|
||||
- Milotic
|
||||
|
||||
### Pokémon Added
|
||||
- Pancham (+ Cosmetic with Black Glasses)
|
||||
- Pangoro (+ Cosmetic with Black Glasses)
|
||||
- Skitty
|
||||
- Delcatty
|
||||
#### Generation 3
|
||||
- Wurmple
|
||||
- Silcoon
|
||||
- Beautifly
|
||||
- Cascoon
|
||||
- Dustox
|
||||
- Skitty
|
||||
- Delcatty
|
||||
- Seviper
|
||||
- Zangoose
|
||||
|
||||
#### Generation 4
|
||||
- Cherubi
|
||||
- Cherrim
|
||||
- Stunky
|
||||
- Skuntank
|
||||
|
||||
#### Generation 5
|
||||
- Audino
|
||||
- Pawniard
|
||||
- Bisharp
|
||||
|
||||
#### Generation 6
|
||||
- Pancham (+ Cosmetic with Black Glasses)
|
||||
- Pangoro (+ Cosmetic with Black Glasses)
|
||||
|
||||
#### Generation 7
|
||||
- Rockruff
|
||||
- Lycanroc
|
||||
|
||||
#### Generation 8
|
||||
- Rolycoly
|
||||
- Carkol
|
||||
- Coalossal
|
||||
- Runerigus
|
||||
- Snom
|
||||
- Frosmoth
|
||||
- Indeedee
|
||||
- Duraludon
|
||||
- Applin
|
||||
- Flapple
|
||||
- Appletun
|
||||
- Dipplin
|
||||
- Hydrapple
|
||||
- Sandshrew (Alolan)
|
||||
- Sandslash (Alolan)
|
||||
- Seviper
|
||||
- Zangoose
|
||||
- Stunky
|
||||
- Skuntank
|
||||
- Pawniard
|
||||
- Bisharp
|
||||
|
||||
#### Generation 9
|
||||
|
||||
- Nymble
|
||||
- Lokix
|
||||
- Kingambit
|
||||
- Dipplin
|
||||
- Archaludon
|
||||
- Hydrapple
|
||||
|
||||
#### Regional Forms
|
||||
- Sandshrew (Alolan Form)
|
||||
- Sandslash (Alolan Form)
|
||||
- Yamask (Galarian Form)
|
||||
- Avalugg (Hisuian Form)
|
||||
|
||||
### Model updates for the following Pokémon
|
||||
- Goomy
|
||||
- Sliggoo
|
||||
- Goodra
|
||||
- Porygon
|
||||
- Porygon2
|
||||
- Porygon-Z
|
||||
|
||||
- Sandshrew
|
||||
- Sandslash
|
||||
- Nidoking
|
||||
- Zubat
|
||||
- Golbat
|
||||
- Crobat
|
||||
- Pidgey
|
||||
- Pidgeotto
|
||||
- Pidgeot
|
||||
- Sandslash
|
||||
- Nidoking
|
||||
- Ditto
|
||||
- Porygon
|
||||
- Porygon2
|
||||
- Porygon-Z
|
||||
- Skarmory
|
||||
- Smoochum
|
||||
- Jynx
|
||||
- Popplio
|
||||
- Brionne
|
||||
- Primarina
|
||||
- Fletchling
|
||||
- Fletchinder
|
||||
- Talonflame
|
||||
- Sableye
|
||||
- Mawile
|
||||
- Goomy
|
||||
- Sliggoo
|
||||
- Goodra
|
||||
- Nacli
|
||||
- Naclestack
|
||||
- Naclstack
|
||||
- Garganacl
|
||||
- Skarmory
|
||||
- Bidoof
|
||||
- Cranidos
|
||||
- Chatot
|
||||
- Yamask
|
||||
- Cofagrigus
|
||||
- Tyranitar
|
||||
- Turtonator
|
||||
- Arbok (New Patterns: Angry, Cartoon, Clown, Comedic, Crow, Grin, Ink, Modern, Rain, Scribble, Silence, Sloth, Suspicious, Will)
|
||||
|
||||
#### Model changes to the regional bias resource pack:
|
||||
|
||||
- Added Mime Jr. (Galar Bias)
|
||||
- Added Bergmite (Hisui Bias)
|
||||
- Changed models for every existing regional bias
|
||||
|
||||
### Animation updates for the following Pokémon
|
||||
- Drampa
|
||||
- Archeops
|
||||
- Skarmory
|
||||
- Skitty
|
||||
- Flygon
|
||||
- Shellos
|
||||
- Riolu
|
||||
- Lucario
|
||||
- Shellos
|
||||
- Skitty
|
||||
- Joltik
|
||||
- Purrloin
|
||||
- Liepard
|
||||
- Flygon
|
||||
- Joltik
|
||||
- Galvantula
|
||||
- Nidoking
|
||||
- Fletchling
|
||||
- Fletchinder
|
||||
- Talonflame
|
||||
- Archeops
|
||||
- Drampa
|
||||
- Sewaddle
|
||||
- Swadloon
|
||||
- Leavanny
|
||||
- Goomy
|
||||
- Sliggoo
|
||||
- Goodra
|
||||
- Shedinja
|
||||
- Skwovet
|
||||
- Greedent
|
||||
- Bidoof
|
||||
- Cranidos
|
||||
- Chatot
|
||||
- Yamask
|
||||
- Cofagrigus
|
||||
- Luvdisc
|
||||
|
||||
#### Added sounds to the following moves
|
||||
- Absorb
|
||||
- Aurora Beam
|
||||
- Double Team
|
||||
- Draco Meteor
|
||||
- Fire Punch
|
||||
- Fire Spin
|
||||
- Flame Charge
|
||||
@@ -146,10 +227,19 @@
|
||||
- Tail Whip
|
||||
|
||||
### Changes
|
||||
- Stat increase and decrease particles have been changed, with the addition of alpha boost particles.
|
||||
- Dragonite's mailbag cosmetic can now also be obtained with a chest in the cosmetic slot
|
||||
|
||||
### Fixes
|
||||
- Adjusted Croagunk's walkspeed
|
||||
- Fixed Blitzle's base scale being `1` rather than the intended `0.8`
|
||||
- Fixed Magnezone's emissive layer for the shiny having the wrong name, thus displaying wrongly
|
||||
- Fixed Litleo not blinking
|
||||
- Fixed Garchomp T-posing when sleeping while out of battle.
|
||||
- Sharp Beak can now be crafted
|
||||
- Metal Alloy can now be crafted
|
||||
- Ability Capsules can now be brewed
|
||||
- Using an Ability Capsule will now return an Empty Bottle
|
||||
- The following items can now be obtained by brushing the following Pokémon:
|
||||
- Pink Wool - Wigglytuff
|
||||
- White Wool - Swablu, Altaria
|
||||
@@ -173,6 +263,50 @@
|
||||
- Cleaned up seat data. Removed all now unused fields from seats in all species files. Also made locator seat names explicit in the species files (however, this doesn't break the legacy format)
|
||||
- Marks can now provide aspects.
|
||||
- Added new shiny and Alpha chance configs
|
||||
- Apricorns now make a new sound when harvested
|
||||
- Lowered the volume of harvesting berries
|
||||
- Added alternate versions of the basic PC Wallpapers based on the original design of Cobblemon's PC, before public release
|
||||
- Improved compatibility with various mods
|
||||
- Farmer's Delight's cutting board can now be used to strip Apricorn and Saccharine's logs, or salvage their planks from various blocks. It can also separate their boats from chests
|
||||
- Farmer's Delight's cutting board can be used to get two white dye from Pep-Up Flowers and two magenta dye from Medicinal Leeks
|
||||
- Farmer's Delight's cutting board can separate Tumblestone Blocks back into Tumblestones
|
||||
- Farmer's Delight's cutting board can salvage Type Gems from their Gem Blocks
|
||||
- Farmer's Delight's Onions, Tomatoes, and Cabbages can be used to cook Open-Faced Sandwiches. Nature's Spirit's Olives also can be used
|
||||
- Farmer's Delight's cutting board can be used on Hearty Grains to obtain rice and straw, similarly to Farmer's Delight's Rice Panicles
|
||||
- Farmer's Delight's Apple Pies and Apple Ciders can now be made using Applin Apples, alongside the Vanilla counterpart
|
||||
- Amendments allows for cooking Leek and Potato Stew in a Cauldron
|
||||
- Any mods that utilise Moonlight's Soft Fluid system can store Leek and Potato Stew, Medicinal Brew, Antidote, Awakening, Burn Heal, Ice Heal, Paralyze Heal, Full Heal, Potion, Super Potion, Hyper Potion, Max Potion, Full Restore, Ability Capsules, Moomoo Milk, and Berry Juice in liquid form (such as Suplementaries' Jar or Goblet, or Cauldrons while using Amendments)
|
||||
- Supplementaries' Hourglass can now use Bright Powder, Quick Powder, Metal Powder, Heal Powder, Silver Powder, and Remedy
|
||||
- Berry Trees are now compatible with the Planter properly
|
||||
- Saccharine Way Signs (added by Supplementaries automagically) now use a premade texture to prevent odd visual issues
|
||||
- Supplementaries' Jar can now store various Cobblemon cookies, candies, and similar items
|
||||
- Some of Supplementaries' items have been added to the `#cobblemon:held/container_held_items` item tag, including sacks, safes, and lunch baskets
|
||||
- Experience Nuggets can be used in place of Sculk for making the EXP Candies if using Create
|
||||
- Rage Candy Bar has a Create Mixing recipe
|
||||
- All Cobblemon ores can now be crushed in Create's Crushing Wheels
|
||||
- All Gem and Tumblestone Clusters can be crushed for automatic, low return, gems
|
||||
- Hearty Grains can be milled like Farmer's Delight Rice Panicles if both Farmer's Delight and Create are installed
|
||||
- Big Roots and Revival Herbs can be milled to their respective powders
|
||||
- Pep-Up Flowers, Medicinal Leeks, and all 70 berries can be milled in Create's Millstone for additional dye drops
|
||||
- Auspicious Armor and Upgrades can be Haunted using Create's crafting systems
|
||||
- Applin Apples can be used to make Honeyed Apples in Create
|
||||
- Golden Apples can now be crafted using the Applin Apples
|
||||
- Prismarine Crystals and Amethyst Shards can now be used to craft Pokedexes
|
||||
- Updated Pokedex Screens item tag to support tagged variants of Redstone and Glowstone Dust
|
||||
- Sounds for interacting with various Cobblemon blocks now play from the center of the block, rather than the corner
|
||||
- The `/pc` command no longer plays PC activation sounds
|
||||
- Lowered the volume of Blitzle and Zebstrika's cry
|
||||
- Fixed Raichu (male) and Alolan Raichu (both genders) not displaying the correct Partner Cap when Pewter Crunchies are in the cosmetic slot.
|
||||
- Regional Foods will now prioritize being eaten instead of used for healing Pokémon, shift clicking will use it as healing
|
||||
- Resolved Z-Fighting on Pokédex Models
|
||||
- Restored Ninjask's ability to do barrel rolls
|
||||
- Fixed a crash caused by adding enchanted items to the Resurrection Machine
|
||||
- Fixed a crash that could occur when starting a PvN battle with only fainted Pokémon.
|
||||
- Fixed the Remember Riding Camera config not being respected when dismounting and resolved the incompatibility with the Auto Third Person mod
|
||||
- Fixed Full Heal block not dropping Full Heals
|
||||
- Fixed status cure items not returning an empty bottle when used
|
||||
- Fixed Panpour consuming its held item when evolving with a Water Stone
|
||||
- Fixed Smoked Tail Curry returning a Wooden Bowl when consumed despite it not being included in the recipe
|
||||
|
||||
### Fixes
|
||||
- Fixed Croagunk's walkspeed
|
||||
@@ -184,6 +318,7 @@
|
||||
- Fixed a compatibility issue with World Game Rules which caused some log spam in specific cases.
|
||||
- Fixed improper validation of Pokémon when performing a trade.
|
||||
- Fixed an issue where particle like beam can disappear when you don't see the beginning of the beam, or you look away
|
||||
- Fixed Bedrock particle materials (alpha/opaque/blend/add) sharing one render batch, which caused incorrect culling on mixed batches; each material now batches and draws with its own blend and cull state, and the alpha material renders as a true cutout
|
||||
- Fixed an occasional crash on retrieving revived fossils
|
||||
- Fixed brushing not working on Neoforge
|
||||
- Fixed braking not working for submarines (whoops).
|
||||
@@ -211,6 +346,19 @@
|
||||
- Fixed various log warnings caused by redundant files and sound events.
|
||||
- Traded Pokémon will now retain the original friendship level when traded back to its OT
|
||||
- Clusters now display correctly on maps
|
||||
- Fixed removing mulch from berry trees with a shovel not working.
|
||||
- Fixed collecting honey from Saccharine Leaf blocks deleting Honey Bottles when the inventory was full
|
||||
- Fixed Stripped Saccharine Wood recipe being missing
|
||||
- Fixed CancelableSoundInstance causing lag by attempting to create sounds every tick when out of range of hearing
|
||||
- Fixed Stat Reducing Aprijuice not being possible to apply on a max out Pokémon, and the possibility of going into negatives
|
||||
- Fixed Exploit where you could take out the bait or drop your Pokérod with bait to avoid consuming bait, now the bobber will just disappear if you do so
|
||||
- Fixed Apricorn and Saccharine boats incorrectly using chest boat models
|
||||
- Fixed wild and trainer battle AI refusing to select moves with the `adjacentAlly` target type, even when it's their only option
|
||||
- Fixed Guilded chests being more accessible than intended
|
||||
- Fixed fished up Pokémon not being added as "seen" to the Pokedex
|
||||
- Fixed surprised Gimmighoul encounters not being added as "seen" to the Pokedex
|
||||
- Fixed a crash involving berry trees that have been world edited.
|
||||
- Fixed visual desync in multiplayer for players mounting Pokémon outside other players' loaded chunks
|
||||
|
||||
### Developer
|
||||
- Added `PokeSnackSpawnerFactory` which allows influence registration on PokeSnack encounters
|
||||
@@ -219,6 +367,10 @@
|
||||
- Tweaked the spawning selector interface to take a bucket function rather than a bucket directly.
|
||||
- Added a new `cobblemon:height_range` processor type to allow developers to restrict structure-spawning ranges.
|
||||
- Separated all the functions of MoLangFunctions.kt into their own files.
|
||||
- Spawn buckets and their base weights are now provided by the spawner, with different base weights for world, fishing, snack, and habitat spawn buckets.
|
||||
- Removed "SpawnBucket" class in favour of just storing maps of bucket names to weights.
|
||||
- Fixed nicknames not being applied to Pokémon spawned from `spawn_pool_world` files.
|
||||
- Added support for `profilePokedexScale`, `profilePokedexTranslation`, `profileSummaryScale`, and `profileSummaryTranslation` in poser files for finer control over profile displays.
|
||||
|
||||
### Molang & Datapacks
|
||||
- Added `moveset_builders` datapack folder for different styles of movesets.
|
||||
@@ -241,8 +393,85 @@
|
||||
- Ride sounds can now be set as exclusive to passengers
|
||||
- Ride sounds no longer play when submerged unless specified with a new setting
|
||||
- NPC Trainers can now call a `command` animation when issuing a command in battle.
|
||||
- Added a q.failed function to move action effects.
|
||||
- Herd spawned Pokémon have their levels controlled differently. The `levelRange` property now acts as a clamp instead of a filter, with a new `herdLevelRange` property to do the filter behaviour.
|
||||
- `q.sound` now accepts three parameters (sound event, volume and pitch) instead of just the sound event and pitch.
|
||||
|
||||
### Particles
|
||||
|
||||
#### New Move Animations
|
||||
- Blizzard
|
||||
- Disarming Voice
|
||||
- Draco Meteor
|
||||
- Dragon Breath
|
||||
- Dragon Rage
|
||||
- Drain Punch
|
||||
- Fairy Wind
|
||||
- Fire Fang
|
||||
- Gust
|
||||
- Horn Drill
|
||||
- Hydro Pump
|
||||
- Ice Fang
|
||||
- Leafage
|
||||
- Leaf Storm
|
||||
- Light Screen
|
||||
- Night Shade
|
||||
- Play Rough
|
||||
- Pyro Ball
|
||||
- Reflect
|
||||
- Rock Slide
|
||||
- Sonic Boom
|
||||
- Sucker Punch
|
||||
- Supersonic
|
||||
- Thunder Fang
|
||||
|
||||
#### Tweaks and Fixes (Moves)
|
||||
|
||||
- Confusion
|
||||
- Dragon Claw
|
||||
- Eruption
|
||||
- Fire Punch
|
||||
- Flame Burst
|
||||
- Flame Charge
|
||||
- Flamethrower
|
||||
- Frost Breath
|
||||
- Hypnosis
|
||||
- Ice Punch
|
||||
- Icy Wind
|
||||
- Mud Sport
|
||||
- Poison Fang
|
||||
- Protect
|
||||
- Psychic
|
||||
- Quick Attack
|
||||
- Rage Powder
|
||||
- Razor Leaf
|
||||
- Sand Attack
|
||||
- Scratch
|
||||
- Shadow Claw
|
||||
- Sing
|
||||
- Spore
|
||||
- String Shot
|
||||
- Tackle
|
||||
- Tail Whip
|
||||
- Thunder
|
||||
- Thunder Punch
|
||||
- Thunder Wave
|
||||
- Water Gun
|
||||
- Water Pulse
|
||||
- Water Sport
|
||||
|
||||
#### Miscellaneous Particles
|
||||
|
||||
+ Added Wild Might particles for Alpha Pokémon
|
||||
+ Updated stat increase and decrease particles
|
||||
|
||||
### Localization
|
||||
- Updated translations for:
|
||||
- Japanese
|
||||
- German
|
||||
- Spanish
|
||||
- Ecuadorian Spanish
|
||||
- Brazilian Portuguese
|
||||
- Simplified Chinese
|
||||
- Traditional Chinese
|
||||
- Korean
|
||||
@@ -72,6 +72,10 @@ tasks.withType<Test> {
|
||||
}
|
||||
}
|
||||
|
||||
tasks.processResources {
|
||||
exclude("data/**/README.md")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
blossom {
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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.mixin.accessor.SinglePoolElementAccessor;
|
||||
import com.cobblemon.mod.common.world.structureprocessors.HeightRangeStructureProcessor;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.levelgen.Heightmap;
|
||||
import net.minecraft.world.level.levelgen.structure.Structure;
|
||||
import net.minecraft.world.level.levelgen.structure.pools.*;
|
||||
import net.minecraft.world.level.levelgen.structure.pools.alias.PoolAliasLookup;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.LiquidSettings;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessor;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorList;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Mixin(JigsawPlacement.class)
|
||||
public class JigsawPlacementMixin {
|
||||
|
||||
@Inject(
|
||||
method = "addPieces(Lnet/minecraft/world/level/levelgen/structure/Structure$GenerationContext;Lnet/minecraft/core/Holder;Ljava/util/Optional;ILnet/minecraft/core/BlockPos;ZLjava/util/Optional;ILnet/minecraft/world/level/levelgen/structure/pools/alias/PoolAliasLookup;Lnet/minecraft/world/level/levelgen/structure/pools/DimensionPadding;Lnet/minecraft/world/level/levelgen/structure/templatesystem/LiquidSettings;)Ljava/util/Optional;",
|
||||
at = @At("RETURN"),
|
||||
cancellable = true
|
||||
)
|
||||
private static void onAddPieces(
|
||||
Structure.GenerationContext context,
|
||||
Holder<StructureTemplatePool> startPool,
|
||||
Optional<ResourceLocation> startJigsawName,
|
||||
int maxDepth,
|
||||
BlockPos pos,
|
||||
boolean useExpansionHack,
|
||||
Optional<Heightmap.Types> projectStartToHeightmap,
|
||||
int maxDistanceFromCenter,
|
||||
PoolAliasLookup aliasLookup,
|
||||
DimensionPadding dimensionPadding,
|
||||
LiquidSettings liquidSettings,
|
||||
CallbackInfoReturnable<Optional<Structure.GenerationStub>> cir
|
||||
) {
|
||||
Optional<Structure.GenerationStub> result = cir.getReturnValue();
|
||||
if (result.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
BlockPos spawnPos = result.get().position();
|
||||
boolean canPlace = cobblemon$canAnyStartElementPlace(context, startPool, spawnPos);
|
||||
|
||||
if (canPlace) {
|
||||
return;
|
||||
}
|
||||
|
||||
cir.setReturnValue(Optional.empty());
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static boolean cobblemon$canAnyStartElementPlace(
|
||||
Structure.GenerationContext generationContext,
|
||||
Holder<StructureTemplatePool> startPool,
|
||||
BlockPos startPos
|
||||
) {
|
||||
for (StructurePoolElement element : startPool.value().templates) {
|
||||
Optional<HeightRangeStructureProcessor> heightRangeProcessor = cobblemon$getHeightRangeProcessor(element);
|
||||
|
||||
if (heightRangeProcessor.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
HeightRangeStructureProcessor processor = heightRangeProcessor.get();
|
||||
boolean valid = processor.isValidPlacement(
|
||||
generationContext.chunkGenerator(),
|
||||
generationContext.heightAccessor(),
|
||||
generationContext.randomState(),
|
||||
startPos
|
||||
);
|
||||
|
||||
if (valid) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static Optional<HeightRangeStructureProcessor> cobblemon$getHeightRangeProcessor(StructurePoolElement element) {
|
||||
if (!(element instanceof SinglePoolElement)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
StructureProcessorList processorList = ((SinglePoolElementAccessor) element).getProcessors().value();
|
||||
for (StructureProcessor processor : processorList.list()) {
|
||||
if (processor instanceof HeightRangeStructureProcessor heightRangeStructureProcessor) {
|
||||
return Optional.of(heightRangeStructureProcessor);
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.accessor;
|
||||
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.world.level.levelgen.structure.pools.SinglePoolElement;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorList;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(SinglePoolElement.class)
|
||||
public interface SinglePoolElementAccessor {
|
||||
@Accessor("processors")
|
||||
Holder<StructureProcessorList> getProcessors();
|
||||
}
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.duck.ChannelDuck;
|
||||
import com.mojang.blaze3d.audio.Channel;
|
||||
@@ -108,6 +108,7 @@ import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
|
||||
import com.cobblemon.mod.common.pokemon.properties.AspectPropertyType
|
||||
import com.cobblemon.mod.common.pokemon.properties.BattleCloneProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.FreezeFrameProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.HeldItemVisibleProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.HiddenAbilityPropertyType
|
||||
import com.cobblemon.mod.common.pokemon.properties.LabelProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.NoAIProperty
|
||||
@@ -308,7 +309,9 @@ object Cobblemon {
|
||||
this.config.maxPokemonLevel,
|
||||
this.config.maxPokemonFriendship,
|
||||
this.config.maxDynamaxLevel,
|
||||
this.config.unlockAllMoveDexMovesByDefault
|
||||
this.config.unlockAllMoveDexMovesByDefault,
|
||||
this.config.pokemonIntrinsicSizeMin,
|
||||
this.config.pokemonIntrinsicSizeMax
|
||||
).sendToPlayer(player)
|
||||
}
|
||||
|
||||
@@ -348,6 +351,7 @@ object Cobblemon {
|
||||
CustomPokemonProperty.register(UnaspectPropertyType)
|
||||
CustomPokemonProperty.register(FreezeFrameProperty)
|
||||
CustomPokemonProperty.register(NoAIProperty)
|
||||
CustomPokemonProperty.register(HeldItemVisibleProperty)
|
||||
|
||||
CobblemonEvents.POKEMON_PROPERTY_INITIALISED.emit(Unit)
|
||||
|
||||
|
||||
@@ -405,7 +405,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val BIG_MALASADA = regionalFoodItem("big_malasada", 64, 7, 0.3f, false)
|
||||
@JvmField
|
||||
val SMOKED_TAIL_CURRY = regionalFoodItem("smoked_tail_curry", 64, 10, 0.6f, false, ItemStack(Items.BOWL, 1))
|
||||
val SMOKED_TAIL_CURRY = regionalFoodItem("smoked_tail_curry", 64, 10, 0.6f, false)
|
||||
@JvmField
|
||||
val JUBILIFE_MUFFIN = regionalFoodItem("jubilife_muffin", 64, 7, 0.3f, false)
|
||||
@JvmField
|
||||
|
||||
@@ -35,6 +35,7 @@ import com.cobblemon.mod.common.client.net.gui.PokedexUIPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.SetActiveTMPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.SetTMMachineContainerDataPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.SummaryUIPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.SynchronizeActiveTMHandler
|
||||
import com.cobblemon.mod.common.client.net.habitat.OpenHabitatBlockEditorHandler
|
||||
import com.cobblemon.mod.common.client.net.npc.CloseNPCEditorHandler
|
||||
import com.cobblemon.mod.common.client.net.npc.OpenNPCEditorHandler
|
||||
@@ -131,6 +132,7 @@ import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.ChangeP
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.RequestPCBoxWallpapersPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.SetPCBoxWallpapersPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.UnlockPCBoxWallpaperPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.tms.TechnicalMachineRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.toast.ToastPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeAcceptanceChangedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeCancelledPacket
|
||||
@@ -146,6 +148,7 @@ import com.cobblemon.mod.common.net.messages.client.ui.PokedexUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.SetActiveTMPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.SetTMMachineContainerDataPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.SummaryUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.SynchronizeActiveTMPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.BattleChallengePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.BattleChallengeResponsePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.BenchMovePacket
|
||||
@@ -374,6 +377,7 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(PlayerInteractOptionsPacket.ID, PlayerInteractOptionsPacket::decode, PlayerInteractOptionsHandler))
|
||||
list.add(PacketRegisterInfo(PokedexUIPacket.ID, PokedexUIPacket::decode, PokedexUIPacketHandler))
|
||||
list.add(PacketRegisterInfo(ExpGainedDataPacket.ID, ExpGainedDataPacket::decode, ExpGainedDataPacketHandler))
|
||||
list.add(PacketRegisterInfo(SynchronizeActiveTMPacket.ID, SynchronizeActiveTMPacket::decode, SynchronizeActiveTMHandler))
|
||||
|
||||
// Starter packets
|
||||
list.add(PacketRegisterInfo(OpenStarterUIPacket.ID, OpenStarterUIPacket::decode, StarterUIPacketHandler))
|
||||
@@ -439,6 +443,7 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(BehaviourSyncPacket.ID, BehaviourSyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(MarkRegistrySyncPacket.ID, MarkRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(RideSettingsSyncPacket.ID, RideSettingsSyncPacket::decode, RideSettingsSyncHandler))
|
||||
list.add(PacketRegisterInfo(TechnicalMachineRegistrySyncPacket.ID, TechnicalMachineRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
|
||||
// Effects
|
||||
list.add(PacketRegisterInfo(SpawnSnowstormParticlePacket.ID, SpawnSnowstormParticlePacket::decode, SpawnSnowstormParticleHandler))
|
||||
|
||||
@@ -108,6 +108,9 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
@JvmField
|
||||
val LEVELUP = this.create("gui.levelup")
|
||||
|
||||
@JvmField
|
||||
val MOVE_LEARN = this.create("gui.move_learn")
|
||||
|
||||
@JvmField
|
||||
val PVN_BATTLE = this.create("battle.pvn.default")
|
||||
@JvmField
|
||||
@@ -212,17 +215,23 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
@JvmField
|
||||
val TM_USE = this.create("item.tm.use")
|
||||
@JvmField
|
||||
val TM_MACHINE_BURN_LOOP = this.create("block.tm_machine.burn_loop")
|
||||
@JvmField
|
||||
val TM_MACHINE_CRAFT = this.create("block.tm_machine.craft")
|
||||
@JvmField
|
||||
val TM_MACHINE_OFF = this.create("block.tm_machine.off")
|
||||
@JvmField
|
||||
val TM_MACHINE_ON = this.create("block.tm_machine.on")
|
||||
val TM_MACHINE_START = this.create("block.tm_machine.start")
|
||||
@JvmField
|
||||
val TM_MACHINE_OPEN = this.create("block.tm_machine.open")
|
||||
@JvmField
|
||||
val TM_MACHINE_CLOSE = this.create("block.tm_machine.close")
|
||||
@JvmField
|
||||
val TM_MACHINE_PLACE_DISC = this.create("block.tm_machine.place_disc")
|
||||
val TM_MACHINE_TM_INSERT = this.create("block.tm_machine.tm_insert")
|
||||
@JvmField
|
||||
val TM_MACHINE_TM_RETRIEVE = this.create("block.tm_machine.tm_retrieve")
|
||||
@JvmField
|
||||
val TM_MACHINE_ITEM_INSERT = this.create("block.tm_machine.item_insert")
|
||||
@JvmField
|
||||
val TM_MACHINE_ITEM_RETRIEVE = this.create("block.tm_machine.item_retrieve")
|
||||
|
||||
|
||||
@JvmField
|
||||
@@ -263,6 +272,9 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
@JvmField
|
||||
val BERRY_EAT_FULL = this.create("item.berry.eat.full")
|
||||
|
||||
@JvmField
|
||||
val APRICORN_HARVEST = this.create("block.apricorn.harvest")
|
||||
|
||||
@JvmField
|
||||
val BIG_ROOT_BREAK = this.create("block.big_root.break")
|
||||
@JvmField
|
||||
@@ -410,9 +422,9 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_BREAK = this.create("block.type_gem_cluster.break")
|
||||
val TYPE_GEM_BLOCK_BREAK = this.create("block.type_gem_block.break")
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_BREAK = this.create("block.type_gem_block.break")
|
||||
val TYPE_GEM_CLUSTER_BREAK = this.create("block.type_gem_cluster.break")
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_HIT = this.create("block.type_gem_block.hit")
|
||||
@JvmField
|
||||
@@ -421,7 +433,7 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
val TYPE_GEM_BLOCK_STEP = this.create("block.type_gem_block.step")
|
||||
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_SOUNDS = SoundType(1f, 1.2f,
|
||||
val TYPE_GEM_CLUSTER_SOUNDS = SoundType(1f, 1.1f,
|
||||
TYPE_GEM_CLUSTER_BREAK,
|
||||
TYPE_GEM_BLOCK_STEP,
|
||||
TYPE_GEM_BLOCK_PLACE,
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ class RidingStatBoostCriterion(
|
||||
val max = context.pokemon.pokemon.getMaxRideBoost(rideStat).toDouble()
|
||||
val min = context.pokemon.pokemon.getBaseRideStat(rideStat).toDouble()
|
||||
val value = context.pokemon.getRideStat(rideStat, ridingStyle, min, max)
|
||||
val isSufficient = (value >= this.statValue && this.statValue > 0.0) || !context.pokemon.pokemon.canAddRideBoost(rideStat)
|
||||
val isSufficient = (value >= this.statValue && this.statValue > 0.0) || !context.pokemon.pokemon.canApplyRideBoost(rideStat)
|
||||
if (isSufficient && this.rideStat == "any") {
|
||||
return true
|
||||
} else if (!isSufficient && this.rideStat == "all") {
|
||||
|
||||
@@ -1,33 +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.data
|
||||
|
||||
import com.cobblemon.mod.common.api.net.Decodable
|
||||
import com.cobblemon.mod.common.api.net.Encodable
|
||||
|
||||
// TODO this is implemented in multiple places but unused. Can we scratch this?
|
||||
/**
|
||||
* Represents an object that will possibly be synchronized to the client during datapack synchronization.
|
||||
*
|
||||
* @param T The type of the object being synchronized.
|
||||
*
|
||||
* @author Licious
|
||||
* @since October 14th, 2022
|
||||
*/
|
||||
interface ClientDataSynchronizer<T> : Decodable, Encodable {
|
||||
|
||||
/**
|
||||
* Compares an object of type [T] with this instance to see if synchronization is necessary.
|
||||
*
|
||||
* @param other The other instance of type [T].
|
||||
* @return If synchronization is necessary.
|
||||
*/
|
||||
fun shouldSynchronize(other: T): Boolean
|
||||
|
||||
}
|
||||
+2
-3
@@ -8,7 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.entity
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnCause
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
|
||||
@@ -21,7 +20,7 @@ class SpawnBucketChosenEvent(
|
||||
/** The cause of the spawn. */
|
||||
val spawnCause: SpawnCause,
|
||||
/** The bucket that was chosen. You can alter this. */
|
||||
var bucket: SpawnBucket,
|
||||
var bucket: String,
|
||||
/** The final weights that were used to make the choice. */
|
||||
val bucketWeights: Map<SpawnBucket, Float>
|
||||
val bucketWeights: Map<String, Float>
|
||||
)
|
||||
+3
-4
@@ -9,7 +9,6 @@
|
||||
package com.cobblemon.mod.common.api.events.fishing
|
||||
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.entity.fishing.PokeRodFishingBobberEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import net.minecraft.world.item.ItemStack
|
||||
@@ -27,7 +26,7 @@ interface BaitSpawnPokemonEvent {
|
||||
*/
|
||||
data class Pre(
|
||||
val bobber: PokeRodFishingBobberEntity, // todo replace this with the bait entity
|
||||
val chosenBucket: SpawnBucket,
|
||||
val chosenBucket: String,
|
||||
val rod: ItemStack
|
||||
) : Cancelable(), BobberSpawnPokemonEvent
|
||||
|
||||
@@ -38,7 +37,7 @@ interface BaitSpawnPokemonEvent {
|
||||
* @param pokemon The Pokemon that is modified.
|
||||
*/
|
||||
data class Modify(
|
||||
val chosenBucket: SpawnBucket,
|
||||
val chosenBucket: String,
|
||||
val rod: ItemStack,
|
||||
val pokemon: PokemonEntity
|
||||
) : BobberSpawnPokemonEvent
|
||||
@@ -52,7 +51,7 @@ interface BaitSpawnPokemonEvent {
|
||||
*/
|
||||
data class Post(
|
||||
val bobber: PokeRodFishingBobberEntity,
|
||||
val chosenBucket: SpawnBucket,
|
||||
val chosenBucket: String,
|
||||
val bait: ItemStack,
|
||||
val pokemon: PokemonEntity
|
||||
) : BobberSpawnPokemonEvent
|
||||
|
||||
+1
-3
@@ -8,12 +8,10 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.fishing
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
|
||||
/**
|
||||
* Event that is fired when a bucket is chosen by the bobber.
|
||||
* @param chosenBucket The bucket that is chosen.
|
||||
* @param buckets The list of buckets that the bobber can choose from.
|
||||
* @param luckOfTheSeaLevel The level of the Luck of the Sea enchantment on the rod.
|
||||
*/
|
||||
class BobberBucketChosenEvent(var chosenBucket: SpawnBucket, val buckets: MutableList<SpawnBucket>, val luckOfTheSeaLevel: Int)
|
||||
class BobberBucketChosenEvent(var chosenBucket: String, val buckets: MutableList<String>, val luckOfTheSeaLevel: Int)
|
||||
+2
-5
@@ -12,10 +12,7 @@ import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
|
||||
import com.cobblemon.mod.common.entity.fishing.PokeRodFishingBobberEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
@@ -73,8 +70,8 @@ interface BobberSpawnPokemonEvent {
|
||||
val bait: ItemStack,
|
||||
val pokemon: PokemonEntity
|
||||
) : BobberSpawnPokemonEvent {
|
||||
val context = mutableMapOf<String, MoValue>(
|
||||
"chosen_bucket" to ObjectValue(spawnAction.bucket, { it.name }),
|
||||
val context = mutableMapOf(
|
||||
"chosen_bucket" to StringValue(spawnAction.bucket),
|
||||
"bait" to pokemon.level().itemRegistry.wrapAsHolder(bait.item).asMoLangValue(Registries.ITEM),
|
||||
"pokemon_entity" to pokemon.asMoLangValue()
|
||||
)
|
||||
|
||||
@@ -1,37 +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.habitats
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* A [HabitatPool] of [ActivatedHabitatSpawn]s. Mainly just to help with deserialization and ensuring
|
||||
* a single type for everything that's applied to a habitat block.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 13th, 2026
|
||||
*/
|
||||
class ActivatedHabitatPool : HabitatPool<ActivatedHabitatSpawn>() {
|
||||
override val type: ResourceLocation = ActivatedHabitatSpawn.TYPE
|
||||
override var spawns: List<ActivatedHabitatSpawn> = listOf()
|
||||
|
||||
companion object {
|
||||
fun default() = ActivatedHabitatPool().apply {
|
||||
id = ResourceLocation("cobblemon", "custom__default_activated_pool")
|
||||
name = "Custom"
|
||||
}
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf, buckets: List<SpawnBucket>) {
|
||||
super.decode(buffer, buckets)
|
||||
spawns = buffer.readList { ActivatedHabitatSpawn().apply { decode(buffer, buckets) } }
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.habitats
|
||||
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
|
||||
/**
|
||||
* A basic [HabitatSpawn] that will be triggered via activated spawning in a habitat. Nothing to it, really.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 13th, 2026
|
||||
*/
|
||||
class ActivatedHabitatSpawn : HabitatSpawn() {
|
||||
companion object {
|
||||
val TYPE = cobblemonResource("activated")
|
||||
}
|
||||
}
|
||||
@@ -8,39 +8,81 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.habitats
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.block.habitat.HabitatBlockEntity
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.ListTag
|
||||
import net.minecraft.nbt.Tag
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* A pool of [HabitatSpawn]s. This is either filled with [NaturalHabitatSpawn]s or [ActivatedHabitatSpawn]s depending
|
||||
* on whether the pool is a [NaturalHabitatPool] or an [ActivatedHabitatPool]. The pool is responsible for converting
|
||||
* A pool of [HabitatSpawn]s. This is filled with [HabitatSpawn]s. The pool is responsible for converting
|
||||
* its spawns into [PokemonSpawnDetail]s.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 13th, 2026
|
||||
*/
|
||||
abstract class HabitatPool<T : HabitatSpawn> {
|
||||
class HabitatPool {
|
||||
companion object {
|
||||
val types = mutableMapOf(
|
||||
ActivatedHabitatSpawn.TYPE to ActivatedHabitatPool::class.java,
|
||||
NaturalHabitatSpawn.TYPE to NaturalHabitatPool::class.java
|
||||
)
|
||||
fun default() = HabitatPool().apply {
|
||||
id = ResourceLocation("cobblemon", "custom__default_pool")
|
||||
name = "Custom"
|
||||
}
|
||||
|
||||
fun writePoolToNBT(pool: HabitatPool, nbt: CompoundTag) {
|
||||
nbt.putString(DataKeys.HABITAT_POOL_ID, pool.id.toString())
|
||||
if (pool.id.path.startsWith("custom__")) {
|
||||
nbt.putString(DataKeys.HABITAT_POOL_NAME, pool.name)
|
||||
val spawnsListNBT = ListTag()
|
||||
for (spawn in pool.spawns) {
|
||||
val spawnTag = CompoundTag()
|
||||
spawn.writeToNBT(spawnTag)
|
||||
spawnsListNBT.add(spawnTag)
|
||||
}
|
||||
nbt.put(DataKeys.HABITAT_POOL_SPAWNS, spawnsListNBT)
|
||||
}
|
||||
}
|
||||
|
||||
fun readPoolFromNBT(nbt: CompoundTag): HabitatPool {
|
||||
val poolId = nbt.getString(DataKeys.HABITAT_POOL_ID).asIdentifierDefaultingNamespace()
|
||||
if (nbt.contains(DataKeys.HABITAT_POOL_NAME)) {
|
||||
// It's one that was stored on the block
|
||||
val pool = HabitatPool()
|
||||
pool.id = poolId
|
||||
pool.name = nbt.getString(DataKeys.HABITAT_POOL_NAME)
|
||||
val spawnsListNBT = nbt.getList(DataKeys.HABITAT_POOL_SPAWNS, Tag.TAG_COMPOUND.toInt())
|
||||
val spawns = mutableListOf<HabitatSpawn>()
|
||||
spawnsListNBT.forEach { spawnTag ->
|
||||
try {
|
||||
val spawn = HabitatSpawn().apply { readFromNBT(spawnTag as CompoundTag) }
|
||||
spawns.add(spawn)
|
||||
} catch (_: Exception) {
|
||||
// If there's an error reading a spawn, skip it and log the error.
|
||||
Cobblemon.LOGGER.error("Error reading activated habitat spawn from NBT for habitat block, skipping.")
|
||||
}
|
||||
}
|
||||
pool.spawns = spawns
|
||||
return pool
|
||||
} else {
|
||||
return HabitatPools.habitatPoolsById[poolId] ?: default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lateinit var id: ResourceLocation
|
||||
var name: String = ""
|
||||
abstract val type: ResourceLocation
|
||||
abstract var spawns: List<T>
|
||||
var spawns: List<HabitatSpawn> = listOf()
|
||||
|
||||
/** Whether a pool is editable is just down to whether it's not from a datapack. */
|
||||
@Transient
|
||||
var editable: Boolean = false
|
||||
|
||||
open fun validate() {
|
||||
fun validate() {
|
||||
for (spawn in spawns) {
|
||||
if (SpawnablePosition.getByName(spawn.spawnablePositionType) == null) {
|
||||
throw IllegalArgumentException("$id mentions non-existent spawnable position type ${spawn.spawnablePositionType} on spawn for ${spawn.species}")
|
||||
@@ -52,14 +94,23 @@ abstract class HabitatPool<T : HabitatSpawn> {
|
||||
return spawns.mapNotNull { it.createSpawnDetail(habitatBlockEntity) }
|
||||
}
|
||||
|
||||
fun getPhaseCount(): Int {
|
||||
return spawns
|
||||
.flatMap { it.phases?.ranges?.flatten() ?: emptySet() }
|
||||
.toSet()
|
||||
.count()
|
||||
.coerceAtLeast(1)
|
||||
}
|
||||
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(id)
|
||||
buffer.writeUtf(name)
|
||||
buffer.writeCollection(spawns) { _, it -> it.encode(buffer) }
|
||||
}
|
||||
|
||||
open fun decode(buffer: RegistryFriendlyByteBuf, buckets: List<SpawnBucket>) {
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
id = buffer.readResourceLocation()
|
||||
name = buffer.readUtf()
|
||||
spawns = buffer.readList { HabitatSpawn().apply { decode(buffer) } }
|
||||
}
|
||||
}
|
||||
@@ -12,16 +12,13 @@ import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.spawning.IntRanges
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.TimeRange
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePositionType
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
import com.cobblemon.mod.common.util.adapters.HabitatPoolAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IntRangeAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IntRangesAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.RegisteredSpawnablePositionAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.SpawnBucketAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.SpeciesAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.pokemonPropertiesShortAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
@@ -36,18 +33,16 @@ import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
import net.minecraft.world.level.storage.LevelResource
|
||||
|
||||
object HabitatPools : JsonDataRegistry<HabitatPool<*>> {
|
||||
object HabitatPools : JsonDataRegistry<HabitatPool> {
|
||||
override val id = cobblemonResource("habitat_pools")
|
||||
override val type = PackType.SERVER_DATA
|
||||
override val observable = SimpleObservable<HabitatPools>()
|
||||
override val typeToken: TypeToken<HabitatPool<*>> = TypeToken.get(HabitatPool::class.java)
|
||||
override val typeToken: TypeToken<HabitatPool> = TypeToken.get(HabitatPool::class.java)
|
||||
override val resourcePath = "habitat_pools"
|
||||
|
||||
override val gson: Gson = GsonBuilder()
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.registerTypeAdapter(HabitatPool::class.java, HabitatPoolAdapter)
|
||||
.registerTypeAdapter(SpawnablePositionType::class.java, RegisteredSpawnablePositionAdapter)
|
||||
.registerTypeAdapter(SpawnBucket::class.java, SpawnBucketAdapter)
|
||||
.registerTypeAdapter(Species::class.java, SpeciesAdapter)
|
||||
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
|
||||
.registerTypeAdapter(TimeRange::class.java, TimeRange.adapter)
|
||||
@@ -56,10 +51,10 @@ object HabitatPools : JsonDataRegistry<HabitatPool<*>> {
|
||||
.setPrettyPrinting()
|
||||
.create()
|
||||
|
||||
private val datapacked = mutableMapOf<ResourceLocation, HabitatPool<*>>()
|
||||
private val worldSaved = mutableMapOf<ResourceLocation, HabitatPool<*>>()
|
||||
private val datapacked = mutableMapOf<ResourceLocation, HabitatPool>()
|
||||
private val worldSaved = mutableMapOf<ResourceLocation, HabitatPool>()
|
||||
|
||||
val habitatPoolsById = mutableMapOf<ResourceLocation, HabitatPool<*>>()
|
||||
val habitatPoolsById = mutableMapOf<ResourceLocation, HabitatPool>()
|
||||
|
||||
private lateinit var worldSavedPoolsFolder: File
|
||||
|
||||
@@ -73,7 +68,7 @@ object HabitatPools : JsonDataRegistry<HabitatPool<*>> {
|
||||
|
||||
worldSavedPoolsFolder.listFiles { it.endsWith(".json") }.forEach {
|
||||
val identifier = ResourceLocation.parse(it.nameWithoutExtension)
|
||||
val habitat = gson.fromJson<HabitatPool<*>>(it.reader())
|
||||
val habitat = gson.fromJson<HabitatPool>(it.reader())
|
||||
habitat.id = identifier
|
||||
habitat.validate()
|
||||
habitat.editable = true
|
||||
@@ -82,7 +77,7 @@ object HabitatPools : JsonDataRegistry<HabitatPool<*>> {
|
||||
setupHabitatPoolsById()
|
||||
}
|
||||
|
||||
override fun reload(data: Map<ResourceLocation, HabitatPool<*>>) {
|
||||
override fun reload(data: Map<ResourceLocation, HabitatPool>) {
|
||||
datapacked.clear()
|
||||
datapacked.putAll(data)
|
||||
data.entries.forEach { (identifier, habitat) ->
|
||||
|
||||
@@ -10,9 +10,7 @@ package com.cobblemon.mod.common.api.habitats
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.spawning.BestSpawner
|
||||
import com.cobblemon.mod.common.api.spawning.IntRanges
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.TimeRange
|
||||
import com.cobblemon.mod.common.api.spawning.condition.AppendageCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
|
||||
@@ -30,9 +28,7 @@ import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
/**
|
||||
* A kind of simplified spawn definition for the purposes of spawning from [HabitatBlockEntity] instances. This is a
|
||||
* very shallow class tree, with [ActivatedHabitatSpawn] being a trivial subclass but the other subclass being
|
||||
* [NaturalHabitatSpawn] which introduces the need to supply a spawn bucket.
|
||||
* A kind of simplified spawn definition for the purposes of spawning from [HabitatBlockEntity] instances.
|
||||
*
|
||||
* These have only a small subset of the properties that a regular [PokemonSpawnDetail] would have to align with the
|
||||
* fact that these are configured from a simple GUI. There are limits to how many properties and conditions could be
|
||||
@@ -46,11 +42,13 @@ import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
* @author Hiroku
|
||||
* @since February 13th, 2026
|
||||
*/
|
||||
sealed class HabitatSpawn {
|
||||
class HabitatSpawn {
|
||||
/** The species that will be spawned. */
|
||||
lateinit var species: Species
|
||||
/** The kind of position the Pokémon will spawn in. Things like "grounded", "seafloor", "fishing". */
|
||||
lateinit var spawnablePositionType: String
|
||||
/** The bucket to spawn from. */
|
||||
var bucket: String = "common"
|
||||
|
||||
/** Selection weight. */
|
||||
var weight: Float = 1F
|
||||
@@ -77,6 +75,7 @@ sealed class HabitatSpawn {
|
||||
timeRange?.let { nbt.putString(DataKeys.HABITAT_POOL_SPAWN_TIMES, TimeRange.adapter.serialize(it)) }
|
||||
minLight?.let { nbt.putInt(DataKeys.HABITAT_POOL_SPAWN_MIN_LIGHT, it) }
|
||||
maxLight?.let { nbt.putInt(DataKeys.HABITAT_POOL_SPAWN_MAX_LIGHT, it) }
|
||||
nbt.putString(DataKeys.HABITAT_POOL_SPAWN_BUCKET, bucket)
|
||||
}
|
||||
|
||||
open fun readFromNBT(nbt: CompoundTag) {
|
||||
@@ -89,6 +88,7 @@ sealed class HabitatSpawn {
|
||||
timeRange = if (nbt.contains(DataKeys.HABITAT_POOL_SPAWN_TIMES)) TimeRange.adapter.deserialize(nbt.getString(DataKeys.HABITAT_POOL_SPAWN_TIMES)) else null
|
||||
minLight = if (nbt.contains(DataKeys.HABITAT_POOL_SPAWN_MIN_LIGHT)) nbt.getInt(DataKeys.HABITAT_POOL_SPAWN_MIN_LIGHT) else null
|
||||
maxLight = if (nbt.contains(DataKeys.HABITAT_POOL_SPAWN_MAX_LIGHT)) nbt.getInt(DataKeys.HABITAT_POOL_SPAWN_MAX_LIGHT) else null
|
||||
bucket = if (nbt.contains(DataKeys.HABITAT_POOL_SPAWN_BUCKET)) nbt.getString(DataKeys.HABITAT_POOL_SPAWN_BUCKET) else "common"
|
||||
}
|
||||
|
||||
open fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
@@ -101,12 +101,10 @@ sealed class HabitatSpawn {
|
||||
buffer.writeNullable(timeRange) { _, value -> buffer.writeUtf(TimeRange.adapter.serialize(value)) }
|
||||
buffer.writeNullable(minLight) { _, value -> buffer.writeInt(value) }
|
||||
buffer.writeNullable(maxLight) { _, value -> buffer.writeInt(value) }
|
||||
buffer.writeUtf(bucket)
|
||||
}
|
||||
|
||||
open fun decode(
|
||||
buffer: RegistryFriendlyByteBuf,
|
||||
buckets: List<SpawnBucket>
|
||||
) {
|
||||
open fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
species = PokemonSpecies.getByIdentifier(buffer.readResourceLocation())!!
|
||||
spawnablePositionType = buffer.readUtf()
|
||||
weight = buffer.readFloat()
|
||||
@@ -116,6 +114,7 @@ sealed class HabitatSpawn {
|
||||
timeRange = buffer.readNullable { _ -> TimeRange.adapter.deserialize(buffer.readUtf()) }
|
||||
minLight = buffer.readNullable { _ -> buffer.readInt() }
|
||||
maxLight = buffer.readNullable { _ -> buffer.readInt() }
|
||||
bucket = buffer.readUtf()
|
||||
}
|
||||
|
||||
/** Creates the [PokemonSpawnDetail] that will be used in the spawner. This is done per habitat block. */
|
||||
@@ -138,7 +137,7 @@ sealed class HabitatSpawn {
|
||||
spawnDetail.apply {
|
||||
this.id = "habitat_${species.resourceIdentifier}"
|
||||
this.pokemon = properties
|
||||
this.bucket = BestSpawner.config.buckets.first { it.weight > 0 }
|
||||
this.bucket = this@HabitatSpawn.bucket
|
||||
this.weight = this@HabitatSpawn.weight
|
||||
this.levelRange = levelRange
|
||||
this.conditions = mutableListOf(condition)
|
||||
|
||||
@@ -1,37 +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.habitats
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* A [HabitatPool] of [NaturalHabitatSpawn]s. Mainly just to help with deserialization and ensuring
|
||||
* a single type for everything that's applied to a habitat block.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 13th, 2026
|
||||
*/
|
||||
class NaturalHabitatPool : HabitatPool<NaturalHabitatSpawn>() {
|
||||
override val type: ResourceLocation = NaturalHabitatSpawn.TYPE
|
||||
override var spawns: List<NaturalHabitatSpawn> = listOf()
|
||||
|
||||
companion object {
|
||||
fun default() = NaturalHabitatPool().apply {
|
||||
id = ResourceLocation("cobblemon", "custom__default_natural_pool")
|
||||
name = "Custom"
|
||||
}
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf, buckets: List<SpawnBucket>) {
|
||||
super.decode(buffer, buckets)
|
||||
spawns = buffer.readList { NaturalHabitatSpawn().apply { decode(buffer, buckets) } }
|
||||
}
|
||||
}
|
||||
@@ -1,63 +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.habitats
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
|
||||
import com.cobblemon.mod.common.block.habitat.HabitatBlockEntity
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
/**
|
||||
* A type of [HabitatSpawn] that spawns Pokémon as part of natural world spawning, and therefore
|
||||
* needs to indicate the [SpawnBucket] it will be part of.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 13th, 2026
|
||||
*/
|
||||
class NaturalHabitatSpawn : HabitatSpawn() {
|
||||
companion object {
|
||||
val TYPE = cobblemonResource("natural")
|
||||
}
|
||||
|
||||
lateinit var bucket: SpawnBucket
|
||||
|
||||
override fun createSpawnDetail(habitatBlockEntity: HabitatBlockEntity): PokemonSpawnDetail? {
|
||||
val spawnDetail = super.createSpawnDetail(habitatBlockEntity)
|
||||
spawnDetail?.bucket = bucket
|
||||
return spawnDetail
|
||||
}
|
||||
|
||||
override fun writeToNBT(nbt: CompoundTag) {
|
||||
super.writeToNBT(nbt)
|
||||
nbt.putString(DataKeys.HABITAT_POOL_SPAWN_BUCKET, bucket.name)
|
||||
}
|
||||
|
||||
override fun readFromNBT(nbt: CompoundTag) {
|
||||
super.readFromNBT(nbt)
|
||||
bucket = Cobblemon.bestSpawner.config.buckets.first { it.name == nbt.getString(DataKeys.HABITAT_POOL_SPAWN_BUCKET) }
|
||||
}
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
buffer.writeUtf(bucket.name)
|
||||
}
|
||||
|
||||
override fun decode(
|
||||
buffer: RegistryFriendlyByteBuf,
|
||||
buckets: List<SpawnBucket>
|
||||
) {
|
||||
super.decode(buffer, buckets)
|
||||
val bucketName = buffer.readUtf()
|
||||
bucket = buckets.first { it.name == bucketName }
|
||||
}
|
||||
}
|
||||
+1
-11
@@ -8,9 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.habitats.dto
|
||||
|
||||
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.spawningstyle.ActivatedHabitatSpawning
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
class ActivatedHabitatSettingsDTO() {
|
||||
@@ -20,7 +18,6 @@ class ActivatedHabitatSettingsDTO() {
|
||||
var spawnRange = 16
|
||||
var maxSpawns = -1
|
||||
var maxSpawnsPerActivation = 1
|
||||
var habitatPool: HabitatPoolSettingsDTO = HabitatPoolSettingsDTO(ActivatedHabitatPool.default())
|
||||
|
||||
constructor(activatedHabitatSpawning: ActivatedHabitatSpawning): this() {
|
||||
this.chance = activatedHabitatSpawning.chance
|
||||
@@ -29,7 +26,6 @@ class ActivatedHabitatSettingsDTO() {
|
||||
this.spawnRange = activatedHabitatSpawning.spawnRange
|
||||
this.maxSpawns = activatedHabitatSpawning.maxSpawns
|
||||
this.maxSpawnsPerActivation = activatedHabitatSpawning.maxSpawnsPerActivation
|
||||
this.habitatPool = HabitatPoolSettingsDTO(activatedHabitatSpawning.pool)
|
||||
}
|
||||
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
@@ -39,20 +35,14 @@ class ActivatedHabitatSettingsDTO() {
|
||||
buffer.writeInt(spawnRange)
|
||||
buffer.writeInt(maxSpawns)
|
||||
buffer.writeInt(maxSpawnsPerActivation)
|
||||
habitatPool.encode(buffer)
|
||||
}
|
||||
|
||||
fun decode(
|
||||
buffer: RegistryFriendlyByteBuf,
|
||||
buckets: List<SpawnBucket>
|
||||
) {
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
this.chance = buffer.readFloat()
|
||||
this.trigger = buffer.readEnum(ActivatedHabitatSpawning.Trigger::class.java)
|
||||
this.cancelledNaturalSpawningRange = buffer.readInt()
|
||||
this.spawnRange = buffer.readInt()
|
||||
this.maxSpawns = buffer.readInt()
|
||||
this.maxSpawnsPerActivation = buffer.readInt()
|
||||
this.habitatPool = HabitatPoolSettingsDTO()
|
||||
habitatPool.decode(buffer, buckets)
|
||||
}
|
||||
}
|
||||
+10
-36
@@ -9,30 +9,23 @@
|
||||
package com.cobblemon.mod.common.api.habitats.dto
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatSpawn
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatPools
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatSpawn
|
||||
import com.cobblemon.mod.common.api.habitats.NaturalHabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.NaturalHabitatSpawn
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
class HabitatPoolSettingsDTO() {
|
||||
lateinit var id: ResourceLocation
|
||||
var isReference = false
|
||||
var isActivated = false
|
||||
|
||||
// Below properties are only there when it's not a reference, otherwise the handler will have to look them up from the id
|
||||
var name: String? = null
|
||||
var spawns: MutableList<HabitatSpawn>? = null
|
||||
|
||||
constructor(habitatPool: HabitatPool<*>): this() {
|
||||
constructor(habitatPool: HabitatPool): this() {
|
||||
this.id = habitatPool.id
|
||||
this.isReference = !id.path.startsWith("custom__")
|
||||
this.isActivated = habitatPool is ActivatedHabitatPool
|
||||
if (!this.isReference) {
|
||||
this.name = habitatPool.name
|
||||
this.spawns = habitatPool.spawns.toMutableList()
|
||||
@@ -42,7 +35,6 @@ class HabitatPoolSettingsDTO() {
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(id)
|
||||
buffer.writeBoolean(isReference)
|
||||
buffer.writeBoolean(isActivated)
|
||||
if (!isReference) {
|
||||
buffer.writeUtf(name!!)
|
||||
buffer.writeCollection(spawns!!) { _, spawn ->
|
||||
@@ -52,50 +44,32 @@ class HabitatPoolSettingsDTO() {
|
||||
}
|
||||
|
||||
fun decode(
|
||||
buffer: RegistryFriendlyByteBuf,
|
||||
buckets: List<SpawnBucket>
|
||||
buffer: RegistryFriendlyByteBuf
|
||||
) {
|
||||
this.id = buffer.readResourceLocation()
|
||||
this.isReference = buffer.readBoolean()
|
||||
this.isActivated = buffer.readBoolean()
|
||||
if (!isReference) {
|
||||
this.name = buffer.readUtf()
|
||||
this.spawns = buffer.readList {
|
||||
val spawn = if (isActivated) {
|
||||
ActivatedHabitatSpawn()
|
||||
} else {
|
||||
NaturalHabitatSpawn()
|
||||
}
|
||||
spawn.decode(buffer, buckets)
|
||||
val spawn = HabitatSpawn()
|
||||
spawn.decode(buffer)
|
||||
spawn
|
||||
}.toMutableList()
|
||||
}
|
||||
}
|
||||
|
||||
fun toHabitatPool(): HabitatPool<*>? {
|
||||
fun toHabitatPool(): HabitatPool? {
|
||||
if (isReference) {
|
||||
val pool = HabitatPools.habitatPoolsById[id]
|
||||
if (pool == null) {
|
||||
Cobblemon.LOGGER.error("Could not find habitat pool with id $id")
|
||||
} else if (pool is ActivatedHabitatPool && !isActivated) {
|
||||
Cobblemon.LOGGER.error("Pool with id $id is an activated habitat pool but was deserialized as a natural pool")
|
||||
} else if (pool !is ActivatedHabitatPool && isActivated) {
|
||||
Cobblemon.LOGGER.error("Pool with id $id is a natural habitat pool but was deserialized as an activated pool")
|
||||
}
|
||||
return pool
|
||||
}
|
||||
return if (isActivated) {
|
||||
val pool = ActivatedHabitatPool()
|
||||
pool.id = id
|
||||
pool.name = name!!
|
||||
pool.spawns = spawns!!.map { it as ActivatedHabitatSpawn }
|
||||
pool
|
||||
} else {
|
||||
val pool = NaturalHabitatPool()
|
||||
pool.id = id
|
||||
pool.name = name!!
|
||||
pool.spawns = spawns!!.map { it as NaturalHabitatSpawn }
|
||||
pool
|
||||
}
|
||||
val pool = HabitatPool()
|
||||
pool.id = id
|
||||
pool.name = name!!
|
||||
pool.spawns = spawns!!
|
||||
return pool
|
||||
}
|
||||
}
|
||||
+9
-11
@@ -9,29 +9,29 @@
|
||||
package com.cobblemon.mod.common.api.habitats.dto
|
||||
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatPhaseOrder
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.spawningstyle.ActivatedHabitatSpawning
|
||||
import com.cobblemon.mod.common.api.habitats.spawningstyle.NaturalHabitatSpawning
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.block.habitat.HabitatBlockEntity
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
class HabitatSettingsDTO() {
|
||||
var numberOfPhases = 1
|
||||
var phaseOrder = HabitatPhaseOrder.SIMPLE
|
||||
lateinit var levelRange: IntRange
|
||||
var modifiers: String = ""
|
||||
lateinit var mimicId: ResourceLocation
|
||||
var isActivatedSpawning = false
|
||||
var pool: HabitatPoolSettingsDTO = HabitatPoolSettingsDTO(HabitatPool.default())
|
||||
var activatedSettings: ActivatedHabitatSettingsDTO? = null
|
||||
var naturalSettings: NaturalHabitatSettingsDTO? = null
|
||||
|
||||
constructor(habitatBlockEntity: HabitatBlockEntity) : this() {
|
||||
this.numberOfPhases = habitatBlockEntity.numberOfPhases
|
||||
this.phaseOrder = habitatBlockEntity.phaseOrder
|
||||
this.levelRange = habitatBlockEntity.levelRange
|
||||
this.modifiers = habitatBlockEntity.modifiers.asString()
|
||||
this.mimicId = habitatBlockEntity.mimicId
|
||||
this.pool = HabitatPoolSettingsDTO(habitatBlockEntity.pool)
|
||||
val style = habitatBlockEntity.spawningStyle
|
||||
if (style is ActivatedHabitatSpawning) {
|
||||
this.isActivatedSpawning = true
|
||||
@@ -42,12 +42,12 @@ class HabitatSettingsDTO() {
|
||||
}
|
||||
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeInt(numberOfPhases)
|
||||
buffer.writeEnum(phaseOrder)
|
||||
buffer.writeInt(levelRange.first)
|
||||
buffer.writeInt(levelRange.last)
|
||||
buffer.writeUtf(modifiers)
|
||||
buffer.writeResourceLocation(mimicId)
|
||||
pool.encode(buffer)
|
||||
buffer.writeBoolean(isActivatedSpawning)
|
||||
if (isActivatedSpawning) {
|
||||
activatedSettings?.encode(buffer)
|
||||
@@ -56,27 +56,25 @@ class HabitatSettingsDTO() {
|
||||
}
|
||||
}
|
||||
|
||||
fun decode(
|
||||
buffer: RegistryFriendlyByteBuf,
|
||||
buckets: List<SpawnBucket>
|
||||
) {
|
||||
this.numberOfPhases = buffer.readInt()
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
this.phaseOrder = buffer.readEnum(HabitatPhaseOrder::class.java)
|
||||
val levelRangeStart = buffer.readInt()
|
||||
val levelRangeEnd = buffer.readInt()
|
||||
this.levelRange = levelRangeStart..levelRangeEnd
|
||||
this.modifiers = buffer.readUtf()
|
||||
this.mimicId = buffer.readResourceLocation()
|
||||
this.pool = HabitatPoolSettingsDTO()
|
||||
this.pool.decode(buffer)
|
||||
if (buffer.readBoolean()) {
|
||||
this.isActivatedSpawning = true
|
||||
this.naturalSettings = null
|
||||
val activatedSettings = ActivatedHabitatSettingsDTO()
|
||||
activatedSettings.decode(buffer, buckets)
|
||||
activatedSettings.decode(buffer)
|
||||
this.activatedSettings = activatedSettings
|
||||
} else {
|
||||
this.activatedSettings = null
|
||||
val naturalSettings = NaturalHabitatSettingsDTO()
|
||||
naturalSettings.decode(buffer, buckets)
|
||||
naturalSettings.decode(buffer)
|
||||
this.naturalSettings = naturalSettings
|
||||
}
|
||||
}
|
||||
|
||||
+1
-11
@@ -8,35 +8,25 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.habitats.dto
|
||||
|
||||
import com.cobblemon.mod.common.api.habitats.NaturalHabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.spawningstyle.NaturalHabitatSpawning
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
class NaturalHabitatSettingsDTO() {
|
||||
var replaceSpawns = false
|
||||
var rangeOfInfluence = 16
|
||||
var habitatPool: HabitatPoolSettingsDTO = HabitatPoolSettingsDTO(NaturalHabitatPool.default())
|
||||
|
||||
constructor(naturalHabitatSpawning: NaturalHabitatSpawning): this() {
|
||||
this.replaceSpawns = naturalHabitatSpawning.replaceSpawns
|
||||
this.rangeOfInfluence = naturalHabitatSpawning.rangeOfInfluence
|
||||
this.habitatPool = HabitatPoolSettingsDTO(naturalHabitatSpawning.pool)
|
||||
}
|
||||
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeBoolean(replaceSpawns)
|
||||
buffer.writeInt(rangeOfInfluence)
|
||||
habitatPool.encode(buffer)
|
||||
}
|
||||
|
||||
fun decode(
|
||||
buffer: RegistryFriendlyByteBuf,
|
||||
buckets: List<SpawnBucket>
|
||||
) {
|
||||
fun decode(buffer: RegistryFriendlyByteBuf, ) {
|
||||
this.replaceSpawns = buffer.readBoolean()
|
||||
this.rangeOfInfluence = buffer.readInt()
|
||||
this.habitatPool = HabitatPoolSettingsDTO()
|
||||
habitatPool.decode(buffer, buckets)
|
||||
}
|
||||
}
|
||||
+12
-23
@@ -8,17 +8,16 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.habitats.spawningstyle
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.habitats.HabitatSpawnActivatedEvent
|
||||
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatSpawn
|
||||
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatSpawningInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnCause
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.FixedAreaSpawner
|
||||
import com.cobblemon.mod.common.block.habitat.HabitatBlockEntity
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
@@ -27,7 +26,7 @@ import net.minecraft.server.level.ServerLevel
|
||||
* A habitat spawning style defined by being triggered by something. The activation trigger, [trigger], is either
|
||||
* a redstone pulse, every tick, or every random tick.
|
||||
*
|
||||
* The spawn details are generated from the [pool] every time there is some change to the habitat block's settings,
|
||||
* The spawn details are generated from the pool every time there is some change to the habitat block's settings,
|
||||
* then those spawn details are used for spawning Pokémon when the habitat block is triggered.
|
||||
*
|
||||
* The [chance] property is applied after the trigger logic so that not every activation necessarily results in a spawn
|
||||
@@ -37,6 +36,10 @@ import net.minecraft.server.level.ServerLevel
|
||||
* @since February 14th, 2026
|
||||
*/
|
||||
class ActivatedHabitatSpawning(val habitatBlockEntity: HabitatBlockEntity) : HabitatSpawningStyle {
|
||||
companion object {
|
||||
val TYPE = cobblemonResource("activated")
|
||||
}
|
||||
|
||||
enum class Trigger {
|
||||
/** Requires a redstone pulse. */
|
||||
REDSTONE,
|
||||
@@ -46,7 +49,8 @@ class ActivatedHabitatSpawning(val habitatBlockEntity: HabitatBlockEntity) : Hab
|
||||
RANDOM_TICK
|
||||
}
|
||||
|
||||
override val type = ActivatedHabitatSpawn.TYPE
|
||||
@Transient
|
||||
override val type = TYPE
|
||||
|
||||
/** The chance that an activation by the [trigger] will result in a spawn attempt. The spawn attempt may still fail. */
|
||||
var chance = 1F
|
||||
@@ -60,11 +64,7 @@ class ActivatedHabitatSpawning(val habitatBlockEntity: HabitatBlockEntity) : Hab
|
||||
var maxSpawns = -1
|
||||
/** The maximum number of Pokémon that can spawn in a single activation. This will still be capped by [maxSpawns]. */
|
||||
var maxSpawnsPerActivation = 1
|
||||
/** The pool of spawns to choose from. */
|
||||
var pool: ActivatedHabitatPool = ActivatedHabitatPool.default()
|
||||
|
||||
// This is cached here so that it doesn't have to be regenerated every time the block is triggered, updated when settings change.
|
||||
var spawnDetails: List<SpawnDetail> = listOf()
|
||||
lateinit var spawner: FixedAreaSpawner
|
||||
|
||||
override fun writeToNBT(nbt: CompoundTag) {
|
||||
@@ -75,10 +75,6 @@ class ActivatedHabitatSpawning(val habitatBlockEntity: HabitatBlockEntity) : Hab
|
||||
nbt.putInt(DataKeys.HABITAT_ACTIVATED_SPAWN_RANGE, spawnRange)
|
||||
nbt.putInt(DataKeys.HABITAT_ACTIVATED_MAX_SPAWNS, maxSpawns)
|
||||
nbt.putInt(DataKeys.HABITAT_ACTIVATED_MAX_SPAWNS_PER_ACTIVATION, maxSpawnsPerActivation)
|
||||
|
||||
val poolNBT = CompoundTag()
|
||||
writePoolToNBT(pool, poolNBT)
|
||||
nbt.put(DataKeys.HABITAT_POOL, poolNBT)
|
||||
}
|
||||
|
||||
override fun readFromNBT(nbt: CompoundTag) {
|
||||
@@ -88,25 +84,18 @@ class ActivatedHabitatSpawning(val habitatBlockEntity: HabitatBlockEntity) : Hab
|
||||
spawnRange = nbt.getInt(DataKeys.HABITAT_ACTIVATED_SPAWN_RANGE)
|
||||
maxSpawns = nbt.getInt(DataKeys.HABITAT_ACTIVATED_MAX_SPAWNS)
|
||||
maxSpawnsPerActivation = nbt.getInt(DataKeys.HABITAT_ACTIVATED_MAX_SPAWNS_PER_ACTIVATION)
|
||||
|
||||
pool = HabitatSpawningStyle.readPoolFromNBT(
|
||||
nbt = nbt.getCompound(DataKeys.HABITAT_POOL),
|
||||
poolInitializer = ::ActivatedHabitatPool,
|
||||
spawnInitializer = ::ActivatedHabitatSpawn,
|
||||
defaultPool = ActivatedHabitatPool::default
|
||||
)
|
||||
}
|
||||
|
||||
fun generateSpawnDetails(world: ServerLevel, pos: BlockPos) {
|
||||
spawnDetails = pool.createSpawnDetails(habitatBlockEntity)
|
||||
spawner = FixedAreaSpawner(
|
||||
name = "habitat_spawner_$pos",
|
||||
world = world,
|
||||
position = pos,
|
||||
spawnPool = SpawnPool("habitat_block").also {
|
||||
it.details.addAll(spawnDetails)
|
||||
it.details.addAll(habitatBlockEntity.spawnDetails)
|
||||
it.precalculate()
|
||||
},
|
||||
buckets = Cobblemon.bestSpawner.config.activatedHabitatBuckets,
|
||||
maxPokemonPerChunk = 16F,
|
||||
verticalRadius = spawnRange,
|
||||
horizontalRadius = spawnRange
|
||||
@@ -118,7 +107,7 @@ class ActivatedHabitatSpawning(val habitatBlockEntity: HabitatBlockEntity) : Hab
|
||||
// Check if the spawn attempt should even happen based on chance.
|
||||
if (chance < 1F && world.random.nextFloat() >= chance) {
|
||||
return
|
||||
} else if (spawnDetails.isEmpty()) {
|
||||
} else if (habitatBlockEntity.spawnDetails.isEmpty()) {
|
||||
// If there are no spawn details, then there's no point in trying to spawn anything.
|
||||
return
|
||||
}
|
||||
|
||||
-51
@@ -8,15 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.habitats.spawningstyle
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatPools
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatSpawn
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.ListTag
|
||||
import net.minecraft.nbt.Tag
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
@@ -33,47 +25,4 @@ sealed interface HabitatSpawningStyle {
|
||||
|
||||
fun writeToNBT(nbt: CompoundTag)
|
||||
fun readFromNBT(nbt: CompoundTag)
|
||||
|
||||
fun writePoolToNBT(pool: HabitatPool<*>, nbt: CompoundTag) {
|
||||
nbt.putString(DataKeys.HABITAT_POOL_ID, pool.id.toString())
|
||||
if (pool.id.path.startsWith("custom__")) {
|
||||
nbt.putString(DataKeys.HABITAT_POOL_NAME, pool.name)
|
||||
val spawnsListNBT = ListTag()
|
||||
for (spawn in pool.spawns) {
|
||||
val spawnTag = CompoundTag()
|
||||
spawn.writeToNBT(spawnTag)
|
||||
spawnsListNBT.add(spawnTag)
|
||||
}
|
||||
nbt.put(DataKeys.HABITAT_POOL_SPAWNS, spawnsListNBT)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun <T : HabitatSpawn, P : HabitatPool<T>> readPoolFromNBT(nbt: CompoundTag, poolInitializer: () -> P, spawnInitializer: () -> T, defaultPool: () -> P): P {
|
||||
var pool: P
|
||||
val poolId = nbt.getString(DataKeys.HABITAT_POOL_ID).asIdentifierDefaultingNamespace()
|
||||
if (nbt.contains(DataKeys.HABITAT_POOL_NAME)) {
|
||||
// It's one that was stored on the block
|
||||
pool = poolInitializer()
|
||||
pool.id = poolId
|
||||
pool.name = nbt.getString(DataKeys.HABITAT_POOL_NAME)
|
||||
val spawnsListNBT = nbt.getList(DataKeys.HABITAT_POOL_SPAWNS, Tag.TAG_COMPOUND.toInt())
|
||||
val spawns = mutableListOf<T>()
|
||||
spawnsListNBT.forEach { spawnTag ->
|
||||
try {
|
||||
val spawn = spawnInitializer().apply { readFromNBT(spawnTag as CompoundTag) }
|
||||
spawns.add(spawn)
|
||||
} catch (_: Exception) {
|
||||
// If there's an error reading a spawn, skip it and log the error.
|
||||
Cobblemon.LOGGER.error("Error reading activated habitat spawn from NBT for habitat block, skipping.")
|
||||
}
|
||||
}
|
||||
pool.spawns = spawns
|
||||
} else {
|
||||
pool = HabitatPools.habitatPoolsById[poolId] as? P ?: defaultPool()
|
||||
}
|
||||
|
||||
return pool
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-24
@@ -8,21 +8,18 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.habitats.spawningstyle
|
||||
|
||||
import com.cobblemon.mod.common.api.habitats.NaturalHabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.NaturalHabitatSpawn
|
||||
import com.cobblemon.mod.common.api.habitats.spawningstyle.HabitatSpawningStyle.Companion.readPoolFromNBT
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.block.habitat.HabitatBlockEntity
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* A habitat spawning style defined by tapping into regular spawning methods. This works by adding to the spawn list
|
||||
* for spawning that's occurring nearby. When [replaceSpawns] is set to true, the original spawns will be
|
||||
* removed completely in favour of the spawns provided in this habitat's [pool].
|
||||
* removed completely in favour of the spawns provided in this habitat's pool.
|
||||
*
|
||||
* The spawn details are generated from the [pool] every time there is some change to the habitat block's settings,
|
||||
* The spawn details are generated from the pool every time there is some change to the habitat block's settings,
|
||||
* then those spawn details are used for spawning Pokémon when world spawning is occurring nearby.
|
||||
*
|
||||
* Whether or not this interactions with overworld or fishing spawns is determined by whether the habitat pool has
|
||||
@@ -32,10 +29,13 @@ import net.minecraft.resources.ResourceLocation
|
||||
* @since February 22nd, 2026
|
||||
*/
|
||||
class NaturalHabitatSpawning(val habitatBlockEntity: HabitatBlockEntity) : HabitatSpawningStyle {
|
||||
companion object {
|
||||
val TYPE = cobblemonResource("natural")
|
||||
}
|
||||
|
||||
@Transient
|
||||
override val type: ResourceLocation = NaturalHabitatSpawn.TYPE
|
||||
override val type: ResourceLocation = TYPE
|
||||
|
||||
var pool: NaturalHabitatPool = NaturalHabitatPool.default()
|
||||
var replaceSpawns = false
|
||||
var rangeOfInfluence = 16
|
||||
|
||||
@@ -43,33 +43,19 @@ class NaturalHabitatSpawning(val habitatBlockEntity: HabitatBlockEntity) : Habit
|
||||
var affectsFishing = false
|
||||
@Transient
|
||||
var affectsOverworld = false
|
||||
@Transient
|
||||
var spawnDetails: List<SpawnDetail> = listOf()
|
||||
|
||||
fun generateSpawnDetails() {
|
||||
spawnDetails = pool.createSpawnDetails(habitatBlockEntity = habitatBlockEntity)
|
||||
affectsFishing = spawnDetails.any { it.spawnablePositionType.name == "fishing" }
|
||||
affectsOverworld = spawnDetails.any { it.spawnablePositionType.name != "fishing" }
|
||||
affectsFishing = habitatBlockEntity.spawnDetails.any { it.spawnablePositionType.name == "fishing" }
|
||||
affectsOverworld = habitatBlockEntity.spawnDetails.any { it.spawnablePositionType.name != "fishing" }
|
||||
}
|
||||
|
||||
override fun writeToNBT(nbt: CompoundTag) {
|
||||
nbt.putBoolean(DataKeys.HABITAT_NATURAL_REPLACE_SPAWNS, replaceSpawns)
|
||||
nbt.putInt(DataKeys.HABITAT_NATURAL_RANGE_OF_INFLUENCE, rangeOfInfluence)
|
||||
|
||||
val poolNBT = CompoundTag()
|
||||
writePoolToNBT(pool, poolNBT)
|
||||
nbt.put(DataKeys.HABITAT_POOL, poolNBT)
|
||||
}
|
||||
|
||||
override fun readFromNBT(nbt: CompoundTag) {
|
||||
replaceSpawns = nbt.getBoolean(DataKeys.HABITAT_NATURAL_REPLACE_SPAWNS)
|
||||
rangeOfInfluence = nbt.getInt(DataKeys.HABITAT_NATURAL_RANGE_OF_INFLUENCE)
|
||||
|
||||
pool = readPoolFromNBT(
|
||||
nbt = nbt.getCompound(DataKeys.HABITAT_POOL),
|
||||
poolInitializer = ::NaturalHabitatPool,
|
||||
spawnInitializer = ::NaturalHabitatSpawn,
|
||||
defaultPool = NaturalHabitatPool::default
|
||||
)
|
||||
}
|
||||
}
|
||||
+8
-3
@@ -51,11 +51,15 @@ data class PokemonInteractionSet(
|
||||
}
|
||||
|
||||
data class PokemonInteraction(
|
||||
val grouping: ResourceLocation,
|
||||
val requirements: List<Requirement> = listOf(),
|
||||
val effects: List<InteractionEffect> = listOf(),
|
||||
val cooldown: ExpressionLike = "0".asExpressionLike()
|
||||
) {
|
||||
// Not put as a necessary constructor parameter because then there isn't a default constructor
|
||||
// and that means Gson just gives null for everything in the constructor and falls back to the JSON
|
||||
// properties - completely ignoring the default values above.
|
||||
lateinit var grouping: ResourceLocation
|
||||
|
||||
fun encode(buffer: FriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(grouping)
|
||||
buffer.writeCollection(requirements) { it, requirement ->
|
||||
@@ -76,11 +80,12 @@ data class PokemonInteraction(
|
||||
val cooldown = buffer.readExpressionLike()
|
||||
|
||||
return PokemonInteraction(
|
||||
grouping,
|
||||
requirements,
|
||||
effects,
|
||||
cooldown,
|
||||
)
|
||||
).also {
|
||||
it.grouping = grouping
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ import net.minecraft.world.phys.AABB
|
||||
* @since July 29th, 2023
|
||||
*/
|
||||
interface PokemonSelectingItem {
|
||||
fun use(player: ServerPlayer, stack: ItemStack): InteractionResultHolder<ItemStack> {
|
||||
fun use(player: ServerPlayer, stack: ItemStack, ignoreShift: Boolean = false): InteractionResultHolder<ItemStack> {
|
||||
val range = player.entityInteractionRange()
|
||||
val entity = player.level()
|
||||
.getEntities(player, AABB.ofSize(player.position(), range, range, range))
|
||||
@@ -60,7 +60,7 @@ interface PokemonSelectingItem {
|
||||
return interactWithSpecificBattle(player, stack, battlePokemon)
|
||||
}
|
||||
} ?: run {
|
||||
if (!player.isShiftKeyDown) {
|
||||
if (ignoreShift || !player.isShiftKeyDown) {
|
||||
return if (entity != null) {
|
||||
val pokemon = entity.pokemon
|
||||
if (entity.ownerUUID == player.uuid) {
|
||||
|
||||
+1
-1
@@ -109,7 +109,7 @@ object PokemonMoLangFunctions : AbstractMoLangFunctionHolder<Pokemon>() {
|
||||
DoubleValue(pokemon.species.nationalPokedexNumber.toDouble())
|
||||
}
|
||||
map["types"] = {
|
||||
pokemon.form.types.map { it.toString() }.asArrayValue(::StringValue)
|
||||
pokemon.form.types.map { it.showdownId }.asArrayValue(::StringValue)
|
||||
}
|
||||
map["gender_ratio"] = {
|
||||
DoubleValue(pokemon.form.maleRatio.toDouble())
|
||||
|
||||
+3
-5
@@ -56,10 +56,8 @@ class AggregatePokedexDef(
|
||||
return speciesToEntry.values.toList()
|
||||
}
|
||||
}
|
||||
|
||||
override fun shouldSynchronize(other: PokedexDef) = true
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
sortOrder = buffer.readSizedInt(IntSize.U_BYTE)
|
||||
val size = buffer.readInt()
|
||||
for (i in 0 until size) {
|
||||
@@ -67,7 +65,7 @@ class AggregatePokedexDef(
|
||||
}
|
||||
}
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeSizedInt(IntSize.U_BYTE, sortOrder)
|
||||
buffer.writeInt(subDexIds.size)
|
||||
subDexIds.forEach {
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.pokedex.def
|
||||
|
||||
import com.cobblemon.mod.common.api.data.ClientDataSynchronizer
|
||||
import com.cobblemon.mod.common.api.pokedex.entry.DexEntries
|
||||
import com.cobblemon.mod.common.api.pokedex.entry.PokedexEntry
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.MapCodec
|
||||
@@ -23,7 +21,7 @@ import net.minecraft.resources.ResourceLocation
|
||||
* @since August 24, 2024
|
||||
* @author Apion
|
||||
*/
|
||||
abstract class PokedexDef: ClientDataSynchronizer<PokedexDef> {
|
||||
abstract class PokedexDef {
|
||||
//The ID used to find the codecs for this "type" of PokedexDef
|
||||
abstract val typeId: ResourceLocation
|
||||
//The ID of this dex in the Dexes registry
|
||||
|
||||
@@ -45,9 +45,7 @@ class SimplePokedexDef(
|
||||
|
||||
override fun getEntries() = entries.mapNotNull { DexEntries.entries[it] }
|
||||
|
||||
override fun shouldSynchronize(other: PokedexDef) = true
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
sortOrder = buffer.readSizedInt(IntSize.U_BYTE)
|
||||
val size = buffer.readInt()
|
||||
for (i in 0 until size) {
|
||||
@@ -55,7 +53,7 @@ class SimplePokedexDef(
|
||||
}
|
||||
}
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeSizedInt(IntSize.U_BYTE, sortOrder)
|
||||
buffer.writeInt(entries.size)
|
||||
entries.forEach {
|
||||
|
||||
+6
-4
@@ -41,16 +41,18 @@ class PokedexCategoryFilter(
|
||||
PokedexCategoryFilterType.UNDISCOVERED_TM_MOVE -> hasUndiscoveredLevelUpTM(entry)
|
||||
PokedexCategoryFilterType.RIDEABLE -> {
|
||||
val species = PokemonSpecies.getByIdentifier(entry.speciesId)
|
||||
species?.forms?.any { form -> !form.riding.behaviours.isNullOrEmpty() } == true
|
||||
&& pokedexManager.getHighestKnowledgeFor(entry) >= PokedexEntryProgress.OWNED
|
||||
// we were silly as standard forms also can be rideable and we were ingoring those
|
||||
(species?.forms.orEmpty() + listOfNotNull(species?.standardForm)).any { form -> !form.riding.behaviours.isNullOrEmpty() }
|
||||
&& (pokedexManager.getHighestKnowledgeFor(entry) >= PokedexEntryProgress.OWNED ||
|
||||
pokedexManager.getHighestKnowledgeFor(entry) >= PokedexEntryProgress.SEEN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun hasUndiscoveredLevelUpTM(entry: PokedexEntry): Boolean {
|
||||
if (pokedexManager.getHighestKnowledgeFor(entry) != PokedexEntryProgress.OWNED) return false
|
||||
if (pokedexManager.getHighestKnowledgeFor(entry) == PokedexEntryProgress.UNREGISTERED) return false
|
||||
val species = PokemonSpecies.getByIdentifier(entry.speciesId) ?: return false
|
||||
val forms = pokedexManager.getCaughtForms(entry)
|
||||
val forms = pokedexManager.getEncounteredForms(entry)
|
||||
val formData = if (forms.isEmpty()) {
|
||||
listOf(species.standardForm)
|
||||
} else {
|
||||
|
||||
@@ -72,7 +72,7 @@ class SearchFilter(val pokedexManager: AbstractPokedexManager, val searchString:
|
||||
}
|
||||
}
|
||||
SearchByType.DROPS -> {
|
||||
if (pokedexManager.getHighestKnowledgeFor(entry) !== PokedexEntryProgress.OWNED) return false
|
||||
if (pokedexManager.getHighestKnowledgeFor(entry) == PokedexEntryProgress.UNREGISTERED) return false
|
||||
val dropsList = mutableListOf<String>()
|
||||
val formsList = if (species.forms.isEmpty()) mutableListOf(species.standardForm) else species.forms
|
||||
formsList.forEach {
|
||||
@@ -131,9 +131,7 @@ class SearchFilter(val pokedexManager: AbstractPokedexManager, val searchString:
|
||||
form.moves.tutorMoves,
|
||||
form.moves.eggMoves,
|
||||
form.moves.evolutionMoves,
|
||||
form.moves.formChangeMoves,
|
||||
form.moves.specialMoves,
|
||||
form.moves.legacyMoves
|
||||
form.moves.formChangeMoves
|
||||
)
|
||||
|
||||
for (moves in alwaysDiscovered) {
|
||||
|
||||
@@ -674,9 +674,6 @@ open class PokemonProperties {
|
||||
@JvmOverloads
|
||||
fun createEntity(world: Level, player: ServerPlayer? = null): PokemonEntity {
|
||||
val pokemon = create(player)
|
||||
if (pokemon.isAlpha) {
|
||||
pokemon.initializeMovesetWithRandomTm()
|
||||
}
|
||||
return PokemonEntity(world, pokemon).also { applyCustomProperties(it) }
|
||||
}
|
||||
|
||||
@@ -762,6 +759,7 @@ open class PokemonProperties {
|
||||
shiny?.let { json.addProperty(DataKeys.POKEMON_SHINY, it) }
|
||||
gender?.let { json.addProperty(DataKeys.POKEMON_GENDER, it.name) }
|
||||
species?.let { json.addProperty(DataKeys.POKEMON_SPECIES_TEXT, it) }
|
||||
nickname?.let { json.addProperty(DataKeys.POKEMON_NICKNAME, it.string) }
|
||||
form?.let { json.addProperty(DataKeys.POKEMON_FORM_ID, it) }
|
||||
friendship?.let { json.addProperty(DataKeys.POKEMON_FRIENDSHIP, it) }
|
||||
fullness?.let { json.addProperty(DataKeys.POKEMON_FULLNESS, it) }
|
||||
@@ -798,6 +796,7 @@ open class PokemonProperties {
|
||||
shiny = json.get(DataKeys.POKEMON_SHINY)?.asBoolean
|
||||
gender = json.get(DataKeys.POKEMON_GENDER)?.asString?.let { Gender.valueOf(it) }
|
||||
species = json.get(DataKeys.POKEMON_SPECIES_TEXT)?.asString
|
||||
nickname = json.get(DataKeys.POKEMON_NICKNAME)?.asString?.let(Component::literal)
|
||||
form = json.get(DataKeys.POKEMON_FORM_ID)?.asString
|
||||
friendship = json.get(DataKeys.POKEMON_FRIENDSHIP)?.asInt
|
||||
fullness = json.get(DataKeys.POKEMON_FULLNESS)?.asInt
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.moves
|
||||
|
||||
import com.cobblemon.mod.common.api.data.ClientDataSynchronizer
|
||||
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.net.IntSize
|
||||
@@ -18,7 +17,7 @@ import com.cobblemon.mod.common.util.writeSizedInt
|
||||
import com.google.gson.JsonElement
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
open class Learnset : ClientDataSynchronizer<Learnset> {
|
||||
open class Learnset {
|
||||
class Interpreter(val loadMove: (JsonElement, Learnset) -> Boolean) {
|
||||
companion object {
|
||||
fun parseFromPrefixIntoList(prefix: String, list: (Learnset) -> MutableList<MoveTemplate>): Interpreter {
|
||||
@@ -111,9 +110,9 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
|
||||
}
|
||||
|
||||
// We only sync level up moves atm
|
||||
override fun shouldSynchronize(other: Learnset) = other.levelUpMoves != this.levelUpMoves
|
||||
fun shouldSynchronize(other: Learnset) = other.levelUpMoves != this.levelUpMoves
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
this.levelUpMoves.clear()
|
||||
repeat(times = buffer.readSizedInt(IntSize.U_BYTE)) {
|
||||
val level = buffer.readSizedInt(IntSize.U_SHORT)
|
||||
@@ -125,7 +124,7 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
|
||||
}
|
||||
}
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeSizedInt(IntSize.U_BYTE, levelUpMoves.size)
|
||||
for ((level, moves) in levelUpMoves) {
|
||||
buffer.writeSizedInt(IntSize.U_SHORT, level)
|
||||
|
||||
@@ -22,8 +22,8 @@ import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
*/
|
||||
data class Seat(
|
||||
val locator: String = "",
|
||||
val condition: Expression?,
|
||||
val poseAnimations: MutableList<SeatPoseAnimations>?,
|
||||
val condition: Expression? = null,
|
||||
val poseAnimations: MutableList<SeatPoseAnimations>? = null,
|
||||
val handsBusy: Boolean = false,
|
||||
) : Encodable {
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.snowstorm
|
||||
|
||||
import com.cobblemon.mod.common.client.render.shader.CobblemonShaders
|
||||
import com.mojang.blaze3d.platform.GlStateManager
|
||||
import com.mojang.blaze3d.systems.RenderSystem
|
||||
import com.mojang.blaze3d.vertex.BufferBuilder
|
||||
@@ -16,6 +15,7 @@ import com.mojang.blaze3d.vertex.DefaultVertexFormat
|
||||
import com.mojang.blaze3d.vertex.Tesselator
|
||||
import com.mojang.blaze3d.vertex.VertexFormat
|
||||
import net.minecraft.client.particle.ParticleRenderType
|
||||
import net.minecraft.client.renderer.GameRenderer
|
||||
import net.minecraft.client.renderer.texture.TextureAtlas
|
||||
import net.minecraft.client.renderer.texture.TextureManager
|
||||
|
||||
@@ -23,8 +23,9 @@ object ParticleMaterials {
|
||||
val ALPHA = object : ParticleRenderType {
|
||||
override fun begin(tessellator: Tesselator, textureManager: TextureManager): BufferBuilder {
|
||||
RenderSystem.enableBlend()
|
||||
RenderSystem.enableCull()
|
||||
RenderSystem.depthMask(true)
|
||||
RenderSystem.setShader { CobblemonShaders.PARTICLE_CUTOUT }
|
||||
RenderSystem.setShader { GameRenderer.getParticleShader() }
|
||||
RenderSystem.setShaderTexture(0, TextureAtlas.LOCATION_PARTICLES)
|
||||
RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA)
|
||||
return tessellator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.PARTICLE)
|
||||
@@ -36,10 +37,11 @@ object ParticleMaterials {
|
||||
val ADD = object : ParticleRenderType {
|
||||
override fun begin(tessellator: Tesselator, textureManager: TextureManager): BufferBuilder {
|
||||
RenderSystem.enableBlend()
|
||||
RenderSystem.disableCull()
|
||||
RenderSystem.depthMask(true)
|
||||
RenderSystem.setShader { CobblemonShaders.PARTICLE_BLEND }
|
||||
RenderSystem.setShader { GameRenderer.getParticleShader() }
|
||||
RenderSystem.setShaderTexture(0, TextureAtlas.LOCATION_PARTICLES)
|
||||
RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA)
|
||||
RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE)
|
||||
return tessellator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.PARTICLE)
|
||||
}
|
||||
|
||||
@@ -49,10 +51,11 @@ object ParticleMaterials {
|
||||
val BLEND = object : ParticleRenderType {
|
||||
override fun begin(tessellator: Tesselator, textureManager: TextureManager): BufferBuilder {
|
||||
RenderSystem.enableBlend()
|
||||
RenderSystem.disableCull()
|
||||
RenderSystem.depthMask(true)
|
||||
RenderSystem.setShader { CobblemonShaders.PARTICLE_BLEND }
|
||||
RenderSystem.setShader { GameRenderer.getParticleShader() }
|
||||
RenderSystem.setShaderTexture(0, TextureAtlas.LOCATION_PARTICLES)
|
||||
RenderSystem.blendFunc(GlStateManager.SourceFactor.DST_COLOR, GlStateManager.DestFactor.ZERO)
|
||||
RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA)
|
||||
return tessellator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.PARTICLE)
|
||||
}
|
||||
|
||||
@@ -62,10 +65,11 @@ object ParticleMaterials {
|
||||
val OPAQUE = object : ParticleRenderType {
|
||||
override fun begin(tessellator: Tesselator, textureManager: TextureManager): BufferBuilder {
|
||||
RenderSystem.enableBlend()
|
||||
RenderSystem.enableCull()
|
||||
RenderSystem.depthMask(true)
|
||||
RenderSystem.setShader { CobblemonShaders.PARTICLE_BLEND }
|
||||
RenderSystem.setShader { GameRenderer.getParticleShader() }
|
||||
RenderSystem.setShaderTexture(0, TextureAtlas.LOCATION_PARTICLES)
|
||||
RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE)
|
||||
RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_COLOR, GlStateManager.DestFactor.ZERO)
|
||||
return tessellator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.PARTICLE)
|
||||
}
|
||||
|
||||
|
||||
@@ -18,12 +18,12 @@ import com.cobblemon.mod.common.api.spawning.condition.SeafloorSpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SubmergedSpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SurfaceSpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.detail.EmptySpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.detail.NPCSpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.detail.PokemonHerdSpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.influence.BucketMultiplyingInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.position.AreaSpawnablePositionResolver
|
||||
import com.cobblemon.mod.common.api.spawning.position.FishingSpawnablePosition
|
||||
@@ -121,6 +121,7 @@ object BestSpawner {
|
||||
SpawnDetail.registerSpawnType(name = PokemonSpawnDetail.TYPE, PokemonSpawnDetail::class.java)
|
||||
SpawnDetail.registerSpawnType(name = NPCSpawnDetail.TYPE, NPCSpawnDetail::class.java)
|
||||
SpawnDetail.registerSpawnType(name = PokemonHerdSpawnDetail.TYPE, PokemonHerdSpawnDetail::class.java)
|
||||
SpawnDetail.registerSpawnType(name = EmptySpawnDetail.TYPE, EmptySpawnDetail::class.java)
|
||||
LOGGER.info("Loaded ${SpawnDetail.spawnDetailTypes.size} spawn detail types.")
|
||||
|
||||
loadConfig()
|
||||
|
||||
@@ -1,33 +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.spawning
|
||||
|
||||
/**
|
||||
* A spawn bucket is a simple referenceable object by spawn files. The bucket is used at the beginning of
|
||||
* the selection process to decide which set of spawns will be used for spawning on that attempt. Weights are used to
|
||||
* make entire buckets more or less likely, and then weighted selections will occur within whichever bucket
|
||||
* was chosen.
|
||||
*
|
||||
* Spawn buckets are configured in the main config.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since June 20th, 2022
|
||||
*/
|
||||
class SpawnBucket() {
|
||||
lateinit var name: String
|
||||
var weight = 0F
|
||||
|
||||
constructor(name: String, weight: Float): this() {
|
||||
this.name = name
|
||||
this.weight = weight
|
||||
}
|
||||
|
||||
override fun hashCode() = name.hashCode()
|
||||
override fun equals(other: Any?) = other is SpawnBucket && other.name == name
|
||||
}
|
||||
@@ -49,7 +49,6 @@ object SpawnDetailPresets : JsonDataRegistry<SpawnDetailPreset> {
|
||||
.setPrettyPrinting()
|
||||
.setLenient()
|
||||
.disableHtmlEscaping()
|
||||
.registerTypeAdapter(SpawnBucket::class.java, SpawnBucketAdapter)
|
||||
.registerTypeAdapter(SpawnablePositionType::class.java, RegisteredSpawnablePositionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, BiomeLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Block::class.java).type, BlockLikeConditionAdapter)
|
||||
|
||||
@@ -67,7 +67,6 @@ object SpawnLoader {
|
||||
.registerTypeAdapter(MoonPhaseRange::class.java, IntRangesAdapter(MoonPhaseRange.moonPhaseRanges) { MoonPhaseRange(*it) })
|
||||
.registerTypeAdapter(ItemDropMethod::class.java, ItemDropMethod.adapter)
|
||||
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
|
||||
.registerTypeAdapter(SpawnBucket::class.java, SpawnBucketAdapter)
|
||||
.registerTypeAdapter(CompoundTag::class.java, NbtCompoundAdapter)
|
||||
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
|
||||
.registerTypeAdapter(PossibleHeldItem::class.java, PossibleHeldItemAdapter)
|
||||
|
||||
+13
-14
@@ -8,10 +8,9 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.condition
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
@@ -22,8 +21,8 @@ import net.minecraft.resources.ResourceLocation
|
||||
* time as sharing that value. The requirement of implementations is only to get that
|
||||
* key from both a [SpawnDetail] and a [SpawnablePosition].
|
||||
*
|
||||
* When a precalculation has been registered with a [Spawner] by adding to [Spawner.precalculators],
|
||||
* the precalculation result will need to be updated by running [Spawner.precalculate]. That
|
||||
* When a precalculation has been registered with a [SpawnPool] by adding to [SpawnPool.precalculators],
|
||||
* the precalculation result will need to be updated by running [SpawnPool.precalculate]. That
|
||||
* might take it a hot minute, but the result will be that there is an extra layer of
|
||||
* shortcutting to the matching algorithm.
|
||||
*
|
||||
@@ -34,7 +33,7 @@ interface SpawningPrecalculation<T : Any> {
|
||||
/** Retrieves the precalculation keys for the given spawn detail. */
|
||||
fun select(detail: SpawnDetail): Collection<T>
|
||||
/** Retrieves the precalculation key for the given spawnable position, if that key exists for this position. */
|
||||
fun select(bucket: SpawnBucket, spawnablePosition: SpawnablePosition): T?
|
||||
fun select(bucket: String, spawnablePosition: SpawnablePosition): T?
|
||||
|
||||
/**
|
||||
* Generates a precalculation result for the list of [SpawnDetail]s and the subsequent precalculations.
|
||||
@@ -74,7 +73,7 @@ interface SpawningPrecalculation<T : Any> {
|
||||
object RootPrecalculation : SpawningPrecalculation<Any> {
|
||||
val list = listOf(Unit)
|
||||
override fun select(detail: SpawnDetail): List<Any> = list
|
||||
override fun select(bucket: SpawnBucket, spawnablePosition: SpawnablePosition): Any = Unit
|
||||
override fun select(bucket: String, spawnablePosition: SpawnablePosition): Any = Unit
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,12 +84,12 @@ object RootPrecalculation : SpawningPrecalculation<Any> {
|
||||
*/
|
||||
object SpawnablePositionTypePrecalculation : SpawningPrecalculation<Class<out SpawnablePosition>> {
|
||||
override fun select(detail: SpawnDetail) = listOf(detail.spawnablePositionType.clazz)
|
||||
override fun select(bucket: SpawnBucket, spawnablePosition: SpawnablePosition) = spawnablePosition::class.java
|
||||
override fun select(bucket: String, spawnablePosition: SpawnablePosition) = spawnablePosition::class.java
|
||||
}
|
||||
|
||||
object BucketPrecalculation : SpawningPrecalculation<SpawnBucket> {
|
||||
object BucketPrecalculation : SpawningPrecalculation<String> {
|
||||
override fun select(detail: SpawnDetail) = listOf(detail.bucket)
|
||||
override fun select(bucket: SpawnBucket, spawnablePosition: SpawnablePosition) = bucket
|
||||
override fun select(bucket: String, spawnablePosition: SpawnablePosition) = bucket
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,7 +98,7 @@ object BucketPrecalculation : SpawningPrecalculation<SpawnBucket> {
|
||||
*/
|
||||
object BiomePrecalculation : SpawningPrecalculation<ResourceLocation> {
|
||||
override fun select(detail: SpawnDetail) = detail.validBiomes
|
||||
override fun select(bucket: SpawnBucket, spawnablePosition: SpawnablePosition) = spawnablePosition.biomeHolder.unwrapKey().map { it.location() }.orElse(null)
|
||||
override fun select(bucket: String, spawnablePosition: SpawnablePosition) = spawnablePosition.biomeHolder.unwrapKey().map { it.location() }.orElse(null)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,7 +113,7 @@ sealed class PrecalculationResult<T : Any>(
|
||||
val calculation: SpawningPrecalculation<*>
|
||||
) {
|
||||
/** Gets the list of spawn details that fit this spawnable position. */
|
||||
abstract fun retrieve(bucket: SpawnBucket, spawnablePosition: SpawnablePosition): List<SpawnDetail>
|
||||
abstract fun retrieve(bucket: String, spawnablePosition: SpawnablePosition): List<SpawnDetail>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -127,7 +126,7 @@ class NestedPrecalculationResult<T : Any>(
|
||||
calculation: SpawningPrecalculation<T>,
|
||||
val mapping: Map<T, PrecalculationResult<*>> = mutableMapOf()
|
||||
) : PrecalculationResult<T>(calculation) {
|
||||
override fun retrieve(bucket: SpawnBucket, spawnablePosition: SpawnablePosition) = mapping[calculation.select(bucket, spawnablePosition)]?.retrieve(bucket, spawnablePosition) ?: emptyList()
|
||||
override fun retrieve(bucket: String, spawnablePosition: SpawnablePosition) = mapping[calculation.select(bucket, spawnablePosition)]?.retrieve(bucket, spawnablePosition) ?: emptyList()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -141,5 +140,5 @@ class FinalPrecalculationResult<T : Any>(
|
||||
calculation: SpawningPrecalculation<*>,
|
||||
val mapping: Map<T, List<SpawnDetail>>
|
||||
) : PrecalculationResult<T>(calculation) {
|
||||
override fun retrieve(bucket: SpawnBucket, spawnablePosition: SpawnablePosition) = mapping[calculation.select(bucket, spawnablePosition)] ?: emptyList()
|
||||
override fun retrieve(bucket: String, spawnablePosition: SpawnablePosition) = mapping[calculation.select(bucket, spawnablePosition)] ?: emptyList()
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.detail
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
|
||||
/**
|
||||
* Does a whole lot of nothing. Spawns nothing.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since May 16th, 2026
|
||||
*/
|
||||
class EmptySpawnAction(
|
||||
spawnablePosition: SpawnablePosition,
|
||||
bucket: String,
|
||||
detail: EmptySpawnDetail
|
||||
) : SpawnAction<Unit>(spawnablePosition, bucket, detail) {
|
||||
override fun run() {}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.spawning.detail
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.selection.SpawnSelectionData
|
||||
|
||||
/**
|
||||
* A spawn detail for nothing. It is used to take a slot, like to weigh down a spawn attempt,
|
||||
* with something that ultimately doesn't spawn anything.
|
||||
*
|
||||
* The use case for this is very niche. With 1.8, the buckets that can be selected by a spawner
|
||||
* during a spawn attempt will be narrowed down to the buckets that are used by the spawn
|
||||
* pool. That means that if a bucket is never used by the pool, it will never be selected. That
|
||||
* way, a bucket won't be selected when it never had any chance of being selected. This is what
|
||||
* you would want in almost every case.
|
||||
*
|
||||
* However, in some cases you may want spawning to not spawn anything or you may want to preserve
|
||||
* the relative rarity of the other buckets, and in that case you'd put something like this in a
|
||||
* bucket so that it technically exists in the spawn pool without actually resulting in a spawned
|
||||
* entity.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since May 16th, 2026
|
||||
*/
|
||||
class EmptySpawnDetail : SpawnDetail() {
|
||||
companion object {
|
||||
val TYPE = "empty"
|
||||
}
|
||||
|
||||
override val type: String = TYPE
|
||||
|
||||
override fun createSpawnAction(
|
||||
spawnablePosition: SpawnablePosition,
|
||||
bucket: String,
|
||||
selectionData: SpawnSelectionData
|
||||
) = EmptySpawnAction(
|
||||
spawnablePosition = spawnablePosition,
|
||||
bucket = bucket,
|
||||
detail = this
|
||||
)
|
||||
}
|
||||
+1
-2
@@ -9,7 +9,6 @@
|
||||
package com.cobblemon.mod.common.api.spawning.detail
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.util.resolveInt
|
||||
@@ -20,7 +19,7 @@ import com.cobblemon.mod.common.util.resolveInt
|
||||
* @author Hiroku
|
||||
* @since October 8th, 2023
|
||||
*/
|
||||
class NPCSpawnAction(spawnablePosition: SpawnablePosition, bucket: SpawnBucket, override val detail: NPCSpawnDetail) : SingleEntitySpawnAction<NPCEntity>(spawnablePosition, bucket, detail) {
|
||||
class NPCSpawnAction(spawnablePosition: SpawnablePosition, bucket: String, override val detail: NPCSpawnDetail) : SingleEntitySpawnAction<NPCEntity>(spawnablePosition, bucket, detail) {
|
||||
override fun createEntity(): NPCEntity {
|
||||
val npc = NPCEntity(spawnablePosition.world)
|
||||
npc.npc = detail.npcClass
|
||||
|
||||
+1
-3
@@ -11,13 +11,11 @@ package com.cobblemon.mod.common.api.spawning.detail
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.npc.NPCClass
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.selection.SpawnSelectionData
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlin.math.ceil
|
||||
|
||||
/**
|
||||
* A [SpawnDetail] describing an [NPCEntity] spawn.
|
||||
@@ -51,7 +49,7 @@ class NPCSpawnDetail : SpawnDetail() {
|
||||
|
||||
override fun createSpawnAction(
|
||||
spawnablePosition: SpawnablePosition,
|
||||
bucket: SpawnBucket,
|
||||
bucket: String,
|
||||
selectionData: SpawnSelectionData
|
||||
) = NPCSpawnAction(spawnablePosition, bucket, this)
|
||||
}
|
||||
+11
-8
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.spawning.detail
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.api.drop.DropTable
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.selection.SpawnSelectionData
|
||||
import com.cobblemon.mod.common.util.weightedSelection
|
||||
@@ -54,8 +53,10 @@ class PokemonHerdSpawnDetail : SpawnDetail() {
|
||||
var pokemon: PokemonProperties = PokemonProperties()
|
||||
val levelRange: IntRange? = null
|
||||
var levelRangeOffset: IntRange? = null
|
||||
val herdLevelRange: IntRange? = null
|
||||
var dropTable: DropTable? = null
|
||||
var isLeader: Boolean? = null
|
||||
var isLeader: Boolean = false
|
||||
var isFollower: Boolean = true
|
||||
var weight: Float = 1F
|
||||
var maxTimes = 10
|
||||
var heldItem: ResourceLocation? = null
|
||||
@@ -83,7 +84,7 @@ class PokemonHerdSpawnDetail : SpawnDetail() {
|
||||
val herdable = getValidHerdMembers(selectionData)
|
||||
val herdCounts = getHerdMemberCounts(selectionData)
|
||||
|
||||
val leaderIsPossible = herdable.any { it.levelRange?.contains(level) != false && it.isLeader == true }
|
||||
val leaderIsPossible = herdable.any { it.herdLevelRange?.contains(level) != false && it.isLeader }
|
||||
val leaderIsSelected = herdCounts.any { it.key?.isLeader == true && it.value > 0 }
|
||||
return leaderIsPossible && !leaderIsSelected
|
||||
}
|
||||
@@ -98,14 +99,16 @@ class PokemonHerdSpawnDetail : SpawnDetail() {
|
||||
|
||||
override fun createSpawnAction(
|
||||
spawnablePosition: SpawnablePosition,
|
||||
bucket: SpawnBucket,
|
||||
bucket: String,
|
||||
selectionData: SpawnSelectionData
|
||||
): PokemonSpawnAction {
|
||||
val validHerdMembers = getValidHerdMembers(selectionData)
|
||||
var searchedForLeader = false
|
||||
val herdable = if (lacksPossibleLeader(selectionData)) {
|
||||
validHerdMembers.filter { it.isLeader == true }
|
||||
searchedForLeader = true
|
||||
validHerdMembers.filter { it.isLeader }
|
||||
} else {
|
||||
validHerdMembers
|
||||
validHerdMembers.filter { it.isFollower }
|
||||
}.weightedSelection { it.weight } ?: herdablePokemon[0]
|
||||
|
||||
val level = selectionData.context[herdLevelKey] as? Int ?: 1
|
||||
@@ -134,7 +137,7 @@ class PokemonHerdSpawnDetail : SpawnDetail() {
|
||||
heldItem = heldItem,
|
||||
levelRange = levelRange
|
||||
).also {
|
||||
if (herdable.isLeader == true) {
|
||||
if (herdable.isLeader && searchedForLeader) {
|
||||
it.labels += HERD_LEADER
|
||||
}
|
||||
}
|
||||
@@ -153,7 +156,7 @@ class PokemonHerdSpawnDetail : SpawnDetail() {
|
||||
val count = counts[herdable] ?: 0
|
||||
return@filter when {
|
||||
count >= herdable.maxTimes -> false
|
||||
herdable.levelRange != null && level !in herdable.levelRange -> false
|
||||
herdable.herdLevelRange != null && level !in herdable.herdLevelRange -> false
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.spawning.detail
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.drop.DropTable
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.feature.SeasonFeatureHandler
|
||||
@@ -26,7 +25,7 @@ import net.minecraft.world.item.ItemStack
|
||||
*/
|
||||
class PokemonSpawnAction(
|
||||
spawnablePosition: SpawnablePosition,
|
||||
bucket: SpawnBucket,
|
||||
bucket: String,
|
||||
override val detail: SpawnDetail,
|
||||
/**
|
||||
* The [PokemonProperties] that are about to be used. When using it, a copy will be taken.
|
||||
|
||||
+2
-2
@@ -14,7 +14,6 @@ import com.cobblemon.mod.common.api.drop.DropTable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.selection.SpawnSelectionData
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
@@ -104,7 +103,8 @@ class PokemonSpawnDetail : SpawnDetail() {
|
||||
}
|
||||
|
||||
override fun createSpawnAction(
|
||||
spawnablePosition: SpawnablePosition, bucket: SpawnBucket,
|
||||
spawnablePosition: SpawnablePosition,
|
||||
bucket: String,
|
||||
selectionData: SpawnSelectionData
|
||||
): SingleEntitySpawnAction<PokemonEntity> {
|
||||
val levelRange = pokemon.deriveLevelRange(levelRange)
|
||||
|
||||
+1
-2
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.spawning.detail
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.entity.SpawnEvent
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import net.minecraft.world.entity.Entity
|
||||
@@ -26,7 +25,7 @@ import net.minecraft.world.entity.MobSpawnType
|
||||
*/
|
||||
abstract class SingleEntitySpawnAction<T : Entity>(
|
||||
spawnablePosition: SpawnablePosition,
|
||||
bucket: SpawnBucket,
|
||||
bucket: String,
|
||||
detail: SpawnDetail
|
||||
) : SpawnAction<EntitySpawnResult>(spawnablePosition, bucket, detail) {
|
||||
abstract fun createEntity(): T?
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.detail
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
@@ -21,7 +20,7 @@ import java.util.concurrent.CompletableFuture
|
||||
*/
|
||||
abstract class SpawnAction<R>(
|
||||
val spawnablePosition: SpawnablePosition,
|
||||
val bucket: SpawnBucket,
|
||||
val bucket: String,
|
||||
open val detail: SpawnDetail
|
||||
) {
|
||||
val labels = mutableListOf<String>()
|
||||
|
||||
@@ -15,7 +15,6 @@ import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.api.ModDependant
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.queryStructOf
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.condition.CompositeSpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.multiplier.WeightMultiplier
|
||||
@@ -54,7 +53,7 @@ abstract class SpawnDetail : ModDependant {
|
||||
var displayName: String? = null
|
||||
@SerializedName("spawnablePositionType", alternate = ["context"])
|
||||
lateinit var spawnablePositionType: SpawnablePositionType<*>
|
||||
var bucket = SpawnBucket("", 0F)
|
||||
var bucket = ""
|
||||
var conditions = mutableListOf<SpawningCondition<*>>()
|
||||
var anticonditions = mutableListOf<SpawningCondition<*>>()
|
||||
var compositeCondition: CompositeSpawningCondition? = null
|
||||
@@ -79,7 +78,7 @@ abstract class SpawnDetail : ModDependant {
|
||||
"weight" to { DoubleValue(weight) },
|
||||
"percentage" to { DoubleValue(percentage) },
|
||||
"id" to { StringValue(id) },
|
||||
"bucket" to { StringValue(bucket.name) },
|
||||
"bucket" to { StringValue(bucket) },
|
||||
"width" to { DoubleValue(width.toDouble()) },
|
||||
"height" to { DoubleValue(height.toDouble()) },
|
||||
"spawnable_position_type" to { StringValue(spawnablePositionType.name) },
|
||||
@@ -136,6 +135,10 @@ abstract class SpawnDetail : ModDependant {
|
||||
containsNullValues = true
|
||||
LOGGER.error("Spawn Detail with id $id is invalid as it contains invalid values in its anticonditions (commonly caused by trailing comma in biomes or other arrays)")
|
||||
}
|
||||
if (bucket.isBlank()) {
|
||||
containsNullValues = true
|
||||
LOGGER.error("Spawn Detail with id $id is invalid as it does not have a bucket assigned")
|
||||
}
|
||||
return super.isModDependencySatisfied() && !containsNullValues
|
||||
}
|
||||
|
||||
@@ -163,7 +166,7 @@ abstract class SpawnDetail : ModDependant {
|
||||
*/
|
||||
fun choose(
|
||||
spawnablePosition: SpawnablePosition,
|
||||
bucket: SpawnBucket,
|
||||
bucket: String,
|
||||
selectionData: SpawnSelectionData
|
||||
): SpawnAction<*> {
|
||||
val action = createSpawnAction(spawnablePosition, bucket, selectionData)
|
||||
@@ -175,10 +178,10 @@ abstract class SpawnDetail : ModDependant {
|
||||
return action
|
||||
}
|
||||
|
||||
/** Instantiates an action at the given [SpawnablePosition] for the given [SpawnBucket]. */
|
||||
/** Instantiates an action at the given [SpawnablePosition] for the given spawn bucket. */
|
||||
protected abstract fun createSpawnAction(
|
||||
spawnablePosition: SpawnablePosition,
|
||||
bucket: SpawnBucket,
|
||||
bucket: String,
|
||||
selectionData: SpawnSelectionData
|
||||
): SpawnAction<*>
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.spawning.detail
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnLoader
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnSet
|
||||
import com.cobblemon.mod.common.api.spawning.condition.PrecalculationResult
|
||||
@@ -61,12 +60,13 @@ class SpawnPool(val name: String) : JsonDataRegistry<SpawnSet>, Iterable<SpawnDe
|
||||
}
|
||||
|
||||
val details = mutableListOf<SpawnDetail>()
|
||||
/** Tracks which buckets are in use by the pool, to prevent spawners for rolling buckets that don't exist. */
|
||||
val buckets = mutableListOf<String>()
|
||||
var precalculation: PrecalculationResult<*> = RootPrecalculation.generate(details, emptyList())
|
||||
val precalculators = mutableListOf<SpawningPrecalculation<*>>()
|
||||
|
||||
override fun iterator() = details.iterator()
|
||||
|
||||
|
||||
fun addPrecalculators(vararg precalculators: SpawningPrecalculation<*>): SpawnPool {
|
||||
this.precalculators.addAll(precalculators)
|
||||
precalculate()
|
||||
@@ -88,6 +88,9 @@ class SpawnPool(val name: String) : JsonDataRegistry<SpawnSet>, Iterable<SpawnDe
|
||||
* be slow, especially if there are many precalculators and spawns.
|
||||
*/
|
||||
fun precalculate() {
|
||||
buckets.clear()
|
||||
buckets.addAll(details.asSequence().map(SpawnDetail::bucket).distinct())
|
||||
|
||||
if (precalculators.isEmpty()) {
|
||||
precalculation = RootPrecalculation.generate(details, emptyList())
|
||||
} else {
|
||||
@@ -103,7 +106,7 @@ class SpawnPool(val name: String) : JsonDataRegistry<SpawnSet>, Iterable<SpawnDe
|
||||
* to leverage the precalculation to get a smaller list of spawns as quickly
|
||||
* as possible.
|
||||
*/
|
||||
fun retrieve(bucket: SpawnBucket, spawnablePosition: SpawnablePosition): List<SpawnDetail> {
|
||||
fun retrieve(bucket: String, spawnablePosition: SpawnablePosition): List<SpawnDetail> {
|
||||
return precalculation.retrieve(bucket, spawnablePosition)
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -145,7 +145,7 @@ class FishingSpawnCause(
|
||||
return
|
||||
}
|
||||
pokemonEntity.pokemon.isAlpha = true
|
||||
pokemonEntity.pokemon.initializeMovesetWithRandomTm(2)
|
||||
pokemonEntity.pokemon.initializeMovesetFromDefault()
|
||||
if (!pokemonEntity.level().isClientSide) {
|
||||
// Alpha status is applied after entity creation in bait flows, so rebuild the brain
|
||||
// to ensure alpha-only sensors/memories are actually attached.
|
||||
|
||||
+5
-5
@@ -8,19 +8,19 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.influence
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
|
||||
/**
|
||||
* A [SpawningInfluence] that multiplies bucket weights by the provided values
|
||||
* and then normalizes the resulting weights so that their total adds up to 100.
|
||||
*
|
||||
* No longer used by the mod, but kept for external suers.
|
||||
*/
|
||||
class BucketMultiplyingInfluence(
|
||||
val multipliers: Map<String, Float>,
|
||||
) : SpawningInfluence {
|
||||
override fun affectBucketWeights(bucketWeights: MutableMap<SpawnBucket, Float>) {
|
||||
val updates = mutableMapOf<SpawnBucket, Float>()
|
||||
override fun affectBucketWeights(bucketWeights: MutableMap<String, Float>) {
|
||||
val updates = mutableMapOf<String, Float>()
|
||||
for ((bucket, weight) in bucketWeights) {
|
||||
val multiplier = multipliers[bucket.name]
|
||||
val multiplier = multipliers[bucket]
|
||||
if (multiplier != null) {
|
||||
updates[bucket] = weight * multiplier
|
||||
}
|
||||
|
||||
+1
-2
@@ -8,7 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.influence
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import kotlin.math.pow
|
||||
|
||||
/**
|
||||
@@ -33,7 +32,7 @@ open class BucketNormalizingInfluence(
|
||||
val gradient: Float = 0.2F,
|
||||
val firstTier: Float = 1.29F
|
||||
) : SpawningInfluence {
|
||||
override fun affectBucketWeights(bucketWeights: MutableMap<SpawnBucket, Float>) {
|
||||
override fun affectBucketWeights(bucketWeights: MutableMap<String, Float>) {
|
||||
if (tier == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
+1
-1
@@ -150,7 +150,7 @@ class SaccharineLogSlatheredInfluence(val pos: BlockPos? = null) : SpawningInflu
|
||||
val chance = Cobblemon.config.honeySlatherAlphaChance
|
||||
if (chance > 0 && Random.nextInt(chance) == 0) {
|
||||
pokemon.isAlpha = true
|
||||
pokemon.initializeMovesetWithRandomTm(2)
|
||||
pokemon.initializeMovesetFromDefault()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
-1
@@ -21,6 +21,8 @@ import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.AlphaLevelMatchingSensor
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.entity.Entity
|
||||
|
||||
/**
|
||||
@@ -47,6 +49,13 @@ open class SpawnBaitInfluence(val effects: List<SpawnBait.Effect>, val onUsed: (
|
||||
Effects.getEffectFunction(effect.type)?.invoke(entity, effect)
|
||||
}
|
||||
}
|
||||
|
||||
if (entity.pokemon.isAlpha) {
|
||||
val spawningPlayer = action.spawnablePosition.cause.entity as? ServerPlayer
|
||||
if (spawningPlayer != null) {
|
||||
AlphaLevelMatchingSensor.applyLevelMatching(entity, spawningPlayer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,4 +139,4 @@ open class SpawnBaitInfluence(val effects: List<SpawnBait.Effect>, val onUsed: (
|
||||
if (used) markUsed()
|
||||
return super.affectWeight(detail, spawnablePosition, newWeight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-4
@@ -9,7 +9,6 @@
|
||||
package com.cobblemon.mod.common.api.spawning.influence
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.BestSpawner
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
@@ -39,9 +38,9 @@ interface SpawningInfluence {
|
||||
/** Applies some influence over the entity that's been spawned. */
|
||||
fun affectSpawn(action: SpawnAction<*>, entity: Entity) {}
|
||||
/** Applies some influence over the weight of spawn buckets. */
|
||||
fun affectBucketWeights(bucketWeights: MutableMap<SpawnBucket, Float>) {}
|
||||
fun affectBucketWeights(bucketWeights: MutableMap<String, Float>) {}
|
||||
/** Normalizes all bucket weights to add up to 100 */
|
||||
fun normalizeBucketWeights(bucketWeights: MutableMap<SpawnBucket, Float>) {
|
||||
fun normalizeBucketWeights(bucketWeights: MutableMap<String, Float>) {
|
||||
val sum = bucketWeights.values.sum()
|
||||
val to100 = 100F / sum
|
||||
bucketWeights.keys.toList().forEach {
|
||||
@@ -56,5 +55,5 @@ interface SpawningInfluence {
|
||||
*
|
||||
* You should make sure the spawn position satisfies any spawn details that you inject.
|
||||
*/
|
||||
fun injectSpawns(bucket: SpawnBucket, spawnablePosition: SpawnablePosition): List<SpawnDetail>? = null
|
||||
fun injectSpawns(bucket: String, spawnablePosition: SpawnablePosition): List<SpawnDetail>? = null
|
||||
}
|
||||
+28
-6
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.spawning.preset
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.api.spawning.BestSpawner
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePositionType
|
||||
import com.cobblemon.mod.common.util.adapters.RegisteredSpawnablePositionAdapter
|
||||
import com.google.gson.GsonBuilder
|
||||
@@ -39,11 +38,34 @@ class BestSpawnerConfig {
|
||||
"surface" to 0.01F,
|
||||
"seafloor" to 1F
|
||||
)
|
||||
val buckets = mutableListOf(
|
||||
SpawnBucket("common", 94.3F),
|
||||
SpawnBucket("uncommon", 5F),
|
||||
SpawnBucket("rare", 0.5F),
|
||||
SpawnBucket("ultra-rare", 0.2F)
|
||||
|
||||
val worldBuckets = mutableMapOf(
|
||||
"common" to 94F,
|
||||
"uncommon" to 5F,
|
||||
"rare" to 0.5F,
|
||||
"ultra-rare" to 0.2F,
|
||||
"boss" to 0.3F,
|
||||
)
|
||||
|
||||
val fishingBuckets = mutableMapOf(
|
||||
"common" to 83.25F,
|
||||
"uncommon" to 11.25F,
|
||||
"rare" to 4.125F,
|
||||
"ultra-rare" to 1.375F
|
||||
)
|
||||
|
||||
val activatedHabitatBuckets: MutableMap<String, Float> = mutableMapOf(
|
||||
"common" to 83.25F,
|
||||
"uncommon" to 11.25F,
|
||||
"rare" to 4.125F,
|
||||
"ultra-rare" to 1.375F
|
||||
)
|
||||
|
||||
val pokeSnackBuckets = mutableMapOf(
|
||||
"common" to 83.25F,
|
||||
"uncommon" to 11.25F,
|
||||
"rare" to 4.125F,
|
||||
"ultra-rare" to 1.375F
|
||||
)
|
||||
|
||||
companion object {
|
||||
|
||||
+2
-3
@@ -8,13 +8,12 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.preset
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnDetailPresets
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnLoader
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePositionType
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.multiplier.WeightMultiplier
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePositionType
|
||||
import com.cobblemon.mod.common.util.MergeMode
|
||||
import com.cobblemon.mod.common.util.adapters.SpawnDetailAdapter
|
||||
import com.google.gson.JsonObject
|
||||
@@ -41,7 +40,7 @@ import com.google.gson.annotations.SerializedName
|
||||
* @since July 8th, 2022
|
||||
*/
|
||||
abstract class SpawnDetailPreset {
|
||||
var bucket: SpawnBucket? = null
|
||||
var bucket: String? = null
|
||||
var spawnDetailType: String? = null
|
||||
@SerializedName("spawnablePositionType", alternate = ["context"])
|
||||
var spawnablePositionType: SpawnablePositionType<*>? = null
|
||||
|
||||
+6
-7
@@ -9,7 +9,6 @@
|
||||
package com.cobblemon.mod.common.api.spawning.selection
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
@@ -88,12 +87,12 @@ open class FlatSelector : SpawningSelector<FlatSelector.SpawnablePositionSelecti
|
||||
class SpawnablePositionSelectionData(
|
||||
val spawner: Spawner,
|
||||
val spawnablePositions: List<SpawnablePosition>,
|
||||
val selectingSpawnInformation: MutableMap<SpawnBucket, SpawnablePositionBucketSelectionData>,
|
||||
val selectingSpawnInformation: MutableMap<String, SpawnablePositionBucketSelectionData>,
|
||||
): SpawnSelectionData {
|
||||
override val spawnActions = mutableListOf<SpawnAction<*>>()
|
||||
override val context = mutableMapOf<String, Any>()
|
||||
|
||||
fun getDataForBucket(bucket: SpawnBucket): SpawnablePositionBucketSelectionData {
|
||||
fun getDataForBucket(bucket: String): SpawnablePositionBucketSelectionData {
|
||||
val existing = selectingSpawnInformation[bucket]
|
||||
if (existing != null) {
|
||||
return existing
|
||||
@@ -130,14 +129,14 @@ open class FlatSelector : SpawningSelector<FlatSelector.SpawnablePositionSelecti
|
||||
spawner: Spawner,
|
||||
spawnablePositions: List<SpawnablePosition>
|
||||
): SpawnablePositionSelectionData {
|
||||
val selectingSpawnInformation: MutableMap<SpawnBucket, SpawnablePositionBucketSelectionData> = mutableMapOf()
|
||||
val selectingSpawnInformation: MutableMap<String, SpawnablePositionBucketSelectionData> = mutableMapOf()
|
||||
|
||||
return SpawnablePositionSelectionData(spawner, spawnablePositions, selectingSpawnInformation)
|
||||
}
|
||||
|
||||
override fun selectSpawnAction(
|
||||
spawner: Spawner,
|
||||
bucket: SpawnBucket,
|
||||
bucket: String,
|
||||
selectionData: SpawnablePositionSelectionData,
|
||||
): SpawnAction<*>? {
|
||||
val selectingSpawnInformation = selectionData.getDataForBucket(bucket) ?: return null
|
||||
@@ -149,7 +148,7 @@ open class FlatSelector : SpawningSelector<FlatSelector.SpawnablePositionSelecti
|
||||
if (percentSum > 100) {
|
||||
Cobblemon.LOGGER.warn(
|
||||
"""
|
||||
A spawn list for ${spawner.name} exceeded 100% on percentage sums in bucket ${bucket.name}...
|
||||
A spawn list for ${spawner.name} exceeded 100% on percentage sums in bucket $bucket...
|
||||
This means you don't understand how this option works.
|
||||
""".trimIndent()
|
||||
)
|
||||
@@ -186,7 +185,7 @@ open class FlatSelector : SpawningSelector<FlatSelector.SpawnablePositionSelecti
|
||||
|
||||
override fun getTotalWeights(
|
||||
spawner: Spawner,
|
||||
bucket: SpawnBucket,
|
||||
bucket: String,
|
||||
spawnablePositions: List<SpawnablePosition>
|
||||
): Map<SpawnDetail, Float> {
|
||||
val selectionData = getSelectionData(spawner, spawnablePositions).getDataForBucket(bucket)
|
||||
|
||||
+5
-6
@@ -9,7 +9,6 @@
|
||||
package com.cobblemon.mod.common.api.spawning.selection
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
@@ -105,7 +104,7 @@ open class FlatSpawnablePositionWeightedSelector : SpawningSelector<FlatSpawnabl
|
||||
override val spawnActions = mutableListOf<SpawnAction<*>>()
|
||||
override val context = mutableMapOf<String, Any>()
|
||||
|
||||
val bucketData = mutableMapOf<SpawnBucket, MutableMap<SpawnablePositionType<*>, SpawnablePositionSelectionData>>()
|
||||
val bucketData = mutableMapOf<String, MutableMap<SpawnablePositionType<*>, SpawnablePositionSelectionData>>()
|
||||
|
||||
override fun removeSpawnDetails(shouldRemove: (SpawnDetail) -> Boolean) {
|
||||
bucketData.values.forEach {
|
||||
@@ -121,7 +120,7 @@ open class FlatSpawnablePositionWeightedSelector : SpawningSelector<FlatSpawnabl
|
||||
}
|
||||
}
|
||||
|
||||
fun getDataForBucket(bucket: SpawnBucket): MutableMap<SpawnablePositionType<*>, SpawnablePositionSelectionData> {
|
||||
fun getDataForBucket(bucket: String): MutableMap<SpawnablePositionType<*>, SpawnablePositionSelectionData> {
|
||||
bucketData[bucket]?.let { return it }
|
||||
|
||||
val spawnablePositionTypeToSpawns = mutableMapOf<SpawnablePositionType<*>, SpawnablePositionSelectionData>()
|
||||
@@ -149,7 +148,7 @@ open class FlatSpawnablePositionWeightedSelector : SpawningSelector<FlatSpawnabl
|
||||
|
||||
override fun selectSpawnAction(
|
||||
spawner: Spawner,
|
||||
bucket: SpawnBucket,
|
||||
bucket: String,
|
||||
selectionData: SeparatedSelectionData
|
||||
): SpawnAction<*>? {
|
||||
val bucketData = selectionData.getDataForBucket(bucket)
|
||||
@@ -172,7 +171,7 @@ open class FlatSpawnablePositionWeightedSelector : SpawningSelector<FlatSpawnabl
|
||||
if (percentSum > 100) {
|
||||
LOGGER.warn(
|
||||
"""
|
||||
A spawn list for ${spawner.name} exceeded 100% on percentage sums in bucket ${bucket.name}...
|
||||
A spawn list for ${spawner.name} exceeded 100% on percentage sums in bucket $bucket...
|
||||
This means you don't understand how this option works.
|
||||
""".trimIndent()
|
||||
)
|
||||
@@ -244,7 +243,7 @@ open class FlatSpawnablePositionWeightedSelector : SpawningSelector<FlatSpawnabl
|
||||
|
||||
override fun getTotalWeights(
|
||||
spawner: Spawner,
|
||||
bucket: SpawnBucket,
|
||||
bucket: String,
|
||||
spawnablePositions: List<SpawnablePosition>
|
||||
): Map<SpawnDetail, Float> {
|
||||
val selectionData = getSelectionData(spawner, spawnablePositions).getDataForBucket(bucket)
|
||||
|
||||
+4
-5
@@ -8,7 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.selection
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
@@ -33,13 +32,13 @@ interface SpawningSelector<T : SpawnSelectionData> {
|
||||
|
||||
fun selectSpawnAction(
|
||||
spawner: Spawner,
|
||||
bucket: SpawnBucket,
|
||||
bucket: String,
|
||||
selectionData: T
|
||||
): SpawnAction<*>?
|
||||
|
||||
fun select(
|
||||
spawner: Spawner,
|
||||
bucketSelector: () -> SpawnBucket,
|
||||
bucketSelector: () -> String,
|
||||
spawnablePositions: List<SpawnablePosition>,
|
||||
maxSpawns: Int
|
||||
): List<SpawnAction<*>> {
|
||||
@@ -65,7 +64,7 @@ interface SpawningSelector<T : SpawnSelectionData> {
|
||||
return spawnActions
|
||||
}
|
||||
|
||||
fun getProbabilities(spawner: Spawner, bucket: SpawnBucket, spawnablePositions: List<SpawnablePosition>): Map<SpawnDetail, Float> {
|
||||
fun getProbabilities(spawner: Spawner, bucket: String, spawnablePositions: List<SpawnablePosition>): Map<SpawnDetail, Float> {
|
||||
val weights = getTotalWeights(spawner, bucket, spawnablePositions)
|
||||
val totalWeight = weights.values.sum()
|
||||
val percentages = mutableMapOf<SpawnDetail, Float>()
|
||||
@@ -73,5 +72,5 @@ interface SpawningSelector<T : SpawnSelectionData> {
|
||||
return percentages
|
||||
}
|
||||
|
||||
fun getTotalWeights(spawner: Spawner, bucket: SpawnBucket, spawnablePositions: List<SpawnablePosition>): Map<SpawnDetail, Float>
|
||||
fun getTotalWeights(spawner: Spawner, bucket: String, spawnablePositions: List<SpawnablePosition>): Map<SpawnDetail, Float>
|
||||
}
|
||||
+2
-1
@@ -25,7 +25,8 @@ import com.cobblemon.mod.common.api.spawning.selection.SpawningSelector
|
||||
open class BasicSpawner(
|
||||
override val name: String,
|
||||
override var spawnPool: SpawnPool,
|
||||
override var maxPokemonPerChunk: Float = Cobblemon.config.pokemonPerChunk
|
||||
override var maxPokemonPerChunk: Float = Cobblemon.config.pokemonPerChunk,
|
||||
override val buckets: Map<String, Float>
|
||||
) : Spawner {
|
||||
override var selector: SpawningSelector<*> = SpawningSelector.DEFAULT
|
||||
override var generator: SpawningZoneGenerator = Cobblemon.spawningZoneGenerator
|
||||
|
||||
+4
-11
@@ -8,11 +8,11 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.spawner
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonPoiTypes
|
||||
import com.cobblemon.mod.common.api.spawning.BestSpawner
|
||||
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
|
||||
import com.cobblemon.mod.common.api.spawning.influence.BucketMultiplyingInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.BucketNormalizingInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.PlayerLevelRangeInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.PlayerLevelRangeInfluence.Companion.TYPICAL_VARIATION
|
||||
@@ -72,16 +72,9 @@ object FishingSpawnerFactory {
|
||||
fun createSharedSpawner(): BasicSpawner {
|
||||
return BasicSpawner(
|
||||
name = "fishing",
|
||||
spawnPool = spawnPool
|
||||
).also { spawner ->
|
||||
spawner.influences += BucketMultiplyingInfluence(
|
||||
multipliers = mapOf(
|
||||
"uncommon" to 2.25f,
|
||||
"rare" to 5.5f,
|
||||
"ultra-rare" to 5.5f,
|
||||
)
|
||||
)
|
||||
}
|
||||
spawnPool = spawnPool,
|
||||
buckets = Cobblemon.bestSpawner.config.fishingBuckets,
|
||||
)
|
||||
}
|
||||
|
||||
fun findHabitatInfluences(world: ServerLevel, position: BlockPos, spawner: Spawner): Array<HabitatBlockEntity> {
|
||||
|
||||
+3
-2
@@ -11,8 +11,8 @@ package com.cobblemon.mod.common.api.spawning.spawner
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnCause
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
|
||||
/**
|
||||
* A spawner that works within a fixed area. Instances provide the center point and the radii and then the spawner
|
||||
@@ -24,12 +24,13 @@ import net.minecraft.core.BlockPos
|
||||
open class FixedAreaSpawner(
|
||||
name: String,
|
||||
spawnPool: SpawnPool,
|
||||
buckets: Map<String, Float>,
|
||||
val world: ServerLevel,
|
||||
val position: BlockPos,
|
||||
val horizontalRadius: Int,
|
||||
val verticalRadius: Int,
|
||||
maxPokemonPerChunk: Float = Cobblemon.config.pokemonPerChunk
|
||||
) : BasicSpawner(name, spawnPool, maxPokemonPerChunk) {
|
||||
) : BasicSpawner(name, spawnPool, maxPokemonPerChunk, buckets) {
|
||||
fun run(cause: SpawnCause, maxSpawns: Int? = null): List<Any> {
|
||||
return runForArea(
|
||||
zoneInput = getZoneInput(cause),
|
||||
|
||||
+8
-6
@@ -8,20 +8,21 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.spawner
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.Cobblemon.config
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnCause
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import com.cobblemon.mod.common.util.nextBetween
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.util.Mth.PI
|
||||
import net.minecraft.util.Mth.ceil
|
||||
import java.util.*
|
||||
import java.util.UUID
|
||||
import kotlin.math.atan
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
import kotlin.random.Random
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.util.Mth.PI
|
||||
import net.minecraft.util.Mth.ceil
|
||||
|
||||
/**
|
||||
* A spawner that works around a single player. It will do basic tracking of a player's speed
|
||||
@@ -35,7 +36,8 @@ open class PlayerSpawner(
|
||||
spawnPool: SpawnPool
|
||||
) : BasicSpawner(
|
||||
name = player.name.string,
|
||||
spawnPool = spawnPool
|
||||
spawnPool = spawnPool,
|
||||
buckets = Cobblemon.bestSpawner.config.worldBuckets
|
||||
) {
|
||||
val uuid: UUID = player.uuid
|
||||
|
||||
|
||||
+2
-10
@@ -8,11 +8,11 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.spawner
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.fishing.SpawnBait
|
||||
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
|
||||
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnRules
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
|
||||
import com.cobblemon.mod.common.api.spawning.influence.BucketMultiplyingInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.BucketNormalizingInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawnBaitInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
|
||||
@@ -61,15 +61,6 @@ object PokeSnackSpawnerFactory {
|
||||
firstTier = 1.2F
|
||||
)
|
||||
},
|
||||
{ _ ->
|
||||
BucketMultiplyingInfluence(
|
||||
mapOf(
|
||||
"uncommon" to 2.25f,
|
||||
"rare" to 5.5f,
|
||||
"ultra-rare" to 5.5f,
|
||||
)
|
||||
)
|
||||
},
|
||||
{ ctx ->
|
||||
SpawnBaitInfluence(effects = ctx.baitEffects)
|
||||
},
|
||||
@@ -81,6 +72,7 @@ object PokeSnackSpawnerFactory {
|
||||
return FixedAreaSpawner(
|
||||
name = ctx.name,
|
||||
spawnPool = spawnPool,
|
||||
buckets = Cobblemon.bestSpawner.config.pokeSnackBuckets,
|
||||
world = ctx.world,
|
||||
position = ctx.pos,
|
||||
horizontalRadius = ctx.horizontalRadius,
|
||||
|
||||
@@ -12,7 +12,6 @@ import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.entity.SpawnBucketChosenEvent
|
||||
import com.cobblemon.mod.common.api.spawning.BestSpawner
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnCause
|
||||
import com.cobblemon.mod.common.api.spawning.SpawningZoneGenerator
|
||||
import com.cobblemon.mod.common.api.spawning.detail.EntitySpawnResult
|
||||
@@ -26,6 +25,7 @@ import com.cobblemon.mod.common.api.spawning.position.calculators.SpawnablePosit
|
||||
import com.cobblemon.mod.common.api.spawning.selection.SpawningSelector
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.isBoxLoaded
|
||||
import com.cobblemon.mod.common.util.removeIf
|
||||
import com.cobblemon.mod.common.util.squeezeWithinBounds
|
||||
import com.cobblemon.mod.common.util.toVec3f
|
||||
import com.cobblemon.mod.common.util.weightedSelection
|
||||
@@ -52,6 +52,7 @@ import net.minecraft.world.phys.Vec3
|
||||
interface Spawner {
|
||||
val name: String
|
||||
val influences: MutableList<SpawningInfluence>
|
||||
val buckets: Map<String, Float>
|
||||
var spawnPool: SpawnPool
|
||||
var selector: SpawningSelector<*>
|
||||
var generator: SpawningZoneGenerator
|
||||
@@ -65,7 +66,7 @@ interface Spawner {
|
||||
const val ENTITY_LIMIT_CHUNK_RANGE = 3
|
||||
}
|
||||
|
||||
fun getMatchingSpawns(bucket: SpawnBucket, spawnablePosition: SpawnablePosition): List<SpawnDetail> {
|
||||
fun getMatchingSpawns(bucket: String, spawnablePosition: SpawnablePosition): List<SpawnDetail> {
|
||||
val spawns = mutableListOf<SpawnDetail>()
|
||||
spawns.addAll(spawnPool.retrieve(bucket, spawnablePosition).filter { it.isSatisfiedBy(spawnablePosition) })
|
||||
spawnablePosition.influences.forEach { influence ->
|
||||
@@ -172,11 +173,12 @@ interface Spawner {
|
||||
|
||||
fun copyInfluences() = influences.filter { !it.isExpired() }.toMutableList()
|
||||
|
||||
fun chooseBucket(cause: SpawnCause, influences: List<SpawningInfluence>): SpawnBucket {
|
||||
val buckets = Cobblemon.bestSpawner.config.buckets
|
||||
val bucketWeights = buckets.associateWith { it.weight }.toMutableMap()
|
||||
fun chooseBucket(cause: SpawnCause, influences: List<SpawningInfluence>): String {
|
||||
val bucketWeights = buckets.toMutableMap()
|
||||
influences.forEach { it.affectBucketWeights(bucketWeights) }
|
||||
val bucket = bucketWeights.entries.weightedSelection { it.value }?.key ?: buckets.first()
|
||||
val usedBuckets = spawnPool.buckets.intersect(bucketWeights.keys)
|
||||
bucketWeights.removeIf { it.key !in usedBuckets }
|
||||
val bucket = bucketWeights.entries.weightedSelection { it.value }?.key ?: buckets.keys.first()
|
||||
val event = SpawnBucketChosenEvent(
|
||||
spawner = this,
|
||||
spawnCause = cause,
|
||||
|
||||
@@ -51,6 +51,8 @@ object CobblemonBiomeTags {
|
||||
@JvmField
|
||||
val IS_FUNGUS = create("nether/is_fungus")
|
||||
@JvmField
|
||||
val IS_WASTELAND = create("nether/is_wasteland")
|
||||
@JvmField
|
||||
val IS_GLACIAL = create("is_glacial")
|
||||
@JvmField
|
||||
val IS_TEMPERATE_OCEAN = create("is_temperate_ocean")
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.moves.adapters.MoveTemplateAdapter
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.net.messages.client.tms.TechnicalMachineRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.util.adapters.TMObtainMethodAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
@@ -70,5 +71,7 @@ object TechnicalMachines : JsonDataRegistry<TechnicalMachine> {
|
||||
|
||||
fun getByResourceLocation(resourceLocation: ResourceLocation) = tmMap[resourceLocation]
|
||||
fun getAllResourceLocations() = tmMap.keys.toSet()
|
||||
override fun sync(player: ServerPlayer) {}
|
||||
override fun sync(player: ServerPlayer) {
|
||||
TechnicalMachineRegistrySyncPacket(tmMap.values.toList()).sendToPlayer(player)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,11 +349,12 @@ object BattleBuilder {
|
||||
npcPartyStores.add(npcParty)
|
||||
}
|
||||
|
||||
if (playerActor.pokemonList.size < battleFormat.battleType.slotsPerActor) {
|
||||
val playerAvailablePokemon = playerActor.pokemonList.count { it.health > 0 }
|
||||
if (playerAvailablePokemon < battleFormat.battleType.slotsPerActor) {
|
||||
errors.participantErrors[playerActor] += BattleStartError.insufficientPokemon(
|
||||
actorEntity = player,
|
||||
requiredCount = battleFormat.battleType.slotsPerActor,
|
||||
hadCount = playerActor.pokemonList.size
|
||||
hadCount = playerAvailablePokemon
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ object ShowdownInterpreter {
|
||||
updateInstructionParser["-endability"] = { _, _, message, _ -> EndAbilityInstruction(message) }
|
||||
updateInstructionParser["-end"] = { _, _, message, _ -> EndInstruction(message) }
|
||||
updateInstructionParser["-enditem"] = { _, _, message, _ -> EndItemInstruction(message) }
|
||||
updateInstructionParser["-fail"] = { _, _, message, _ -> FailInstruction(message) }
|
||||
updateInstructionParser["-fail"] = { battle, _, message, _ -> FailInstruction(battle, message) }
|
||||
updateInstructionParser["faint"] = { battle, _, message, _ -> FaintInstruction(battle, message) }
|
||||
updateInstructionParser["-fieldactivate"] = { _, _, message, _ -> FieldActivateInstruction(message) }
|
||||
updateInstructionParser["-fieldend"] = { _, _, message, _ -> FieldEndInstruction(message) }
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.cobblemon.mod.common.battles.PassActionResponse
|
||||
import com.cobblemon.mod.common.battles.ShowdownActionResponse
|
||||
import com.cobblemon.mod.common.battles.ShowdownMoveset
|
||||
import com.cobblemon.mod.common.battles.SwitchActionResponse
|
||||
import com.cobblemon.mod.common.exception.IllegalActionChoiceException
|
||||
|
||||
/**
|
||||
* AI that randomly chooses a move from its moveset at a random target.
|
||||
@@ -40,17 +41,19 @@ class RandomBattleAI : BattleAI {
|
||||
return SwitchActionResponse(switchTo.uuid)
|
||||
}
|
||||
|
||||
if (moveset == null) {
|
||||
if (moveset == null)
|
||||
return PassActionResponse
|
||||
|
||||
val move = moveset.moves.filter { it.canBeUsed() }.randomOrNull()
|
||||
if (move == null) {
|
||||
if (moveset.moves.any { it.id == "struggle" })
|
||||
return MoveActionResponse("struggle")
|
||||
else throw IllegalActionChoiceException(activeBattlePokemon.actor,
|
||||
"No valid AI move choices, but Showdown never injected Struggle!")
|
||||
}
|
||||
val move = moveset.moves
|
||||
.filter { it.canBeUsed() }
|
||||
.filter { it.mustBeUsed() || it.target.targetList(activeBattlePokemon)?.isEmpty() != true }
|
||||
.randomOrNull()
|
||||
?: return MoveActionResponse("struggle")
|
||||
|
||||
val target = if (move.mustBeUsed()) null else move.target.targetList(activeBattlePokemon)
|
||||
return if (target == null) {
|
||||
return if (target.isNullOrEmpty()) {
|
||||
MoveActionResponse(move.id)
|
||||
} else {
|
||||
// prioritize opponents rather than allies
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.cobblemon.mod.common.battles.ai.strongBattleAI.AIUtility
|
||||
import com.cobblemon.mod.common.battles.ai.strongBattleAI.ActiveTracker
|
||||
import com.cobblemon.mod.common.battles.ai.strongBattleAI.TrackerPokemon
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.exception.IllegalActionChoiceException
|
||||
import com.cobblemon.mod.common.pokemon.status.PersistentStatus
|
||||
import kotlin.random.Random
|
||||
import kotlin.collections.associateWith
|
||||
@@ -94,14 +95,14 @@ class StrongBattleAI(skill: Int) : BattleAI {
|
||||
return null to 0.0 // we do not care about moves that dont target opponents during damage calculations
|
||||
val singleTargetList = move.first.target.targetList.invoke(attacker)?.map { it as ActiveBattlePokemon }?.filter { it.battlePokemon != null }
|
||||
val multiTargetList = attacker.getMultiTargetList(move.first.target)?.map { it as ActiveBattlePokemon }?.filter { it.battlePokemon != null }
|
||||
if (singleTargetList != null && singleTargetList.isNotEmpty()) {
|
||||
if (!singleTargetList.isNullOrEmpty()) {
|
||||
// figure out target that will take the most damage
|
||||
val potentialTargets = activeTracker.opponentSide.activePokemon
|
||||
.filter { opp -> opp.id in singleTargetList.map { it.battlePokemon!!.uuid } }
|
||||
.map { opp -> singleTargetList.first { it.battlePokemon!!.uuid == opp.id} to calculateDamage(move.first, attackerTracker, opp) }
|
||||
|
||||
return potentialTargets.maxBy { it.second }
|
||||
} else if (multiTargetList != null && multiTargetList.isNotEmpty()) {
|
||||
} else if (!multiTargetList.isNullOrEmpty()) {
|
||||
val (allies, opponents) = multiTargetList.partition { it.isAllied(attacker) }
|
||||
// weigh up allied damage against opponents
|
||||
val totalOpponentDamage = activeTracker.opponentSide.activePokemon
|
||||
@@ -188,10 +189,8 @@ class StrongBattleAI(skill: Int) : BattleAI {
|
||||
|
||||
// function for finding the most damaging moves in the moveset
|
||||
fun mostDamagingMove(selectedMove: InBattleMove, moveset: ShowdownMoveset?, mon: TrackerPokemon, opponent: TrackerPokemon): Boolean {
|
||||
|
||||
if (moveset != null) {
|
||||
for (move in moveset.moves.filter { !it.disabled && it.id != selectedMove.id}) {
|
||||
|
||||
if (calculateDamage(move, mon, opponent) > calculateDamage(selectedMove, mon, opponent)) {
|
||||
return false
|
||||
}
|
||||
@@ -251,18 +250,19 @@ class StrongBattleAI(skill: Int) : BattleAI {
|
||||
|
||||
val availableMoves = moveset.moves.filter { it.canBeUsed() }.map { Pair(it, Moves.getByName(it.id)!!)}
|
||||
|
||||
if (!checkSkillLevel()){
|
||||
val move = availableMoves
|
||||
.filter { it.first.target.targetList(activeBattlePokemon)?.isEmpty() != true }
|
||||
.randomOrNull()
|
||||
?: return MoveActionResponse("struggle")
|
||||
|
||||
return chooseMove(move.first, activeBattlePokemon)
|
||||
val randomMove = availableMoves.randomOrNull()
|
||||
if (randomMove == null) {
|
||||
if (moveset.moves.any { it.id == "struggle" })
|
||||
return MoveActionResponse("struggle")
|
||||
else throw IllegalActionChoiceException(activeBattlePokemon.actor,
|
||||
"No valid AI move choices, but Showdown never injected Struggle!")
|
||||
}
|
||||
|
||||
if (!checkSkillLevel()) return chooseMove(randomMove.first, activeBattlePokemon)
|
||||
|
||||
// switch out based on current matchup on the field
|
||||
if (checkSwitchOutSkill() && shouldSwitchOut(aiSide, battle, activeBattlePokemon, moveset)) {
|
||||
considerSwitching(activeBattlePokemon, activeTrackerPokemon, opponents, availableMoves, availableSwitches, battle, aiSide)
|
||||
considerSwitching(activeBattlePokemon, activeTrackerPokemon, opponents, availableMoves, availableSwitches, battle, aiSide)?.let { return it }
|
||||
}
|
||||
|
||||
// If HP is below 30% and not switching out, always use the most damaging move
|
||||
@@ -358,7 +358,7 @@ class StrongBattleAI(skill: Int) : BattleAI {
|
||||
}
|
||||
}
|
||||
|
||||
considerAntiBoost(activeBattlePokemon, activeTrackerPokemon, opponents, availableMoves)
|
||||
considerAntiBoost(activeBattlePokemon, activeTrackerPokemon, opponents, availableMoves)?.let { return it }
|
||||
|
||||
// Court Change
|
||||
for (move in availableMoves) {
|
||||
@@ -414,7 +414,7 @@ class StrongBattleAI(skill: Int) : BattleAI {
|
||||
}
|
||||
}
|
||||
|
||||
considerInflictingStatus(activeBattlePokemon, activeTrackerPokemon, opponents, availableMoves, battle, aiSide)
|
||||
considerInflictingStatus(activeBattlePokemon, activeTrackerPokemon, opponents, availableMoves, battle, aiSide)?.let { return it }
|
||||
|
||||
// Protect style moves
|
||||
for (move in availableMoves) {
|
||||
@@ -430,7 +430,7 @@ class StrongBattleAI(skill: Int) : BattleAI {
|
||||
}
|
||||
}
|
||||
|
||||
findAndUseMostDamagingMove(activeBattlePokemon, activeTrackerPokemon, opponents, availableMoves, moveset, battle, aiSide)
|
||||
findAndUseMostDamagingMove(activeBattlePokemon, activeTrackerPokemon, opponents, availableMoves, moveset, battle, aiSide)?.let { return it }
|
||||
}
|
||||
|
||||
// healing wish (dealing with it here because you'd only use it if you should switch out anyway)
|
||||
@@ -450,13 +450,7 @@ class StrongBattleAI(skill: Int) : BattleAI {
|
||||
}
|
||||
activeTrackerPokemon.firstTurn = false
|
||||
|
||||
val move = availableMoves
|
||||
.filter { it.first.canBeUsed() }
|
||||
.filter { it.first.mustBeUsed() || it.first.target.targetList(activeBattlePokemon)?.isEmpty() != true }
|
||||
.randomOrNull()
|
||||
?: return MoveActionResponse("struggle")
|
||||
|
||||
return chooseMove(move.first, activeBattlePokemon)
|
||||
return chooseMove(randomMove.first, activeBattlePokemon)
|
||||
}
|
||||
|
||||
// estimate mid-battle switch in value
|
||||
@@ -978,12 +972,12 @@ class StrongBattleAI(skill: Int) : BattleAI {
|
||||
|
||||
val bestMove = moveValues.maxBy { it.value.second }
|
||||
val target = if (bestMove.key.first.mustBeUsed()) null else bestMove.value.first
|
||||
return MoveActionResponse(bestMove.key.first.id, target?.getPNX())
|
||||
return chooseMove(bestMove.key.first, activeBattlePokemon, target)
|
||||
}
|
||||
|
||||
private fun chooseMove(move: InBattleMove, activeBattlePokemon: ActiveBattlePokemon, target: ActiveBattlePokemon? = null): MoveActionResponse {
|
||||
val targets = if (move.mustBeUsed()) null else move.target.targetList(activeBattlePokemon)
|
||||
if (targets == null)
|
||||
if (targets.isNullOrEmpty())
|
||||
return MoveActionResponse(move.id)
|
||||
else {
|
||||
val chosenTarget = target ?: targets.filter { !it.isAllied(activeBattlePokemon) }.randomOrNull() ?: targets.random()
|
||||
|
||||
+4
-2
@@ -163,7 +163,8 @@ class TrackerActor(val uuid: UUID) {
|
||||
//we assume that AI knows it all about our allies
|
||||
trackerActor.party.addAll(actor.pokemonList.map { createAllied(it) })
|
||||
actor.activePokemon.forEach {
|
||||
val active = trackerActor.party.first { trackMon -> trackMon.id == it.battlePokemon!!.uuid }
|
||||
val battlePokemon = it.battlePokemon ?: return@forEach
|
||||
val active = trackerActor.party.first { trackMon -> trackMon.id == battlePokemon.uuid }
|
||||
trackerActor.party.remove(active)
|
||||
trackerActor.activePokemon.add(active)
|
||||
}
|
||||
@@ -176,7 +177,8 @@ class TrackerActor(val uuid: UUID) {
|
||||
//only know basics of what we see right now
|
||||
trackerActor.party.addAll(actor.pokemonList.map { createOpposing(it) })
|
||||
actor.activePokemon.forEach {
|
||||
val active = trackerActor.party.first { trackMon -> trackMon.id == it.battlePokemon!!.uuid }
|
||||
val battlePokemon = it.battlePokemon ?: return@forEach
|
||||
val active = trackerActor.party.first { trackMon -> trackMon.id == battlePokemon.uuid }
|
||||
trackerActor.party.remove(active)
|
||||
trackerActor.activePokemon.add(active)
|
||||
}
|
||||
|
||||
+2
-2
@@ -22,8 +22,8 @@ import com.cobblemon.mod.common.util.battleLang
|
||||
* @author Hunter
|
||||
* @since September 25th, 2022
|
||||
*/
|
||||
class FailInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
class FailInstruction(val battle: PokemonBattle, val message: BattleMessage): InterpreterInstruction {
|
||||
val target = message.battlePokemon(0, battle)
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchWaiting(1.5F){
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatchWaiting
|
||||
|
||||
+11
@@ -137,6 +137,8 @@ class MoveInstruction(
|
||||
|
||||
val subsequentInstructions = instructionSet.findInstructionsCausedBy(this)
|
||||
val missedTargets = subsequentInstructions.filterIsInstance<MissInstruction>().mapNotNull { it.target }
|
||||
val failed = subsequentInstructions.any { it is FailInstruction }
|
||||
val failedTargets = subsequentInstructions.filterIsInstance<FailInstruction>().mapNotNull { it.target }
|
||||
val hitCountInstruction = subsequentInstructions.filterIsInstance<HitCountInstruction>().firstOrNull()
|
||||
|
||||
runtime.environment.query.addFunction("missed") { params ->
|
||||
@@ -148,6 +150,15 @@ class MoveInstruction(
|
||||
}
|
||||
}
|
||||
|
||||
runtime.environment.query.addFunction("failed") { params ->
|
||||
if (params.params.size == 0) {
|
||||
return@addFunction DoubleValue(failed)
|
||||
} else {
|
||||
val entityUUID = params.getString(0)
|
||||
return@addFunction DoubleValue(failedTargets.any { it.entity?.stringUUID == entityUUID })
|
||||
}
|
||||
}
|
||||
|
||||
val hurtTargets = subsequentInstructions.filterIsInstance<DamageInstruction>().mapNotNull { it.expectedTarget }
|
||||
runtime.environment.query.addFunction("hurt") { params ->
|
||||
if (params.params.size == 0) {
|
||||
|
||||
+1
@@ -66,6 +66,7 @@ class GraalShowdownService : ShowdownService {
|
||||
.allowArrayAccess(true)
|
||||
.allowListAccess(true)
|
||||
.allowMapAccess(true)
|
||||
.allowAccessAnnotatedBy(Export::class.java)
|
||||
.build()
|
||||
context = Context.newBuilder("js")
|
||||
.allowIO(true)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.apricorn.Apricorn
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.farming.ApricornHarvestEvent
|
||||
@@ -176,7 +177,7 @@ class ApricornBlock(settings: Properties, val apricorn: Apricorn) : HorizontalDi
|
||||
world.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(player, resetState))
|
||||
|
||||
if (!world.isClientSide) {
|
||||
world.playSoundServer(position = pos.toVec3d(), sound = SoundEvents.ITEM_PICKUP, volume = 0.7F, pitch = 1.4F)
|
||||
world.playSoundServer(position = pos.toVec3d(), sound = CobblemonSounds.APRICORN_HARVEST, volume = 1.0F, pitch = 1.0F)
|
||||
|
||||
if (world is ServerLevel && player is ServerPlayer) {
|
||||
CobblemonEvents.APRICORN_HARVESTED.post(ApricornHarvestEvent(player, apricorn, world, pos))
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
//import dev.lambdaurora.lambdynlights.util.SodiumDynamicLightHandler.pos
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.berry.Berries
|
||||
import com.cobblemon.mod.common.api.berry.Berry
|
||||
@@ -40,7 +39,11 @@ import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.LevelReader
|
||||
import net.minecraft.world.level.block.*
|
||||
import net.minecraft.world.level.block.BaseEntityBlock
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.BonemealableBlock
|
||||
import net.minecraft.world.level.block.RenderShape
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.entity.BlockEntityTicker
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
@@ -128,7 +131,10 @@ class BerryBlock(private val berryIdentifier: ResourceLocation, settings: Proper
|
||||
val newAge = curAge + 1
|
||||
if (newAge > FRUIT_AGE) return
|
||||
val newState = state.setValue(AGE, newAge)
|
||||
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
|
||||
val treeEntity = world.getBlockEntity(pos) as? BerryBlockEntity ?: run {
|
||||
world.setBlock(pos, Blocks.AIR.defaultBlockState(), UPDATE_CLIENTS)
|
||||
return
|
||||
}
|
||||
if (curAge == MATURE_AGE) {
|
||||
treeEntity.generateGrowthPoints(world, newState, pos, null)
|
||||
determineMutation(world, random, pos, newState)
|
||||
@@ -203,6 +209,7 @@ class BerryBlock(private val berryIdentifier: ResourceLocation, settings: Proper
|
||||
): InteractionResult {
|
||||
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
|
||||
if (player.getItemInHand(InteractionHand.MAIN_HAND).item is ShovelItem && getMulch(treeEntity) != MulchVariant.NONE) {
|
||||
treeEntity.setMulch(MulchVariant.NONE, world, state, pos)
|
||||
treeEntity.setChanged()
|
||||
world.playSound(null, pos, CobblemonSounds.MULCH_REMOVE, SoundSource.BLOCKS, 0.6F, 1F)
|
||||
this.spawnDestroyParticles(world, player, pos, state.setValue(AGE, 0))
|
||||
|
||||
@@ -127,7 +127,7 @@ class LecternBlock(properties: Properties): BaseEntityBlock(properties) {
|
||||
else {
|
||||
blockEntity.incrementViewerCount()
|
||||
PokedexUIPacket(type = (blockEntity.getItemStack().item as PokedexItem).type, blockPos = blockPos).sendToPlayer(player as ServerPlayer)
|
||||
level.playSoundServer(position = blockPos.toVec3d(), sound = CobblemonSounds.POKEDEX_OPEN, volume = 0.25F)
|
||||
level.playSoundServer(position = blockPos.bottomCenter, sound = CobblemonSounds.POKEDEX_OPEN, volume = 0.25F)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,7 +287,7 @@ class PCBlock(properties: Properties): BaseEntityBlock(properties), SimpleWaterl
|
||||
PCLinkManager.addLink(ProximityPCLink(pc, player.uuid, baseEntity))
|
||||
OpenPCPacket(pc).sendToPlayer(player)
|
||||
world.playSoundServer(
|
||||
position = blockPos.toVec3d(),
|
||||
position = blockPos.bottomCenter,
|
||||
sound = CobblemonSounds.PC_ON,
|
||||
volume = 0.5F,
|
||||
pitch = 1F
|
||||
|
||||
@@ -290,7 +290,7 @@ class PastureBlock(settings: Properties): BaseEntityBlock(settings), SimpleWater
|
||||
PastureLinkManager.createLink(player.uuid, PastureLink(linkId, pcId, ResourceLocation.tryParse(world.dimensionTypeRegistration().registeredName)!!, getBasePosition(state, pos), perms))
|
||||
|
||||
world.playSoundServer(
|
||||
position = pos.toVec3d(),
|
||||
position = pos.bottomCenter,
|
||||
sound = CobblemonSounds.PC_ON,
|
||||
volume = 0.5F,
|
||||
pitch = 1F
|
||||
|
||||
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.block
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.entity.MoLangScriptingEntity
|
||||
import com.cobblemon.mod.common.util.giveOrDropItemStack
|
||||
import com.cobblemon.mod.common.util.isServerSide
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
@@ -285,7 +286,7 @@ class SaccharineLeafBlock(settings: Properties) : LeavesBlock(settings) {
|
||||
// Replace the consumed empty bottle with a honey bottle
|
||||
player.setItemInHand(hand, ItemStack(Items.HONEY_BOTTLE));
|
||||
} else {
|
||||
player.addItem(Items.HONEY_BOTTLE.defaultInstance)
|
||||
player.giveOrDropItemStack(Items.HONEY_BOTTLE.defaultInstance)
|
||||
}
|
||||
|
||||
level.playSound(null, pos, SoundEvents.BOTTLE_FILL, SoundSource.BLOCKS)
|
||||
|
||||
+5
-27
@@ -16,24 +16,20 @@ import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.core.NonNullList
|
||||
import net.minecraft.core.component.DataComponents
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import net.minecraft.sounds.SoundSource
|
||||
import net.minecraft.world.ContainerHelper
|
||||
import net.minecraft.world.Containers
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.ItemInteractionResult
|
||||
import net.minecraft.world.WorldlyContainer
|
||||
import net.minecraft.world.*
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.world.level.gameevent.GameEvent
|
||||
import java.util.OptionalInt
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import java.util.*
|
||||
import kotlin.math.pow
|
||||
|
||||
class DiscShelfBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(CobblemonBlockEntities.DISC_SHELF, pos, state), WorldlyContainer {
|
||||
@@ -134,25 +130,7 @@ class DiscShelfBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobbl
|
||||
return stack.item is TechnicalMachineItem
|
||||
|| stack.item == CobblemonItems.UPGRADE
|
||||
|| stack.item == CobblemonItems.DUBIOUS_DISC
|
||||
|| stack.item == Items.MUSIC_DISC_13
|
||||
|| stack.item == Items.MUSIC_DISC_CAT
|
||||
|| stack.item == Items.MUSIC_DISC_BLOCKS
|
||||
|| stack.item == Items.MUSIC_DISC_CHIRP
|
||||
|| stack.item == Items.MUSIC_DISC_FAR
|
||||
|| stack.item == Items.MUSIC_DISC_MALL
|
||||
|| stack.item == Items.MUSIC_DISC_MELLOHI
|
||||
|| stack.item == Items.MUSIC_DISC_STAL
|
||||
|| stack.item == Items.MUSIC_DISC_STRAD
|
||||
|| stack.item == Items.MUSIC_DISC_WARD
|
||||
|| stack.item == Items.MUSIC_DISC_11
|
||||
|| stack.item == Items.MUSIC_DISC_WAIT
|
||||
|| stack.item == Items.MUSIC_DISC_OTHERSIDE
|
||||
|| stack.item == Items.MUSIC_DISC_5
|
||||
|| stack.item == Items.MUSIC_DISC_RELIC
|
||||
|| stack.item == Items.MUSIC_DISC_PRECIPICE
|
||||
|| stack.item == Items.MUSIC_DISC_CREATOR
|
||||
|| stack.item == Items.MUSIC_DISC_CREATOR_MUSIC_BOX
|
||||
|| stack.item == Items.MUSIC_DISC_PIGSTEP
|
||||
|| stack.has(DataComponents.JUKEBOX_PLAYABLE)
|
||||
}
|
||||
|
||||
fun onNoteBlockPulse(level: Level, noteBlockPos: BlockPos, soundEvent: SoundEvent) {
|
||||
|
||||
@@ -103,8 +103,6 @@ class GildedChestBlockEntity(pos: BlockPos, state: BlockState, val type: Type =
|
||||
return dir == Direction.DOWN
|
||||
}
|
||||
|
||||
override fun stillValid(player: Player) = !player.isSpectator
|
||||
|
||||
override fun setItems(inventory: NonNullList<ItemStack>) {
|
||||
inventoryContents = inventory
|
||||
}
|
||||
|
||||
+1
-1
@@ -120,7 +120,7 @@ class HealingMachineBlockEntity(
|
||||
updateBlockChargeLevel(HealingMachineBlock.MAX_CHARGE_LEVEL + 1)
|
||||
val world = level ?: return
|
||||
if (!world.isClientSide) world.playSoundServer(
|
||||
position = blockPos.toVec3d(),
|
||||
position = blockPos.bottomCenter,
|
||||
sound = CobblemonSounds.HEALING_MACHINE_ACTIVE,
|
||||
volume = 1F,
|
||||
pitch = 1F
|
||||
|
||||
+23
-1
@@ -16,6 +16,8 @@ import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
||||
import com.cobblemon.mod.common.block.tmmachine.TMMachineBlock
|
||||
import com.cobblemon.mod.common.block.tmmachine.TMMachineMenu
|
||||
import com.cobblemon.mod.common.client.gui.tmmachine.TMMachineScreen
|
||||
import com.cobblemon.mod.common.client.sound.BlockEntitySoundTracker
|
||||
import com.cobblemon.mod.common.client.sound.instances.CancellableSoundInstance
|
||||
import com.cobblemon.mod.common.item.components.TMMoveComponent
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.HolderLookup
|
||||
@@ -38,6 +40,7 @@ import net.minecraft.sounds.SoundSource
|
||||
import net.minecraft.world.Container
|
||||
import net.minecraft.world.inventory.ContainerData
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
|
||||
class TMMachineBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
BaseContainerBlockEntity(CobblemonBlockEntities.TM_MACHINE, pos, state), TintBlockEntity {
|
||||
@@ -60,6 +63,19 @@ class TMMachineBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
const val POSITION_Y_INDEX = 4
|
||||
const val POSITION_Z_INDEX = 5
|
||||
|
||||
fun clientTick(level: Level, pos: BlockPos, state: BlockState, blockEntity: TMMachineBlockEntity) {
|
||||
if (!level.isClientSide) return
|
||||
|
||||
val burning = blockEntity.containerData.get(BURN_ACTIVE_INDEX) == 1
|
||||
val isBurningSoundActive = BlockEntitySoundTracker.isActive(pos, CobblemonSounds.TM_MACHINE_BURN_LOOP.location)
|
||||
|
||||
if (burning) {
|
||||
if (!isBurningSoundActive) BlockEntitySoundTracker.play(pos, CancellableSoundInstance(CobblemonSounds.TM_MACHINE_BURN_LOOP, pos, true, 1F, 1F))
|
||||
} else {
|
||||
BlockEntitySoundTracker.stop(pos, CobblemonSounds.TM_MACHINE_BURN_LOOP.location)
|
||||
}
|
||||
}
|
||||
|
||||
fun serverTick(level: Level, pos: BlockPos, state: BlockState, blockEntity: TMMachineBlockEntity) {
|
||||
if (level.isClientSide) return
|
||||
|
||||
@@ -112,6 +128,11 @@ class TMMachineBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
}
|
||||
}
|
||||
|
||||
override fun setRemoved() {
|
||||
if (level?.isClientSide == true) BlockEntitySoundTracker.stop(blockPos, CancellableSoundInstance(CobblemonSounds.TM_MACHINE_BURN_LOOP, blockPos, true, 1F, 1F))
|
||||
return super.setRemoved()
|
||||
}
|
||||
|
||||
override var tint: Int? = null
|
||||
|
||||
var tmMachineInventory = TMMachineBlockInventory(this)
|
||||
@@ -210,6 +231,8 @@ class TMMachineBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
resultStack.grow(1)
|
||||
}
|
||||
|
||||
playSound(CobblemonSounds.TM_MACHINE_CRAFT, 1.0F, 1.0F)
|
||||
|
||||
tmMachineInventory.setChanged()
|
||||
setChanged()
|
||||
}
|
||||
@@ -278,7 +301,6 @@ class TMMachineBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
if (state.hasProperty(TMMachineBlock.EMPTY)) {
|
||||
val isEmpty = getItem(TMMachineMenu.BLANK_TM_SLOT).isEmpty
|
||||
if (currentState.getValue(TMMachineBlock.EMPTY) != isEmpty) {
|
||||
if (!isEmpty) playSound(CobblemonSounds.TM_MACHINE_PLACE_DISC, 0.5F)
|
||||
currentState = currentState.setValue(TMMachineBlock.EMPTY, isEmpty)
|
||||
updated = true
|
||||
}
|
||||
|
||||
+19
-29
@@ -11,16 +11,13 @@ package com.cobblemon.mod.common.block.habitat
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatSpawn
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatPhaseOrder
|
||||
import com.cobblemon.mod.common.api.habitats.NaturalHabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.dto.HabitatSettingsDTO
|
||||
import com.cobblemon.mod.common.api.habitats.spawningstyle.ActivatedHabitatSpawning
|
||||
import com.cobblemon.mod.common.api.habitats.spawningstyle.HabitatSpawningStyle
|
||||
import com.cobblemon.mod.common.api.habitats.spawningstyle.NaturalHabitatSpawning
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
@@ -63,18 +60,23 @@ class HabitatBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
|
||||
var spawningStyle: HabitatSpawningStyle = ActivatedHabitatSpawning(this)
|
||||
|
||||
var numberOfPhases: Int = 1
|
||||
var phaseOrder = HabitatPhaseOrder.SIMPLE
|
||||
var currentPhase = 1
|
||||
|
||||
val numberOfPhases: Int
|
||||
get() = pool.getPhaseCount()
|
||||
|
||||
var levelRange: IntRange = 1..Cobblemon.config.maxPokemonLevel
|
||||
var modifiers: PokemonProperties = PokemonProperties()
|
||||
|
||||
var pool: HabitatPool = HabitatPool.default()
|
||||
var mimicId: ResourceLocation = BuiltInRegistries.BLOCK.getKey(Blocks.STONE)
|
||||
val mimickedState: BlockState
|
||||
get() = BuiltInRegistries.BLOCK.get(mimicId).defaultBlockState() ?: Blocks.STONE.defaultBlockState()
|
||||
var displaySpeciesIds: List<ResourceLocation> = emptyList()
|
||||
|
||||
// This is cached here so that it doesn't have to be regenerated every time the block is triggered, updated when settings change.
|
||||
var spawnDetails: List<SpawnDetail> = listOf()
|
||||
|
||||
fun onUse(player: Player, hit: BlockHitResult): InteractionResult {
|
||||
if (player.level().isClientSide) {
|
||||
return InteractionResult.SUCCESS
|
||||
@@ -97,6 +99,7 @@ class HabitatBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
val spawningStyle = spawningStyle
|
||||
var activatedStyle = false
|
||||
var cancelsRegularSpawns = false
|
||||
spawnDetails = pool.createSpawnDetails(this)
|
||||
if (spawningStyle is ActivatedHabitatSpawning) {
|
||||
spawningStyle.generateSpawnDetails(level as ServerLevel, blockPos)
|
||||
activatedStyle = true
|
||||
@@ -136,21 +139,13 @@ class HabitatBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
|
||||
fun applySettings(habitatSettingsDTO: HabitatSettingsDTO) {
|
||||
val activatedSettings = habitatSettingsDTO.activatedSettings
|
||||
val activatedPool = habitatSettingsDTO.activatedSettings?.habitatPool?.toHabitatPool() as? ActivatedHabitatPool
|
||||
val isActivated = habitatSettingsDTO.isActivatedSpawning
|
||||
if (isActivated && activatedPool == null) {
|
||||
return // Pool was bad, check console logs
|
||||
}
|
||||
val naturalSettings = habitatSettingsDTO.naturalSettings
|
||||
val naturalPool = habitatSettingsDTO.naturalSettings?.habitatPool?.toHabitatPool() as? NaturalHabitatPool
|
||||
if (!isActivated && naturalPool == null) {
|
||||
return // Pool was bad, check console logs
|
||||
}
|
||||
this.numberOfPhases = habitatSettingsDTO.numberOfPhases
|
||||
val isActivated = habitatSettingsDTO.isActivatedSpawning
|
||||
this.phaseOrder = habitatSettingsDTO.phaseOrder
|
||||
this.levelRange = habitatSettingsDTO.levelRange
|
||||
this.modifiers = PokemonProperties.parse(habitatSettingsDTO.modifiers)
|
||||
this.mimicId = habitatSettingsDTO.mimicId
|
||||
this.pool = habitatSettingsDTO.pool.toHabitatPool() ?: HabitatPool.default()
|
||||
if (isActivated && activatedSettings != null) {
|
||||
val activatedHabitatSpawning = ActivatedHabitatSpawning(this)
|
||||
activatedHabitatSpawning.trigger = activatedSettings.trigger
|
||||
@@ -159,13 +154,11 @@ class HabitatBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
activatedHabitatSpawning.spawnRange = activatedSettings.spawnRange
|
||||
activatedHabitatSpawning.maxSpawns = activatedSettings.maxSpawns
|
||||
activatedHabitatSpawning.maxSpawnsPerActivation = activatedSettings.maxSpawnsPerActivation
|
||||
activatedHabitatSpawning.pool = activatedPool!!
|
||||
this.spawningStyle = activatedHabitatSpawning
|
||||
} else if (naturalSettings != null && naturalPool != null) {
|
||||
} else if (naturalSettings != null) {
|
||||
val naturalHabitatSpawning = NaturalHabitatSpawning(this)
|
||||
naturalHabitatSpawning.replaceSpawns = naturalSettings.replaceSpawns
|
||||
naturalHabitatSpawning.rangeOfInfluence = naturalSettings.rangeOfInfluence
|
||||
naturalHabitatSpawning.pool = naturalPool
|
||||
this.spawningStyle = naturalHabitatSpawning
|
||||
}
|
||||
|
||||
@@ -177,7 +170,6 @@ class HabitatBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
override fun saveAdditional(tag: CompoundTag, registryLookup: HolderLookup.Provider) {
|
||||
super.saveAdditional(tag, registryLookup)
|
||||
tag.putString(DataKeys.HABITAT_MIMIC, mimicId.toString())
|
||||
tag.putInt(DataKeys.HABITAT_PHASE_COUNT, numberOfPhases)
|
||||
tag.putString(DataKeys.HABITAT_PHASE_ORDER, phaseOrder.toString())
|
||||
// Only save the level range when it's a non-trivial one
|
||||
if (levelRange.first != 1 || levelRange.last != Cobblemon.config.maxPokemonLevel) {
|
||||
@@ -186,6 +178,7 @@ class HabitatBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
tag.putString(DataKeys.HABITAT_MODIFIERS, modifiers.asString())
|
||||
tag.putString(DataKeys.HABITAT_SPAWNING_STYLE, spawningStyle.type.toString())
|
||||
spawningStyle.writeToNBT(tag)
|
||||
HabitatPool.writePoolToNBT(pool = pool, nbt = tag)
|
||||
val speciesListTag = ListTag()
|
||||
displaySpeciesIds.forEach { speciesListTag.add(StringTag.valueOf(it.toString())) }
|
||||
tag.put(DISPLAY_SPECIES_KEY, speciesListTag)
|
||||
@@ -208,17 +201,17 @@ class HabitatBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
return // Client doesn't need to try loading the rest
|
||||
}
|
||||
|
||||
numberOfPhases = tag.getInt(DataKeys.HABITAT_PHASE_COUNT)
|
||||
phaseOrder = HabitatPhaseOrder.valueOf(tag.getString(DataKeys.HABITAT_PHASE_ORDER))
|
||||
levelRange = tag.getString(DataKeys.HABITAT_LEVEL_RANGE).takeIf { it.isNotBlank() }?.let(IntRangeAdapter::deserialize) ?: 1..Cobblemon.config.maxPokemonLevel
|
||||
modifiers = tag.getString(DataKeys.HABITAT_MODIFIERS).takeIf { it.isNotBlank() }?.let(PokemonProperties::parse) ?: PokemonProperties()
|
||||
val spawningStyleType = tag.getString(DataKeys.HABITAT_SPAWNING_STYLE).asIdentifierDefaultingNamespace()
|
||||
spawningStyle = if (spawningStyleType == ActivatedHabitatSpawn.TYPE) {
|
||||
spawningStyle = if (spawningStyleType == ActivatedHabitatSpawning.TYPE) {
|
||||
ActivatedHabitatSpawning(this)
|
||||
} else {
|
||||
NaturalHabitatSpawning(this)
|
||||
}
|
||||
spawningStyle.readFromNBT(tag)
|
||||
pool = HabitatPool.readPoolFromNBT(tag)
|
||||
refreshDisplaySpeciesIds()
|
||||
}
|
||||
|
||||
@@ -237,10 +230,7 @@ class HabitatBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
}
|
||||
|
||||
private fun refreshDisplaySpeciesIds() {
|
||||
displaySpeciesIds = when (val style = spawningStyle) {
|
||||
is ActivatedHabitatSpawning -> style.pool.spawns.map { it.species.resourceIdentifier }.distinct()
|
||||
is NaturalHabitatSpawning -> style.pool.spawns.map { it.species.resourceIdentifier }.distinct()
|
||||
}
|
||||
displaySpeciesIds = pool.spawns.map { it.species.resourceIdentifier }.distinct()
|
||||
}
|
||||
|
||||
fun getInfluentialRange(spawner: Spawner): Int {
|
||||
@@ -273,7 +263,7 @@ class HabitatBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
return if (spawningStyle is NaturalHabitatSpawning) {
|
||||
if (spawningStyle.replaceSpawns) {
|
||||
// Replace mode! Permit spawning only if it's from this block
|
||||
detail in spawningStyle.spawnDetails
|
||||
detail in spawnDetails
|
||||
} else {
|
||||
true // We don't mess with other types
|
||||
}
|
||||
@@ -285,10 +275,10 @@ class HabitatBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
}
|
||||
}
|
||||
|
||||
override fun injectSpawns(bucket: SpawnBucket, spawnablePosition: SpawnablePosition): List<SpawnDetail>? {
|
||||
override fun injectSpawns(bucket: String, spawnablePosition: SpawnablePosition): List<SpawnDetail>? {
|
||||
val spawningStyle = spawningStyle
|
||||
return if (spawningStyle is NaturalHabitatSpawning) {
|
||||
spawningStyle.spawnDetails.filter { it.spawnablePositionType.clazz == spawnablePosition::class.java && it.isSatisfiedBy(spawnablePosition) }
|
||||
spawnDetails.filter { it.spawnablePositionType.clazz == spawnablePosition::class.java && it.isSatisfiedBy(spawnablePosition) }
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
+7
-3
@@ -384,7 +384,7 @@ class FossilMultiblockStructure (
|
||||
val chance = Cobblemon.config.fossilMachineAlphaChance
|
||||
if (Random.nextInt(chance) == 0) {
|
||||
pokemon.isAlpha = true
|
||||
pokemon.initializeMovesetWithRandomTm(2)
|
||||
pokemon.initializeMovesetFromDefault()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -630,7 +630,8 @@ class FossilMultiblockStructure (
|
||||
result.putInt(DataKeys.ORGANIC_MATERIAL, organicMaterialInside)
|
||||
val fossilInv = ListTag()
|
||||
|
||||
fossilInventory.forEach { fossilInv.add(ItemStack.CODEC.encodeStart(NbtOps.INSTANCE, it).orThrow) }
|
||||
val ops = registryLookup.createSerializationContext(NbtOps.INSTANCE)
|
||||
fossilInventory.forEach { fossilInv.add(ItemStack.CODEC.encodeStart(ops, it).getOrThrow()) }
|
||||
result.put(DataKeys.FOSSIL_INVENTORY, fossilInv)
|
||||
result.putString(DataKeys.CONNECTOR_DIRECTION, tankConnectorDirection?.toString())
|
||||
|
||||
@@ -669,7 +670,10 @@ class FossilMultiblockStructure (
|
||||
|
||||
val fossilInv = if (nbt.contains(DataKeys.FOSSIL_INVENTORY)) { (nbt.get(DataKeys.FOSSIL_INVENTORY) as ListTag) } else ListTag()
|
||||
val actualFossilList = mutableListOf<ItemStack>()
|
||||
fossilInv.forEach { ItemStack.parse(registryLookup, it).ifPresent(actualFossilList::add) }
|
||||
|
||||
val ops = registryLookup.createSerializationContext(NbtOps.INSTANCE)
|
||||
fossilInv.forEach { ItemStack.CODEC.parse(ops, it).result().ifPresent(actualFossilList::add) }
|
||||
|
||||
result.fossilInventory = actualFossilList
|
||||
result.tankConnectorDirection = Direction.byName(nbt.getString(DataKeys.CONNECTOR_DIRECTION))
|
||||
|
||||
|
||||
@@ -122,8 +122,8 @@ class TMMachineBlock(properties: Properties) : BaseEntityBlock(properties), Simp
|
||||
|
||||
@Nullable
|
||||
protected fun <T : BlockEntity> createTicker(level: Level, serverType: BlockEntityType<T>, clientType: BlockEntityType<out TMMachineBlockEntity>): BlockEntityTicker<T>? {
|
||||
return if (!level.isClientSide) createTickerHelper(serverType, clientType, TMMachineBlockEntity::serverTick)
|
||||
else null
|
||||
return if (level.isClientSide) createTickerHelper(serverType, clientType, TMMachineBlockEntity::clientTick)
|
||||
else createTickerHelper(serverType, clientType, TMMachineBlockEntity::serverTick)
|
||||
}
|
||||
|
||||
override fun fallOn(level: Level, state: BlockState, pos: BlockPos, entity: Entity, fallDistance: Float) {
|
||||
@@ -133,7 +133,7 @@ class TMMachineBlock(properties: Properties) : BaseEntityBlock(properties), Simp
|
||||
state.hasProperty(OPEN) && state.getValue(OPEN)
|
||||
) {
|
||||
level.setBlockAndUpdate(pos, state.setValue(OPEN, false))
|
||||
level.playSoundServer(position = pos.toVec3d(), sound = CobblemonSounds.TM_MACHINE_CLOSE, volume = 1F, pitch = 1F)
|
||||
level.playSoundServer(position = pos.bottomCenter, sound = CobblemonSounds.TM_MACHINE_CLOSE, volume = 1F, pitch = 1F)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,8 +156,8 @@ class TMMachineBlock(properties: Properties) : BaseEntityBlock(properties), Simp
|
||||
val isOpen = state.getValue(OPEN)
|
||||
level.setBlockAndUpdate(pos, state.setValue(OPEN, !isOpen))
|
||||
level.playSoundServer(
|
||||
position = pos.toVec3d(),
|
||||
sound = if (!isOpen) CobblemonSounds.TM_MACHINE_CLOSE else CobblemonSounds.TM_MACHINE_OPEN,
|
||||
position = pos.bottomCenter,
|
||||
sound = if (isOpen) CobblemonSounds.TM_MACHINE_CLOSE else CobblemonSounds.TM_MACHINE_OPEN,
|
||||
volume = 1F,
|
||||
pitch = 1F
|
||||
)
|
||||
@@ -176,12 +176,6 @@ class TMMachineBlock(properties: Properties) : BaseEntityBlock(properties), Simp
|
||||
val blockEntity = level.getBlockEntity(pos)
|
||||
if (blockEntity is TMMachineBlockEntity) {
|
||||
player.openMenu(blockEntity)
|
||||
level.playSoundServer(
|
||||
position = pos.toVec3d(),
|
||||
sound = CobblemonSounds.TM_MACHINE_ON,
|
||||
volume = 0.5F,
|
||||
pitch = 1F
|
||||
)
|
||||
level.gameEvent(player, GameEvent.BLOCK_OPEN, pos)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,11 +10,13 @@ package com.cobblemon.mod.common.block.tmmachine
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.CobblemonMenuType
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachine
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
||||
import com.cobblemon.mod.common.block.entity.TMMachineBlockEntity
|
||||
import com.cobblemon.mod.common.util.itemRegistry
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.SynchronizeActiveTMPacket
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.world.Container
|
||||
import net.minecraft.world.SimpleContainer
|
||||
@@ -35,38 +37,20 @@ class TMMachineMenu(menuType: MenuType<*>, syncId: Int) : AbstractContainerMenu(
|
||||
var inventory: Container? = null
|
||||
var containerData: ContainerData? = null
|
||||
|
||||
var currentlySelectedTM: TechnicalMachine? = null
|
||||
private var lastSelectedTM: TechnicalMachine? = null
|
||||
|
||||
var onSelectedTMUpdate: (TechnicalMachine?) -> Unit = {}
|
||||
|
||||
constructor(syncId: Int, playerInventory: Inventory) : this(CobblemonMenuType.TM_MACHINE, syncId) {
|
||||
this.playerInventory = playerInventory
|
||||
this.inventory = SimpleContainer(5)
|
||||
this.containerData = SimpleContainerData(6)
|
||||
this.tmMachineEntity = (inventory as? TMMachineBlockEntity.TMMachineBlockInventory)?.blockEntity
|
||||
|
||||
this.addDataSlots(containerData!!)
|
||||
inventory?.startOpen(playerInventory.player)
|
||||
|
||||
val startX = 15
|
||||
val startY = 144
|
||||
val slotWidth = 18
|
||||
val slotHeight = 18
|
||||
|
||||
for (row in 0..2) {
|
||||
for (col in 0..8) {
|
||||
this.addSlot(Slot(playerInventory, 9 + (row * 9) + col, startX + (slotWidth * col), startY + (slotHeight * row)))
|
||||
}
|
||||
}
|
||||
for (col in 0..8) {
|
||||
this.addSlot(Slot(playerInventory, col, startX + (slotWidth * col), startY + 58))
|
||||
}
|
||||
|
||||
val container = inventory
|
||||
container?.let {
|
||||
this.addSlot(TMResultSlot(container, RESULT_SLOT, startX + 37, startY - 28)) // Output slot
|
||||
this.addSlot(BlankTMSlot(container, BLANK_TM_SLOT, startX + 90, startY - 28)) // Blank TM
|
||||
for (index in INGREDIENT_SLOTS) {
|
||||
this.addSlot(Slot(container, index, startX + 114 + (18 * (index - INGREDIENT_SLOTS.first)), startY - 28))
|
||||
}
|
||||
}
|
||||
this.addSlotListener(this)
|
||||
initiateSlots()
|
||||
}
|
||||
|
||||
constructor(syncId: Int, playerInventory: Inventory, inventory: Container, containerData: ContainerData) : this(CobblemonMenuType.TM_MACHINE, syncId) {
|
||||
@@ -78,6 +62,20 @@ class TMMachineMenu(menuType: MenuType<*>, syncId: Int) : AbstractContainerMenu(
|
||||
this.addDataSlots(containerData)
|
||||
inventory.startOpen(playerInventory.player)
|
||||
|
||||
initiateSlots()
|
||||
|
||||
tmMachineEntity?.let {
|
||||
val move = Moves.getByName(it.activeMove)
|
||||
move.let {
|
||||
currentlySelectedTM = TechnicalMachines.moveToTM[it]
|
||||
synchronizeSelectedTM()
|
||||
}
|
||||
}
|
||||
|
||||
this.addSlotListener(this)
|
||||
}
|
||||
|
||||
private fun initiateSlots() {
|
||||
val startX = 15
|
||||
val startY = 144
|
||||
val slotWidth = 18
|
||||
@@ -92,7 +90,7 @@ class TMMachineMenu(menuType: MenuType<*>, syncId: Int) : AbstractContainerMenu(
|
||||
this.addSlot(Slot(playerInventory, col, startX + (slotWidth * col), startY + 58))
|
||||
}
|
||||
|
||||
val container = inventory
|
||||
val container = inventory!!
|
||||
container.let {
|
||||
this.addSlot(TMResultSlot(container, RESULT_SLOT, startX + 37, startY - 28)) // Output slot
|
||||
this.addSlot(BlankTMSlot(container, BLANK_TM_SLOT, startX + 90, startY - 28)) // Blank TM
|
||||
@@ -100,7 +98,6 @@ class TMMachineMenu(menuType: MenuType<*>, syncId: Int) : AbstractContainerMenu(
|
||||
this.addSlot(Slot(container, index, startX + 114 + (18 * (index - INGREDIENT_SLOTS.first)), startY - 28))
|
||||
}
|
||||
}
|
||||
this.addSlotListener(this)
|
||||
}
|
||||
|
||||
override fun quickMoveStack(player: Player, index: Int): ItemStack {
|
||||
@@ -207,4 +204,25 @@ class TMMachineMenu(menuType: MenuType<*>, syncId: Int) : AbstractContainerMenu(
|
||||
override fun dataChanged(containerMenu: AbstractContainerMenu, dataSlotIndex: Int, value: Int) {
|
||||
broadcastChanges()
|
||||
}
|
||||
|
||||
fun synchronizeSelectedTM() {
|
||||
val serverPlayer = playerInventory?.player as? ServerPlayer ?: return
|
||||
serverPlayer.sendPacket(SynchronizeActiveTMPacket(currentlySelectedTM))
|
||||
lastSelectedTM = currentlySelectedTM
|
||||
}
|
||||
|
||||
override fun sendAllDataToRemote() {
|
||||
super.sendAllDataToRemote()
|
||||
synchronizeSelectedTM()
|
||||
}
|
||||
|
||||
override fun broadcastChanges() {
|
||||
super.broadcastChanges()
|
||||
if (lastSelectedTM != currentlySelectedTM) synchronizeSelectedTM()
|
||||
}
|
||||
|
||||
fun clientSetSelectedTM(tm: TechnicalMachine?) {
|
||||
currentlySelectedTM = tm
|
||||
onSelectedTMUpdate(tm)
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user